The response has been limited to 50k tokens of the smallest files in the repo. You can remove this limitation by removing the max tokens filter.
├── .github
    ├── auto-author-assign-config.yml
    ├── release-drafter-config.yml
    └── workflows
    │   ├── auto-author-assign.yml
    │   ├── coverage.yml
    │   ├── docusaurus.yml
    │   ├── javascript.yml
    │   ├── prepare-release.yml
    │   ├── publish-docs.yml
    │   ├── publish-javascript.yml
    │   ├── publish-python.yml
    │   ├── publish-rust.yml
    │   ├── python.yml
    │   ├── release-drafter.yml
    │   └── rust.yml
├── .gitignore
├── AGENTS.md
├── Cargo.lock
├── Cargo.toml
├── LICENSE
├── README.md
├── cli
    ├── Cargo.toml
    ├── src
    │   ├── cli.rs
    │   ├── command.rs
    │   ├── helper.rs
    │   ├── lib.rs
    │   ├── main.rs
    │   └── print.rs
    └── tests
    │   └── dump.rs
├── core
    ├── Cargo.toml
    └── src
    │   ├── ast.rs
    │   ├── ast
    │       ├── ast_literal.rs
    │       ├── data_type.rs
    │       ├── ddl.rs
    │       ├── expr.rs
    │       ├── function.rs
    │       ├── operator.rs
    │       └── query.rs
    │   ├── ast_builder.rs
    │   ├── ast_builder
    │       ├── alter_table.rs
    │       ├── assignment.rs
    │       ├── build.rs
    │       ├── column_def.rs
    │       ├── column_list.rs
    │       ├── create_table.rs
    │       ├── data_type.rs
    │       ├── delete.rs
    │       ├── drop_table.rs
    │       ├── error.rs
    │       ├── execute.rs
    │       ├── expr.rs
    │       ├── expr
    │       │   ├── aggregate.rs
    │       │   ├── alias_as.rs
    │       │   ├── between.rs
    │       │   ├── binary_op.rs
    │       │   ├── case.rs
    │       │   ├── exists.rs
    │       │   ├── function.rs
    │       │   ├── in_list.rs
    │       │   ├── is_null.rs
    │       │   ├── like.rs
    │       │   ├── nested.rs
    │       │   ├── numeric.rs
    │       │   └── unary_op.rs
    │       ├── expr_list.rs
    │       ├── expr_with_alias.rs
    │       ├── index.rs
    │       ├── index_item.rs
    │       ├── index_item
    │       │   ├── cmp_expr.rs
    │       │   ├── non_clustered.rs
    │       │   └── primary_key.rs
    │       ├── insert.rs
    │       ├── order_by_expr.rs
    │       ├── order_by_expr_list.rs
    │       ├── query.rs
    │       ├── select.rs
    │       ├── select
    │       │   ├── filter.rs
    │       │   ├── group_by.rs
    │       │   ├── having.rs
    │       │   ├── join.rs
    │       │   ├── join
    │       │   │   ├── hash_join.rs
    │       │   │   ├── join_constraint.rs
    │       │   │   └── root.rs
    │       │   ├── limit.rs
    │       │   ├── offset.rs
    │       │   ├── offset_limit.rs
    │       │   ├── order_by.rs
    │       │   ├── project.rs
    │       │   ├── root.rs
    │       │   └── values.rs
    │       ├── select_item.rs
    │       ├── select_item_list.rs
    │       ├── show_columns.rs
    │       ├── table_factor.rs
    │       ├── table_name.rs
    │       ├── transaction.rs
    │       └── update.rs
    │   ├── data.rs
    │   ├── data
    │       ├── bigdecimal_ext.rs
    │       ├── function.rs
    │       ├── interval.rs
    │       ├── interval
    │       │   ├── error.rs
    │       │   ├── primitive.rs
    │       │   └── string.rs
    │       ├── key.rs
    │       ├── literal.rs
    │       ├── point.rs
    │       ├── row.rs
    │       ├── schema.rs
    │       ├── string_ext.rs
    │       ├── table.rs
    │       ├── value.rs
    │       └── value
    │       │   ├── binary_op.rs
    │       │   ├── binary_op
    │       │       ├── decimal.rs
    │       │       ├── f32.rs
    │       │       ├── f64.rs
    │       │       ├── integer.rs
    │       │       └── integer
    │       │       │   ├── i128.rs
    │       │       │   ├── i16.rs
    │       │       │   ├── i32.rs
    │       │       │   ├── i64.rs
    │       │       │   ├── i8.rs
    │       │       │   ├── macros.rs
    │       │       │   ├── u128.rs
    │       │       │   ├── u16.rs
    │       │       │   ├── u32.rs
    │       │       │   ├── u64.rs
    │       │       │   └── u8.rs
    │       │   ├── convert.rs
    │       │   ├── date.rs
    │       │   ├── error.rs
    │       │   ├── expr.rs
    │       │   ├── json.rs
    │       │   ├── literal.rs
    │       │   ├── selector.rs
    │       │   └── uuid.rs
    │   ├── executor.rs
    │   ├── executor
    │       ├── aggregate.rs
    │       ├── aggregate
    │       │   └── state.rs
    │       ├── alter.rs
    │       ├── alter
    │       │   ├── alter_table.rs
    │       │   ├── error.rs
    │       │   ├── function.rs
    │       │   ├── index.rs
    │       │   ├── table.rs
    │       │   └── validate.rs
    │       ├── context.rs
    │       ├── context
    │       │   ├── aggregate_context.rs
    │       │   └── row_context.rs
    │       ├── delete.rs
    │       ├── evaluate.rs
    │       ├── evaluate
    │       │   ├── error.rs
    │       │   ├── evaluated.rs
    │       │   ├── expr.rs
    │       │   └── function.rs
    │       ├── execute.rs
    │       ├── fetch.rs
    │       ├── filter.rs
    │       ├── insert.rs
    │       ├── join.rs
    │       ├── limit.rs
    │       ├── select.rs
    │       ├── select
    │       │   ├── error.rs
    │       │   └── project.rs
    │       ├── sort.rs
    │       ├── update.rs
    │       └── validate.rs
    │   ├── glue.rs
    │   ├── lib.rs
    │   ├── mock.rs
    │   ├── parse_sql.rs
    │   ├── plan.rs
    │   ├── plan
    │       ├── context.rs
    │       ├── error.rs
    │       ├── evaluable.rs
    │       ├── expr.rs
    │       ├── expr
    │       │   ├── aggregate.rs
    │       │   └── function.rs
    │       ├── index.rs
    │       ├── join.rs
    │       ├── planner.rs
    │       ├── primary_key.rs
    │       ├── schema.rs
    │       └── validate.rs
    │   ├── result.rs
    │   ├── store.rs
    │   ├── store
    │       ├── alter_table.rs
    │       ├── data_row.rs
    │       ├── function.rs
    │       ├── index.rs
    │       ├── metadata.rs
    │       └── transaction.rs
    │   ├── translate.rs
    │   └── translate
    │       ├── ast_literal.rs
    │       ├── data_type.rs
    │       ├── ddl.rs
    │       ├── error.rs
    │       ├── expr.rs
    │       ├── function.rs
    │       ├── operator.rs
    │       └── query.rs
├── docs
    ├── .gitignore
    ├── README.md
    ├── babel.config.js
    ├── blog
    │   ├── 2023-05-27-release-v0.14.md
    │   ├── 2023-05-29-breaking-the-boundary.md
    │   ├── 2023-05-30-revolutionizing-databases-by-unifying-query-interfaces.md
    │   ├── 2023-05-30-test-driven-documentation.md
    │   ├── 2023-11-18-release-v0.15.md
    │   └── assets
    │   │   ├── blog-test-driven-documentation-insert-errorcase.jpg
    │   │   └── blog-test-driven-documentation-insert.jpg
    ├── docs
    │   ├── ast-builder
    │   │   ├── _category_.json
    │   │   ├── expressions
    │   │   │   ├── _category_.json
    │   │   │   ├── conditional.md
    │   │   │   ├── nested.md
    │   │   │   ├── operator-based.md
    │   │   │   ├── pattern-matching.md
    │   │   │   └── value-checking.md
    │   │   ├── functions
    │   │   │   ├── _category_.json
    │   │   │   ├── date-&-time
    │   │   │   │   ├── _category_.json
    │   │   │   │   ├── conversion.md
    │   │   │   │   ├── current-date-and-time.md
    │   │   │   │   ├── date-and-time-extraction.md
    │   │   │   │   └── formatting.md
    │   │   │   ├── geometry
    │   │   │   │   ├── _category_.json
    │   │   │   │   ├── coordinate-extraction.md
    │   │   │   │   ├── distance-calculation.md
    │   │   │   │   └── point-creation.md
    │   │   │   ├── list-&-map
    │   │   │   │   ├── _category_.json
    │   │   │   │   ├── list-and-map-concatenation.md
    │   │   │   │   └── list-manipulation.md
    │   │   │   ├── math
    │   │   │   │   ├── _category_.json
    │   │   │   │   ├── basic-arithmetic.md
    │   │   │   │   ├── conversion.md
    │   │   │   │   ├── logarithmic-and-exponential.md
    │   │   │   │   ├── rounding.md
    │   │   │   │   ├── special-mathematical.md
    │   │   │   │   └── trigonometric.md
    │   │   │   ├── others
    │   │   │   │   ├── _category_.json
    │   │   │   │   ├── null-handling.md
    │   │   │   │   ├── type-conversion.md
    │   │   │   │   └── unique-identifier.md
    │   │   │   └── text
    │   │   │   │   ├── _category_.json
    │   │   │   │   ├── case-conversion.md
    │   │   │   │   ├── character-conversion.md
    │   │   │   │   ├── padding.md
    │   │   │   │   ├── position-and-indexing.md
    │   │   │   │   ├── text-manipulation.md
    │   │   │   │   └── trimming.md
    │   │   ├── intro.md
    │   │   └── statements
    │   │   │   ├── _category_.json
    │   │   │   ├── data-manipulation
    │   │   │       ├── _category_.json
    │   │   │       ├── deleting-data.md
    │   │   │       ├── inserting-data.md
    │   │   │       └── updating-data.md
    │   │   │   └── querying
    │   │   │       ├── _category_.json
    │   │   │       ├── creating-derived-subqueries.md
    │   │   │       ├── data-aggregation.md
    │   │   │       ├── data-injection.md
    │   │   │       ├── data-joining.md
    │   │   │       ├── data-selection-and-projection.md
    │   │   │       ├── data-sorting-and-limiting.md
    │   │   │       ├── fetching-data-from-storage.md
    │   │   │       └── using-preloaded-data.md
    │   ├── getting-started
    │   │   ├── _category_.json
    │   │   ├── cli.md
    │   │   ├── javascript-web.md
    │   │   ├── nodejs.md
    │   │   └── rust.md
    │   ├── index.md
    │   ├── sql-syntax
    │   │   ├── _category_.json
    │   │   ├── data-types
    │   │   │   ├── _category_.json
    │   │   │   ├── boolean.md
    │   │   │   ├── bytea.md
    │   │   │   ├── date.md
    │   │   │   ├── decimal.md
    │   │   │   ├── float.md
    │   │   │   ├── inet.md
    │   │   │   ├── integers.md
    │   │   │   ├── interval.md
    │   │   │   ├── list.md
    │   │   │   ├── map.md
    │   │   │   ├── text.md
    │   │   │   ├── time.md
    │   │   │   ├── timestamp.md
    │   │   │   └── uuid.md
    │   │   ├── functions
    │   │   │   ├── _category_.json
    │   │   │   ├── datetime
    │   │   │   │   ├── _category_.json
    │   │   │   │   ├── add-month.md
    │   │   │   │   ├── extract.md
    │   │   │   │   ├── format.md
    │   │   │   │   ├── last-day.md
    │   │   │   │   ├── now.md
    │   │   │   │   ├── to-date.md
    │   │   │   │   ├── to-time.md
    │   │   │   │   └── to-timestamp.md
    │   │   │   ├── geometry
    │   │   │   │   ├── _category_.json
    │   │   │   │   ├── calc-distance.md
    │   │   │   │   ├── get-x.md
    │   │   │   │   ├── get-y.md
    │   │   │   │   └── point.md
    │   │   │   ├── list-map
    │   │   │   │   ├── _category_.json
    │   │   │   │   ├── append.md
    │   │   │   │   ├── concat.md
    │   │   │   │   ├── dedup.md
    │   │   │   │   ├── entries.md
    │   │   │   │   ├── is-empty.md
    │   │   │   │   ├── keys.md
    │   │   │   │   ├── length.md
    │   │   │   │   ├── prepend.md
    │   │   │   │   ├── skip.md
    │   │   │   │   ├── slice.md
    │   │   │   │   ├── sort.md
    │   │   │   │   ├── splice.md
    │   │   │   │   ├── take.md
    │   │   │   │   └── values.md
    │   │   │   ├── math
    │   │   │   │   ├── _category_.json
    │   │   │   │   ├── abs.md
    │   │   │   │   ├── acos.md
    │   │   │   │   ├── asin.md
    │   │   │   │   ├── atan.md
    │   │   │   │   ├── ceil.md
    │   │   │   │   ├── cos.md
    │   │   │   │   ├── degrees.md
    │   │   │   │   ├── div.md
    │   │   │   │   ├── exp.md
    │   │   │   │   ├── floor.md
    │   │   │   │   ├── gcd.md
    │   │   │   │   ├── lcm.md
    │   │   │   │   ├── ln.md
    │   │   │   │   ├── log.md
    │   │   │   │   ├── log10.md
    │   │   │   │   ├── log2.md
    │   │   │   │   ├── mod.md
    │   │   │   │   ├── pi.md
    │   │   │   │   ├── power.md
    │   │   │   │   ├── radians.md
    │   │   │   │   ├── rand.md
    │   │   │   │   ├── round.md
    │   │   │   │   ├── sign.md
    │   │   │   │   ├── sin.md
    │   │   │   │   ├── sqrt.md
    │   │   │   │   └── tan.md
    │   │   │   ├── others
    │   │   │   │   ├── _category_.json
    │   │   │   │   ├── cast.md
    │   │   │   │   ├── coalesce.md
    │   │   │   │   ├── generate-uuid.md
    │   │   │   │   ├── greatest.md
    │   │   │   │   └── ifnull.md
    │   │   │   └── text
    │   │   │   │   ├── _category_.json
    │   │   │   │   ├── ascii.md
    │   │   │   │   ├── chr.md
    │   │   │   │   ├── concat-ws.md
    │   │   │   │   ├── concat.md
    │   │   │   │   ├── find-idx.md
    │   │   │   │   ├── initcap.md
    │   │   │   │   ├── left.md
    │   │   │   │   ├── lower.md
    │   │   │   │   ├── lpad.md
    │   │   │   │   ├── ltrim.md
    │   │   │   │   ├── md5.md
    │   │   │   │   ├── position.md
    │   │   │   │   ├── repeat.md
    │   │   │   │   ├── reverse.md
    │   │   │   │   ├── right.md
    │   │   │   │   ├── rpad.md
    │   │   │   │   ├── rtrim.md
    │   │   │   │   ├── substr.md
    │   │   │   │   ├── trim.md
    │   │   │   │   └── upper.md
    │   │   ├── intro.md
    │   │   └── statements
    │   │   │   ├── _category_.json
    │   │   │   ├── data-definition
    │   │   │       ├── _category_.json
    │   │   │       ├── alter-table.md
    │   │   │       ├── create-index.md
    │   │   │       ├── create-table.md
    │   │   │       ├── drop-index.md
    │   │   │       └── drop-table.md
    │   │   │   ├── data-manipulation
    │   │   │       ├── _category_.json
    │   │   │       ├── delete.md
    │   │   │       ├── insert.md
    │   │   │       └── update.md
    │   │   │   ├── metadata
    │   │   │       ├── _category_.json
    │   │   │       ├── data-dictionary.md
    │   │   │       └── show-tables.md
    │   │   │   ├── querying
    │   │   │       ├── _category_.json
    │   │   │       ├── aggregation.md
    │   │   │       ├── join.md
    │   │   │       ├── limit.md
    │   │   │       ├── schemaless.md
    │   │   │       └── where.md
    │   │   │   └── transaction.md
    │   └── storages
    │   │   ├── _category_.json
    │   │   ├── developing-custom-storages
    │   │       ├── _category_.json
    │   │       ├── intro.md
    │   │       ├── store-traits
    │   │       │   ├── _category_.json
    │   │       │   ├── alter-table.md
    │   │       │   ├── custom-function-mut.md
    │   │       │   ├── custom-function.md
    │   │       │   ├── index-mut.md
    │   │       │   ├── index-trait.md
    │   │       │   ├── metadata.md
    │   │       │   ├── store-mut.md
    │   │       │   ├── store.md
    │   │       │   └── transaction.md
    │   │       └── using-test-suite.md
    │   │   ├── intro.md
    │   │   └── supported-storages
    │   │       ├── _category_.json
    │   │       ├── composite-storage.md
    │   │       ├── csv-storage.md
    │   │       ├── file-storage.md
    │   │       ├── git-storage.md
    │   │       ├── idb-storage.md
    │   │       ├── json-storage.md
    │   │       ├── memory-storage.md
    │   │       ├── mongo-storage.md
    │   │       ├── parquet-storage.md
    │   │       ├── redb-storage.md
    │   │       ├── redis-storage.md
    │   │       ├── shared-memory-storage.md
    │   │       ├── sled-storage.md
    │   │       └── web-storage.md
    ├── docusaurus.config.js
    ├── package-lock.json
    ├── package.json
    ├── sidebars.js
    ├── src
    │   └── css
    │   │   └── custom.css
    └── static
    │   ├── .nojekyll
    │   └── img
    │       ├── favicon.ico
    │       ├── gluesql.jpg
    │       ├── undraw_docusaurus_mountain.svg
    │       ├── undraw_docusaurus_react.svg
    │       └── undraw_docusaurus_tree.svg
├── pkg
    ├── javascript
    │   ├── .gitignore
    │   ├── Cargo.toml
    │   ├── DEVELOPMENT.md
    │   ├── README.md
    │   ├── examples
    │   │   ├── nodejs
    │   │   │   └── main.js
    │   │   └── web
    │   │   │   ├── module
    │   │   │       ├── README.md
    │   │   │       └── index.html
    │   │   │   ├── rollup
    │   │   │       ├── index.html
    │   │   │       ├── main.js
    │   │   │       ├── package.json
    │   │   │       └── rollup.config.js
    │   │   │   └── webpack
    │   │   │       ├── index.html
    │   │   │       ├── main.js
    │   │   │       ├── package.json
    │   │   │       └── webpack.config.js
    │   ├── gluesql.js
    │   ├── gluesql.node.js
    │   ├── gluesql.rollup.js
    │   ├── package.json
    │   ├── src
    │   │   ├── lib.rs
    │   │   ├── payload.rs
    │   │   └── utils.rs
    │   ├── tests
    │   │   ├── composite_storage.rs
    │   │   ├── error.rs
    │   │   ├── join_multiple_storages.rs
    │   │   └── payload.rs
    │   └── webdriver.json
    ├── python
    │   ├── Cargo.toml
    │   ├── DEVELOPMENT.md
    │   ├── README.md
    │   ├── examples
    │   │   ├── json-storage
    │   │   │   ├── fixtures
    │   │   │   │   └── test.json
    │   │   │   └── main.py
    │   │   ├── memory-storage
    │   │   │   └── main.py
    │   │   ├── shared-memory-storage
    │   │   │   └── main.py
    │   │   └── sled-storage
    │   │   │   └── main.py
    │   ├── gluesql.pyi
    │   ├── pyproject.toml
    │   ├── pytest.ini
    │   ├── requirements.txt
    │   ├── src
    │   │   ├── error.rs
    │   │   ├── lib.rs
    │   │   ├── payload.rs
    │   │   └── storages.rs
    │   └── tests
    │   │   └── payload.py
    └── rust
    │   ├── Cargo.toml
    │   ├── examples
    │       ├── api_usage.rs
    │       ├── hello_ast_builder.rs
    │       ├── hello_world.rs
    │       ├── memory_storage_usage.rs
    │       ├── sled_multi_threaded.rs
    │       └── using_config.rs
    │   ├── src
    │       ├── lib.rs
    │       └── main.rs
    │   └── tests
    │       └── glue.rs
├── rust-toolchain.toml
├── storages
    ├── composite-storage
    │   ├── Cargo.toml
    │   ├── src
    │   │   ├── lib.rs
    │   │   ├── store.rs
    │   │   ├── store_mut.rs
    │   │   └── transaction.rs
    │   └── tests
    │   │   ├── basic.rs
    │   │   ├── composite_storage.rs
    │   │   ├── error.rs
    │   │   └── memory_and_sled.rs
    ├── csv-storage
    │   ├── Cargo.toml
    │   ├── src
    │   │   ├── error.rs
    │   │   ├── lib.rs
    │   │   ├── store.rs
    │   │   └── store_mut.rs
    │   └── tests
    │   │   ├── basic.rs
    │   │   ├── csv_storage.rs
    │   │   ├── error.rs
    │   │   ├── samples
    │   │       ├── Book.csv
    │   │       ├── Book.sql
    │   │       ├── City.csv
    │   │       ├── City.sql
    │   │       ├── Employee.csv
    │   │       ├── Grocery.csv
    │   │       ├── Grocery.types.csv
    │   │       ├── Student.csv
    │   │       ├── Student.sql
    │   │       ├── Student.types.csv
    │   │       └── WrongSchemaName.sql
    │   │   ├── schema.rs
    │   │   ├── schemaless.rs
    │   │   ├── schemaless_without_types.rs
    │   │   └── types_only.rs
    ├── file-storage
    │   ├── Cargo.toml
    │   ├── src
    │   │   ├── lib.rs
    │   │   ├── store.rs
    │   │   └── store_mut.rs
    │   └── tests
    │   │   └── file_storage.rs
    ├── git-storage
    │   ├── Cargo.toml
    │   ├── src
    │   │   ├── command_ext.rs
    │   │   ├── lib.rs
    │   │   ├── store.rs
    │   │   └── store_mut.rs
    │   └── tests
    │   │   ├── git_storage_csv.rs
    │   │   ├── git_storage_file.rs
    │   │   ├── git_storage_json.rs
    │   │   └── pull_and_push.rs
    ├── idb-storage
    │   ├── Cargo.toml
    │   ├── README.md
    │   ├── src
    │   │   ├── convert.rs
    │   │   ├── error.rs
    │   │   └── lib.rs
    │   └── tests
    │   │   ├── convert.rs
    │   │   └── idb_storage.rs
    ├── json-storage
    │   ├── Cargo.toml
    │   ├── src
    │   │   ├── alter_table.rs
    │   │   ├── error.rs
    │   │   ├── function.rs
    │   │   ├── index.rs
    │   │   ├── lib.rs
    │   │   ├── store.rs
    │   │   ├── store_mut.rs
    │   │   └── transaction.rs
    │   └── tests
    │   │   ├── error.rs
    │   │   ├── json_dml.rs
    │   │   ├── json_storage.rs
    │   │   ├── primary_key.rs
    │   │   ├── samples
    │   │       ├── ArrayOfJsonsSchema.json
    │   │       ├── ArrayOfJsonsSchema.sql
    │   │       ├── ArrayOfJsonsSchemaless.json
    │   │       ├── Duplicated.json
    │   │       ├── Duplicated.jsonl
    │   │       ├── JsonArrayTypeRequired.json
    │   │       ├── JsonObjectTypeRequired.json
    │   │       ├── Schema.jsonl
    │   │       ├── Schema.sql
    │   │       ├── Schemaless.jsonl
    │   │       ├── SingleJsonSchema.json
    │   │       ├── SingleJsonSchema.sql
    │   │       ├── SingleJsonSchemaless.json
    │   │       ├── WrongFormatJson.json
    │   │       ├── WrongFormatJsonl.jsonl
    │   │       ├── WrongSchema.jsonl
    │   │       ├── WrongSchema.sql
    │   │       ├── WrongTableName.jsonl
    │   │       └── WrongTableName.sql
    │   │   ├── schema.rs
    │   │   └── schemaless.rs
    ├── memory-storage
    │   ├── Cargo.toml
    │   ├── src
    │   │   ├── alter_table.rs
    │   │   ├── index.rs
    │   │   ├── lib.rs
    │   │   ├── metadata.rs
    │   │   └── transaction.rs
    │   └── tests
    │   │   └── memory_storage.rs
    ├── mongo-storage
    │   ├── Cargo.toml
    │   ├── README.md
    │   ├── src
    │   │   ├── description.rs
    │   │   ├── error.rs
    │   │   ├── lib.rs
    │   │   ├── row.rs
    │   │   ├── row
    │   │   │   ├── data_type.rs
    │   │   │   ├── key.rs
    │   │   │   └── value.rs
    │   │   ├── store.rs
    │   │   ├── store_mut.rs
    │   │   └── utils.rs
    │   └── tests
    │   │   ├── mongo_indexes.rs
    │   │   ├── mongo_schemaless.rs
    │   │   ├── mongo_storage.rs
    │   │   └── mongo_types.rs
    ├── parquet-storage
    │   ├── Cargo.toml
    │   ├── src
    │   │   ├── alter_table.rs
    │   │   ├── column_def.rs
    │   │   ├── error.rs
    │   │   ├── function.rs
    │   │   ├── index.rs
    │   │   ├── lib.rs
    │   │   ├── store.rs
    │   │   ├── store_mut.rs
    │   │   ├── transaction.rs
    │   │   └── value.rs
    │   └── tests
    │   │   ├── error.rs
    │   │   ├── parquet_storage.rs
    │   │   ├── samples
    │   │       ├── all_types_with_nulls.parquet
    │   │       ├── alltypes_dictionary.parquet
    │   │       ├── alltypes_plain.parquet
    │   │       ├── alltypes_plain_snappy.parquet
    │   │       ├── nested_lists_snappy.parquet
    │   │       └── nested_maps_snappy.parquet
    │   │   └── schema.rs
    ├── redb-storage
    │   ├── Cargo.toml
    │   ├── src
    │   │   ├── core.rs
    │   │   ├── error.rs
    │   │   └── lib.rs
    │   └── tests
    │   │   ├── begin_write_error.rs
    │   │   ├── nested_transaction.rs
    │   │   ├── redb_storage.rs
    │   │   ├── reserved_table_name.rs
    │   │   └── storage_interface_error.rs
    ├── redis-storage
    │   ├── Cargo.toml
    │   ├── src
    │   │   ├── alter_table.rs
    │   │   ├── index.rs
    │   │   ├── lib.rs
    │   │   ├── metadata.rs
    │   │   └── transaction.rs
    │   └── tests
    │   │   ├── redis-storage.toml
    │   │   ├── redis_errors.rs
    │   │   ├── redis_reconnect.rs
    │   │   ├── redis_store.rs
    │   │   └── redis_table.rs
    ├── shared-memory-storage
    │   ├── Cargo.toml
    │   ├── src
    │   │   ├── alter_table.rs
    │   │   ├── index.rs
    │   │   ├── lib.rs
    │   │   └── transaction.rs
    │   └── tests
    │   │   ├── concurrent_access.rs
    │   │   └── shared_memory_storage.rs
    ├── sled-storage
    │   ├── Cargo.toml
    │   ├── benches
    │   │   └── sled_benchmark.rs
    │   ├── src
    │   │   ├── alter_table.rs
    │   │   ├── error.rs
    │   │   ├── gc.rs
    │   │   ├── index.rs
    │   │   ├── index_mut.rs
    │   │   ├── index_sync.rs
    │   │   ├── key.rs
    │   │   ├── lib.rs
    │   │   ├── lock.rs
    │   │   ├── snapshot.rs
    │   │   ├── store.rs
    │   │   ├── store_mut.rs
    │   │   └── transaction.rs
    │   └── tests
    │   │   ├── export_and_import.rs
    │   │   ├── sled_storage.rs
    │   │   └── sled_transaction.rs
    └── web-storage
    │   ├── Cargo.toml
    │   ├── README.md
    │   ├── src
    │       └── lib.rs
    │   └── tests
    │       ├── local_storage.rs
    │       └── session_storage.rs
├── test-suite
    ├── Cargo.toml
    └── src
    │   ├── aggregate.rs
    │   ├── aggregate
    │       ├── avg.rs
    │       ├── count.rs
    │       ├── error.rs
    │       ├── group_by.rs
    │       ├── max.rs
    │       ├── min.rs
    │       ├── stdev.rs
    │       ├── sum.rs
    │       └── variance.rs
    │   ├── alter.rs
    │   ├── alter
    │       ├── alter_table.rs
    │       ├── create_table.rs
    │       ├── drop_indexed.rs
    │       └── drop_table.rs
    │   ├── arithmetic.rs
    │   ├── arithmetic
    │       ├── error.rs
    │       ├── on_where.rs
    │       └── project.rs
    │   ├── array.rs
    │   ├── ast_builder.rs
    │   ├── ast_builder
    │       ├── alias_as.rs
    │       ├── basic.rs
    │       ├── delete.rs
    │       ├── expr.rs
    │       ├── expr
    │       │   └── pattern_matching.rs
    │       ├── function.rs
    │       ├── function
    │       │   ├── datetime.rs
    │       │   ├── datetime
    │       │   │   ├── conversion.rs
    │       │   │   ├── current_date_and_time.rs
    │       │   │   └── formatting.rs
    │       │   ├── math.rs
    │       │   ├── math
    │       │   │   ├── basic_arithmetic.rs
    │       │   │   ├── conversion.rs
    │       │   │   └── rounding.rs
    │       │   ├── other.rs
    │       │   ├── other
    │       │   │   ├── coalesce.rs
    │       │   │   └── ifnull.rs
    │       │   ├── text.rs
    │       │   └── text
    │       │   │   ├── case_conversion.rs
    │       │   │   ├── character_conversion.rs
    │       │   │   ├── padding.rs
    │       │   │   ├── position_and_indexing.rs
    │       │   │   └── trimming.rs
    │       ├── index_by.rs
    │       ├── insert.rs
    │       ├── schemaless.rs
    │       ├── schemaless
    │       │   └── basic.rs
    │       ├── select.rs
    │       ├── statements.rs
    │       ├── statements
    │       │   ├── querying.rs
    │       │   └── querying
    │       │   │   ├── data_aggregation.rs
    │       │   │   └── data_selection_and_projection.rs
    │       ├── update.rs
    │       └── values.rs
    │   ├── basic.rs
    │   ├── bitwise_and.rs
    │   ├── bitwise_shift_left.rs
    │   ├── bitwise_shift_right.rs
    │   ├── case.rs
    │   ├── column_alias.rs
    │   ├── concat.rs
    │   ├── custom_function.rs
    │   ├── data_type.rs
    │   ├── data_type
    │       ├── bytea.rs
    │       ├── date.rs
    │       ├── decimal.rs
    │       ├── float32.rs
    │       ├── inet.rs
    │       ├── int128.rs
    │       ├── int16.rs
    │       ├── int32.rs
    │       ├── int64.rs
    │       ├── int8.rs
    │       ├── interval.rs
    │       ├── list.rs
    │       ├── map.rs
    │       ├── point.rs
    │       ├── sql_types.rs
    │       ├── time.rs
    │       ├── timestamp.rs
    │       ├── uint128.rs
    │       ├── uint16.rs
    │       ├── uint32.rs
    │       ├── uint64.rs
    │       ├── uint8.rs
    │       └── uuid.rs
    │   ├── default.rs
    │   ├── delete.rs
    │   ├── dictionary.rs
    │   ├── dictionary_index.rs
    │   ├── filter.rs
    │   ├── foreign_key.rs
    │   ├── function.rs
    │   ├── function
    │       ├── abs.rs
    │       ├── add_month.rs
    │       ├── append.rs
    │       ├── ascii.rs
    │       ├── cast.rs
    │       ├── ceil.rs
    │       ├── chr.rs
    │       ├── coalesce.rs
    │       ├── concat.rs
    │       ├── concat_ws.rs
    │       ├── dedup.rs
    │       ├── degrees.rs
    │       ├── div_mod.rs
    │       ├── entries.rs
    │       ├── exp_log.rs
    │       ├── extract.rs
    │       ├── find_idx.rs
    │       ├── floor.rs
    │       ├── format.rs
    │       ├── gcd_lcm.rs
    │       ├── generate_uuid.rs
    │       ├── geometry.rs
    │       ├── geometry
    │       │   ├── calc_distance.rs
    │       │   ├── get_x.rs
    │       │   └── get_y.rs
    │       ├── greatest.rs
    │       ├── ifnull.rs
    │       ├── initcap.rs
    │       ├── is_empty.rs
    │       ├── keys.rs
    │       ├── last_day.rs
    │       ├── left_right.rs
    │       ├── length.rs
    │       ├── lpad_rpad.rs
    │       ├── ltrim_rtrim.rs
    │       ├── math_function.rs
    │       ├── md5.rs
    │       ├── now.rs
    │       ├── pi.rs
    │       ├── position.rs
    │       ├── prepend.rs
    │       ├── radians.rs
    │       ├── rand.rs
    │       ├── repeat.rs
    │       ├── replace.rs
    │       ├── reverse.rs
    │       ├── round.rs
    │       ├── sign.rs
    │       ├── skip.rs
    │       ├── slice.rs
    │       ├── sort.rs
    │       ├── splice.rs
    │       ├── sqrt_power.rs
    │       ├── substr.rs
    │       ├── take.rs
    │       ├── to_date.rs
    │       ├── trim.rs
    │       ├── upper_lower.rs
    │       └── values.rs
    │   ├── index.rs
    │   ├── index
    │       ├── and.rs
    │       ├── basic.rs
    │       ├── expr.rs
    │       ├── nested.rs
    │       ├── null.rs
    │       ├── order_by.rs
    │       ├── showindexes.rs
    │       └── value.rs
    │   ├── inline_view.rs
    │   ├── insert.rs
    │   ├── join.rs
    │   ├── lib.rs
    │   ├── like_ilike.rs
    │   ├── limit.rs
    │   ├── metadata.rs
    │   ├── metadata
    │       ├── index.rs
    │       └── table.rs
    │   ├── migrate.rs
    │   ├── nested_select.rs
    │   ├── nullable.rs
    │   ├── order_by.rs
    │   ├── ordering.rs
    │   ├── primary_key.rs
    │   ├── project.rs
    │   ├── schemaless.rs
    │   ├── schemaless
    │       ├── basic.rs
    │       └── error.rs
    │   ├── series.rs
    │   ├── show_columns.rs
    │   ├── store.rs
    │   ├── store
    │       └── insert_schema.rs
    │   ├── synthesize.rs
    │   ├── tester.rs
    │   ├── tester
    │       └── macros.rs
    │   ├── transaction.rs
    │   ├── transaction
    │       ├── alter_table.rs
    │       ├── ast_builder.rs
    │       ├── basic.rs
    │       ├── dictionary.rs
    │       ├── index.rs
    │       └── table.rs
    │   ├── type_match.rs
    │   ├── unary_operator.rs
    │   ├── update.rs
    │   ├── validate.rs
    │   ├── validate
    │       ├── types.rs
    │       └── unique.rs
    │   └── values.rs
└── utils
    ├── Cargo.toml
    └── src
        ├── hashmap.rs
        ├── indexmap.rs
        ├── lib.rs
        ├── or_stream.rs
        └── vector.rs


