├── .asf.yaml ├── .github ├── actions │ └── setup-builder │ │ └── action.yaml ├── dependabot.yml └── workflows │ ├── rust.yml │ └── stale.yml ├── .gitignore ├── .tool-versions ├── CHANGELOG.md ├── Cargo.toml ├── HEADER ├── LICENSE.TXT ├── README.md ├── SECURITY.md ├── changelog ├── 0.51.0-pre.md ├── 0.52.0.md ├── 0.53.0.md ├── 0.54.0.md ├── 0.55.0.md └── 0.56.0.md ├── derive ├── Cargo.toml ├── LICENSE.TXT ├── README.md └── src │ └── lib.rs ├── dev └── release │ ├── README.md │ ├── check-rat-report.py │ ├── create-tarball.sh │ ├── generate-changelog.py │ ├── rat_exclude_files.txt │ ├── release-tarball.sh │ ├── run-rat.sh │ └── verify-release-candidate.sh ├── docs ├── benchmarking.md ├── custom_sql_parser.md └── fuzzing.md ├── examples ├── cli.rs └── parse_select.rs ├── fuzz ├── .gitignore ├── Cargo.toml └── fuzz_targets │ └── fuzz_parse_sql.rs ├── rustfmt.toml ├── sqlparser_bench ├── Cargo.toml ├── README.md ├── benches │ └── sqlparser_bench.rs └── img │ └── flamegraph.svg ├── src ├── ast │ ├── data_type.rs │ ├── dcl.rs │ ├── ddl.rs │ ├── dml.rs │ ├── helpers │ │ ├── attached_token.rs │ │ ├── key_value_options.rs │ │ ├── mod.rs │ │ ├── stmt_create_table.rs │ │ └── stmt_data_loading.rs │ ├── mod.rs │ ├── operator.rs │ ├── query.rs │ ├── spans.rs │ ├── trigger.rs │ ├── value.rs │ └── visitor.rs ├── dialect │ ├── ansi.rs │ ├── bigquery.rs │ ├── clickhouse.rs │ ├── databricks.rs │ ├── duckdb.rs │ ├── generic.rs │ ├── hive.rs │ ├── mod.rs │ ├── mssql.rs │ ├── mysql.rs │ ├── postgresql.rs │ ├── redshift.rs │ ├── snowflake.rs │ └── sqlite.rs ├── display_utils.rs ├── keywords.rs ├── lib.rs ├── parser │ ├── alter.rs │ └── mod.rs ├── test_utils.rs └── tokenizer.rs └── tests ├── pretty_print.rs ├── queries └── tpch │ ├── 1.sql │ ├── 10.sql │ ├── 11.sql │ ├── 12.sql │ ├── 13.sql │ ├── 14.sql │ ├── 15.sql │ ├── 16.sql │ ├── 17.sql │ ├── 18.sql │ ├── 19.sql │ ├── 2.sql │ ├── 20.sql │ ├── 21.sql │ ├── 22.sql │ ├── 3.sql │ ├── 4.sql │ ├── 5.sql │ ├── 6.sql │ ├── 7.sql │ ├── 8.sql │ └── 9.sql ├── sqlparser_bigquery.rs ├── sqlparser_clickhouse.rs ├── sqlparser_common.rs ├── sqlparser_custom_dialect.rs ├── sqlparser_databricks.rs ├── sqlparser_duckdb.rs ├── sqlparser_hive.rs ├── sqlparser_mssql.rs ├── sqlparser_mysql.rs ├── sqlparser_postgres.rs ├── sqlparser_redshift.rs ├── sqlparser_regression.rs ├── sqlparser_snowflake.rs ├── sqlparser_sqlite.rs └── test_utils └── mod.rs /.asf.yaml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # This file controls the settings of this repository 19 | # 20 | # See more details at 21 | # https://cwiki.apache.org/confluence/display/INFRA/Git+-+.asf.yaml+features 22 | 23 | notifications: 24 | commits: commits@datafusion.apache.org 25 | issues: github@datafusion.apache.org 26 | pullrequests: github@datafusion.apache.org 27 | github: 28 | description: "Extensible SQL Lexer and Parser for Rust" 29 | labels: 30 | - big-data 31 | - rust 32 | - sql 33 | enabled_merge_buttons: 34 | squash: true 35 | merge: false 36 | rebase: false 37 | features: 38 | issues: true 39 | -------------------------------------------------------------------------------- /.github/actions/setup-builder/action.yaml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | name: Prepare Rust Builder 19 | description: 'Prepare Rust Build Environment' 20 | inputs: 21 | rust-version: 22 | description: 'version of rust to install (e.g. stable)' 23 | required: true 24 | default: 'stable' 25 | targets: 26 | description: 'The toolchain targets to add, comma-separated' 27 | default: '' 28 | 29 | runs: 30 | using: "composite" 31 | steps: 32 | - name: Setup Rust Toolchain 33 | shell: bash 34 | run: | 35 | echo "Installing ${{ inputs.rust-version }}" 36 | if [ -n "${{ inputs.targets}}" ]; then 37 | rustup toolchain install ${{ inputs.rust-version }} -t ${{ inputs.targets }} 38 | else 39 | rustup toolchain install ${{ inputs.rust-version }} 40 | fi 41 | rustup default ${{ inputs.rust-version }} 42 | rustup component add rustfmt clippy 43 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | version: 2 19 | updates: 20 | - package-ecosystem: cargo 21 | directory: "/" 22 | schedule: 23 | interval: daily 24 | open-pull-requests-limit: 10 25 | - package-ecosystem: cargo 26 | directory: "/sqlparser_bench" 27 | schedule: 28 | interval: daily 29 | open-pull-requests-limit: 10 30 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | name: Rust 19 | 20 | on: [push, pull_request] 21 | 22 | jobs: 23 | 24 | codestyle: 25 | runs-on: ubuntu-latest 26 | steps: 27 | - uses: actions/checkout@v4 28 | - name: Setup Rust Toolchain 29 | uses: ./.github/actions/setup-builder 30 | - run: cargo fmt --all -- --check 31 | 32 | lint: 33 | runs-on: ubuntu-latest 34 | steps: 35 | - uses: actions/checkout@v4 36 | - name: Setup Rust Toolchain 37 | uses: ./.github/actions/setup-builder 38 | - run: cargo clippy --all-targets --all-features -- -D warnings 39 | 40 | benchmark-lint: 41 | runs-on: ubuntu-latest 42 | steps: 43 | - uses: actions/checkout@v4 44 | - name: Setup Rust Toolchain 45 | uses: ./.github/actions/setup-builder 46 | - run: cd sqlparser_bench && cargo clippy --all-targets --all-features -- -D warnings 47 | 48 | compile: 49 | runs-on: ubuntu-latest 50 | steps: 51 | - uses: actions/checkout@v4 52 | - name: Setup Rust Toolchain 53 | uses: ./.github/actions/setup-builder 54 | - run: cargo check --all-targets --all-features 55 | 56 | docs: 57 | runs-on: ubuntu-latest 58 | env: 59 | RUSTDOCFLAGS: "-Dwarnings" 60 | steps: 61 | - uses: actions/checkout@v4 62 | - name: Setup Rust Toolchain 63 | uses: ./.github/actions/setup-builder 64 | - run: cargo doc --document-private-items --no-deps --workspace --all-features 65 | 66 | compile-no-std: 67 | runs-on: ubuntu-latest 68 | steps: 69 | - uses: actions/checkout@v4 70 | - name: Setup Rust Toolchain 71 | uses: ./.github/actions/setup-builder 72 | with: 73 | targets: 'thumbv6m-none-eabi' 74 | - run: cargo check --no-default-features --target thumbv6m-none-eabi 75 | 76 | test: 77 | strategy: 78 | matrix: 79 | rust: [stable, beta, nightly] 80 | runs-on: ubuntu-latest 81 | steps: 82 | - name: Checkout 83 | uses: actions/checkout@v4 84 | - name: Setup Rust Toolchain 85 | uses: ./.github/actions/setup-builder 86 | with: 87 | rust-version: ${{ matrix.rust }} 88 | - name: Install Tarpaulin 89 | uses: actions-rs/install@v0.1 90 | with: 91 | crate: cargo-tarpaulin 92 | version: 0.14.2 93 | use-tool-cache: true 94 | - name: Test 95 | run: cargo test --all-features 96 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | name: "Close stale PRs" 19 | on: 20 | schedule: 21 | - cron: "30 1 * * *" 22 | 23 | jobs: 24 | close-stale-prs: 25 | runs-on: ubuntu-latest 26 | permissions: 27 | issues: write 28 | pull-requests: write 29 | steps: 30 | - uses: actions/stale@v9 31 | with: 32 | stale-pr-message: "Thank you for your contribution. Unfortunately, this pull request is stale because it has been open 60 days with no activity. Please remove the stale label or comment or this will be closed in 7 days." 33 | days-before-pr-stale: 60 34 | days-before-pr-close: 7 35 | # do not close stale issues 36 | days-before-issue-stale: -1 37 | days-before-issue-close: -1 38 | repo-token: ${{ secrets.GITHUB_TOKEN }} 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | /sqlparser_bench/target/ 5 | /derive/target/ 6 | dev/dist 7 | 8 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 9 | # More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock 10 | Cargo.lock 11 | 12 | # These are backup files generated by rustfmt 13 | **/*.rs.bk 14 | 15 | # IDEs 16 | .idea 17 | .vscode 18 | 19 | *.swp 20 | 21 | .DS_store -------------------------------------------------------------------------------- /.tool-versions: -------------------------------------------------------------------------------- 1 | rust 1.75.0 -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 19 | 20 | # Changelog 21 | All notable changes to this project will be documented in one of the linked 22 | files. 23 | 24 | This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). 25 | 26 | Given that the parser produces a typed AST, any changes to the AST will 27 | technically be breaking and thus will result in a `0.(N+1)` version. 28 | 29 | 30 | - Unreleased: Check https://github.com/sqlparser-rs/sqlparser-rs/commits/main for undocumented changes. 31 | - `0.56.0`: [changelog/0.56.0.md](changelog/0.56.0.md) 32 | - `0.55.0`: [changelog/0.55.0.md](changelog/0.55.0.md) 33 | - `0.54.0`: [changelog/0.54.0.md](changelog/0.54.0.md) 34 | - `0.53.0`: [changelog/0.53.0.md](changelog/0.53.0.md) 35 | - `0.52.0`: [changelog/0.52.0.md](changelog/0.52.0.md) 36 | - `0.51.0` and earlier: [changelog/0.51.0-pre.md](changelog/0.51.0-pre.md) 37 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | [package] 19 | name = "sqlparser" 20 | description = "Extensible SQL Lexer and Parser with support for ANSI SQL:2011" 21 | version = "0.56.0" 22 | authors = ["Apache DataFusion "] 23 | homepage = "https://github.com/apache/datafusion-sqlparser-rs" 24 | documentation = "https://docs.rs/sqlparser/" 25 | keywords = ["ansi", "sql", "lexer", "parser"] 26 | repository = "https://github.com/apache/datafusion-sqlparser-rs" 27 | license = "Apache-2.0" 28 | include = [ 29 | "src/**/*.rs", 30 | "Cargo.toml", 31 | "LICENSE.TXT", 32 | ] 33 | edition = "2021" 34 | 35 | [lib] 36 | name = "sqlparser" 37 | path = "src/lib.rs" 38 | 39 | [features] 40 | default = ["std", "recursive-protection"] 41 | std = [] 42 | recursive-protection = ["std", "recursive"] 43 | # Enable JSON output in the `cli` example: 44 | json_example = ["serde_json", "serde"] 45 | visitor = ["sqlparser_derive"] 46 | 47 | [dependencies] 48 | bigdecimal = { version = "0.4.1", features = ["serde"], optional = true } 49 | log = "0.4" 50 | recursive = { version = "0.1.1", optional = true} 51 | 52 | serde = { version = "1.0", default-features = false, features = ["derive", "alloc"], optional = true } 53 | # serde_json is only used in examples/cli, but we have to put it outside 54 | # of dev-dependencies because of 55 | # https://github.com/rust-lang/cargo/issues/1596 56 | serde_json = { version = "1.0", optional = true } 57 | sqlparser_derive = { version = "0.3.0", path = "derive", optional = true } 58 | 59 | [dev-dependencies] 60 | simple_logger = "5.0" 61 | matches = "0.1" 62 | pretty_assertions = "1" 63 | 64 | [package.metadata.docs.rs] 65 | # Document these features on docs.rs 66 | features = ["serde", "visitor"] 67 | -------------------------------------------------------------------------------- /HEADER: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 19 | 20 | # Security Policy 21 | 22 | ## Reporting a Vulnerability 23 | 24 | Please report security issues to `andrew@nerdnetworks.org` -------------------------------------------------------------------------------- /changelog/0.52.0.md: -------------------------------------------------------------------------------- 1 | 19 | 20 | # sqlparser-rs 0.52.0 Changelog 21 | 22 | This release consists of 45 commits from 20 contributors. See credits at the end of this changelog for more information. 23 | 24 | **Implemented enhancements:** 25 | 26 | - feat: support explain options [#1426](https://github.com/apache/datafusion-sqlparser-rs/pull/1426) (kysshsy) 27 | - feat: adding Display implementation to DELETE and INSERT [#1427](https://github.com/apache/datafusion-sqlparser-rs/pull/1427) (seve-martinez) 28 | 29 | **Fixed bugs:** 30 | 31 | - fix: `maybe_parse` preventing parser from erroring on recursion limit [#1464](https://github.com/apache/datafusion-sqlparser-rs/pull/1464) (tomershaniii) 32 | 33 | **Other:** 34 | 35 | - Fix parsing of negative values [#1419](https://github.com/apache/datafusion-sqlparser-rs/pull/1419) (agscpp) 36 | - Allow to use ON CLUSTER cluster_name in TRUNCATE syntax [#1428](https://github.com/apache/datafusion-sqlparser-rs/pull/1428) (git-hulk) 37 | - chore: remove redundant punctuation [#1434](https://github.com/apache/datafusion-sqlparser-rs/pull/1434) (Fischer0522) 38 | - MS SQL Server: add support for IDENTITY column option [#1432](https://github.com/apache/datafusion-sqlparser-rs/pull/1432) (7phs) 39 | - Update to ASF header / add when missing [#1437](https://github.com/apache/datafusion-sqlparser-rs/pull/1437) (alamb) 40 | - Some small optimizations [#1424](https://github.com/apache/datafusion-sqlparser-rs/pull/1424) (exrok) 41 | - Fix `codestyle` CI check [#1438](https://github.com/apache/datafusion-sqlparser-rs/pull/1438) (alamb) 42 | - Implements CREATE POLICY syntax for PostgreSQL [#1440](https://github.com/apache/datafusion-sqlparser-rs/pull/1440) (git-hulk) 43 | - make `parse_expr_with_alias` public [#1444](https://github.com/apache/datafusion-sqlparser-rs/pull/1444) (Eason0729) 44 | - Implements DROP POLICY syntax for PostgreSQL [#1445](https://github.com/apache/datafusion-sqlparser-rs/pull/1445) (git-hulk) 45 | - Support `DROP DATABASE` [#1443](https://github.com/apache/datafusion-sqlparser-rs/pull/1443) (linhr) 46 | - Implements ALTER POLICY syntax for PostgreSQL [#1446](https://github.com/apache/datafusion-sqlparser-rs/pull/1446) (git-hulk) 47 | - Add a note discouraging new use of `dialect_of` macro [#1448](https://github.com/apache/datafusion-sqlparser-rs/pull/1448) (alamb) 48 | - Expand handling of `LIMIT 1, 2` handling to include sqlite [#1447](https://github.com/apache/datafusion-sqlparser-rs/pull/1447) (joshuawarner32) 49 | - Fix always uses CommentDef::WithoutEq while parsing the inline comment [#1453](https://github.com/apache/datafusion-sqlparser-rs/pull/1453) (git-hulk) 50 | - Add support for the LIKE ANY and ILIKE ANY pattern-matching condition [#1456](https://github.com/apache/datafusion-sqlparser-rs/pull/1456) (yoavcloud) 51 | - added ability to parse extension to parse_comment inside postgres dialect [#1451](https://github.com/apache/datafusion-sqlparser-rs/pull/1451) (MaxwellKnight) 52 | - Snowflake: support of views column comment [#1441](https://github.com/apache/datafusion-sqlparser-rs/pull/1441) (7phs) 53 | - Add SQLite "ON CONFLICT" column option in CREATE TABLE statements [#1442](https://github.com/apache/datafusion-sqlparser-rs/pull/1442) (nucccc) 54 | - Add support for ASC and DESC in CREATE TABLE column constraints for SQLite. [#1462](https://github.com/apache/datafusion-sqlparser-rs/pull/1462) (caldwell) 55 | - Add support of `EXPLAIN QUERY PLAN` syntax for SQLite dialect [#1458](https://github.com/apache/datafusion-sqlparser-rs/pull/1458) (git-hulk) 56 | - Add "DROP TYPE" support. [#1461](https://github.com/apache/datafusion-sqlparser-rs/pull/1461) (caldwell) 57 | - chore: Add asf.yaml [#1463](https://github.com/apache/datafusion-sqlparser-rs/pull/1463) (Xuanwo) 58 | - Add support for quantified comparison predicates (ALL/ANY/SOME) [#1459](https://github.com/apache/datafusion-sqlparser-rs/pull/1459) (yoavcloud) 59 | - MySQL dialect: Add support for hash comments [#1466](https://github.com/apache/datafusion-sqlparser-rs/pull/1466) (hansott) 60 | - Fix #1469 (SET ROLE regression) [#1474](https://github.com/apache/datafusion-sqlparser-rs/pull/1474) (lovasoa) 61 | - Add support for parsing MsSql alias with equals [#1467](https://github.com/apache/datafusion-sqlparser-rs/pull/1467) (yoavcloud) 62 | - Snowflake: support for extended column options in `CREATE TABLE` [#1454](https://github.com/apache/datafusion-sqlparser-rs/pull/1454) (7phs) 63 | - MsSQL TRY_CONVERT [#1477](https://github.com/apache/datafusion-sqlparser-rs/pull/1477) (yoavcloud) 64 | - Add PostgreSQL specfic "CREATE TYPE t AS ENUM (...)" support. [#1460](https://github.com/apache/datafusion-sqlparser-rs/pull/1460) (caldwell) 65 | - Fix build [#1483](https://github.com/apache/datafusion-sqlparser-rs/pull/1483) (yoavcloud) 66 | - Fix complex blocks warning when running clippy [#1488](https://github.com/apache/datafusion-sqlparser-rs/pull/1488) (git-hulk) 67 | - Add support for SHOW DATABASES/SCHEMAS/TABLES/VIEWS in Hive [#1487](https://github.com/apache/datafusion-sqlparser-rs/pull/1487) (yoavcloud) 68 | - Fix typo in `Dialect::supports_eq_alias_assigment` [#1478](https://github.com/apache/datafusion-sqlparser-rs/pull/1478) (alamb) 69 | - Add support for PostgreSQL `LISTEN/NOTIFY` syntax [#1485](https://github.com/apache/datafusion-sqlparser-rs/pull/1485) (wugeer) 70 | - Add support for TOP before ALL/DISTINCT [#1495](https://github.com/apache/datafusion-sqlparser-rs/pull/1495) (yoavcloud) 71 | - add support for `FOR ORDINALITY` and `NESTED` in JSON_TABLE [#1493](https://github.com/apache/datafusion-sqlparser-rs/pull/1493) (lovasoa) 72 | - Add Apache License to additional files [#1502](https://github.com/apache/datafusion-sqlparser-rs/pull/1502) (alamb) 73 | - Move CHANGELOG content [#1503](https://github.com/apache/datafusion-sqlparser-rs/pull/1503) (alamb) 74 | - improve support for T-SQL EXECUTE statements [#1490](https://github.com/apache/datafusion-sqlparser-rs/pull/1490) (lovasoa) 75 | 76 | ## Credits 77 | 78 | Thank you to everyone who contributed to this release. Here is a breakdown of commits (PRs merged) per contributor. 79 | 80 | ``` 81 | 8 Andrew Lamb 82 | 7 Yoav Cohen 83 | 7 hulk 84 | 3 Aleksei Piianin 85 | 3 David Caldwell 86 | 3 Ophir LOJKINE 87 | 1 Agaev Guseyn 88 | 1 Eason 89 | 1 Fischer 90 | 1 Hans Ott 91 | 1 Heran Lin 92 | 1 Joshua Warner 93 | 1 Maxwell Knight 94 | 1 Seve Martinez 95 | 1 Siyuan Huang 96 | 1 Thomas Dagenais 97 | 1 Xuanwo 98 | 1 nucccc 99 | 1 tomershaniii 100 | 1 wugeer 101 | ``` 102 | 103 | Thank you also to everyone who contributed in other ways such as filing issues, reviewing PRs, and providing feedback on this release. 104 | 105 | -------------------------------------------------------------------------------- /changelog/0.53.0.md: -------------------------------------------------------------------------------- 1 | 19 | 20 | # sqlparser-rs 0.53.0 Changelog 21 | 22 | This release consists of 47 commits from 16 contributors. See credits at the end of this changelog for more information. 23 | 24 | **Other:** 25 | 26 | - hive: support for special not expression `!a` and raise error for `a!` factorial operator [#1472](https://github.com/apache/datafusion-sqlparser-rs/pull/1472) (wugeer) 27 | - Add support for MSSQL's `OPENJSON WITH` clause [#1498](https://github.com/apache/datafusion-sqlparser-rs/pull/1498) (gaoqiangz) 28 | - Parse true and false as identifiers in mssql [#1510](https://github.com/apache/datafusion-sqlparser-rs/pull/1510) (lovasoa) 29 | - Fix the parsing error in MSSQL for multiple statements that include `DECLARE` statements [#1497](https://github.com/apache/datafusion-sqlparser-rs/pull/1497) (wugeer) 30 | - Add support for Snowflake SHOW DATABASES/SCHEMAS/TABLES/VIEWS/COLUMNS statements [#1501](https://github.com/apache/datafusion-sqlparser-rs/pull/1501) (yoavcloud) 31 | - Add support of COMMENT ON syntax for Snowflake [#1516](https://github.com/apache/datafusion-sqlparser-rs/pull/1516) (git-hulk) 32 | - Add support for MYSQL's `CREATE TABLE SELECT` expr [#1515](https://github.com/apache/datafusion-sqlparser-rs/pull/1515) (wugeer) 33 | - Add support for MSSQL's `XQuery` methods [#1500](https://github.com/apache/datafusion-sqlparser-rs/pull/1500) (gaoqiangz) 34 | - Add support for Hive's `LOAD DATA` expr [#1520](https://github.com/apache/datafusion-sqlparser-rs/pull/1520) (wugeer) 35 | - Fix ClickHouse document link from `Russian` to `English` [#1527](https://github.com/apache/datafusion-sqlparser-rs/pull/1527) (git-hulk) 36 | - Support ANTI and SEMI joins without LEFT/RIGHT [#1528](https://github.com/apache/datafusion-sqlparser-rs/pull/1528) (delamarch3) 37 | - support sqlite's OR clauses in update statements [#1530](https://github.com/apache/datafusion-sqlparser-rs/pull/1530) (lovasoa) 38 | - support column type definitions in table aliases [#1526](https://github.com/apache/datafusion-sqlparser-rs/pull/1526) (lovasoa) 39 | - Add support for MSSQL's `JSON_ARRAY`/`JSON_OBJECT` expr [#1507](https://github.com/apache/datafusion-sqlparser-rs/pull/1507) (gaoqiangz) 40 | - Add support for PostgreSQL `UNLISTEN` syntax and Add support for Postgres `LOAD extension` expr [#1531](https://github.com/apache/datafusion-sqlparser-rs/pull/1531) (wugeer) 41 | - Parse byte/bit string literals in MySQL and Postgres [#1532](https://github.com/apache/datafusion-sqlparser-rs/pull/1532) (mvzink) 42 | - Allow example CLI to read from stdin [#1536](https://github.com/apache/datafusion-sqlparser-rs/pull/1536) (mvzink) 43 | - recursive select calls are parsed with bad trailing_commas parameter [#1521](https://github.com/apache/datafusion-sqlparser-rs/pull/1521) (tomershaniii) 44 | - PartiQL queries in Redshift [#1534](https://github.com/apache/datafusion-sqlparser-rs/pull/1534) (yoavcloud) 45 | - Include license file in sqlparser_derive crate [#1543](https://github.com/apache/datafusion-sqlparser-rs/pull/1543) (ankane) 46 | - Fallback to identifier parsing if expression parsing fails [#1513](https://github.com/apache/datafusion-sqlparser-rs/pull/1513) (yoavcloud) 47 | - support `json_object('k':'v')` in postgres [#1546](https://github.com/apache/datafusion-sqlparser-rs/pull/1546) (lovasoa) 48 | - Document micro benchmarks [#1555](https://github.com/apache/datafusion-sqlparser-rs/pull/1555) (alamb) 49 | - Implement `Spanned` to retrieve source locations on AST nodes [#1435](https://github.com/apache/datafusion-sqlparser-rs/pull/1435) (Nyrox) 50 | - Fix error in benchmark queries [#1560](https://github.com/apache/datafusion-sqlparser-rs/pull/1560) (alamb) 51 | - Fix clippy warnings on rust 1.83 [#1570](https://github.com/apache/datafusion-sqlparser-rs/pull/1570) (iffyio) 52 | - Support relation visitor to visit the `Option` field [#1556](https://github.com/apache/datafusion-sqlparser-rs/pull/1556) (goldmedal) 53 | - Rename `TokenWithLocation` to `TokenWithSpan`, in backwards compatible way [#1562](https://github.com/apache/datafusion-sqlparser-rs/pull/1562) (alamb) 54 | - Support MySQL size variants for BLOB and TEXT columns [#1564](https://github.com/apache/datafusion-sqlparser-rs/pull/1564) (mvzink) 55 | - Increase version of sqlparser_derive from 0.2.2 to 0.3.0 [#1571](https://github.com/apache/datafusion-sqlparser-rs/pull/1571) (alamb) 56 | - `json_object('k' VALUE 'v')` in postgres [#1547](https://github.com/apache/datafusion-sqlparser-rs/pull/1547) (lovasoa) 57 | - Support snowflake double dot notation for object name [#1540](https://github.com/apache/datafusion-sqlparser-rs/pull/1540) (ayman-sigma) 58 | - Update comments / docs for `Spanned` [#1549](https://github.com/apache/datafusion-sqlparser-rs/pull/1549) (alamb) 59 | - Support Databricks struct literal [#1542](https://github.com/apache/datafusion-sqlparser-rs/pull/1542) (ayman-sigma) 60 | - Encapsulate CreateFunction [#1573](https://github.com/apache/datafusion-sqlparser-rs/pull/1573) (philipcristiano) 61 | - Support BIT column types [#1577](https://github.com/apache/datafusion-sqlparser-rs/pull/1577) (mvzink) 62 | - Support parsing optional nulls handling for unique constraint [#1567](https://github.com/apache/datafusion-sqlparser-rs/pull/1567) (mvzink) 63 | - Fix displaying WORK or TRANSACTION after BEGIN [#1565](https://github.com/apache/datafusion-sqlparser-rs/pull/1565) (mvzink) 64 | - Add support of the ENUM8|ENUM16 for ClickHouse dialect [#1574](https://github.com/apache/datafusion-sqlparser-rs/pull/1574) (git-hulk) 65 | - Parse Snowflake USE ROLE and USE SECONDARY ROLES [#1578](https://github.com/apache/datafusion-sqlparser-rs/pull/1578) (yoavcloud) 66 | - Snowflake ALTER TABLE clustering options [#1579](https://github.com/apache/datafusion-sqlparser-rs/pull/1579) (yoavcloud) 67 | - Support INSERT OVERWRITE INTO syntax [#1584](https://github.com/apache/datafusion-sqlparser-rs/pull/1584) (yuval-illumex) 68 | - Parse `INSERT` with subquery when lacking column names [#1586](https://github.com/apache/datafusion-sqlparser-rs/pull/1586) (iffyio) 69 | - Add support for ODBC functions [#1585](https://github.com/apache/datafusion-sqlparser-rs/pull/1585) (iffyio) 70 | 71 | ## Credits 72 | 73 | Thank you to everyone who contributed to this release. Here is a breakdown of commits (PRs merged) per contributor. 74 | 75 | ``` 76 | 8 Andrew Lamb 77 | 6 Michael Victor Zink 78 | 5 Ophir LOJKINE 79 | 5 Yoav Cohen 80 | 5 wugeer 81 | 3 Ifeanyi Ubah 82 | 3 gaoqiangz 83 | 3 hulk 84 | 2 Ayman Elkfrawy 85 | 1 Andrew Kane 86 | 1 Jax Liu 87 | 1 Mark-Oliver Junge 88 | 1 Philip Cristiano 89 | 1 Yuval Shkolar 90 | 1 delamarch3 91 | 1 tomershaniii 92 | ``` 93 | 94 | Thank you also to everyone who contributed in other ways such as filing issues, reviewing PRs, and providing feedback on this release. 95 | 96 | -------------------------------------------------------------------------------- /changelog/0.56.0.md: -------------------------------------------------------------------------------- 1 | 19 | 20 | # sqlparser-rs 0.56.0 Changelog 21 | 22 | This release consists of 48 commits from 19 contributors. See credits at the end of this changelog for more information. 23 | 24 | **Other:** 25 | 26 | - Ignore escaped LIKE wildcards in MySQL [#1735](https://github.com/apache/datafusion-sqlparser-rs/pull/1735) (mvzink) 27 | - Parse SET NAMES syntax in Postgres [#1752](https://github.com/apache/datafusion-sqlparser-rs/pull/1752) (mvzink) 28 | - re-add support for nested comments in mssql [#1754](https://github.com/apache/datafusion-sqlparser-rs/pull/1754) (lovasoa) 29 | - Extend support for INDEX parsing [#1707](https://github.com/apache/datafusion-sqlparser-rs/pull/1707) (LucaCappelletti94) 30 | - Parse MySQL `ALTER TABLE DROP FOREIGN KEY` syntax [#1762](https://github.com/apache/datafusion-sqlparser-rs/pull/1762) (mvzink) 31 | - add support for `with` clauses (CTEs) in `delete` statements [#1764](https://github.com/apache/datafusion-sqlparser-rs/pull/1764) (lovasoa) 32 | - SET with a list of comma separated assignments [#1757](https://github.com/apache/datafusion-sqlparser-rs/pull/1757) (MohamedAbdeen21) 33 | - Preserve MySQL-style `LIMIT , ` syntax [#1765](https://github.com/apache/datafusion-sqlparser-rs/pull/1765) (mvzink) 34 | - Add support for `DROP MATERIALIZED VIEW` [#1743](https://github.com/apache/datafusion-sqlparser-rs/pull/1743) (iffyio) 35 | - Add `CASE` and `IF` statement support [#1741](https://github.com/apache/datafusion-sqlparser-rs/pull/1741) (iffyio) 36 | - BigQuery: Add support for `CREATE SCHEMA` options [#1742](https://github.com/apache/datafusion-sqlparser-rs/pull/1742) (iffyio) 37 | - Snowflake: Support dollar quoted comments [#1755](https://github.com/apache/datafusion-sqlparser-rs/pull/1755) 38 | - Add LOCK operation for ALTER TABLE [#1768](https://github.com/apache/datafusion-sqlparser-rs/pull/1768) (MohamedAbdeen21) 39 | - Add support for `RAISE` statement [#1766](https://github.com/apache/datafusion-sqlparser-rs/pull/1766) (iffyio) 40 | - Add GLOBAL context/modifier to SET statements [#1767](https://github.com/apache/datafusion-sqlparser-rs/pull/1767) (MohamedAbdeen21) 41 | - Parse `SUBSTR` as alias for `SUBSTRING` [#1769](https://github.com/apache/datafusion-sqlparser-rs/pull/1769) (mvzink) 42 | - SET statements: scope modifier for multiple assignments [#1772](https://github.com/apache/datafusion-sqlparser-rs/pull/1772) (MohamedAbdeen21) 43 | - Support qualified column names in `MATCH AGAINST` clause [#1774](https://github.com/apache/datafusion-sqlparser-rs/pull/1774) (tomershaniii) 44 | - Mysql: Add support for := operator [#1779](https://github.com/apache/datafusion-sqlparser-rs/pull/1779) (barsela1) 45 | - Add cipherstash-proxy to list of users in README.md [#1782](https://github.com/apache/datafusion-sqlparser-rs/pull/1782) (coderdan) 46 | - Fix typos [#1785](https://github.com/apache/datafusion-sqlparser-rs/pull/1785) (jayvdb) 47 | - Add support for Databricks TIMESTAMP_NTZ. [#1781](https://github.com/apache/datafusion-sqlparser-rs/pull/1781) (romanb) 48 | - Enable double-dot-notation for mssql. [#1787](https://github.com/apache/datafusion-sqlparser-rs/pull/1787) (romanb) 49 | - Fix: Snowflake ALTER SESSION cannot be followed by other statements. [#1786](https://github.com/apache/datafusion-sqlparser-rs/pull/1786) (romanb) 50 | - Add GreptimeDB to the "Users" in README [#1788](https://github.com/apache/datafusion-sqlparser-rs/pull/1788) (MichaelScofield) 51 | - Extend snowflake grant options support [#1794](https://github.com/apache/datafusion-sqlparser-rs/pull/1794) (yoavcloud) 52 | - Fix clippy lint on rust 1.86 [#1796](https://github.com/apache/datafusion-sqlparser-rs/pull/1796) (iffyio) 53 | - Allow single quotes in EXTRACT() for Redshift. [#1795](https://github.com/apache/datafusion-sqlparser-rs/pull/1795) (romanb) 54 | - MSSQL: Add support for functionality `MERGE` output clause [#1790](https://github.com/apache/datafusion-sqlparser-rs/pull/1790) (dilovancelik) 55 | - Support additional DuckDB integer types such as HUGEINT, UHUGEINT, etc [#1797](https://github.com/apache/datafusion-sqlparser-rs/pull/1797) (alexander-beedie) 56 | - Add support for MSSQL IF/ELSE statements. [#1791](https://github.com/apache/datafusion-sqlparser-rs/pull/1791) (romanb) 57 | - Allow literal backslash escapes for string literals in Redshift dialect. [#1801](https://github.com/apache/datafusion-sqlparser-rs/pull/1801) (romanb) 58 | - Add support for MySQL's STRAIGHT_JOIN join operator. [#1802](https://github.com/apache/datafusion-sqlparser-rs/pull/1802) (romanb) 59 | - Snowflake COPY INTO target columns, select items and optional alias [#1805](https://github.com/apache/datafusion-sqlparser-rs/pull/1805) (yoavcloud) 60 | - Fix tokenization of qualified identifiers with numeric prefix. [#1803](https://github.com/apache/datafusion-sqlparser-rs/pull/1803) (romanb) 61 | - Add support for `INHERITS` option in `CREATE TABLE` statement [#1806](https://github.com/apache/datafusion-sqlparser-rs/pull/1806) (LucaCappelletti94) 62 | - Add `DROP TRIGGER` support for SQL Server [#1813](https://github.com/apache/datafusion-sqlparser-rs/pull/1813) (aharpervc) 63 | - Snowflake: support nested join without parentheses [#1799](https://github.com/apache/datafusion-sqlparser-rs/pull/1799) (barsela1) 64 | - Add support for parenthesized subquery as `IN` predicate [#1793](https://github.com/apache/datafusion-sqlparser-rs/pull/1793) (adamchainz) 65 | - Fix `STRAIGHT_JOIN` constraint when table alias is absent [#1812](https://github.com/apache/datafusion-sqlparser-rs/pull/1812) (killertux) 66 | - Add support for `PRINT` statement for SQL Server [#1811](https://github.com/apache/datafusion-sqlparser-rs/pull/1811) (aharpervc) 67 | - enable `supports_filter_during_aggregation` for Generic dialect [#1815](https://github.com/apache/datafusion-sqlparser-rs/pull/1815) (goldmedal) 68 | - Add support for `XMLTABLE` [#1817](https://github.com/apache/datafusion-sqlparser-rs/pull/1817) (lovasoa) 69 | - Add `CREATE FUNCTION` support for SQL Server [#1808](https://github.com/apache/datafusion-sqlparser-rs/pull/1808) (aharpervc) 70 | - Add `OR ALTER` support for `CREATE VIEW` [#1818](https://github.com/apache/datafusion-sqlparser-rs/pull/1818) (aharpervc) 71 | - Add `DECLARE ... CURSOR FOR` support for SQL Server [#1821](https://github.com/apache/datafusion-sqlparser-rs/pull/1821) (aharpervc) 72 | - Handle missing login in changelog generate script [#1823](https://github.com/apache/datafusion-sqlparser-rs/pull/1823) (iffyio) 73 | - Snowflake: Add support for `CONNECT_BY_ROOT` [#1780](https://github.com/apache/datafusion-sqlparser-rs/pull/1780) (tomershaniii) 74 | 75 | ## Credits 76 | 77 | Thank you to everyone who contributed to this release. Here is a breakdown of commits (PRs merged) per contributor. 78 | 79 | ``` 80 | 8 Roman Borschel 81 | 6 Ifeanyi Ubah 82 | 5 Andrew Harper 83 | 5 Michael Victor Zink 84 | 4 Mohamed Abdeen 85 | 3 Ophir LOJKINE 86 | 2 Luca Cappelletti 87 | 2 Yoav Cohen 88 | 2 bar sela 89 | 2 tomershaniii 90 | 1 Adam Johnson 91 | 1 Aleksei Piianin 92 | 1 Alexander Beedie 93 | 1 Bruno Clemente 94 | 1 Dan Draper 95 | 1 DilovanCelik 96 | 1 Jax Liu 97 | 1 John Vandenberg 98 | 1 LFC 99 | ``` 100 | 101 | Thank you also to everyone who contributed in other ways such as filing issues, reviewing PRs, and providing feedback on this release. 102 | 103 | -------------------------------------------------------------------------------- /derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | [package] 19 | name = "sqlparser_derive" 20 | description = "Procedural (proc) macros for sqlparser" 21 | version = "0.3.0" 22 | authors = ["sqlparser-rs authors"] 23 | homepage = "https://github.com/sqlparser-rs/sqlparser-rs" 24 | documentation = "https://docs.rs/sqlparser_derive/" 25 | keywords = ["ansi", "sql", "lexer", "parser"] 26 | repository = "https://github.com/sqlparser-rs/sqlparser-rs" 27 | license = "Apache-2.0" 28 | include = [ 29 | "src/**/*.rs", 30 | "Cargo.toml", 31 | "LICENSE.TXT", 32 | ] 33 | edition = "2021" 34 | 35 | [lib] 36 | proc-macro = true 37 | 38 | [dependencies] 39 | syn = { version = "2.0", default-features = false, features = ["printing", "parsing", "derive", "proc-macro"] } 40 | proc-macro2 = "1.0" 41 | quote = "1.0" 42 | -------------------------------------------------------------------------------- /derive/LICENSE.TXT: -------------------------------------------------------------------------------- 1 | ../LICENSE.TXT -------------------------------------------------------------------------------- /derive/README.md: -------------------------------------------------------------------------------- 1 | 19 | 20 | # SQL Parser Derive Macro 21 | 22 | ## Visit 23 | 24 | This crate contains a procedural macro that can automatically derive 25 | implementations of the `Visit` trait in the [sqlparser](https://crates.io/crates/sqlparser) crate 26 | 27 | ```rust 28 | #[derive(Visit, VisitMut)] 29 | struct Foo { 30 | boolean: bool, 31 | bar: Bar, 32 | } 33 | 34 | #[derive(Visit, VisitMut)] 35 | enum Bar { 36 | A(), 37 | B(String, bool), 38 | C { named: i32 }, 39 | } 40 | ``` 41 | 42 | Will generate code akin to 43 | 44 | ```rust 45 | impl Visit for Foo { 46 | fn visit(&self, visitor: &mut V) -> ControlFlow { 47 | self.boolean.visit(visitor)?; 48 | self.bar.visit(visitor)?; 49 | ControlFlow::Continue(()) 50 | } 51 | } 52 | 53 | impl Visit for Bar { 54 | fn visit(&self, visitor: &mut V) -> ControlFlow { 55 | match self { 56 | Self::A() => {} 57 | Self::B(_1, _2) => { 58 | _1.visit(visitor)?; 59 | _2.visit(visitor)?; 60 | } 61 | Self::C { named } => { 62 | named.visit(visitor)?; 63 | } 64 | } 65 | ControlFlow::Continue(()) 66 | } 67 | } 68 | ``` 69 | 70 | Some types may wish to call a corresponding method on the visitor: 71 | 72 | ```rust 73 | #[derive(Visit, VisitMut)] 74 | #[visit(with = "visit_expr")] 75 | enum Expr { 76 | IsNull(Box), 77 | .. 78 | } 79 | ``` 80 | 81 | This will result in the following sequence of visitor calls when an `IsNull` 82 | expression is visited 83 | 84 | ``` 85 | visitor.pre_visit_expr() 86 | visitor.pre_visit_expr() 87 | visitor.post_visit_expr() 88 | visitor.post_visit_expr() 89 | ``` 90 | 91 | For some types it is only appropriate to call a particular visitor method in 92 | some contexts. For example, not every `ObjectName` refers to a relation. 93 | 94 | In these cases, the `visit` attribute can be used on the field for which we'd 95 | like to call the method: 96 | 97 | ```rust 98 | #[derive(Visit, VisitMut)] 99 | #[visit(with = "visit_table_factor")] 100 | pub enum TableFactor { 101 | Table { 102 | #[visit(with = "visit_relation")] 103 | name: ObjectName, 104 | alias: Option, 105 | }, 106 | .. 107 | } 108 | ``` 109 | 110 | This will generate 111 | 112 | ```rust 113 | impl Visit for TableFactor { 114 | fn visit(&self, visitor: &mut V) -> ControlFlow { 115 | visitor.pre_visit_table_factor(self)?; 116 | match self { 117 | Self::Table { name, alias } => { 118 | visitor.pre_visit_relation(name)?; 119 | name.visit(visitor)?; 120 | visitor.post_visit_relation(name)?; 121 | alias.visit(visitor)?; 122 | } 123 | } 124 | visitor.post_visit_table_factor(self)?; 125 | ControlFlow::Continue(()) 126 | } 127 | } 128 | ``` 129 | 130 | Note that annotating both the type and the field is incorrect as it will result 131 | in redundant calls to the method. For example 132 | 133 | ```rust 134 | #[derive(Visit, VisitMut)] 135 | #[visit(with = "visit_expr")] 136 | enum Expr { 137 | IsNull(#[visit(with = "visit_expr")] Box), 138 | .. 139 | } 140 | ``` 141 | 142 | will result in these calls to the visitor 143 | 144 | 145 | ``` 146 | visitor.pre_visit_expr() 147 | visitor.pre_visit_expr() 148 | visitor.pre_visit_expr() 149 | visitor.post_visit_expr() 150 | visitor.post_visit_expr() 151 | visitor.post_visit_expr() 152 | ``` 153 | 154 | If the field is a `Option` and add `#[with = "visit_xxx"]` to the field, the generated code 155 | will try to access the field only if it is `Some`: 156 | 157 | ```rust 158 | #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] 159 | pub struct ShowStatementIn { 160 | pub clause: ShowStatementInClause, 161 | pub parent_type: Option, 162 | #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))] 163 | pub parent_name: Option, 164 | } 165 | ``` 166 | 167 | This will generate 168 | 169 | ```rust 170 | impl sqlparser::ast::Visit for ShowStatementIn { 171 | fn visit( 172 | &self, 173 | visitor: &mut V, 174 | ) -> ::std::ops::ControlFlow { 175 | sqlparser::ast::Visit::visit(&self.clause, visitor)?; 176 | sqlparser::ast::Visit::visit(&self.parent_type, visitor)?; 177 | if let Some(value) = &self.parent_name { 178 | visitor.pre_visit_relation(value)?; 179 | sqlparser::ast::Visit::visit(value, visitor)?; 180 | visitor.post_visit_relation(value)?; 181 | } 182 | ::std::ops::ControlFlow::Continue(()) 183 | } 184 | } 185 | 186 | impl sqlparser::ast::VisitMut for ShowStatementIn { 187 | fn visit( 188 | &mut self, 189 | visitor: &mut V, 190 | ) -> ::std::ops::ControlFlow { 191 | sqlparser::ast::VisitMut::visit(&mut self.clause, visitor)?; 192 | sqlparser::ast::VisitMut::visit(&mut self.parent_type, visitor)?; 193 | if let Some(value) = &mut self.parent_name { 194 | visitor.pre_visit_relation(value)?; 195 | sqlparser::ast::VisitMut::visit(value, visitor)?; 196 | visitor.post_visit_relation(value)?; 197 | } 198 | ::std::ops::ControlFlow::Continue(()) 199 | } 200 | } 201 | ``` 202 | 203 | ## Releasing 204 | 205 | This crate's release is not automated. Instead it is released manually as needed 206 | 207 | Steps: 208 | 1. Update the version in `Cargo.toml` 209 | 2. Update the corresponding version in `../Cargo.toml` 210 | 3. Commit via PR 211 | 4. Publish to crates.io: 212 | 213 | ```shell 214 | # update to latest checked in main branch and publish via 215 | cargo publish 216 | ``` 217 | 218 | -------------------------------------------------------------------------------- /dev/release/README.md: -------------------------------------------------------------------------------- 1 | 19 | 20 | 21 | ## Process Overview 22 | 23 | As part of the Apache governance model, official releases consist of signed 24 | source tarballs approved by the DataFusion PMC. 25 | 26 | We then use the code in the approved artifacts to release to crates.io. 27 | 28 | ### Change Log 29 | 30 | We maintain a `CHANGELOG.md` so our users know what has been changed between releases. 31 | 32 | You will need a GitHub Personal Access Token for the following steps. Follow 33 | [these instructions](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token) 34 | to generate one if you do not already have one. 35 | 36 | The changelog is generated using a Python script which needs `PyGitHub`, installed using pip: 37 | 38 | ```shell 39 | pip3 install PyGitHub 40 | ``` 41 | 42 | To generate the changelog, set the `GITHUB_TOKEN` environment variable to a valid token and then run the script 43 | providing two commit ids or tags followed by the version number of the release being created. The following 44 | example generates a change log of all changes between the first commit and the current HEAD revision. 45 | 46 | ```shell 47 | export GITHUB_TOKEN= 48 | python ./dev/release/generate-changelog.py v0.51.0 HEAD 0.52.0 > changelog/0.52.0.md 49 | ``` 50 | 51 | This script creates a changelog from GitHub PRs based on the labels associated with them as well as looking for 52 | titles starting with `feat:`, `fix:`, or `docs:`. 53 | 54 | Add an entry to CHANGELOG.md for the new version 55 | 56 | ## Prepare release commits and PR 57 | 58 | ### Update Version 59 | 60 | Checkout the main commit to be released 61 | 62 | ```shell 63 | git fetch apache 64 | git checkout apache/main 65 | ``` 66 | 67 | Manually update the version in the root `Cargo.toml` to the release version (e.g. `0.52.0`). 68 | 69 | Lastly commit the version change: 70 | 71 | ```shell 72 | git commit -a -m 'Update version' 73 | ``` 74 | 75 | ## Prepare release candidate artifacts 76 | 77 | After the PR gets merged, you are ready to create release artifacts from the 78 | merged commit. 79 | 80 | (Note you need to be a committer to run these scripts as they upload to the apache svn distribution servers) 81 | 82 | ### Pick a Release Candidate (RC) number 83 | 84 | Pick numbers in sequential order, with `0` for `rc0`, `1` for `rc1`, etc. 85 | 86 | ### Create git tag for the release: 87 | 88 | While the official release artifacts are signed tarballs and zip files, we also 89 | tag the commit it was created for convenience and code archaeology. 90 | 91 | Using a string such as `v0.52.0` as the ``, create and push the tag by running these commands: 92 | 93 | For example to tag version `0.52.0` 94 | 95 | ```shell 96 | git fetch apache 97 | git tag v0.52.0-rc1 apache/main 98 | # push tag to Github remote 99 | git push apache v0.52.0-rc1 100 | ``` 101 | 102 | ### Create, sign, and upload artifacts 103 | 104 | Run `create-tarball.sh` with the `` tag and `` and you found in previous steps: 105 | 106 | ```shell 107 | GITHUB_TOKEN= ./dev/release/create-tarball.sh 0.52.0 1 108 | ``` 109 | 110 | The `create-tarball.sh` script 111 | 112 | 1. creates and uploads all release candidate artifacts to the [datafusion 113 | dev](https://dist.apache.org/repos/dist/dev/datafusion) location on the 114 | apache distribution svn server 115 | 116 | 2. provide you an email template to 117 | send to dev@datafusion.apache.org for release voting. 118 | 119 | ### Vote on Release Candidate artifacts 120 | 121 | Send the email output from the script to dev@datafusion.apache.org. 122 | 123 | For the release to become "official" it needs at least three PMC members to vote +1 on it. 124 | 125 | ### Verifying Release Candidates 126 | 127 | The `dev/release/verify-release-candidate.sh` is a script in this repository that can assist in the verification process. Run it like: 128 | 129 | ```shell 130 | ./dev/release/verify-release-candidate.sh 0.52.0 1 131 | ``` 132 | 133 | #### If the release is not approved 134 | 135 | If the release is not approved, fix whatever the problem is, merge changelog 136 | changes into main if there is any and try again with the next RC number. 137 | 138 | ## Finalize the release 139 | 140 | NOTE: steps in this section can only be done by PMC members. 141 | 142 | ### After the release is approved 143 | 144 | Move artifacts to the release location in SVN, using the `release-tarball.sh` script: 145 | 146 | ```shell 147 | ./dev/release/release-tarball.sh 0.52.0 1 148 | ``` 149 | 150 | Promote the rc tag to the release tag 151 | ```shell 152 | git tag v0.52.0 v0.52.0-rc3 153 | git push apache v0.52.0 154 | ``` 155 | 156 | Congratulations! The release is now official! 157 | 158 | ### Publish on Crates.io 159 | 160 | Only approved releases of the tarball should be published to 161 | crates.io, in order to conform to Apache Software Foundation 162 | governance standards. 163 | 164 | A DataFusion committer can publish this crate after an official project release has 165 | been made to crates.io using the following instructions. 166 | 167 | Follow [these 168 | instructions](https://doc.rust-lang.org/cargo/reference/publishing.html) to 169 | create an account and login to crates.io before asking to be added as an owner 170 | to the sqlparser DataFusion crates. 171 | 172 | Download and unpack the official release tarball 173 | 174 | Verify that the Cargo.toml in the tarball contains the correct version 175 | (e.g. `version = "0.52.0"`) and then publish the crates by running the following commands 176 | 177 | ```shell 178 | cargo publish 179 | ``` 180 | 181 | If necessary, also publish the `sqlparser_derive` crate: 182 | 183 | crates.io homepage: https://crates.io/crates/sqlparser_derive 184 | 185 | ```shell 186 | (cd derive && cargo publish 187 | ``` 188 | -------------------------------------------------------------------------------- /dev/release/check-rat-report.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | ############################################################################## 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | ############################################################################## 20 | import fnmatch 21 | import re 22 | import sys 23 | import xml.etree.ElementTree as ET 24 | 25 | if len(sys.argv) != 3: 26 | sys.stderr.write("Usage: %s exclude_globs.lst rat_report.xml\n" % 27 | sys.argv[0]) 28 | sys.exit(1) 29 | 30 | exclude_globs_filename = sys.argv[1] 31 | xml_filename = sys.argv[2] 32 | 33 | globs = [line.strip() for line in open(exclude_globs_filename, "r")] 34 | 35 | tree = ET.parse(xml_filename) 36 | root = tree.getroot() 37 | resources = root.findall('resource') 38 | 39 | all_ok = True 40 | for r in resources: 41 | approvals = r.findall('license-approval') 42 | if not approvals or approvals[0].attrib['name'] == 'true': 43 | continue 44 | clean_name = re.sub('^[^/]+/', '', r.attrib['name']) 45 | excluded = False 46 | for g in globs: 47 | if fnmatch.fnmatch(clean_name, g): 48 | excluded = True 49 | break 50 | if not excluded: 51 | sys.stdout.write("NOT APPROVED: %s (%s): %s\n" % ( 52 | clean_name, r.attrib['name'], approvals[0].attrib['name'])) 53 | all_ok = False 54 | 55 | if not all_ok: 56 | sys.exit(1) 57 | 58 | print('OK') 59 | sys.exit(0) 60 | -------------------------------------------------------------------------------- /dev/release/create-tarball.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # 20 | 21 | # Adapted from https://github.com/apache/datafusion/tree/master/dev/release/create-tarball.sh 22 | 23 | # This script creates a signed tarball in 24 | # dev/dist/apache-datafusion-sqlparser-rs--rc.tar.gz and uploads it to 25 | # the "dev" area of the dist.apache.datafusion repository and prepares an 26 | # email for sending to the dev@datafusion.apache.org list for a formal 27 | # vote. 28 | # 29 | # See release/README.md for full release instructions 30 | # 31 | # Requirements: 32 | # 33 | # 1. gpg setup for signing and have uploaded your public 34 | # signature to https://pgp.mit.edu/ 35 | # 36 | # 2. Logged into the apache svn server with the appropriate 37 | # credentials 38 | # 39 | # 3. Install the requests python package 40 | # 41 | # 42 | # Based in part on 02-source.sh from apache/arrow 43 | # 44 | 45 | set -e 46 | 47 | SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 48 | SOURCE_TOP_DIR="$(cd "${SOURCE_DIR}/../../" && pwd)" 49 | 50 | if [ "$#" -ne 2 ]; then 51 | echo "Usage: $0 " 52 | echo "ex. $0 0.52.0 2" 53 | exit 54 | fi 55 | 56 | if [[ -z "${GITHUB_TOKEN}" ]]; then 57 | echo "Please set personal github token through GITHUB_TOKEN environment variable" 58 | exit 59 | fi 60 | 61 | version=$1 62 | rc=$2 63 | tag="v${version}-rc${rc}" 64 | 65 | echo "Attempting to create ${tarball} from tag ${tag}" 66 | release_hash=$(cd "${SOURCE_TOP_DIR}" && git rev-list --max-count=1 ${tag}) 67 | 68 | release=apache-datafusion-sqlparser-rs-${version} 69 | distdir=${SOURCE_TOP_DIR}/dev/dist/${release}-rc${rc} 70 | tarname=${release}.tar.gz 71 | tarball=${distdir}/${tarname} 72 | url="https://dist.apache.org/repos/dist/dev/datafusion/${release}-rc${rc}" 73 | 74 | if [ -z "$release_hash" ]; then 75 | echo "Cannot continue: unknown git tag: ${tag}" 76 | fi 77 | 78 | echo "Draft email for dev@datafusion.apache.org mailing list" 79 | echo "" 80 | echo "---------------------------------------------------------" 81 | cat < containing the files in git at $release_hash 116 | # the files in the tarball are prefixed with {version} (e.g. 4.0.1) 117 | mkdir -p ${distdir} 118 | (cd "${SOURCE_TOP_DIR}" && git archive ${release_hash} --prefix ${release}/ | gzip > ${tarball}) 119 | 120 | echo "Running rat license checker on ${tarball}" 121 | ${SOURCE_DIR}/run-rat.sh ${tarball} 122 | 123 | echo "Signing tarball and creating checksums" 124 | gpg --armor --output ${tarball}.asc --detach-sig ${tarball} 125 | # create signing with relative path of tarball 126 | # so that they can be verified with a command such as 127 | # shasum --check apache-datafusion-sqlparser-rs-0.52.0-rc1.tar.gz.sha512 128 | (cd ${distdir} && shasum -a 256 ${tarname}) > ${tarball}.sha256 129 | (cd ${distdir} && shasum -a 512 ${tarname}) > ${tarball}.sha512 130 | 131 | 132 | echo "Uploading to sqlparser-rs dist/dev to ${url}" 133 | svn co --depth=empty https://dist.apache.org/repos/dist/dev/datafusion ${SOURCE_TOP_DIR}/dev/dist 134 | svn add ${distdir} 135 | svn ci -m "Apache DataFusion ${version} ${rc}" ${distdir} 136 | -------------------------------------------------------------------------------- /dev/release/generate-changelog.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Licensed to the Apache Software Foundation (ASF) under one or more 4 | # contributor license agreements. See the NOTICE file distributed with 5 | # this work for additional information regarding copyright ownership. 6 | # The ASF licenses this file to You under the Apache License, Version 2.0 7 | # (the "License"); you may not use this file except in compliance with 8 | # the License. You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | import argparse 19 | import sys 20 | from github import Github 21 | import os 22 | import re 23 | import subprocess 24 | 25 | def print_pulls(repo_name, title, pulls): 26 | if len(pulls) > 0: 27 | print("**{}:**".format(title)) 28 | print() 29 | for (pull, commit) in pulls: 30 | url = "https://github.com/{}/pull/{}".format(repo_name, pull.number) 31 | author = f"({commit.author.login})" if commit.author else '' 32 | print("- {} [#{}]({}) {}".format(pull.title, pull.number, url, author)) 33 | print() 34 | 35 | 36 | def generate_changelog(repo, repo_name, tag1, tag2, version): 37 | 38 | # get a list of commits between two tags 39 | print(f"Fetching list of commits between {tag1} and {tag2}", file=sys.stderr) 40 | comparison = repo.compare(tag1, tag2) 41 | 42 | # get the pull requests for these commits 43 | print("Fetching pull requests", file=sys.stderr) 44 | unique_pulls = [] 45 | all_pulls = [] 46 | for commit in comparison.commits: 47 | pulls = commit.get_pulls() 48 | for pull in pulls: 49 | # there can be multiple commits per PR if squash merge is not being used and 50 | # in this case we should get all the author names, but for now just pick one 51 | if pull.number not in unique_pulls: 52 | unique_pulls.append(pull.number) 53 | all_pulls.append((pull, commit)) 54 | 55 | # we split the pulls into categories 56 | breaking = [] 57 | bugs = [] 58 | docs = [] 59 | enhancements = [] 60 | performance = [] 61 | other = [] 62 | 63 | # categorize the pull requests based on GitHub labels 64 | print("Categorizing pull requests", file=sys.stderr) 65 | for (pull, commit) in all_pulls: 66 | 67 | # see if PR title uses Conventional Commits 68 | cc_type = '' 69 | cc_scope = '' 70 | cc_breaking = '' 71 | parts = re.findall(r'^([a-z]+)(\([a-z]+\))?(!)?:', pull.title) 72 | if len(parts) == 1: 73 | parts_tuple = parts[0] 74 | cc_type = parts_tuple[0] # fix, feat, docs, chore 75 | cc_scope = parts_tuple[1] # component within project 76 | cc_breaking = parts_tuple[2] == '!' 77 | 78 | labels = [label.name for label in pull.labels] 79 | if 'api change' in labels or cc_breaking: 80 | breaking.append((pull, commit)) 81 | elif 'bug' in labels or cc_type == 'fix': 82 | bugs.append((pull, commit)) 83 | elif 'performance' in labels or cc_type == 'perf': 84 | performance.append((pull, commit)) 85 | elif 'enhancement' in labels or cc_type == 'feat': 86 | enhancements.append((pull, commit)) 87 | elif 'documentation' in labels or cc_type == 'docs' or cc_type == 'doc': 88 | docs.append((pull, commit)) 89 | else: 90 | other.append((pull, commit)) 91 | 92 | # produce the changelog content 93 | print("Generating changelog content", file=sys.stderr) 94 | 95 | # ASF header 96 | print("""\n""") 114 | 115 | print(f"# sqlparser-rs {version} Changelog\n") 116 | 117 | # get the number of commits 118 | commit_count = subprocess.check_output(f"git log --pretty=oneline {tag1}..{tag2} | wc -l", shell=True, text=True).strip() 119 | 120 | # get number of contributors 121 | contributor_count = subprocess.check_output(f"git shortlog -sn {tag1}..{tag2} | wc -l", shell=True, text=True).strip() 122 | 123 | print(f"This release consists of {commit_count} commits from {contributor_count} contributors. " 124 | f"See credits at the end of this changelog for more information.\n") 125 | 126 | print_pulls(repo_name, "Breaking changes", breaking) 127 | print_pulls(repo_name, "Performance related", performance) 128 | print_pulls(repo_name, "Implemented enhancements", enhancements) 129 | print_pulls(repo_name, "Fixed bugs", bugs) 130 | print_pulls(repo_name, "Documentation updates", docs) 131 | print_pulls(repo_name, "Other", other) 132 | 133 | # show code contributions 134 | credits = subprocess.check_output(f"git shortlog -sn {tag1}..{tag2}", shell=True, text=True).rstrip() 135 | 136 | print("## Credits\n") 137 | print("Thank you to everyone who contributed to this release. Here is a breakdown of commits (PRs merged) " 138 | "per contributor.\n") 139 | print("```") 140 | print(credits) 141 | print("```\n") 142 | 143 | print("Thank you also to everyone who contributed in other ways such as filing issues, reviewing " 144 | "PRs, and providing feedback on this release.\n") 145 | 146 | def cli(args=None): 147 | """Process command line arguments.""" 148 | if not args: 149 | args = sys.argv[1:] 150 | 151 | parser = argparse.ArgumentParser() 152 | parser.add_argument("tag1", help="The previous commit or tag (e.g. 0.1.0)") 153 | parser.add_argument("tag2", help="The current commit or tag (e.g. HEAD)") 154 | parser.add_argument("version", help="The version number to include in the changelog") 155 | args = parser.parse_args() 156 | 157 | token = os.getenv("GITHUB_TOKEN") 158 | project = "apache/datafusion-sqlparser-rs" 159 | 160 | g = Github(token) 161 | repo = g.get_repo(project) 162 | generate_changelog(repo, project, args.tag1, args.tag2, args.version) 163 | 164 | if __name__ == "__main__": 165 | cli() 166 | -------------------------------------------------------------------------------- /dev/release/rat_exclude_files.txt: -------------------------------------------------------------------------------- 1 | # Files to exclude from the Apache Rat (license) check 2 | .gitignore 3 | .tool-versions 4 | dev/release/rat_exclude_files.txt 5 | fuzz/.gitignore 6 | sqlparser_bench/img/flamegraph.svg 7 | 8 | -------------------------------------------------------------------------------- /dev/release/release-tarball.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # 20 | 21 | # Adapted from https://github.com/apache/arrow-rs/tree/master/dev/release/release-tarball.sh 22 | 23 | # This script copies a tarball from the "dev" area of the 24 | # dist.apache.datafusion repository to the "release" area 25 | # 26 | # This script should only be run after the release has been approved 27 | # by the Apache DataFusion PMC committee. 28 | # 29 | # See release/README.md for full release instructions 30 | # 31 | # Based in part on post-01-upload.sh from apache/arrow 32 | 33 | 34 | set -e 35 | set -u 36 | 37 | if [ "$#" -ne 2 ]; then 38 | echo "Usage: $0 " 39 | echo "ex. $0 0.52.0 2" 40 | exit 41 | fi 42 | 43 | version=$1 44 | rc=$2 45 | 46 | tmp_dir=tmp-apache-datafusion-dist 47 | 48 | echo "Recreate temporary directory: ${tmp_dir}" 49 | rm -rf ${tmp_dir} 50 | mkdir -p ${tmp_dir} 51 | 52 | echo "Clone dev dist repository" 53 | svn \ 54 | co \ 55 | https://dist.apache.org/repos/dist/dev/datafusion/apache-datafusion-sqlparser-rs-${version}-rc${rc} \ 56 | ${tmp_dir}/dev 57 | 58 | echo "Clone release dist repository" 59 | svn co https://dist.apache.org/repos/dist/release/datafusion ${tmp_dir}/release 60 | 61 | echo "Copy ${version}-rc${rc} to release working copy" 62 | release_version=datafusion-sqlparser-rs-${version} 63 | mkdir -p ${tmp_dir}/release/${release_version} 64 | cp -r ${tmp_dir}/dev/* ${tmp_dir}/release/${release_version}/ 65 | svn add ${tmp_dir}/release/${release_version} 66 | 67 | echo "Commit release" 68 | svn ci -m "Apache DataFusion sqlparser-rs ${version}" ${tmp_dir}/release 69 | 70 | echo "Clean up" 71 | rm -rf ${tmp_dir} 72 | 73 | echo "Success! The release is available here:" 74 | echo " https://dist.apache.org/repos/dist/release/datafusion/${release_version}" 75 | -------------------------------------------------------------------------------- /dev/release/run-rat.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # 20 | 21 | RAT_VERSION=0.13 22 | 23 | # download apache rat 24 | if [ ! -f apache-rat-${RAT_VERSION}.jar ]; then 25 | curl -s https://repo1.maven.org/maven2/org/apache/rat/apache-rat/${RAT_VERSION}/apache-rat-${RAT_VERSION}.jar > apache-rat-${RAT_VERSION}.jar 26 | fi 27 | 28 | RAT="java -jar apache-rat-${RAT_VERSION}.jar -x " 29 | 30 | RELEASE_DIR=$(cd "$(dirname "$BASH_SOURCE")"; pwd) 31 | 32 | # generate the rat report 33 | $RAT $1 > rat.txt 34 | python $RELEASE_DIR/check-rat-report.py $RELEASE_DIR/rat_exclude_files.txt rat.txt > filtered_rat.txt 35 | cat filtered_rat.txt 36 | UNAPPROVED=`cat filtered_rat.txt | grep "NOT APPROVED" | wc -l` 37 | 38 | if [ "0" -eq "${UNAPPROVED}" ]; then 39 | echo "No unapproved licenses" 40 | else 41 | echo "${UNAPPROVED} unapproved licences. Check rat report: rat.txt" 42 | exit 1 43 | fi 44 | -------------------------------------------------------------------------------- /dev/release/verify-release-candidate.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # 20 | 21 | case $# in 22 | 2) VERSION="$1" 23 | RC_NUMBER="$2" 24 | ;; 25 | *) echo "Usage: $0 X.Y.Z RC_NUMBER" 26 | exit 1 27 | ;; 28 | esac 29 | 30 | set -e 31 | set -x 32 | set -o pipefail 33 | 34 | SOURCE_DIR="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")" && pwd)" 35 | ARROW_DIR="$(dirname $(dirname ${SOURCE_DIR}))" 36 | ARROW_DIST_URL='https://dist.apache.org/repos/dist/dev/datafusion' 37 | 38 | download_dist_file() { 39 | curl \ 40 | --silent \ 41 | --show-error \ 42 | --fail \ 43 | --location \ 44 | --remote-name $ARROW_DIST_URL/$1 45 | } 46 | 47 | download_rc_file() { 48 | download_dist_file apache-datafusion-sqlparser-rs-${VERSION}-rc${RC_NUMBER}/$1 49 | } 50 | 51 | import_gpg_keys() { 52 | download_dist_file KEYS 53 | gpg --import KEYS 54 | } 55 | 56 | if type shasum >/dev/null 2>&1; then 57 | sha256_verify="shasum -a 256 -c" 58 | sha512_verify="shasum -a 512 -c" 59 | else 60 | sha256_verify="sha256sum -c" 61 | sha512_verify="sha512sum -c" 62 | fi 63 | 64 | fetch_archive() { 65 | local dist_name=$1 66 | download_rc_file ${dist_name}.tar.gz 67 | download_rc_file ${dist_name}.tar.gz.asc 68 | download_rc_file ${dist_name}.tar.gz.sha256 69 | download_rc_file ${dist_name}.tar.gz.sha512 70 | verify_dir_artifact_signatures 71 | } 72 | 73 | verify_dir_artifact_signatures() { 74 | # verify the signature and the checksums of each artifact 75 | find . -name '*.asc' | while read sigfile; do 76 | artifact=${sigfile/.asc/} 77 | gpg --verify $sigfile $artifact || exit 1 78 | 79 | # go into the directory because the checksum files contain only the 80 | # basename of the artifact 81 | pushd $(dirname $artifact) 82 | base_artifact=$(basename $artifact) 83 | ${sha256_verify} $base_artifact.sha256 || exit 1 84 | ${sha512_verify} $base_artifact.sha512 || exit 1 85 | popd 86 | done 87 | } 88 | 89 | setup_tempdir() { 90 | cleanup() { 91 | if [ "${TEST_SUCCESS}" = "yes" ]; then 92 | rm -fr "${ARROW_TMPDIR}" 93 | else 94 | echo "Failed to verify release candidate. See ${ARROW_TMPDIR} for details." 95 | fi 96 | } 97 | 98 | if [ -z "${ARROW_TMPDIR}" ]; then 99 | # clean up automatically if ARROW_TMPDIR is not defined 100 | ARROW_TMPDIR=$(mktemp -d -t "$1.XXXXX") 101 | trap cleanup EXIT 102 | else 103 | # don't clean up automatically 104 | mkdir -p "${ARROW_TMPDIR}" 105 | fi 106 | } 107 | 108 | test_source_distribution() { 109 | # install rust toolchain in a similar fashion like test-miniconda 110 | export RUSTUP_HOME=$PWD/test-rustup 111 | export CARGO_HOME=$PWD/test-rustup 112 | 113 | curl https://sh.rustup.rs -sSf | sh -s -- -y --no-modify-path 114 | 115 | export PATH=$RUSTUP_HOME/bin:$PATH 116 | source $RUSTUP_HOME/env 117 | 118 | # build and test rust 119 | 120 | # raises on any formatting errors 121 | rustup component add rustfmt --toolchain stable 122 | cargo fmt --all -- --check 123 | 124 | cargo build 125 | cargo test --all-features 126 | 127 | if ( find -iname 'Cargo.toml' | xargs grep SNAPSHOT ); then 128 | echo "Cargo.toml version should not contain SNAPSHOT for releases" 129 | exit 1 130 | fi 131 | 132 | # Check that publish works 133 | cargo publish --dry-run 134 | } 135 | 136 | TEST_SUCCESS=no 137 | 138 | setup_tempdir "datafusion-sqlparser-rs-${VERSION}" 139 | echo "Working in sandbox ${ARROW_TMPDIR}" 140 | cd ${ARROW_TMPDIR} 141 | 142 | dist_name="apache-datafusion-sqlparser-rs-${VERSION}" 143 | import_gpg_keys 144 | fetch_archive ${dist_name} 145 | tar xf ${dist_name}.tar.gz 146 | pushd ${dist_name} 147 | test_source_distribution 148 | popd 149 | 150 | TEST_SUCCESS=yes 151 | echo 'Release candidate looks good!' 152 | exit 0 153 | -------------------------------------------------------------------------------- /docs/benchmarking.md: -------------------------------------------------------------------------------- 1 | 19 | 20 | # Benchmarking 21 | 22 | Run `cargo bench` in the project `sqlparser_bench` execute the queries. 23 | It will report results using the `criterion` library to perform the benchmarking. 24 | 25 | The bench project lives in another crate, to avoid the negative impact on building the `sqlparser` crate. 26 | -------------------------------------------------------------------------------- /docs/custom_sql_parser.md: -------------------------------------------------------------------------------- 1 | 19 | 20 | # Writing a Custom SQL Parser 21 | 22 | I have explored many different ways of building this library to make it easy to extend it for custom SQL dialects. Most of my attempts ended in failure but I have now found a workable solution. It is not without downsides but this seems to be the most pragmatic solution. 23 | 24 | The concept is simply to write a new parser that delegates to the ANSI parser so that as much as possible of the core functionality can be re-used. 25 | 26 | I also plan on building in specific support for custom data types, where a lambda function can be passed to the parser to parse data types. 27 | 28 | For an example of this, see the [DataFusion](https://github.com/apache/arrow-datafusion) project and its [query planner](https://github.com/apache/arrow-datafusion/tree/master/datafusion/sql). 29 | 30 | -------------------------------------------------------------------------------- /docs/fuzzing.md: -------------------------------------------------------------------------------- 1 | 19 | 20 | # Fuzzing 21 | 22 | ## Installing `honggfuzz` 23 | 24 | ``` 25 | cargo install honggfuzz 26 | ``` 27 | 28 | Install [dependencies](https://github.com/rust-fuzz/honggfuzz-rs#dependencies) for your system. 29 | 30 | ## Running the fuzzer 31 | 32 | Running the fuzzer is as easy as running in the `fuzz` directory. 33 | 34 | Choose a target: 35 | 36 | These are `[[bin]]` entries in `Cargo.toml`. 37 | List them with `cargo read-manifest | jq '.targets[].name'` from the `fuzz` directory. 38 | 39 | Run the fuzzer: 40 | 41 | ```shell 42 | cd fuzz 43 | cargo hfuzz run 44 | ``` 45 | 46 | After a panic is found, get a stack trace with: 47 | 48 | ```shell 49 | cargo hfuzz run-debug hfuzz_workspace//*.fuzz 50 | ``` 51 | 52 | For example, with the `fuzz_parse_sql` target: 53 | 54 | ```shell 55 | cargo hfuzz run fuzz_parse_sql 56 | cargo hfuzz run-debug fuzz_parse_sql hfuzz_workspace/fuzz_parse_sql/*.fuzz 57 | ``` 58 | -------------------------------------------------------------------------------- /examples/cli.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | #![warn(clippy::all)] 19 | 20 | //! A small command-line app to run the parser. 21 | //! Run with `cargo run --example cli` 22 | 23 | use std::fs; 24 | use std::io::{stdin, Read}; 25 | 26 | use simple_logger::SimpleLogger; 27 | use sqlparser::dialect::*; 28 | use sqlparser::parser::Parser; 29 | 30 | fn main() { 31 | SimpleLogger::new().init().unwrap(); 32 | 33 | let filename = std::env::args().nth(1).expect( 34 | r#" 35 | No arguments provided! 36 | 37 | Usage: 38 | $ cargo run --example cli FILENAME.sql [--dialectname] 39 | 40 | To print the parse results as JSON: 41 | $ cargo run --feature json_example --example cli FILENAME.sql [--dialectname] 42 | 43 | To read from stdin instead of a file: 44 | $ cargo run --example cli - [--dialectname] 45 | 46 | "#, 47 | ); 48 | 49 | let dialect: Box = match std::env::args().nth(2).unwrap_or_default().as_ref() { 50 | "--ansi" => Box::new(AnsiDialect {}), 51 | "--bigquery" => Box::new(BigQueryDialect {}), 52 | "--postgres" => Box::new(PostgreSqlDialect {}), 53 | "--ms" => Box::new(MsSqlDialect {}), 54 | "--mysql" => Box::new(MySqlDialect {}), 55 | "--snowflake" => Box::new(SnowflakeDialect {}), 56 | "--hive" => Box::new(HiveDialect {}), 57 | "--redshift" => Box::new(RedshiftSqlDialect {}), 58 | "--clickhouse" => Box::new(ClickHouseDialect {}), 59 | "--duckdb" => Box::new(DuckDbDialect {}), 60 | "--sqlite" => Box::new(SQLiteDialect {}), 61 | "--generic" | "" => Box::new(GenericDialect {}), 62 | s => panic!("Unexpected parameter: {s}"), 63 | }; 64 | 65 | let contents = if filename == "-" { 66 | println!("Parsing from stdin using {:?}", dialect); 67 | let mut buf = Vec::new(); 68 | stdin() 69 | .read_to_end(&mut buf) 70 | .expect("failed to read from stdin"); 71 | String::from_utf8(buf).expect("stdin content wasn't valid utf8") 72 | } else { 73 | println!("Parsing from file '{}' using {:?}", &filename, dialect); 74 | fs::read_to_string(&filename) 75 | .unwrap_or_else(|_| panic!("Unable to read the file {}", &filename)) 76 | }; 77 | let without_bom = if contents.chars().next().unwrap() as u64 != 0xfeff { 78 | contents.as_str() 79 | } else { 80 | let mut chars = contents.chars(); 81 | chars.next(); 82 | chars.as_str() 83 | }; 84 | let parse_result = Parser::parse_sql(&*dialect, without_bom); 85 | match parse_result { 86 | Ok(statements) => { 87 | println!( 88 | "Round-trip:\n'{}'", 89 | statements 90 | .iter() 91 | .map(std::string::ToString::to_string) 92 | .collect::>() 93 | .join("\n") 94 | ); 95 | 96 | if cfg!(feature = "json_example") { 97 | #[cfg(feature = "json_example")] 98 | { 99 | let serialized = serde_json::to_string_pretty(&statements).unwrap(); 100 | println!("Serialized as JSON:\n{serialized}"); 101 | } 102 | } else { 103 | println!("Parse results:\n{statements:#?}"); 104 | } 105 | 106 | std::process::exit(0); 107 | } 108 | Err(e) => { 109 | println!("Error during parsing: {e:?}"); 110 | std::process::exit(1); 111 | } 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /examples/parse_select.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | #![warn(clippy::all)] 19 | 20 | use sqlparser::dialect::GenericDialect; 21 | use sqlparser::parser::*; 22 | 23 | fn main() { 24 | let sql = "SELECT a, b, 123, myfunc(b) \ 25 | FROM table_1 \ 26 | WHERE a > b AND b < 100 \ 27 | ORDER BY a DESC, b"; 28 | 29 | let dialect = GenericDialect {}; 30 | 31 | let ast = Parser::parse_sql(&dialect, sql).unwrap(); 32 | 33 | println!("AST: {ast:?}"); 34 | } 35 | -------------------------------------------------------------------------------- /fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | corpus 2 | hfuzz_target 3 | hfuzz_workspace 4 | -------------------------------------------------------------------------------- /fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | [package] 19 | name = "fuzz" 20 | version = "0.1.0" 21 | edition = "2018" 22 | publish = false 23 | 24 | [dependencies] 25 | honggfuzz = "0.5.54" 26 | sqlparser = { path = ".." } 27 | 28 | # Prevent this from interfering with workspaces 29 | [workspace] 30 | members = ["."] 31 | 32 | [[bin]] 33 | name = "fuzz_parse_sql" 34 | path = "fuzz_targets/fuzz_parse_sql.rs" 35 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/fuzz_parse_sql.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use honggfuzz::fuzz; 19 | use sqlparser::dialect::GenericDialect; 20 | use sqlparser::parser::Parser; 21 | 22 | fn main() { 23 | loop { 24 | fuzz!(|data: String| { 25 | let dialect = GenericDialect {}; 26 | let _ = Parser::parse_sql(&dialect, &data); 27 | }); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # We use rustfmt's default settings to format the source code -------------------------------------------------------------------------------- /sqlparser_bench/Cargo.toml: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | [package] 19 | name = "sqlparser_bench" 20 | description = "Benchmarks for sqlparser" 21 | version = "0.1.0" 22 | authors = ["Dandandan "] 23 | edition = "2018" 24 | 25 | [dependencies] 26 | sqlparser = { path = "../" } 27 | 28 | [dev-dependencies] 29 | criterion = "0.6" 30 | 31 | [[bench]] 32 | name = "sqlparser_bench" 33 | harness = false 34 | -------------------------------------------------------------------------------- /sqlparser_bench/README.md: -------------------------------------------------------------------------------- 1 | 19 | 20 | Benchmarks for sqlparser. See [the main README](../README.md) for more information. 21 | 22 | Note: this is in a separate, non workspace crate to avoid adding a dependency 23 | on `criterion` to the main crate (which complicates testing without std). 24 | 25 | # Running Benchmarks 26 | 27 | ```shell 28 | cargo bench --bench sqlparser_bench 29 | ``` 30 | 31 | # Profiling 32 | 33 | Note you can generate a [flamegraph] using the following command: 34 | 35 | ```shell 36 | cargo flamegraph --bench sqlparser_bench 37 | ``` 38 | 39 | [flamegraph]: https://crates.io/crates/flamegraph 40 | 41 | Here is an example flamegraph: 42 | ![flamegraph](img/flamegraph.svg) 43 | -------------------------------------------------------------------------------- /sqlparser_bench/benches/sqlparser_bench.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use criterion::{criterion_group, criterion_main, Criterion}; 19 | use sqlparser::dialect::GenericDialect; 20 | use sqlparser::parser::Parser; 21 | 22 | fn basic_queries(c: &mut Criterion) { 23 | let mut group = c.benchmark_group("sqlparser-rs parsing benchmark"); 24 | let dialect = GenericDialect {}; 25 | 26 | let string = "SELECT * FROM my_table WHERE 1 = 1"; 27 | group.bench_function("sqlparser::select", |b| { 28 | b.iter(|| Parser::parse_sql(&dialect, string).unwrap()); 29 | }); 30 | 31 | let with_query = " 32 | WITH derived AS ( 33 | SELECT MAX(a) AS max_a, 34 | COUNT(b) AS b_num, 35 | user_id 36 | FROM MY_TABLE 37 | GROUP BY user_id 38 | ) 39 | SELECT * FROM my_table 40 | LEFT JOIN derived USING (user_id) 41 | "; 42 | group.bench_function("sqlparser::with_select", |b| { 43 | b.iter(|| Parser::parse_sql(&dialect, with_query).unwrap()); 44 | }); 45 | 46 | let large_statement = { 47 | let expressions = (0..1000) 48 | .map(|n| format!("FN_{}(COL_{})", n, n)) 49 | .collect::>() 50 | .join(", "); 51 | let tables = (0..1000) 52 | .map(|n| format!("TABLE_{}", n)) 53 | .collect::>() 54 | .join(" JOIN "); 55 | let where_condition = (0..1000) 56 | .map(|n| format!("COL_{} = {}", n, n)) 57 | .collect::>() 58 | .join(" OR "); 59 | let order_condition = (0..1000) 60 | .map(|n| format!("COL_{} DESC", n)) 61 | .collect::>() 62 | .join(", "); 63 | 64 | format!( 65 | "SELECT {} FROM {} WHERE {} ORDER BY {}", 66 | expressions, tables, where_condition, order_condition 67 | ) 68 | }; 69 | 70 | group.bench_function("parse_large_statement", |b| { 71 | b.iter(|| Parser::parse_sql(&dialect, std::hint::black_box(large_statement.as_str()))); 72 | }); 73 | 74 | let large_statement = Parser::parse_sql(&dialect, large_statement.as_str()) 75 | .unwrap() 76 | .pop() 77 | .unwrap(); 78 | 79 | group.bench_function("format_large_statement", |b| { 80 | b.iter(|| { 81 | let _formatted_query = large_statement.to_string(); 82 | }); 83 | }); 84 | } 85 | 86 | criterion_group!(benches, basic_queries); 87 | criterion_main!(benches); 88 | -------------------------------------------------------------------------------- /src/ast/helpers/attached_token.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use core::cmp::{Eq, Ord, Ordering, PartialEq, PartialOrd}; 19 | use core::fmt::{self, Debug, Formatter}; 20 | use core::hash::{Hash, Hasher}; 21 | 22 | use crate::tokenizer::TokenWithSpan; 23 | 24 | #[cfg(feature = "serde")] 25 | use serde::{Deserialize, Serialize}; 26 | 27 | #[cfg(feature = "visitor")] 28 | use sqlparser_derive::{Visit, VisitMut}; 29 | 30 | /// A wrapper over [`TokenWithSpan`]s that ignores the token and source 31 | /// location in comparisons and hashing. 32 | /// 33 | /// This type is used when the token and location is not relevant for semantics, 34 | /// but is still needed for accurate source location tracking, for example, in 35 | /// the nodes in the [ast](crate::ast) module. 36 | /// 37 | /// Note: **All** `AttachedTokens` are equal. 38 | /// 39 | /// # Examples 40 | /// 41 | /// Same token, different location are equal 42 | /// ``` 43 | /// # use sqlparser::ast::helpers::attached_token::AttachedToken; 44 | /// # use sqlparser::tokenizer::{Location, Span, Token, TokenWithLocation}; 45 | /// // commas @ line 1, column 10 46 | /// let tok1 = TokenWithLocation::new( 47 | /// Token::Comma, 48 | /// Span::new(Location::new(1, 10), Location::new(1, 11)), 49 | /// ); 50 | /// // commas @ line 2, column 20 51 | /// let tok2 = TokenWithLocation::new( 52 | /// Token::Comma, 53 | /// Span::new(Location::new(2, 20), Location::new(2, 21)), 54 | /// ); 55 | /// 56 | /// assert_ne!(tok1, tok2); // token with locations are *not* equal 57 | /// assert_eq!(AttachedToken(tok1), AttachedToken(tok2)); // attached tokens are 58 | /// ``` 59 | /// 60 | /// Different token, different location are equal 🤯 61 | /// 62 | /// ``` 63 | /// # use sqlparser::ast::helpers::attached_token::AttachedToken; 64 | /// # use sqlparser::tokenizer::{Location, Span, Token, TokenWithLocation}; 65 | /// // commas @ line 1, column 10 66 | /// let tok1 = TokenWithLocation::new( 67 | /// Token::Comma, 68 | /// Span::new(Location::new(1, 10), Location::new(1, 11)), 69 | /// ); 70 | /// // period @ line 2, column 20 71 | /// let tok2 = TokenWithLocation::new( 72 | /// Token::Period, 73 | /// Span::new(Location::new(2, 10), Location::new(2, 21)), 74 | /// ); 75 | /// 76 | /// assert_ne!(tok1, tok2); // token with locations are *not* equal 77 | /// assert_eq!(AttachedToken(tok1), AttachedToken(tok2)); // attached tokens are 78 | /// ``` 79 | /// // period @ line 2, column 20 80 | #[derive(Clone)] 81 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 82 | #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] 83 | pub struct AttachedToken(pub TokenWithSpan); 84 | 85 | impl AttachedToken { 86 | /// Return a new Empty AttachedToken 87 | pub fn empty() -> Self { 88 | AttachedToken(TokenWithSpan::new_eof()) 89 | } 90 | } 91 | 92 | // Conditional Implementations 93 | impl Debug for AttachedToken { 94 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 95 | self.0.fmt(f) 96 | } 97 | } 98 | 99 | // Blanket Implementations 100 | impl PartialEq for AttachedToken { 101 | fn eq(&self, _: &Self) -> bool { 102 | true 103 | } 104 | } 105 | 106 | impl Eq for AttachedToken {} 107 | 108 | impl PartialOrd for AttachedToken { 109 | fn partial_cmp(&self, other: &Self) -> Option { 110 | Some(self.cmp(other)) 111 | } 112 | } 113 | 114 | impl Ord for AttachedToken { 115 | fn cmp(&self, _: &Self) -> Ordering { 116 | Ordering::Equal 117 | } 118 | } 119 | 120 | impl Hash for AttachedToken { 121 | fn hash(&self, _state: &mut H) { 122 | // Do nothing 123 | } 124 | } 125 | 126 | impl From for AttachedToken { 127 | fn from(value: TokenWithSpan) -> Self { 128 | AttachedToken(value) 129 | } 130 | } 131 | 132 | impl From for TokenWithSpan { 133 | fn from(value: AttachedToken) -> Self { 134 | value.0 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/ast/helpers/key_value_options.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | //! Key-value options for SQL statements. 19 | //! See [this page](https://docs.snowflake.com/en/sql-reference/commands-data-loading) for more details. 20 | 21 | #[cfg(not(feature = "std"))] 22 | use alloc::string::String; 23 | #[cfg(not(feature = "std"))] 24 | use alloc::vec::Vec; 25 | use core::fmt; 26 | use core::fmt::Formatter; 27 | 28 | #[cfg(feature = "serde")] 29 | use serde::{Deserialize, Serialize}; 30 | 31 | #[cfg(feature = "visitor")] 32 | use sqlparser_derive::{Visit, VisitMut}; 33 | 34 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 35 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 36 | #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] 37 | pub struct KeyValueOptions { 38 | pub options: Vec, 39 | } 40 | 41 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 42 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 43 | #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] 44 | pub enum KeyValueOptionType { 45 | STRING, 46 | BOOLEAN, 47 | ENUM, 48 | NUMBER, 49 | } 50 | 51 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 52 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 53 | #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] 54 | pub struct KeyValueOption { 55 | pub option_name: String, 56 | pub option_type: KeyValueOptionType, 57 | pub value: String, 58 | } 59 | 60 | impl fmt::Display for KeyValueOptions { 61 | fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { 62 | if !self.options.is_empty() { 63 | let mut first = false; 64 | for option in &self.options { 65 | if !first { 66 | first = true; 67 | } else { 68 | f.write_str(" ")?; 69 | } 70 | write!(f, "{}", option)?; 71 | } 72 | } 73 | Ok(()) 74 | } 75 | } 76 | 77 | impl fmt::Display for KeyValueOption { 78 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 79 | match self.option_type { 80 | KeyValueOptionType::STRING => { 81 | write!(f, "{}='{}'", self.option_name, self.value)?; 82 | } 83 | KeyValueOptionType::ENUM | KeyValueOptionType::BOOLEAN | KeyValueOptionType::NUMBER => { 84 | write!(f, "{}={}", self.option_name, self.value)?; 85 | } 86 | } 87 | Ok(()) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/ast/helpers/mod.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | pub mod attached_token; 18 | pub mod key_value_options; 19 | pub mod stmt_create_table; 20 | pub mod stmt_data_loading; 21 | -------------------------------------------------------------------------------- /src/ast/helpers/stmt_data_loading.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | //! AST types specific to loading and unloading syntax, like one available in Snowflake which 19 | //! contains: STAGE ddl operations, PUT upload or COPY INTO 20 | //! See [this page](https://docs.snowflake.com/en/sql-reference/commands-data-loading) for more details. 21 | 22 | #[cfg(not(feature = "std"))] 23 | use alloc::string::String; 24 | use core::fmt; 25 | 26 | #[cfg(feature = "serde")] 27 | use serde::{Deserialize, Serialize}; 28 | 29 | use crate::ast::helpers::key_value_options::KeyValueOptions; 30 | use crate::ast::{Ident, ObjectName, SelectItem}; 31 | #[cfg(feature = "visitor")] 32 | use sqlparser_derive::{Visit, VisitMut}; 33 | 34 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 35 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 36 | #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] 37 | pub struct StageParamsObject { 38 | pub url: Option, 39 | pub encryption: KeyValueOptions, 40 | pub endpoint: Option, 41 | pub storage_integration: Option, 42 | pub credentials: KeyValueOptions, 43 | } 44 | 45 | /// This enum enables support for both standard SQL select item expressions 46 | /// and Snowflake-specific ones for data loading. 47 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 48 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 49 | #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] 50 | pub enum StageLoadSelectItemKind { 51 | SelectItem(SelectItem), 52 | StageLoadSelectItem(StageLoadSelectItem), 53 | } 54 | 55 | impl fmt::Display for StageLoadSelectItemKind { 56 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 57 | match &self { 58 | StageLoadSelectItemKind::SelectItem(item) => write!(f, "{item}"), 59 | StageLoadSelectItemKind::StageLoadSelectItem(item) => write!(f, "{item}"), 60 | } 61 | } 62 | } 63 | 64 | #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 65 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 66 | #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] 67 | pub struct StageLoadSelectItem { 68 | pub alias: Option, 69 | pub file_col_num: i32, 70 | pub element: Option, 71 | pub item_as: Option, 72 | } 73 | 74 | impl fmt::Display for StageParamsObject { 75 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 76 | let url = &self.url.as_ref(); 77 | let storage_integration = &self.storage_integration.as_ref(); 78 | let endpoint = &self.endpoint.as_ref(); 79 | 80 | if url.is_some() { 81 | write!(f, " URL='{}'", url.unwrap())?; 82 | } 83 | if storage_integration.is_some() { 84 | write!(f, " STORAGE_INTEGRATION={}", storage_integration.unwrap())?; 85 | } 86 | if endpoint.is_some() { 87 | write!(f, " ENDPOINT='{}'", endpoint.unwrap())?; 88 | } 89 | if !self.credentials.options.is_empty() { 90 | write!(f, " CREDENTIALS=({})", self.credentials)?; 91 | } 92 | if !self.encryption.options.is_empty() { 93 | write!(f, " ENCRYPTION=({})", self.encryption)?; 94 | } 95 | 96 | Ok(()) 97 | } 98 | } 99 | 100 | impl fmt::Display for StageLoadSelectItem { 101 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 102 | if self.alias.is_some() { 103 | write!(f, "{}.", self.alias.as_ref().unwrap())?; 104 | } 105 | write!(f, "${}", self.file_col_num)?; 106 | if self.element.is_some() { 107 | write!(f, ":{}", self.element.as_ref().unwrap())?; 108 | } 109 | if self.item_as.is_some() { 110 | write!(f, " AS {}", self.item_as.as_ref().unwrap())?; 111 | } 112 | Ok(()) 113 | } 114 | } 115 | 116 | #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] 117 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 118 | #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] 119 | pub struct FileStagingCommand { 120 | #[cfg_attr(feature = "visitor", visit(with = "visit_relation"))] 121 | pub stage: ObjectName, 122 | pub pattern: Option, 123 | } 124 | 125 | impl fmt::Display for FileStagingCommand { 126 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 127 | write!(f, "{}", self.stage)?; 128 | if let Some(pattern) = self.pattern.as_ref() { 129 | write!(f, " PATTERN='{pattern}'")?; 130 | } 131 | Ok(()) 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/ast/trigger.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | //! SQL Abstract Syntax Tree (AST) for triggers. 19 | use super::*; 20 | 21 | /// This specifies whether the trigger function should be fired once for every row affected by the trigger event, or just once per SQL statement. 22 | #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)] 23 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 24 | #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] 25 | pub enum TriggerObject { 26 | Row, 27 | Statement, 28 | } 29 | 30 | impl fmt::Display for TriggerObject { 31 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 32 | match self { 33 | TriggerObject::Row => write!(f, "ROW"), 34 | TriggerObject::Statement => write!(f, "STATEMENT"), 35 | } 36 | } 37 | } 38 | 39 | /// This clause indicates whether the following relation name is for the before-image transition relation or the after-image transition relation 40 | #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)] 41 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 42 | #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] 43 | pub enum TriggerReferencingType { 44 | OldTable, 45 | NewTable, 46 | } 47 | 48 | impl fmt::Display for TriggerReferencingType { 49 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 50 | match self { 51 | TriggerReferencingType::OldTable => write!(f, "OLD TABLE"), 52 | TriggerReferencingType::NewTable => write!(f, "NEW TABLE"), 53 | } 54 | } 55 | } 56 | 57 | /// This keyword immediately precedes the declaration of one or two relation names that provide access to the transition relations of the triggering statement 58 | #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] 59 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 60 | #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] 61 | pub struct TriggerReferencing { 62 | pub refer_type: TriggerReferencingType, 63 | pub is_as: bool, 64 | pub transition_relation_name: ObjectName, 65 | } 66 | 67 | impl fmt::Display for TriggerReferencing { 68 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 69 | write!( 70 | f, 71 | "{refer_type}{is_as} {relation_name}", 72 | refer_type = self.refer_type, 73 | is_as = if self.is_as { " AS" } else { "" }, 74 | relation_name = self.transition_relation_name 75 | ) 76 | } 77 | } 78 | 79 | /// Used to describe trigger events 80 | #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] 81 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 82 | #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] 83 | pub enum TriggerEvent { 84 | Insert, 85 | Update(Vec), 86 | Delete, 87 | Truncate, 88 | } 89 | 90 | impl fmt::Display for TriggerEvent { 91 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 92 | match self { 93 | TriggerEvent::Insert => write!(f, "INSERT"), 94 | TriggerEvent::Update(columns) => { 95 | write!(f, "UPDATE")?; 96 | if !columns.is_empty() { 97 | write!(f, " OF")?; 98 | write!(f, " {}", display_comma_separated(columns))?; 99 | } 100 | Ok(()) 101 | } 102 | TriggerEvent::Delete => write!(f, "DELETE"), 103 | TriggerEvent::Truncate => write!(f, "TRUNCATE"), 104 | } 105 | } 106 | } 107 | 108 | /// Trigger period 109 | #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)] 110 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 111 | #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] 112 | pub enum TriggerPeriod { 113 | For, 114 | After, 115 | Before, 116 | InsteadOf, 117 | } 118 | 119 | impl fmt::Display for TriggerPeriod { 120 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 121 | match self { 122 | TriggerPeriod::For => write!(f, "FOR"), 123 | TriggerPeriod::After => write!(f, "AFTER"), 124 | TriggerPeriod::Before => write!(f, "BEFORE"), 125 | TriggerPeriod::InsteadOf => write!(f, "INSTEAD OF"), 126 | } 127 | } 128 | } 129 | 130 | /// Types of trigger body execution body. 131 | #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)] 132 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 133 | #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] 134 | pub enum TriggerExecBodyType { 135 | Function, 136 | Procedure, 137 | } 138 | 139 | impl fmt::Display for TriggerExecBodyType { 140 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 141 | match self { 142 | TriggerExecBodyType::Function => write!(f, "FUNCTION"), 143 | TriggerExecBodyType::Procedure => write!(f, "PROCEDURE"), 144 | } 145 | } 146 | } 147 | /// This keyword immediately precedes the declaration of one or two relation names that provide access to the transition relations of the triggering statement 148 | #[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Hash)] 149 | #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] 150 | #[cfg_attr(feature = "visitor", derive(Visit, VisitMut))] 151 | pub struct TriggerExecBody { 152 | pub exec_type: TriggerExecBodyType, 153 | pub func_desc: FunctionDesc, 154 | } 155 | 156 | impl fmt::Display for TriggerExecBody { 157 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 158 | write!( 159 | f, 160 | "{exec_type} {func_desc}", 161 | exec_type = self.exec_type, 162 | func_desc = self.func_desc 163 | ) 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/dialect/ansi.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use crate::dialect::Dialect; 19 | 20 | /// A [`Dialect`] for [ANSI SQL](https://en.wikipedia.org/wiki/SQL:2011). 21 | #[derive(Debug)] 22 | pub struct AnsiDialect {} 23 | 24 | impl Dialect for AnsiDialect { 25 | fn is_identifier_start(&self, ch: char) -> bool { 26 | ch.is_ascii_lowercase() || ch.is_ascii_uppercase() 27 | } 28 | 29 | fn is_identifier_part(&self, ch: char) -> bool { 30 | ch.is_ascii_lowercase() || ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_' 31 | } 32 | 33 | fn require_interval_qualifier(&self) -> bool { 34 | true 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/dialect/bigquery.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use crate::ast::Statement; 19 | use crate::dialect::Dialect; 20 | use crate::keywords::Keyword; 21 | use crate::parser::{Parser, ParserError}; 22 | 23 | /// These keywords are disallowed as column identifiers. Such that 24 | /// `SELECT 5 AS FROM T` is rejected by BigQuery. 25 | const RESERVED_FOR_COLUMN_ALIAS: &[Keyword] = &[ 26 | Keyword::WITH, 27 | Keyword::SELECT, 28 | Keyword::WHERE, 29 | Keyword::GROUP, 30 | Keyword::HAVING, 31 | Keyword::ORDER, 32 | Keyword::LATERAL, 33 | Keyword::LIMIT, 34 | Keyword::FETCH, 35 | Keyword::UNION, 36 | Keyword::EXCEPT, 37 | Keyword::INTERSECT, 38 | Keyword::FROM, 39 | Keyword::INTO, 40 | Keyword::END, 41 | ]; 42 | 43 | /// A [`Dialect`] for [Google Bigquery](https://cloud.google.com/bigquery/) 44 | #[derive(Debug, Default)] 45 | pub struct BigQueryDialect; 46 | 47 | impl Dialect for BigQueryDialect { 48 | fn parse_statement(&self, parser: &mut Parser) -> Option> { 49 | self.maybe_parse_statement(parser) 50 | } 51 | 52 | /// See 53 | fn is_delimited_identifier_start(&self, ch: char) -> bool { 54 | ch == '`' 55 | } 56 | 57 | fn supports_projection_trailing_commas(&self) -> bool { 58 | true 59 | } 60 | 61 | /// See 62 | fn supports_column_definition_trailing_commas(&self) -> bool { 63 | true 64 | } 65 | 66 | fn is_identifier_start(&self, ch: char) -> bool { 67 | ch.is_ascii_lowercase() || ch.is_ascii_uppercase() || ch == '_' 68 | // BigQuery supports `@@foo.bar` variable syntax in its procedural language. 69 | // https://cloud.google.com/bigquery/docs/reference/standard-sql/procedural-language#beginexceptionend 70 | || ch == '@' 71 | } 72 | 73 | fn is_identifier_part(&self, ch: char) -> bool { 74 | ch.is_ascii_lowercase() || ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '_' 75 | } 76 | 77 | /// See [doc](https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#quoted_literals) 78 | fn supports_triple_quoted_string(&self) -> bool { 79 | true 80 | } 81 | 82 | /// See [doc](https://cloud.google.com/bigquery/docs/reference/standard-sql/navigation_functions#first_value) 83 | fn supports_window_function_null_treatment_arg(&self) -> bool { 84 | true 85 | } 86 | 87 | // See https://cloud.google.com/bigquery/docs/reference/standard-sql/lexical#escape_sequences 88 | fn supports_string_literal_backslash_escape(&self) -> bool { 89 | true 90 | } 91 | 92 | /// See [doc](https://cloud.google.com/bigquery/docs/reference/standard-sql/window-function-calls#ref_named_window) 93 | fn supports_window_clause_named_window_reference(&self) -> bool { 94 | true 95 | } 96 | 97 | /// See [doc](https://cloud.google.com/bigquery/docs/reference/standard-sql/procedural-language#set) 98 | fn supports_parenthesized_set_variables(&self) -> bool { 99 | true 100 | } 101 | 102 | // See https://cloud.google.com/bigquery/docs/reference/standard-sql/query-syntax#select_except 103 | fn supports_select_wildcard_except(&self) -> bool { 104 | true 105 | } 106 | 107 | fn require_interval_qualifier(&self) -> bool { 108 | true 109 | } 110 | 111 | // See https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types#constructing_a_struct 112 | fn supports_struct_literal(&self) -> bool { 113 | true 114 | } 115 | 116 | /// See 117 | fn supports_select_expr_star(&self) -> bool { 118 | true 119 | } 120 | 121 | /// See 122 | fn supports_execute_immediate(&self) -> bool { 123 | true 124 | } 125 | 126 | // See 127 | fn supports_timestamp_versioning(&self) -> bool { 128 | true 129 | } 130 | 131 | // See 132 | fn supports_group_by_expr(&self) -> bool { 133 | true 134 | } 135 | 136 | fn is_column_alias(&self, kw: &Keyword, _parser: &mut Parser) -> bool { 137 | !RESERVED_FOR_COLUMN_ALIAS.contains(kw) 138 | } 139 | 140 | fn supports_pipe_operator(&self) -> bool { 141 | true 142 | } 143 | } 144 | 145 | impl BigQueryDialect { 146 | fn maybe_parse_statement(&self, parser: &mut Parser) -> Option> { 147 | if parser.peek_keyword(Keyword::BEGIN) { 148 | return Some(self.parse_begin(parser)); 149 | } 150 | None 151 | } 152 | 153 | /// Parse a `BEGIN` statement. 154 | /// 155 | fn parse_begin(&self, parser: &mut Parser) -> Result { 156 | parser.expect_keyword(Keyword::BEGIN)?; 157 | 158 | let statements = parser.parse_statement_list(&[Keyword::EXCEPTION, Keyword::END])?; 159 | 160 | let has_exception_when_clause = parser.parse_keywords(&[ 161 | Keyword::EXCEPTION, 162 | Keyword::WHEN, 163 | Keyword::ERROR, 164 | Keyword::THEN, 165 | ]); 166 | let exception_statements = if has_exception_when_clause { 167 | if !parser.peek_keyword(Keyword::END) { 168 | Some(parser.parse_statement_list(&[Keyword::END])?) 169 | } else { 170 | Some(Default::default()) 171 | } 172 | } else { 173 | None 174 | }; 175 | 176 | parser.expect_keyword(Keyword::END)?; 177 | 178 | Ok(Statement::StartTransaction { 179 | begin: true, 180 | statements, 181 | exception_statements, 182 | has_end_keyword: true, 183 | transaction: None, 184 | modifier: None, 185 | modes: Default::default(), 186 | }) 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /src/dialect/clickhouse.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use crate::dialect::Dialect; 19 | 20 | // A [`Dialect`] for [ClickHouse](https://clickhouse.com/). 21 | #[derive(Debug)] 22 | pub struct ClickHouseDialect {} 23 | 24 | impl Dialect for ClickHouseDialect { 25 | fn is_identifier_start(&self, ch: char) -> bool { 26 | // See https://clickhouse.com/docs/en/sql-reference/syntax/#syntax-identifiers 27 | ch.is_ascii_lowercase() || ch.is_ascii_uppercase() || ch == '_' 28 | } 29 | 30 | fn is_identifier_part(&self, ch: char) -> bool { 31 | self.is_identifier_start(ch) || ch.is_ascii_digit() 32 | } 33 | 34 | fn supports_string_literal_backslash_escape(&self) -> bool { 35 | true 36 | } 37 | 38 | fn supports_select_wildcard_except(&self) -> bool { 39 | true 40 | } 41 | 42 | fn describe_requires_table_keyword(&self) -> bool { 43 | true 44 | } 45 | 46 | fn require_interval_qualifier(&self) -> bool { 47 | true 48 | } 49 | 50 | fn supports_limit_comma(&self) -> bool { 51 | true 52 | } 53 | 54 | fn supports_insert_table_function(&self) -> bool { 55 | true 56 | } 57 | 58 | fn supports_insert_format(&self) -> bool { 59 | true 60 | } 61 | 62 | fn supports_numeric_literal_underscores(&self) -> bool { 63 | true 64 | } 65 | 66 | // ClickHouse uses this for some FORMAT expressions in `INSERT` context, e.g. when inserting 67 | // with FORMAT JSONEachRow a raw JSON key-value expression is valid and expected. 68 | // 69 | // [ClickHouse formats](https://clickhouse.com/docs/en/interfaces/formats) 70 | fn supports_dictionary_syntax(&self) -> bool { 71 | true 72 | } 73 | 74 | /// See 75 | fn supports_lambda_functions(&self) -> bool { 76 | true 77 | } 78 | 79 | fn supports_from_first_select(&self) -> bool { 80 | true 81 | } 82 | 83 | /// See 84 | fn supports_order_by_all(&self) -> bool { 85 | true 86 | } 87 | 88 | // See 89 | fn supports_group_by_expr(&self) -> bool { 90 | true 91 | } 92 | 93 | /// See 94 | fn supports_group_by_with_modifier(&self) -> bool { 95 | true 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/dialect/databricks.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use crate::dialect::Dialect; 19 | 20 | /// A [`Dialect`] for [Databricks SQL](https://www.databricks.com/) 21 | /// 22 | /// See . 23 | #[derive(Debug, Default)] 24 | pub struct DatabricksDialect; 25 | 26 | impl Dialect for DatabricksDialect { 27 | // see https://docs.databricks.com/en/sql/language-manual/sql-ref-identifiers.html 28 | 29 | fn is_delimited_identifier_start(&self, ch: char) -> bool { 30 | matches!(ch, '`') 31 | } 32 | 33 | fn is_identifier_start(&self, ch: char) -> bool { 34 | matches!(ch, 'a'..='z' | 'A'..='Z' | '_') 35 | } 36 | 37 | fn is_identifier_part(&self, ch: char) -> bool { 38 | matches!(ch, 'a'..='z' | 'A'..='Z' | '0'..='9' | '_') 39 | } 40 | 41 | fn supports_filter_during_aggregation(&self) -> bool { 42 | true 43 | } 44 | 45 | // https://docs.databricks.com/en/sql/language-manual/sql-ref-syntax-qry-select-groupby.html 46 | fn supports_group_by_expr(&self) -> bool { 47 | true 48 | } 49 | 50 | fn supports_lambda_functions(&self) -> bool { 51 | true 52 | } 53 | 54 | // https://docs.databricks.com/en/sql/language-manual/sql-ref-syntax-qry-select.html#syntax 55 | fn supports_select_wildcard_except(&self) -> bool { 56 | true 57 | } 58 | 59 | fn require_interval_qualifier(&self) -> bool { 60 | true 61 | } 62 | 63 | // See https://docs.databricks.com/en/sql/language-manual/functions/struct.html 64 | fn supports_struct_literal(&self) -> bool { 65 | true 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/dialect/duckdb.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use crate::dialect::Dialect; 19 | 20 | /// A [`Dialect`] for [DuckDB](https://duckdb.org/) 21 | #[derive(Debug, Default)] 22 | pub struct DuckDbDialect; 23 | 24 | // In most cases the redshift dialect is identical to [`PostgresSqlDialect`]. 25 | impl Dialect for DuckDbDialect { 26 | fn supports_trailing_commas(&self) -> bool { 27 | true 28 | } 29 | 30 | fn is_identifier_start(&self, ch: char) -> bool { 31 | ch.is_alphabetic() || ch == '_' 32 | } 33 | 34 | fn is_identifier_part(&self, ch: char) -> bool { 35 | ch.is_alphabetic() || ch.is_ascii_digit() || ch == '$' || ch == '_' 36 | } 37 | 38 | fn supports_filter_during_aggregation(&self) -> bool { 39 | true 40 | } 41 | 42 | fn supports_group_by_expr(&self) -> bool { 43 | true 44 | } 45 | 46 | fn supports_named_fn_args_with_eq_operator(&self) -> bool { 47 | true 48 | } 49 | 50 | fn supports_named_fn_args_with_assignment_operator(&self) -> bool { 51 | true 52 | } 53 | 54 | // DuckDB uses this syntax for `STRUCT`s. 55 | // 56 | // https://duckdb.org/docs/sql/data_types/struct.html#creating-structs 57 | fn supports_dictionary_syntax(&self) -> bool { 58 | true 59 | } 60 | 61 | // DuckDB uses this syntax for `MAP`s. 62 | // 63 | // https://duckdb.org/docs/sql/data_types/map.html#creating-maps 64 | fn support_map_literal_syntax(&self) -> bool { 65 | true 66 | } 67 | 68 | /// See 69 | fn supports_lambda_functions(&self) -> bool { 70 | true 71 | } 72 | 73 | // DuckDB is compatible with PostgreSQL syntax for this statement, 74 | // although not all features may be implemented. 75 | fn supports_explain_with_utility_options(&self) -> bool { 76 | true 77 | } 78 | 79 | /// See DuckDB 80 | fn supports_load_extension(&self) -> bool { 81 | true 82 | } 83 | 84 | // See DuckDB 85 | fn supports_array_typedef_with_brackets(&self) -> bool { 86 | true 87 | } 88 | 89 | fn supports_from_first_select(&self) -> bool { 90 | true 91 | } 92 | 93 | /// See DuckDB 94 | fn supports_order_by_all(&self) -> bool { 95 | true 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/dialect/generic.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use crate::dialect::Dialect; 19 | 20 | /// A permissive, general purpose [`Dialect`], which parses a wide variety of SQL 21 | /// statements, from many different dialects. 22 | #[derive(Debug, Default)] 23 | pub struct GenericDialect; 24 | 25 | impl Dialect for GenericDialect { 26 | fn is_delimited_identifier_start(&self, ch: char) -> bool { 27 | ch == '"' || ch == '`' 28 | } 29 | 30 | fn is_identifier_start(&self, ch: char) -> bool { 31 | ch.is_alphabetic() || ch == '_' || ch == '#' || ch == '@' 32 | } 33 | 34 | fn is_identifier_part(&self, ch: char) -> bool { 35 | ch.is_alphabetic() 36 | || ch.is_ascii_digit() 37 | || ch == '@' 38 | || ch == '$' 39 | || ch == '#' 40 | || ch == '_' 41 | } 42 | 43 | fn supports_unicode_string_literal(&self) -> bool { 44 | true 45 | } 46 | 47 | fn supports_group_by_expr(&self) -> bool { 48 | true 49 | } 50 | 51 | fn supports_group_by_with_modifier(&self) -> bool { 52 | true 53 | } 54 | 55 | fn supports_connect_by(&self) -> bool { 56 | true 57 | } 58 | 59 | fn supports_match_recognize(&self) -> bool { 60 | true 61 | } 62 | 63 | fn supports_start_transaction_modifier(&self) -> bool { 64 | true 65 | } 66 | 67 | fn supports_window_function_null_treatment_arg(&self) -> bool { 68 | true 69 | } 70 | 71 | fn supports_dictionary_syntax(&self) -> bool { 72 | true 73 | } 74 | 75 | fn supports_window_clause_named_window_reference(&self) -> bool { 76 | true 77 | } 78 | 79 | fn supports_parenthesized_set_variables(&self) -> bool { 80 | true 81 | } 82 | 83 | fn supports_select_wildcard_except(&self) -> bool { 84 | true 85 | } 86 | 87 | fn support_map_literal_syntax(&self) -> bool { 88 | true 89 | } 90 | 91 | fn allow_extract_custom(&self) -> bool { 92 | true 93 | } 94 | 95 | fn allow_extract_single_quotes(&self) -> bool { 96 | true 97 | } 98 | 99 | fn supports_create_index_with_clause(&self) -> bool { 100 | true 101 | } 102 | 103 | fn supports_explain_with_utility_options(&self) -> bool { 104 | true 105 | } 106 | 107 | fn supports_limit_comma(&self) -> bool { 108 | true 109 | } 110 | 111 | fn supports_asc_desc_in_column_definition(&self) -> bool { 112 | true 113 | } 114 | 115 | fn supports_try_convert(&self) -> bool { 116 | true 117 | } 118 | 119 | fn supports_comment_on(&self) -> bool { 120 | true 121 | } 122 | 123 | fn supports_load_extension(&self) -> bool { 124 | true 125 | } 126 | 127 | fn supports_named_fn_args_with_assignment_operator(&self) -> bool { 128 | true 129 | } 130 | 131 | fn supports_struct_literal(&self) -> bool { 132 | true 133 | } 134 | 135 | fn supports_empty_projections(&self) -> bool { 136 | true 137 | } 138 | 139 | fn supports_nested_comments(&self) -> bool { 140 | true 141 | } 142 | 143 | fn supports_user_host_grantee(&self) -> bool { 144 | true 145 | } 146 | 147 | fn supports_string_escape_constant(&self) -> bool { 148 | true 149 | } 150 | 151 | fn supports_array_typedef_with_brackets(&self) -> bool { 152 | true 153 | } 154 | 155 | fn supports_match_against(&self) -> bool { 156 | true 157 | } 158 | 159 | fn supports_set_names(&self) -> bool { 160 | true 161 | } 162 | 163 | fn supports_comma_separated_set_assignments(&self) -> bool { 164 | true 165 | } 166 | 167 | fn supports_filter_during_aggregation(&self) -> bool { 168 | true 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /src/dialect/hive.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use crate::dialect::Dialect; 19 | 20 | /// A [`Dialect`] for [Hive](https://hive.apache.org/). 21 | #[derive(Debug)] 22 | pub struct HiveDialect {} 23 | 24 | impl Dialect for HiveDialect { 25 | fn is_delimited_identifier_start(&self, ch: char) -> bool { 26 | (ch == '"') || (ch == '`') 27 | } 28 | 29 | fn is_identifier_start(&self, ch: char) -> bool { 30 | ch.is_ascii_lowercase() || ch.is_ascii_uppercase() || ch.is_ascii_digit() || ch == '$' 31 | } 32 | 33 | fn is_identifier_part(&self, ch: char) -> bool { 34 | ch.is_ascii_lowercase() 35 | || ch.is_ascii_uppercase() 36 | || ch.is_ascii_digit() 37 | || ch == '_' 38 | || ch == '$' 39 | || ch == '{' 40 | || ch == '}' 41 | } 42 | 43 | fn supports_filter_during_aggregation(&self) -> bool { 44 | true 45 | } 46 | 47 | fn supports_numeric_prefix(&self) -> bool { 48 | true 49 | } 50 | 51 | fn require_interval_qualifier(&self) -> bool { 52 | true 53 | } 54 | 55 | /// See 56 | fn supports_bang_not_operator(&self) -> bool { 57 | true 58 | } 59 | 60 | /// See 61 | fn supports_load_data(&self) -> bool { 62 | true 63 | } 64 | 65 | /// See 66 | fn supports_table_sample_before_alias(&self) -> bool { 67 | true 68 | } 69 | 70 | /// See 71 | fn supports_group_by_with_modifier(&self) -> bool { 72 | true 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/dialect/mysql.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | #[cfg(not(feature = "std"))] 19 | use alloc::boxed::Box; 20 | 21 | use crate::{ 22 | ast::{BinaryOperator, Expr, LockTable, LockTableType, Statement}, 23 | dialect::Dialect, 24 | keywords::Keyword, 25 | parser::{Parser, ParserError}, 26 | }; 27 | 28 | use super::keywords; 29 | 30 | const RESERVED_FOR_TABLE_ALIAS_MYSQL: &[Keyword] = &[ 31 | Keyword::USE, 32 | Keyword::IGNORE, 33 | Keyword::FORCE, 34 | Keyword::STRAIGHT_JOIN, 35 | ]; 36 | 37 | /// A [`Dialect`] for [MySQL](https://www.mysql.com/) 38 | #[derive(Debug)] 39 | pub struct MySqlDialect {} 40 | 41 | impl Dialect for MySqlDialect { 42 | fn is_identifier_start(&self, ch: char) -> bool { 43 | // See https://dev.mysql.com/doc/refman/8.0/en/identifiers.html. 44 | // Identifiers which begin with a digit are recognized while tokenizing numbers, 45 | // so they can be distinguished from exponent numeric literals. 46 | ch.is_alphabetic() 47 | || ch == '_' 48 | || ch == '$' 49 | || ch == '@' 50 | || ('\u{0080}'..='\u{ffff}').contains(&ch) 51 | } 52 | 53 | fn is_identifier_part(&self, ch: char) -> bool { 54 | self.is_identifier_start(ch) || ch.is_ascii_digit() 55 | } 56 | 57 | fn is_delimited_identifier_start(&self, ch: char) -> bool { 58 | ch == '`' 59 | } 60 | 61 | fn identifier_quote_style(&self, _identifier: &str) -> Option { 62 | Some('`') 63 | } 64 | 65 | // See https://dev.mysql.com/doc/refman/8.0/en/string-literals.html#character-escape-sequences 66 | fn supports_string_literal_backslash_escape(&self) -> bool { 67 | true 68 | } 69 | 70 | fn ignores_wildcard_escapes(&self) -> bool { 71 | true 72 | } 73 | 74 | fn supports_numeric_prefix(&self) -> bool { 75 | true 76 | } 77 | 78 | fn parse_infix( 79 | &self, 80 | parser: &mut crate::parser::Parser, 81 | expr: &crate::ast::Expr, 82 | _precedence: u8, 83 | ) -> Option> { 84 | // Parse DIV as an operator 85 | if parser.parse_keyword(Keyword::DIV) { 86 | Some(Ok(Expr::BinaryOp { 87 | left: Box::new(expr.clone()), 88 | op: BinaryOperator::MyIntegerDivide, 89 | right: Box::new(parser.parse_expr().unwrap()), 90 | })) 91 | } else { 92 | None 93 | } 94 | } 95 | 96 | fn parse_statement(&self, parser: &mut Parser) -> Option> { 97 | if parser.parse_keywords(&[Keyword::LOCK, Keyword::TABLES]) { 98 | Some(parse_lock_tables(parser)) 99 | } else if parser.parse_keywords(&[Keyword::UNLOCK, Keyword::TABLES]) { 100 | Some(parse_unlock_tables(parser)) 101 | } else { 102 | None 103 | } 104 | } 105 | 106 | fn require_interval_qualifier(&self) -> bool { 107 | true 108 | } 109 | 110 | fn supports_limit_comma(&self) -> bool { 111 | true 112 | } 113 | 114 | /// See: 115 | fn supports_create_table_select(&self) -> bool { 116 | true 117 | } 118 | 119 | /// See: 120 | fn supports_insert_set(&self) -> bool { 121 | true 122 | } 123 | 124 | fn supports_user_host_grantee(&self) -> bool { 125 | true 126 | } 127 | 128 | fn is_table_factor_alias(&self, explicit: bool, kw: &Keyword, _parser: &mut Parser) -> bool { 129 | explicit 130 | || (!keywords::RESERVED_FOR_TABLE_ALIAS.contains(kw) 131 | && !RESERVED_FOR_TABLE_ALIAS_MYSQL.contains(kw)) 132 | } 133 | 134 | fn supports_table_hints(&self) -> bool { 135 | true 136 | } 137 | 138 | fn requires_single_line_comment_whitespace(&self) -> bool { 139 | true 140 | } 141 | 142 | fn supports_match_against(&self) -> bool { 143 | true 144 | } 145 | 146 | fn supports_set_names(&self) -> bool { 147 | true 148 | } 149 | 150 | fn supports_comma_separated_set_assignments(&self) -> bool { 151 | true 152 | } 153 | } 154 | 155 | /// `LOCK TABLES` 156 | /// 157 | fn parse_lock_tables(parser: &mut Parser) -> Result { 158 | let tables = parser.parse_comma_separated(parse_lock_table)?; 159 | Ok(Statement::LockTables { tables }) 160 | } 161 | 162 | // tbl_name [[AS] alias] lock_type 163 | fn parse_lock_table(parser: &mut Parser) -> Result { 164 | let table = parser.parse_identifier()?; 165 | let alias = 166 | parser.parse_optional_alias(&[Keyword::READ, Keyword::WRITE, Keyword::LOW_PRIORITY])?; 167 | let lock_type = parse_lock_tables_type(parser)?; 168 | 169 | Ok(LockTable { 170 | table, 171 | alias, 172 | lock_type, 173 | }) 174 | } 175 | 176 | // READ [LOCAL] | [LOW_PRIORITY] WRITE 177 | fn parse_lock_tables_type(parser: &mut Parser) -> Result { 178 | if parser.parse_keyword(Keyword::READ) { 179 | if parser.parse_keyword(Keyword::LOCAL) { 180 | Ok(LockTableType::Read { local: true }) 181 | } else { 182 | Ok(LockTableType::Read { local: false }) 183 | } 184 | } else if parser.parse_keyword(Keyword::WRITE) { 185 | Ok(LockTableType::Write { 186 | low_priority: false, 187 | }) 188 | } else if parser.parse_keywords(&[Keyword::LOW_PRIORITY, Keyword::WRITE]) { 189 | Ok(LockTableType::Write { low_priority: true }) 190 | } else { 191 | parser.expected("an lock type in LOCK TABLES", parser.peek_token()) 192 | } 193 | } 194 | 195 | /// UNLOCK TABLES 196 | /// 197 | fn parse_unlock_tables(_parser: &mut Parser) -> Result { 198 | Ok(Statement::UnlockTables) 199 | } 200 | -------------------------------------------------------------------------------- /src/dialect/redshift.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | use crate::dialect::Dialect; 19 | use core::iter::Peekable; 20 | use core::str::Chars; 21 | 22 | use super::PostgreSqlDialect; 23 | 24 | /// A [`Dialect`] for [RedShift](https://aws.amazon.com/redshift/) 25 | #[derive(Debug)] 26 | pub struct RedshiftSqlDialect {} 27 | 28 | // In most cases the redshift dialect is identical to [`PostgresSqlDialect`]. 29 | // 30 | // Notable differences: 31 | // 1. Redshift treats brackets `[` and `]` differently. For example, `SQL SELECT a[1][2] FROM b` 32 | // in the Postgres dialect, the query will be parsed as an array, while in the Redshift dialect it will 33 | // be a json path 34 | impl Dialect for RedshiftSqlDialect { 35 | /// Determine if a character starts a potential nested quoted identifier. 36 | /// Example: RedShift supports the following quote styles to all mean the same thing: 37 | /// ```sql 38 | /// SELECT 1 AS foo; 39 | /// SELECT 1 AS "foo"; 40 | /// SELECT 1 AS [foo]; 41 | /// SELECT 1 AS ["foo"]; 42 | /// ``` 43 | fn is_nested_delimited_identifier_start(&self, ch: char) -> bool { 44 | ch == '[' 45 | } 46 | 47 | /// Only applicable whenever [`Self::is_nested_delimited_identifier_start`] returns true 48 | /// If the next sequence of tokens potentially represent a nested identifier, then this method 49 | /// returns a tuple containing the outer quote style, and if present, the inner (nested) quote style. 50 | /// 51 | /// Example (Redshift): 52 | /// ```text 53 | /// `["foo"]` => Some(`[`, Some(`"`)) 54 | /// `[foo]` => Some(`[`, None) 55 | /// `[0]` => None 56 | /// `"foo"` => None 57 | /// ``` 58 | fn peek_nested_delimited_identifier_quotes( 59 | &self, 60 | mut chars: Peekable>, 61 | ) -> Option<(char, Option)> { 62 | if chars.peek() != Some(&'[') { 63 | return None; 64 | } 65 | 66 | chars.next(); 67 | 68 | let mut not_white_chars = chars.skip_while(|ch| ch.is_whitespace()).peekable(); 69 | 70 | if let Some(&ch) = not_white_chars.peek() { 71 | if ch == '"' { 72 | return Some(('[', Some('"'))); 73 | } 74 | if self.is_identifier_start(ch) { 75 | return Some(('[', None)); 76 | } 77 | } 78 | 79 | None 80 | } 81 | 82 | fn is_identifier_start(&self, ch: char) -> bool { 83 | // Extends Postgres dialect with sharp 84 | PostgreSqlDialect {}.is_identifier_start(ch) || ch == '#' 85 | } 86 | 87 | fn is_identifier_part(&self, ch: char) -> bool { 88 | // Extends Postgres dialect with sharp 89 | PostgreSqlDialect {}.is_identifier_part(ch) || ch == '#' 90 | } 91 | 92 | /// redshift has `CONVERT(type, value)` instead of `CONVERT(value, type)` 93 | /// 94 | fn convert_type_before_value(&self) -> bool { 95 | true 96 | } 97 | 98 | fn supports_connect_by(&self) -> bool { 99 | true 100 | } 101 | 102 | /// Redshift expects the `TOP` option before the `ALL/DISTINCT` option: 103 | /// 104 | fn supports_top_before_distinct(&self) -> bool { 105 | true 106 | } 107 | 108 | /// Redshift supports PartiQL: 109 | fn supports_partiql(&self) -> bool { 110 | true 111 | } 112 | 113 | fn supports_string_escape_constant(&self) -> bool { 114 | true 115 | } 116 | 117 | fn supports_geometric_types(&self) -> bool { 118 | true 119 | } 120 | 121 | fn supports_array_typedef_with_brackets(&self) -> bool { 122 | true 123 | } 124 | 125 | fn allow_extract_single_quotes(&self) -> bool { 126 | true 127 | } 128 | 129 | fn supports_string_literal_backslash_escape(&self) -> bool { 130 | true 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /src/dialect/sqlite.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | #[cfg(not(feature = "std"))] 19 | use alloc::boxed::Box; 20 | 21 | use crate::ast::BinaryOperator; 22 | use crate::ast::{Expr, Statement}; 23 | use crate::dialect::Dialect; 24 | use crate::keywords::Keyword; 25 | use crate::parser::{Parser, ParserError}; 26 | 27 | /// A [`Dialect`] for [SQLite](https://www.sqlite.org) 28 | /// 29 | /// This dialect allows columns in a 30 | /// [`CREATE TABLE`](https://sqlite.org/lang_createtable.html) statement with no 31 | /// type specified, as in `CREATE TABLE t1 (a)`. In the AST, these columns will 32 | /// have the data type [`Unspecified`](crate::ast::DataType::Unspecified). 33 | #[derive(Debug)] 34 | pub struct SQLiteDialect {} 35 | 36 | impl Dialect for SQLiteDialect { 37 | // see https://www.sqlite.org/lang_keywords.html 38 | // parse `...`, [...] and "..." as identifier 39 | // TODO: support depending on the context tread '...' as identifier too. 40 | fn is_delimited_identifier_start(&self, ch: char) -> bool { 41 | ch == '`' || ch == '"' || ch == '[' 42 | } 43 | 44 | fn identifier_quote_style(&self, _identifier: &str) -> Option { 45 | Some('`') 46 | } 47 | 48 | fn is_identifier_start(&self, ch: char) -> bool { 49 | // See https://www.sqlite.org/draft/tokenreq.html 50 | ch.is_ascii_lowercase() 51 | || ch.is_ascii_uppercase() 52 | || ch == '_' 53 | || ('\u{007f}'..='\u{ffff}').contains(&ch) 54 | } 55 | 56 | fn supports_filter_during_aggregation(&self) -> bool { 57 | true 58 | } 59 | 60 | fn supports_start_transaction_modifier(&self) -> bool { 61 | true 62 | } 63 | 64 | fn is_identifier_part(&self, ch: char) -> bool { 65 | self.is_identifier_start(ch) || ch.is_ascii_digit() 66 | } 67 | 68 | fn parse_statement(&self, parser: &mut Parser) -> Option> { 69 | if parser.parse_keyword(Keyword::REPLACE) { 70 | parser.prev_token(); 71 | Some(parser.parse_insert()) 72 | } else { 73 | None 74 | } 75 | } 76 | 77 | fn parse_infix( 78 | &self, 79 | parser: &mut crate::parser::Parser, 80 | expr: &crate::ast::Expr, 81 | _precedence: u8, 82 | ) -> Option> { 83 | // Parse MATCH and REGEXP as operators 84 | // See 85 | for (keyword, op) in [ 86 | (Keyword::REGEXP, BinaryOperator::Regexp), 87 | (Keyword::MATCH, BinaryOperator::Match), 88 | ] { 89 | if parser.parse_keyword(keyword) { 90 | let left = Box::new(expr.clone()); 91 | let right = Box::new(parser.parse_expr().unwrap()); 92 | return Some(Ok(Expr::BinaryOp { left, op, right })); 93 | } 94 | } 95 | None 96 | } 97 | 98 | fn supports_in_empty_list(&self) -> bool { 99 | true 100 | } 101 | 102 | fn supports_limit_comma(&self) -> bool { 103 | true 104 | } 105 | 106 | fn supports_asc_desc_in_column_definition(&self) -> bool { 107 | true 108 | } 109 | 110 | fn supports_dollar_placeholder(&self) -> bool { 111 | true 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /src/display_utils.rs: -------------------------------------------------------------------------------- 1 | //! Utilities for formatting SQL AST nodes with pretty printing support. 2 | //! 3 | //! The module provides formatters that implement the `Display` trait with support 4 | //! for both regular (`{}`) and pretty (`{:#}`) formatting modes. Pretty printing 5 | //! adds proper indentation and line breaks to make SQL statements more readable. 6 | 7 | use core::fmt::{self, Display, Write}; 8 | 9 | /// A wrapper around a value that adds an indent to the value when displayed with {:#}. 10 | pub(crate) struct Indent(pub T); 11 | 12 | const INDENT: &str = " "; 13 | 14 | impl Display for Indent 15 | where 16 | T: Display, 17 | { 18 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 19 | if f.alternate() { 20 | f.write_str(INDENT)?; 21 | write!(Indent(f), "{:#}", self.0) 22 | } else { 23 | self.0.fmt(f) 24 | } 25 | } 26 | } 27 | 28 | /// Adds an indent to the inner writer 29 | impl Write for Indent 30 | where 31 | T: Write, 32 | { 33 | fn write_str(&mut self, s: &str) -> fmt::Result { 34 | self.0.write_str(s)?; 35 | // Our NewLine and SpaceOrNewline utils always print individual newlines as a single-character string. 36 | if s == "\n" { 37 | self.0.write_str(INDENT)?; 38 | } 39 | Ok(()) 40 | } 41 | } 42 | 43 | /// A value that inserts a newline when displayed with {:#}, but not when displayed with {}. 44 | pub(crate) struct NewLine; 45 | 46 | impl Display for NewLine { 47 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 48 | if f.alternate() { 49 | f.write_char('\n') 50 | } else { 51 | Ok(()) 52 | } 53 | } 54 | } 55 | 56 | /// A value that inserts a space when displayed with {}, but a newline when displayed with {:#}. 57 | pub(crate) struct SpaceOrNewline; 58 | 59 | impl Display for SpaceOrNewline { 60 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 61 | if f.alternate() { 62 | f.write_char('\n') 63 | } else { 64 | f.write_char(' ') 65 | } 66 | } 67 | } 68 | 69 | /// A value that displays a comma-separated list of values. 70 | /// When pretty-printed (using {:#}), it displays each value on a new line. 71 | pub(crate) struct DisplayCommaSeparated<'a, T: fmt::Display>(pub(crate) &'a [T]); 72 | 73 | impl fmt::Display for DisplayCommaSeparated<'_, T> { 74 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 75 | let mut first = true; 76 | for t in self.0 { 77 | if !first { 78 | f.write_char(',')?; 79 | SpaceOrNewline.fmt(f)?; 80 | } 81 | first = false; 82 | t.fmt(f)?; 83 | } 84 | Ok(()) 85 | } 86 | } 87 | 88 | /// Displays a whitespace, followed by a comma-separated list that is indented when pretty-printed. 89 | pub(crate) fn indented_list(f: &mut fmt::Formatter, items: &[T]) -> fmt::Result { 90 | SpaceOrNewline.fmt(f)?; 91 | Indent(DisplayCommaSeparated(items)).fmt(f) 92 | } 93 | 94 | #[cfg(test)] 95 | mod tests { 96 | use super::*; 97 | 98 | #[test] 99 | fn test_indent() { 100 | struct TwoLines; 101 | 102 | impl Display for TwoLines { 103 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 104 | f.write_str("line 1")?; 105 | SpaceOrNewline.fmt(f)?; 106 | f.write_str("line 2") 107 | } 108 | } 109 | 110 | let indent = Indent(TwoLines); 111 | assert_eq!( 112 | indent.to_string(), 113 | TwoLines.to_string(), 114 | "Only the alternate form should be indented" 115 | ); 116 | assert_eq!(format!("{:#}", indent), " line 1\n line 2"); 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | //! # SQL Parser for Rust 19 | //! 20 | //! This crate provides an ANSI:SQL 2011 lexer and parser that can parse SQL 21 | //! into an Abstract Syntax Tree ([`AST`]). See the [sqlparser crates.io page] 22 | //! for more information. 23 | //! 24 | //! For more information: 25 | //! 1. [`Parser::parse_sql`] and [`Parser::new`] for the Parsing API 26 | //! 2. [`ast`] for the AST structure 27 | //! 3. [`Dialect`] for supported SQL dialects 28 | //! 4. [`Spanned`] for source text locations (see "Source Spans" below for details) 29 | //! 30 | //! [`Spanned`]: ast::Spanned 31 | //! 32 | //! # Example parsing SQL text 33 | //! 34 | //! ``` 35 | //! use sqlparser::dialect::GenericDialect; 36 | //! use sqlparser::parser::Parser; 37 | //! 38 | //! let dialect = GenericDialect {}; // or AnsiDialect 39 | //! 40 | //! let sql = "SELECT a, b, 123, myfunc(b) \ 41 | //! FROM table_1 \ 42 | //! WHERE a > b AND b < 100 \ 43 | //! ORDER BY a DESC, b"; 44 | //! 45 | //! let ast = Parser::parse_sql(&dialect, sql).unwrap(); 46 | //! 47 | //! println!("AST: {:?}", ast); 48 | //! ``` 49 | //! 50 | //! # Creating SQL text from AST 51 | //! 52 | //! This crate allows users to recover the original SQL text (with comments 53 | //! removed, normalized whitespace and identifier capitalization), which is 54 | //! useful for tools that analyze and manipulate SQL. 55 | //! 56 | //! ``` 57 | //! # use sqlparser::dialect::GenericDialect; 58 | //! # use sqlparser::parser::Parser; 59 | //! let sql = "SELECT a FROM table_1"; 60 | //! 61 | //! // parse to a Vec 62 | //! let ast = Parser::parse_sql(&GenericDialect, sql).unwrap(); 63 | //! 64 | //! // The original SQL text can be generated from the AST 65 | //! assert_eq!(ast[0].to_string(), sql); 66 | //! ``` 67 | //! 68 | //! # Pretty Printing 69 | //! 70 | //! SQL statements can be pretty-printed with proper indentation and line breaks using the alternate flag (`{:#}`): 71 | //! 72 | //! ``` 73 | //! # use sqlparser::dialect::GenericDialect; 74 | //! # use sqlparser::parser::Parser; 75 | //! let sql = "SELECT a, b FROM table_1"; 76 | //! let ast = Parser::parse_sql(&GenericDialect, sql).unwrap(); 77 | //! 78 | //! // Pretty print with indentation and line breaks 79 | //! let pretty_sql = format!("{:#}", ast[0]); 80 | //! assert_eq!(pretty_sql, r#" 81 | //! SELECT 82 | //! a, 83 | //! b 84 | //! FROM 85 | //! table_1 86 | //! "#.trim()); 87 | //! ``` 88 | //! [sqlparser crates.io page]: https://crates.io/crates/sqlparser 89 | //! [`Parser::parse_sql`]: crate::parser::Parser::parse_sql 90 | //! [`Parser::new`]: crate::parser::Parser::new 91 | //! [`AST`]: crate::ast 92 | //! [`ast`]: crate::ast 93 | //! [`Dialect`]: crate::dialect::Dialect 94 | //! 95 | //! # Source Spans 96 | //! 97 | //! Starting with version `0.53.0` sqlparser introduced source spans to the 98 | //! AST. This feature provides source information for syntax errors, enabling 99 | //! better error messages. See [issue #1548] for more information and the 100 | //! [`Spanned`] trait to access the spans. 101 | //! 102 | //! [issue #1548]: https://github.com/apache/datafusion-sqlparser-rs/issues/1548 103 | //! [`Spanned`]: ast::Spanned 104 | //! 105 | //! ## Migration Guide 106 | //! 107 | //! For the next few releases, we will be incrementally adding source spans to the 108 | //! AST nodes, trying to minimize the impact on existing users. Some breaking 109 | //! changes are inevitable, and the following is a summary of the changes: 110 | //! 111 | //! #### New fields for spans (must be added to any existing pattern matches) 112 | //! 113 | //! The primary change is that new fields will be added to AST nodes to store the source `Span` or `TokenWithLocation`. 114 | //! 115 | //! This will require 116 | //! 1. Adding new fields to existing pattern matches. 117 | //! 2. Filling in the proper span information when constructing AST nodes. 118 | //! 119 | //! For example, since `Ident` now stores a `Span`, to construct an `Ident` you 120 | //! must provide now provide one: 121 | //! 122 | //! Previously: 123 | //! ```text 124 | //! # use sqlparser::ast::Ident; 125 | //! Ident { 126 | //! value: "name".into(), 127 | //! quote_style: None, 128 | //! } 129 | //! ``` 130 | //! Now 131 | //! ```rust 132 | //! # use sqlparser::ast::Ident; 133 | //! # use sqlparser::tokenizer::Span; 134 | //! Ident { 135 | //! value: "name".into(), 136 | //! quote_style: None, 137 | //! span: Span::empty(), 138 | //! }; 139 | //! ``` 140 | //! 141 | //! Similarly, when pattern matching on `Ident`, you must now account for the 142 | //! `span` field. 143 | //! 144 | //! #### Misc. 145 | //! - [`TokenWithLocation`] stores a full `Span`, rather than just a source location. 146 | //! Users relying on `token.location` should use `token.location.start` instead. 147 | //! 148 | //![`TokenWithLocation`]: tokenizer::TokenWithLocation 149 | 150 | #![cfg_attr(not(feature = "std"), no_std)] 151 | #![allow(clippy::upper_case_acronyms)] 152 | // Permit large enum variants to keep a unified, expressive AST. 153 | // Splitting complex nodes (expressions, statements, types) into separate types 154 | // would bloat the API and hide intent. Extra memory is a worthwhile tradeoff. 155 | #![allow(clippy::large_enum_variant)] 156 | 157 | // Allow proc-macros to find this crate 158 | extern crate self as sqlparser; 159 | 160 | #[cfg(not(feature = "std"))] 161 | extern crate alloc; 162 | 163 | #[macro_use] 164 | #[cfg(test)] 165 | extern crate pretty_assertions; 166 | 167 | pub mod ast; 168 | #[macro_use] 169 | pub mod dialect; 170 | mod display_utils; 171 | pub mod keywords; 172 | pub mod parser; 173 | pub mod tokenizer; 174 | 175 | #[doc(hidden)] 176 | // This is required to make utilities accessible by both the crate-internal 177 | // unit-tests and by the integration tests 178 | // External users are not supposed to rely on this module. 179 | pub mod test_utils; 180 | -------------------------------------------------------------------------------- /tests/pretty_print.rs: -------------------------------------------------------------------------------- 1 | use sqlparser::dialect::GenericDialect; 2 | use sqlparser::parser::Parser; 3 | 4 | fn prettify(sql: &str) -> String { 5 | let ast = Parser::parse_sql(&GenericDialect {}, sql).unwrap(); 6 | format!("{:#}", ast[0]) 7 | } 8 | 9 | #[test] 10 | fn test_pretty_print_select() { 11 | assert_eq!( 12 | prettify("SELECT a, b, c FROM my_table WHERE x = 1 AND y = 2"), 13 | r#" 14 | SELECT 15 | a, 16 | b, 17 | c 18 | FROM 19 | my_table 20 | WHERE 21 | x = 1 AND y = 2 22 | "# 23 | .trim() 24 | ); 25 | } 26 | 27 | #[test] 28 | fn test_pretty_print_join() { 29 | assert_eq!( 30 | prettify("SELECT a FROM table1 JOIN table2 ON table1.id = table2.id"), 31 | r#" 32 | SELECT 33 | a 34 | FROM 35 | table1 36 | JOIN table2 ON table1.id = table2.id 37 | "# 38 | .trim() 39 | ); 40 | } 41 | 42 | #[test] 43 | fn test_pretty_print_subquery() { 44 | assert_eq!( 45 | prettify("SELECT * FROM (SELECT a, b FROM my_table) AS subquery"), 46 | r#" 47 | SELECT 48 | * 49 | FROM 50 | ( 51 | SELECT 52 | a, 53 | b 54 | FROM 55 | my_table 56 | ) AS subquery 57 | "# 58 | .trim() 59 | ); 60 | } 61 | 62 | #[test] 63 | fn test_pretty_print_union() { 64 | assert_eq!( 65 | prettify("SELECT a FROM table1 UNION SELECT b FROM table2"), 66 | r#" 67 | SELECT 68 | a 69 | FROM 70 | table1 71 | UNION 72 | SELECT 73 | b 74 | FROM 75 | table2 76 | "# 77 | .trim() 78 | ); 79 | } 80 | 81 | #[test] 82 | fn test_pretty_print_group_by() { 83 | assert_eq!( 84 | prettify("SELECT a, COUNT(*) FROM my_table GROUP BY a HAVING COUNT(*) > 1"), 85 | r#" 86 | SELECT 87 | a, 88 | COUNT(*) 89 | FROM 90 | my_table 91 | GROUP BY 92 | a 93 | HAVING 94 | COUNT(*) > 1 95 | "# 96 | .trim() 97 | ); 98 | } 99 | 100 | #[test] 101 | fn test_pretty_print_cte() { 102 | assert_eq!( 103 | prettify("WITH cte AS (SELECT a, b FROM my_table) SELECT * FROM cte"), 104 | r#" 105 | WITH cte AS ( 106 | SELECT 107 | a, 108 | b 109 | FROM 110 | my_table 111 | ) 112 | SELECT 113 | * 114 | FROM 115 | cte 116 | "# 117 | .trim() 118 | ); 119 | } 120 | 121 | #[test] 122 | fn test_pretty_print_case_when() { 123 | assert_eq!( 124 | prettify("SELECT CASE WHEN x > 0 THEN 'positive' WHEN x < 0 THEN 'negative' ELSE 'zero' END FROM my_table"), 125 | r#" 126 | SELECT 127 | CASE 128 | WHEN x > 0 THEN 129 | 'positive' 130 | WHEN x < 0 THEN 131 | 'negative' 132 | ELSE 133 | 'zero' 134 | END 135 | FROM 136 | my_table 137 | "#.trim() 138 | ); 139 | } 140 | 141 | #[test] 142 | fn test_pretty_print_window_function() { 143 | assert_eq!( 144 | prettify("SELECT id, value, ROW_NUMBER() OVER (PARTITION BY category ORDER BY value DESC) as rank FROM my_table"), 145 | r#" 146 | SELECT 147 | id, 148 | value, 149 | ROW_NUMBER() OVER ( 150 | PARTITION BY category 151 | ORDER BY value DESC 152 | ) AS rank 153 | FROM 154 | my_table 155 | "#.trim() 156 | ); 157 | } 158 | 159 | #[test] 160 | fn test_pretty_print_multiline_string() { 161 | assert_eq!( 162 | prettify("SELECT 'multiline\nstring' AS str"), 163 | r#" 164 | SELECT 165 | 'multiline 166 | string' AS str 167 | "# 168 | .trim(), 169 | "A literal string with a newline should be kept as is. The contents of the string should not be indented." 170 | ); 171 | } 172 | 173 | #[test] 174 | fn test_pretty_print_insert_values() { 175 | assert_eq!( 176 | prettify("INSERT INTO my_table (a, b, c) VALUES (1, 2, 3), (4, 5, 6)"), 177 | r#" 178 | INSERT INTO my_table (a, b, c) 179 | VALUES 180 | (1, 2, 3), 181 | (4, 5, 6) 182 | "# 183 | .trim() 184 | ); 185 | } 186 | 187 | #[test] 188 | fn test_pretty_print_insert_select() { 189 | assert_eq!( 190 | prettify("INSERT INTO my_table (a, b) SELECT x, y FROM source_table RETURNING a AS id"), 191 | r#" 192 | INSERT INTO my_table (a, b) 193 | SELECT 194 | x, 195 | y 196 | FROM 197 | source_table 198 | RETURNING 199 | a AS id 200 | "# 201 | .trim() 202 | ); 203 | } 204 | 205 | #[test] 206 | fn test_pretty_print_update() { 207 | assert_eq!( 208 | prettify("UPDATE my_table SET a = 1, b = 2 WHERE x > 0 RETURNING id, name"), 209 | r#" 210 | UPDATE my_table 211 | SET 212 | a = 1, 213 | b = 2 214 | WHERE 215 | x > 0 216 | RETURNING 217 | id, 218 | name 219 | "# 220 | .trim() 221 | ); 222 | } 223 | 224 | #[test] 225 | fn test_pretty_print_delete() { 226 | assert_eq!( 227 | prettify("DELETE FROM my_table WHERE x > 0 RETURNING id, name"), 228 | r#" 229 | DELETE FROM 230 | my_table 231 | WHERE 232 | x > 0 233 | RETURNING 234 | id, 235 | name 236 | "# 237 | .trim() 238 | ); 239 | 240 | assert_eq!( 241 | prettify("DELETE table1, table2"), 242 | r#" 243 | DELETE 244 | table1, 245 | table2 246 | "# 247 | .trim() 248 | ); 249 | } 250 | 251 | #[test] 252 | fn test_pretty_print_create_table() { 253 | assert_eq!( 254 | prettify("CREATE TABLE my_table (id INT PRIMARY KEY, name VARCHAR(255) NOT NULL, CONSTRAINT fk_other FOREIGN KEY (id) REFERENCES other_table(id))"), 255 | r#" 256 | CREATE TABLE my_table ( 257 | id INT PRIMARY KEY, 258 | name VARCHAR(255) NOT NULL, 259 | CONSTRAINT fk_other FOREIGN KEY (id) REFERENCES other_table(id) 260 | ) 261 | "# 262 | .trim() 263 | ); 264 | } 265 | 266 | #[test] 267 | fn test_pretty_print_create_view() { 268 | assert_eq!( 269 | prettify("CREATE VIEW my_view AS SELECT a, b FROM my_table WHERE x > 0"), 270 | r#" 271 | CREATE VIEW my_view AS 272 | SELECT 273 | a, 274 | b 275 | FROM 276 | my_table 277 | WHERE 278 | x > 0 279 | "# 280 | .trim() 281 | ); 282 | } 283 | 284 | #[test] 285 | #[ignore = "https://github.com/apache/datafusion-sqlparser-rs/issues/1850"] 286 | fn test_pretty_print_create_function() { 287 | assert_eq!( 288 | prettify("CREATE FUNCTION my_func() RETURNS INT BEGIN SELECT COUNT(*) INTO @count FROM my_table; RETURN @count; END"), 289 | r#" 290 | CREATE FUNCTION my_func() RETURNS INT 291 | BEGIN 292 | SELECT COUNT(*) INTO @count FROM my_table; 293 | RETURN @count; 294 | END 295 | "# 296 | .trim() 297 | ); 298 | } 299 | 300 | #[test] 301 | #[ignore = "https://github.com/apache/datafusion-sqlparser-rs/issues/1850"] 302 | fn test_pretty_print_json_table() { 303 | assert_eq!( 304 | prettify("SELECT * FROM JSON_TABLE(@json, '$[*]' COLUMNS (id INT PATH '$.id', name VARCHAR(255) PATH '$.name')) AS jt"), 305 | r#" 306 | SELECT 307 | * 308 | FROM 309 | JSON_TABLE( 310 | @json, 311 | '$[*]' COLUMNS ( 312 | id INT PATH '$.id', 313 | name VARCHAR(255) PATH '$.name' 314 | ) 315 | ) AS jt 316 | "# 317 | .trim() 318 | ); 319 | } 320 | 321 | #[test] 322 | #[ignore = "https://github.com/apache/datafusion-sqlparser-rs/issues/1850"] 323 | fn test_pretty_print_transaction_blocks() { 324 | assert_eq!( 325 | prettify("BEGIN; UPDATE my_table SET x = 1; COMMIT;"), 326 | r#" 327 | BEGIN; 328 | UPDATE my_table SET x = 1; 329 | COMMIT; 330 | "# 331 | .trim() 332 | ); 333 | } 334 | 335 | #[test] 336 | #[ignore = "https://github.com/apache/datafusion-sqlparser-rs/issues/1850"] 337 | fn test_pretty_print_control_flow() { 338 | assert_eq!( 339 | prettify("IF x > 0 THEN SELECT 'positive'; ELSE SELECT 'negative'; END IF;"), 340 | r#" 341 | IF x > 0 THEN 342 | SELECT 'positive'; 343 | ELSE 344 | SELECT 'negative'; 345 | END IF; 346 | "# 347 | .trim() 348 | ); 349 | } 350 | 351 | #[test] 352 | #[ignore = "https://github.com/apache/datafusion-sqlparser-rs/issues/1850"] 353 | fn test_pretty_print_merge() { 354 | assert_eq!( 355 | prettify("MERGE INTO target_table t USING source_table s ON t.id = s.id WHEN MATCHED THEN UPDATE SET t.value = s.value WHEN NOT MATCHED THEN INSERT (id, value) VALUES (s.id, s.value)"), 356 | r#" 357 | MERGE INTO target_table t 358 | USING source_table s ON t.id = s.id 359 | WHEN MATCHED THEN 360 | UPDATE SET t.value = s.value 361 | WHEN NOT MATCHED THEN 362 | INSERT (id, value) VALUES (s.id, s.value) 363 | "# 364 | .trim() 365 | ); 366 | } 367 | 368 | #[test] 369 | #[ignore = "https://github.com/apache/datafusion-sqlparser-rs/issues/1850"] 370 | fn test_pretty_print_create_index() { 371 | assert_eq!( 372 | prettify("CREATE INDEX idx_name ON my_table (column1, column2)"), 373 | r#" 374 | CREATE INDEX idx_name 375 | ON my_table (column1, column2) 376 | "# 377 | .trim() 378 | ); 379 | } 380 | 381 | #[test] 382 | #[ignore = "https://github.com/apache/datafusion-sqlparser-rs/issues/1850"] 383 | fn test_pretty_print_explain() { 384 | assert_eq!( 385 | prettify("EXPLAIN ANALYZE SELECT * FROM my_table WHERE x > 0"), 386 | r#" 387 | EXPLAIN ANALYZE 388 | SELECT 389 | * 390 | FROM 391 | my_table 392 | WHERE 393 | x > 0 394 | "# 395 | .trim() 396 | ); 397 | } 398 | -------------------------------------------------------------------------------- /tests/queries/tpch/1.sql: -------------------------------------------------------------------------------- 1 | -- Licensed to the Apache Software Foundation (ASF) under one 2 | -- or more contributor license agreements. See the NOTICE file 3 | -- distributed with this work for additional information 4 | -- regarding copyright ownership. The ASF licenses this file 5 | -- to you under the Apache License, Version 2.0 (the 6 | -- "License"); you may not use this file except in compliance 7 | -- with the License. You may obtain a copy of the License at 8 | -- 9 | -- http://www.apache.org/licenses/LICENSE-2.0 10 | -- 11 | -- Unless required by applicable law or agreed to in writing, 12 | -- software distributed under the License is distributed on an 13 | -- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | -- KIND, either express or implied. See the License for the 15 | -- specific language governing permissions and limitations 16 | -- under the License. 17 | 18 | 19 | select 20 | l_returnflag, 21 | l_linestatus, 22 | sum(l_quantity) as sum_qty, 23 | sum(l_extendedprice) as sum_base_price, 24 | sum(l_extendedprice * (1 - l_discount)) as sum_disc_price, 25 | sum(l_extendedprice * (1 - l_discount) * (1 + l_tax)) as sum_charge, 26 | avg(l_quantity) as avg_qty, 27 | avg(l_extendedprice) as avg_price, 28 | avg(l_discount) as avg_disc, 29 | count(*) as count_order 30 | from 31 | lineitem 32 | where 33 | l_shipdate <= date '1998-12-01' - interval '90' day (3) 34 | group by 35 | l_returnflag, 36 | l_linestatus 37 | order by 38 | l_returnflag, 39 | l_linestatus; 40 | -------------------------------------------------------------------------------- /tests/queries/tpch/10.sql: -------------------------------------------------------------------------------- 1 | -- Licensed to the Apache Software Foundation (ASF) under one 2 | -- or more contributor license agreements. See the NOTICE file 3 | -- distributed with this work for additional information 4 | -- regarding copyright ownership. The ASF licenses this file 5 | -- to you under the Apache License, Version 2.0 (the 6 | -- "License"); you may not use this file except in compliance 7 | -- with the License. You may obtain a copy of the License at 8 | -- 9 | -- http://www.apache.org/licenses/LICENSE-2.0 10 | -- 11 | -- Unless required by applicable law or agreed to in writing, 12 | -- software distributed under the License is distributed on an 13 | -- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | -- KIND, either express or implied. See the License for the 15 | -- specific language governing permissions and limitations 16 | -- under the License. 17 | 18 | -- using default substitutions 19 | 20 | 21 | select 22 | c_custkey, 23 | c_name, 24 | sum(l_extendedprice * (1 - l_discount)) as revenue, 25 | c_acctbal, 26 | n_name, 27 | c_address, 28 | c_phone, 29 | c_comment 30 | from 31 | customer, 32 | orders, 33 | lineitem, 34 | nation 35 | where 36 | c_custkey = o_custkey 37 | and l_orderkey = o_orderkey 38 | and o_orderdate >= date '1993-10-01' 39 | and o_orderdate < date '1993-10-01' + interval '3' month 40 | and l_returnflag = 'R' 41 | and c_nationkey = n_nationkey 42 | group by 43 | c_custkey, 44 | c_name, 45 | c_acctbal, 46 | c_phone, 47 | n_name, 48 | c_address, 49 | c_comment 50 | order by 51 | revenue desc; 52 | -------------------------------------------------------------------------------- /tests/queries/tpch/11.sql: -------------------------------------------------------------------------------- 1 | -- Licensed to the Apache Software Foundation (ASF) under one 2 | -- or more contributor license agreements. See the NOTICE file 3 | -- distributed with this work for additional information 4 | -- regarding copyright ownership. The ASF licenses this file 5 | -- to you under the Apache License, Version 2.0 (the 6 | -- "License"); you may not use this file except in compliance 7 | -- with the License. You may obtain a copy of the License at 8 | -- 9 | -- http://www.apache.org/licenses/LICENSE-2.0 10 | -- 11 | -- Unless required by applicable law or agreed to in writing, 12 | -- software distributed under the License is distributed on an 13 | -- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | -- KIND, either express or implied. See the License for the 15 | -- specific language governing permissions and limitations 16 | -- under the License. 17 | 18 | -- using default substitutions 19 | 20 | 21 | select 22 | ps_partkey, 23 | sum(ps_supplycost * ps_availqty) as value 24 | from 25 | partsupp, 26 | supplier, 27 | nation 28 | where 29 | ps_suppkey = s_suppkey 30 | and s_nationkey = n_nationkey 31 | and n_name = 'GERMANY' 32 | group by 33 | ps_partkey having 34 | sum(ps_supplycost * ps_availqty) > ( 35 | select 36 | sum(ps_supplycost * ps_availqty) * 0.0001000000 37 | from 38 | partsupp, 39 | supplier, 40 | nation 41 | where 42 | ps_suppkey = s_suppkey 43 | and s_nationkey = n_nationkey 44 | and n_name = 'GERMANY' 45 | ) 46 | order by 47 | value desc; 48 | -------------------------------------------------------------------------------- /tests/queries/tpch/12.sql: -------------------------------------------------------------------------------- 1 | -- Licensed to the Apache Software Foundation (ASF) under one 2 | -- or more contributor license agreements. See the NOTICE file 3 | -- distributed with this work for additional information 4 | -- regarding copyright ownership. The ASF licenses this file 5 | -- to you under the Apache License, Version 2.0 (the 6 | -- "License"); you may not use this file except in compliance 7 | -- with the License. You may obtain a copy of the License at 8 | -- 9 | -- http://www.apache.org/licenses/LICENSE-2.0 10 | -- 11 | -- Unless required by applicable law or agreed to in writing, 12 | -- software distributed under the License is distributed on an 13 | -- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | -- KIND, either express or implied. See the License for the 15 | -- specific language governing permissions and limitations 16 | -- under the License. 17 | 18 | -- using default substitutions 19 | 20 | 21 | select 22 | l_shipmode, 23 | sum(case 24 | when o_orderpriority = '1-URGENT' 25 | or o_orderpriority = '2-HIGH' 26 | then 1 27 | else 0 28 | end) as high_line_count, 29 | sum(case 30 | when o_orderpriority <> '1-URGENT' 31 | and o_orderpriority <> '2-HIGH' 32 | then 1 33 | else 0 34 | end) as low_line_count 35 | from 36 | orders, 37 | lineitem 38 | where 39 | o_orderkey = l_orderkey 40 | and l_shipmode in ('MAIL', 'SHIP') 41 | and l_commitdate < l_receiptdate 42 | and l_shipdate < l_commitdate 43 | and l_receiptdate >= date '1994-01-01' 44 | and l_receiptdate < date '1994-01-01' + interval '1' year 45 | group by 46 | l_shipmode 47 | order by 48 | l_shipmode; 49 | -------------------------------------------------------------------------------- /tests/queries/tpch/13.sql: -------------------------------------------------------------------------------- 1 | -- Licensed to the Apache Software Foundation (ASF) under one 2 | -- or more contributor license agreements. See the NOTICE file 3 | -- distributed with this work for additional information 4 | -- regarding copyright ownership. The ASF licenses this file 5 | -- to you under the Apache License, Version 2.0 (the 6 | -- "License"); you may not use this file except in compliance 7 | -- with the License. You may obtain a copy of the License at 8 | -- 9 | -- http://www.apache.org/licenses/LICENSE-2.0 10 | -- 11 | -- Unless required by applicable law or agreed to in writing, 12 | -- software distributed under the License is distributed on an 13 | -- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | -- KIND, either express or implied. See the License for the 15 | -- specific language governing permissions and limitations 16 | -- under the License. 17 | 18 | -- using default substitutions 19 | 20 | 21 | select 22 | c_count, 23 | count(*) as custdist 24 | from 25 | ( 26 | select 27 | c_custkey, 28 | count(o_orderkey) 29 | from 30 | customer left outer join orders on 31 | c_custkey = o_custkey 32 | and o_comment not like '%special%requests%' 33 | group by 34 | c_custkey 35 | ) as c_orders (c_custkey, c_count) 36 | group by 37 | c_count 38 | order by 39 | custdist desc, 40 | c_count desc; 41 | -------------------------------------------------------------------------------- /tests/queries/tpch/14.sql: -------------------------------------------------------------------------------- 1 | -- Licensed to the Apache Software Foundation (ASF) under one 2 | -- or more contributor license agreements. See the NOTICE file 3 | -- distributed with this work for additional information 4 | -- regarding copyright ownership. The ASF licenses this file 5 | -- to you under the Apache License, Version 2.0 (the 6 | -- "License"); you may not use this file except in compliance 7 | -- with the License. You may obtain a copy of the License at 8 | -- 9 | -- http://www.apache.org/licenses/LICENSE-2.0 10 | -- 11 | -- Unless required by applicable law or agreed to in writing, 12 | -- software distributed under the License is distributed on an 13 | -- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | -- KIND, either express or implied. See the License for the 15 | -- specific language governing permissions and limitations 16 | -- under the License. 17 | 18 | -- using default substitutions 19 | 20 | 21 | select 22 | 100.00 * sum(case 23 | when p_type like 'PROMO%' 24 | then l_extendedprice * (1 - l_discount) 25 | else 0 26 | end) / sum(l_extendedprice * (1 - l_discount)) as promo_revenue 27 | from 28 | lineitem, 29 | part 30 | where 31 | l_partkey = p_partkey 32 | and l_shipdate >= date '1995-09-01' 33 | and l_shipdate < date '1995-09-01' + interval '1' month; 34 | -------------------------------------------------------------------------------- /tests/queries/tpch/15.sql: -------------------------------------------------------------------------------- 1 | -- Licensed to the Apache Software Foundation (ASF) under one 2 | -- or more contributor license agreements. See the NOTICE file 3 | -- distributed with this work for additional information 4 | -- regarding copyright ownership. The ASF licenses this file 5 | -- to you under the Apache License, Version 2.0 (the 6 | -- "License"); you may not use this file except in compliance 7 | -- with the License. You may obtain a copy of the License at 8 | -- 9 | -- http://www.apache.org/licenses/LICENSE-2.0 10 | -- 11 | -- Unless required by applicable law or agreed to in writing, 12 | -- software distributed under the License is distributed on an 13 | -- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | -- KIND, either express or implied. See the License for the 15 | -- specific language governing permissions and limitations 16 | -- under the License. 17 | 18 | -- using default substitutions 19 | 20 | create view revenue0 (supplier_no, total_revenue) as 21 | select 22 | l_suppkey, 23 | sum(l_extendedprice * (1 - l_discount)) 24 | from 25 | lineitem 26 | where 27 | l_shipdate >= date '1996-01-01' 28 | and l_shipdate < date '1996-01-01' + interval '3' month 29 | group by 30 | l_suppkey; 31 | 32 | 33 | select 34 | s_suppkey, 35 | s_name, 36 | s_address, 37 | s_phone, 38 | total_revenue 39 | from 40 | supplier, 41 | revenue0 42 | where 43 | s_suppkey = supplier_no 44 | and total_revenue = ( 45 | select 46 | max(total_revenue) 47 | from 48 | revenue0 49 | ) 50 | order by 51 | s_suppkey; 52 | 53 | drop view revenue0; 54 | -------------------------------------------------------------------------------- /tests/queries/tpch/16.sql: -------------------------------------------------------------------------------- 1 | -- Licensed to the Apache Software Foundation (ASF) under one 2 | -- or more contributor license agreements. See the NOTICE file 3 | -- distributed with this work for additional information 4 | -- regarding copyright ownership. The ASF licenses this file 5 | -- to you under the Apache License, Version 2.0 (the 6 | -- "License"); you may not use this file except in compliance 7 | -- with the License. You may obtain a copy of the License at 8 | -- 9 | -- http://www.apache.org/licenses/LICENSE-2.0 10 | -- 11 | -- Unless required by applicable law or agreed to in writing, 12 | -- software distributed under the License is distributed on an 13 | -- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | -- KIND, either express or implied. See the License for the 15 | -- specific language governing permissions and limitations 16 | -- under the License. 17 | 18 | -- using default substitutions 19 | 20 | 21 | select 22 | p_brand, 23 | p_type, 24 | p_size, 25 | count(distinct ps_suppkey) as supplier_cnt 26 | from 27 | partsupp, 28 | part 29 | where 30 | p_partkey = ps_partkey 31 | and p_brand <> 'Brand#45' 32 | and p_type not like 'MEDIUM POLISHED%' 33 | and p_size in (49, 14, 23, 45, 19, 3, 36, 9) 34 | and ps_suppkey not in ( 35 | select 36 | s_suppkey 37 | from 38 | supplier 39 | where 40 | s_comment like '%Customer%Complaints%' 41 | ) 42 | group by 43 | p_brand, 44 | p_type, 45 | p_size 46 | order by 47 | supplier_cnt desc, 48 | p_brand, 49 | p_type, 50 | p_size; 51 | -------------------------------------------------------------------------------- /tests/queries/tpch/17.sql: -------------------------------------------------------------------------------- 1 | -- Licensed to the Apache Software Foundation (ASF) under one 2 | -- or more contributor license agreements. See the NOTICE file 3 | -- distributed with this work for additional information 4 | -- regarding copyright ownership. The ASF licenses this file 5 | -- to you under the Apache License, Version 2.0 (the 6 | -- "License"); you may not use this file except in compliance 7 | -- with the License. You may obtain a copy of the License at 8 | -- 9 | -- http://www.apache.org/licenses/LICENSE-2.0 10 | -- 11 | -- Unless required by applicable law or agreed to in writing, 12 | -- software distributed under the License is distributed on an 13 | -- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | -- KIND, either express or implied. See the License for the 15 | -- specific language governing permissions and limitations 16 | -- under the License. 17 | 18 | -- using default substitutions 19 | 20 | 21 | select 22 | sum(l_extendedprice) / 7.0 as avg_yearly 23 | from 24 | lineitem, 25 | part 26 | where 27 | p_partkey = l_partkey 28 | and p_brand = 'Brand#23' 29 | and p_container = 'MED BOX' 30 | and l_quantity < ( 31 | select 32 | 0.2 * avg(l_quantity) 33 | from 34 | lineitem 35 | where 36 | l_partkey = p_partkey 37 | ); 38 | -------------------------------------------------------------------------------- /tests/queries/tpch/18.sql: -------------------------------------------------------------------------------- 1 | -- Licensed to the Apache Software Foundation (ASF) under one 2 | -- or more contributor license agreements. See the NOTICE file 3 | -- distributed with this work for additional information 4 | -- regarding copyright ownership. The ASF licenses this file 5 | -- to you under the Apache License, Version 2.0 (the 6 | -- "License"); you may not use this file except in compliance 7 | -- with the License. You may obtain a copy of the License at 8 | -- 9 | -- http://www.apache.org/licenses/LICENSE-2.0 10 | -- 11 | -- Unless required by applicable law or agreed to in writing, 12 | -- software distributed under the License is distributed on an 13 | -- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | -- KIND, either express or implied. See the License for the 15 | -- specific language governing permissions and limitations 16 | -- under the License. 17 | 18 | -- using default substitutions 19 | 20 | 21 | select 22 | c_name, 23 | c_custkey, 24 | o_orderkey, 25 | o_orderdate, 26 | o_totalprice, 27 | sum(l_quantity) 28 | from 29 | customer, 30 | orders, 31 | lineitem 32 | where 33 | o_orderkey in ( 34 | select 35 | l_orderkey 36 | from 37 | lineitem 38 | group by 39 | l_orderkey having 40 | sum(l_quantity) > 300 41 | ) 42 | and c_custkey = o_custkey 43 | and o_orderkey = l_orderkey 44 | group by 45 | c_name, 46 | c_custkey, 47 | o_orderkey, 48 | o_orderdate, 49 | o_totalprice 50 | order by 51 | o_totalprice desc, 52 | o_orderdate; 53 | -------------------------------------------------------------------------------- /tests/queries/tpch/19.sql: -------------------------------------------------------------------------------- 1 | -- Licensed to the Apache Software Foundation (ASF) under one 2 | -- or more contributor license agreements. See the NOTICE file 3 | -- distributed with this work for additional information 4 | -- regarding copyright ownership. The ASF licenses this file 5 | -- to you under the Apache License, Version 2.0 (the 6 | -- "License"); you may not use this file except in compliance 7 | -- with the License. You may obtain a copy of the License at 8 | -- 9 | -- http://www.apache.org/licenses/LICENSE-2.0 10 | -- 11 | -- Unless required by applicable law or agreed to in writing, 12 | -- software distributed under the License is distributed on an 13 | -- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | -- KIND, either express or implied. See the License for the 15 | -- specific language governing permissions and limitations 16 | -- under the License. 17 | 18 | -- using default substitutions 19 | 20 | 21 | select 22 | sum(l_extendedprice* (1 - l_discount)) as revenue 23 | from 24 | lineitem, 25 | part 26 | where 27 | ( 28 | p_partkey = l_partkey 29 | and p_brand = 'Brand#12' 30 | and p_container in ('SM CASE', 'SM BOX', 'SM PACK', 'SM PKG') 31 | and l_quantity >= 1 and l_quantity <= 1 + 10 32 | and p_size between 1 and 5 33 | and l_shipmode in ('AIR', 'AIR REG') 34 | and l_shipinstruct = 'DELIVER IN PERSON' 35 | ) 36 | or 37 | ( 38 | p_partkey = l_partkey 39 | and p_brand = 'Brand#23' 40 | and p_container in ('MED BAG', 'MED BOX', 'MED PKG', 'MED PACK') 41 | and l_quantity >= 10 and l_quantity <= 10 + 10 42 | and p_size between 1 and 10 43 | and l_shipmode in ('AIR', 'AIR REG') 44 | and l_shipinstruct = 'DELIVER IN PERSON' 45 | ) 46 | or 47 | ( 48 | p_partkey = l_partkey 49 | and p_brand = 'Brand#34' 50 | and p_container in ('LG CASE', 'LG BOX', 'LG PACK', 'LG PKG') 51 | and l_quantity >= 20 and l_quantity <= 20 + 10 52 | and p_size between 1 and 15 53 | and l_shipmode in ('AIR', 'AIR REG') 54 | and l_shipinstruct = 'DELIVER IN PERSON' 55 | ); 56 | -------------------------------------------------------------------------------- /tests/queries/tpch/2.sql: -------------------------------------------------------------------------------- 1 | -- Licensed to the Apache Software Foundation (ASF) under one 2 | -- or more contributor license agreements. See the NOTICE file 3 | -- distributed with this work for additional information 4 | -- regarding copyright ownership. The ASF licenses this file 5 | -- to you under the Apache License, Version 2.0 (the 6 | -- "License"); you may not use this file except in compliance 7 | -- with the License. You may obtain a copy of the License at 8 | -- 9 | -- http://www.apache.org/licenses/LICENSE-2.0 10 | -- 11 | -- Unless required by applicable law or agreed to in writing, 12 | -- software distributed under the License is distributed on an 13 | -- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | -- KIND, either express or implied. See the License for the 15 | -- specific language governing permissions and limitations 16 | -- under the License. 17 | 18 | -- using default substitutions 19 | 20 | select 21 | s_acctbal, 22 | s_name, 23 | n_name, 24 | p_partkey, 25 | p_mfgr, 26 | s_address, 27 | s_phone, 28 | s_comment 29 | from 30 | part, 31 | supplier, 32 | partsupp, 33 | nation, 34 | region 35 | where 36 | p_partkey = ps_partkey 37 | and s_suppkey = ps_suppkey 38 | and p_size = 15 39 | and p_type like '%BRASS' 40 | and s_nationkey = n_nationkey 41 | and n_regionkey = r_regionkey 42 | and r_name = 'EUROPE' 43 | and ps_supplycost = ( 44 | select 45 | min(ps_supplycost) 46 | from 47 | partsupp, 48 | supplier, 49 | nation, 50 | region 51 | where 52 | p_partkey = ps_partkey 53 | and s_suppkey = ps_suppkey 54 | and s_nationkey = n_nationkey 55 | and n_regionkey = r_regionkey 56 | and r_name = 'EUROPE' 57 | ) 58 | order by 59 | s_acctbal desc, 60 | n_name, 61 | s_name, 62 | p_partkey; 63 | -------------------------------------------------------------------------------- /tests/queries/tpch/20.sql: -------------------------------------------------------------------------------- 1 | -- Licensed to the Apache Software Foundation (ASF) under one 2 | -- or more contributor license agreements. See the NOTICE file 3 | -- distributed with this work for additional information 4 | -- regarding copyright ownership. The ASF licenses this file 5 | -- to you under the Apache License, Version 2.0 (the 6 | -- "License"); you may not use this file except in compliance 7 | -- with the License. You may obtain a copy of the License at 8 | -- 9 | -- http://www.apache.org/licenses/LICENSE-2.0 10 | -- 11 | -- Unless required by applicable law or agreed to in writing, 12 | -- software distributed under the License is distributed on an 13 | -- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | -- KIND, either express or implied. See the License for the 15 | -- specific language governing permissions and limitations 16 | -- under the License. 17 | 18 | -- using default substitutions 19 | 20 | 21 | select 22 | s_name, 23 | s_address 24 | from 25 | supplier, 26 | nation 27 | where 28 | s_suppkey in ( 29 | select 30 | ps_suppkey 31 | from 32 | partsupp 33 | where 34 | ps_partkey in ( 35 | select 36 | p_partkey 37 | from 38 | part 39 | where 40 | p_name like 'forest%' 41 | ) 42 | and ps_availqty > ( 43 | select 44 | 0.5 * sum(l_quantity) 45 | from 46 | lineitem 47 | where 48 | l_partkey = ps_partkey 49 | and l_suppkey = ps_suppkey 50 | and l_shipdate >= date '1994-01-01' 51 | and l_shipdate < date '1994-01-01' + interval '1' year 52 | ) 53 | ) 54 | and s_nationkey = n_nationkey 55 | and n_name = 'CANADA' 56 | order by 57 | s_name; 58 | -------------------------------------------------------------------------------- /tests/queries/tpch/21.sql: -------------------------------------------------------------------------------- 1 | -- Licensed to the Apache Software Foundation (ASF) under one 2 | -- or more contributor license agreements. See the NOTICE file 3 | -- distributed with this work for additional information 4 | -- regarding copyright ownership. The ASF licenses this file 5 | -- to you under the Apache License, Version 2.0 (the 6 | -- "License"); you may not use this file except in compliance 7 | -- with the License. You may obtain a copy of the License at 8 | -- 9 | -- http://www.apache.org/licenses/LICENSE-2.0 10 | -- 11 | -- Unless required by applicable law or agreed to in writing, 12 | -- software distributed under the License is distributed on an 13 | -- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | -- KIND, either express or implied. See the License for the 15 | -- specific language governing permissions and limitations 16 | -- under the License. 17 | 18 | -- using default substitutions 19 | 20 | 21 | select 22 | s_name, 23 | count(*) as numwait 24 | from 25 | supplier, 26 | lineitem l1, 27 | orders, 28 | nation 29 | where 30 | s_suppkey = l1.l_suppkey 31 | and o_orderkey = l1.l_orderkey 32 | and o_orderstatus = 'F' 33 | and l1.l_receiptdate > l1.l_commitdate 34 | and exists ( 35 | select 36 | * 37 | from 38 | lineitem l2 39 | where 40 | l2.l_orderkey = l1.l_orderkey 41 | and l2.l_suppkey <> l1.l_suppkey 42 | ) 43 | and not exists ( 44 | select 45 | * 46 | from 47 | lineitem l3 48 | where 49 | l3.l_orderkey = l1.l_orderkey 50 | and l3.l_suppkey <> l1.l_suppkey 51 | and l3.l_receiptdate > l3.l_commitdate 52 | ) 53 | and s_nationkey = n_nationkey 54 | and n_name = 'SAUDI ARABIA' 55 | group by 56 | s_name 57 | order by 58 | numwait desc, 59 | s_name; 60 | -------------------------------------------------------------------------------- /tests/queries/tpch/22.sql: -------------------------------------------------------------------------------- 1 | -- Licensed to the Apache Software Foundation (ASF) under one 2 | -- or more contributor license agreements. See the NOTICE file 3 | -- distributed with this work for additional information 4 | -- regarding copyright ownership. The ASF licenses this file 5 | -- to you under the Apache License, Version 2.0 (the 6 | -- "License"); you may not use this file except in compliance 7 | -- with the License. You may obtain a copy of the License at 8 | -- 9 | -- http://www.apache.org/licenses/LICENSE-2.0 10 | -- 11 | -- Unless required by applicable law or agreed to in writing, 12 | -- software distributed under the License is distributed on an 13 | -- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | -- KIND, either express or implied. See the License for the 15 | -- specific language governing permissions and limitations 16 | -- under the License. 17 | 18 | -- using default substitutions 19 | 20 | 21 | select 22 | cntrycode, 23 | count(*) as numcust, 24 | sum(c_acctbal) as totacctbal 25 | from 26 | ( 27 | select 28 | substring(c_phone from 1 for 2) as cntrycode, 29 | c_acctbal 30 | from 31 | customer 32 | where 33 | substring(c_phone from 1 for 2) in 34 | ('13', '31', '23', '29', '30', '18', '17') 35 | and c_acctbal > ( 36 | select 37 | avg(c_acctbal) 38 | from 39 | customer 40 | where 41 | c_acctbal > 0.00 42 | and substring(c_phone from 1 for 2) in 43 | ('13', '31', '23', '29', '30', '18', '17') 44 | ) 45 | and not exists ( 46 | select 47 | * 48 | from 49 | orders 50 | where 51 | o_custkey = c_custkey 52 | ) 53 | ) as custsale 54 | group by 55 | cntrycode 56 | order by 57 | cntrycode; 58 | -------------------------------------------------------------------------------- /tests/queries/tpch/3.sql: -------------------------------------------------------------------------------- 1 | -- Licensed to the Apache Software Foundation (ASF) under one 2 | -- or more contributor license agreements. See the NOTICE file 3 | -- distributed with this work for additional information 4 | -- regarding copyright ownership. The ASF licenses this file 5 | -- to you under the Apache License, Version 2.0 (the 6 | -- "License"); you may not use this file except in compliance 7 | -- with the License. You may obtain a copy of the License at 8 | -- 9 | -- http://www.apache.org/licenses/LICENSE-2.0 10 | -- 11 | -- Unless required by applicable law or agreed to in writing, 12 | -- software distributed under the License is distributed on an 13 | -- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | -- KIND, either express or implied. See the License for the 15 | -- specific language governing permissions and limitations 16 | -- under the License. 17 | 18 | -- using default substitutions 19 | 20 | 21 | select 22 | l_orderkey, 23 | sum(l_extendedprice * (1 - l_discount)) as revenue, 24 | o_orderdate, 25 | o_shippriority 26 | from 27 | customer, 28 | orders, 29 | lineitem 30 | where 31 | c_mktsegment = 'BUILDING' 32 | and c_custkey = o_custkey 33 | and l_orderkey = o_orderkey 34 | and o_orderdate < date '1995-03-15' 35 | and l_shipdate > date '1995-03-15' 36 | group by 37 | l_orderkey, 38 | o_orderdate, 39 | o_shippriority 40 | order by 41 | revenue desc, 42 | o_orderdate; 43 | -------------------------------------------------------------------------------- /tests/queries/tpch/4.sql: -------------------------------------------------------------------------------- 1 | -- Licensed to the Apache Software Foundation (ASF) under one 2 | -- or more contributor license agreements. See the NOTICE file 3 | -- distributed with this work for additional information 4 | -- regarding copyright ownership. The ASF licenses this file 5 | -- to you under the Apache License, Version 2.0 (the 6 | -- "License"); you may not use this file except in compliance 7 | -- with the License. You may obtain a copy of the License at 8 | -- 9 | -- http://www.apache.org/licenses/LICENSE-2.0 10 | -- 11 | -- Unless required by applicable law or agreed to in writing, 12 | -- software distributed under the License is distributed on an 13 | -- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | -- KIND, either express or implied. See the License for the 15 | -- specific language governing permissions and limitations 16 | -- under the License. 17 | 18 | -- using default substitutions 19 | 20 | 21 | select 22 | o_orderpriority, 23 | count(*) as order_count 24 | from 25 | orders 26 | where 27 | o_orderdate >= date '1993-07-01' 28 | and o_orderdate < date '1993-07-01' + interval '3' month 29 | and exists ( 30 | select 31 | * 32 | from 33 | lineitem 34 | where 35 | l_orderkey = o_orderkey 36 | and l_commitdate < l_receiptdate 37 | ) 38 | group by 39 | o_orderpriority 40 | order by 41 | o_orderpriority; 42 | -------------------------------------------------------------------------------- /tests/queries/tpch/5.sql: -------------------------------------------------------------------------------- 1 | -- Licensed to the Apache Software Foundation (ASF) under one 2 | -- or more contributor license agreements. See the NOTICE file 3 | -- distributed with this work for additional information 4 | -- regarding copyright ownership. The ASF licenses this file 5 | -- to you under the Apache License, Version 2.0 (the 6 | -- "License"); you may not use this file except in compliance 7 | -- with the License. You may obtain a copy of the License at 8 | -- 9 | -- http://www.apache.org/licenses/LICENSE-2.0 10 | -- 11 | -- Unless required by applicable law or agreed to in writing, 12 | -- software distributed under the License is distributed on an 13 | -- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | -- KIND, either express or implied. See the License for the 15 | -- specific language governing permissions and limitations 16 | -- under the License. 17 | 18 | -- using default substitutions 19 | 20 | 21 | select 22 | n_name, 23 | sum(l_extendedprice * (1 - l_discount)) as revenue 24 | from 25 | customer, 26 | orders, 27 | lineitem, 28 | supplier, 29 | nation, 30 | region 31 | where 32 | c_custkey = o_custkey 33 | and l_orderkey = o_orderkey 34 | and l_suppkey = s_suppkey 35 | and c_nationkey = s_nationkey 36 | and s_nationkey = n_nationkey 37 | and n_regionkey = r_regionkey 38 | and r_name = 'ASIA' 39 | and o_orderdate >= date '1994-01-01' 40 | and o_orderdate < date '1994-01-01' + interval '1' year 41 | group by 42 | n_name 43 | order by 44 | revenue desc; 45 | -------------------------------------------------------------------------------- /tests/queries/tpch/6.sql: -------------------------------------------------------------------------------- 1 | -- Licensed to the Apache Software Foundation (ASF) under one 2 | -- or more contributor license agreements. See the NOTICE file 3 | -- distributed with this work for additional information 4 | -- regarding copyright ownership. The ASF licenses this file 5 | -- to you under the Apache License, Version 2.0 (the 6 | -- "License"); you may not use this file except in compliance 7 | -- with the License. You may obtain a copy of the License at 8 | -- 9 | -- http://www.apache.org/licenses/LICENSE-2.0 10 | -- 11 | -- Unless required by applicable law or agreed to in writing, 12 | -- software distributed under the License is distributed on an 13 | -- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | -- KIND, either express or implied. See the License for the 15 | -- specific language governing permissions and limitations 16 | -- under the License. 17 | 18 | 19 | -- using default substitutions 20 | 21 | select 22 | sum(l_extendedprice * l_discount) as revenue 23 | from 24 | lineitem 25 | where 26 | l_shipdate >= date '1994-01-01' 27 | and l_shipdate < date '1994-01-01' + interval '1' year 28 | and l_discount between .06 - 0.01 and .06 + 0.01 29 | and l_quantity < 24; 30 | -------------------------------------------------------------------------------- /tests/queries/tpch/7.sql: -------------------------------------------------------------------------------- 1 | -- Licensed to the Apache Software Foundation (ASF) under one 2 | -- or more contributor license agreements. See the NOTICE file 3 | -- distributed with this work for additional information 4 | -- regarding copyright ownership. The ASF licenses this file 5 | -- to you under the Apache License, Version 2.0 (the 6 | -- "License"); you may not use this file except in compliance 7 | -- with the License. You may obtain a copy of the License at 8 | -- 9 | -- http://www.apache.org/licenses/LICENSE-2.0 10 | -- 11 | -- Unless required by applicable law or agreed to in writing, 12 | -- software distributed under the License is distributed on an 13 | -- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | -- KIND, either express or implied. See the License for the 15 | -- specific language governing permissions and limitations 16 | -- under the License. 17 | 18 | -- using default substitutions 19 | 20 | 21 | select 22 | supp_nation, 23 | cust_nation, 24 | l_year, 25 | sum(volume) as revenue 26 | from 27 | ( 28 | select 29 | n1.n_name as supp_nation, 30 | n2.n_name as cust_nation, 31 | extract(year from l_shipdate) as l_year, 32 | l_extendedprice * (1 - l_discount) as volume 33 | from 34 | supplier, 35 | lineitem, 36 | orders, 37 | customer, 38 | nation n1, 39 | nation n2 40 | where 41 | s_suppkey = l_suppkey 42 | and o_orderkey = l_orderkey 43 | and c_custkey = o_custkey 44 | and s_nationkey = n1.n_nationkey 45 | and c_nationkey = n2.n_nationkey 46 | and ( 47 | (n1.n_name = 'FRANCE' and n2.n_name = 'GERMANY') 48 | or (n1.n_name = 'GERMANY' and n2.n_name = 'FRANCE') 49 | ) 50 | and l_shipdate between date '1995-01-01' and date '1996-12-31' 51 | ) as shipping 52 | group by 53 | supp_nation, 54 | cust_nation, 55 | l_year 56 | order by 57 | supp_nation, 58 | cust_nation, 59 | l_year; 60 | -------------------------------------------------------------------------------- /tests/queries/tpch/8.sql: -------------------------------------------------------------------------------- 1 | -- Licensed to the Apache Software Foundation (ASF) under one 2 | -- or more contributor license agreements. See the NOTICE file 3 | -- distributed with this work for additional information 4 | -- regarding copyright ownership. The ASF licenses this file 5 | -- to you under the Apache License, Version 2.0 (the 6 | -- "License"); you may not use this file except in compliance 7 | -- with the License. You may obtain a copy of the License at 8 | -- 9 | -- http://www.apache.org/licenses/LICENSE-2.0 10 | -- 11 | -- Unless required by applicable law or agreed to in writing, 12 | -- software distributed under the License is distributed on an 13 | -- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | -- KIND, either express or implied. See the License for the 15 | -- specific language governing permissions and limitations 16 | -- under the License. 17 | 18 | -- using default substitutions 19 | 20 | 21 | select 22 | o_year, 23 | sum(case 24 | when nation = 'BRAZIL' then volume 25 | else 0 26 | end) / sum(volume) as mkt_share 27 | from 28 | ( 29 | select 30 | extract(year from o_orderdate) as o_year, 31 | l_extendedprice * (1 - l_discount) as volume, 32 | n2.n_name as nation 33 | from 34 | part, 35 | supplier, 36 | lineitem, 37 | orders, 38 | customer, 39 | nation n1, 40 | nation n2, 41 | region 42 | where 43 | p_partkey = l_partkey 44 | and s_suppkey = l_suppkey 45 | and l_orderkey = o_orderkey 46 | and o_custkey = c_custkey 47 | and c_nationkey = n1.n_nationkey 48 | and n1.n_regionkey = r_regionkey 49 | and r_name = 'AMERICA' 50 | and s_nationkey = n2.n_nationkey 51 | and o_orderdate between date '1995-01-01' and date '1996-12-31' 52 | and p_type = 'ECONOMY ANODIZED STEEL' 53 | ) as all_nations 54 | group by 55 | o_year 56 | order by 57 | o_year; 58 | -------------------------------------------------------------------------------- /tests/queries/tpch/9.sql: -------------------------------------------------------------------------------- 1 | -- Licensed to the Apache Software Foundation (ASF) under one 2 | -- or more contributor license agreements. See the NOTICE file 3 | -- distributed with this work for additional information 4 | -- regarding copyright ownership. The ASF licenses this file 5 | -- to you under the Apache License, Version 2.0 (the 6 | -- "License"); you may not use this file except in compliance 7 | -- with the License. You may obtain a copy of the License at 8 | -- 9 | -- http://www.apache.org/licenses/LICENSE-2.0 10 | -- 11 | -- Unless required by applicable law or agreed to in writing, 12 | -- software distributed under the License is distributed on an 13 | -- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | -- KIND, either express or implied. See the License for the 15 | -- specific language governing permissions and limitations 16 | -- under the License. 17 | 18 | -- using default substitutions 19 | 20 | 21 | select 22 | nation, 23 | o_year, 24 | sum(amount) as sum_profit 25 | from 26 | ( 27 | select 28 | n_name as nation, 29 | extract(year from o_orderdate) as o_year, 30 | l_extendedprice * (1 - l_discount) - ps_supplycost * l_quantity as amount 31 | from 32 | part, 33 | supplier, 34 | lineitem, 35 | partsupp, 36 | orders, 37 | nation 38 | where 39 | s_suppkey = l_suppkey 40 | and ps_suppkey = l_suppkey 41 | and ps_partkey = l_partkey 42 | and p_partkey = l_partkey 43 | and o_orderkey = l_orderkey 44 | and s_nationkey = n_nationkey 45 | and p_name like '%green%' 46 | ) as profit 47 | group by 48 | nation, 49 | o_year 50 | order by 51 | nation, 52 | o_year desc; 53 | -------------------------------------------------------------------------------- /tests/sqlparser_custom_dialect.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | //! Test the ability for dialects to override parsing 19 | 20 | use sqlparser::{ 21 | ast::{BinaryOperator, Expr, Statement, Value}, 22 | dialect::Dialect, 23 | keywords::Keyword, 24 | parser::{Parser, ParserError}, 25 | tokenizer::Token, 26 | }; 27 | 28 | #[test] 29 | fn custom_prefix_parser() -> Result<(), ParserError> { 30 | #[derive(Debug)] 31 | struct MyDialect {} 32 | 33 | impl Dialect for MyDialect { 34 | fn is_identifier_start(&self, ch: char) -> bool { 35 | is_identifier_start(ch) 36 | } 37 | 38 | fn is_identifier_part(&self, ch: char) -> bool { 39 | is_identifier_part(ch) 40 | } 41 | 42 | fn parse_prefix(&self, parser: &mut Parser) -> Option> { 43 | if parser.consume_token(&Token::Number("1".to_string(), false)) { 44 | Some(Ok(Expr::Value(Value::Null.with_empty_span()))) 45 | } else { 46 | None 47 | } 48 | } 49 | } 50 | 51 | let dialect = MyDialect {}; 52 | let sql = "SELECT 1 + 2"; 53 | let ast = Parser::parse_sql(&dialect, sql)?; 54 | let query = &ast[0]; 55 | assert_eq!("SELECT NULL + 2", &format!("{query}")); 56 | Ok(()) 57 | } 58 | 59 | #[test] 60 | fn custom_infix_parser() -> Result<(), ParserError> { 61 | #[derive(Debug)] 62 | struct MyDialect {} 63 | 64 | impl Dialect for MyDialect { 65 | fn is_identifier_start(&self, ch: char) -> bool { 66 | is_identifier_start(ch) 67 | } 68 | 69 | fn is_identifier_part(&self, ch: char) -> bool { 70 | is_identifier_part(ch) 71 | } 72 | 73 | fn parse_infix( 74 | &self, 75 | parser: &mut Parser, 76 | expr: &Expr, 77 | _precedence: u8, 78 | ) -> Option> { 79 | if parser.consume_token(&Token::Plus) { 80 | Some(Ok(Expr::BinaryOp { 81 | left: Box::new(expr.clone()), 82 | op: BinaryOperator::Multiply, // translate Plus to Multiply 83 | right: Box::new(parser.parse_expr().unwrap()), 84 | })) 85 | } else { 86 | None 87 | } 88 | } 89 | } 90 | 91 | let dialect = MyDialect {}; 92 | let sql = "SELECT 1 + 2"; 93 | let ast = Parser::parse_sql(&dialect, sql)?; 94 | let query = &ast[0]; 95 | assert_eq!("SELECT 1 * 2", &format!("{query}")); 96 | Ok(()) 97 | } 98 | 99 | #[test] 100 | fn custom_statement_parser() -> Result<(), ParserError> { 101 | #[derive(Debug)] 102 | struct MyDialect {} 103 | 104 | impl Dialect for MyDialect { 105 | fn is_identifier_start(&self, ch: char) -> bool { 106 | is_identifier_start(ch) 107 | } 108 | 109 | fn is_identifier_part(&self, ch: char) -> bool { 110 | is_identifier_part(ch) 111 | } 112 | 113 | fn parse_statement(&self, parser: &mut Parser) -> Option> { 114 | if parser.parse_keyword(Keyword::SELECT) { 115 | for _ in 0..3 { 116 | let _ = parser.next_token(); 117 | } 118 | Some(Ok(Statement::Commit { 119 | chain: false, 120 | end: false, 121 | modifier: None, 122 | })) 123 | } else { 124 | None 125 | } 126 | } 127 | } 128 | 129 | let dialect = MyDialect {}; 130 | let sql = "SELECT 1 + 2"; 131 | let ast = Parser::parse_sql(&dialect, sql)?; 132 | let query = &ast[0]; 133 | assert_eq!("COMMIT", &format!("{query}")); 134 | Ok(()) 135 | } 136 | 137 | #[test] 138 | fn test_map_syntax_not_support_default() -> Result<(), ParserError> { 139 | #[derive(Debug)] 140 | struct MyDialect {} 141 | 142 | impl Dialect for MyDialect { 143 | fn is_identifier_start(&self, ch: char) -> bool { 144 | is_identifier_start(ch) 145 | } 146 | 147 | fn is_identifier_part(&self, ch: char) -> bool { 148 | is_identifier_part(ch) 149 | } 150 | } 151 | 152 | let dialect = MyDialect {}; 153 | let sql = "SELECT MAP {1: 2}"; 154 | let ast = Parser::parse_sql(&dialect, sql); 155 | assert!(ast.is_err()); 156 | Ok(()) 157 | } 158 | 159 | fn is_identifier_start(ch: char) -> bool { 160 | ch.is_ascii_lowercase() || ch.is_ascii_uppercase() || ch == '_' 161 | } 162 | 163 | fn is_identifier_part(ch: char) -> bool { 164 | ch.is_ascii_lowercase() 165 | || ch.is_ascii_uppercase() 166 | || ch.is_ascii_digit() 167 | || ch == '$' 168 | || ch == '_' 169 | } 170 | -------------------------------------------------------------------------------- /tests/sqlparser_regression.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | #![warn(clippy::all)] 19 | 20 | use sqlparser::dialect::GenericDialect; 21 | use sqlparser::parser::Parser; 22 | 23 | macro_rules! tpch_tests { 24 | ($($name:ident: $value:expr,)*) => { 25 | const QUERIES: &[&str] = &[ 26 | $(include_str!(concat!("queries/tpch/", $value, ".sql"))),* 27 | ]; 28 | $( 29 | 30 | #[test] 31 | fn $name() { 32 | let dialect = GenericDialect {}; 33 | let res = Parser::parse_sql(&dialect, QUERIES[$value -1]); 34 | assert!(res.is_ok()); 35 | } 36 | )* 37 | } 38 | } 39 | 40 | tpch_tests! { 41 | tpch_1: 1, 42 | tpch_2: 2, 43 | tpch_3: 3, 44 | tpch_4: 4, 45 | tpch_5: 5, 46 | tpch_6: 6, 47 | tpch_7: 7, 48 | tpch_8: 8, 49 | tpch_9: 9, 50 | tpch_10: 10, 51 | tpch_11: 11, 52 | tpch_12: 12, 53 | tpch_13: 13, 54 | tpch_14: 14, 55 | tpch_15: 15, 56 | tpch_16: 16, 57 | tpch_17: 17, 58 | tpch_18: 18, 59 | tpch_19: 19, 60 | tpch_20: 20, 61 | tpch_21: 21, 62 | tpch_22: 22, 63 | } 64 | -------------------------------------------------------------------------------- /tests/test_utils/mod.rs: -------------------------------------------------------------------------------- 1 | // Licensed to the Apache Software Foundation (ASF) under one 2 | // or more contributor license agreements. See the NOTICE file 3 | // distributed with this work for additional information 4 | // regarding copyright ownership. The ASF licenses this file 5 | // to you under the Apache License, Version 2.0 (the 6 | // "License"); you may not use this file except in compliance 7 | // with the License. You may obtain a copy of the License at 8 | // 9 | // http://www.apache.org/licenses/LICENSE-2.0 10 | // 11 | // Unless required by applicable law or agreed to in writing, 12 | // software distributed under the License is distributed on an 13 | // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | // KIND, either express or implied. See the License for the 15 | // specific language governing permissions and limitations 16 | // under the License. 17 | 18 | // Re-export everything from `src/test_utils.rs`. 19 | pub use sqlparser::test_utils::*; 20 | 21 | // For the test-only macros we take a different approach of keeping them here 22 | // rather than in the library crate. 23 | // 24 | // This is because we don't need any of them to be shared between the 25 | // integration tests (i.e. `tests/*`) and the unit tests (i.e. `src/*`), 26 | // but also because Rust doesn't scope macros to a particular module 27 | // (and while we export internal helpers as sqlparser::test_utils::<...>, 28 | // expecting our users to abstain from relying on them, exporting internal 29 | // macros at the top level, like `sqlparser::nest` was deemed too confusing). 30 | 31 | #[macro_export] 32 | macro_rules! nest { 33 | ($base:expr $(, $join:expr)*) => { 34 | TableFactor::NestedJoin { table_with_joins: Box::new(TableWithJoins { 35 | relation: $base, 36 | joins: vec![$(join($join)),*] 37 | }), alias: None} 38 | }; 39 | } 40 | --------------------------------------------------------------------------------