/.github/auto-author-assign-config.yml:
--------------------------------------------------------------------------------
 1 | addAssignees: author
 2 | 
 3 | addReviewers: true
 4 | reviewers:
 5 |   - panarch
 6 |   - ever0de
 7 |   - devgony
 8 |   - zmrdltl
 9 | numberOfReviewers: 0
10 | 
11 | runOnDraft: true
12 | 


--------------------------------------------------------------------------------
/.github/release-drafter-config.yml:
--------------------------------------------------------------------------------
 1 | name-template: "v$RESOLVED_VERSION 🌈"
 2 | tag-template: "v$RESOLVED_VERSION"
 3 | categories:
 4 |   - title: "🚀 Features"
 5 |     labels: "enhancement"
 6 |   - title: "🐛 Bug Fixes"
 7 |     labels: "bug"
 8 | change-template: "- $TITLE @$AUTHOR (#$NUMBER)"
 9 | change-title-escapes: '\<*_&'
10 | version-resolver:
11 |   major:
12 |     labels:
13 |       - "major"
14 |   minor:
15 |     labels:
16 |       - "minor"
17 |   patch:
18 |     labels:
19 |       - "patch"
20 |   default: patch
21 | template: |
22 |   ## Changes
23 | 
24 |   $CHANGES
25 | 


--------------------------------------------------------------------------------
/.github/workflows/auto-author-assign.yml:
--------------------------------------------------------------------------------
 1 | name: auto-author-assign
 2 | 
 3 | on:
 4 |   pull_request_target:
 5 |     types: [opened]
 6 | 
 7 | permissions:
 8 |   pull-requests: write
 9 | 
10 | jobs:
11 |   assign-author:
12 |     runs-on: ubuntu-latest
13 |     steps:
14 |       - uses: kentaro-m/auto-assign-action@v1.2.5
15 |         with:
16 |           configuration-path: '.github/auto-author-assign-config.yml'
17 | 


--------------------------------------------------------------------------------
/.github/workflows/python.yml:
--------------------------------------------------------------------------------
 1 | name: Python
 2 | 
 3 | on:
 4 |   push:
 5 |     branches: [main, release-*]
 6 |     paths-ignore:
 7 |       - 'docs/**'
 8 |   pull_request:
 9 |     paths-ignore:
10 |       - 'docs/**'
11 | 
12 | env:
13 |   CARGO_TERM_COLOR: always
14 | 
15 | jobs:
16 |   python_storage_tests:
17 |     runs-on: ubuntu-latest
18 |     steps:
19 |       - uses: actions/checkout@v4
20 |       - name: Set up Python
21 |         uses: actions/setup-python@v5
22 |         with:
23 |           python-version: "3.11.4"
24 | 
25 |       - name: Set up pip and requirements
26 |         run: |
27 |           cd pkg/python
28 |           python -m pip install --upgrade pip
29 |           pip install -r requirements.txt
30 | 
31 |       - name: Set up Rust toolchain
32 |         uses: actions-rs/toolchain@v1
33 |         with:
34 |           toolchain: stable
35 |           override: true
36 | 
37 |       - name: Build and Test
38 |         run: |
39 |           cd pkg/python
40 |           maturin build
41 |           pip install .
42 |           pytest
43 | 


--------------------------------------------------------------------------------
/.github/workflows/release-drafter.yml:
--------------------------------------------------------------------------------
 1 | name: Release Drafter
 2 | 
 3 | on:
 4 |   push:
 5 |     branches:
 6 |       - main
 7 | 
 8 | jobs:
 9 |   update_release_draft:
10 |     runs-on: ubuntu-latest
11 |     steps:
12 |       - uses: release-drafter/release-drafter@v5
13 |         with:
14 |           config-name: release-drafter-config.yml
15 |         env:
16 |           GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
17 | 


--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
 1 | # GlueSQL project files
 2 | /data/
 3 | /pkg/rust/data/
 4 | /cli/tmp/
 5 | /storages/**/data/
 6 | /storages/**/tmp/
 7 | /reports/
 8 | /docs/gluesql.github.io
 9 | 
10 | # Vim
11 | *.swp
12 | 
13 | # Visual Studio Code
14 | .vscode
15 | 
16 | # Clion
17 | .idea
18 | 
19 | # Mac-OS
20 | .DS_Store
21 | .AppleDouble
22 | .LSOverride
23 | 
24 | # Generated by Cargo
25 | # will have compiled files and executables
26 | /target/
27 | /**/target/
28 | 
29 | # These are backup files generated by rustfmt
30 | **/*.rs.bk
31 | 
32 | __pycache__
33 | 


--------------------------------------------------------------------------------
/AGENTS.md:
--------------------------------------------------------------------------------
1 | # AGENTS.md
2 | - After editing code, run `cargo clippy --all-targets -- -D warnings`.
3 | - Then run `cargo fmt --all`.
4 | - Commit only when both commands succeed.
5 | 


--------------------------------------------------------------------------------
/cli/Cargo.toml:
--------------------------------------------------------------------------------
 1 | [package]
 2 | name = "gluesql-cli"
 3 | authors = ["Taehoon Moon <taehoon.moon@outlook.com>"]
 4 | version.workspace = true
 5 | edition.workspace = true
 6 | description.workspace = true
 7 | license.workspace = true
 8 | repository.workspace = true
 9 | documentation.workspace = true
10 | 
11 | [dependencies]
12 | gluesql-core.workspace = true
13 | gluesql_sled_storage.workspace = true
14 | gluesql_memory_storage.workspace = true
15 | gluesql-json-storage.workspace = true
16 | gluesql-csv-storage.workspace = true
17 | gluesql-parquet-storage.workspace = true
18 | gluesql-file-storage.workspace = true
19 | gluesql-redb-storage.workspace = true
20 | 
21 | clap = { version = "3.2.2", features = ["derive"] }
22 | rustyline = "9.1"
23 | rustyline-derive = "0.6"
24 | tabled = "0.8"
25 | thiserror = "1.0"
26 | edit = "0.1.4"
27 | futures = "0.3"
28 | anyhow = "1.0"
29 | strum_macros = "0.25"
30 | 
31 | [dev-dependencies]
32 | tokio = { version = "1", features = ["rt", "macros"] }
33 | 


--------------------------------------------------------------------------------
/cli/src/helper.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     rustyline::{
 3 |         Result,
 4 |         validate::{ValidationContext, ValidationResult, Validator},
 5 |     },
 6 |     rustyline_derive::{Completer, Helper, Highlighter, Hinter},
 7 | };
 8 | 
 9 | #[derive(Completer, Helper, Highlighter, Hinter)]
10 | pub struct CliHelper;
11 | 
12 | impl Validator for CliHelper {
13 |     fn validate(&self, ctx: &mut ValidationContext<'_>) -> Result<ValidationResult> {
14 |         let input = ctx.input().trim();
15 | 
16 |         if input.ends_with(';') || input.starts_with('.') {
17 |             Ok(ValidationResult::Valid(None))
18 |         } else {
19 |             Ok(ValidationResult::Incomplete)
20 |         }
21 |     }
22 | }
23 | 


--------------------------------------------------------------------------------
/cli/src/main.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 |     gluesql_cli::run().unwrap();
3 | }
4 | 


--------------------------------------------------------------------------------
/core/Cargo.toml:
--------------------------------------------------------------------------------
 1 | [package]
 2 | name = "gluesql-core"
 3 | authors = ["Taehoon Moon <taehoon.moon@outlook.com>"]
 4 | version.workspace = true
 5 | edition.workspace = true
 6 | description.workspace = true
 7 | license.workspace = true
 8 | repository.workspace = true
 9 | documentation.workspace = true
10 | 
11 | [dependencies]
12 | utils.workspace = true
13 | 
14 | regex = "1"
15 | async-trait = "0.1"
16 | async-recursion = "1"
17 | cfg-if = "1"
18 | futures-enum = "0.1.17"
19 | futures = "0.3"
20 | chrono = { version = "0.4.38", features = ["serde", "wasmbind"] }
21 | rust_decimal = { version = "1", features = ["serde-str"] }
22 | im-rc = "15"
23 | iter-enum = "1"
24 | itertools = "0.12"
25 | serde = { version = "1", features = ["derive"] }
26 | serde_json = "1"
27 | sqlparser = { version = "0.52", features = ["serde", "bigdecimal"] }
28 | thiserror = "1.0"
29 | strum_macros = "0.25"
30 | bigdecimal = { version = "0.4.1", features = ["serde", "string-only"] }
31 | hex = "0.4"
32 | rand = "0.8"
33 | ordered-float = { version = "4", features = ["serde"] }
34 | md-5 = "0.10.5"
35 | 
36 | [target.'cfg(target_arch = "wasm32")'.dependencies.uuid]
37 | version = "1"
38 | features = ["v4", "js"]
39 | [target.'cfg(not(target_arch = "wasm32"))'.dependencies.uuid]
40 | version = "1"
41 | features = ["v4"]
42 | 
43 | [dev-dependencies]
44 | pretty_assertions = "1"
45 | 


--------------------------------------------------------------------------------
/core/src/ast/data_type.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     serde::{Deserialize, Serialize},
 3 |     strum_macros::Display,
 4 | };
 5 | 
 6 | #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Display)]
 7 | #[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
 8 | pub enum DataType {
 9 |     Boolean,
10 |     Int8,
11 |     Int16,
12 |     Int32,
13 |     Int,
14 |     Int128,
15 |     Uint8,
16 |     Uint16,
17 |     Uint32,
18 |     Uint64,
19 |     Uint128,
20 |     Float32,
21 |     Float,
22 |     Text,
23 |     Bytea,
24 |     Inet,
25 |     Date,
26 |     Timestamp,
27 |     Time,
28 |     Interval,
29 |     Uuid,
30 |     Map,
31 |     List,
32 |     Decimal,
33 |     Point,
34 | }
35 | 


--------------------------------------------------------------------------------
/core/src/ast_builder/build.rs:
--------------------------------------------------------------------------------
1 | use crate::{ast::Statement, result::Result};
2 | 
3 | pub trait Build {
4 |     fn build(self) -> Result<Statement>;
5 | }
6 | 


--------------------------------------------------------------------------------
/core/src/ast_builder/column_def.rs:
--------------------------------------------------------------------------------
 1 | use crate::{
 2 |     ast::ColumnDef,
 3 |     parse_sql::parse_column_def,
 4 |     result::{Error, Result},
 5 |     translate::translate_column_def,
 6 | };
 7 | 
 8 | #[derive(Clone, Debug)]
 9 | pub enum ColumnDefNode {
10 |     Text(String),
11 | }
12 | 
13 | impl From<&str> for ColumnDefNode {
14 |     fn from(column_def: &str) -> Self {
15 |         ColumnDefNode::Text(column_def.to_owned())
16 |     }
17 | }
18 | 
19 | impl TryFrom<ColumnDefNode> for ColumnDef {
20 |     type Error = Error;
21 | 
22 |     fn try_from(column_def_node: ColumnDefNode) -> Result<ColumnDef> {
23 |         match column_def_node {
24 |             ColumnDefNode::Text(column_def) => parse_column_def(column_def)
25 |                 .and_then(|column_def| translate_column_def(&column_def)),
26 |         }
27 |     }
28 | }
29 | 


--------------------------------------------------------------------------------
/core/src/ast_builder/column_list.rs:
--------------------------------------------------------------------------------
 1 | use crate::{
 2 |     parse_sql::parse_identifiers,
 3 |     result::{Error, Result},
 4 |     translate::translate_idents,
 5 | };
 6 | 
 7 | #[derive(Clone, Debug)]
 8 | pub enum ColumnList {
 9 |     Text(String),
10 |     Columns(Vec<String>),
11 | }
12 | 
13 | impl From<&str> for ColumnList {
14 |     fn from(columns: &str) -> Self {
15 |         ColumnList::Text(columns.to_owned())
16 |     }
17 | }
18 | 
19 | impl From<Vec<&str>> for ColumnList {
20 |     fn from(columns: Vec<&str>) -> Self {
21 |         ColumnList::Columns(columns.into_iter().map(ToOwned::to_owned).collect())
22 |     }
23 | }
24 | 
25 | impl TryFrom<ColumnList> for Vec<String> {
26 |     type Error = Error;
27 | 
28 |     fn try_from(column_list: ColumnList) -> Result<Self> {
29 |         match column_list {
30 |             ColumnList::Text(columns) => {
31 |                 let idents = parse_identifiers(columns)?;
32 |                 Ok(translate_idents(idents.as_slice()))
33 |             }
34 |             ColumnList::Columns(columns) => Ok(columns),
35 |         }
36 |     }
37 | }
38 | 


--------------------------------------------------------------------------------
/core/src/ast_builder/data_type.rs:
--------------------------------------------------------------------------------
 1 | use crate::{
 2 |     ast::DataType,
 3 |     parse_sql::parse_data_type,
 4 |     result::{Error, Result},
 5 |     translate::translate_data_type,
 6 | };
 7 | 
 8 | #[derive(Clone, Debug)]
 9 | pub enum DataTypeNode {
10 |     DataType(DataType),
11 |     Text(String),
12 | }
13 | 
14 | impl From<DataType> for DataTypeNode {
15 |     fn from(data_type: DataType) -> Self {
16 |         Self::DataType(data_type)
17 |     }
18 | }
19 | 
20 | impl From<&str> for DataTypeNode {
21 |     fn from(data_type: &str) -> Self {
22 |         Self::Text(data_type.to_owned())
23 |     }
24 | }
25 | 
26 | impl TryFrom<DataTypeNode> for DataType {
27 |     type Error = Error;
28 | 
29 |     fn try_from(data_type: DataTypeNode) -> Result<Self> {
30 |         match data_type {
31 |             DataTypeNode::DataType(data_type) => Ok(data_type),
32 |             DataTypeNode::Text(data_type) => {
33 |                 parse_data_type(data_type).and_then(|datatype| translate_data_type(&datatype))
34 |             }
35 |         }
36 |     }
37 | }
38 | 


--------------------------------------------------------------------------------
/core/src/ast_builder/error.rs:
--------------------------------------------------------------------------------
1 | use {serde::Serialize, std::fmt::Debug, thiserror::Error};
2 | 
3 | #[derive(Error, Serialize, Debug, PartialEq, Eq)]
4 | pub enum AstBuilderError {
5 |     #[error("failed to parse numeric value: {0}")]
6 |     FailedToParseNumeric(String),
7 | }
8 | 


--------------------------------------------------------------------------------
/core/src/ast_builder/execute.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     super::Build,
 3 |     crate::{
 4 |         ast::Statement,
 5 |         executor::Payload,
 6 |         prelude::Glue,
 7 |         result::Result,
 8 |         store::{GStore, GStoreMut},
 9 |     },
10 |     async_trait::async_trait,
11 | };
12 | 
13 | #[async_trait(?Send)]
14 | pub trait Execute<T: GStore + GStoreMut>
15 | where
16 |     Self: Sized + Build,
17 | {
18 |     async fn execute(self, glue: &mut Glue<T>) -> Result<Payload> {
19 |         let statement = self.build()?;
20 | 
21 |         glue.execute_stmt(&statement).await
22 |     }
23 | }
24 | 
25 | #[async_trait(?Send)]
26 | impl<T: GStore + GStoreMut, B: Build> Execute<T> for B {}
27 | 
28 | impl Build for Statement {
29 |     fn build(self) -> Result<Statement> {
30 |         Ok(self)
31 |     }
32 | }
33 | 


--------------------------------------------------------------------------------
/core/src/ast_builder/expr/alias_as.rs:
--------------------------------------------------------------------------------
 1 | use {super::ExprNode, crate::ast_builder::ExprWithAliasNode};
 2 | 
 3 | impl<'a> ExprNode<'a> {
 4 |     pub fn alias_as(self, alias: &str) -> ExprWithAliasNode<'a> {
 5 |         ExprWithAliasNode {
 6 |             expr: self,
 7 |             alias: alias.to_owned(),
 8 |         }
 9 |     }
10 | }
11 | 


--------------------------------------------------------------------------------
/core/src/ast_builder/expr/is_null.rs:
--------------------------------------------------------------------------------
 1 | use super::ExprNode;
 2 | 
 3 | impl<'a> ExprNode<'a> {
 4 |     pub fn is_null(self) -> Self {
 5 |         Self::IsNull(Box::new(self))
 6 |     }
 7 | 
 8 |     pub fn is_not_null(self) -> Self {
 9 |         Self::IsNotNull(Box::new(self))
10 |     }
11 | }
12 | 
13 | #[cfg(test)]
14 | mod tests {
15 |     use crate::ast_builder::{col, num, test_expr};
16 | 
17 |     #[test]
18 |     fn is_null() {
19 |         let actual = col("id").is_null();
20 |         let expected = "id IS NULL";
21 |         test_expr(actual, expected);
22 | 
23 |         let actual = num(10).add("id").is_not_null();
24 |         let expected = "10 + id IS NOT NULL";
25 |         test_expr(actual, expected);
26 |     }
27 | }
28 | 


--------------------------------------------------------------------------------
/core/src/ast_builder/expr/nested.rs:
--------------------------------------------------------------------------------
 1 | use super::ExprNode;
 2 | 
 3 | impl<'a> ExprNode<'a> {
 4 |     pub fn nested(self) -> Self {
 5 |         nested(self)
 6 |     }
 7 | }
 8 | 
 9 | pub fn nested<'a, T: Into<ExprNode<'a>>>(expr: T) -> ExprNode<'a> {
10 |     ExprNode::Nested(Box::new(expr.into()))
11 | }
12 | 
13 | #[cfg(test)]
14 | mod tests {
15 |     use crate::ast_builder::{col, nested, test_expr};
16 | 
17 |     #[test]
18 |     fn test_nested() {
19 |         let actual = col("val1").add(col("val2")).nested();
20 |         let expected = "(val1 + val2)";
21 |         test_expr(actual, expected);
22 | 
23 |         let actual = nested(col("val1").add(col("val2")));
24 |         let expected = "(val1 + val2)";
25 |         test_expr(actual, expected);
26 |     }
27 | }
28 | 


--------------------------------------------------------------------------------
/core/src/ast_builder/expr_with_alias.rs:
--------------------------------------------------------------------------------
 1 | use crate::{
 2 |     ast::Expr,
 3 |     ast_builder::ExprNode,
 4 |     result::{Error, Result},
 5 | };
 6 | 
 7 | #[derive(Clone, Debug)]
 8 | pub struct ExprWithAliasNode<'a> {
 9 |     pub expr: ExprNode<'a>,
10 |     pub alias: String,
11 | }
12 | 
13 | impl<'a> TryFrom<ExprWithAliasNode<'a>> for (Expr, String) {
14 |     type Error = Error;
15 | 
16 |     fn try_from(node: ExprWithAliasNode<'a>) -> Result<Self> {
17 |         Ok((Expr::try_from(node.expr)?, node.alias))
18 |     }
19 | }
20 | 


--------------------------------------------------------------------------------
/core/src/ast_builder/index_item/primary_key.rs:
--------------------------------------------------------------------------------
 1 | use {super::IndexItemNode, crate::ast_builder::ExprNode};
 2 | 
 3 | #[derive(Clone, Debug)]
 4 | pub struct PrimaryKeyNode;
 5 | 
 6 | impl<'a> PrimaryKeyNode {
 7 |     pub fn eq<T: Into<ExprNode<'a>>>(self, expr: T) -> IndexItemNode<'a> {
 8 |         IndexItemNode::PrimaryKey(expr.into())
 9 |     }
10 | }
11 | 
12 | /// Entry point function to Primary Key
13 | pub fn primary_key() -> PrimaryKeyNode {
14 |     PrimaryKeyNode
15 | }
16 | 
17 | #[cfg(test)]
18 | mod tests {
19 |     use crate::{
20 |         ast::{AstLiteral, Expr},
21 |         ast_builder::{index_item::IndexItem, primary_key, select::Prebuild},
22 |     };
23 | 
24 |     #[test]
25 |     fn test() {
26 |         let actual = primary_key().eq("1").prebuild().unwrap();
27 |         let expected = IndexItem::PrimaryKey(Expr::Literal(AstLiteral::Number(1.into())));
28 |         assert_eq!(actual, expected);
29 |     }
30 | }
31 | 


--------------------------------------------------------------------------------
/core/src/ast_builder/select/join.rs:
--------------------------------------------------------------------------------
 1 | mod hash_join;
 2 | mod join_constraint;
 3 | mod root;
 4 | 
 5 | pub use {hash_join::HashJoinNode, join_constraint::JoinConstraintNode, root::JoinNode};
 6 | 
 7 | use crate::ast::{JoinConstraint, JoinExecutor, JoinOperator, Select, TableFactor};
 8 | 
 9 | #[derive(Clone, Copy, Debug)]
10 | pub enum JoinOperatorType {
11 |     Inner,
12 |     Left,
13 | }
14 | 
15 | impl From<JoinOperatorType> for JoinOperator {
16 |     fn from(join_operator_type: JoinOperatorType) -> Self {
17 |         match join_operator_type {
18 |             JoinOperatorType::Inner => JoinOperator::Inner(JoinConstraint::None),
19 |             JoinOperatorType::Left => JoinOperator::LeftOuter(JoinConstraint::None),
20 |         }
21 |     }
22 | }
23 | 
24 | pub struct JoinConstraintData {
25 |     select: Select,
26 |     relation: TableFactor,
27 |     operator_type: JoinOperatorType,
28 |     executor: JoinExecutor,
29 | }
30 | 


--------------------------------------------------------------------------------
/core/src/ast_builder/show_columns.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     super::Build,
 3 |     crate::{ast::Statement, result::Result},
 4 | };
 5 | 
 6 | #[derive(Clone, Debug)]
 7 | pub struct ShowColumnsNode {
 8 |     table_name: String,
 9 | }
10 | 
11 | impl ShowColumnsNode {
12 |     pub fn new(table_name: String) -> Self {
13 |         Self { table_name }
14 |     }
15 | }
16 | 
17 | impl Build for ShowColumnsNode {
18 |     fn build(self) -> Result<Statement> {
19 |         let table_name = self.table_name;
20 |         Ok(Statement::ShowColumns { table_name })
21 |     }
22 | }
23 | 
24 | #[cfg(test)]
25 | mod tests {
26 |     use crate::ast_builder::{Build, table, test};
27 | 
28 |     #[test]
29 |     fn show_columns() {
30 |         let actual = table("Foo").show_columns().build();
31 |         let expected = "SHOW COLUMNS FROM Foo";
32 |         test(actual, expected);
33 |     }
34 | }
35 | 


--------------------------------------------------------------------------------
/core/src/ast_builder/transaction.rs:
--------------------------------------------------------------------------------
 1 | use crate::ast::Statement;
 2 | 
 3 | pub fn begin() -> Statement {
 4 |     Statement::StartTransaction
 5 | }
 6 | pub fn commit() -> Statement {
 7 |     Statement::Commit
 8 | }
 9 | pub fn rollback() -> Statement {
10 |     Statement::Rollback
11 | }
12 | 
13 | #[cfg(test)]
14 | mod tests {
15 |     use crate::ast_builder::{begin, commit, rollback, test};
16 | 
17 |     #[test]
18 |     fn transaction() {
19 |         let actual = begin();
20 |         let expected = "START TRANSACTION";
21 |         test(Ok(actual), expected);
22 | 
23 |         let actual = commit();
24 |         let expected = "COMMIT";
25 |         test(Ok(actual), expected);
26 | 
27 |         let actual = rollback();
28 |         let expected = "ROLLBACK";
29 |         test(Ok(actual), expected);
30 |     }
31 | }
32 | 


--------------------------------------------------------------------------------
/core/src/data.rs:
--------------------------------------------------------------------------------
 1 | mod bigdecimal_ext;
 2 | mod function;
 3 | mod interval;
 4 | mod key;
 5 | mod literal;
 6 | mod point;
 7 | mod row;
 8 | mod string_ext;
 9 | mod table;
10 | 
11 | pub mod schema;
12 | pub mod value;
13 | 
14 | pub use {
15 |     bigdecimal_ext::BigDecimalExt,
16 |     function::CustomFunction,
17 |     interval::{Interval, IntervalError},
18 |     key::{Key, KeyError},
19 |     literal::{Literal, LiteralError},
20 |     point::Point,
21 |     row::{Row, RowError},
22 |     schema::{Schema, SchemaIndex, SchemaIndexOrd, SchemaParseError},
23 |     string_ext::{StringExt, StringExtError},
24 |     table::{TableError, get_alias, get_index},
25 |     value::{ConvertError, HashMapJsonExt, NumericBinaryOperator, Value, ValueError},
26 | };
27 | 


--------------------------------------------------------------------------------
/core/src/data/function.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     crate::ast::{Expr, OperateFunctionArg},
 3 |     serde::{Deserialize, Serialize},
 4 | };
 5 | 
 6 | #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
 7 | pub struct CustomFunction {
 8 |     pub func_name: String,
 9 |     pub args: Vec<OperateFunctionArg>,
10 |     pub body: Expr,
11 | }
12 | 
13 | impl CustomFunction {
14 |     pub fn to_str(&self) -> String {
15 |         let name = &self.func_name;
16 |         let args = self
17 |             .args
18 |             .iter()
19 |             .map(|arg| format!("{}: {}", arg.name, arg.data_type))
20 |             .collect::<Vec<String>>()
21 |             .join(", ");
22 |         format!("{name}({args})")
23 |     }
24 | }
25 | 


--------------------------------------------------------------------------------
/core/src/data/string_ext.rs:
--------------------------------------------------------------------------------
 1 | use {crate::result::Result, regex::Regex, serde::Serialize, thiserror::Error};
 2 | 
 3 | #[derive(Error, Serialize, Debug, PartialEq, Eq)]
 4 | pub enum StringExtError {
 5 |     #[error("unreachable literal unary operation")]
 6 |     UnreachablePatternParsing,
 7 | }
 8 | 
 9 | pub trait StringExt {
10 |     fn like(&self, pattern: &str, case_sensitive: bool) -> Result<bool>;
11 | }
12 | 
13 | impl StringExt for str {
14 |     fn like(&self, pattern: &str, case_sensitive: bool) -> Result<bool> {
15 |         let (match_string, match_pattern) = match case_sensitive {
16 |             true => (self.to_owned(), pattern.to_owned()),
17 |             false => {
18 |                 let lowercase_string = self.to_lowercase();
19 |                 let lowercase_pattern = pattern.to_lowercase();
20 | 
21 |                 (lowercase_string, lowercase_pattern)
22 |             }
23 |         };
24 | 
25 |         Ok(Regex::new(&format!(
26 |             "^{}
quot;,
27 |             regex::escape(match_pattern.as_str())
28 |                 .replace('%', ".*")
29 |                 .replace('_', ".")
30 |         ))
31 |         .map_err(|_| StringExtError::UnreachablePatternParsing)?
32 |         .is_match(match_string.as_str()))
33 |     }
34 | }
35 | 


--------------------------------------------------------------------------------
/core/src/data/table.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     crate::ast::{IndexItem, TableAlias, TableFactor},
 3 |     serde::Serialize,
 4 |     std::fmt::Debug,
 5 |     thiserror::Error,
 6 | };
 7 | 
 8 | #[derive(Error, Serialize, Debug, PartialEq, Eq)]
 9 | pub enum TableError {
10 |     #[error("unreachable")]
11 |     Unreachable,
12 | }
13 | 
14 | pub fn get_alias(table_factor: &TableFactor) -> &String {
15 |     match table_factor {
16 |         TableFactor::Table {
17 |             name, alias: None, ..
18 |         }
19 |         | TableFactor::Table {
20 |             alias: Some(TableAlias { name, .. }),
21 |             ..
22 |         }
23 |         | TableFactor::Derived {
24 |             alias: TableAlias { name, .. },
25 |             ..
26 |         }
27 |         | TableFactor::Series {
28 |             alias: TableAlias { name, .. },
29 |             ..
30 |         }
31 |         | TableFactor::Dictionary {
32 |             alias: TableAlias { name, .. },
33 |             ..
34 |         } => name,
35 |     }
36 | }
37 | 
38 | pub fn get_index(table_factor: &TableFactor) -> Option<&IndexItem> {
39 |     match table_factor {
40 |         TableFactor::Table { index, .. } => index.as_ref(),
41 |         TableFactor::Derived { .. }
42 |         | TableFactor::Series { .. }
43 |         | TableFactor::Dictionary { .. } => None,
44 |     }
45 | }
46 | 


--------------------------------------------------------------------------------
/core/src/data/value/binary_op.rs:
--------------------------------------------------------------------------------
 1 | use crate::{prelude::Value, result::Result};
 2 | 
 3 | mod decimal;
 4 | mod f32;
 5 | mod f64;
 6 | 
 7 | mod integer;
 8 | 
 9 | pub trait TryBinaryOperator {
10 |     type Rhs;
11 | 
12 |     fn try_add(&self, rhs: &Self::Rhs) -> Result<Value>;
13 |     fn try_subtract(&self, rhs: &Self::Rhs) -> Result<Value>;
14 |     fn try_multiply(&self, rhs: &Self::Rhs) -> Result<Value>;
15 |     fn try_divide(&self, rhs: &Self::Rhs) -> Result<Value>;
16 |     fn try_modulo(&self, rhs: &Self::Rhs) -> Result<Value>;
17 | }
18 | 


--------------------------------------------------------------------------------
/core/src/data/value/binary_op/integer.rs:
--------------------------------------------------------------------------------
 1 | mod i128;
 2 | mod i16;
 3 | mod i32;
 4 | mod i64;
 5 | mod i8;
 6 | mod u128;
 7 | mod u16;
 8 | mod u32;
 9 | mod u64;
10 | mod u8;
11 | 
12 | mod macros;
13 | 


--------------------------------------------------------------------------------
/core/src/data/value/binary_op/integer/i128.rs:
--------------------------------------------------------------------------------
 1 | use {crate::prelude::Value, std::cmp::Ordering};
 2 | 
 3 | super::macros::impl_try_binary_op!(I128, i128);
 4 | #[cfg(test)]
 5 | super::macros::generate_binary_op_tests!(I128, i128);
 6 | 
 7 | super::macros::impl_partial_cmp_ord_method!(i128);
 8 | #[cfg(test)]
 9 | super::macros::generate_cmp_ord_tests!(i128);
10 | 


--------------------------------------------------------------------------------
/core/src/data/value/binary_op/integer/i16.rs:
--------------------------------------------------------------------------------
 1 | use {crate::prelude::Value, std::cmp::Ordering};
 2 | 
 3 | super::macros::impl_try_binary_op!(I16, i16);
 4 | #[cfg(test)]
 5 | super::macros::generate_binary_op_tests!(I16, i16);
 6 | 
 7 | super::macros::impl_partial_cmp_ord_method!(i16);
 8 | #[cfg(test)]
 9 | super::macros::generate_cmp_ord_tests!(i16);
10 | 


--------------------------------------------------------------------------------
/core/src/data/value/binary_op/integer/i32.rs:
--------------------------------------------------------------------------------
 1 | use {crate::prelude::Value, std::cmp::Ordering};
 2 | 
 3 | super::macros::impl_try_binary_op!(I32, i32);
 4 | #[cfg(test)]
 5 | super::macros::generate_binary_op_tests!(I32, i32);
 6 | 
 7 | super::macros::impl_partial_cmp_ord_method!(i32);
 8 | #[cfg(test)]
 9 | super::macros::generate_cmp_ord_tests!(i32);
10 | 


--------------------------------------------------------------------------------
/core/src/data/value/binary_op/integer/i64.rs:
--------------------------------------------------------------------------------
 1 | use {crate::prelude::Value, std::cmp::Ordering};
 2 | 
 3 | super::macros::impl_try_binary_op!(I64, i64);
 4 | #[cfg(test)]
 5 | super::macros::generate_binary_op_tests!(I64, i64);
 6 | 
 7 | super::macros::impl_partial_cmp_ord_method!(i64);
 8 | #[cfg(test)]
 9 | super::macros::generate_cmp_ord_tests!(i64);
10 | 


--------------------------------------------------------------------------------
/core/src/data/value/binary_op/integer/i8.rs:
--------------------------------------------------------------------------------
 1 | use {crate::prelude::Value, std::cmp::Ordering};
 2 | 
 3 | super::macros::impl_try_binary_op!(I8, i8);
 4 | #[cfg(test)]
 5 | super::macros::generate_binary_op_tests!(I8, i8);
 6 | 
 7 | super::macros::impl_partial_cmp_ord_method!(i8);
 8 | #[cfg(test)]
 9 | super::macros::generate_cmp_ord_tests!(i8);
10 | 


--------------------------------------------------------------------------------
/core/src/data/value/binary_op/integer/u128.rs:
--------------------------------------------------------------------------------
 1 | use {crate::prelude::Value, std::cmp::Ordering};
 2 | 
 3 | super::macros::impl_try_binary_op!(U128, u128);
 4 | #[cfg(test)]
 5 | super::macros::generate_binary_op_tests!(U128, u128);
 6 | 
 7 | super::macros::impl_partial_cmp_ord_method!(u128);
 8 | #[cfg(test)]
 9 | super::macros::generate_cmp_ord_tests!(u128);
10 | 


--------------------------------------------------------------------------------
/core/src/data/value/binary_op/integer/u16.rs:
--------------------------------------------------------------------------------
 1 | use {crate::prelude::Value, std::cmp::Ordering};
 2 | 
 3 | super::macros::impl_try_binary_op!(U16, u16);
 4 | #[cfg(test)]
 5 | super::macros::generate_binary_op_tests!(U16, u16);
 6 | 
 7 | super::macros::impl_partial_cmp_ord_method!(u16);
 8 | #[cfg(test)]
 9 | super::macros::generate_cmp_ord_tests!(u16);
10 | 


--------------------------------------------------------------------------------
/core/src/data/value/binary_op/integer/u32.rs:
--------------------------------------------------------------------------------
 1 | use {crate::prelude::Value, std::cmp::Ordering};
 2 | 
 3 | super::macros::impl_try_binary_op!(U32, u32);
 4 | #[cfg(test)]
 5 | super::macros::generate_binary_op_tests!(U32, u32);
 6 | 
 7 | super::macros::impl_partial_cmp_ord_method!(u32);
 8 | #[cfg(test)]
 9 | super::macros::generate_cmp_ord_tests!(u32);
10 | 


--------------------------------------------------------------------------------
/core/src/data/value/binary_op/integer/u64.rs:
--------------------------------------------------------------------------------
 1 | use {crate::prelude::Value, std::cmp::Ordering};
 2 | 
 3 | super::macros::impl_try_binary_op!(U64, u64);
 4 | #[cfg(test)]
 5 | super::macros::generate_binary_op_tests!(U64, u64);
 6 | 
 7 | super::macros::impl_partial_cmp_ord_method!(u64);
 8 | #[cfg(test)]
 9 | super::macros::generate_cmp_ord_tests!(u64);
10 | 


--------------------------------------------------------------------------------
/core/src/data/value/binary_op/integer/u8.rs:
--------------------------------------------------------------------------------
 1 | use {crate::prelude::Value, std::cmp::Ordering};
 2 | 
 3 | super::macros::impl_try_binary_op!(U8, u8);
 4 | #[cfg(test)]
 5 | super::macros::generate_binary_op_tests!(U8, u8);
 6 | 
 7 | super::macros::impl_partial_cmp_ord_method!(u8);
 8 | #[cfg(test)]
 9 | super::macros::generate_cmp_ord_tests!(u8);
10 | 


--------------------------------------------------------------------------------
/core/src/data/value/uuid.rs:
--------------------------------------------------------------------------------
 1 | use {super::ValueError, crate::result::Result, uuid::Uuid};
 2 | 
 3 | pub fn parse_uuid(v: &str) -> Result<u128> {
 4 |     match Uuid::parse_str(v) {
 5 |         Ok(u) => Ok(u.as_u128()),
 6 |         _ => Err(ValueError::FailedToParseUUID(v.to_owned()).into()),
 7 |     }
 8 | }
 9 | 
10 | #[cfg(test)]
11 | mod tests {
12 |     use crate::data::value::ValueError;
13 | 
14 |     #[test]
15 |     fn parse_uuid() {
16 |         macro_rules! test (
17 |             ($str: literal, $result: expr) => {
18 |                 assert_eq!(super::parse_uuid($str), $result)
19 |             }
20 |         );
21 | 
22 |         test!(
23 |             "936DA01F9ABD4d9d80C702AF85C822A8",
24 |             Ok(195965723427462096757863453463987888808)
25 |         );
26 |         test!(
27 |             "550e8400-e29b-41d4-a716-446655440000",
28 |             Ok(113059749145936325402354257176981405696)
29 |         );
30 |         test!(
31 |             "urn:uuid:F9168C5E-CEB2-4faa-B6BF-329BF39FA1E4",
32 |             Ok(331094848530093083170738142201201533412)
33 |         );
34 | 
35 |         test!(
36 |             "1",
37 |             Err(ValueError::FailedToParseUUID("1".to_owned()).into())
38 |         );
39 |         test!(
40 |             "NOT_UUID_STRING",
41 |             Err(ValueError::FailedToParseUUID("NOT_UUID_STRING".to_owned()).into())
42 |         );
43 |     }
44 | }
45 | 


--------------------------------------------------------------------------------
/core/src/executor.rs:
--------------------------------------------------------------------------------
 1 | mod aggregate;
 2 | mod alter;
 3 | mod context;
 4 | mod delete;
 5 | mod evaluate;
 6 | mod execute;
 7 | mod fetch;
 8 | mod filter;
 9 | mod insert;
10 | mod join;
11 | mod limit;
12 | mod select;
13 | mod sort;
14 | mod update;
15 | mod validate;
16 | 
17 | pub use {
18 |     alter::{AlterError, Referencing},
19 |     context::RowContext,
20 |     delete::DeleteError,
21 |     evaluate::{EvaluateError, evaluate_stateless},
22 |     execute::{ExecuteError, Payload, PayloadVariable, execute},
23 |     fetch::FetchError,
24 |     insert::InsertError,
25 |     select::SelectError,
26 |     sort::SortError,
27 |     update::UpdateError,
28 |     validate::ValidateError,
29 | };
30 | 


--------------------------------------------------------------------------------
/core/src/executor/alter.rs:
--------------------------------------------------------------------------------
 1 | mod alter_table;
 2 | mod error;
 3 | mod function;
 4 | mod index;
 5 | mod table;
 6 | mod validate;
 7 | 
 8 | use validate::{validate, validate_arg_names, validate_column_names, validate_default_args};
 9 | 
10 | pub use {
11 |     alter_table::alter_table,
12 |     error::AlterError,
13 |     function::{delete_function, insert_function},
14 |     index::create_index,
15 |     table::{CreateTableOptions, Referencing, create_table, drop_table},
16 | };
17 | 


--------------------------------------------------------------------------------
/core/src/executor/context.rs:
--------------------------------------------------------------------------------
1 | mod aggregate_context;
2 | mod row_context;
3 | 
4 | pub use {aggregate_context::AggregateContext, row_context::RowContext};
5 | 


--------------------------------------------------------------------------------
/core/src/executor/context/aggregate_context.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     super::RowContext,
 3 |     crate::{ast::Aggregate, data::Value},
 4 |     im_rc::HashMap,
 5 |     std::{fmt::Debug, rc::Rc},
 6 | };
 7 | 
 8 | #[derive(Debug)]
 9 | pub struct AggregateContext<'a> {
10 |     pub aggregated: Option<HashMap<&'a Aggregate, Value>>,
11 |     pub next: Rc<RowContext<'a>>,
12 | }
13 | 


--------------------------------------------------------------------------------
/core/src/executor/select/error.rs:
--------------------------------------------------------------------------------
1 | use {serde::Serialize, std::fmt::Debug, thiserror::Error};
2 | 
3 | #[derive(Error, Serialize, Debug, PartialEq, Eq)]
4 | pub enum SelectError {
5 |     #[error("VALUES lists must all be the same length")]
6 |     NumberOfValuesDifferent,
7 | }
8 | 


--------------------------------------------------------------------------------
/core/src/lib.rs:
--------------------------------------------------------------------------------
 1 | #![deny(clippy::str_to_string)]
 2 | 
 3 | // re-export
 4 | pub use {chrono, sqlparser};
 5 | 
 6 | mod glue;
 7 | mod mock;
 8 | mod result;
 9 | 
10 | pub mod ast;
11 | pub mod ast_builder;
12 | pub mod data;
13 | pub mod executor;
14 | pub mod parse_sql;
15 | pub mod plan;
16 | pub mod store;
17 | pub mod translate;
18 | 
19 | pub mod prelude {
20 |     pub use crate::{
21 |         ast::DataType,
22 |         data::{Key, Value},
23 |         executor::{Payload, PayloadVariable, execute},
24 |         glue::Glue,
25 |         parse_sql::parse,
26 |         plan::plan,
27 |         result::{Error, Result},
28 |         translate::translate,
29 |     };
30 | }
31 | 
32 | pub mod error {
33 |     pub use crate::result::*;
34 | }
35 | 


--------------------------------------------------------------------------------
/core/src/plan.rs:
--------------------------------------------------------------------------------
 1 | mod context;
 2 | mod error;
 3 | mod evaluable;
 4 | mod expr;
 5 | mod index;
 6 | mod join;
 7 | mod planner;
 8 | mod primary_key;
 9 | mod schema;
10 | mod validate;
11 | 
12 | use crate::{ast::Statement, result::Result, store::Store};
13 | 
14 | pub use {
15 |     self::validate::validate, error::*, index::plan as plan_index, join::plan as plan_join,
16 |     primary_key::plan as plan_primary_key, schema::fetch_schema_map,
17 | };
18 | 
19 | pub async fn plan<T: Store>(storage: &T, statement: Statement) -> Result<Statement> {
20 |     let schema_map = fetch_schema_map(storage, &statement).await?;
21 |     validate(&schema_map, &statement)?;
22 |     let statement = plan_primary_key(&schema_map, statement);
23 |     let statement = plan_index(&schema_map, statement)?;
24 |     let statement = plan_join(&schema_map, statement);
25 | 
26 |     Ok(statement)
27 | }
28 | 


--------------------------------------------------------------------------------
/core/src/plan/error.rs:
--------------------------------------------------------------------------------
 1 | use {serde::Serialize, std::fmt::Debug, thiserror::Error as ThisError};
 2 | 
 3 | #[derive(ThisError, Serialize, Debug, PartialEq, Eq)]
 4 | pub enum PlanError {
 5 |     /// Error that that omits when user projects common column name from multiple tables in `JOIN`
 6 |     /// situation.
 7 |     #[error("column reference {0} is ambiguous, please specify the table name")]
 8 |     ColumnReferenceAmbiguous(String),
 9 | }
10 | 


--------------------------------------------------------------------------------
/core/src/store/function.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     crate::{
 3 |         data::CustomFunction as StructCustomFunction,
 4 |         result::{Error, Result},
 5 |     },
 6 |     async_trait::async_trait,
 7 | };
 8 | 
 9 | #[async_trait(?Send)]
10 | pub trait CustomFunction {
11 |     async fn fetch_function<'a>(
12 |         &'a self,
13 |         _func_name: &str,
14 |     ) -> Result<Option<&'a StructCustomFunction>> {
15 |         Err(Error::StorageMsg(
16 |             "[Storage] CustomFunction is not supported".to_owned(),
17 |         ))
18 |     }
19 | 
20 |     async fn fetch_all_functions<'a>(&'a self) -> Result<Vec<&'a StructCustomFunction>> {
21 |         Err(Error::StorageMsg(
22 |             "[Storage] CustomFunction is not supported".to_owned(),
23 |         ))
24 |     }
25 | }
26 | 
27 | #[async_trait(?Send)]
28 | pub trait CustomFunctionMut {
29 |     async fn insert_function(&mut self, _func: StructCustomFunction) -> Result<()> {
30 |         Err(Error::StorageMsg(
31 |             "[Storage] CustomFunction is not supported".to_owned(),
32 |         ))
33 |     }
34 | 
35 |     async fn delete_function(&mut self, _func_name: &str) -> Result<()> {
36 |         Err(Error::StorageMsg(
37 |             "[Storage] CustomFunction is not supported".to_owned(),
38 |         ))
39 |     }
40 | }
41 | 


--------------------------------------------------------------------------------
/core/src/store/metadata.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     crate::{prelude::Value, result::Result},
 3 |     async_trait::async_trait,
 4 |     std::{collections::HashMap, iter::empty},
 5 | };
 6 | 
 7 | type ObjectName = String;
 8 | pub type MetaIter = Box<dyn Iterator<Item = Result<(ObjectName, HashMap<String, Value>)>>>;
 9 | 
10 | #[async_trait(?Send)]
11 | pub trait Metadata {
12 |     async fn scan_table_meta(&self) -> Result<MetaIter> {
13 |         Ok(Box::new(empty()))
14 |     }
15 | }
16 | 


--------------------------------------------------------------------------------
/core/src/store/transaction.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     crate::result::{Error, Result},
 3 |     async_trait::async_trait,
 4 | };
 5 | 
 6 | #[async_trait(?Send)]
 7 | pub trait Transaction {
 8 |     async fn begin(&mut self, autocommit: bool) -> Result<bool> {
 9 |         if autocommit {
10 |             return Ok(false);
11 |         }
12 | 
13 |         Err(Error::StorageMsg(
14 |             "[Storage] Transaction::begin is not supported".to_owned(),
15 |         ))
16 |     }
17 | 
18 |     async fn rollback(&mut self) -> Result<()> {
19 |         Ok(())
20 |     }
21 | 
22 |     async fn commit(&mut self) -> Result<()> {
23 |         Ok(())
24 |     }
25 | }
26 | 


--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
 1 | # Dependencies
 2 | /node_modules
 3 | 
 4 | # Production
 5 | /build
 6 | 
 7 | # Generated files
 8 | .docusaurus
 9 | .cache-loader
10 | 
11 | # Misc
12 | .DS_Store
13 | .env.local
14 | .env.development.local
15 | .env.test.local
16 | .env.production.local
17 | 
18 | npm-debug.log*
19 | yarn-debug.log*
20 | yarn-error.log*
21 | 


--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
 1 | # GlueSQL Docs
 2 | 
 3 | This website is built using [Docusaurus 2](https://docusaurus.io/), a modern static website generator.
 4 | 
 5 | ### Installation
 6 | 
 7 | ```
 8 | $ npm install
 9 | ```
10 | 
11 | ### Local Development
12 | 
13 | ```
14 | $ npm run start
15 | $ npm run start:blog
16 | ```
17 | 
18 | This command starts a local development server and opens up a browser window. Most changes are reflected live without having to restart the server.
19 | 
20 | ### Build
21 | 
22 | ```
23 | $ npm run build
24 | $ npm run build:blog
25 | ```
26 | 
27 | This command generates static content into the `build` directory and can be served using any static contents hosting service.
28 | 


--------------------------------------------------------------------------------
/docs/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 |   presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
3 | };
4 | 


--------------------------------------------------------------------------------
/docs/blog/assets/blog-test-driven-documentation-insert-errorcase.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gluesql/gluesql/78b51bf019fc8c29a51182e464357bd76df35700/docs/blog/assets/blog-test-driven-documentation-insert-errorcase.jpg


--------------------------------------------------------------------------------
/docs/blog/assets/blog-test-driven-documentation-insert.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gluesql/gluesql/78b51bf019fc8c29a51182e464357bd76df35700/docs/blog/assets/blog-test-driven-documentation-insert.jpg


--------------------------------------------------------------------------------
/docs/docs/ast-builder/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "AST Builder",
3 |   "position": 4,
4 |   "collapsed": false
5 | }
6 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/expressions/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "Expressions",
3 |   "position": 3,
4 |   "collapsed": true
5 | }
6 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/expressions/conditional.md:
--------------------------------------------------------------------------------
1 | # Conditional
2 | 
3 | ## Todo
4 | 
5 |     - CASE: Returns a value on a condition.
6 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/expressions/nested.md:
--------------------------------------------------------------------------------
1 | # Nested
2 | 
3 | ## Todo
4 | 
5 |     - NESTED: Represents a nested expression, allowing the use of complex and grouped expressions.
6 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/expressions/operator-based.md:
--------------------------------------------------------------------------------
1 | # Operator Based
2 | 
3 | ## Todo
4 | 
5 |     - BINARY_OP: Represents a binary operation (like +, -, *, /, etc.).
6 |     - UNARY_OP: Represents a unary operation (like NOT, -, etc.).
7 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/expressions/value-checking.md:
--------------------------------------------------------------------------------
1 | # Value Checking
2 | 
3 | ## Todo
4 | 
5 |     - BETWEEN: Checks if a value is within a range of values.
6 |     - IN_LIST: Checks if a value is within a list of values.
7 |     - IS_NULL: Checks if a value is NULL.
8 |     - EXISTS: Checks if a subquery returns any rows.
9 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/functions/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "Functions",
3 |   "position": 4,
4 |   "collapsed": true
5 | }
6 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/functions/date-&-time/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "Date & Time",
3 |   "position": 3,
4 |   "collapsed": true
5 | }
6 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/functions/date-&-time/current-date-and-time.md:
--------------------------------------------------------------------------------
 1 | # Current Date and Time
 2 | 
 3 | GlueSQL provides a function to get the current date and time: `now`.
 4 | 
 5 | ## Now - now
 6 | 
 7 | The `now` function returns the current date and time.
 8 | 
 9 | ```rust
10 | let actual = table("Record")
11 |     .select()
12 |     .filter(col("time_stamp").gt(now()))  // select rows where "time_stamp" is later than current time
13 |     .project("id, time_stamp")
14 |     .execute(glue)
15 |     .await;
16 | ```
17 | 
18 | In the above example, the `filter` method uses `now` to select rows where the "time_stamp" column is later than the current time.
19 | 
20 | When inserting data into a table, you can use the `now` function to record the current time:
21 | 
22 | ```rust
23 | let actual = table("Record")
24 |     .insert()
25 |     .values(vec![
26 |         "1, '2022-12-23T05:30:11.164932863'",
27 |         "2, NOW()",  // Inserts the current time
28 |         "3, '9999-12-31T23:59:40.364832862'",
29 |     ])
30 |     .execute(glue)
31 |     .await;
32 | ```
33 | In the example above, the "time_stamp" column for the row with id 2 is set to the current time at the moment of insertion.
34 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/functions/date-&-time/date-and-time-extraction.md:
--------------------------------------------------------------------------------
1 | # Date and Time Extraction
2 | 
3 | ## Todo
4 | 
5 |     - EXTRACT: Extracts a part of a date or time (like day, month, year, hour, minute, etc.).
6 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/functions/geometry/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "Geometry",
3 |   "position": 5,
4 |   "collapsed": true
5 | }
6 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/functions/geometry/coordinate-extraction.md:
--------------------------------------------------------------------------------
1 | # Coordinate Extraction
2 | 
3 | ## Todo
4 | 
5 |     - GET_X: Extracts the x-coordinate from a point.
6 |     - GET_Y: Extracts the y-coordinate from a point.
7 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/functions/geometry/distance-calculation.md:
--------------------------------------------------------------------------------
1 | # Distance Calculation
2 | 
3 | ## Todo
4 | 
5 |     - CALC_DISTANCE: Calculates the distance between two points.
6 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/functions/geometry/point-creation.md:
--------------------------------------------------------------------------------
1 | # Point Creation
2 | 
3 | ## Todo
4 | 
5 |     - POINT: Creates a geometric point with an x and y coordinate.
6 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/functions/list-&-map/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "List & Map",
3 |   "position": 4,
4 |   "collapsed": true
5 | }
6 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/functions/list-&-map/list-and-map-concatenation.md:
--------------------------------------------------------------------------------
1 | # List and Map Concatenation
2 | 
3 | ## Todo
4 | 
5 |     - CONCAT: Combines two or more lists or maps into one.
6 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/functions/list-&-map/list-manipulation.md:
--------------------------------------------------------------------------------
1 | # List Manipulation
2 | 
3 | ## Todo
4 | 
5 |     - APPEND: Adds an element to the end of a list.
6 |     - PREPEND: Adds an element to the beginning of a list.
7 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/functions/math/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "Math",
3 |   "position": 2,
4 |   "collapsed": true
5 | }
6 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/functions/math/logarithmic-and-exponential.md:
--------------------------------------------------------------------------------
 1 | # Logarithmic and Exponential 
 2 | ## Todo
 3 |     - EXP: Returns e raised to the power of a specified number.
 4 |     - LN: Returns the natural logarithm of a number.
 5 |     - LOG: Returns the logarithm of a number to a specified base.
 6 |     - LOG10: Returns the base-10 logarithm of a number.
 7 |     - LOG2: Returns the base-2 logarithm of a number.
 8 |     - POWER: Raises a number to the power of another number.
 9 | 
10 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/functions/math/special-mathematical.md:
--------------------------------------------------------------------------------
1 | # Special Mathematical 
2 | ## Todo
3 |     - PI: Returns the constant value of Pi.
4 |     - RAND: Returns a random number.
5 |     - SIGN: Returns the sign of a number.
6 |     - SQRT: Returns the square root of a number.
7 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/functions/math/trigonometric.md:
--------------------------------------------------------------------------------
 1 | # Trigonometric 
 2 | ## Todo
 3 |     - ACOS: Returns the arc cosine of a number.
 4 |     - ASIN: Returns the arc sine of a number.
 5 |     - ATAN: Returns the arc tangent of a number.
 6 |     - COS: Returns the cosine of a number.
 7 |     - SIN: Returns the sine of a number.
 8 |     - TAN: Returns the tangent of a number.
 9 | 
10 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/functions/others/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "Others",
3 |   "position": 6,
4 |   "collapsed": true
5 | }
6 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/functions/others/type-conversion.md:
--------------------------------------------------------------------------------
1 | # Type Conversion 
2 | ## Todo
3 |     - CAST: Converts a value from one data type to another.
4 | 
5 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/functions/others/unique-identifier.md:
--------------------------------------------------------------------------------
1 | # Unique Identifier 
2 | ## Todo
3 |     - GENERATE_UUID: Generates a universally unique identifier (UUID).
4 | 
5 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/functions/text/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "Text",
3 |   "position": 1,
4 |   "collapsed": true
5 | }
6 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/functions/text/text-manipulation.md:
--------------------------------------------------------------------------------
1 | # Text Manipulation 
2 | ## Todo
3 |     - CONCAT: Concatenates two or more strings into one.
4 |     - CONCAT_WS: Concatenates two or more strings into one with a separator.
5 |     - SUBSTR: Returns a part of a string.
6 |     - REPEAT: Repeats a string a specified number of times.
7 |     - REVERSE: Reverses the order of the characters in a string.
8 | 
9 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/statements/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "Statements",
3 |   "position": 1,
4 |   "collapsed": true
5 | }
6 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/statements/data-manipulation/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "Data Manipulation",
3 |   "position": 2,
4 |   "collapsed": true
5 | }
6 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/statements/data-manipulation/deleting-data.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | sidebar_position: 5
 3 | ---
 4 | 
 5 | # Deleting Data
 6 | 
 7 | In this section, we will discuss how to delete data from a table using GlueSQL.
 8 | 
 9 | ## Delete with Filter
10 | 
11 | To delete specific rows from a table, you can use the `delete` method on a table object, followed by the `filter` method to provide a condition that the rows must meet. You can then use the `execute` method to apply the changes.
12 | 
13 | ```rust
14 | let actual = table("Foo")
15 |     .delete()
16 |     .filter(col("flag").eq(false))
17 |     .execute(glue)
18 |     .await;
19 | let expected = Ok(Payload::Delete(1));
20 | test(actual, expected);
21 | ```
22 | 
23 | This code deletes the rows in the table `Foo` where the `flag` column value is false.
24 | 
25 | ## Delete All Rows
26 | 
27 | To delete all rows from a table, you can use the `delete` method on a table object, followed by the `execute` method.
28 | 
29 | ```rust
30 | let actual = table("Foo").delete().execute(glue).await;
31 | let expected = Ok(Payload::Delete(2));
32 | test(actual, expected);
33 | ```
34 | 
35 | This code deletes all rows from the table `Foo`.
36 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/statements/querying/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "Querying",
3 |   "position": 1,
4 |   "collapsed": true
5 | }
6 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/statements/querying/data-injection.md:
--------------------------------------------------------------------------------
1 | # Data Injection
2 | ## Todo
3 |     - VALUES: Allows users to manually input data into the query.
4 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/statements/querying/data-joining.md:
--------------------------------------------------------------------------------
1 | # Data Joining
2 | ## Todo
3 |     - JOIN: Combines rows from two or more tables based on a related column between them.
4 | 
5 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/statements/querying/data-selection-and-projection.md:
--------------------------------------------------------------------------------
1 | # Data Selection and Projection
2 | ## Todo
3 |     - FILTER: Filters rows in a dataset based on specified conditions.
4 |     - PROJECT: Selects which columns to include in the result set.
5 | 
6 | 


--------------------------------------------------------------------------------
/docs/docs/ast-builder/statements/querying/data-sorting-and-limiting.md:
--------------------------------------------------------------------------------
1 | # Data Sorting and Limiting
2 | ## Todo
3 |     - ORDER_BY: Sorts the result set in ascending or descending order based on specified column(s).
4 |     - LIMIT: Limits the number of rows returned in the result set.
5 |     - OFFSET: Skips a specified number of rows in the result set.
6 | 
7 | 


--------------------------------------------------------------------------------
/docs/docs/getting-started/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "Getting Started",
3 |   "position": 2,
4 |   "collapsed": false
5 | }
6 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "SQL Syntax",
3 |   "position": 3,
4 |   "collapsed": false
5 | }
6 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/data-types/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "Data types",
3 |   "position": 2,
4 |   "collapsed": true
5 | }
6 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "Functions",
3 |   "position": 3,
4 |   "collapsed": true
5 | }
6 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/datetime/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "Date & Time",
3 |   "position": 3,
4 |   "collapsed": true
5 | }
6 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/datetime/add-month.md:
--------------------------------------------------------------------------------
 1 | # ADD_MONTH
 2 | 
 3 | `ADD_MONTH` shifts a date by the given number of months.
 4 | 
 5 | ## Syntax
 6 | 
 7 | ```sql
 8 | ADD_MONTH(date, months)
 9 | ```
10 | 
11 | ## Parameters
12 | 
13 | - `date` – A `DATE` value.
14 | - `months` – Integer number of months to add. Negative values subtract months.
15 | 
16 | ## Examples
17 | 
18 | ```sql
19 | SELECT ADD_MONTH('2017-06-15', 1) AS next_month;
20 | SELECT ADD_MONTH('2017-06-15', -1) AS prev_month;
21 | ```
22 | 
23 | These return `2017-07-15` and `2017-05-15` respectively.
24 | 
25 | ## Notes
26 | 
27 | If the resulting day does not exist in the target month, the last day of that month is used.
28 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/datetime/last-day.md:
--------------------------------------------------------------------------------
 1 | # LAST_DAY
 2 | 
 3 | The `LAST_DAY` function returns the last day of the month of a given date or timestamp.
 4 | 
 5 | ## Syntax
 6 | 
 7 | ```sql
 8 | LAST_DAY(value)
 9 | ```
10 | 
11 | ## Parameters
12 | 
13 | - `value` – A `DATE` or `TIMESTAMP` expression.
14 | 
15 | ## Examples
16 | 
17 | ```sql
18 | SELECT LAST_DAY('2017-12-15');
19 | ```
20 | 
21 | This returns `2017-12-31`.
22 | 
23 | ## Notes
24 | 
25 | `LAST_DAY` accepts only date or timestamp values; other types produce an error.
26 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/datetime/now.md:
--------------------------------------------------------------------------------
 1 | # NOW
 2 | 
 3 | The `NOW()` function in SQL returns the current date and time in UTC. You can use it to retrieve the current UTC timestamp, or as a default value for a TIMESTAMP column in a table. 
 4 | 
 5 | ## Syntax
 6 | 
 7 | ```
 8 | NOW()
 9 | ```
10 | 
11 | ## Examples
12 | 
13 | ### Creating a table with a TIMESTAMP column and setting the default value to NOW()
14 | 
15 | ```sql
16 | CREATE TABLE Item (time TIMESTAMP DEFAULT NOW());
17 | ```
18 | 
19 | This creates a table named `Item` with a column `time` of the type TIMESTAMP. The default value for this column is the current UTC timestamp.
20 | 
21 | ### Inserting data into the table
22 | 
23 | ```sql
24 | INSERT INTO Item (time) VALUES
25 |     ('2021-10-13T06:42:40.364832862'),
26 |     ('9999-12-31T23:59:40.364832862');
27 | ```
28 | 
29 | Here we're inserting two rows into the `Item` table with specific timestamps.
30 | 
31 | ### Selecting rows where the timestamp is greater than the current timestamp
32 | 
33 | ```sql
34 | SELECT time FROM Item WHERE time > NOW();
35 | ```
36 | 
37 | This query selects the `time` column from the `Item` table where the `time` is greater than the current UTC timestamp. In this case, the result will be:
38 | 
39 | ```
40 | 9999-12-31T23:59:40.364832862
41 | ```


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/geometry/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "Geometry",
3 |   "position": 5,
4 |   "collapsed": true
5 | }
6 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/geometry/calc-distance.md:
--------------------------------------------------------------------------------
 1 | # CALC_DISTANCE
 2 | 
 3 | The `CALC_DISTANCE` function is used to calculate the Euclidean distance between two `Point` type geographical coordinates.
 4 | 
 5 | ## Syntax
 6 | 
 7 | ```sql
 8 | CALC_DISTANCE(point1, point2)
 9 | ```
10 | 
11 | **Parameters:**
12 | 
13 | - `point1`: The first geographical coordinate of type `Point`.
14 | - `point2`: The second geographical coordinate of type `Point`.
15 | 
16 | ## Examples
17 | 
18 | Consider the following table `Foo`:
19 | 
20 | ```sql
21 | CREATE TABLE Foo (
22 |     geo1 Point,
23 |     geo2 Point,
24 |     bar Float
25 | );
26 | ```
27 | 
28 | With the following data:
29 | 
30 | ```sql
31 | INSERT INTO Foo VALUES (POINT(0.3134, 3.156), POINT(1.415, 3.231), 3);
32 | ```
33 | 
34 | ### Example 1: Calculate the distance between two points
35 | 
36 | ```sql
37 | SELECT CALC_DISTANCE(geo1, geo2) AS georesult FROM Foo;
38 | ```
39 | 
40 | **Result:**
41 | 
42 | | georesult       |
43 | |-----------------|
44 | | 1.104150152832485|
45 | 
46 | ## Errors
47 | 
48 | 1. If the number of arguments is not 2, a `FunctionArgsLengthNotMatching` error will be thrown.
49 | 2. If any of the arguments are not of type `Point`, a `FunctionRequiresPointValue` error will be thrown.
50 | 3. If any of the arguments are `NULL`, the result will be `NULL`.
51 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/geometry/point.md:
--------------------------------------------------------------------------------
 1 | 
 2 | # POINT
 3 | 
 4 | The `POINT` function creates a point value using the provided x and y coordinates. A point value represents a two-dimensional geometric point with a pair of floating-point numbers, often used for storing spatial data.
 5 | 
 6 | ## Syntax
 7 | 
 8 | ```sql
 9 | POINT(x, y)
10 | ```
11 | 
12 | ## Examples
13 | 
14 | Create a table with a `POINT` data type column:
15 | 
16 | ```sql
17 | CREATE TABLE Foo (point_field POINT);
18 | ```
19 | 
20 | Insert a record with a point value:
21 | 
22 | ```sql
23 | INSERT INTO Foo VALUES (POINT(0.3134, 0.156));
24 | ```
25 | 
26 | Select the `point_field` column:
27 | 
28 | ```sql
29 | SELECT point_field AS point_field FROM Foo;
30 | ```
31 | 
32 | Update the `point_field` column:
33 | 
34 | ```sql
35 | UPDATE Foo SET point_field = POINT(2.0, 1.0) WHERE point_field = POINT(0.3134, 0.156);
36 | ```
37 | 
38 | Select the updated `point_field` column:
39 | 
40 | ```sql
41 | SELECT point_field AS point_field FROM Foo;
42 | ```
43 | 
44 | Delete the record with the specified point value:
45 | 
46 | ```sql
47 | DELETE FROM Foo WHERE point_field = POINT(2.0, 1.0);
48 | ```
49 | 
50 | Casting a string to a `POINT`:
51 | 
52 | ```sql
53 | SELECT CAST('POINT(-71.064544 42.28787)' AS POINT) AS pt;
54 | ```


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/list-map/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "List & Map",
3 |   "position": 4,
4 |   "collapsed": true
5 | }
6 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/list-map/concat.md:
--------------------------------------------------------------------------------
 1 | # CONCAT
 2 | 
 3 | The `CONCAT` function is used to concatenate two or more list values together.
 4 | 
 5 | ## Syntax
 6 | 
 7 | ```sql
 8 | CONCAT(list_value1, list_value2, ...)
 9 | ```
10 | 
11 | **Parameters:**
12 | 
13 | - `list_value1`, `list_value2`, ...: List values that will be concatenated.
14 | 
15 | ## Examples
16 | 
17 | ### Example: CONCAT two lists
18 | 
19 | ```sql
20 | SELECT CONCAT(
21 |   CAST('[1, 2, 3]' AS LIST),
22 |   CAST('["one", "two", "three"]' AS LIST)
23 | ) AS myconcat;
24 | ```
25 | 
26 | **Result:**
27 | 
28 | | myconcat                            |
29 | |-------------------------------------|
30 | | [1, 2, 3, "one", "two", "three"]    |
31 | 
32 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/list-map/dedup.md:
--------------------------------------------------------------------------------
 1 | # DEDUP
 2 | 
 3 | `DEDUP` removes duplicate elements from a list while preserving order.
 4 | 
 5 | ## Syntax
 6 | 
 7 | ```sql
 8 | DEDUP(list)
 9 | ```
10 | 
11 | ## Parameters
12 | 
13 | - `list` – List value to process.
14 | 
15 | ## Examples
16 | 
17 | ```sql
18 | SELECT DEDUP(CAST('[1, 2, 3, 3, 4, 5, 5]' AS LIST));
19 | ```
20 | 
21 | This returns `[1, 2, 3, 4, 5]`.
22 | 
23 | ## Notes
24 | 
25 | A non-list argument results in an error.
26 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/list-map/entries.md:
--------------------------------------------------------------------------------
 1 | # ENTRIES
 2 | 
 3 | `ENTRIES` converts a map into a list of key–value pairs.
 4 | 
 5 | ## Syntax
 6 | 
 7 | ```sql
 8 | ENTRIES(map)
 9 | ```
10 | 
11 | ## Parameters
12 | 
13 | - `map` – Map expression to convert.
14 | 
15 | ## Examples
16 | 
17 | ```sql
18 | SELECT ENTRIES(CAST('{"name":"GlueSQL"}' AS MAP));
19 | ```
20 | 
21 | This returns `[["name", "GlueSQL"]]`.
22 | 
23 | ## Notes
24 | 
25 | `ENTRIES` requires a map argument.
26 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/list-map/is-empty.md:
--------------------------------------------------------------------------------
 1 | # IS_EMPTY
 2 | 
 3 | `IS_EMPTY` checks whether a list or map contains no elements.
 4 | 
 5 | ## Syntax
 6 | 
 7 | ```sql
 8 | IS_EMPTY(value)
 9 | ```
10 | 
11 | ## Parameters
12 | 
13 | - `value` – List or map expression.
14 | 
15 | ## Examples
16 | 
17 | ```sql
18 | SELECT IS_EMPTY(CAST('[]' AS LIST));    -- true
19 | SELECT IS_EMPTY(CAST('{"a":1}' AS MAP)); -- false
20 | ```
21 | 
22 | ## Notes
23 | 
24 | Using other data types results in an error.
25 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/list-map/keys.md:
--------------------------------------------------------------------------------
 1 | # KEYS
 2 | 
 3 | `KEYS` returns the keys of a map as a list.
 4 | 
 5 | ## Syntax
 6 | 
 7 | ```sql
 8 | KEYS(map)
 9 | ```
10 | 
11 | ## Parameters
12 | 
13 | - `map` – Map expression.
14 | 
15 | ## Examples
16 | 
17 | ```sql
18 | SELECT KEYS(CAST('{"id":1, "name":"alice"}' AS MAP));
19 | ```
20 | 
21 | This returns `["id", "name"]`.
22 | 
23 | ## Notes
24 | 
25 | A non-map value will cause an error.
26 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/list-map/length.md:
--------------------------------------------------------------------------------
 1 | # LENGTH
 2 | 
 3 | `LENGTH` returns the number of elements in a list or map, or the number of characters in a string.
 4 | 
 5 | ## Syntax
 6 | 
 7 | ```sql
 8 | LENGTH(value)
 9 | ```
10 | 
11 | ## Parameters
12 | 
13 | - `value` – List, map or string expression.
14 | 
15 | ## Examples
16 | 
17 | ```sql
18 | SELECT LENGTH('Hello.');       -- returns 6
19 | SELECT LENGTH(CAST('[1,2,3]' AS LIST));  -- returns 3
20 | SELECT LENGTH(CAST('{"a":1, "b":5}' AS MAP)); -- returns 2
21 | ```
22 | 
23 | ## Notes
24 | 
25 | If `value` is `NULL` the result is `NULL`.
26 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/list-map/skip.md:
--------------------------------------------------------------------------------
 1 | # SKIP
 2 | 
 3 | `SKIP` drops the first N elements from a list and returns the remaining values.
 4 | 
 5 | ## Syntax
 6 | 
 7 | ```sql
 8 | SKIP(list, count)
 9 | ```
10 | 
11 | ## Parameters
12 | 
13 | - `list` – List value.
14 | - `count` – Number of elements to drop. Must be a non‑negative integer.
15 | 
16 | ## Examples
17 | 
18 | ```sql
19 | SELECT SKIP(CAST('[1,2,3,4,5]' AS LIST), 2);
20 | ```
21 | 
22 | This returns `[3, 4, 5]`.
23 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/list-map/sort.md:
--------------------------------------------------------------------------------
 1 | # SORT
 2 | 
 3 | `SORT` orders the elements of a list.
 4 | 
 5 | ## Syntax
 6 | 
 7 | ```sql
 8 | SORT(list [, 'ASC' | 'DESC'])
 9 | ```
10 | 
11 | ## Parameters
12 | 
13 | - `list` – List to sort.
14 | - Optional sort order `'ASC'` (default) or `'DESC'`.
15 | 
16 | ## Examples
17 | 
18 | ```sql
19 | SELECT SORT(CAST('[3,1,4,2]' AS LIST));         -- [1,2,3,4]
20 | SELECT SORT(CAST('[3,1,4,2]' AS LIST), 'DESC'); -- [4,3,2,1]
21 | ```
22 | 
23 | ## Notes
24 | 
25 | Non-comparable values or invalid order strings will produce an error.
26 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/list-map/take.md:
--------------------------------------------------------------------------------
 1 | # TAKE
 2 | 
 3 | `TAKE` returns the first N elements from a list.
 4 | 
 5 | ## Syntax
 6 | 
 7 | ```sql
 8 | TAKE(list, count)
 9 | ```
10 | 
11 | ## Parameters
12 | 
13 | - `list` – List value.
14 | - `count` – Number of elements to take. Must be a non‑negative integer.
15 | 
16 | ## Examples
17 | 
18 | ```sql
19 | SELECT TAKE(CAST('[1,2,3,4,5]' AS LIST), 3);
20 | ```
21 | 
22 | This returns `[1, 2, 3]`.
23 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/list-map/values.md:
--------------------------------------------------------------------------------
 1 | # VALUES
 2 | 
 3 | `VALUES` returns the values of a map as a list.
 4 | 
 5 | ## Syntax
 6 | 
 7 | ```sql
 8 | VALUES(map)
 9 | ```
10 | 
11 | ## Parameters
12 | 
13 | - `map` – Map expression.
14 | 
15 | ## Examples
16 | 
17 | ```sql
18 | SELECT VALUES(CAST('{"id":1, "name":"alice"}' AS MAP));
19 | ```
20 | 
21 | This returns `[1, "alice"]`.
22 | 
23 | ## Notes
24 | 
25 | A non-map value will cause an error.
26 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/math/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "Math",
3 |   "position": 2,
4 |   "collapsed": true
5 | }
6 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/math/div.md:
--------------------------------------------------------------------------------
 1 | # DIV
 2 | 
 3 | The `DIV` function is used to perform integer division. It takes two arguments (a dividend and a divisor) and returns the integer quotient of the division operation. Both dividend and divisor can be FLOAT or INTEGER type. The return type of the function is INTEGER.
 4 | 
 5 | ## Example
 6 | The following example demonstrates the usage of the `DIV` function in a SQL query:
 7 | 
 8 | ```sql
 9 | CREATE TABLE FloatDiv (
10 |     dividend FLOAT DEFAULT DIV(30, 11),
11 |     divisor FLOAT DEFAULT DIV(3, 2)
12 | );
13 | 
14 | INSERT INTO FloatDiv (dividend, divisor) VALUES (12.0, 3.0), (12.34, 56.78), (-12.3, 4.0);
15 | 
16 | SELECT DIV(dividend, divisor) FROM FloatDiv;
17 | ```
18 | 
19 | This will return the following result:
20 | 
21 | ```
22 | DIV(dividend, divisor)
23 | 4
24 | 0
25 | -4
26 | ```
27 | 
28 | ## Errors
29 | 1. If the divisor is zero, a `DivisorShouldNotBeZero` error will be raised.
30 | 2. If either of the arguments is not of FLOAT or INTEGER type, a `FunctionRequiresFloatOrIntegerValue` error will be raised.
31 | 3. If the number of arguments provided to the function is not equal to 2, a `FunctionArgsLengthNotMatching` error will be raised.


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/math/exp.md:
--------------------------------------------------------------------------------
 1 | # EXP
 2 | 
 3 | The `EXP` function is used to calculate the exponential value of a number. It takes a single FLOAT or INTEGER argument and returns a FLOAT value representing the exponential value of the given number.
 4 | 
 5 | ## Example
 6 | The following example demonstrates the usage of the `EXP` function in a SQL query:
 7 | 
 8 | ```sql
 9 | SELECT
10 |     EXP(2.0) as exp1,
11 |     EXP(5.5) as exp2;
12 | ```
13 | 
14 | This will return the following result:
15 | 
16 | ```
17 | exp1           | exp2
18 | ---------------+-------------------
19 | 2.0_f64.exp()  | 5.5_f64.exp()
20 | ```
21 | 
22 | ## Errors
23 | 1. If the argument is not of FLOAT or INTEGER type, a `FunctionRequiresFloatValue` error will be raised.
24 | 2. If the number of arguments provided to the function is not equal to 1, a `FunctionArgsLengthNotMatching` error will be raised.


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/math/gcd.md:
--------------------------------------------------------------------------------
 1 | # GCD
 2 | 
 3 | The `GCD` function is used to find the greatest common divisor (GCD) of two integers. It takes two INTEGER arguments and returns an INTEGER value representing the greatest common divisor of the given integers.
 4 | 
 5 | ## Example
 6 | The following example demonstrates the usage of the `GCD` function in a SQL query:
 7 | 
 8 | ```sql
 9 | CREATE TABLE GcdI64 (
10 |     left INTEGER NULL DEFAULT GCD(3, 4),
11 |     right INTEGER NULL
12 | );
13 | 
14 | INSERT INTO GcdI64 VALUES (0, 3), (2, 4), (6, 8), (3, 5), (1, NULL), (NULL, 1);
15 | 
16 | SELECT GCD(left, right) AS test FROM GcdI64;
17 | ```
18 | 
19 | This will return the following result:
20 | 
21 | ```
22 | test
23 | 3
24 | 2
25 | 2
26 | 1
27 | NULL
28 | NULL
29 | ```
30 | 
31 | ## Errors
32 | 1. If either of the arguments is not of INTEGER type, a `FunctionRequiresIntegerValue` error will be raised.
33 | 2. If the number of arguments provided to the function is not equal to 2, a `FunctionArgsLengthNotMatching` error will be raised.
34 | 3. If either of the arguments is the minimum i64 value (`-9223372036854775808`), an overflow occurs when attempting to take the absolute value. In this case, a `GcdLcmOverflowError` is raised.


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/math/ln.md:
--------------------------------------------------------------------------------
 1 | # LN
 2 | 
 3 | The `LN` function is used to calculate the natural logarithm (base `e`) of a number. It takes a single FLOAT or INTEGER argument and returns a FLOAT value representing the natural logarithm of the given number.
 4 | 
 5 | ## Example
 6 | The following example demonstrates the usage of the `LN` function in a SQL query:
 7 | 
 8 | ```sql
 9 | CREATE TABLE SingleItem (id INTEGER DEFAULT LN(10));
10 | 
11 | INSERT INTO SingleItem VALUES (0);
12 | 
13 | SELECT
14 |     LN(64.0) as ln1,
15 |     LN(0.04) as ln2
16 | FROM SingleItem;
17 | ```
18 | 
19 | This will return the following result:
20 | 
21 | ```
22 | ln1     | ln2
23 | --------+-------------------
24 | 4.1589  | -3.2189
25 | ```
26 | 
27 | ## Errors
28 | 1. If the argument is not of FLOAT or INTEGER type, a `FunctionRequiresFloatValue` error will be raised.
29 | 2. If the number of arguments provided to the function is not equal to 1, a `FunctionArgsLengthNotMatching` error will be raised.


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/math/log.md:
--------------------------------------------------------------------------------
 1 | # LOG
 2 | 
 3 | The `LOG` function calculates the logarithm of a number with a specified base. It takes two FLOAT or INTEGER arguments and returns a FLOAT value representing the logarithm of the first argument with the base specified by the second argument.
 4 | 
 5 | ## Example
 6 | The following example demonstrates the usage of the `LOG` function in a SQL query:
 7 | 
 8 | ```sql
 9 | CREATE TABLE SingleItem (id INTEGER DEFAULT LOG(2, 64));
10 | 
11 | INSERT INTO SingleItem VALUES (0);
12 | 
13 | SELECT
14 |     LOG(64.0, 2.0) as log_1,
15 |     LOG(0.04, 10.0) as log_2
16 | FROM SingleItem;
17 | ```
18 | 
19 | This will return the following result:
20 | 
21 | ```
22 | log_1 | log_2
23 | ------+-------------------
24 | 6.0   | -1.39794
25 | ```
26 | 
27 | ## Errors
28 | 1. If either of the arguments is not of FLOAT or INTEGER type, a `FunctionRequiresFloatValue` error will be raised.
29 | 2. If the number of arguments provided to the function is not equal to 2, a `FunctionArgsLengthNotMatching` error will be raised.


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/math/log10.md:
--------------------------------------------------------------------------------
 1 | # LOG10
 2 | 
 3 | The `LOG10` function is used to calculate the base-10 logarithm of a number. It takes a single FLOAT or INTEGER argument and returns a FLOAT value representing the base-10 logarithm of the given number.
 4 | 
 5 | ## Example
 6 | The following example demonstrates the usage of the `LOG10` function in a SQL query:
 7 | 
 8 | ```sql
 9 | CREATE TABLE SingleItem (id INTEGER DEFAULT LOG10(100));
10 | 
11 | INSERT INTO SingleItem VALUES (0);
12 | 
13 | SELECT
14 |     LOG10(64.0) as log10_1,
15 |     LOG10(0.04) as log10_2
16 | FROM SingleItem;
17 | ```
18 | 
19 | This will return the following result:
20 | 
21 | ```
22 | log10_1 | log10_2
23 | --------+-------------------
24 | 1.8062  | -1.3979
25 | ```
26 | 
27 | ## Errors
28 | 1. If the argument is not of FLOAT or INTEGER type, a `FunctionRequiresFloatValue` error will be raised.
29 | 2. If the number of arguments provided to the function is not equal to 1, a `FunctionArgsLengthNotMatching` error will be raised.


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/math/log2.md:
--------------------------------------------------------------------------------
 1 | # LOG2
 2 | 
 3 | The `LOG2` function is used to calculate the base-2 logarithm of a number. It takes a single FLOAT or INTEGER argument and returns a FLOAT value representing the base-2 logarithm of the given number.
 4 | 
 5 | ## Example
 6 | The following example demonstrates the usage of the `LOG2` function in a SQL query:
 7 | 
 8 | ```sql
 9 | CREATE TABLE SingleItem (id INTEGER DEFAULT LOG2(1024));
10 | 
11 | INSERT INTO SingleItem VALUES (0);
12 | 
13 | SELECT
14 |     LOG2(64.0) as log2_1,
15 |     LOG2(0.04) as log2_2
16 | FROM SingleItem;
17 | ```
18 | 
19 | This will return the following result:
20 | 
21 | ```
22 | log2_1 | log2_2
23 | -------+-------------------
24 | 6.0    | -4.5850
25 | ```
26 | 
27 | ## Errors
28 | 1. If the argument is not of FLOAT or INTEGER type, a `FunctionRequiresFloatValue` error will be raised.
29 | 2. If the number of arguments provided to the function is not equal to 1, a `FunctionArgsLengthNotMatching` error will be raised.


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/math/mod.md:
--------------------------------------------------------------------------------
 1 | # MOD
 2 | 
 3 | The `MOD` function is used to calculate the remainder of a division operation. It takes two arguments (a dividend and a divisor) and returns the remainder of the division operation. Both dividend and divisor can be FLOAT or INTEGER type. The return type of the function is FLOAT.
 4 | 
 5 | ## Example
 6 | The following example demonstrates the usage of the `MOD` function in a SQL query:
 7 | 
 8 | ```sql
 9 | CREATE TABLE FloatDiv (
10 |     dividend FLOAT DEFAULT MOD(30, 11),
11 |     divisor FLOAT DEFAULT DIV(3, 2)
12 | );
13 | 
14 | INSERT INTO FloatDiv (dividend, divisor) VALUES (12.0, 3.0), (12.34, 56.78), (-12.3, 4.0);
15 | 
16 | SELECT MOD(dividend, divisor) FROM FloatDiv;
17 | ```
18 | 
19 | This will return the following result:
20 | 
21 | ```
22 | MOD(dividend, divisor)
23 | 0.0
24 | 12.34
25 | -0.3
26 | ```
27 | 
28 | ## Errors
29 | 1. If the divisor is zero, a `DivisorShouldNotBeZero` error will be raised.
30 | 2. If either of the arguments is not of FLOAT or INTEGER type, a `FunctionRequiresFloatOrIntegerValue` error will be raised.
31 | 3. If the number of arguments provided to the function is not equal to 2, a `FunctionArgsLengthNotMatching` error will be raised.


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/math/pi.md:
--------------------------------------------------------------------------------
 1 | # PI
 2 | 
 3 | The `PI` function is used to retrieve the mathematical constant π (pi), which is approximately 3.141592653589793. The function takes no arguments.
 4 | 
 5 | ## Syntax
 6 | 
 7 | ```sql
 8 | PI()
 9 | ```
10 | 
11 | ## Examples
12 | 
13 | Let's consider a table named `SingleItem` with the following schema:
14 | 
15 | ```sql
16 | CREATE TABLE SingleItem (id FLOAT);
17 | ```
18 | 
19 | Insert a row into the `SingleItem` table:
20 | 
21 | ```sql
22 | INSERT INTO SingleItem VALUES (0);
23 | ```
24 | 
25 | ### Example 1: Using PI function
26 | 
27 | ```sql
28 | SELECT PI() as pi FROM SingleItem;
29 | ```
30 | 
31 | Result:
32 | 
33 | ```
34 |         pi
35 | ----------------
36 | 3.141592653589793
37 | ```
38 | 
39 | ## Errors
40 | 
41 | The `PI` function expects no arguments. Providing any arguments will result in an error.
42 | 
43 | ### Example 2: Using PI with an argument
44 | 
45 | ```sql
46 | SELECT PI(0) as pi FROM SingleItem;
47 | ```
48 | 
49 | Error: Function expects 0 arguments, but 1 was provided.


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/math/sqrt.md:
--------------------------------------------------------------------------------
 1 | # SQRT
 2 | 
 3 | The `SQRT` function is used to calculate the square root of a number. It takes one argument, which must be of the FLOAT type. The result will also be of the FLOAT type.
 4 | 
 5 | ## Syntax
 6 | 
 7 | ```sql
 8 | SQRT(number)
 9 | ```
10 | 
11 | ## Examples
12 | 
13 | 1. Using the `SQRT` function:
14 | 
15 | ```sql
16 | SELECT SQRT(2.0) as sqrt_1;
17 | -- Result: 1.4142135623730951
18 | ```
19 | 
20 | 2. Using the `SQRT` function with a decimal:
21 | 
22 | ```sql
23 | SELECT SQRT(0.07) as sqrt_2;
24 | -- Result: 0.2645751311064591
25 | ```
26 | 
27 | 3. Using the `SQRT` function with an integer:
28 | 
29 | ```sql
30 | SELECT SQRT(32) as sqrt_with_int;
31 | -- Result: 5.656854249492381
32 | ```
33 | 
34 | 4. Using the `SQRT` function with zero:
35 | 
36 | ```sql
37 | SELECT SQRT(0) as sqrt_with_zero;
38 | -- Result: 0.0
39 | ```
40 | 
41 | 5. Using the `SQRT` function with NULL:
42 | 
43 | ```sql
44 | SELECT SQRT(NULL) AS sqrt;
45 | -- Result: NULL
46 | ```
47 | 
48 | ## Error Cases
49 | 
50 | 1. The `SQRT` function requires the argument to be of FLOAT type:
51 | 
52 | ```sql
53 | SELECT SQRT('string') AS sqrt;
54 | -- Error: SqrtOnNonNumeric("string")
55 | ```


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/others/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "Others",
3 |   "position": 6,
4 |   "collapsed": true
5 | }
6 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/others/coalesce.md:
--------------------------------------------------------------------------------
 1 | # COALESCE
 2 | 
 3 | The `COALESCE` function returns the first non-`NULL` value from the list of expressions.
 4 | 
 5 | ## Syntax
 6 | 
 7 | ```sql
 8 | COALESCE(expr1, expr2, ...)
 9 | ```
10 | 
11 | ## Parameters
12 | 
13 | - `expr1`, `expr2`, ... – Expressions evaluated in order. At least one expression must be provided.
14 | 
15 | ## Examples
16 | 
17 | ```sql
18 | CREATE TABLE example (a INT NULL, b INT NULL);
19 | INSERT INTO example VALUES (NULL, 2), (3, NULL), (NULL, NULL);
20 | 
21 | SELECT COALESCE(a, b, 0) AS result FROM example;
22 | ```
23 | 
24 | This returns `2`, `3` and `0` for the three rows.
25 | 
26 | ## Notes
27 | 
28 | If all arguments are `NULL`, the result is `NULL`.
29 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/others/greatest.md:
--------------------------------------------------------------------------------
 1 | # GREATEST
 2 | 
 3 | `GREATEST` returns the highest value among the supplied expressions.
 4 | 
 5 | ## Syntax
 6 | 
 7 | ```sql
 8 | GREATEST(expr1, expr2, ...)
 9 | ```
10 | 
11 | ## Parameters
12 | 
13 | - `expr1`, `expr2`, ... – Two or more comparable expressions.
14 | 
15 | ## Examples
16 | 
17 | ```sql
18 | SELECT GREATEST(1, 6, 9, 7, 0, 10) AS result;
19 | ```
20 | 
21 | The query above returns `10`.
22 | 
23 | ## Notes
24 | 
25 | All arguments must be of comparable types. At least two expressions are required.
26 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/text/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "Text",
3 |   "position": 1,
4 |   "collapsed": true
5 | }
6 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/text/lower.md:
--------------------------------------------------------------------------------
 1 | # LOWER
 2 | 
 3 | The `LOWER` function in SQL returns a string in which all alphabetic characters in a specified string are converted to lowercase.
 4 | 
 5 | ## Syntax
 6 | 
 7 | ```sql
 8 | LOWER(string)
 9 | ```
10 | 
11 | ## Parameters
12 | 
13 | - `string`: The original string to convert.
14 | 
15 | ## Return Value
16 | 
17 | The function returns a new string that is the same as the original string, but with all uppercase characters converted to lowercase. Non-alphabetic characters in the string are unaffected.
18 | 
19 | ## Errors
20 | 
21 | - If the `string` argument is not a string, a `FunctionRequiresStringValue` error will be returned.
22 | 
23 | ## Examples
24 | 
25 | Consider a table `Item` created and filled with the following data:
26 | 
27 | ```sql
28 | CREATE TABLE Item (
29 |     name TEXT
30 | );
31 | INSERT INTO Item VALUES ('ABCD'), ('Abcd'), ('abcd');
32 | ```
33 | 
34 | You can use the `LOWER` function to convert all `name` values to lowercase:
35 | 
36 | ```sql
37 | SELECT LOWER(name) AS lower_name FROM Item;
38 | ```
39 | 
40 | This will return:
41 | 
42 | ```
43 | abcd
44 | abcd
45 | abcd
46 | ```
47 | 
48 | Note that the `LOWER` function affects only alphabetic characters. Non-alphabetic characters in the string remain unchanged.


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/text/md5.md:
--------------------------------------------------------------------------------
 1 | # MD5
 2 | 
 3 | `MD5` calculates the MD5 hash of a string.
 4 | 
 5 | ## Syntax
 6 | 
 7 | ```sql
 8 | MD5(text)
 9 | ```
10 | 
11 | ## Parameters
12 | 
13 | - `text` – The string to hash.
14 | 
15 | ## Examples
16 | 
17 | ```sql
18 | SELECT MD5('GlueSQL');
19 | ```
20 | 
21 | This returns `4274ecec96f3ee59b51b168dc6137231`.
22 | 
23 | ## Notes
24 | 
25 | `MD5` requires exactly one argument.
26 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/text/repeat.md:
--------------------------------------------------------------------------------
 1 | # REPEAT
 2 | 
 3 | The `REPEAT` function in SQL is used to repeat a string for a specified number of times.
 4 | 
 5 | ## Syntax
 6 | 
 7 | ```sql
 8 | REPEAT(string, number)
 9 | ```
10 | 
11 | ## Parameters
12 | 
13 | - `string`: The string to be repeated.
14 | - `number`: The number of times to repeat the string. 
15 | 
16 | ## Return Value
17 | 
18 | The function returns a string which is the concatenation of the input string repeated the specified number of times.
19 | 
20 | ## Errors
21 | 
22 | - If the parameters are not in the correct format, a `TranslateError::FunctionArgsLengthNotMatching` error will be returned. This function requires exactly two arguments.
23 | - If either `string` or `number` are not string values, a `EvaluateError::FunctionRequiresStringValue` error will be returned.
24 | 
25 | ## Examples
26 | 
27 | Consider a table `Item` created and filled with the following data:
28 | 
29 | ```sql
30 | CREATE TABLE Item (name TEXT);
31 | INSERT INTO Item VALUES ('hello');
32 | ```
33 | 
34 | You can use the `REPEAT` function to repeat the `name` values:
35 | 
36 | ```sql
37 | SELECT REPEAT(name, 2) AS test FROM Item;
38 | ```
39 | 
40 | This will return:
41 | 
42 | ```
43 | hellohello
44 | ```
45 | 
46 | The 'hello' string is repeated twice as specified by the second parameter to the `REPEAT` function.


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/text/reverse.md:
--------------------------------------------------------------------------------
 1 | # REVERSE
 2 | 
 3 | The `REVERSE` function in SQL is used to reverse a string.
 4 | 
 5 | ## Syntax
 6 | 
 7 | ```sql
 8 | REVERSE(string)
 9 | ```
10 | 
11 | ## Parameters
12 | 
13 | - `string`: The string to be reversed.
14 | 
15 | ## Return Value
16 | 
17 | The function returns a string which is the reverse of the input string.
18 | 
19 | ## Errors
20 | 
21 | If the parameter is not a string value, a `EvaluateError::FunctionRequiresStringValue` error will be returned.
22 | 
23 | ## Examples
24 | 
25 | Consider a table `Item` created and filled with the following data:
26 | 
27 | ```sql
28 | CREATE TABLE Item (name TEXT);
29 | INSERT INTO Item VALUES ('Let''s meet');
30 | ```
31 | 
32 | You can use the `REVERSE` function to reverse the `name` values:
33 | 
34 | ```sql
35 | SELECT REVERSE(name) AS test FROM Item;
36 | ```
37 | 
38 | This will return:
39 | 
40 | ```
41 | teem s'teL
42 | ```
43 | 
44 | The 'Let''s meet' string is reversed as 'teem s'teL'.


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/functions/text/upper.md:
--------------------------------------------------------------------------------
 1 | # UPPER
 2 | 
 3 | The `UPPER` function in SQL converts all lowercase alphabetic characters in a specified string to uppercase.
 4 | 
 5 | ## Syntax
 6 | 
 7 | ```sql
 8 | UPPER(string)
 9 | ```
10 | 
11 | ## Parameters
12 | 
13 | - `string`: The original string to convert.
14 | 
15 | ## Return Value
16 | 
17 | The function returns a new string that is the same as the original string, but with all lowercase characters converted to uppercase. Non-alphabetic characters in the string are unaffected.
18 | 
19 | ## Errors
20 | 
21 | - If the `string` argument is not a string, a `FunctionRequiresStringValue` error will be returned.
22 | 
23 | ## Examples
24 | 
25 | Consider a table `Item` created and filled with the following data:
26 | 
27 | ```sql
28 | CREATE TABLE Item (
29 |     name TEXT
30 | );
31 | INSERT INTO Item VALUES ('abcd'), ('Abcd'), ('ABCD');
32 | ```
33 | 
34 | You can use the `UPPER` function to convert all `name` values to uppercase:
35 | 
36 | ```sql
37 | SELECT UPPER(name) AS upper_name FROM Item;
38 | ```
39 | 
40 | This will return:
41 | 
42 | ```
43 | ABCD
44 | ABCD
45 | ABCD
46 | ```
47 | 
48 | Note that the `UPPER` function affects only alphabetic characters. Non-alphabetic characters in the string remain unchanged.


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/statements/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "Statements",
3 |   "position": 1,
4 |   "collapsed": true
5 | }
6 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/statements/data-definition/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "Data definition",
3 |   "position": 2
4 | }
5 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/statements/data-manipulation/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "Data manipulation",
3 |   "position": 3
4 | }
5 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/statements/metadata/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "Metadata",
3 |   "position": 5
4 | }
5 | 


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/statements/metadata/show-tables.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | sidebar_position: 1
 3 | ---
 4 | 
 5 | # SHOW TABLES
 6 | 
 7 | The `SHOW TABLES` statement in GlueSQL is used to display a list of tables available in the database. This statement is useful when you want to inspect the current structure of your database or when you want to manage multiple tables.
 8 | 
 9 | ## Syntax
10 | 
11 | ```sql
12 | SHOW TABLES;
13 | ```
14 | 
15 | ## Example
16 | 
17 | Consider the following example where we create a few tables and then use the `SHOW TABLES` statement to list them:
18 | 
19 | ```sql
20 | CREATE TABLE Foo (id INTEGER, name TEXT NULL, type TEXT NULL);
21 | CREATE TABLE Zoo (id INTEGER);
22 | CREATE TABLE Bar (id INTEGER, name TEXT NULL);
23 | 
24 | SHOW TABLES;
25 | ```
26 | 
27 | The output of the `SHOW TABLES` statement will be:
28 | 
29 | ```
30 | Bar
31 | Foo
32 | Zoo
33 | ```
34 | 
35 | The tables are listed in alphabetical order.


--------------------------------------------------------------------------------
/docs/docs/sql-syntax/statements/querying/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "Querying",
3 |   "position": 1
4 | }
5 | 


--------------------------------------------------------------------------------
/docs/docs/storages/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "Storages",
3 |   "position": 4,
4 |   "collapsed": false
5 | }
6 | 


--------------------------------------------------------------------------------
/docs/docs/storages/developing-custom-storages/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "Developing Custom Storages",
3 |   "position": 3,
4 |   "collapsed": true
5 | }
6 | 


--------------------------------------------------------------------------------
/docs/docs/storages/developing-custom-storages/store-traits/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "Understanding Store traits",
3 |   "position": 2,
4 |   "collapsed": true
5 | }
6 | 


--------------------------------------------------------------------------------
/docs/docs/storages/developing-custom-storages/store-traits/custom-function-mut.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | sidebar_position: 6
 3 | ---
 4 | 
 5 | # CustomFunctionMut
 6 | 
 7 | By implementing both the `CustomFunction` and `CustomFunctionMut` traits, users can create, use, and delete user-level custom functions. Although GlueSQL plans to continuously add various functions, users may still find them insufficient. In such cases, users can create their own user-level custom functions to supplement the built-in functions. Additionally, if there are repetitive business logic codes, they can be stored as custom functions.
 8 | 
 9 | Example:
10 | 
11 | ```sql
12 | CREATE FUNCTION ADD_ONE (n INT, x INT DEFAULT 1) RETURN n + x;
13 | 
14 | SELECT ADD_ONE(10) AS test;
15 | 
16 | DROP FUNCTION ADD_ONE;
17 | ```
18 | 
19 | There are two methods available:
20 | 
21 | 1. `insert_function`: This method inserts a new custom function into the storage system.
22 | 
23 | 2. `delete_function`: This method deletes a custom function from the storage system using the provided function name.
24 | 
25 | ```rust
26 | #[async_trait(?Send)]
27 | pub trait CustomFunctionMut {
28 |     async fn insert_function(&mut self, _func: StructCustomFunction) -> Result<()>;
29 | 
30 |     async fn delete_function(&mut self, _func_name: &str) -> Result<()>;
31 | }
32 | ```


--------------------------------------------------------------------------------
/docs/docs/storages/developing-custom-storages/store-traits/custom-function.md:
--------------------------------------------------------------------------------
 1 | ---
 2 | sidebar_position: 5
 3 | ---
 4 | 
 5 | # CustomFunction
 6 | 
 7 | The `CustomFunction` trait is an optional trait for supporting user-level custom functions. Through the `CustomFunction` trait, you can retrieve custom functions stored in the storage system. You can choose to implement the `CustomFunction` trait alone or together with the `CustomFunctionMut` trait.
 8 | 
 9 | In some cases, you might want to provide storage-specific functions pre-built and separately available for each storage system. In such cases, you can implement the `CustomFunction` trait and create additional functions stored in advance when using it. To achieve this, the `CustomFunction` and `CustomFunctionMut` traits are provided separately for implementation.
10 | 
11 | There are two methods available:
12 | 
13 | 1. `fetch_function`: This method retrieves a custom function from the storage system using the provided function name.
14 | 
15 | 2. `fetch_all_functions`: This method retrieves all custom functions stored in the storage system.
16 | 
17 | ```rust
18 | #[async_trait(?Send)]
19 | pub trait CustomFunction {
20 |     async fn fetch_function(&self, _func_name: &str) -> Result<Option<&StructCustomFunction>>;
21 | 
22 |     async fn fetch_all_functions(&self) -> Result<Vec<&StructCustomFunction>>;
23 | }
24 | ```
25 | 


--------------------------------------------------------------------------------
/docs/docs/storages/supported-storages/_category_.json:
--------------------------------------------------------------------------------
1 | {
2 |   "label": "Supported Storages",
3 |   "position": 2,
4 |   "collapsed": true
5 | }
6 | 


--------------------------------------------------------------------------------
/docs/docs/storages/supported-storages/csv-storage.md:
--------------------------------------------------------------------------------
 1 | # CSV Storage
 2 | 
 3 | Introducing `CSVStorage`: a utility to process *.csv files, enabling SQL-like query operations such as SELECT, INSERT, and UPDATE.
 4 | 
 5 | ## Key Features:
 6 | 
 7 | 1. **SQL Queries on CSV**: Directly parse and operate on *.csv files using familiar SQL query operations.
 8 | 
 9 | 2. **Optional Schema Support**: An associated schema can be provided for each CSV file. For instance, for a data file named `Book.csv`, its corresponding schema file should be named `Book.sql`.
10 |    - If an associated schema file is found, it will be read and applied.
11 |    - In the absence of a schema file, the first row of the data file will be treated as column headers and all column types will default to TEXT.
12 | 
13 | 3. **Type Info File for Schemaless Data**: An auxiliary types file (`*.types.csv`) can be used to support data type recognition for schemaless data.
14 |    - For a CSV data file named `Book.csv`, its corresponding types file will be `Book.types.csv`.
15 |    - The types file will have a 1:1 mapping with the CSV data file entries, specifying the data type for each entry in alignment with the GlueSQL conventions.
16 | 


--------------------------------------------------------------------------------
/docs/docs/storages/supported-storages/redb-storage.md:
--------------------------------------------------------------------------------
 1 | # Redb Storage
 2 | 
 3 | RedbStorage allows GlueSQL to persist data using the [redb](https://github.com/cberner/redb) embedded key-value database. It provides ACID transactions, fast single-file access, and a stable API.
 4 | 
 5 | RedbStorage implements GlueSQL's `Store`, `StoreMut`, and `Transaction` traits.
 6 | ## Example
 7 | 
 8 | ```rust
 9 | use gluesql::{prelude::Glue, redb_storage::RedbStorage};
10 | 
11 | #[tokio::main]
12 | async fn main() {
13 |     let storage = RedbStorage::new("data/my_db.redb").unwrap();
14 |     let mut glue = Glue::new(storage);
15 | 
16 |     let sql = "
17 |         CREATE TABLE Foo (id INT, name TEXT);
18 |         INSERT INTO Foo VALUES (1, 'Alice'), (2, 'Bob');
19 |         SELECT * FROM Foo;
20 |     ";
21 | 
22 |     let payloads = glue.execute(sql).await.unwrap();
23 |     println!("{:#?}", payloads);
24 | }
25 | ```
26 | 
27 | ## Things to keep in mind
28 | 
29 | - Nested transactions are not supported.
30 | - Only one RedbStorage instance should open the same database file at a time.
31 | 
32 | RedbStorage gives you an embedded, serverless database that integrates seamlessly with GlueSQL. Use `RedbStorage::new` to open or create a database file and execute SQL through `Glue`.
33 | 


--------------------------------------------------------------------------------
/docs/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "docs",
 3 |   "version": "0.0.0",
 4 |   "private": true,
 5 |   "scripts": {
 6 |     "docusaurus": "docusaurus",
 7 |     "start": "docusaurus start",
 8 |     "start:blog": "GLUESQL_DOC_TYPE=blog docusaurus start",
 9 |     "build": "docusaurus build",
10 |     "build:blog": "GLUESQL_DOC_TYPE=blog docusaurus build",
11 |     "swizzle": "docusaurus swizzle",
12 |     "deploy": "docusaurus deploy",
13 |     "clear": "docusaurus clear",
14 |     "serve": "docusaurus serve",
15 |     "write-translations": "docusaurus write-translations",
16 |     "write-heading-ids": "docusaurus write-heading-ids"
17 |   },
18 |   "dependencies": {
19 |     "@docusaurus/core": "^2.4.3",
20 |     "@docusaurus/preset-classic": "^2.4.3",
21 |     "@mdx-js/react": "^1.6.22",
22 |     "clsx": "^1.2.1",
23 |     "prism-react-renderer": "^1.3.5",
24 |     "react": "^17.0.2",
25 |     "react-dom": "^17.0.2"
26 |   },
27 |   "devDependencies": {
28 |     "@docusaurus/module-type-aliases": "^2.4.3"
29 |   },
30 |   "browserslist": {
31 |     "production": [
32 |       ">0.5%",
33 |       "not dead",
34 |       "not op_mini all"
35 |     ],
36 |     "development": [
37 |       "last 1 chrome version",
38 |       "last 1 firefox version",
39 |       "last 1 safari version"
40 |     ]
41 |   },
42 |   "engines": {
43 |     "node": ">=16.14"
44 |   }
45 | }
46 | 


--------------------------------------------------------------------------------
/docs/sidebars.js:
--------------------------------------------------------------------------------
 1 | /**
 2 |  * Creating a sidebar enables you to:
 3 |  - create an ordered group of docs
 4 |  - render a sidebar for each doc of that group
 5 |  - provide next/previous navigation
 6 | 
 7 |  The sidebars can be generated from the filesystem, or explicitly defined here.
 8 | 
 9 |  Create as many sidebars as you want.
10 |  */
11 | 
12 | // @ts-check
13 | 
14 | /** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
15 | const sidebars = {
16 |   autoSidebar: [{type: 'autogenerated', dirName: '.'}],
17 | };
18 | 
19 | module.exports = sidebars;
20 | 


--------------------------------------------------------------------------------
/docs/static/.nojekyll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gluesql/gluesql/78b51bf019fc8c29a51182e464357bd76df35700/docs/static/.nojekyll


--------------------------------------------------------------------------------
/docs/static/img/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gluesql/gluesql/78b51bf019fc8c29a51182e464357bd76df35700/docs/static/img/favicon.ico


--------------------------------------------------------------------------------
/docs/static/img/gluesql.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gluesql/gluesql/78b51bf019fc8c29a51182e464357bd76df35700/docs/static/img/gluesql.jpg


--------------------------------------------------------------------------------
/pkg/javascript/.gitignore:
--------------------------------------------------------------------------------
 1 | dist_web/
 2 | dist_nodejs/
 3 | dist/
 4 | node_modules/
 5 | bin/
 6 | pkg/
 7 | target/
 8 | **/*.rs.bk
 9 | wasm-pack.log
10 | Cargo.lock
11 | package-lock.json
12 | 


--------------------------------------------------------------------------------
/pkg/javascript/Cargo.toml:
--------------------------------------------------------------------------------
 1 | [package]
 2 | name = "gluesql-js"
 3 | authors = ["Taehoon Moon <taehoon.moon@outlook.com>"]
 4 | version.workspace = true
 5 | edition.workspace = true
 6 | description.workspace = true
 7 | license.workspace = true
 8 | repository.workspace = true
 9 | documentation.workspace = true
10 | readme = false
11 | 
12 | [lib]
13 | crate-type = ["cdylib", "rlib"]
14 | 
15 | [features]
16 | default = ["console_error_panic_hook"]
17 | 
18 | nodejs = ["console_error_panic_hook"]
19 | 
20 | [dependencies]
21 | gluesql-core.workspace = true
22 | gluesql_memory_storage.workspace = true
23 | gluesql-web-storage.workspace = true
24 | gluesql-idb-storage.workspace = true
25 | gluesql-composite-storage.workspace = true
26 | 
27 | wasm-bindgen = { version = "0.2.100" }
28 | wasm-bindgen-futures = "0.4.29"
29 | js-sys = "0.3"
30 | 
31 | serde = "1"
32 | serde_json = "1"
33 | gloo-utils = { version = "0.1.6", features = ["serde"] }
34 | 
35 | # The `console_error_panic_hook` crate provides better debugging of panics by
36 | # logging them with `console.error`. This is great for development, but requires
37 | # all the `std::fmt` and `std::panicking` infrastructure, so isn't great for
38 | # code size when deploying.
39 | console_error_panic_hook = { version = "0.1.6", optional = true }
40 | 
41 | [dev-dependencies]
42 | test-suite.workspace = true
43 | 
44 | wasm-bindgen-test = "0.3.50"
45 | async-trait = "0.1"
46 | 


--------------------------------------------------------------------------------
/pkg/javascript/DEVELOPMENT.md:
--------------------------------------------------------------------------------
 1 | ## 🚴 Usage
 2 | 
 3 | ### Setup
 4 | Enable `opt-level = "s"` option
 5 | ```
 6 | # ref. https://github.com/rustwasm/wasm-pack/issues/1111
 7 | # enable this only for gluesql-js build
 8 | [profile.release]
 9 | opt-level = "s"
10 | ```
11 | 
12 | ### Build
13 | ```
14 | # browser module, webpack and rollup
15 | wasm-pack build --no-pack --target web --no-typescript --release --out-dir ./dist_web
16 | 
17 | # nodejs
18 | wasm-pack build --no-pack --target nodejs --no-typescript --release --out-dir ./dist_nodejs -- --no-default-features --features nodejs
19 | ```
20 | 
21 | ### 🔬 Test in Headless Browsers with `wasm-pack test`
22 | ```
23 | wasm-pack test --headless --firefox --chrome
24 | ```
25 | 


--------------------------------------------------------------------------------
/pkg/javascript/examples/nodejs/main.js:
--------------------------------------------------------------------------------
 1 | const { gluesql } = require('../../gluesql.node.js');
 2 | const db = gluesql();
 3 | 
 4 | async function run() {
 5 |   await db.query(`
 6 |     CREATE TABLE User (id INTEGER, name TEXT);
 7 |     CREATE TABLE Device (name TEXT, userId INTEGER);
 8 |     INSERT INTO User VALUES
 9 |       (1, 'glue'), (2, 'sticky'), (3, 'watt');
10 |     INSERT INTO Device VALUES
11 |       ('Phone', 1), ('Mic', 1), ('Monitor', 3),
12 |       ('Mouse', 2), ('Touchpad', 2);
13 |   `);
14 | 
15 |   let sql;
16 | 
17 |   sql = 'SHOW TABLES;';
18 |   const [{ tables }] = await db.query(sql);
19 |   console.log(`\n[Query]\n${sql}`);
20 |   console.table(tables);
21 | 
22 |   sql = `
23 |     SELECT
24 |       u.name as user,
25 |       d.name as device
26 |     FROM User u
27 |     JOIN Device d ON u.id = d.userId
28 |   `.trim().replace(/[ ]{4}/g, '');
29 |   const [{ rows }] = await db.query(sql);
30 |   console.log(`\n[Query]\n${sql}`);
31 |   console.table(rows);
32 | }
33 | 
34 | run();
35 | 


--------------------------------------------------------------------------------
/pkg/javascript/examples/web/module/README.md:
--------------------------------------------------------------------------------
 1 | ## Guide: Running module example
 2 | 
 3 | ### How to run?
 4 | 0. Before going on, you should build and generate `dist` directory. So go to under `gluesql/gluesql-js/web`. And run these commands
 5 | ```sh
 6 | # install dependencies
 7 | $ yarn
 8 | 
 9 | # build for examples/web/module
10 | $ yarn build:browser
11 | ```
12 | 
13 | 1. Go to under `gluesql/gluesql-js` 
14 | 
15 | 2. Serve files using proper application
16 | There are many http server applications. Here are some examples
17 | 
18 | - [simple-http-server](https://crates.io/crates/simple-http-server)
19 | ```sh
20 | # 1. install
21 | $ cargo install simple-http-server
22 | 
23 | # 2. serve files under `gluesql/gluesql-js`. Now open the browser and go to `http://localhost:3030`
24 | $ simple-http-server --port 3000
25 | 
26 | # 3. navigate to `examples/web/module/index.html`. The url should be `http://localhost:3000/examples/web/module/index.html`
27 | # 4. Check the result
28 | ```
29 | 
30 | - [http-server](https://www.npmjs.com/package/http-server)
31 | ```sh
32 | # 1. install or run
33 | $ npx http-server
34 | 
35 | # Remaining steps are same as `simple-http-server` section guide
36 | ```
37 | 


--------------------------------------------------------------------------------
/pkg/javascript/examples/web/rollup/index.html:
--------------------------------------------------------------------------------
 1 | <!doctype html>
 2 | <html>
 3 |   <head>
 4 |     <meta charset="utf-8">
 5 |     <title>GlueSQL Javascript Rollup example</title>
 6 |     <script src="dist/bundle.js"></script>
 7 |   </head>
 8 | <style>
 9 | body {
10 |   padding: 0;
11 |   margin: 0;
12 | }
13 | 
14 | #box {
15 |   display: flex;
16 |   flex-direction: column;
17 |   padding: 90px 20px 80px 20px;
18 |   background-color: #222;
19 | 
20 |   color: white;
21 | }
22 | 
23 | code {
24 |   margin: 5px;
25 |   padding: 10px;
26 |   font-family: monospace;
27 |   border: 1px solid white;
28 | }
29 | 
30 | </style>
31 |   <body>
32 |     <div id="box">
33 |     </div>
34 |   </body>
35 | </html>
36 | 


--------------------------------------------------------------------------------
/pkg/javascript/examples/web/rollup/main.js:
--------------------------------------------------------------------------------
 1 | import { gluesql } from 'gluesql/gluesql.rollup';
 2 | 
 3 | async function run() {
 4 |   const db = await gluesql();
 5 |   await db.loadIndexedDB();
 6 | 
 7 |   const result = await db.query(`
 8 |     DROP TABLE IF EXISTS Foo, Bar;
 9 |     CREATE TABLE Foo (id INTEGER, name TEXT);
10 |     CREATE TABLE Bar (bar_id INTEGER) ENGINE = indexedDB;
11 |     INSERT INTO Foo VALUES (1, 'hello'), (2, 'world');
12 |     INSERT INTO Bar VALUES (10), (20);
13 |     SELECT *, id as wow_id FROM Foo JOIN Bar;
14 |   `);
15 | 
16 |   for (const item of result) {
17 |     const node = document.createElement('code');
18 | 
19 |     node.innerHTML = `
20 |       type: ${item.type}
21 |       <br>
22 |       ${item.affected ? `affected: ${item.affected}` : ''}
23 |       ${item.rows ? `rows: ${JSON.stringify(item.rows)}` : ''}
24 |     `;
25 | 
26 |     console.log(item);
27 |     document.querySelector('#box').append(node);
28 |   }
29 | }
30 | 
31 | window.onload = run;
32 | 


--------------------------------------------------------------------------------
/pkg/javascript/examples/web/rollup/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "gluesql-js-web-rollup-example",
 3 |   "version": "0.1.0",
 4 |   "description": "GlueSQL javascript rollup example",
 5 |   "main": "index.js",
 6 |   "repository": {
 7 |     "type": "git",
 8 |     "url": "git+https://github.com/gluesql/gluesql.git"
 9 |   },
10 |   "author": "Taehoon Moon",
11 |   "license": "Apache-2.0",
12 |   "bugs": {
13 |     "url": "https://github.com/gluesql/gluesql/issues"
14 |   },
15 |   "homepage": "https://github.com/gluesql/gluesql#readme",
16 |   "scripts": {
17 |     "build": "rollup -c"
18 |   },
19 |   "devDependencies": {
20 |     "@rollup/plugin-node-resolve": "^13.2.1",
21 |     "@rollup/plugin-wasm": "^6.1.3",
22 |     "gluesql": "file:../../..",
23 |     "rollup": "^2.70.2"
24 |   }
25 | }
26 | 


--------------------------------------------------------------------------------
/pkg/javascript/examples/web/rollup/rollup.config.js:
--------------------------------------------------------------------------------
 1 | import resolve from '@rollup/plugin-node-resolve';
 2 | import { wasm } from '@rollup/plugin-wasm';
 3 | 
 4 | export default {
 5 |   input: 'main.js',
 6 |   output: {
 7 |     file: 'dist/bundle.js',
 8 |     format: 'iife',
 9 |   },
10 |   plugins: [
11 |     resolve({ browser: true }),
12 |     wasm({ targetEnv: 'auto-inline' }),
13 |   ],
14 | };
15 | 


--------------------------------------------------------------------------------
/pkg/javascript/examples/web/webpack/index.html:
--------------------------------------------------------------------------------
 1 | <!doctype html>
 2 | <html>
 3 |   <head>
 4 |     <meta charset="utf-8">
 5 |     <title>GlueSQL Javascript Webpack example</title>
 6 |     <script src="dist/bundle.js"></script>
 7 |   </head>
 8 | <style>
 9 | body {
10 |   padding: 0;
11 |   margin: 0;
12 | }
13 | 
14 | #box {
15 |   display: flex;
16 |   flex-direction: column;
17 |   padding: 90px 20px 80px 20px;
18 |   background-color: #222;
19 | 
20 |   color: white;
21 | }
22 | 
23 | code {
24 |   margin: 5px;
25 |   padding: 10px;
26 |   font-family: monospace;
27 |   border: 1px solid white;
28 | }
29 | 
30 | </style>
31 |   <body>
32 |     <div id="box">
33 |     </div>
34 |   </body>
35 | </html>
36 | 


--------------------------------------------------------------------------------
/pkg/javascript/examples/web/webpack/main.js:
--------------------------------------------------------------------------------
 1 | import { gluesql } from 'gluesql';
 2 | 
 3 | async function run() {
 4 |   const db = await gluesql();
 5 |   await db.loadIndexedDB();
 6 | 
 7 |   const result = await db.query(`
 8 |     DROP TABLE IF EXISTS Foo;
 9 |     CREATE TABLE Foo (id INTEGER, name TEXT) ENGINE = indexedDB;
10 |     INSERT INTO Foo VALUES (1, 'hello'), (2, 'world');
11 |     SELECT *, id as wow_id FROM Foo;
12 |   `);
13 | 
14 |   for (const item of result) {
15 |     const node = document.createElement('code');
16 | 
17 |     node.innerHTML = `
18 |       type: ${item.type}
19 |       <br>
20 |       ${item.affected ? `affected: ${item.affected}` : ''}
21 |       ${item.rows ? `rows: ${JSON.stringify(item.rows)}` : ''}
22 |     `;
23 | 
24 |     console.log(item);
25 |     document.querySelector('#box').append(node);
26 |   }
27 | }
28 | 
29 | window.onload = run;
30 | 


--------------------------------------------------------------------------------
/pkg/javascript/examples/web/webpack/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "gluesql-js-web-webpack-example",
 3 |   "version": "0.1.0",
 4 |   "description": "GlueSQL javascript webpack example",
 5 |   "main": "index.js",
 6 |   "repository": {
 7 |     "type": "git",
 8 |     "url": "git+https://github.com/gluesql/gluesql.git"
 9 |   },
10 |   "author": "Taehoon Moon",
11 |   "license": "Apache-2.0",
12 |   "bugs": {
13 |     "url": "https://github.com/gluesql/gluesql/issues"
14 |   },
15 |   "homepage": "https://github.com/gluesql/gluesql#readme",
16 |   "scripts": {
17 |     "build": "yarn upgrade gluesql && webpack"
18 |   },
19 |   "devDependencies": {
20 |     "gluesql": "file:../../..",
21 |     "webpack": "^5.72.0",
22 |     "webpack-cli": "^4.9.2"
23 |   }
24 | }
25 | 


--------------------------------------------------------------------------------
/pkg/javascript/examples/web/webpack/webpack.config.js:
--------------------------------------------------------------------------------
 1 | const path = require('path');
 2 | 
 3 | module.exports = {
 4 |   entry: "./main.js",
 5 |   output: {
 6 |     path: path.resolve(__dirname, "dist"),
 7 |     filename: "bundle.js",
 8 |   },
 9 |   mode: "development"
10 | };
11 | 


--------------------------------------------------------------------------------
/pkg/javascript/gluesql.js:
--------------------------------------------------------------------------------
 1 | import init, { Glue } from './dist_web/gluesql_js.js';
 2 | 
 3 | let loaded = false;
 4 | 
 5 | async function load(module_or_path) {
 6 |   await init(module_or_path);
 7 | 
 8 |   loaded = true;
 9 | }
10 | 
11 | export async function gluesql(module_or_path) {
12 |   if (!loaded) {
13 |     await load(module_or_path);
14 |   }
15 | 
16 |   return new Glue();
17 | }
18 | 


--------------------------------------------------------------------------------
/pkg/javascript/gluesql.node.js:
--------------------------------------------------------------------------------
1 | const { Glue } = require('./dist_nodejs/gluesql_js.js');
2 | 
3 | function gluesql() {
4 |   return new Glue();
5 | }
6 | 
7 | module.exports = { gluesql };
8 | 


--------------------------------------------------------------------------------
/pkg/javascript/gluesql.rollup.js:
--------------------------------------------------------------------------------
 1 | import init, { Glue } from './dist_web/gluesql_js.js';
 2 | import loadDB from './dist_web/gluesql_js_bg.wasm';
 3 | 
 4 | let loaded = false;
 5 | 
 6 | export async function gluesql() {
 7 |   if (!loaded) {
 8 |     const instance = await loadDB();
 9 |     await init(instance);
10 | 
11 |     loaded = true;
12 |   }
13 | 
14 |   return new Glue();
15 | }
16 | 


--------------------------------------------------------------------------------
/pkg/javascript/package.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "name": "gluesql",
 3 |   "version": "0.17.0",
 4 |   "description": "GlueSQL is quite sticky, it attaches to anywhere",
 5 |   "browser": "gluesql.js",
 6 |   "main": "gluesql.node.js",
 7 |   "repository": {
 8 |     "type": "git",
 9 |     "url": "git+https://github.com/gluesql/gluesql.git"
10 |   },
11 |   "keywords": [
12 |     "SQL",
13 |     "Database",
14 |     "SQL.js",
15 |     "websql",
16 |     "rust",
17 |     "webassembly",
18 |     "wasm"
19 |   ],
20 |   "author": "Taehoon Moon",
21 |   "license": "Apache-2.0",
22 |   "bugs": {
23 |     "url": "https://github.com/gluesql/gluesql/issues"
24 |   },
25 |   "homepage": "https://gluesql.org/docs",
26 |   "files": [
27 |     "dist_nodejs/gluesql_js.js",
28 |     "dist_nodejs/gluesql_js_bg.wasm",
29 |     "dist_web/gluesql_js.js",
30 |     "dist_web/gluesql_js_bg.wasm",
31 |     "gluesql.js",
32 |     "gluesql.node.js",
33 |     "gluesql.rollup.js",
34 |     "package.json",
35 |     "README.md"
36 |   ]
37 | }
38 | 


--------------------------------------------------------------------------------
/pkg/javascript/src/utils.rs:
--------------------------------------------------------------------------------
 1 | pub fn set_panic_hook() {
 2 |     // When the `console_error_panic_hook` feature is enabled, we can call the
 3 |     // `set_panic_hook` function at least once during initialization, and then
 4 |     // we will get better error messages if our code ever panics.
 5 |     //
 6 |     // For more details see
 7 |     // https://github.com/rustwasm/console_error_panic_hook#readme
 8 |     #[cfg(feature = "console_error_panic_hook")]
 9 |     console_error_panic_hook::set_once();
10 | }
11 | 


--------------------------------------------------------------------------------
/pkg/javascript/tests/composite_storage.rs:
--------------------------------------------------------------------------------
 1 | #![cfg(target_arch = "wasm32")]
 2 | 
 3 | use {
 4 |     async_trait::async_trait, gluesql_composite_storage::CompositeStorage,
 5 |     gluesql_core::prelude::Glue, gluesql_memory_storage::MemoryStorage, test_suite::*,
 6 |     wasm_bindgen_test::*,
 7 | };
 8 | 
 9 | wasm_bindgen_test_configure!(run_in_browser);
10 | 
11 | struct CompositeTester {
12 |     glue: Glue<CompositeStorage>,
13 | }
14 | 
15 | #[async_trait(?Send)]
16 | impl Tester<CompositeStorage> for CompositeTester {
17 |     async fn new(_: &str) -> Self {
18 |         let mut storage = CompositeStorage::default();
19 |         storage.push("memory", MemoryStorage::default());
20 |         storage.set_default("memory");
21 | 
22 |         let glue = Glue::new(storage);
23 | 
24 |         Self { glue }
25 |     }
26 | 
27 |     fn get_glue(&mut self) -> &mut Glue<CompositeStorage> {
28 |         &mut self.glue
29 |     }
30 | }
31 | 
32 | generate_store_tests!(wasm_bindgen_test, CompositeTester);
33 | 


--------------------------------------------------------------------------------
/pkg/javascript/webdriver.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "moz:firefoxOptions": {
 3 |     "prefs": {
 4 |       "media.navigator.streams.fake": true,
 5 |       "media.navigator.permission.disabled": true
 6 |     },
 7 |     "args": []
 8 |   },
 9 |   "goog:chromeOptions": {
10 |     "args": [
11 |       "--use-fake-device-for-media-stream",
12 |       "--use-fake-ui-for-media-stream"
13 |     ]
14 |   }
15 | }
16 | 


--------------------------------------------------------------------------------
/pkg/python/Cargo.toml:
--------------------------------------------------------------------------------
 1 | [package]
 2 | name = "gluesql-py"
 3 | authors = [
 4 |     "Gyubong Lee <jopemachine@naver.com>",
 5 |     "Taehoon Moon <taehoon.moon@outlook.com>",
 6 | ]
 7 | version.workspace = true
 8 | edition.workspace = true
 9 | description.workspace = true
10 | license.workspace = true
11 | repository.workspace = true
12 | documentation.workspace = true
13 | 
14 | [lib]
15 | name = "gluesql"
16 | crate-type = ["cdylib", "rlib"]
17 | 
18 | [features]
19 | default = []
20 | include-python-workspace = []
21 | 
22 | [dependencies]
23 | gluesql-core.workspace = true
24 | gluesql_memory_storage.workspace = true
25 | gluesql-web-storage.workspace = true
26 | gluesql-idb-storage.workspace = true
27 | gluesql-composite-storage.workspace = true
28 | gluesql_sled_storage.workspace = true
29 | gluesql-json-storage.workspace = true
30 | gluesql-shared-memory-storage.workspace = true
31 | 
32 | pyo3 = { version = "0.19.2", features = ["extension-module"] }
33 | pythonize = "0.19.0"
34 | tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros"] }
35 | serde = "1"
36 | serde_json = "1"
37 | gloo-utils = { version = "0.1.6", features = ["serde"] }
38 | 
39 | [dev-dependencies]
40 | test-suite.workspace = true
41 | 


--------------------------------------------------------------------------------
/pkg/python/DEVELOPMENT.md:
--------------------------------------------------------------------------------
 1 | ## 🚴 Usage
 2 | 
 3 | ### Build
 4 | 
 5 | To build `gluesql-py`, run below command.
 6 | 
 7 | ```
 8 | maturin build
 9 | ```
10 | 
11 | And to install the builded package, run below command.
12 | 
13 | ```
14 | pip install .
15 | ```
16 | 
17 | ### Test
18 | 
19 | To run `gluesql-py` tests, run below command.
20 | 
21 | ```
22 | pytest
23 | ```
24 | 
25 | ## Deployment
26 | 
27 | To build `gluesql-py` in release mode, run below command.
28 | 
29 | ```
30 | maturin build --release --strip
31 | ```
32 | 
33 | To deploy `gluesql-py` in [pypi](https://pypi.org/), run below command.
34 | 
35 | ```
36 | maturin publish
37 | ```
38 | 


--------------------------------------------------------------------------------
/pkg/python/README.md:
--------------------------------------------------------------------------------
 1 | # GlueSQL.py
 2 | 
 3 | GlueSQL.py is a Python binding for the [GlueSQL](https://github.com/gluesql/gluesql) database engine. It provides an embedded SQL database that works with a selection of storage backends.
 4 | 
 5 | Supported storages:
 6 | 
 7 | - `MemoryStorage`
 8 | - `JsonStorage`
 9 | - `SharedMemoryStorage`
10 | - `SledStorage`
11 | 
12 | Learn more at **<https://gluesql.org/docs>**.
13 | 
14 | ## Installation
15 | 
16 | Install from PyPI:
17 | 
18 | ```bash
19 | pip install gluesql
20 | ```
21 | 
22 | ## Usage
23 | 
24 | ```python
25 | from gluesql import Glue, MemoryStorage
26 | 
27 | storage = MemoryStorage()
28 | 
29 | engine = Glue(storage)
30 | 
31 | engine.query(
32 |     """
33 |     CREATE TABLE User (id INTEGER, name TEXT);
34 |     INSERT INTO User VALUES (1, 'Hello'), (2, 'World');
35 |     """
36 | )
37 | 
38 | result = engine.query("SELECT * FROM User;")
39 | print(result)
40 | ```
41 | 
42 | ## License
43 | 
44 | This project is licensed under the Apache License, Version 2.0 - see the [LICENSE](https://raw.githubusercontent.com/gluesql/gluesql/main/LICENSE) file for details.
45 | 


--------------------------------------------------------------------------------
/pkg/python/examples/json-storage/fixtures/test.json:
--------------------------------------------------------------------------------
 1 | [
 2 |   {
 3 |     "id": 1,
 4 |     "name": "Glue"
 5 |   },
 6 |   {
 7 |     "id": 2,
 8 |     "name": "SQL"
 9 |   }
10 | ]
11 | 


--------------------------------------------------------------------------------
/pkg/python/examples/memory-storage/main.py:
--------------------------------------------------------------------------------
 1 | from gluesql import Glue, MemoryStorage
 2 | from tabulate import tabulate
 3 | 
 4 | db = Glue(MemoryStorage())
 5 | 
 6 | 
 7 | def run():
 8 |     db.query(
 9 |         """
10 |         CREATE TABLE User (id INTEGER, name TEXT);
11 |         CREATE TABLE Device (name TEXT, userId INTEGER);
12 |         INSERT INTO User VALUES
13 |         (1, 'glue'), (2, 'sticky'), (3, 'watt');
14 |         INSERT INTO Device VALUES
15 |         ('Phone', 1), ('Mic', 1), ('Monitor', 3),
16 |         ('Mouse', 2), ('Touchpad', 2);
17 |         """
18 |     )
19 | 
20 |     sql = "SHOW TABLES;"
21 |     result = db.query(sql)
22 |     tables = result[0].get("tables")
23 |     tables = list(map(lambda t: [t], tables))
24 |     print(f"\n[Query]\n{sql}")
25 |     print(
26 |         tabulate(tables, headers=["Values"], showindex=True, tablefmt="simple_outline")
27 |     )
28 | 
29 |     sql = """
30 |         SELECT
31 |         u.name as user,
32 |         d.name as device
33 |         FROM User u
34 |         JOIN Device d ON u.id = d.userId
35 |     """.strip().replace(
36 |         "    ", ""
37 |     )
38 | 
39 |     result = db.query(sql)
40 |     rows = result[0].get("rows")
41 |     print(f"\n[Query]\n{sql}")
42 |     print(tabulate(rows, headers="keys", showindex=True, tablefmt="simple_outline"))
43 | 
44 | 
45 | if __name__ == "__main__":
46 |     run()
47 | 


--------------------------------------------------------------------------------
/pkg/python/examples/shared-memory-storage/main.py:
--------------------------------------------------------------------------------
 1 | from gluesql import Glue, SharedMemoryStorage
 2 | from tabulate import tabulate
 3 | 
 4 | db = Glue(SharedMemoryStorage())
 5 | 
 6 | 
 7 | def run():
 8 |     db.query(
 9 |         """
10 |         CREATE TABLE User (id INTEGER, name TEXT);
11 |         CREATE TABLE Device (name TEXT, userId INTEGER);
12 |         INSERT INTO User VALUES
13 |         (1, 'glue'), (2, 'sticky'), (3, 'watt');
14 |         INSERT INTO Device VALUES
15 |         ('Phone', 1), ('Mic', 1), ('Monitor', 3),
16 |         ('Mouse', 2), ('Touchpad', 2);
17 |         """
18 |     )
19 | 
20 |     sql = "SHOW TABLES;"
21 |     result = db.query(sql)
22 |     tables = result[0].get("tables")
23 |     tables = list(map(lambda t: [t], tables))
24 |     print(f"\n[Query]\n{sql}")
25 |     print(
26 |         tabulate(tables, headers=["Values"], showindex=True, tablefmt="simple_outline")
27 |     )
28 | 
29 |     sql = """
30 |         SELECT
31 |         u.name as user,
32 |         d.name as device
33 |         FROM User u
34 |         JOIN Device d ON u.id = d.userId
35 |     """.strip().replace(
36 |         "    ", ""
37 |     )
38 | 
39 |     result = db.query(sql)
40 |     rows = result[0].get("rows")
41 |     print(f"\n[Query]\n{sql}")
42 |     print(tabulate(rows, headers="keys", showindex=True, tablefmt="simple_outline"))
43 | 
44 | 
45 | if __name__ == "__main__":
46 |     run()
47 | 


--------------------------------------------------------------------------------
/pkg/python/pyproject.toml:
--------------------------------------------------------------------------------
 1 | [build-system]
 2 | requires = ["maturin>=1.0,<2.0"]
 3 | build-backend = "maturin"
 4 | 
 5 | [tool.maturin]
 6 | bindings = "pyo3"
 7 | features = ["include-python-workspace"]
 8 | 
 9 | [project]
10 | name = "gluesql"
11 | requires-python = ">=3.10"
12 | description = "GlueSQL is quite sticky. It attaches to anywhere."
13 | readme = "README.md"
14 | classifiers = [
15 |     "Programming Language :: Rust",
16 |     "Programming Language :: Python :: Implementation :: CPython",
17 | ]
18 | 


--------------------------------------------------------------------------------
/pkg/python/pytest.ini:
--------------------------------------------------------------------------------
1 | [pytest]
2 | python_files = tests/*.py
3 | 


--------------------------------------------------------------------------------
/pkg/python/requirements.txt:
--------------------------------------------------------------------------------
1 | maturin==1.2.3
2 | pytest==7.3.1
3 | tabulate==0.9.0


--------------------------------------------------------------------------------
/pkg/python/src/error.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     gluesql_core::prelude::Error,
 3 |     pyo3::{create_exception, exceptions::PyException, prelude::*, pyclass::CompareOp},
 4 | };
 5 | 
 6 | #[pyclass(name = "GlueSQLError")]
 7 | pub struct PyGlueSQLError(pub Error);
 8 | 
 9 | create_exception!(gluesql, GlueSQLError, PyException);
10 | 
11 | #[pymethods]
12 | impl PyGlueSQLError {
13 |     pub fn __richcmp__(&self, py: Python, rhs: &PyGlueSQLError, op: CompareOp) -> PyObject {
14 |         match op {
15 |             CompareOp::Eq => (self.0 == rhs.0).into_py(py),
16 |             CompareOp::Ne => (self.0 != rhs.0).into_py(py),
17 |             _ => py.NotImplemented(),
18 |         }
19 |     }
20 | 
21 |     pub fn __repr__(&self) -> String {
22 |         format!("{}", self.0)
23 |     }
24 | }
25 | 
26 | impl From<PyGlueSQLError> for PyErr {
27 |     fn from(e: PyGlueSQLError) -> PyErr {
28 |         GlueSQLError::new_err(e.0.to_string())
29 |     }
30 | }
31 | 


--------------------------------------------------------------------------------
/pkg/rust/examples/api_usage.rs:
--------------------------------------------------------------------------------
 1 | #[cfg(feature = "gluesql_sled_storage")]
 2 | mod api_usage {
 3 |     use gluesql::prelude::{Glue, SledStorage};
 4 | 
 5 |     pub async fn run() {
 6 |         let storage = SledStorage::new("data/mutable-api").unwrap();
 7 |         let mut glue = Glue::new(storage);
 8 | 
 9 |         let sqls = [
10 |             "CREATE TABLE Glue (id INTEGER);",
11 |             "INSERT INTO Glue VALUES (100);",
12 |             "INSERT INTO Glue VALUES (200);",
13 |             "DROP TABLE Glue;",
14 |         ];
15 | 
16 |         for sql in sqls {
17 |             glue.execute(sql).await.unwrap();
18 |         }
19 |     }
20 | }
21 | 
22 | fn main() {
23 |     #[cfg(feature = "gluesql_sled_storage")]
24 |     futures::executor::block_on(api_usage::run());
25 | }
26 | 


--------------------------------------------------------------------------------
/pkg/rust/examples/memory_storage_usage.rs:
--------------------------------------------------------------------------------
 1 | #[cfg(feature = "gluesql_memory_storage")]
 2 | mod api_usage {
 3 |     use gluesql::{gluesql_memory_storage::MemoryStorage, prelude::Glue};
 4 | 
 5 |     pub async fn run() {
 6 |         let storage = MemoryStorage::default();
 7 |         let mut glue = Glue::new(storage);
 8 | 
 9 |         glue.execute("DROP TABLE IF EXISTS api_test").await.unwrap();
10 | 
11 |         glue.execute(
12 |             "CREATE TABLE api_test (
13 |                 id INTEGER,
14 |                 name TEXT,
15 |                 nullable TEXT NULL,
16 |                 is BOOLEAN
17 |             )",
18 |         )
19 |         .await
20 |         .unwrap();
21 | 
22 |         glue.execute(
23 |             "INSERT INTO api_test (
24 |                 id,
25 |                 name,
26 |                 nullable,
27 |                 is
28 |             ) VALUES
29 |                 (1, 'test1', 'not null', TRUE),
30 |                 (2, 'test2', NULL, FALSE)",
31 |         )
32 |         .await
33 |         .unwrap();
34 |     }
35 | }
36 | 
37 | fn main() {
38 |     #[cfg(feature = "gluesql_memory_storage")]
39 |     futures::executor::block_on(api_usage::run());
40 | }
41 | 


--------------------------------------------------------------------------------
/pkg/rust/examples/using_config.rs:
--------------------------------------------------------------------------------
 1 | #[cfg(feature = "gluesql_sled_storage")]
 2 | use {
 3 |     futures::executor::block_on,
 4 |     gluesql::{gluesql_sled_storage::SledStorage, prelude::Glue},
 5 |     gluesql_sled_storage::sled,
 6 |     std::convert::TryFrom,
 7 | };
 8 | 
 9 | fn main() {
10 |     #[cfg(feature = "gluesql_sled_storage")]
11 |     {
12 |         let config = sled::Config::default()
13 |             .path("data/using_config")
14 |             .temporary(true)
15 |             .mode(sled::Mode::HighThroughput);
16 | 
17 |         let storage = SledStorage::try_from(config).unwrap();
18 | 
19 |         let mut glue = Glue::new(storage);
20 | 
21 |         let sqls = "
22 |             CREATE TABLE Glue (id INTEGER);
23 |             INSERT INTO Glue VALUES (100);
24 |             INSERT INTO Glue VALUES (200);
25 |             DROP TABLE Glue;
26 |         ";
27 | 
28 |         block_on(glue.execute(sqls)).unwrap();
29 |     }
30 | }
31 | 


--------------------------------------------------------------------------------
/pkg/rust/src/main.rs:
--------------------------------------------------------------------------------
1 | fn main() {
2 |     #[cfg(feature = "cli")]
3 |     cli::run().unwrap();
4 | }
5 | 


--------------------------------------------------------------------------------
/rust-toolchain.toml:
--------------------------------------------------------------------------------
1 | [toolchain]
2 | channel = "1.87"
3 | components = ["rustfmt", "clippy"]
4 | 


--------------------------------------------------------------------------------
/storages/composite-storage/Cargo.toml:
--------------------------------------------------------------------------------
 1 | [package]
 2 | name = "gluesql-composite-storage"
 3 | authors = ["Taehoon Moon <taehoon.moon@outlook.com>"]
 4 | version.workspace = true
 5 | edition.workspace = true
 6 | description.workspace = true
 7 | license.workspace = true
 8 | repository.workspace = true
 9 | documentation.workspace = true
10 | 
11 | [dependencies]
12 | gluesql-core.workspace = true
13 | async-trait = "0.1"
14 | serde = { version = "1", features = ["derive"] }
15 | futures = "0.3"
16 | 
17 | [dev-dependencies]
18 | test-suite.workspace = true
19 | gluesql_memory_storage.workspace = true
20 | gluesql_sled_storage.workspace = true
21 | 
22 | tokio = { version = "1", features = ["rt", "macros"] }
23 | 


--------------------------------------------------------------------------------
/storages/composite-storage/src/transaction.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     super::CompositeStorage,
 3 |     async_trait::async_trait,
 4 |     gluesql_core::{
 5 |         error::{Error, Result},
 6 |         store::Transaction,
 7 |     },
 8 | };
 9 | 
10 | #[async_trait(?Send)]
11 | impl Transaction for CompositeStorage {
12 |     async fn begin(&mut self, autocommit: bool) -> Result<bool> {
13 |         if autocommit {
14 |             for storage in self.storages.values_mut() {
15 |                 storage.begin(autocommit).await?;
16 |             }
17 | 
18 |             return Ok(true);
19 |         }
20 | 
21 |         Err(Error::StorageMsg(
22 |             "[CompositeStorage] Transaction::begin is not supported".to_owned(),
23 |         ))
24 |     }
25 | 
26 |     async fn rollback(&mut self) -> Result<()> {
27 |         for storage in self.storages.values_mut() {
28 |             storage.commit().await?;
29 |         }
30 | 
31 |         Ok(())
32 |     }
33 | 
34 |     async fn commit(&mut self) -> Result<()> {
35 |         for storage in self.storages.values_mut() {
36 |             storage.commit().await?;
37 |         }
38 | 
39 |         Ok(())
40 |     }
41 | }
42 | 


--------------------------------------------------------------------------------
/storages/composite-storage/tests/composite_storage.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     async_trait::async_trait, gluesql_composite_storage::CompositeStorage,
 3 |     gluesql_core::prelude::Glue, gluesql_memory_storage::MemoryStorage, test_suite::*,
 4 | };
 5 | 
 6 | struct CompositeTester {
 7 |     glue: Glue<CompositeStorage>,
 8 | }
 9 | 
10 | #[async_trait(?Send)]
11 | impl Tester<CompositeStorage> for CompositeTester {
12 |     async fn new(_: &str) -> Self {
13 |         let mut storage = CompositeStorage::default();
14 |         storage.push("MEMORY", MemoryStorage::default());
15 |         storage.set_default("MEMORY");
16 | 
17 |         let glue = Glue::new(storage);
18 | 
19 |         Self { glue }
20 |     }
21 | 
22 |     fn get_glue(&mut self) -> &mut Glue<CompositeStorage> {
23 |         &mut self.glue
24 |     }
25 | }
26 | 
27 | generate_store_tests!(tokio::test, CompositeTester);
28 | generate_alter_table_tests!(tokio::test, CompositeTester);
29 | 


--------------------------------------------------------------------------------
/storages/csv-storage/Cargo.toml:
--------------------------------------------------------------------------------
 1 | [package]
 2 | name = "gluesql-csv-storage"
 3 | authors = [
 4 |   "Taehoon Moon <taehoon.moon@outlook.com>",
 5 | ]
 6 | version.workspace = true
 7 | edition.workspace = true
 8 | description.workspace = true
 9 | license.workspace = true
10 | repository.workspace = true
11 | documentation.workspace = true
12 | 
13 | [dependencies]
14 | gluesql-core.workspace = true
15 | utils.workspace = true
16 | 
17 | async-trait = "0.1"
18 | serde = { version = "1", features = ["derive"] }
19 | serde_json = "1"
20 | thiserror = "1.0"
21 | csv = "1.2.2"
22 | futures = "0.3"
23 | 
24 | [dev-dependencies]
25 | test-suite.workspace = true
26 | 
27 | tokio = { version = "1", features = ["rt", "macros"] }
28 | rust_decimal = "1"
29 | rust_decimal_macros = "1"
30 | 


--------------------------------------------------------------------------------
/storages/csv-storage/tests/csv_storage.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     async_trait::async_trait, gluesql_core::prelude::Glue, gluesql_csv_storage::CsvStorage,
 3 |     std::fs::remove_dir_all, test_suite::*,
 4 | };
 5 | 
 6 | struct CsvTester {
 7 |     glue: Glue<CsvStorage>,
 8 | }
 9 | 
10 | #[async_trait(?Send)]
11 | impl Tester<CsvStorage> for CsvTester {
12 |     async fn new(namespace: &str) -> Self {
13 |         let path = format!("tmp/{namespace}");
14 | 
15 |         if let Err(e) = remove_dir_all(&path) {
16 |             println!("fs::remove_file {:?}", e);
17 |         };
18 | 
19 |         let storage = CsvStorage::new(&path).expect("CsvStorage::new");
20 |         let glue = Glue::new(storage);
21 |         CsvTester { glue }
22 |     }
23 | 
24 |     fn get_glue(&mut self) -> &mut Glue<CsvStorage> {
25 |         &mut self.glue
26 |     }
27 | }
28 | 
29 | generate_store_tests!(tokio::test, CsvTester);
30 | generate_alter_table_tests!(tokio::test, CsvTester);
31 | 


--------------------------------------------------------------------------------
/storages/csv-storage/tests/error.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     gluesql_core::prelude::Glue,
 3 |     gluesql_csv_storage::{CsvStorage, error::CsvStorageError},
 4 | };
 5 | 
 6 | #[tokio::test]
 7 | async fn error() {
 8 |     let path = "./tests/samples/";
 9 |     let storage = CsvStorage::new(path).unwrap();
10 |     let mut glue = Glue::new(storage);
11 | 
12 |     let actual = glue.execute("SELECT * FROM WrongSchemaName").await;
13 |     let expected = Err(CsvStorageError::TableNameDoesNotMatchWithFile.into());
14 |     assert_eq!(actual, expected);
15 | }
16 | 


--------------------------------------------------------------------------------
/storages/csv-storage/tests/samples/Book.csv:
--------------------------------------------------------------------------------
1 | Author,Genre,Price,Publisher,Title,Year
2 | Harper Lee,,,J. B. Lippincott & Co.,To Kill a Mockingbird,1960
3 | F. Scott Fitzgerald,Classic,9.99,,The Great Gatsby,1925
4 | George Orwell,Dystopian,7.99,Secker & Warburg,1984,
5 | J. D. Salinger,Coming-of-age,6.99,"Little, Brown and Company",The Catcher in the Rye,1951
6 | Jane Austen,Romance,,,Pride and Prejudice,1813
7 | 


--------------------------------------------------------------------------------
/storages/csv-storage/tests/samples/Book.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE "Book";
2 | 


--------------------------------------------------------------------------------
/storages/csv-storage/tests/samples/City.csv:
--------------------------------------------------------------------------------
1 | Country,City,Population
2 | South Korea,Seoul,9736962
3 | Japan,Tokyo,13515271
4 | China,Shanghai,24281000
5 | United States,New York City,8336817
6 | Italy,Milan,2837332
7 | 


--------------------------------------------------------------------------------
/storages/csv-storage/tests/samples/City.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE City (Country TEXT NOT NULL, City TEXT NOT NULL, Population INT NOT NULL);
2 | 


--------------------------------------------------------------------------------
/storages/csv-storage/tests/samples/Employee.csv:
--------------------------------------------------------------------------------
1 | Name,Age,Gender,Occupation
2 | John,25,Male,Engineer
3 | Sarah,30,Female,Doctor
4 | Michael,40,Male,Lawyer
5 | Emily,28,Female,Teacher
6 | David,35,Male,Programmer


--------------------------------------------------------------------------------
/storages/csv-storage/tests/samples/Grocery.csv:
--------------------------------------------------------------------------------
1 | Product Name,Price,Quantity,Manufacturer,Expiration Date
2 | Apple,1.99,100,Apple Inc.,2022-08-31
3 | Banana,0.99,50,Chiquita Brands LLC,2022-09-01
4 | Milk,2.49,,Dean Foods Company,2022-08-30
5 | Bread,1.99,30,Sara Lee Corporation,
6 | Eggs,2.99,40,,2022-09-02
7 | 


--------------------------------------------------------------------------------
/storages/csv-storage/tests/samples/Grocery.types.csv:
--------------------------------------------------------------------------------
1 | Product Name,Price,Quantity,Manufacturer,Expiration Date
2 | TEXT,DECIMAL,INT,TEXT,DATE
3 | TEXT,DECIMAL,INT,TEXT,DATE
4 | TEXT,DECIMAL,,TEXT,DATE
5 | TEXT,DECIMAL,INT,TEXT,NULL
6 | TEXT,DECIMAL,INT,,DATE
7 | 


--------------------------------------------------------------------------------
/storages/csv-storage/tests/samples/Student.csv:
--------------------------------------------------------------------------------
1 | Name,Gender,Age,Grade
2 | John,Male,18,A
3 | Jane,Female,17,B
4 | Bob,NULL,NULL,C
5 | Alice,Female,18,NULL
6 | Mike,Male,NULL,B
7 | Lisa,NULL,18,A
8 | 


--------------------------------------------------------------------------------
/storages/csv-storage/tests/samples/Student.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE "Student";
2 | 


--------------------------------------------------------------------------------
/storages/csv-storage/tests/samples/Student.types.csv:
--------------------------------------------------------------------------------
1 | Name,Gender,Age,Grade
2 | TEXT,TEXT,INT,TEXT
3 | TEXT,TEXT,INT,TEXT
4 | TEXT,,,TEXT
5 | TEXT,TEXT,INT,
6 | TEXT,TEXT,,TEXT
7 | TEXT,,INT,TEXT
8 | 


--------------------------------------------------------------------------------
/storages/csv-storage/tests/samples/WrongSchemaName.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE AnotherSchemaName;
2 | 


--------------------------------------------------------------------------------
/storages/file-storage/Cargo.toml:
--------------------------------------------------------------------------------
 1 | [package]
 2 | name = "gluesql-file-storage"
 3 | authors = ["Taehoon Moon <taehoon.moon@outlook.com>"]
 4 | version.workspace = true
 5 | edition.workspace = true
 6 | description.workspace = true
 7 | license.workspace = true
 8 | repository.workspace = true
 9 | documentation.workspace = true
10 | 
11 | [dependencies]
12 | gluesql-core.workspace = true
13 | async-trait = "0.1"
14 | serde = { version = "1", features = ["derive"] }
15 | futures = "0.3"
16 | uuid = { version = "1.10.0", features = ["v7"] }
17 | hex = "0.4.3"
18 | ron = { version = "0.8.1", features = ["integer128"] }
19 | 
20 | [dev-dependencies]
21 | test-suite.workspace = true
22 | tokio = { version = "1", features = ["rt", "macros"] }
23 | 


--------------------------------------------------------------------------------
/storages/git-storage/Cargo.toml:
--------------------------------------------------------------------------------
 1 | [package]
 2 | name = "gluesql-git-storage"
 3 | authors = ["Taehoon Moon <taehoon.moon@outlook.com>"]
 4 | version.workspace = true
 5 | edition.workspace = true
 6 | description.workspace = true
 7 | license.workspace = true
 8 | repository.workspace = true
 9 | documentation.workspace = true
10 | 
11 | [dependencies]
12 | gluesql-core.workspace = true
13 | gluesql-file-storage.workspace = true
14 | gluesql-csv-storage.workspace = true
15 | gluesql-json-storage.workspace = true
16 | async-trait = "0.1"
17 | strum_macros = "0.26.4"
18 | 
19 | [dev-dependencies]
20 | test-suite.workspace = true
21 | tokio = { version = "1", features = ["rt", "macros"] }
22 | uuid = { version = "1.10.0", features = ["v7"] }
23 | 
24 | [features]
25 | test-git-remote = []
26 | 


--------------------------------------------------------------------------------
/storages/git-storage/src/store.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     crate::GitStorage,
 3 |     async_trait::async_trait,
 4 |     gluesql_core::{
 5 |         data::{Key, Schema},
 6 |         error::Result,
 7 |         store::{DataRow, RowIter, Store},
 8 |     },
 9 | };
10 | 
11 | #[async_trait(?Send)]
12 | impl Store for GitStorage {
13 |     async fn fetch_all_schemas(&self) -> Result<Vec<Schema>> {
14 |         self.get_store().fetch_all_schemas().await
15 |     }
16 | 
17 |     async fn fetch_schema(&self, table_name: &str) -> Result<Option<Schema>> {
18 |         self.get_store().fetch_schema(table_name).await
19 |     }
20 | 
21 |     async fn fetch_data(&self, table_name: &str, key: &Key) -> Result<Option<DataRow>> {
22 |         self.get_store().fetch_data(table_name, key).await
23 |     }
24 | 
25 |     async fn scan_data<'a>(&'a self, table_name: &str) -> Result<RowIter<'a>> {
26 |         self.get_store().scan_data(table_name).await
27 |     }
28 | }
29 | 


--------------------------------------------------------------------------------
/storages/git-storage/tests/git_storage_csv.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     async_trait::async_trait,
 3 |     gluesql_core::prelude::Glue,
 4 |     gluesql_git_storage::{GitStorage, StorageType},
 5 |     std::fs::remove_dir_all,
 6 |     test_suite::*,
 7 | };
 8 | 
 9 | struct GitStorageTester {
10 |     glue: Glue<GitStorage>,
11 | }
12 | 
13 | #[async_trait(?Send)]
14 | impl Tester<GitStorage> for GitStorageTester {
15 |     async fn new(namespace: &str) -> Self {
16 |         let path = format!("tmp/git_storage_csv/{namespace}");
17 | 
18 |         if let Err(e) = remove_dir_all(&path) {
19 |             println!("fs::remove_file {:?}", e);
20 |         };
21 | 
22 |         let storage = GitStorage::init(&path, StorageType::Csv).expect("GitStorage::init - CSV");
23 |         let glue = Glue::new(storage);
24 |         GitStorageTester { glue }
25 |     }
26 | 
27 |     fn get_glue(&mut self) -> &mut Glue<GitStorage> {
28 |         &mut self.glue
29 |     }
30 | }
31 | 
32 | generate_store_tests!(tokio::test, GitStorageTester);
33 | generate_alter_table_tests!(tokio::test, GitStorageTester);
34 | 


--------------------------------------------------------------------------------
/storages/git-storage/tests/git_storage_file.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     async_trait::async_trait,
 3 |     gluesql_core::prelude::Glue,
 4 |     gluesql_git_storage::{GitStorage, StorageType},
 5 |     std::fs::remove_dir_all,
 6 |     test_suite::*,
 7 | };
 8 | 
 9 | struct GitStorageTester {
10 |     glue: Glue<GitStorage>,
11 | }
12 | 
13 | #[async_trait(?Send)]
14 | impl Tester<GitStorage> for GitStorageTester {
15 |     async fn new(namespace: &str) -> Self {
16 |         let path = format!("tmp/git_storage_file/{namespace}");
17 | 
18 |         if let Err(e) = remove_dir_all(&path) {
19 |             println!("fs::remove_file {:?}", e);
20 |         };
21 | 
22 |         let storage = GitStorage::init(&path, StorageType::File).expect("GitStorage::init - File");
23 |         let glue = Glue::new(storage);
24 |         GitStorageTester { glue }
25 |     }
26 | 
27 |     fn get_glue(&mut self) -> &mut Glue<GitStorage> {
28 |         &mut self.glue
29 |     }
30 | }
31 | 
32 | generate_store_tests!(tokio::test, GitStorageTester);
33 | generate_alter_table_tests!(tokio::test, GitStorageTester);
34 | 


--------------------------------------------------------------------------------
/storages/git-storage/tests/git_storage_json.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     async_trait::async_trait,
 3 |     gluesql_core::prelude::Glue,
 4 |     gluesql_git_storage::{GitStorage, StorageType},
 5 |     std::fs::remove_dir_all,
 6 |     test_suite::*,
 7 | };
 8 | 
 9 | struct GitStorageTester {
10 |     glue: Glue<GitStorage>,
11 | }
12 | 
13 | #[async_trait(?Send)]
14 | impl Tester<GitStorage> for GitStorageTester {
15 |     async fn new(namespace: &str) -> Self {
16 |         let path = format!("tmp/git_storage_json/{namespace}");
17 | 
18 |         if let Err(e) = remove_dir_all(&path) {
19 |             println!("fs::remove_file {:?}", e);
20 |         };
21 | 
22 |         let storage = GitStorage::init(&path, StorageType::Json).expect("GitStorage::init - JSON");
23 |         let glue = Glue::new(storage);
24 |         GitStorageTester { glue }
25 |     }
26 | 
27 |     fn get_glue(&mut self) -> &mut Glue<GitStorage> {
28 |         &mut self.glue
29 |     }
30 | }
31 | 
32 | generate_store_tests!(tokio::test, GitStorageTester);
33 | generate_alter_table_tests!(tokio::test, GitStorageTester);
34 | 


--------------------------------------------------------------------------------
/storages/idb-storage/Cargo.toml:
--------------------------------------------------------------------------------
 1 | [package]
 2 | name = "gluesql-idb-storage"
 3 | authors = ["Taehoon Moon <taehoon.moon@outlook.com>"]
 4 | version.workspace = true
 5 | edition.workspace = true
 6 | description.workspace = true
 7 | license.workspace = true
 8 | repository.workspace = true
 9 | documentation.workspace = true
10 | 
11 | [dependencies]
12 | gluesql-core.workspace = true
13 | async-trait = "0.1"
14 | serde = { version = "1", features = ["derive"] }
15 | idb = "0.6.1"
16 | serde-wasm-bindgen = "0.6.3"
17 | wasm-bindgen = "0.2.90"
18 | web-sys = { version = "0.3.67", features = ["console"] }
19 | serde_json = "1.0.111"
20 | gloo-utils = { version = "0.2.0", features = ["serde"] }
21 | futures = "0.3"
22 | 
23 | [dev-dependencies]
24 | test-suite.workspace = true
25 | wasm-bindgen-test = "0.3.40"
26 | 


--------------------------------------------------------------------------------
/storages/idb-storage/README.md:
--------------------------------------------------------------------------------
1 | ## 🚴 IdbStorage - IndexedDB storage support for GlueSQL
2 | 
3 | ### 🔬 Test in Headless Browsers with `wasm-pack test`
4 | ```
5 | WASM_BINDGEN_TEST_TIMEOUT=60 wasm-pack test --headless --firefox --chrome
6 | ```
7 | 


--------------------------------------------------------------------------------
/storages/idb-storage/src/error.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     async_trait::async_trait,
 3 |     core::fmt::Display,
 4 |     gluesql_core::error::{Error, Result},
 5 |     std::{future::IntoFuture, result::Result as StdResult},
 6 | };
 7 | 
 8 | pub trait ErrInto<T> {
 9 |     fn err_into(self) -> Result<T>;
10 | }
11 | 
12 | impl<T, E: Display> ErrInto<T> for Result<T, E> {
13 |     fn err_into(self) -> Result<T> {
14 |         self.map_err(|e| Error::StorageMsg(e.to_string()))
15 |     }
16 | }
17 | 
18 | #[async_trait(?Send)]
19 | pub trait StoreReqIntoFuture<T> {
20 |     async fn into_future(self) -> Result<T>;
21 | }
22 | 
23 | #[async_trait(?Send)]
24 | impl<F, T, E: Display> StoreReqIntoFuture<T> for Result<F, E>
25 | where
26 |     F: IntoFuture<Output = StdResult<T, E>>,
27 | {
28 |     async fn into_future(self) -> Result<T> {
29 |         self.err_into()?.await.err_into()
30 |     }
31 | }
32 | 


--------------------------------------------------------------------------------
/storages/idb-storage/tests/idb_storage.rs:
--------------------------------------------------------------------------------
 1 | #![cfg(target_arch = "wasm32")]
 2 | 
 3 | use {
 4 |     async_trait::async_trait,
 5 |     gluesql_core::prelude::Glue,
 6 |     gluesql_idb_storage::IdbStorage,
 7 |     test_suite::*,
 8 |     wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure},
 9 | };
10 | 
11 | wasm_bindgen_test_configure!(run_in_browser);
12 | 
13 | struct IdbStorageTester {
14 |     glue: Glue<IdbStorage>,
15 | }
16 | 
17 | #[async_trait(?Send)]
18 | impl Tester<IdbStorage> for IdbStorageTester {
19 |     async fn new(namespace: &str) -> Self {
20 |         let storage = IdbStorage::new(Some(namespace.to_owned())).await.unwrap();
21 |         let glue = Glue::new(storage);
22 | 
23 |         Self { glue }
24 |     }
25 | 
26 |     fn get_glue(&mut self) -> &mut Glue<IdbStorage> {
27 |         &mut self.glue
28 |     }
29 | }
30 | 
31 | generate_store_tests!(wasm_bindgen_test, IdbStorageTester);
32 | generate_alter_table_tests!(wasm_bindgen_test, IdbStorageTester);
33 | 


--------------------------------------------------------------------------------
/storages/json-storage/Cargo.toml:
--------------------------------------------------------------------------------
 1 | [package]
 2 | name = "gluesql-json-storage"
 3 | authors = [
 4 |   "Hyoungkwan Cho <devgony@gmail.com>",
 5 |   "Taehoon Moon <taehoon.moon@outlook.com>",
 6 | ]
 7 | version.workspace = true
 8 | edition.workspace = true
 9 | description.workspace = true
10 | license.workspace = true
11 | repository.workspace = true
12 | documentation.workspace = true
13 | 
14 | [dependencies]
15 | gluesql-core.workspace = true
16 | utils.workspace = true
17 | 
18 | async-trait = "0.1"
19 | serde = { version = "1", features = ["derive"] }
20 | serde_json = "1"
21 | futures = "0.3"
22 | tokio = { version = "1", features = ["rt", "macros"] }
23 | hex = "0.4"
24 | thiserror = "1.0"
25 | iter-enum = "1"
26 | 
27 | [dev-dependencies]
28 | test-suite.workspace = true
29 | uuid = "1"
30 | 


--------------------------------------------------------------------------------
/storages/json-storage/src/alter_table.rs:
--------------------------------------------------------------------------------
1 | use {super::JsonStorage, gluesql_core::store::AlterTable};
2 | 
3 | impl AlterTable for JsonStorage {}
4 | 


--------------------------------------------------------------------------------
/storages/json-storage/src/function.rs:
--------------------------------------------------------------------------------
1 | use {
2 |     super::JsonStorage,
3 |     gluesql_core::store::{CustomFunction, CustomFunctionMut},
4 | };
5 | 
6 | impl CustomFunction for JsonStorage {}
7 | impl CustomFunctionMut for JsonStorage {}
8 | 


--------------------------------------------------------------------------------
/storages/json-storage/src/index.rs:
--------------------------------------------------------------------------------
1 | use {
2 |     super::JsonStorage,
3 |     gluesql_core::store::{Index, IndexMut},
4 | };
5 | 
6 | impl Index for JsonStorage {}
7 | impl IndexMut for JsonStorage {}
8 | 


--------------------------------------------------------------------------------
/storages/json-storage/src/transaction.rs:
--------------------------------------------------------------------------------
1 | use {super::JsonStorage, gluesql_core::store::Transaction};
2 | 
3 | impl Transaction for JsonStorage {}
4 | 


--------------------------------------------------------------------------------
/storages/json-storage/tests/json_storage.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     async_trait::async_trait, gluesql_core::prelude::Glue, gluesql_json_storage::JsonStorage,
 3 |     std::fs::remove_dir_all, test_suite::*,
 4 | };
 5 | 
 6 | struct JsonTester {
 7 |     glue: Glue<JsonStorage>,
 8 | }
 9 | 
10 | #[async_trait(?Send)]
11 | impl Tester<JsonStorage> for JsonTester {
12 |     async fn new(namespace: &str) -> Self {
13 |         let path = format!("tmp/{namespace}");
14 | 
15 |         if let Err(e) = remove_dir_all(&path) {
16 |             println!("fs::remove_file {:?}", e);
17 |         };
18 | 
19 |         let storage = JsonStorage::new(&path).expect("JsonStorage::new");
20 |         let glue = Glue::new(storage);
21 |         JsonTester { glue }
22 |     }
23 | 
24 |     fn get_glue(&mut self) -> &mut Glue<JsonStorage> {
25 |         &mut self.glue
26 |     }
27 | }
28 | 
29 | generate_store_tests!(tokio::test, JsonTester);
30 | generate_alter_table_tests!(tokio::test, JsonTester);
31 | 


--------------------------------------------------------------------------------
/storages/json-storage/tests/samples/ArrayOfJsonsSchema.json:
--------------------------------------------------------------------------------
 1 | [
 2 |   {
 3 |     "id": 1,
 4 |     "name": "Glue"
 5 |   },
 6 |   {
 7 |     "id": 2,
 8 |     "name": "SQL"
 9 |   }
10 | ]
11 | 


--------------------------------------------------------------------------------
/storages/json-storage/tests/samples/ArrayOfJsonsSchema.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE ArrayOfJsonsSchema (
2 |   id INT,
3 |   name TEXT
4 | );
5 | 


--------------------------------------------------------------------------------
/storages/json-storage/tests/samples/ArrayOfJsonsSchemaless.json:
--------------------------------------------------------------------------------
 1 | [
 2 |   {
 3 |     "id": 1,
 4 |     "name": "Glue"
 5 |   },
 6 |   {
 7 |     "id": 2,
 8 |     "name": "SQL"
 9 |   }
10 | ]
11 | 


--------------------------------------------------------------------------------
/storages/json-storage/tests/samples/Duplicated.json:
--------------------------------------------------------------------------------
1 | {
2 |   "id": 1,
3 |   "notice": "This file exists to throw error if both *.jsonl and *.json file exist with identical name"
4 | }
5 | 


--------------------------------------------------------------------------------
/storages/json-storage/tests/samples/Duplicated.jsonl:
--------------------------------------------------------------------------------
1 | {"id": 1, "notice": "This file exists to throw error if both *.jsonl and *.json file exist with identical name"}
2 | 


--------------------------------------------------------------------------------
/storages/json-storage/tests/samples/JsonArrayTypeRequired.json:
--------------------------------------------------------------------------------
1 | "neither Array of jsons nor a single Json"
2 | 


--------------------------------------------------------------------------------
/storages/json-storage/tests/samples/JsonObjectTypeRequired.json:
--------------------------------------------------------------------------------
1 | [
2 |   "Not a Json"
3 | ]
4 | 


--------------------------------------------------------------------------------
/storages/json-storage/tests/samples/Schema.sql:
--------------------------------------------------------------------------------
 1 | CREATE TABLE Schema (
 2 |   boolean BOOLEAN,
 3 |   int8 INT8,
 4 |   int16 INT16,
 5 |   int32 INT32,
 6 |   int64 INT,
 7 |   uint8 UINT8,
 8 |   text TEXT,
 9 |   bytea BYTEA,
10 |   inet INET,
11 |   date DATE,
12 |   timestamp TIMESTAMP,
13 |   time TIME,
14 |   interval INTERVAL,
15 |   uuid UUID,
16 |   map MAP,
17 |   list LIST
18 | );
19 | 


--------------------------------------------------------------------------------
/storages/json-storage/tests/samples/Schemaless.jsonl:
--------------------------------------------------------------------------------
1 | {"id": 1}
2 | {"name": "Glue"}
3 | {"id": 3, "name": "SQL"}
4 | 


--------------------------------------------------------------------------------
/storages/json-storage/tests/samples/SingleJsonSchema.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "data": [
 3 |     {
 4 |       "id": 1,
 5 |       "name": "Glue"
 6 |     },
 7 |     {
 8 |       "id": 2,
 9 |       "name": "SQL"
10 |     }
11 |   ]
12 | }
13 | 


--------------------------------------------------------------------------------
/storages/json-storage/tests/samples/SingleJsonSchema.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE SingleJsonSchema (
2 |   data LIST
3 | );
4 | 


--------------------------------------------------------------------------------
/storages/json-storage/tests/samples/SingleJsonSchemaless.json:
--------------------------------------------------------------------------------
 1 | {
 2 |   "data": [
 3 |     {
 4 |       "id": 1,
 5 |       "name": "Glue"
 6 |     },
 7 |     {
 8 |       "id": 2,
 9 |       "name": "SQL"
10 |     }
11 |   ]
12 | }
13 | 


--------------------------------------------------------------------------------
/storages/json-storage/tests/samples/WrongFormatJson.json:
--------------------------------------------------------------------------------
1 | {
2 |   "id": 1,
3 |   "notice": "*.json usage1: An array of jsons"
4 | },
5 | {
6 |   "id": 2,
7 |   "notice": "*.json usage2: A single json in a file"
8 | }
9 | 


--------------------------------------------------------------------------------
/storages/json-storage/tests/samples/WrongFormatJsonl.jsonl:
--------------------------------------------------------------------------------
 1 | [
 2 |   {
 3 |     "id": 1,
 4 |     "notice": "*.jsonl is a newline-delimited JSON file"
 5 |   },
 6 |   {
 7 |     "id": 2,
 8 |     "notice": "Only a single json must be written to a single line"
 9 |   },
10 |   {
11 |     "id": 3,
12 |     "notice": "To use this array of jsons format, use *.json extension"
13 |   }
14 | ]
15 | 


--------------------------------------------------------------------------------
/storages/json-storage/tests/samples/WrongSchema.jsonl:
--------------------------------------------------------------------------------
1 | {"id": 1, "notice": "This file exists to test WrongSchema.sql file"}
2 | 


--------------------------------------------------------------------------------
/storages/json-storage/tests/samples/WrongSchema.sql:
--------------------------------------------------------------------------------
1 | ALTER TABLE WrongSchema ADD COLUMN dostNotWork INT;
2 | 


--------------------------------------------------------------------------------
/storages/json-storage/tests/samples/WrongTableName.jsonl:
--------------------------------------------------------------------------------
1 | {"id": 1, "notice": "This file exists to test WrongTableName.sql file"}
2 | 


--------------------------------------------------------------------------------
/storages/json-storage/tests/samples/WrongTableName.sql:
--------------------------------------------------------------------------------
1 | CREATE TABLE TableNameDoesNotMatchWithFileName (
2 |   id int
3 | );
4 | 


--------------------------------------------------------------------------------
/storages/memory-storage/Cargo.toml:
--------------------------------------------------------------------------------
 1 | [package]
 2 | name = "gluesql_memory_storage"
 3 | authors = ["Taehoon Moon <taehoon.moon@outlook.com>"]
 4 | version.workspace = true
 5 | edition.workspace = true
 6 | description.workspace = true
 7 | license.workspace = true
 8 | repository.workspace = true
 9 | documentation.workspace = true
10 | 
11 | [dependencies]
12 | gluesql-core.workspace = true
13 | async-trait = "0.1"
14 | serde = { version = "1", features = ["derive"] }
15 | futures = "0.3"
16 | 
17 | [dev-dependencies]
18 | test-suite.workspace = true
19 | tokio = { version = "1", features = ["rt", "macros"] }
20 | 


--------------------------------------------------------------------------------
/storages/memory-storage/src/index.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     super::MemoryStorage,
 3 |     async_trait::async_trait,
 4 |     gluesql_core::{
 5 |         ast::{IndexOperator, OrderByExpr},
 6 |         data::Value,
 7 |         error::{Error, Result},
 8 |         store::{Index, IndexMut, RowIter},
 9 |     },
10 | };
11 | 
12 | #[async_trait(?Send)]
13 | impl Index for MemoryStorage {
14 |     async fn scan_indexed_data<'a>(
15 |         &'a self,
16 |         _table_name: &str,
17 |         _index_name: &str,
18 |         _asc: Option<bool>,
19 |         _cmp_value: Option<(&IndexOperator, Value)>,
20 |     ) -> Result<RowIter<'a>> {
21 |         Err(Error::StorageMsg(
22 |             "[MemoryStorage] index is not supported".to_owned(),
23 |         ))
24 |     }
25 | }
26 | 
27 | #[async_trait(?Send)]
28 | impl IndexMut for MemoryStorage {
29 |     async fn create_index(
30 |         &mut self,
31 |         _table_name: &str,
32 |         _index_name: &str,
33 |         _column: &OrderByExpr,
34 |     ) -> Result<()> {
35 |         Err(Error::StorageMsg(
36 |             "[MemoryStorage] index is not supported".to_owned(),
37 |         ))
38 |     }
39 | 
40 |     async fn drop_index(&mut self, _table_name: &str, _index_name: &str) -> Result<()> {
41 |         Err(Error::StorageMsg(
42 |             "[MemoryStorage] index is not supported".to_owned(),
43 |         ))
44 |     }
45 | }
46 | 


--------------------------------------------------------------------------------
/storages/memory-storage/src/metadata.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     crate::MemoryStorage,
 3 |     async_trait::async_trait,
 4 |     gluesql_core::{
 5 |         error::Result,
 6 |         store::{MetaIter, Metadata},
 7 |     },
 8 | };
 9 | 
10 | #[async_trait(?Send)]
11 | impl Metadata for MemoryStorage {
12 |     async fn scan_table_meta(&self) -> Result<MetaIter> {
13 |         let meta = self.metadata.clone().into_iter().map(Ok);
14 | 
15 |         Ok(Box::new(meta))
16 |     }
17 | }
18 | 


--------------------------------------------------------------------------------
/storages/memory-storage/src/transaction.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     super::MemoryStorage,
 3 |     async_trait::async_trait,
 4 |     gluesql_core::{
 5 |         error::{Error, Result},
 6 |         store::Transaction,
 7 |     },
 8 | };
 9 | 
10 | #[async_trait(?Send)]
11 | impl Transaction for MemoryStorage {
12 |     async fn begin(&mut self, autocommit: bool) -> Result<bool> {
13 |         if autocommit {
14 |             return Ok(false);
15 |         }
16 | 
17 |         Err(Error::StorageMsg(
18 |             "[MemoryStorage] transaction is not supported".to_owned(),
19 |         ))
20 |     }
21 | 
22 |     async fn rollback(&mut self) -> Result<()> {
23 |         Ok(())
24 |     }
25 | 
26 |     async fn commit(&mut self) -> Result<()> {
27 |         Ok(())
28 |     }
29 | }
30 | 


--------------------------------------------------------------------------------
/storages/mongo-storage/Cargo.toml:
--------------------------------------------------------------------------------
 1 | [package]
 2 | name = "gluesql-mongo-storage"
 3 | authors = [
 4 |   "Hyoungkwan Cho <devgony@gmail.com>",
 5 |   "Taehoon Moon <taehoon.moon@outlook.com>",
 6 | ]
 7 | version.workspace = true
 8 | edition.workspace = true
 9 | description.workspace = true
10 | license.workspace = true
11 | repository.workspace = true
12 | documentation.workspace = true
13 | 
14 | [dependencies]
15 | gluesql-core.workspace = true
16 | 
17 | async-trait = "0.1"
18 | futures = "0.3"
19 | thiserror = "1.0"
20 | mongodb = "2.5.0"
21 | bson = { version = "2.6.1", features = ["chrono-0_4"] }
22 | chrono = { version = "0.4.26", features = ["serde", "wasmbind"] }
23 | strum_macros = "0.24"
24 | strum = "0.24"
25 | rust_decimal = { version = "1", features = ["serde-str"] }
26 | serde_json = "1.0.115"
27 | serde = "1.0.197"
28 | 
29 | [dev-dependencies]
30 | test-suite.workspace = true
31 | tokio = { version = "1", features = ["rt", "macros"] }
32 | 
33 | [features]
34 | test-mongo = []
35 | 


--------------------------------------------------------------------------------
/storages/mongo-storage/README.md:
--------------------------------------------------------------------------------
 1 | ## 🚴 MongoStorage - Mongo storage support for GlueSQL
 2 | 
 3 | ### ⚙️ Prerequisites
 4 | 
 5 | Install & start up MongoDB
 6 | 
 7 | #### 1. By Docker
 8 | 
 9 | ##### 1-1) Install docker
10 | 
11 | https://docs.docker.com/engine/install/
12 | 
13 | ##### 1-2) Start up MongoDB by docker
14 | 
15 | ```
16 | docker run --name mongo-glue -d -p 27017:27017 mongo
17 | ```
18 | 
19 | #### 2. By local installation
20 | 
21 | https://www.mongodb.com/docs/manual/installation/
22 | 
23 | ### 🧪 Test with features
24 | 
25 | ```
26 | cargo test --features test-mongo
27 | ```
28 | 


--------------------------------------------------------------------------------
/storages/mongo-storage/src/description.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     gluesql_core::ast::{Expr, ForeignKey},
 3 |     serde::{Deserialize, Serialize},
 4 | };
 5 | 
 6 | #[derive(Serialize, Deserialize)]
 7 | pub struct TableDescription {
 8 |     pub foreign_keys: Vec<ForeignKey>,
 9 |     pub comment: Option<String>,
10 | }
11 | 
12 | #[derive(Serialize, Deserialize)]
13 | pub struct ColumnDescription {
14 |     pub default: Option<Expr>,
15 |     pub comment: Option<String>,
16 | }
17 | 


--------------------------------------------------------------------------------
/storages/mongo-storage/src/error.rs:
--------------------------------------------------------------------------------
 1 | use {gluesql_core::error::Error, thiserror::Error};
 2 | 
 3 | pub trait ResultExt<T, E: ToString> {
 4 |     fn map_storage_err(self) -> Result<T, Error>;
 5 | }
 6 | 
 7 | impl<T, E: ToString> ResultExt<T, E> for std::result::Result<T, E> {
 8 |     fn map_storage_err(self) -> Result<T, Error> {
 9 |         self.map_err(|e| e.to_string()).map_err(Error::StorageMsg)
10 |     }
11 | }
12 | 
13 | pub trait OptionExt<T, E: ToString> {
14 |     fn map_storage_err(self, error: E) -> Result<T, Error>;
15 | }
16 | 
17 | impl<T, E: ToString> OptionExt<T, E> for std::option::Option<T> {
18 |     fn map_storage_err(self, error: E) -> Result<T, Error> {
19 |         self.ok_or_else(|| error.to_string())
20 |             .map_err(Error::StorageMsg)
21 |     }
22 | }
23 | 
24 | #[derive(Error, Debug)]
25 | pub enum MongoStorageError {
26 |     #[error("invalid document")]
27 |     InvalidDocument,
28 | 
29 |     #[error("unreachable")]
30 |     Unreachable,
31 | 
32 |     #[error("unsupported bson type")]
33 |     UnsupportedBsonType,
34 | 
35 |     #[error(r#"Invalid bsonType - it should be Array eg) ["string"] or ["string", "null"]"#)]
36 |     InvalidBsonType,
37 | 
38 |     #[error("Invalid glueType - it should be type of GlueSQL Value")]
39 |     InvalidGlueType,
40 | }
41 | 


--------------------------------------------------------------------------------
/storages/mongo-storage/src/lib.rs:
--------------------------------------------------------------------------------
 1 | mod description;
 2 | pub mod error;
 3 | pub mod row;
 4 | mod store;
 5 | mod store_mut;
 6 | pub mod utils;
 7 | 
 8 | use {
 9 |     error::ResultExt,
10 |     gluesql_core::{
11 |         error::Result,
12 |         store::{
13 |             AlterTable, CustomFunction, CustomFunctionMut, Index, IndexMut, Metadata, Transaction,
14 |         },
15 |     },
16 |     mongodb::{Client, Database, options::ClientOptions},
17 | };
18 | 
19 | pub struct MongoStorage {
20 |     pub db: Database,
21 | }
22 | 
23 | impl MongoStorage {
24 |     pub async fn new(conn_str: &str, db_name: &str) -> Result<Self> {
25 |         let client_options = ClientOptions::parse(conn_str).await.map_storage_err()?;
26 |         let client = Client::with_options(client_options).map_storage_err()?;
27 |         let db = client.database(db_name);
28 | 
29 |         Ok(Self { db })
30 |     }
31 | 
32 |     pub async fn drop_database(&self) -> Result<()> {
33 |         self.db.drop(None).await.map_storage_err()
34 |     }
35 | }
36 | 
37 | impl Metadata for MongoStorage {}
38 | impl AlterTable for MongoStorage {}
39 | impl CustomFunction for MongoStorage {}
40 | impl CustomFunctionMut for MongoStorage {}
41 | impl Index for MongoStorage {}
42 | impl IndexMut for MongoStorage {}
43 | impl Transaction for MongoStorage {}
44 | 


--------------------------------------------------------------------------------
/storages/mongo-storage/src/row.rs:
--------------------------------------------------------------------------------
 1 | pub mod data_type;
 2 | pub mod key;
 3 | pub mod value;
 4 | 
 5 | use {
 6 |     self::value::IntoValue,
 7 |     crate::error::ResultExt,
 8 |     gluesql_core::{
 9 |         prelude::{DataType, Key, Result},
10 |         store::DataRow,
11 |     },
12 |     mongodb::bson::Document,
13 | };
14 | 
15 | pub trait IntoRow {
16 |     fn into_row<'a>(
17 |         self,
18 |         data_types: impl Iterator<Item = &'a DataType>,
19 |         is_primary: bool,
20 |     ) -> Result<(Key, DataRow)>;
21 | }
22 | 
23 | impl IntoRow for Document {
24 |     fn into_row<'a>(
25 |         self,
26 |         data_types: impl Iterator<Item = &'a DataType>,
27 |         has_primary: bool,
28 |     ) -> Result<(Key, DataRow)> {
29 |         let key = match has_primary {
30 |             true => self.get_binary_generic("_id").map_storage_err()?.to_owned(),
31 |             false => self
32 |                 .get_object_id("_id")
33 |                 .map_storage_err()?
34 |                 .bytes()
35 |                 .to_vec(),
36 |         };
37 |         let key = Key::Bytea(key);
38 | 
39 |         let row = self
40 |             .into_iter()
41 |             .skip(1)
42 |             .zip(data_types)
43 |             .map(|((_, bson), data_type)| bson.into_value(data_type).map_storage_err())
44 |             .collect::<Result<Vec<_>>>()?;
45 | 
46 |         Ok((key, DataRow::Vec(row)))
47 |     }
48 | }
49 | 


--------------------------------------------------------------------------------
/storages/mongo-storage/src/row/key.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     crate::error::MongoStorageError,
 3 |     bson::{Binary, Bson},
 4 |     gluesql_core::prelude::Key,
 5 | };
 6 | 
 7 | type Result<T> = std::result::Result<T, MongoStorageError>;
 8 | 
 9 | pub trait KeyIntoBson {
10 |     fn into_bson(self, has_primary: bool) -> Result<Bson>;
11 | }
12 | 
13 | impl KeyIntoBson for Key {
14 |     fn into_bson(self, has_primary: bool) -> Result<Bson> {
15 |         match has_primary {
16 |             true => Ok(Bson::Binary(Binary {
17 |                 subtype: bson::spec::BinarySubtype::Generic,
18 |                 bytes: self
19 |                     .to_cmp_be_bytes()
20 |                     .map_err(|_| MongoStorageError::UnsupportedBsonType)?,
21 |             })),
22 |             false => into_object_id(self),
23 |         }
24 |     }
25 | }
26 | 
27 | pub fn into_object_id(key: Key) -> Result<Bson> {
28 |     match key {
29 |         Key::Bytea(bytes) => {
30 |             let mut byte_array: [u8; 12] = [0; 12];
31 |             byte_array[..].copy_from_slice(&bytes[..]);
32 | 
33 |             Ok(Bson::ObjectId(bson::oid::ObjectId::from_bytes(byte_array)))
34 |         }
35 |         _ => Err(MongoStorageError::UnsupportedBsonType),
36 |     }
37 | }
38 | 


--------------------------------------------------------------------------------
/storages/mongo-storage/tests/mongo_storage.rs:
--------------------------------------------------------------------------------
 1 | #![cfg(feature = "test-mongo")]
 2 | 
 3 | use {
 4 |     async_trait::async_trait, gluesql_core::prelude::Glue, gluesql_mongo_storage::MongoStorage,
 5 |     test_suite::*,
 6 | };
 7 | 
 8 | struct MongoTester {
 9 |     glue: Glue<MongoStorage>,
10 | }
11 | 
12 | #[async_trait(?Send)]
13 | impl Tester<MongoStorage> for MongoTester {
14 |     async fn new(namespace: &str) -> Self {
15 |         let conn_str = "mongodb://localhost:27017";
16 |         let storage = MongoStorage::new(conn_str, namespace)
17 |             .await
18 |             .expect("MongoStorage::new");
19 |         storage.drop_database().await.expect("database dropped");
20 |         let glue = Glue::new(storage);
21 | 
22 |         MongoTester { glue }
23 |     }
24 | 
25 |     fn get_glue(&mut self) -> &mut Glue<MongoStorage> {
26 |         &mut self.glue
27 |     }
28 | }
29 | 
30 | generate_store_tests!(tokio::test, MongoTester);
31 | generate_alter_table_tests!(tokio::test, MongoTester);
32 | 


--------------------------------------------------------------------------------
/storages/parquet-storage/Cargo.toml:
--------------------------------------------------------------------------------
 1 | [package]
 2 | name = "gluesql-parquet-storage"
 3 | authors = ["MeenSeek Kim <meenseek5929@naver.com>"]
 4 | version.workspace = true
 5 | edition.workspace = true
 6 | description.workspace = true
 7 | license.workspace = true
 8 | repository.workspace = true
 9 | documentation.workspace = true
10 | 
11 | [dependencies]
12 | gluesql-core.workspace = true
13 | async-trait = "0.1"
14 | serde = { version = "1", features = ["derive"] }
15 | serde_json = "1.0.115"
16 | bincode = "1.3"
17 | futures = "0.3"
18 | tokio = { version = "1", features = ["rt", "macros"] }
19 | thiserror = "1.0"
20 | parquet = "43.0.0"
21 | byteorder = "1.4.3"
22 | num-traits = "0.2.5"
23 | rust_decimal = "1.15"
24 | lazy_static = "1.4.0"
25 | 
26 | [dev-dependencies]
27 | test-suite.workspace = true
28 | uuid = "1"
29 | 


--------------------------------------------------------------------------------
/storages/parquet-storage/src/alter_table.rs:
--------------------------------------------------------------------------------
1 | use {super::ParquetStorage, gluesql_core::store::AlterTable};
2 | 
3 | impl AlterTable for ParquetStorage {}
4 | 


--------------------------------------------------------------------------------
/storages/parquet-storage/src/function.rs:
--------------------------------------------------------------------------------
1 | use {
2 |     super::ParquetStorage,
3 |     gluesql_core::store::{CustomFunction, CustomFunctionMut},
4 | };
5 | 
6 | impl CustomFunctionMut for ParquetStorage {}
7 | impl CustomFunction for ParquetStorage {}
8 | 


--------------------------------------------------------------------------------
/storages/parquet-storage/src/index.rs:
--------------------------------------------------------------------------------
1 | use {
2 |     super::ParquetStorage,
3 |     gluesql_core::store::{Index, IndexMut},
4 | };
5 | 
6 | impl Index for ParquetStorage {}
7 | impl IndexMut for ParquetStorage {}
8 | 


--------------------------------------------------------------------------------
/storages/parquet-storage/src/transaction.rs:
--------------------------------------------------------------------------------
1 | use {super::ParquetStorage, gluesql_core::store::Transaction};
2 | 
3 | impl Transaction for ParquetStorage {}
4 | 


--------------------------------------------------------------------------------
/storages/parquet-storage/tests/error.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     gluesql_core::prelude::Glue,
 3 |     gluesql_parquet_storage::{ParquetStorage, error::ParquetStorageError},
 4 | };
 5 | 
 6 | #[tokio::test]
 7 | async fn test_from_parquet_storage_error_to_error() {
 8 |     let path_str = "./tests/samples/";
 9 |     let parquet_storage = ParquetStorage::new(path_str).unwrap();
10 |     let mut glue = Glue::new(parquet_storage);
11 | 
12 |     let cases = vec![(
13 |         glue.execute("SELECT * FROM nested_maps_snappy").await,
14 |         Err(ParquetStorageError::UnexpectedKeyTypeForMap("Int(1)".to_owned()).into()),
15 |     )];
16 | 
17 |     for (actual, expected) in cases {
18 |         assert_eq!(actual.map(|mut payloads| payloads.remove(0)), expected);
19 |     }
20 | }
21 | 


--------------------------------------------------------------------------------
/storages/parquet-storage/tests/parquet_storage.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     async_trait::async_trait, gluesql_core::prelude::Glue, gluesql_parquet_storage::ParquetStorage,
 3 |     std::fs::remove_dir_all, test_suite::*,
 4 | };
 5 | 
 6 | struct ParquetTester {
 7 |     glue: Glue<ParquetStorage>,
 8 | }
 9 | 
10 | #[async_trait(?Send)]
11 | impl Tester<ParquetStorage> for ParquetTester {
12 |     async fn new(namespace: &str) -> Self {
13 |         let path: String = format!("tmp/{namespace}");
14 | 
15 |         if let Err(e) = remove_dir_all(&path) {
16 |             println!("fs::remove_file {:?}", e);
17 |         }
18 |         let storage = ParquetStorage::new(&path).expect("ParquetStorage::new");
19 |         let glue = Glue::new(storage);
20 | 
21 |         ParquetTester { glue }
22 |     }
23 | 
24 |     fn get_glue(&mut self) -> &mut Glue<ParquetStorage> {
25 |         &mut self.glue
26 |     }
27 | }
28 | 
29 | generate_store_tests!(tokio::test, ParquetTester);
30 | generate_alter_table_tests!(tokio::test, ParquetTester);
31 | 


--------------------------------------------------------------------------------
/storages/parquet-storage/tests/samples/all_types_with_nulls.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gluesql/gluesql/78b51bf019fc8c29a51182e464357bd76df35700/storages/parquet-storage/tests/samples/all_types_with_nulls.parquet


--------------------------------------------------------------------------------
/storages/parquet-storage/tests/samples/alltypes_dictionary.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gluesql/gluesql/78b51bf019fc8c29a51182e464357bd76df35700/storages/parquet-storage/tests/samples/alltypes_dictionary.parquet


--------------------------------------------------------------------------------
/storages/parquet-storage/tests/samples/alltypes_plain.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gluesql/gluesql/78b51bf019fc8c29a51182e464357bd76df35700/storages/parquet-storage/tests/samples/alltypes_plain.parquet


--------------------------------------------------------------------------------
/storages/parquet-storage/tests/samples/alltypes_plain_snappy.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gluesql/gluesql/78b51bf019fc8c29a51182e464357bd76df35700/storages/parquet-storage/tests/samples/alltypes_plain_snappy.parquet


--------------------------------------------------------------------------------
/storages/parquet-storage/tests/samples/nested_lists_snappy.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gluesql/gluesql/78b51bf019fc8c29a51182e464357bd76df35700/storages/parquet-storage/tests/samples/nested_lists_snappy.parquet


--------------------------------------------------------------------------------
/storages/parquet-storage/tests/samples/nested_maps_snappy.parquet:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gluesql/gluesql/78b51bf019fc8c29a51182e464357bd76df35700/storages/parquet-storage/tests/samples/nested_maps_snappy.parquet


--------------------------------------------------------------------------------
/storages/redb-storage/Cargo.toml:
--------------------------------------------------------------------------------
 1 | [package]
 2 | name = "gluesql-redb-storage"
 3 | authors = ["Taehoon Moon <taehoon.moon@outlook.com>"]
 4 | version.workspace = true
 5 | edition.workspace = true
 6 | description.workspace = true
 7 | license.workspace = true
 8 | repository.workspace = true
 9 | documentation.workspace = true
10 | 
11 | [dependencies]
12 | gluesql-core.workspace = true
13 | redb = "2.6"
14 | async-trait = "0.1"
15 | serde = { version = "1", features = ["derive"] }
16 | futures = "0.3"
17 | thiserror = "2.0.3"
18 | bincode = "1.3.3"
19 | uuid = { version = "1.11.0", features = ["v7"] }
20 | async-stream = "0.3.6"
21 | 
22 | [dev-dependencies]
23 | test-suite.workspace = true
24 | tokio = { version = "1", features = ["rt", "macros"] }
25 | 


--------------------------------------------------------------------------------
/storages/redb-storage/src/error.rs:
--------------------------------------------------------------------------------
 1 | use {gluesql_core::error::Error, thiserror::Error as ThisError};
 2 | 
 3 | #[derive(ThisError, Debug)]
 4 | pub enum StorageError {
 5 |     #[error("nested transaction is not supported")]
 6 |     NestedTransactionNotSupported,
 7 |     #[error("transaction not found")]
 8 |     TransactionNotFound,
 9 |     #[error("cannot create table with reserved name: {0}")]
10 |     ReservedTableName(String),
11 | 
12 |     #[error(transparent)]
13 |     Glue(#[from] Error),
14 | 
15 |     #[error(transparent)]
16 |     RedbDatabase(#[from] redb::DatabaseError),
17 |     #[error(transparent)]
18 |     RedbStorage(#[from] redb::StorageError),
19 |     #[error(transparent)]
20 |     RedbTable(#[from] redb::TableError),
21 |     #[error(transparent)]
22 |     RedbTransaction(Box<redb::TransactionError>),
23 |     #[error(transparent)]
24 |     RedbCommit(#[from] redb::CommitError),
25 | 
26 |     #[error(transparent)]
27 |     Bincode(#[from] bincode::Error),
28 | }
29 | 
30 | impl From<StorageError> for Error {
31 |     fn from(e: StorageError) -> Error {
32 |         Error::StorageMsg(e.to_string())
33 |     }
34 | }
35 | 
36 | impl From<redb::TransactionError> for StorageError {
37 |     fn from(e: redb::TransactionError) -> StorageError {
38 |         StorageError::RedbTransaction(Box::new(e))
39 |     }
40 | }
41 | 


--------------------------------------------------------------------------------
/storages/redb-storage/tests/nested_transaction.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     gluesql_core::prelude::{Error, Glue},
 3 |     gluesql_redb_storage::RedbStorage,
 4 | };
 5 | 
 6 | #[tokio::test]
 7 | async fn redb_nested_transaction() {
 8 |     let _ = std::fs::create_dir("tmp");
 9 |     let path = "tmp/redb_nested_transaction";
10 |     let _ = std::fs::remove_file(path);
11 | 
12 |     let storage = RedbStorage::new(path).unwrap();
13 |     let mut glue = Glue::new(storage);
14 | 
15 |     glue.execute("BEGIN").await.unwrap();
16 |     let result = glue.execute("BEGIN").await;
17 |     assert_eq!(
18 |         result,
19 |         Err(Error::StorageMsg(
20 |             "nested transaction is not supported".to_owned()
21 |         ))
22 |         .map(|payload| vec![payload])
23 |     );
24 |     glue.execute("COMMIT;").await.unwrap();
25 | }
26 | 


--------------------------------------------------------------------------------
/storages/redb-storage/tests/redb_storage.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     async_trait::async_trait,
 3 |     gluesql_core::prelude::Glue,
 4 |     gluesql_redb_storage::RedbStorage,
 5 |     std::fs::{create_dir, remove_file},
 6 |     test_suite::*,
 7 | };
 8 | 
 9 | struct RedbTester {
10 |     glue: Glue<RedbStorage>,
11 | }
12 | 
13 | #[async_trait(?Send)]
14 | impl Tester<RedbStorage> for RedbTester {
15 |     async fn new(namespace: &str) -> Self {
16 |         let _ = create_dir("tmp");
17 |         let path = format!("tmp/{}", namespace);
18 |         let _ = remove_file(&path);
19 | 
20 |         let storage = RedbStorage::new(path).expect("[RedbTester] failed to create storage");
21 |         let glue = Glue::new(storage);
22 | 
23 |         Self { glue }
24 |     }
25 | 
26 |     fn get_glue(&mut self) -> &mut Glue<RedbStorage> {
27 |         &mut self.glue
28 |     }
29 | }
30 | 
31 | generate_store_tests!(tokio::test, RedbTester);
32 | generate_transaction_tests!(tokio::test, RedbTester);
33 | 


--------------------------------------------------------------------------------
/storages/redb-storage/tests/reserved_table_name.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     gluesql_core::prelude::{Error, Glue},
 3 |     gluesql_redb_storage::RedbStorage,
 4 |     std::fs::{create_dir, remove_file},
 5 | };
 6 | 
 7 | #[tokio::test]
 8 | async fn reserved_table_name() {
 9 |     let _ = create_dir("tmp");
10 |     let path = "tmp/redb_reserved_table_name";
11 |     let _ = remove_file(path);
12 | 
13 |     let storage = RedbStorage::new(path).unwrap();
14 |     let mut glue = Glue::new(storage);
15 | 
16 |     let result = glue.execute("CREATE TABLE __SCHEMA__ (id INTEGER);").await;
17 | 
18 |     assert_eq!(
19 |         result,
20 |         Err(Error::StorageMsg(
21 |             "cannot create table with reserved name: __SCHEMA__".to_owned(),
22 |         ))
23 |         .map(|payload| vec![payload])
24 |     );
25 | }
26 | 


--------------------------------------------------------------------------------
/storages/redb-storage/tests/storage_interface_error.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     gluesql_core::error::Error,
 3 |     gluesql_redb_storage::RedbStorage,
 4 |     std::fs::{create_dir, remove_file},
 5 | };
 6 | 
 7 | #[tokio::test]
 8 | async fn redb_storage_interface_error() {
 9 |     let _ = create_dir("tmp");
10 |     let path = "tmp/redb_storage_interface_error";
11 |     let _ = remove_file(path);
12 | 
13 |     let storage1 = RedbStorage::new(path).expect("open first storage");
14 | 
15 |     // Attempt to open the same database again using the storage interface
16 |     let result = RedbStorage::new(path);
17 |     let err = result.err().expect("second open should fail");
18 | 
19 |     match err {
20 |         Error::StorageMsg(msg) => assert!(msg.contains("Database already open")),
21 |         other => panic!("unexpected error: {other:?}"),
22 |     }
23 | 
24 |     drop(storage1);
25 | }
26 | 


--------------------------------------------------------------------------------
/storages/redis-storage/Cargo.toml:
--------------------------------------------------------------------------------
 1 | [package]
 2 | name = "gluesql-redis-storage"
 3 | authors = [
 4 |     "Gioh Kim <gurugio@gmail.com>",
 5 |     "Taehoon Moon <taehoon.moon@outlook.com>",
 6 | ]
 7 | version.workspace = true
 8 | edition.workspace = true
 9 | description.workspace = true
10 | license.workspace = true
11 | repository.workspace = true
12 | documentation.workspace = true
13 | 
14 | [dependencies]
15 | gluesql-core.workspace = true
16 | async-trait = "0.1"
17 | serde = { version = "1", features = ["derive"] }
18 | redis = "0.23.3"
19 | serde_json = "1.0.105"
20 | chrono = { version = "0.4.31", features = ["serde", "wasmbind"] }
21 | futures = "0.3"
22 | 
23 | [dev-dependencies]
24 | test-suite.workspace = true
25 | tokio = { version = "1", features = ["rt", "macros"] }
26 | toml = "0.8.6"
27 | 
28 | [features]
29 | test-redis = []
30 | 


--------------------------------------------------------------------------------
/storages/redis-storage/src/index.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     super::RedisStorage,
 3 |     async_trait::async_trait,
 4 |     gluesql_core::{
 5 |         ast::{IndexOperator, OrderByExpr},
 6 |         data::Value,
 7 |         error::{Error, Result},
 8 |         store::{Index, IndexMut, RowIter},
 9 |     },
10 | };
11 | 
12 | // Index is one of MUST-be-implemented traits.
13 | 
14 | #[async_trait(?Send)]
15 | impl Index for RedisStorage {
16 |     async fn scan_indexed_data<'a>(
17 |         &'a self,
18 |         _table_name: &str,
19 |         _index_name: &str,
20 |         _asc: Option<bool>,
21 |         _cmp_value: Option<(&IndexOperator, Value)>,
22 |     ) -> Result<RowIter<'a>> {
23 |         Err(Error::StorageMsg(
24 |             "[RedisStorage] index is not supported".to_owned(),
25 |         ))
26 |     }
27 | }
28 | 
29 | #[async_trait(?Send)]
30 | impl IndexMut for RedisStorage {
31 |     async fn create_index(
32 |         &mut self,
33 |         _table_name: &str,
34 |         _index_name: &str,
35 |         _column: &OrderByExpr,
36 |     ) -> Result<()> {
37 |         Err(Error::StorageMsg(
38 |             "[RedisStorage] index is not supported".to_owned(),
39 |         ))
40 |     }
41 | 
42 |     async fn drop_index(&mut self, _table_name: &str, _index_name: &str) -> Result<()> {
43 |         Err(Error::StorageMsg(
44 |             "[RedisStorage] index is not supported".to_owned(),
45 |         ))
46 |     }
47 | }
48 | 


--------------------------------------------------------------------------------
/storages/redis-storage/src/transaction.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     super::RedisStorage,
 3 |     async_trait::async_trait,
 4 |     gluesql_core::{
 5 |         error::{Error, Result},
 6 |         store::Transaction,
 7 |     },
 8 | };
 9 | 
10 | #[async_trait(?Send)]
11 | impl Transaction for RedisStorage {
12 |     async fn begin(&mut self, autocommit: bool) -> Result<bool> {
13 |         if autocommit {
14 |             return Ok(false);
15 |         }
16 | 
17 |         Err(Error::StorageMsg(
18 |             "[RedisStorage] transaction is not supported".to_owned(),
19 |         ))
20 |     }
21 | 
22 |     async fn rollback(&mut self) -> Result<()> {
23 |         Ok(())
24 |     }
25 | 
26 |     async fn commit(&mut self) -> Result<()> {
27 |         Ok(())
28 |     }
29 | }
30 | 


--------------------------------------------------------------------------------
/storages/redis-storage/tests/redis-storage.toml:
--------------------------------------------------------------------------------
1 | [redis]
2 | url="localhost"
3 | port=6379
4 | 


--------------------------------------------------------------------------------
/storages/shared-memory-storage/Cargo.toml:
--------------------------------------------------------------------------------
 1 | [package]
 2 | name = "gluesql-shared-memory-storage"
 3 | authors = [
 4 | 	"Jiseok CHOI <jiseok.dev@gmail.com>",
 5 | 	"Taehoon Moon <taehoon.moon@outlook.com>",
 6 | ]
 7 | version.workspace = true
 8 | edition.workspace = true
 9 | description.workspace = true
10 | license.workspace = true
11 | repository.workspace = true
12 | documentation.workspace = true
13 | 
14 | [dependencies]
15 | gluesql-core.workspace = true
16 | gluesql_memory_storage.workspace = true
17 | 
18 | async-trait = "0.1"
19 | serde = { version = "1", features = ["derive"] }
20 | tokio = { version = "1", features = ["sync"] }
21 | futures = "0.3"
22 | 
23 | [dev-dependencies]
24 | test-suite.workspace = true
25 | tokio = { version = "1", features = ["rt", "macros"] }
26 | 


--------------------------------------------------------------------------------
/storages/shared-memory-storage/src/index.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     super::SharedMemoryStorage,
 3 |     async_trait::async_trait,
 4 |     gluesql_core::{
 5 |         ast::{IndexOperator, OrderByExpr},
 6 |         data::Value,
 7 |         error::{Error, Result},
 8 |         store::{Index, IndexMut, RowIter},
 9 |     },
10 | };
11 | 
12 | #[async_trait(?Send)]
13 | impl Index for SharedMemoryStorage {
14 |     async fn scan_indexed_data<'a>(
15 |         &'a self,
16 |         _table_name: &str,
17 |         _index_name: &str,
18 |         _asc: Option<bool>,
19 |         _cmp_value: Option<(&IndexOperator, Value)>,
20 |     ) -> Result<RowIter<'a>> {
21 |         Err(Error::StorageMsg(
22 |             "[Shared MemoryStorage] index is not supported".to_owned(),
23 |         ))
24 |     }
25 | }
26 | 
27 | #[async_trait(?Send)]
28 | impl IndexMut for SharedMemoryStorage {
29 |     async fn create_index(
30 |         &mut self,
31 |         _table_name: &str,
32 |         _index_name: &str,
33 |         _column: &OrderByExpr,
34 |     ) -> Result<()> {
35 |         Err(Error::StorageMsg(
36 |             "[Shared MemoryStorage] index is not supported".to_owned(),
37 |         ))
38 |     }
39 | 
40 |     async fn drop_index(&mut self, _table_name: &str, _index_name: &str) -> Result<()> {
41 |         Err(Error::StorageMsg(
42 |             "[Shared MemoryStorage] index is not supported".to_owned(),
43 |         ))
44 |     }
45 | }
46 | 


--------------------------------------------------------------------------------
/storages/shared-memory-storage/src/transaction.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     super::SharedMemoryStorage,
 3 |     async_trait::async_trait,
 4 |     gluesql_core::{
 5 |         error::{Error, Result},
 6 |         store::Transaction,
 7 |     },
 8 | };
 9 | 
10 | #[async_trait(?Send)]
11 | impl Transaction for SharedMemoryStorage {
12 |     async fn begin(&mut self, autocommit: bool) -> Result<bool> {
13 |         if autocommit {
14 |             return Ok(false);
15 |         }
16 | 
17 |         Err(Error::StorageMsg(
18 |             "[Shared MemoryStorage] transaction is not supported".to_owned(),
19 |         ))
20 |     }
21 | 
22 |     async fn rollback(&mut self) -> Result<()> {
23 |         Err(Error::StorageMsg(
24 |             "[Shared MemoryStorage] transaction is not supported".to_owned(),
25 |         ))
26 |     }
27 | 
28 |     async fn commit(&mut self) -> Result<()> {
29 |         Err(Error::StorageMsg(
30 |             "[Shared MemoryStorage] transaction is not supported".to_owned(),
31 |         ))
32 |     }
33 | }
34 | 


--------------------------------------------------------------------------------
/storages/sled-storage/Cargo.toml:
--------------------------------------------------------------------------------
 1 | [package]
 2 | name = "gluesql_sled_storage"
 3 | authors = ["Taehoon Moon <taehoon.moon@outlook.com>"]
 4 | version.workspace = true
 5 | edition.workspace = true
 6 | description.workspace = true
 7 | license.workspace = true
 8 | repository.workspace = true
 9 | documentation.workspace = true
10 | 
11 | [dependencies]
12 | gluesql-core.workspace = true
13 | utils.workspace = true
14 | async-trait = "0.1"
15 | iter-enum = "1"
16 | serde = { version = "1", features = ["derive"] }
17 | thiserror = "1"
18 | bincode = "1"
19 | sled = "0.34"
20 | async-io = "1"
21 | futures = "0.3"
22 | 
23 | [dev-dependencies]
24 | test-suite.workspace = true
25 | criterion = "0.3"
26 | tokio = { version = "1", features = ["rt", "macros"] }
27 | 
28 | [[bench]]
29 | name = "sled_benchmark"
30 | harness = false
31 | 


--------------------------------------------------------------------------------
/storages/web-storage/Cargo.toml:
--------------------------------------------------------------------------------
 1 | [package]
 2 | name = "gluesql-web-storage"
 3 | authors = ["Taehoon Moon <taehoon.moon@outlook.com>"]
 4 | version.workspace = true
 5 | edition.workspace = true
 6 | description.workspace = true
 7 | license.workspace = true
 8 | repository.workspace = true
 9 | documentation.workspace = true
10 | 
11 | [dependencies]
12 | gluesql-core.workspace = true
13 | async-trait = "0.1"
14 | serde = { version = "1", features = ["derive"] }
15 | gloo-storage = "0.2.2"
16 | uuid = { version = "1.2.2", features = ["v4", "v7"] }
17 | web-sys = { version = "0.3.60" }
18 | futures = "0.3"
19 | 
20 | [dev-dependencies]
21 | test-suite.workspace = true
22 | wasm-bindgen-test = "0.3.33"
23 | 


--------------------------------------------------------------------------------
/storages/web-storage/README.md:
--------------------------------------------------------------------------------
1 | ## 🚴 WebStorage
2 | * localStorage
3 | * sessionStorage
4 | 
5 | ### 🔬 Test in Headless Browsers with `wasm-pack test`
6 | ```
7 | wasm-pack test --headless --firefox --chrome
8 | ```
9 | 


--------------------------------------------------------------------------------
/storages/web-storage/tests/local_storage.rs:
--------------------------------------------------------------------------------
 1 | #![cfg(target_arch = "wasm32")]
 2 | 
 3 | use {
 4 |     async_trait::async_trait,
 5 |     gluesql_core::prelude::Glue,
 6 |     gluesql_web_storage::{WebStorage, WebStorageType},
 7 |     test_suite::*,
 8 |     wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure},
 9 | };
10 | 
11 | wasm_bindgen_test_configure!(run_in_browser);
12 | 
13 | struct LocalStorageTester {
14 |     glue: Glue<WebStorage>,
15 | }
16 | 
17 | #[async_trait(?Send)]
18 | impl Tester<WebStorage> for LocalStorageTester {
19 |     async fn new(_: &str) -> Self {
20 |         let storage = WebStorage::new(WebStorageType::Local);
21 |         storage.raw().clear().unwrap();
22 | 
23 |         let glue = Glue::new(storage);
24 | 
25 |         LocalStorageTester { glue }
26 |     }
27 | 
28 |     fn get_glue(&mut self) -> &mut Glue<WebStorage> {
29 |         &mut self.glue
30 |     }
31 | }
32 | 
33 | generate_store_tests!(wasm_bindgen_test, LocalStorageTester);
34 | generate_alter_table_tests!(wasm_bindgen_test, LocalStorageTester);
35 | 


--------------------------------------------------------------------------------
/storages/web-storage/tests/session_storage.rs:
--------------------------------------------------------------------------------
 1 | #![cfg(target_arch = "wasm32")]
 2 | 
 3 | use {
 4 |     async_trait::async_trait,
 5 |     gluesql_core::prelude::Glue,
 6 |     gluesql_web_storage::{WebStorage, WebStorageType},
 7 |     test_suite::*,
 8 |     wasm_bindgen_test::{wasm_bindgen_test, wasm_bindgen_test_configure},
 9 | };
10 | 
11 | wasm_bindgen_test_configure!(run_in_browser);
12 | 
13 | struct SessionStorageTester {
14 |     glue: Glue<WebStorage>,
15 | }
16 | 
17 | #[async_trait(?Send)]
18 | impl Tester<WebStorage> for SessionStorageTester {
19 |     async fn new(_: &str) -> Self {
20 |         let storage = WebStorage::new(WebStorageType::Session);
21 |         storage.raw().clear().unwrap();
22 | 
23 |         let glue = Glue::new(storage);
24 | 
25 |         SessionStorageTester { glue }
26 |     }
27 | 
28 |     fn get_glue(&mut self) -> &mut Glue<WebStorage> {
29 |         &mut self.glue
30 |     }
31 | }
32 | 
33 | generate_store_tests!(wasm_bindgen_test, SessionStorageTester);
34 | generate_alter_table_tests!(wasm_bindgen_test, SessionStorageTester);
35 | 


--------------------------------------------------------------------------------
/test-suite/Cargo.toml:
--------------------------------------------------------------------------------
 1 | [package]
 2 | name = "gluesql-test-suite"
 3 | authors = ["Taehoon Moon <taehoon.moon@outlook.com>"]
 4 | version.workspace = true
 5 | edition.workspace = true
 6 | description.workspace = true
 7 | license.workspace = true
 8 | repository.workspace = true
 9 | documentation.workspace = true
10 | 
11 | [dependencies]
12 | gluesql-core.workspace = true
13 | async-trait = "0.1"
14 | bigdecimal = "0.4.2"
15 | chrono = "0.4.31"
16 | rust_decimal = "1"
17 | hex = "0.4"
18 | serde_json = "1.0.91"
19 | pretty_assertions = "1"
20 | 
21 | [target.'cfg(target_arch = "wasm32")'.dependencies.uuid]
22 | version = "1"
23 | features = ["v4", "v7", "js"]
24 | [target.'cfg(not(target_arch = "wasm32"))'.dependencies.uuid]
25 | version = "1"
26 | features = ["v4", "v7"]
27 | 


--------------------------------------------------------------------------------
/test-suite/src/aggregate.rs:
--------------------------------------------------------------------------------
 1 | pub mod avg;
 2 | pub mod count;
 3 | pub mod error;
 4 | pub mod group_by;
 5 | pub mod max;
 6 | pub mod min;
 7 | pub mod stdev;
 8 | pub mod sum;
 9 | pub mod variance;
10 | 


--------------------------------------------------------------------------------
/test-suite/src/aggregate/avg.rs:
--------------------------------------------------------------------------------
 1 | use {crate::*, gluesql_core::prelude::Value::*};
 2 | 
 3 | test_case!(avg, {
 4 |     let g = get_tester!();
 5 | 
 6 |     g.run(
 7 |         "
 8 |         CREATE TABLE Item (
 9 |             id INTEGER,
10 |             quantity INTEGER,
11 |             age INTEGER NULL,
12 |             total INTEGER
13 |         );
14 |     ",
15 |     )
16 |     .await;
17 |     g.run(
18 |         "
19 |         INSERT INTO Item (id, quantity, age, total) VALUES
20 |             (1, 10,   11, 1),
21 |             (2,  0,   90, 2),
22 |             (3,  9, NULL, 3),
23 |             (4,  3,    3, 1),
24 |             (5, 25, NULL, 1);
25 |     ",
26 |     )
27 |     .await;
28 | 
29 |     let test_cases = [
30 |         (
31 |             "SELECT AVG(age) FROM Item",
32 |             select_with_null!("AVG(age)"; Null),
33 |         ),
34 |         (
35 |             "SELECT AVG(id), AVG(quantity) FROM Item",
36 |             select!(
37 |                 "AVG(id)" | "AVG(quantity)"
38 |                 F64       | F64;
39 |                 3.0         9.4
40 |             ),
41 |         ),
42 |     ];
43 | 
44 |     for (sql, expected) in test_cases {
45 |         g.test(sql, Ok(expected)).await;
46 |     }
47 | });
48 | 


--------------------------------------------------------------------------------
/test-suite/src/aggregate/error.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     crate::*,
 3 |     gluesql_core::error::{EvaluateError, TranslateError},
 4 | };
 5 | 
 6 | test_case!(error, {
 7 |     let g = get_tester!();
 8 | 
 9 |     g.run(
10 |         "
11 |         CREATE TABLE Item (
12 |             id INTEGER,
13 |             quantity INTEGER,
14 |             age INTEGER NULL,
15 |             total INTEGER
16 |         );
17 |     ",
18 |     )
19 |     .await;
20 |     g.run(
21 |         "
22 |         INSERT INTO Item (id, quantity, age, total) VALUES
23 |             (1, 10,   11, 1),
24 |             (2,  0,   90, 2),
25 |             (3,  9, NULL, 3),
26 |             (4,  3,    3, 1),
27 |             (5, 25, NULL, 1);
28 |     ",
29 |     )
30 |     .await;
31 | 
32 |     let test_cases = [
33 |         (
34 |             "SELECT SUM(num) FROM Item;",
35 |             EvaluateError::IdentifierNotFound("num".to_owned()).into(),
36 |         ),
37 |         (
38 |             "SELECT COUNT(Foo.*) FROM Item;",
39 |             TranslateError::QualifiedWildcardInCountNotSupported("Foo.*".to_owned()).into(),
40 |         ),
41 |         (
42 |             "SELECT SUM(*) FROM Item;",
43 |             TranslateError::WildcardFunctionArgNotAccepted.into(),
44 |         ),
45 |     ];
46 | 
47 |     for (sql, error) in test_cases {
48 |         g.test(sql, Err(error)).await;
49 |     }
50 | });
51 | 


--------------------------------------------------------------------------------
/test-suite/src/aggregate/stdev.rs:
--------------------------------------------------------------------------------
 1 | use {crate::*, gluesql_core::prelude::Value::*};
 2 | 
 3 | test_case!(stdev, {
 4 |     let g = get_tester!();
 5 | 
 6 |     g.run(
 7 |         "
 8 |     CREATE TABLE Item (
 9 |         id INTEGER,
10 |         quantity INTEGER,
11 |         age INTEGER NULL,
12 |         total INTEGER
13 |     );
14 |     ",
15 |     )
16 |     .await;
17 |     g.run(
18 |         "
19 |     INSERT INTO Item (id, quantity, age, total) VALUES
20 |         (1, 10,   11, 1),
21 |         (2,  0,   90, 2),
22 |         (3,  9, NULL, 3),
23 |         (4,  3,    3, 1),
24 |         (5, 25, NULL, 1);
25 |     ",
26 |     )
27 |     .await;
28 | 
29 |     let test_cases = [
30 |         (
31 |             "SELECT STDEV(age) FROM Item",
32 |             select_with_null!("STDEV(age)"; Null),
33 |         ),
34 |         (
35 |             "SELECT STDEV(total) FROM Item",
36 |             select!(
37 |                 "STDEV(total)"
38 |                 F64;
39 |                 0.8
40 |             ),
41 |         ),
42 |     ];
43 | 
44 |     for (sql, expected) in test_cases {
45 |         g.test(sql, Ok(expected)).await;
46 |     }
47 | });
48 | 


--------------------------------------------------------------------------------
/test-suite/src/aggregate/variance.rs:
--------------------------------------------------------------------------------
 1 | use {crate::*, gluesql_core::prelude::Value::*};
 2 | 
 3 | test_case!(variance, {
 4 |     let g = get_tester!();
 5 | 
 6 |     g.run(
 7 |         "
 8 |     CREATE TABLE Item (
 9 |         id INTEGER,
10 |         quantity INTEGER,
11 |         age INTEGER NULL,
12 |         total INTEGER
13 |     );
14 |     ",
15 |     )
16 |     .await;
17 |     g.run(
18 |         "
19 |     INSERT INTO Item (id, quantity, age, total) VALUES
20 |         (1, 10,   11, 1),
21 |         (2,  0,   90, 2),
22 |         (3,  9, NULL, 3),
23 |         (4,  3,    3, 1),
24 |         (5, 25, NULL, 1);
25 |     ",
26 |     )
27 |     .await;
28 | 
29 |     let test_cases = [
30 |         (
31 |             "SELECT VARIANCE(age) FROM Item",
32 |             select_with_null!("VARIANCE(age)"; Null),
33 |         ),
34 |         (
35 |             "SELECT VARIANCE(id), VARIANCE(quantity) FROM Item",
36 |             select!(
37 |                 "VARIANCE(id)" | "VARIANCE(quantity)"
38 |                 F64            | F64;
39 |                 2.0              74.64
40 |             ),
41 |         ),
42 |     ];
43 | 
44 |     for (sql, expected) in test_cases {
45 |         g.test(sql, Ok(expected)).await;
46 |     }
47 | });
48 | 


--------------------------------------------------------------------------------
/test-suite/src/alter.rs:
--------------------------------------------------------------------------------
 1 | mod alter_table;
 2 | mod create_table;
 3 | mod drop_indexed;
 4 | mod drop_table;
 5 | 
 6 | pub use {
 7 |     alter_table::{alter_table_add_drop, alter_table_rename},
 8 |     create_table::create_table,
 9 |     drop_indexed::{drop_indexed_column, drop_indexed_table},
10 |     drop_table::drop_table,
11 | };
12 | 


--------------------------------------------------------------------------------
/test-suite/src/arithmetic.rs:
--------------------------------------------------------------------------------
1 | pub mod error;
2 | pub mod on_where;
3 | pub mod project;
4 | 


--------------------------------------------------------------------------------
/test-suite/src/ast_builder.rs:
--------------------------------------------------------------------------------
 1 | pub mod alias_as;
 2 | pub mod basic;
 3 | pub mod delete;
 4 | pub mod expr;
 5 | pub mod function;
 6 | pub mod index_by;
 7 | pub mod insert;
 8 | pub mod schemaless;
 9 | pub mod select;
10 | pub mod statements;
11 | pub mod update;
12 | pub mod values;
13 | 


--------------------------------------------------------------------------------
/test-suite/src/ast_builder/expr.rs:
--------------------------------------------------------------------------------
1 | pub mod pattern_matching;
2 | 


--------------------------------------------------------------------------------
/test-suite/src/ast_builder/function.rs:
--------------------------------------------------------------------------------
1 | pub mod datetime;
2 | pub mod math;
3 | pub mod other;
4 | pub mod text;
5 | 


--------------------------------------------------------------------------------
/test-suite/src/ast_builder/function/datetime.rs:
--------------------------------------------------------------------------------
1 | mod conversion;
2 | mod current_date_and_time;
3 | mod formatting;
4 | 
5 | pub use {
6 |     conversion::conversion, current_date_and_time::current_date_and_time, formatting::formatting,
7 | };
8 | 


--------------------------------------------------------------------------------
/test-suite/src/ast_builder/function/math.rs:
--------------------------------------------------------------------------------
1 | mod basic_arithmetic;
2 | mod conversion;
3 | mod rounding;
4 | pub use {basic_arithmetic::basic_arithmetic, conversion::conversion, rounding::rounding};
5 | 


--------------------------------------------------------------------------------
/test-suite/src/ast_builder/function/other.rs:
--------------------------------------------------------------------------------
1 | pub mod coalesce;
2 | pub mod ifnull;
3 | 


--------------------------------------------------------------------------------
/test-suite/src/ast_builder/function/text.rs:
--------------------------------------------------------------------------------
 1 | mod trimming;
 2 | pub use trimming::trimming;
 3 | 
 4 | mod case_conversion;
 5 | pub use case_conversion::case_conversion;
 6 | 
 7 | mod character_conversion;
 8 | pub use character_conversion::character_conversion;
 9 | 
10 | mod padding;
11 | pub use padding::padding;
12 | 
13 | mod position_and_indexing;
14 | pub use position_and_indexing::position_and_indexing;
15 | 


--------------------------------------------------------------------------------
/test-suite/src/ast_builder/index_by.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     crate::*,
 3 |     gluesql_core::{
 4 |         ast_builder::*,
 5 |         prelude::{Payload, Value::*},
 6 |     },
 7 | };
 8 | 
 9 | test_case!(index_by, {
10 |     let glue = get_glue!();
11 | 
12 |     let actual = table("Foo")
13 |         .create_table()
14 |         .add_column("id INTEGER PRIMARY KEY")
15 |         .add_column("name TEXT")
16 |         .execute(glue)
17 |         .await;
18 |     let expected = Ok(Payload::Create);
19 |     assert_eq!(actual, expected, "create table - Foo");
20 | 
21 |     let actual = table("Foo")
22 |         .insert()
23 |         .columns("id, name")
24 |         .values(vec![vec![num(1), text("Drink")]])
25 |         .execute(glue)
26 |         .await;
27 |     let expected = Ok(Payload::Insert(1));
28 |     assert_eq!(actual, expected, "insert - specifying columns");
29 | 
30 |     let actual = table("Foo")
31 |         .index_by(primary_key().eq("1"))
32 |         .select()
33 |         .project("id, name")
34 |         .execute(glue)
35 |         .await;
36 |     let expected = Ok(select!(
37 |         id  | name
38 |         I64 | Str;
39 |         1     "Drink".to_owned()
40 |     ));
41 |     assert_eq!(actual, expected, "basic select with index by");
42 | });
43 | 


--------------------------------------------------------------------------------
/test-suite/src/ast_builder/schemaless.rs:
--------------------------------------------------------------------------------
1 | pub mod basic;
2 | 


--------------------------------------------------------------------------------
/test-suite/src/ast_builder/statements.rs:
--------------------------------------------------------------------------------
1 | pub mod querying;
2 | 


--------------------------------------------------------------------------------
/test-suite/src/ast_builder/statements/querying.rs:
--------------------------------------------------------------------------------
1 | pub mod data_aggregation;
2 | pub use data_aggregation::data_aggregation;
3 | 
4 | pub mod data_selection_and_projection;
5 | pub use data_selection_and_projection::data_selection_and_projection;
6 | 


--------------------------------------------------------------------------------
/test-suite/src/data_type.rs:
--------------------------------------------------------------------------------
 1 | pub mod bytea;
 2 | pub mod date;
 3 | pub mod decimal;
 4 | pub mod float32;
 5 | pub mod inet;
 6 | pub mod int128;
 7 | pub mod int16;
 8 | pub mod int32;
 9 | pub mod int64;
10 | pub mod int8;
11 | pub mod interval;
12 | pub mod list;
13 | pub mod map;
14 | pub mod point;
15 | pub mod sql_types;
16 | pub mod time;
17 | pub mod timestamp;
18 | pub mod uint128;
19 | pub mod uint16;
20 | pub mod uint32;
21 | pub mod uint64;
22 | pub mod uint8;
23 | pub mod uuid;
24 | 


--------------------------------------------------------------------------------
/test-suite/src/function.rs:
--------------------------------------------------------------------------------
 1 | pub mod abs;
 2 | pub mod add_month;
 3 | pub mod append;
 4 | pub mod ascii;
 5 | pub mod cast;
 6 | pub mod ceil;
 7 | pub mod chr;
 8 | pub mod coalesce;
 9 | pub mod concat;
10 | pub mod concat_ws;
11 | pub mod dedup;
12 | pub mod degrees;
13 | pub mod div_mod;
14 | pub mod entries;
15 | pub mod exp_log;
16 | pub mod extract;
17 | pub mod find_idx;
18 | pub mod floor;
19 | pub mod format;
20 | pub mod gcd_lcm;
21 | pub mod generate_uuid;
22 | pub mod geometry;
23 | pub mod greatest;
24 | pub mod ifnull;
25 | pub mod initcap;
26 | pub mod is_empty;
27 | pub mod keys;
28 | pub mod last_day;
29 | pub mod left_right;
30 | pub mod length;
31 | pub mod lpad_rpad;
32 | pub mod ltrim_rtrim;
33 | pub mod math_function;
34 | pub mod md5;
35 | pub mod now;
36 | pub mod pi;
37 | pub mod position;
38 | pub mod prepend;
39 | pub mod radians;
40 | pub mod rand;
41 | pub mod repeat;
42 | pub mod replace;
43 | pub mod reverse;
44 | pub mod round;
45 | pub mod sign;
46 | pub mod skip;
47 | pub mod slice;
48 | pub mod sort;
49 | pub mod splice;
50 | pub mod sqrt_power;
51 | pub mod substr;
52 | pub mod take;
53 | pub mod to_date;
54 | pub mod trim;
55 | pub mod upper_lower;
56 | pub mod values;
57 | 


--------------------------------------------------------------------------------
/test-suite/src/function/dedup.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     crate::*,
 3 |     gluesql_core::{executor::EvaluateError, prelude::Value::*},
 4 | };
 5 | 
 6 | test_case!(dedup, {
 7 |     let g = get_tester!();
 8 | 
 9 |     g.named_test(
10 |         "DEDUP(CAST('[1, 2, 3, 3, 4, 5, 5]' AS List)) should return '[1, 2, 3, 4, 5]'",
11 |         "SELECT DEDUP(CAST('[1, 2, 3, 3, 4, 5, 5]' AS List)) as actual",
12 |         Ok(select!(actual List; vec![I64(1), I64(2), I64(3), I64(4), I64(5)])),
13 |     )
14 |     .await;
15 | 
16 |     g.named_test(
17 |         "DEDUP(CAST('['1', 1, '1']' AS List)) should return '['1', 1]'",
18 |         r#"SELECT DEDUP(CAST('["1", 1, 1, "1", "1"]' AS List)) as actual"#,
19 |         Ok(select!(actual List; vec![Str("1".to_owned()), I64(1), Str("1".to_owned())])),
20 |     )
21 |     .await;
22 | 
23 |     g.named_test(
24 |         "DEDUP with invalid value should return EvaluateError::ListTypeRequired",
25 |         "SELECT DEDUP(1) AS actual",
26 |         Err(EvaluateError::ListTypeRequired.into()),
27 |     )
28 |     .await;
29 | });
30 | 


--------------------------------------------------------------------------------
/test-suite/src/function/entries.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     crate::*,
 3 |     gluesql_core::{
 4 |         error::EvaluateError,
 5 |         prelude::{Payload, Value::*},
 6 |     },
 7 | };
 8 | 
 9 | test_case!(entries, {
10 |     let g = get_tester!();
11 | 
12 |     g.named_test(
13 |         "test entries function works while creating a table simultaneously",
14 |         "CREATE TABLE Item (map MAP)",
15 |         Ok(Payload::Create),
16 |     )
17 |     .await;
18 |     g.named_test(
19 |         "test if the sample string gets inserted to table",
20 |         r#"INSERT INTO Item VALUES ('{"name":"GlueSQL"}')"#,
21 |         Ok(Payload::Insert(1)),
22 |     )
23 |     .await;
24 |     g.named_test(
25 |         "check id the entries function works with the previously inserted string",
26 |         "SELECT ENTRIES(map) AS test FROM Item",
27 |         Ok(select!(
28 |             "test";
29 |             List;
30 |             vec![
31 |                 List(vec![Str("name".to_owned()), Str("GlueSQL".to_owned())])
32 |             ]
33 |         )),
34 |     )
35 |     .await;
36 |     g.named_test(
37 |         "test ENTRIES function requires map value",
38 |         "SELECT ENTRIES(1) FROM Item",
39 |         Err(EvaluateError::FunctionRequiresMapValue("ENTRIES".to_owned()).into()),
40 |     )
41 |     .await;
42 | });
43 | 


--------------------------------------------------------------------------------
/test-suite/src/function/generate_uuid.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     crate::*,
 3 |     gluesql_core::{ast::DataType, error::TranslateError},
 4 | };
 5 | 
 6 | test_case!(generate_uuid, {
 7 |     let g = get_tester!();
 8 | 
 9 |     let test_cases = [(
10 |         "SELECT generate_uuid(0) as uuid",
11 |         Err(TranslateError::FunctionArgsLengthNotMatching {
12 |             name: "GENERATE_UUID".to_owned(),
13 |             expected: 0,
14 |             found: 1,
15 |         }
16 |         .into()),
17 |     )];
18 | 
19 |     for (sql, expected) in test_cases {
20 |         g.test(sql, expected).await;
21 |     }
22 | 
23 |     g.count("SELECT GENERATE_UUID()", 1).await;
24 |     g.count("VALUES (GENERATE_UUID())", 1).await;
25 |     g.type_match("SELECT GENERATE_UUID() as uuid", &[DataType::Uuid])
26 |         .await;
27 |     g.type_match("VALUES (GENERATE_UUID())", &[DataType::Uuid])
28 |         .await;
29 | });
30 | 


--------------------------------------------------------------------------------
/test-suite/src/function/geometry.rs:
--------------------------------------------------------------------------------
1 | mod calc_distance;
2 | mod get_x;
3 | mod get_y;
4 | 
5 | pub use {calc_distance::calc_distance, get_x::get_x, get_y::get_y};
6 | 


--------------------------------------------------------------------------------
/test-suite/src/function/md5.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     crate::*,
 3 |     gluesql_core::{error::TranslateError, prelude::Value::*},
 4 | };
 5 | 
 6 | test_case!(md5, {
 7 |     let g = get_tester!();
 8 | 
 9 |     g.test(
10 |         "VALUES(MD5('GlueSQL'))",
11 |         Ok(select!(
12 |             column1
13 |             Str;
14 |             "4274ecec96f3ee59b51b168dc6137231".to_owned()
15 |         )),
16 |     )
17 |     .await;
18 | 
19 |     g.test(
20 |         "VALUES(MD5('GlueSQL Hi'))",
21 |         Ok(select!(
22 |             column1
23 |             Str;
24 |             "eab30259ac1a92b66794f301a6ac3ff3".to_owned()
25 |         )),
26 |     )
27 |     .await;
28 | 
29 |     g.test(r#"VALUES(MD5(NULL))"#, Ok(select_with_null!(column1; Null)))
30 |         .await;
31 | 
32 |     g.test(
33 |         r#"VALUES(MD5())"#,
34 |         Err(TranslateError::FunctionArgsLengthNotMatching {
35 |             name: "MD5".to_owned(),
36 |             expected: 1,
37 |             found: 0,
38 |         }
39 |         .into()),
40 |     )
41 |     .await;
42 | });
43 | 


--------------------------------------------------------------------------------
/test-suite/src/function/now.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     crate::*,
 3 |     gluesql_core::prelude::{Payload, Value::*},
 4 | };
 5 | 
 6 | test_case!(now, {
 7 |     let g = get_tester!();
 8 | 
 9 |     macro_rules! t {
10 |         ($timestamp: expr) => {
11 |             $timestamp.parse().unwrap()
12 |         };
13 |     }
14 | 
15 |     let test_cases = [
16 |         (
17 |             "CREATE TABLE Item (time TIMESTAMP DEFAULT NOW())",
18 |             Ok(Payload::Create),
19 |         ),
20 |         (
21 |             "INSERT INTO Item VALUES
22 |                 ('2021-10-13T06:42:40.364832862'),
23 |                 ('9999-12-31T23:59:40.364832862');",
24 |             Ok(Payload::Insert(2)),
25 |         ),
26 |         (
27 |             "SELECT time FROM Item WHERE time > NOW();",
28 |             Ok(select!("time" Timestamp; t!("9999-12-31T23:59:40.364832862"))),
29 |         ),
30 |     ];
31 | 
32 |     for (sql, expected) in test_cases {
33 |         g.test(sql, expected).await;
34 |     }
35 | });
36 | 


--------------------------------------------------------------------------------
/test-suite/src/function/pi.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     crate::*,
 3 |     gluesql_core::{error::TranslateError, prelude::Value::*},
 4 | };
 5 | 
 6 | test_case!(pi, {
 7 |     let g = get_tester!();
 8 | 
 9 |     let test_cases = [
10 |         (
11 |             "SELECT PI() AS pi",
12 |             Ok(select!(
13 |                 pi
14 |                 F64;
15 |                 std::f64::consts::PI
16 |             )),
17 |         ),
18 |         (
19 |             "SELECT PI(0) AS pi",
20 |             Err(TranslateError::FunctionArgsLengthNotMatching {
21 |                 name: "PI".to_owned(),
22 |                 expected: 0,
23 |                 found: 1,
24 |             }
25 |             .into()),
26 |         ),
27 |     ];
28 | 
29 |     for (sql, expected) in test_cases {
30 |         g.test(sql, expected).await;
31 |     }
32 | });
33 | 


--------------------------------------------------------------------------------
/test-suite/src/function/position.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     crate::*,
 3 |     gluesql_core::{
 4 |         error::ValueError,
 5 |         prelude::{
 6 |             Payload,
 7 |             Value::{self, *},
 8 |         },
 9 |     },
10 | };
11 | 
12 | test_case!(position, {
13 |     let g = get_tester!();
14 | 
15 |     let test_cases = [
16 |         ("CREATE TABLE Food (name Text null)", Ok(Payload::Create)),
17 |         ("INSERT INTO Food VALUES ('pork')", Ok(Payload::Insert(1))),
18 |         ("INSERT INTO Food VALUES ('burger')", Ok(Payload::Insert(1))),
19 |         (
20 |             "SELECT POSITION('e' IN name) AS test FROM Food",
21 |             Ok(select!(test; I64; 0; 5)),
22 |         ),
23 |         (
24 |             "SELECT POSITION('s' IN 'cheese') AS test",
25 |             Ok(select!(test; I64; 5)),
26 |         ),
27 |         (
28 |             "SELECT POSITION(NULL IN 'cheese') AS test",
29 |             Ok(select_with_null!(test; Null)),
30 |         ),
31 |         (
32 |             "SELECT POSITION(1 IN 'cheese') AS test",
33 |             Err(ValueError::NonStringParameterInPosition {
34 |                 from: Value::Str("cheese".to_owned()),
35 |                 sub: Value::I64(1),
36 |             }
37 |             .into()),
38 |         ),
39 |     ];
40 |     for (sql, expected) in test_cases {
41 |         g.test(sql, expected).await;
42 |     }
43 | });
44 | 


--------------------------------------------------------------------------------
/test-suite/src/index.rs:
--------------------------------------------------------------------------------
 1 | mod and;
 2 | mod basic;
 3 | mod expr;
 4 | mod nested;
 5 | mod null;
 6 | mod order_by;
 7 | mod showindexes;
 8 | mod value;
 9 | 
10 | pub use {
11 |     and::and,
12 |     basic::basic,
13 |     expr::expr,
14 |     nested::nested,
15 |     null::null,
16 |     order_by::{order_by, order_by_multi},
17 |     showindexes::showindexes,
18 |     value::value,
19 | };
20 | 


--------------------------------------------------------------------------------
/test-suite/src/metadata.rs:
--------------------------------------------------------------------------------
1 | pub mod index;
2 | pub mod table;
3 | 


--------------------------------------------------------------------------------
/test-suite/src/metadata/index.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     crate::{concat_with, row, select, stringify_label, test_case},
 3 |     gluesql_core::prelude::{Payload, Value::Str},
 4 | };
 5 | 
 6 | test_case!(index, {
 7 |     let g = get_tester!();
 8 | 
 9 |     let cases = vec![
10 |         ("CREATE TABLE Meta (id INT, name TEXT)", Ok(Payload::Create)),
11 |         (
12 |             "CREATE INDEX Meta_id ON Meta (id)",
13 |             Ok(Payload::CreateIndex),
14 |         ),
15 |         (
16 |             "CREATE INDEX Meta_name ON Meta (name)",
17 |             Ok(Payload::CreateIndex),
18 |         ),
19 |         (
20 |             "SELECT OBJECT_NAME, OBJECT_TYPE FROM GLUE_OBJECTS",
21 |             Ok(select!(
22 |                 OBJECT_NAME            | OBJECT_TYPE       ;
23 |                 Str                    | Str               ;
24 |                 "Meta".to_owned()        "TABLE".to_owned();
25 |                 "Meta_id".to_owned()     "INDEX".to_owned();
26 |                 "Meta_name".to_owned()   "INDEX".to_owned()
27 |             )),
28 |         ),
29 |     ];
30 | 
31 |     for (actual, expected) in cases {
32 |         g.test(actual, expected).await;
33 |     }
34 | });
35 | 


--------------------------------------------------------------------------------
/test-suite/src/metadata/table.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     crate::{row, select, stringify_label, test_case},
 3 |     gluesql_core::prelude::{Payload, Value::Str},
 4 | };
 5 | 
 6 | test_case!(table, {
 7 |     let g = get_tester!();
 8 | 
 9 |     let cases = vec![
10 |         ("CREATE TABLE Meta (id INT, name TEXT)", Ok(Payload::Create)),
11 |         (
12 |             "SELECT OBJECT_NAME, OBJECT_TYPE
13 |                      FROM GLUE_OBJECTS
14 |                      WHERE CREATED > NOW() - INTERVAL 1 MINUTE",
15 |             Ok(select!(
16 |                 OBJECT_NAME       | OBJECT_TYPE       ;
17 |                 Str               | Str               ;
18 |                 "Meta".to_owned()   "TABLE".to_owned()
19 |             )),
20 |         ),
21 |         ("DROP TABLE Meta", Ok(Payload::DropTable(1))),
22 |         (
23 |             "SELECT COUNT(*)
24 |                      FROM GLUE_OBJECTS
25 |                      WHERE CREATED > NOW() - INTERVAL 1 MINUTE",
26 |             Ok(Payload::Select {
27 |                 labels: vec!["COUNT(*)".to_owned()],
28 |                 rows: Vec::new(),
29 |             }),
30 |         ),
31 |     ];
32 | 
33 |     for (actual, expected) in cases {
34 |         g.test(actual, expected).await;
35 |     }
36 | });
37 | 


--------------------------------------------------------------------------------
/test-suite/src/schemaless.rs:
--------------------------------------------------------------------------------
1 | mod basic;
2 | mod error;
3 | 
4 | pub use {basic::basic, error::error};
5 | 


--------------------------------------------------------------------------------
/test-suite/src/store.rs:
--------------------------------------------------------------------------------
1 | pub mod insert_schema;
2 | 


--------------------------------------------------------------------------------
/test-suite/src/transaction.rs:
--------------------------------------------------------------------------------
 1 | mod alter_table;
 2 | mod ast_builder;
 3 | mod basic;
 4 | mod dictionary;
 5 | mod index;
 6 | mod table;
 7 | 
 8 | pub use {
 9 |     alter_table::*, ast_builder::*, basic::basic, dictionary::dictionary, index::*, table::*,
10 | };
11 | 


--------------------------------------------------------------------------------
/test-suite/src/transaction/dictionary.rs:
--------------------------------------------------------------------------------
 1 | use {crate::*, gluesql_core::prelude::*};
 2 | 
 3 | test_case!(dictionary, {
 4 |     let g = get_tester!();
 5 | 
 6 |     let tables = |v: Vec<&str>| {
 7 |         Ok(Payload::ShowVariable(PayloadVariable::Tables(
 8 |             v.into_iter().map(ToOwned::to_owned).collect(),
 9 |         )))
10 |     };
11 | 
12 |     g.run("CREATE TABLE Garlic (id INTEGER);").await;
13 |     g.test("SHOW TABLES;", tables(vec!["Garlic"])).await;
14 | 
15 |     g.run("BEGIN;").await;
16 |     g.test("SHOW TABLES;", tables(vec!["Garlic"])).await;
17 | 
18 |     g.run("CREATE TABLE Noodle (id INTEGER);").await;
19 |     g.test("SHOW TABLES;", tables(vec!["Garlic", "Noodle"]))
20 |         .await;
21 | 
22 |     g.run("ROLLBACK;").await;
23 |     g.test("SHOW TABLES;", tables(vec!["Garlic"])).await;
24 | 
25 |     g.run("BEGIN;").await;
26 |     g.run("CREATE TABLE Apple (id INTEGER);").await;
27 |     g.run("CREATE TABLE Rice (id INTEGER);").await;
28 |     g.test("SHOW TABLES;", tables(vec!["Apple", "Garlic", "Rice"]))
29 |         .await;
30 | 
31 |     g.run("COMMIT;").await;
32 |     g.test("SHOW TABLES;", tables(vec!["Apple", "Garlic", "Rice"]))
33 |         .await;
34 | });
35 | 


--------------------------------------------------------------------------------
/test-suite/src/type_match.rs:
--------------------------------------------------------------------------------
 1 | use {crate::*, gluesql_core::ast::DataType};
 2 | 
 3 | test_case!(type_match, {
 4 |     let g = get_tester!();
 5 | 
 6 |     g.run("CREATE TABLE TypeMatch (uuid_value UUID, float_value FLOAT, int_value INT, bool_value BOOLEAN)").await;
 7 |     g.run("INSERT INTO TypeMatch values(GENERATE_UUID(), 1.0, 1, true)")
 8 |         .await;
 9 |     g.type_match(
10 |         "SELECT * FROM TypeMatch",
11 |         &[
12 |             DataType::Uuid,
13 |             DataType::Float,
14 |             DataType::Int,
15 |             DataType::Boolean,
16 |         ],
17 |     )
18 |     .await;
19 | });
20 | 


--------------------------------------------------------------------------------
/test-suite/src/validate.rs:
--------------------------------------------------------------------------------
1 | pub mod types;
2 | pub mod unique;
3 | 


--------------------------------------------------------------------------------
/utils/Cargo.toml:
--------------------------------------------------------------------------------
 1 | [package]
 2 | name = "gluesql-utils"
 3 | authors = ["Taehoon Moon <taehoon.moon@outlook.com>"]
 4 | version.workspace = true
 5 | edition.workspace = true
 6 | description.workspace = true
 7 | license.workspace = true
 8 | repository.workspace = true
 9 | documentation.workspace = true
10 | 
11 | [dependencies]
12 | indexmap = "1"
13 | pin-project = "1"
14 | futures = "0.3"
15 | 


--------------------------------------------------------------------------------
/utils/src/hashmap.rs:
--------------------------------------------------------------------------------
 1 | use std::{collections::HashMap, hash::Hash};
 2 | 
 3 | pub trait HashMapExt<K, V, I> {
 4 |     fn concat(self, entries: I) -> Self;
 5 | }
 6 | 
 7 | impl<K, V, I> HashMapExt<K, V, I> for HashMap<K, V>
 8 | where
 9 |     K: Hash + Eq,
10 |     I: Iterator<Item = (K, V)>,
11 | {
12 |     fn concat(mut self, entries: I) -> Self {
13 |         for (key, value) in entries {
14 |             self.insert(key, value);
15 |         }
16 | 
17 |         self
18 |     }
19 | }
20 | 
21 | #[cfg(test)]
22 | mod tests {
23 |     use {super::HashMapExt, std::collections::HashMap};
24 | 
25 |     #[test]
26 |     fn concat() {
27 |         let values: HashMap<&str, i64> = [("a", 10), ("b", 20)].into();
28 |         let new_items = [("c", 30), ("d", 40), ("e", 50)];
29 | 
30 |         let actual = values.concat(new_items.into_iter());
31 |         let expected = [("a", 10), ("b", 20), ("c", 30), ("d", 40), ("e", 50)].into();
32 | 
33 |         assert_eq!(actual, expected);
34 |     }
35 | }
36 | 


--------------------------------------------------------------------------------
/utils/src/indexmap.rs:
--------------------------------------------------------------------------------
 1 | use {
 2 |     indexmap::map::{IntoIter, Keys},
 3 |     std::{cmp::Eq, hash::Hash},
 4 | };
 5 | 
 6 | /// HashMap which provides
 7 | /// 1. Immutable APIs
 8 | /// 2. Preserving insertion order
 9 | pub struct IndexMap<K, V>(indexmap::IndexMap<K, V>);
10 | 
11 | impl<K: Hash + Eq, V> IndexMap<K, V> {
12 |     pub fn new() -> Self {
13 |         Self(indexmap::IndexMap::new())
14 |     }
15 | 
16 |     pub fn insert(mut self, key: K, value: V) -> (Self, Option<V>) {
17 |         let existing = self.0.insert(key, value);
18 | 
19 |         (self, existing)
20 |     }
21 | 
22 |     pub fn get(&self, key: &K) -> Option<&V> {
23 |         self.0.get(key)
24 |     }
25 | 
26 |     pub fn keys(&self) -> Keys<K, V> {
27 |         self.0.keys()
28 |     }
29 | 
30 |     pub fn len(&self) -> usize {
31 |         self.0.len()
32 |     }
33 | 
34 |     pub fn is_empty(&self) -> bool {
35 |         self.0.is_empty()
36 |     }
37 | }
38 | 
39 | impl<K: Hash + Eq, V> Default for IndexMap<K, V> {
40 |     fn default() -> Self {
41 |         Self::new()
42 |     }
43 | }
44 | 
45 | impl<K: Hash + Eq, V> IntoIterator for IndexMap<K, V> {
46 |     type Item = (K, V);
47 |     type IntoIter = IntoIter<K, V>;
48 | 
49 |     fn into_iter(self) -> Self::IntoIter {
50 |         self.0.into_iter()
51 |     }
52 | }
53 | 


--------------------------------------------------------------------------------
/utils/src/lib.rs:
--------------------------------------------------------------------------------
1 | #![deny(clippy::str_to_string)]
2 | 
3 | mod hashmap;
4 | mod indexmap;
5 | mod or_stream;
6 | mod vector;
7 | 
8 | pub use {self::indexmap::IndexMap, hashmap::HashMapExt, or_stream::OrStream, vector::Vector};
9 | 


--------------------------------------------------------------------------------