diff --git a/collector/benchmarks/README.md b/collector/benchmarks/README.md index 2a4969ef4..c4091d840 100644 --- a/collector/benchmarks/README.md +++ b/collector/benchmarks/README.md @@ -41,6 +41,10 @@ These are real programs that are important in some way, and worth tracking. These are real programs that are known to stress the compiler in interesting ways. +- **diesel**: A type save SQL query builder. Utilizes the type system to + ensure a lot of invariants. Stresses anything related to resolving + trait bounds, by having a lot of trait impls for a large number of different + types. - **encoding**: Character encoding support. Contains some large tables. - **html5ever**: An HTML parser. Stresses macro parsing code significantly. - **inflate**: An old implementation of the DEFLATE algorithm. Stresses the diff --git a/collector/benchmarks/diesel/.editorconfig b/collector/benchmarks/diesel/.editorconfig new file mode 100644 index 000000000..d5188ec09 --- /dev/null +++ b/collector/benchmarks/diesel/.editorconfig @@ -0,0 +1,29 @@ +# EditorConfig helps developers define and maintain consistent +# coding styles between different editors and IDEs +# editorconfig.org + +root = true + + +[*] +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +indent_style = space +indent_size = 4 + +[*.rs] +indent_style = space +indent_size = 4 + +[*.toml] +indent_style = space +indent_size = 4 + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_style = space +indent_size = 2 diff --git a/collector/benchmarks/diesel/.env.sample b/collector/benchmarks/diesel/.env.sample new file mode 100644 index 000000000..c3a6e365c --- /dev/null +++ b/collector/benchmarks/diesel/.env.sample @@ -0,0 +1,15 @@ +# The database to use when testing against Postgres. +PG_DATABASE_URL=postgresql://postgres:postgres@localhost:5432/diesel_test +# The database to use when running the Postgres examples during testing. +PG_EXAMPLE_DATABASE_URL=postgresql://postgres:postgres@localhost:5432/diesel_example + +# The database to use when testing against MySQL. +MYSQL_DATABASE_URL=mysql://root@127.0.0.1:3306/diesel_test +# The database to use when running the MySQL examples during testing. +MYSQL_EXAMPLE_DATABASE_URL=mysql://root@127.0.0.1:3306/diesel_example +# A database different from the others above used for certain unit tests. +# TODO: this is magical, explain what it's there for. +MYSQL_UNIT_TEST_DATABASE_URL=mysql://root@127.0.0.1:3306/diesel_unit_test + +# The database to use when testing against SQLite. +SQLITE_DATABASE_URL=/tmp/diesel_test.sqlite diff --git a/collector/benchmarks/diesel/.github/FUNDING.yml b/collector/benchmarks/diesel/.github/FUNDING.yml new file mode 100644 index 000000000..3df2b91d4 --- /dev/null +++ b/collector/benchmarks/diesel/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [sgrif, weiznich] diff --git a/collector/benchmarks/diesel/.github/ISSUE_TEMPLATE.md b/collector/benchmarks/diesel/.github/ISSUE_TEMPLATE.md new file mode 100644 index 000000000..2d89b9ac1 --- /dev/null +++ b/collector/benchmarks/diesel/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,51 @@ + + +## Setup + +### Versions + +- **Rust:** +- **Diesel:** +- **Database:** +- **Operating System** + +### Feature Flags + +- **diesel:** + +## Problem Description + + +### What are you trying to accomplish? + + +### What is the expected output? + + +### What is the actual output? + + +### Are you seeing any additional errors? + + +### Steps to reproduce + + + +## Checklist + +- [ ] I have already looked over the [issue tracker](https://github.com/diesel-rs/diesel/issues) for similar issues. +- [ ] This issue can be reproduced on Rust's stable channel. (Your issue will be + closed if this is not the case) + + diff --git a/collector/benchmarks/diesel/.github/workflows/audit.yml b/collector/benchmarks/diesel/.github/workflows/audit.yml new file mode 100644 index 000000000..1f37f8bb1 --- /dev/null +++ b/collector/benchmarks/diesel/.github/workflows/audit.yml @@ -0,0 +1,16 @@ +name: Security audit +on: + push: + paths: + - '**/Cargo.toml' + - '**/Cargo.lock' + schedule: + - cron: '0 0 */7 * *' +jobs: + security_audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/audit-check@v1 + with: + token: ${{ secrets.GITHUB_TOKEN }} diff --git a/collector/benchmarks/diesel/.github/workflows/benches.yml b/collector/benchmarks/diesel/.github/workflows/benches.yml new file mode 100644 index 000000000..970ef57f4 --- /dev/null +++ b/collector/benchmarks/diesel/.github/workflows/benches.yml @@ -0,0 +1,53 @@ +on: + pull_request: + types: + - labeled + +name: Benchmarks + +jobs: + benchmarks: + if: github.event.label.name == 'run-benchmarks' + runs-on: ubuntu-latest + strategy: + matrix: + backend: ["postgres", "sqlite", "mysql"] + steps: + - name: Checkout sources + if: steps.bench.outputs.triggered == 'true' + uses: actions/checkout@v2 + + - name: Install postgres (Linux) + if: matrix.backend == 'postgres' + run: | + sudo apt-get update + sudo apt-get install -y libpq-dev postgresql + echo "host all all 127.0.0.1/32 md5" > sudo tee -a /etc/postgresql/10/main/pg_hba.conf + sudo service postgresql restart && sleep 3 + sudo -u postgres psql -c "ALTER USER postgres PASSWORD 'postgres';" + sudo service postgresql restart && sleep 3 + echo '::set-env name=PG_DATABASE_URL::postgres://postgres:postgres@localhost/' + + - name: Install sqlite (Linux) + if: matrix.backend == 'sqlite' + run: | + sudo apt-get update + sudo apt-get install -y libsqlite3-dev + echo '::set-env name=SQLITE_DATABASE_URL::/tmp/test.db' + + + - name: Install mysql (Linux) + if: matrix.backend == 'mysql' + run: | + sudo apt-get update + sudo apt-get -y install mysql-server libmysqlclient-dev + sudo /etc/init.d/mysql start + mysql -e "create database diesel_test; create database diesel_unit_test; grant all on \`diesel_%\`.* to 'root'@'localhost';" -uroot -proot + echo '::set-env name=MYSQL_DATABASE_URL::mysql://root:root@localhost/diesel_test' + + - name: Run benches + uses: jasonwilliams/criterion-compare-action@move_to_actions + with: + cwd: "diesel_bench" + token: ${{ secrets.GITHUB_TOKEN }} + diff --git a/collector/benchmarks/diesel/.github/workflows/ci.yml b/collector/benchmarks/diesel/.github/workflows/ci.yml new file mode 100644 index 000000000..891001d1a --- /dev/null +++ b/collector/benchmarks/diesel/.github/workflows/ci.yml @@ -0,0 +1,426 @@ +on: + pull_request: + types: [opened, synchronize, reopened] + push: + branches: + - master + - '0.[0-9]+.x' + - '1.[0-9]+.x' + + +name: CI Tests + +jobs: + check_and_test: + name: Check + strategy: + fail-fast: false + matrix: + rust: ["stable", "beta", "nightly"] + backend: ["postgres", "sqlite", "mysql"] + os: [ubuntu-20.04, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Cache cargo registry + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: ${{ runner.os }}-${{ matrix.backend }}-cargo-${{ hashFiles('**/Cargo.toml') }} + + - name: Set environment variables + shell: bash + if: matrix.backend == 'mysql' + run: | + echo "RUST_TEST_THREADS=1" >> $GITHUB_ENV + + - name: Set environment variables + shell: bash + if: matrix.rust == 'nightly' + run: | + echo "RUSTFLAGS=--cap-lints=warn" >> $GITHUB_ENV + + - name: Install postgres (Linux) + if: runner.os == 'Linux' && matrix.backend == 'postgres' + run: | + sudo apt-get update + sudo apt-get install -y libpq-dev postgresql + echo "host all all 127.0.0.1/32 md5" > sudo tee -a /etc/postgresql/10/main/pg_hba.conf + sudo service postgresql restart && sleep 3 + sudo -u postgres psql -c "ALTER USER postgres PASSWORD 'postgres';" + sudo service postgresql restart && sleep 3 + echo "PG_DATABASE_URL=postgres://postgres:postgres@localhost/" >> $GITHUB_ENV + echo "PG_EXAMPLE_DATABASE_URL=postgres://postgres:postgres@localhost/diesel_example" >> $GITHUB_ENV + + - name: Install sqlite (Linux) + if: runner.os == 'Linux' && matrix.backend == 'sqlite' + run: | + curl -fsS --retry 3 -o sqlite-autoconf-3310100.tar.gz https://sqlite.org/2020/sqlite-autoconf-3310100.tar.gz + tar zxf sqlite-autoconf-3310100.tar.gz + cd sqlite-autoconf-3310100 + CFLAGS="$CFLAGS -O2 -fno-strict-aliasing \ + -DSQLITE_DEFAULT_FOREIGN_KEYS=1 \ + -DSQLITE_SECURE_DELETE \ + -DSQLITE_ENABLE_COLUMN_METADATA \ + -DSQLITE_ENABLE_FTS3_PARENTHESIS \ + -DSQLITE_ENABLE_RTREE=1 \ + -DSQLITE_SOUNDEX=1 \ + -DSQLITE_ENABLE_UNLOCK_NOTIFY \ + -DSQLITE_OMIT_LOOKASIDE=1 \ + -DSQLITE_ENABLE_DBSTAT_VTAB \ + -DSQLITE_ENABLE_UPDATE_DELETE_LIMIT=1 \ + -DSQLITE_ENABLE_LOAD_EXTENSION \ + -DSQLITE_ENABLE_JSON1 \ + -DSQLITE_LIKE_DOESNT_MATCH_BLOBS \ + -DSQLITE_THREADSAFE=1 \ + -DSQLITE_ENABLE_FTS3_TOKENIZER=1 \ + -DSQLITE_MAX_SCHEMA_RETRY=25 \ + -DSQLITE_ENABLE_PREUPDATE_HOOK \ + -DSQLITE_ENABLE_SESSION \ + -DSQLITE_ENABLE_STMTVTAB \ + -DSQLITE_MAX_VARIABLE_NUMBER=250000" \ + ./configure --prefix=/usr \ + --enable-threadsafe \ + --enable-dynamic-extensions \ + --libdir=/usr/lib/x86_64-linux-gnu \ + --libexecdir=/usr/lib/x86_64-linux-gnu/sqlite3 + sudo make + sudo make install + echo "SQLITE_DATABASE_URL=/tmp/test.db" >> $GITHUB_ENV + + - name: Install mysql (Linux) + if: runner.os == 'Linux' && matrix.backend == 'mysql' + run: | + sudo apt-get update + sudo apt-get -y install mysql-server libmysqlclient-dev + sudo /etc/init.d/mysql start + mysql -e "create database diesel_test; create database diesel_unit_test; grant all on \`diesel_%\`.* to 'root'@'localhost';" -uroot -proot + echo "MYSQL_DATABASE_URL=mysql://root:root@localhost/diesel_test" >> $GITHUB_ENV + echo "MYSQL_EXAMPLE_DATABASE_URL=mysql://root:root@localhost/diesel_example" >> $GITHUB_ENV + echo "MYSQL_UNIT_TEST_DATABASE_URL=mysql://root:root@localhost/diesel_unit_test" >> $GITHUB_ENV + + - name: Install postgres (MacOS) + if: runner.os == 'macOS' && matrix.backend == 'postgres' + run: | + /usr/local/opt/postgres/bin/pg_ctl -D /usr/local/var/postgres start + sleep 3 + /usr/local/opt/postgres/bin/createuser -s postgres + echo "PG_DATABASE_URL=postgres://postgres@localhost/" >> $GITHUB_ENV + echo "PG_EXAMPLE_DATABASE_URL=postgres://postgres@localhost/diesel_example" >> $GITHUB_ENV + + - name: Install sqlite (MacOS) + if: runner.os == 'macOS' && matrix.backend == 'sqlite' + run: | + brew uninstall openssl@1.0.2t + brew uninstall python@2.7.17 + brew untap local/openssl + brew untap local/python2 + brew cask install xquartz + brew update + brew install sqlite + echo "SQLITE_DATABASE_URL=/tmp/test.db" >> $GITHUB_ENV + + - name: Install mysql (MacOS) + if: runner.os == 'macOS' && matrix.backend == 'mysql' + run: | + brew uninstall openssl@1.0.2t + brew uninstall python@2.7.17 + brew untap local/openssl + brew untap local/python2 + brew cask install xquartz + brew update + brew install mysql + brew services start mysql + brew services stop mysql;sleep 3;brew services start mysql + sleep 3 + macos_mysql_version="$(ls /usr/local/Cellar/mysql)" + /usr/local/Cellar/mysql/${macos_mysql_version}/bin/mysql -e "create database diesel_test; create database diesel_unit_test; grant all on \`diesel_%\`.* to 'root'@'localhost';" -uroot + echo "MYSQL_DATABASE_URL=mysql://root@localhost/diesel_test" >> $GITHUB_ENV + echo "MYSQL_EXAMPLE_DATABASE_URL=mysql://root@localhost/diesel_example" >> $GITHUB_ENV + echo "MYSQL_UNIT_TEST_DATABASE_URL=mysql://root@localhost/diesel_unit_test" >> $GITHUB_ENV + echo "MYSQLCLIENT_LIB_DIR=/usr/local/Cellar/mysql/${macos_mysql_version}/lib" >> $GITHUB_ENV + + - name: Install sqlite (Windows) + if: runner.os == 'Windows' && matrix.backend == 'sqlite' + shell: cmd + run: | + choco install sqlite + cd /D C:\ProgramData\chocolatey\lib\SQLite\tools + call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat" + lib /machine:x64 /def:sqlite3.def /out:sqlite3.lib + + - name: Set variables for sqlite (Windows) + if: runner.os == 'Windows' && matrix.backend == 'sqlite' + shell: bash + run: | + echo "C:\ProgramData\chocolatey\lib\SQLite\tools" >> $GITHUB_PATH + echo "SQLITE3_LIB_DIR=C:\ProgramData\chocolatey\lib\SQLite\tools" >> $GITHUB_ENV + echo "SQLITE_DATABASE_URL=C:\test.db" >> $GITHUB_ENV + + - name: Install postgres (Windows) + if: runner.os == 'Windows' && matrix.backend == 'postgres' + shell: bash + run: | + choco install postgresql12 --force --params '/Password:root' + echo "C:\Program Files\PostgreSQL\12\bin" >> $GITHUB_PATH + echo "C:\Program Files\PostgreSQL\12\lib" >> $GITHUB_PATH + echo "PQ_LIB_DIR=C:\Program Files\PostgreSQL\12\lib" >> $GITHUB_ENV + echo "PG_DATABASE_URL=postgres://postgres:root@localhost/" >> $GITHUB_ENV + echo "PG_EXAMPLE_DATABASE_URL=postgres://postgres:root@localhost/diesel_example" >> $GITHUB_ENV + + - name: Install mysql (Windows) + if: runner.os == 'Windows' && matrix.backend == 'mysql' + shell: cmd + run: | + choco install mysql + "C:\tools\mysql\current\bin\mysql" -e "create database diesel_test; create database diesel_unit_test; grant all on `diesel_%`.* to 'root'@'localhost';" -uroot + + - name: Set variables for mysql (Windows) + if: runner.os == 'Windows' && matrix.backend == 'mysql' + shell: bash + run: | + echo "MYSQL_DATABASE_URL=mysql://root@localhost/diesel_test" >> $GITHUB_ENV + echo "MYSQL_EXAMPLE_DATABASE_URL=mysql://root@localhost/diesel_example" >> $GITHUB_ENV + echo "MYSQL_UNIT_TEST_DATABASE_URL=mysql://root@localhost/diesel_unit_test" >> $GITHUB_ENV + echo "MYSQLCLIENT_LIB_DIR=C:\tools\mysql\current\lib" >> $GITHUB_ENV + + - name: Install rust toolchain + uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.rust }} + override: true + - name: Rust version check + uses: actions-rs/cargo@v1 + with: + command: version + - name: Test diesel (nightly) + if: matrix.rust == 'nightly' + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel/Cargo.toml --no-default-features --features "${{ matrix.backend }} unstable extras" + + - name: Test diesel + if: matrix.rust != 'nightly' + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel/Cargo.toml --no-default-features --features "${{ matrix.backend }} extras" + + - name: Test diesel (with-deprecated) + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel/Cargo.toml --no-default-features --features "${{ matrix.backend }} extras with-deprecated" + + - name: Test diesel-derives (nightly) + if: matrix.rust == 'nightly' + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel_derives/Cargo.toml --no-default-features --features "diesel/${{ matrix.backend }} diesel/unstable" + + - name: Test diesel-derives + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel_derives/Cargo.toml --no-default-features --features "diesel/${{ matrix.backend }}" + + - name: Test diesel-cli + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel_cli/Cargo.toml --no-default-features --features "${{ matrix.backend }}" + + - name: Test diesel examples + shell: bash + run: | + (cd examples/${{ matrix.backend }} && BACKEND=${{ matrix.backend }} ./test_all) + + - name: Test migrations-internals + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel_migrations/migrations_internals/Cargo.toml + + - name: Test migrations-macros + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel_migrations/migrations_macros/Cargo.toml --features "diesel/${{ matrix.backend }} ${{ matrix.backend }}" + + - name: Test diesel_migrations + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel_migrations/Cargo.toml --features "${{ matrix.backend }} diesel/${{ matrix.backend }}" + + - name: Run diesel_tests (nightly) + if: matrix.run == 'nightly' + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel_tests/Cargo.toml --no-default-features --features "${{ matrix.backend }} unstable" + + - name: Run diesel_tests + if: matrix.run != 'nightly' + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel_tests/Cargo.toml --no-default-features --features "${{ matrix.backend }}" + + - name: Run diesel_dynamic_schema tests + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel_dynamic_schema/Cargo.toml --no-default-features --features "${{ matrix.backend }}" + + - name: Run diesel_benches + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel_bench/Cargo.toml --no-default-features --features "${{ matrix.backend }}" --bench benchmarks + + - name: Run rustdoc (nightly) + if: matrix.run == 'nightly' + uses: actions-rs/cargo@v1 + with: + command: doc + args: --manifest-path diesel/Cargo.toml --no-deps --no-default-features --features "${{ matrix.backend }} unstable" + + - name: Run rustdoc + if: matrix.run != 'nightly' + uses: actions-rs/cargo@v1 + with: + command: doc + args: --manifest-path diesel/Cargo.toml --no-deps --no-default-features --features "${{ matrix.backend }}" + + compile_tests: + name: Compiletests + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: nightly-2020-05-01 + profile: minimal + override: true + - name: Cache cargo registry + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: compile_test-cargo-${{ hashFiles('**/Cargo.toml') }} + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get -y install libsqlite3-dev libpq-dev libmysqlclient-dev + + - name: Run compile tests + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel_compile_tests/Cargo.toml + + rustfmt_and_clippy: + name: Check rustfmt style && run clippy + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: 1.40.0 + profile: minimal + components: clippy, rustfmt + override: true + - name: Cache cargo registry + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: clippy-cargo-${{ hashFiles('**/Cargo.toml') }} + + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get -y install libsqlite3-dev libpq-dev libmysqlclient-dev + + - name: Run clippy + uses: actions-rs/cargo@v1 + with: + command: clippy + + - name: Check formating + uses: actions-rs/cargo@v1 + with: + command: fmt + args: --all -- --check + + sqlite_bundled: + name: Check sqlite bundled + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + override: true + - name: Cache cargo registry + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: sqlite_bundled-cargo-${{ hashFiles('**/Cargo.toml') }} + + - name: Setup environment + run: | + echo "SQLITE_DATABASE_URL=/tmp/test.db" >> $GITHUB_ENV + + - name: Test diesel-cli + uses: actions-rs/cargo@v1 + with: + command: test + args: --manifest-path diesel_cli/Cargo.toml --no-default-features --features "sqlite-bundled" + + minimal_rust_version: + name: Check Minimal supported rust version (1.40.0) + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions-rs/toolchain@v1 + with: + toolchain: 1.40.0 + profile: minimal + override: true + - name: Cache cargo registry + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: minimal_rust_version-cargo-${{ hashFiles('**/Cargo.toml') }} + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get -y install libsqlite3-dev libpq-dev libmysqlclient-dev + - name: Use minimal dependencies + run: | + RUSTC_BOOTSTRAP=1 cargo update -Z minimal-versions + + - name: Check building with rust 1.40.0 + uses: actions-rs/cargo@v1 + with: + command: check + # This example pulls generic-array v0.14.1 that cannot compile with rustc 1.40.0. + # We could exclude it as it won't affect our MSRV. + args: --all --exclude=advanced-blog-cli diff --git a/collector/benchmarks/diesel/.github/workflows/doc.yml b/collector/benchmarks/diesel/.github/workflows/doc.yml new file mode 100644 index 000000000..45386eaab --- /dev/null +++ b/collector/benchmarks/diesel/.github/workflows/doc.yml @@ -0,0 +1,54 @@ +on: + push: + branches: + - master + - '0.[0-9]+.x' + - '1.[0-9]+.x' + +name: Publish Docs +jobs: + publish_docs: + if: github.repository == 'diesel-rs/diesel' + name: Publish Docs + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v2 + - name: Cache cargo registry + uses: actions/cache@v2 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + key: cargo-doc-cargo-${{ hashFiles('**/Cargo.toml') }} + - name: Get the branch name + id: current_branch + shell: bash + run: | + echo "##[set-output name=branch;]$(echo ${GITHUB_REF#refs/heads/})" + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + profile: minimal + override: true + - name: Install dependencies + run: | + sudo apt-get update + sudo apt-get -y install libmysqlclient-dev libsqlite3-dev libpq-dev + - name: Build documentation + uses: actions-rs/cargo@v1 + with: + command: doc + args: --manifest-path diesel/Cargo.toml --features "postgres sqlite mysql extras" --workspace + + - name: Publish documentation + if: success() + uses: JamesIves/github-pages-deploy-action@releases/v3 + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + BRANCH: gh-pages # The branch the action should deploy to. + FOLDER: target/doc # The folder the action should deploy. + # Store documentation for each branch in a different folder + # This allows us to differentiate between docs for master + # and docs for already released versions + TARGET_FOLDER: ${{ steps.current_branch.outputs.branch }} diff --git a/collector/benchmarks/diesel/.gitignore b/collector/benchmarks/diesel/.gitignore new file mode 100644 index 000000000..d4da73439 --- /dev/null +++ b/collector/benchmarks/diesel/.gitignore @@ -0,0 +1,4 @@ +target +Cargo.lock +!diesel_cli/Cargo.lock +.env \ No newline at end of file diff --git a/collector/benchmarks/diesel/CHANGELOG.md b/collector/benchmarks/diesel/CHANGELOG.md new file mode 100644 index 000000000..fd0fc965d --- /dev/null +++ b/collector/benchmarks/diesel/CHANGELOG.md @@ -0,0 +1,1867 @@ +# Change Log + +All user visible changes to this project will be documented in this file. +This project adheres to [Semantic Versioning](http://semver.org/), as described +for Rust libraries in [RFC #1105](https://github.com/rust-lang/rfcs/blob/master/text/1105-api-evolution.md) + +## Unreleased + +### Added + +* `Connection` and `SimpleConnection` traits are implemented for a broader range + of `r2d2::PooledConnection` types when the `r2d2` feature is enabled. + +* Added `DatabaseErrorKind::ReadOnlyTransaction` to allow applications to + handle errors caused by writing when only allowed to read. + +* All expression methods can now be called on expressions of nullable types. + +* Added `BoxedSqlQuery`. This allows users to do a variable amount of `.sql` or + `.bind` calls without changing the underlying type. + +* Added `.sql` to `SqlQuery` and `UncheckedBind` to allow appending SQL code to + an existing query. + +* The `MacAddr` SQL type can now be used without enabling the `network-address` + feature. + +* Added support for SQLite's `UPSERT`. + You can use this feature above SQLite version 3.24.0. + +* Added ability to create custom aggregate functions in SQLite. + +* Multiple aggregate expressions can now appear together in the same select + clause. See [the upgrade notes](#2-0-0-upgrade-non-aggregate) for details. + +* `ValidGrouping` has been added to represent whether an expression is valid for + a given group by clause, and whether or not it's aggregate. It replaces the + functionality of `NonAggregate`. See [the upgrade + notes](#2-0-0-upgrade-non-aggregate) for details. + +* It is now possible to inspect the type of values returned from the database + in such a way to support constructing a dynamic value depending on this type. + +* Added a `without-deprecated` feature that unconditionally disables deprecated items. + Use this feature flag to verify that none of your dependencies is setting + the `with-deprecated` flag internally. + +* Added support for PostgreSQL's `SIMILAR TO` and `NOT SIMILAR TO`. + +* Added `#[diesel(serialize_as)]` analogous to `#[diesel(deserialize_as)]`. This allows + customization of the serialization behaviour of `Insertable` structs. + +* Added support for `GROUP BY` clauses + +### Removed + +* All previously deprecated items have been removed. +* Support for `uuid` version < 0.7.0 has been removed. +* Support for `bigdecimal` < 0.0.13 has been removed. +* Support for `pq-sys` < 0.4.0 has been removed. +* Support for `mysqlclient-sys` < 0.2.0 has been removed. +* Support for `time` types has been removed. +* Support for `chrono` < 0.4.19 has been removed. +* The `NonNull` for sql types has been removed in favour of the new `SqlType` trait. + +* `no_arg_sql_function!` has been deprecated without replacement. + [`sql_function!`][sql-function-2-0-0] can now be used for functions with zero + arguments. See [the migration guide][2-0-migration] for more details. + +### Changed + +* The way [the `Backend` trait][backend-2-0-0] handles its `RawValue` type has + been changed to allow non-references. Users of this type (e.g. code written + `&DB::RawValue` or `&::RawValue>`) should use + [`backend::RawValue`][raw-value-2-0-0] instead. Implementors of `Backend` + should check the relevant section of [the migration guide][2-0-migration]. + +[backend-2-0-0]: http://docs.diesel.rs/diesel/backend/trait.Backend.html +[raw-value-2-0-0]: http://docs.diesel.rs/diesel/backend/type.RawValue.html + +* The type metadata for MySQL has been changed to include sign information. If + you are implementing `HasSqlType` for `Mysql` manually, you may need to adjust + your implementation to fully use the new unsigned variants in `MysqlType` + +* The minimal officially supported rustc version is now 1.40.0 + +* The `RawValue` types for the `Mysql` and `Postgresql` backend where changed + from `[u8]` to distinct opaque types. If you used the concrete `RawValue` type + somewhere you need to change it to `mysql::MysqlValue` or `pg::PgValue`. + +* The uuidv07 feature was renamed to uuid, due to the removal of support for older uuid versions + +* Boxed queries (constructed from `.into_boxed()`) are now `Send`. + +* The handling of mixed aggregate values is more robust. Invalid queries such as + `.select(max(id) + other_column)` are now correctly rejected, and valid + queries such as `.select((count_star(), max(other_column)))` are now correctly + accepted. For more details, see [the upgrade notes](#2-0-0-upgrade-non-aggregate). + +* `NonAggregate` is now a trait alias for `ValidGrouping<()>` for expressions + that are not aggregate. On stable this is a normal trait with a blanket impl, + but it should never be implemented directly. With the `unstable` feature, it + will use trait aliases which prevent manual implementations. + + Due to language limitations, we cannot make the new trait alias by itself + represent everything it used to, so in some rare cases code changes may be + required. See [the upgrade notes](#2-0-0-upgrade-non-aggregate) for details. + +* Various `__NonExhaustive` variants in different (error-) enums are replaced with + `#[non_exhaustive]`. If you matched on one of those variants explicitly you need to + introduce a wild card match instead. + +* `FromSql::from_sql` is changed to construct value from non nullable database values. + To construct a rust value for nullable values use the new `FromSql::from_nullable_sql` + method instead. + +* Custom sql types are now required to implement the new `SqlType` trait. Diesel will + automatically create implementations of that trait for all types having a `#[derive(SqlType)]` + +* The workflow for manually implementing support custom types has changed. Implementing + `FromSqlRow` is not required anymore, as this is now implied by implementing + `FromSql`. The requirement of implementing `Queryable` remains + unchanged. For types using `#[derive(FromSqlRow)]` no changes are required as the + derive automatically generates the correct code + +* The structure of our deserialization trait has changed. Loading values from the database + requires now that the result type implements `FromSqlRow`. Diesel provides wild + card implementations for types implementing `Queryable` or `QueryableByName` + so non generic code does not require any change. For generic code you likely need to + replace a trait bound on `Queryable` with a trait bound on `FromSqlRow` + and a bound to `QueryableByName` with `FromSqlRow`. + +* Diesel's dsl now accept not nullable expressions in positions where nullable expressions + are expected, without needing to call `.nullable()` explicitly + +* CLI flags of `only-tables` and `except-tables` are now interpreted as regular expressions. + Similary, `only_tabels` and `except_tables` in `diesel.toml` are treated as regular expressions. + +* Now you can sort column fields by name with the `column-sorting` option. +It can be set to either `ordinal_position` (default) or `name`. +This ensures stable sorting even if columns are removed and re-added. + +### Fixed + +* Many types were incorrectly considered non-aggregate when they should not + have been. All types in Diesel are now correctly only considered + non-aggregate if their parts are. + +* Offset clauses without limit clauses resulted into invalid sql using the mysql or + sqlite backend. Both do not support such clauses without a preceding limit clause. + For those backend Diesel does now generate a fake limit clause in case no explicit + limit clause was given. As consequence of this change generic query code may + require additional trait bounds as requested from the compiler. Third party + backends are required to explicitly provide `QueryFragment` impls for + `LimitOffsetClause` now. + +* Nullability requirements are now properly enforced for nested joins. + Previously, only the rules for the outer-most join were considered. For + example, `users.left_join(posts).left_join(comments)` would allow selecting + any columns from `posts`. That will now fail to compile, and any selections + from `posts` will need to be made explicitly nullable. + +* Diesel CLI will now look for `diesel.toml` to determine the project root + before looking for `Cargo.toml`. + +* Any relative paths in `diesel.toml` will now be treated as relative to the + project root (the directory containing either `diesel.toml` or `Cargo.toml`). + They are no longer dependent on the current working directory (for all + directories in the same project) + +* The SQLite backend is now configured to interpret URIs. + See [the SQLite URI documentation] for additional details. + +[the SQLite URI documentation]: https://www.sqlite.org/uri.html + +* We've refactored our type translation layer for Mysql to handle more types now. + +* We've refactored our type level representation of nullable values. This allowed us to + fix multiple long standing bugs regarding the correct handling of nullable values in some + corner cases (#104, #2274) + +* Parenthesis are now inserted around all infix operations provided by diesel's `ExpressionMethods` traits + +### Deprecated + +* `diesel_(prefix|postfix|infix)_operator!` have been deprecated. These macros + are now available without the `diesel_` prefix. With Rust 2018 they can be + invoked as `diesel::infix_operator!` instead. + +* `diesel::pg::upsert` has been deprecated to support upsert queries on more than one backend. + Please use `diesel::upsert` instead. + +### Upgrade Notes + +#### Replacement of `NonAggregate` with `ValidGrouping` + + +FIXME: This should probably be on the website, but I wanted to document it in +the PR adding the changes. + +Key points: + +- Rules for aggregation are now correctly enforced. They match the semantics of + PG or MySQL with `ONLY_FULL_GROUP_BY` enabled. + - As before, `sql` is the escape hatch if needed. + - MySQL users can use `ANY_VALUE`, PG users can use `DISTINCT ON`. Also + consider using max/min/etc to get deterministic values. +- Any `impl NonAggregate` must be replaced with `impl ValidGrouping` +- For most code, `T: NonAggregate` should continue to work. Unless you're + getting a compiler error, you most likely don't need to change it. +- The full equivalent of what `T: NonAggregate` used to mean is: + + where + T: ValidGrouping<()>, + T::IsAggregate: MixedGrouping, + is_aggreagte::No: MixedGrouping, + +- With `feature = "unstable"`, `T: NonAggregate` implies the first two bounds, + but not the third. On stable only the first bound is implied. This is a + language limitation. +- `T: NonAggregate` can still be passed everywhere it could before, but `T: + NonAggregate` no longer implies `(OtherType, T): NonAggregate`. + - With `feature = "unstable"`, `(T, OtherType): NonAggregate` is still implied. + +- In diesel v1.4 sql functions without arguments used the `no_arg_sql_function!` macro, + which has since been deprecated. The new `sql_function!` macro supports functions without + arguments. + +[2-0-migration]: FIXME write a migration guide + +## [1.4.5] - 2020-06-09 + +### Fixed + +* Update several dependencies +* Fixed an issue where transactions that would fail to commit would leave the connection + in a broken non-committed non-rolled-back state. +* Fix a bug that result in leaking sockets/file descriptors on failed connection attempts + for postgresql +* Fix an incompatibility with newer `libmysqlclient` versions +* Remove some potential harmful usages of `mem::uninitialized` + +## [1.4.4] - 2020-03-22 + +### Fixed + +* Update several dependencies +* Fixed a bug with printing embeded migrations + +## [1.4.3] - 2019-10-11 + +### Fixed + +* Updated several dependencies +* Fixed an issue where the postgresql backend exploits implementation defined behaviour +* Fixed issue where rustdoc failed to build the documentation +* `diesel_derives` and `diesel_migrations` are updated to syn 1.0 + + +## [1.4.2] - 2019-03-19 + +### Fixed + +* Parenthesis are now inserted around all mathematical operations. This means + that `(2.into_sql() + 3) * 4` will correctly evaluate to 20 as expected. + Previously we would generate SQL that evaluated to 14. This could even result + in runtime errors if multiple types were involved (for example, `interval * + (integer + 1)`) + +## [1.4.1] - 2019-01-24 + +### Fixed + +* This release fixes a minor memory safety issue in SQLite. This bug would only + occur in an error handling branch that should never occur in practice. + +## [1.4.0] - 2019-01-20 + +### Fixed + +* `embed_migrations!` will no longer emit an unused import warning +* Diesel now supports uuid 0.7 by adding the new feature flag `uuidv07` + +### Added + +* Diesel CLI can be configured to error if a command would result in changes + to your schema file by passing `--locked-schema`. This is intended for use + in CI and production deploys, to ensure that the committed schema file is + up to date. + +* A helper trait has been added for implementing `ToSql` for PG composite types. + See [`WriteTuple`][write-tuple-1-4-0] for details. + +[write-tuple-1-4-0]: docs.diesel.rs/diesel/serialize/trait.WriteTuple.html + +* Added support for MySQL's `UNSIGNED TINYINT` + +* `DatabaseErrorKind::SerializationFailure` has been added, corresponding to + SQLSTATE code 40001 (A `SERIALIZABLE` isolation level transaction failed to + commit due to a read/write dependency on another transaction). This error is + currently only detected on PostgreSQL. + +* Diesel CLI can now generate completions for zsh and fish. See `diesel + completions --help` for details. + +* `#[belongs_to]` can now accept types that are generic over lifetimes (for + example, if one of the fields has the type `Cow<'a, str>`). To define an + association to such a type, write `#[belongs_to(parent = "User<'_>")]` + +* `Nullable` now supports `ilike` expression on in PostgreSQL. + +* `diesel_manage_updated_at('table_name')` is now available on SQLite. This + function can be called in your migrations to create a trigger which + automatically sets the `updated_at` column, unless that column was updated in + the query. + +### Changed + +* Diesel's derives now require that `extern crate diesel;` be at your crate root + (e.g. `src/lib.rs` or `src/main.rs`) + +* `Tinyint` has been renamed to `TinyInt` and an alias has been created from `Tinyint` to `TinyInt`. + +* The minimal officially supported rustc version is now 1.31.0 + +## [1.3.3] - 2018-09-12 + +### Fixed + +* Fixed an issue that occurred with MySQL 8.0 when calling `.execute` or + `.batch_execute` with a single query that returned a result set (such as our + `SELECT 1` health check in `r2d2`). + +## [1.3.2] - 2018-06-13 + +### Fixed + +* The behavior of unsigned types in MySQL has been corrected to properly set the + `is_unsigned` flag. + +* Fixed an issue with `sql_function!` when `#[sql_name]` was used on functions + with no return type. + +## [1.3.1] - 2018-05-23 + +### Fixed + +* Fixed an issue with Diesel CLI's use of temp files that caused errors on + Windows. + +## [1.3.0] - 2018-05-22 + +### Added + +* Diesel CLI now supports a configuration file. See + diesel.rs/guides/configuring-diesel-cli for details. + +* `sql_function!` now supports generic functions. See [the documentation for + `sql_function!`][sql-function-1-3-0] for more details. + +* `sql_function!` now supports aggregate functions like `sum` and `max`, by + annotating them with `#[aggregate]`. This skips the implementation of + `NonAggregate` for your function. See [the documentation for + `sql_function!`][sql-function-1-3-0] for more details. + +* `sql_function!` now supports renaming the function by annotating it with + `#[sql_name = "SOME_FUNCTION"]`. This can be used to support functions with + multiple signatures such as coalesce, by defining multiple rust functions + (with different names) that have the same `#[sql_name]`. + +* Added `sqlite-bundled` feature to `diesel_cli` to make installing on + some platforms easier. + +* Custom SQL functions can now be used with SQLite. See [the + docs][sql-function-sqlite-1-3-0] for details. + +[sql-function-sqlite-1-3-0]: http://docs.diesel.rs/diesel/macro.sql_function.html#use-with-sqlite + +* All functions and operators provided by Diesel can now be used with numeric + operators if the SQL type supports it. + +* `PgInterval` can now be used with `-`, `*`, and `/`. + +* `Vec` is now `Insertable`. It is no longer required to always place an `&` + in front of `.values`. + +* Added support for PG tuples. See [`sql_types::Record`][record-1-3-0] for details. + +[record-1-3-0]: http://docs.diesel.rs/diesel/pg/types/sql_types/struct.Record.html + +* Added support for a wider range of locking clauses, including `FOR SHARE`, + `SKIP LOCKED`, `NO WAIT`, and more. See [`QueryDsl`][locking-clause-1-3-0] for details. + +[locking-clause-1-3-0]: http://docs.diesel.rs/diesel/query_dsl/trait.QueryDsl.html#method.for_update + +### Changed + +* `sql_function!` has been redesigned. The syntax is now `sql_function!(fn + lower(x: Text) -> Text);`. The output of the new syntax is slightly different + than what was generated in the past. See [the documentation for + `sql_function!`][sql-function-1-3-0] for more details. + +[sql-function-1-3-0]: http://docs.diesel.rs/diesel/macro.sql_function.html + +* Diesel's minimum supported Rust version is 1.24.0. This was already true, but + it is now tested and enforced. Any future changes to our minimum supported + version will be listed in this change log. + +### Fixed + +* `diesel print-schema` and `infer_schema!` now properly handle unsigned types + in MySQL + +### Deprecated + +* `diesel_infer_schema` has been deprecated. `diesel print-schema` is now the + only way to generate database schema. Diesel CLI can be configured to + automatically regenerate your schema file when migrations are run. See + diesel.rs/guides/configuring-diesel-cli for details. + +* Uses of `sql_function!` in the form `sql_function!(foo, foo_t, (x: Integer))` + have been deprecated in favor of a new design (listed above). Note: Due to [a + bug in Rust](https://github.com/rust-lang/rust/issues/49912), you may not see + a deprecation warning from usage of the old form. As always, if you're + concerned about relying on deprecated code, we recommend attempting to build + your app with `default-features` turned off (specifically excluding the + `with-deprecated` feature). + +* The `--whitelist` and `--blacklist` options to `diesel print-schema` have been + deprecated and renamed `--only-tables` and `--exclude-tables`. + +## [1.2.2] - 2018-04-12 + +### Changed + +* Warnings are now allowed inside the crate. The way we had attempted to + deprecate old feature names caused builds to break. We are still not happy + with how this deprecation gets communicated, and will revisit it in the + future. + +## [1.2.1] - 2018-04-11 + +### Changed + +* Renamed `x32-column-tables`, `x64-column-tables`, and `x128-column-tables` to + `32-column-tables`, `64-column-tables`, and `128-column-tables`. The leading + `x` was due to a bug in crates.io discovered while publishing 1.2.0. The bug + has since been fixed. + +## [1.2.0] - 2018-04-06 + +### Added + +* Added `SqlLiteral::bind()`. + This is intended to be used for binding values to small SQL fragments. + Use `sql_query` if you are writing full queries. + +* Added support for `INSERT INTO table (...) SELECT ...` queries. Tables, select + select statements, and boxed select statements can now be used just like any + other `Insertable` value. + +* Any insert query written as `insert_into(table).values(values)` can now be + written as `values.insert_into(table)`. This is particularly useful when + inserting from a select statement, as select statements tend to span multiple + lines. + +* Diesel's derives can now produce improved error messages if you are using a + nightly compiler, and enable the `unstable` feature. For the best errors, you + should also set `RUSTFLAGS="--cfg procmacro2_semver_exempt"`. + +* Added support for specifying `ISOLATION LEVEL`, `DEFERRABLE`, and `READ ONLY` + on PG transactions. See [`PgConnection::build_transaction`] for details. + +[`PgConnection::build_transaction`]: http://docs.diesel.rs/diesel/pg/struct.PgConnection.html#method.build_transaction + +* Added support for `BEGIN IMMEDIATE` and `BEGIN EXCLUSIVE` on SQLite. + See [`SqliteConnection::immediate_transaction`] and + [`SqliteConnection::exclusive_transaction`] for details + +[`SqliteConnection::immediate_transaction`]: http://docs.diesel.rs/diesel/sqlite/struct.SqliteConnection.html#method.immediate_transaction +[`SqliteConnection::exclusive_transaction`]: http://docs.diesel.rs/diesel/sqlite/struct.SqliteConnection.html#method.exclusive_transaction + +* Tables with more than 56 columns are now supported by enabling the + `128-column-tables` feature. + +* Delete statements can now be boxed. This is useful for conditionally modifying + the where clause of a delete statement. See [`DeleteStatement::into_boxed`] + for details. + +[`DeleteStatement::into_boxed`]: http://docs.diesel.rs/diesel/query_builder/struct.DeleteStatement.html#method.into_boxed + +* Update statements can now be boxed. This is useful for conditionally modifying + the where clause of a update statement. See [`UpdateStatement::into_boxed`] + for details. + +[`UpdateStatement::into_boxed`]: http://docs.diesel.rs/diesel/query_builder/struct.UpdateStatement.html#method.into_boxed + +* Added `order_by` as an alias for `order`. + +* Added `then_order_by`, which appends to an `ORDER BY` clause rather than + replacing it. This is useful with boxed queries to dynamically construct an + order by clause containing an unknown number of columns. + +* `#[derive(Insertable)]` can now work on structs with fields that implement + `Insertable` (meaning one field can map to more than one column). Add + `#[diesel(embed)]` to the field to enable this behavior. + +* Queries that treat a subselect as a single value (e.g. `foo = (subselect)`) + are now supported by calling [`.single_value()`]. + +* `#[derive(Insertable)]` implements now `Insertable` also on the struct itself, + not only on references to the struct + +[`.single_value()`]: http://docs.diesel.rs/diesel/query_dsl/trait.QueryDsl.html#method.single_value + +* `ConnectionError` now implements `PartialEq`. + +* Columns generated by `table!` now implement `Default` + +* `#[derive(AsChangeset)]` now implements `AsChangeset` on the struct itself, + and not only on a reference to the struct + +* Added support for deserializing `Numeric` into `BigDecimal` on SQLite. SQLite + has no arbitrary precision type, so the result will still have floating point + rounding issues. This is primarily to support things like `avg(int_col)`, + which we define as returning `Numeric` + +### Changed + +* The bounds on `impl ToSql for Cow<'a, T>` have been loosened to no longer + require that `T::Owned: ToSql`. + +* `32-column-tables` are now enabled by default. + +### Deprecated + +* `ne_any` has been renamed to `ne_all`. + +* The `large-tables` feature has been has been renamed to `32-column-tables`. + +* The `huge-tables` feature has been renamed to `64-column-tables`. + +* `IncompleteUpdateStatement` has been removed. Use `UpdateStatement` instead. + +### Fixed + +* `diesel database setup` now correctly handles database URLs containing query + strings + +* `diesel migration list` shows the proper migration order when mixing + old and new timestamp formats. (The migrations were always run in the correct + order, this only affects the display logic of `migration list`) + +* `#[derive(Identifiable)]` now correctly associates `#[primary_key]` with the + column name, not field name. + +* Select statements can no longer incorrectly appear in an expression context. + +* `exists` can no longer incorrectly receive values other than select + statements. + +* `MysqlConnection::establish` can now properly handle IPv6 addresses wrapped in + square brackets. + +### Jokes + +* Diesel is now powered by the blockchain because it's 2018. + +## [1.1.2] - 2018-04-05 + +* No changes + +## [1.1.1] - 2018-01-16 + +### Added + +* Added `diesel::r2d2::PoolError` as an alias for `r2d2::Error`. Previously this + type was inaccessible due to `diesel::r2d2::Error`. + +## [1.1.0] - 2018-01-15 + +### Added + +* `r2d2-diesel` has been merged into Diesel proper. You should no longer rely + directly on `r2d2-diesel` or `r2d2`. The functionality of both is exposed from + `diesel::r2d2`. + +* `r2d2::PooledConnection` now implements `Connection`. This means that you + should no longer need to write `&*connection` when using `r2d2`. + +* The `BINARY` column type name is now supported for SQLite. + +* The `QueryId` trait can now be derived. + +* `FromSqlRow` can now be derived for types which implement `FromSql`. + +* `AsExpression` can now be derived for types which implement `ToSql`. + +* `HasSqlType`, `NotNull`, and `SingleValue` can now be derived with + `#[derive(SqlType)]`. See the docs for those traits for more information. + +* The return type of `FromSql`, `FromSqlRow`, and `QueryableByName` can now be + written as `deserialize::Result`. + +* The return type of `ToSql` can now be written as `serialize::Result`. + +* Added support for SQLite's `INSERT OR IGNORE` and MySQL's `INSERT IGNORE` + via the `insert_or_ignore` function. + +* `min` and `max` can now be used with array expressions. + +* Added `diesel::dsl::array`, which corresponds to a PG `ARRAY[]` literal. + +* Added the `not_none!` macro, used by implementations of `FromSql` which do not + expect `NULL`. + +* Added `result::UnexpectedNullError`, an `Error` type indicating that an + unexpected `NULL` was received during deserialization. + +* Added `.or_filter`, which behaves identically to `.filter`, but using `OR` + instead of `AND`. + +* `helper_types` now contains a type for every method defined in + `expression_methods`, and every function in `dsl`. + +* Added `FromSql` impls for `*const str` and `*const [u8]` everywhere that + `String` and `Vec` are supported. These impls do not allocate, and are + intended for use by other impls which need to parse a string or bytes, and + don't want to allocate. These impls should never be used outside of another + `FromSql` impl. + +### Deprecated + +* *IMPORTANT NOTE* Due to [several][rust-deprecation-bug-1] + [bugs][rust-deprecation-bug-2] in Rust, many of the deprecations in this + release may not show a warning. If you want to ensure you are not using any + deprecated items, we recommend attempting to compile your code without the + `with-deprecated` feature by adding `default-features = false` to + `Cargo.toml`. + +[rust-deprecation-bug-1]: https://github.com/rust-lang/rust/issues/47236 +[rust-deprecation-bug-2]: https://github.com/rust-lang/rust/issues/47237 + +* Deprecated `impl_query_id!` in favor of `#[derive(QueryId)]` + +* Deprecated specifying a column name as `#[column_name(foo)]`. `#[column_name = + "foo"]` should be used instead. + +* The `types` module has been deprecated. It has been split into `sql_types`, + `serialize`, and `deserialize`. + +* `query_source::Queryable` and `query_source::QueryableByName` have been + deprecated. These traits have been moved to `deserialize`. + +* `backend::TypeMetadata` has been deprecated. It has been moved to `sql_types`. + +* `types::ToSqlOutput` has been deprecated. It has been renamed to + `serialize::Output`. + +* `helper_types::Not` is now `helper_types::not` + +### Fixed + +* `infer_schema!` generates valid code when run against a database with no + tables. + +## [1.0.0] - 2018-01-02 + +### Added + +* `#[derive(QueryableByName)]` can now handle structs that have no associated + table. If the `#[table_name]` annotation is left off, you must annotate each + field with `#[sql_type = "Integer"]` + +* `#[derive(QueryableByName)]` can now handle embedding other structs. To have a + field whose type is a struct which implements `QueryableByName`, rather than a + single column in the query, add the annotation `#[diesel(embed)]` + +* The `QueryDsl` trait encompasses the majority of the traits that were + previously in the `query_dsl` module. + +### Fixed + +* Executing select statements on SQLite will no longer panic when the database + returns `SQLITE_BUSY` + +* `table!`s which use the `Datetime` type with MySQL will now compile correctly, + even without the `chrono` feature enabled. + +* `#[derive(QueryableByName)]` will now compile correctly when there is a shadowed `Result` type in scope. + +* `BoxableExpression` can now be used with types that are not `'static` + +### Changed + +* `Connection::test_transaction` now requires that the error returned implement `Debug`. + +* `query_builder::insert_statement::InsertStatement` is now accessed as + `query_builder::InsertStatement` + +* `query_builder::insert_statement::UndecoratedInsertRecord` is now accessed as + `query_builder::UndecoratedInsertRecord` + +* `#[derive(QueryableByName)]` now requires that the table name be explicitly + stated. + +* Most of the traits in `query_dsl` have been moved to `query_dsl::methods`. + These traits are no longer exported in `prelude`. This should not affect most + apps, as the behavior of these traits is provided by `QueryDsl`. However, if + you were using these traits in `where` clauses for generic code, you will need + to explicitly do `use diesel::query_dsl::methods::WhateverDsl`. You may also + need to use UFCS in these cases. + +* If you have a type which implemented `QueryFragment` or `Query`, which you + intended to be able to call `execute` or `load` on, you will need to manually + implement `RunQueryDsl` for that type. The trait should be unconditionally + implemented (no where clause beyond what your type requires), and the body + should be empty. + +### Removed + +* All deprecated items have been removed. + +* `LoadDsl` and `FirstDsl` have been removed. Their functionality now lives in + `LoadQuery`. + +## [0.99.1] - 2017-12-01 + +### Changed + +* Diesel CLI now properly restricts its `clap` dependency. 0.99.0 mistakenly had + no upper bound on the version. + +## [0.99.0] - 2017-11-28 + +### Added + +* The `.for_update()` method has been added to select statements, allowing + construction of `SELECT ... FOR UPDATE`. + +* Added `insert_into(table).default_values()` as a replacement for + `insert_default_values()` + +* Added `insert_into(table).values(values)` as a replacement for + `insert(values).into(table)`. + +* Added support for MySQL's `REPLACE INTO` as `replace_into(table)`. + +* Added `replace_into(table).values(values)` as a replacement for + `insert_or_replace(values).into(table)`. + +* Added `on_conflict_do_nothing` on `InsertStatement` as a replacement for + `on_conflict_do_nothing` on `Insertable` structs. + +* Added `on_conflict` on `InsertStatement` as a replacement for + `on_conflict` on `Insertable` structs. + +* `filter` can now be called on update and delete statements. This means that + instead of `update(users.filter(...))` you can write + `update(users).filter(...)`. This allows line breaks to more naturally be + introduced. + +* Subselects can now reference columns from the outer table. For example, + `users.filter(exists(posts.filter(user_id.eq(users::id))))` will now compile. + +* `TextExpressionMethods` is now implemented for expressions of type + `Nullable` as well as `Text`. + +* `allow_tables_to_appear_in_same_query!` can now take more than 2 tables, and is the same + as invoking it separately for every combination of those tables. + +* Added `sql_query`, a new API for dropping to raw SQL that is more pleasant to + use than `sql` for complete queries. The main difference from `sql` is that + you do not need to state the return type, and data is loaded from the query by + name rather than by index. + +* Added a way to rename a table in the `table!` macro with `#[sql_name="the_table_name"]` + +* Added support for PostgreSQL's `DISTINCT ON`. See + [`.distinct_on()`][0.99.0-distinct-on] for more details + +### Changed + +* The signatures of `QueryId`, `Column`, and `FromSqlRow` have all changed to + use associated constants where appropriate. + +* You will now need to invoke `allow_tables_to_appear_in_same_query!` any time two tables + appear together in the same query, even if there is a `joinable!` invocation for those tables. + +* `diesel_codegen` should no longer explicitly be used as a dependency. Unless + you are using `infer_schema!` or `embed_migrations!`, you can simply remove it + from your `Cargo.toml`. All other functionality is now provided by `diesel` + itself. + +* Code using `infer_schema!` or `infer_table_from_schema!` must now add + `diesel_infer_schema` to `Cargo.toml`, and `#[macro_use] extern crate + diesel_infer_schema` to `src/lib.rs` + +* Code using `embed_migrations!` must now add `diesel_migrations` to `Cargo.toml`, + and `#[macro_use] extern crate diesel_migrations` to `src/lib.rs` + +* The `migrations` module has been moved out of `diesel` and into + `diesel_migrations` + +### Deprecated + +* Deprecated `insert_default_values()` in favor of + `insert_into(table).default_values()` + +* Deprecated `insert(values).into(table)` in favor of + `insert_into(table).values(values)`. + +* Deprecated `insert_or_replace(values).into(table)` in favor of + `replace_into(table).values(values)`. + +* Deprecated `.values(x.on_conflict_do_nothing())` in favor of + `.values(x).on_conflict_do_nothing()` + +* Deprecated `.values(x.on_conflict(y, do_nothing()))` in favor of + `.values(x).on_conflict(y).do_nothing()` + +* Deprecated `.values(x.on_conflict(y, do_update().set(z)))` in favor of + `.values(x).on_conflict(y).do_update().set(z)` + +* Deprecated `enable_multi_table_joins` in favor of + `allow_tables_to_appear_in_same_query!` + +* Deprecated `SqlLiteral#bind`. `sql` is intended for use with small fragments + of SQL, not complete queries. Writing bind parameters in raw SQL when you are + not writing the whole query is error-prone. Use `sql_query` if you need raw + SQL with bind parameters. + +### Removed + +* `IntoInsertStatement` and `BatchInsertStatement` have been removed. It's + unlikely that your application is using these types, but `InsertStatement` is + now the only "insert statement" type. + +* `Citext` as a type alias for `Text` has been removed. Writing + `citext_column.eq("foo")` would perform a case-sensitive comparison. More + fleshed out support will be required. + +### Fixed + +* When using MySQL and SQLite, dates which cannot be represented by `chrono` + (such as `0000-00-00`) will now properly return an error instead of panicking. + +* MySQL URLs will now properly percent decode the username and password. + +* References to types other than `str` and slice can now appear on structs which + derive `Insertable` or `AsChangeset`. + +* Deserializing a date/time/timestamp column into a chrono type on SQLite will + now handle any value that is in a format documented as valid for SQLite's + `strftime` function except for the string `'now'`. + +[0.99.0-distinct-on]: http://docs.diesel.rs/diesel/query_dsl/trait.DistinctOnDsl.html#tymethod.distinct_on + +## [0.16.0] - 2017-08-24 + +### Added + +* Added helper types for inner join and left outer join + +* `diesel::debug_query` has been added as a replacement for `debug_sql!`. This + function differs from the macro by allowing you to specify the backend, and + will generate the actual query which will be run. The returned value will + implement `Display` and `Debug` to show the query in different ways + +* `diesel::pg::PgConnection`, `diesel::mysql::MysqlConnection`, and + `diesel::sqlite::SqliteConnection` are now exported from `diesel::prelude`. + You should no longer need to import these types explicitly. + +* Added support for the Decimal datatype on MySQL, using the [BigDecimal crate][bigdecimal-0.16.0]. + +* Added support for the [Range][range-0.16.0] type on postgreSQL. + +* Added support for the Datetime type on MySQL. + +* Added support for the Blob type on MySQL. + +* `infer_schema!` will now automatically detect which tables can be joined based + on the presence of foreign key constraints. + +* Added support for `Add` and `Sub` to timestamp types. + +* Added a way to rename columns in the table macro with `#[sql_name="the_column_name"]` + +* Schema inference now also generates documentation comments for tables and + columns. For `infer_schema!`, this is enabled by default. If you are using + Diesel's CLI tool, pass the new `--with-docs` parameter: + `diesel print-schema --with-docs`. + +* `infer_schema!` now automatically renames columns that conflict with + a Rust keyword by placing a _ at the end of the name. For example, + a column called `type` will be referenced as `type_` in Rust. + +### Changed + +* The deprecated `debug_sql!` and `print_sql!` functions will now generate + backend specific SQL. (The specific backend they will generate for will be + arbitrarily chosen based on the backends enabled). + +* `#[belongs_to]` will no longer generate the code required to join between two + tables. You will need to explicitly invoke `joinable!` instead, unless you are + using `infer_schema!` + +* Changed the migration directory name format to `%Y-%m-%d-%H%M%S`. + +* `between` and `not_between` now take two arguments, rather than a range. + +### Removed + +* `debug_sql!` has been deprecated in favor of `diesel::debug_query`. + +* `print_sql!` has been deprecated without replacement. + +* `diesel::backend::Debug` has been removed. + +### Fixed + +* Diesel now properly supports joins in the form: + `grandchild.join(child.join(parent))`. Previously only + `parent.join(child.join(grandchild))` would compile. + +* When encoding a `BigDecimal` on PG, `1.0` is no longer encoded as if it were + `1`. + +[bigdecimal-0.16.0]: https://crates.io/crates/bigdecimal +[range-0.16.0]: https://docs.diesel.rs/diesel/pg/types/sql_types/struct.Range.html + +## [0.15.2] - 2017-07-28 + +### Fixed + +* `BigDecimal` now properly encodes numbers starting with `10000` on postgres. + See [issue #1044][] for details. + +[issue #1044]: https://github.com/diesel-rs/diesel/issues/1044 + +## [0.15.1] - 2017-07-24 + +* No changes to public API + +## [0.15.0] - 2017-07-23 + +### Added + +* Added support for the PG `IS DISTINCT FROM` operator + +* The `ON` clause of a join can now be manually specified. See [the + docs][join-on-dsl-0.15.0] for details. + +[join-on-dsl-0.15.0]: https://docs.diesel.rs/diesel/prelude/trait.JoinOnDsl.html#method.on + +### Changed + +* Diesel will now automatically invoke `numeric_expr!` for your columns in the + common cases. You will likely need to delete any manual invocations of this + macro. + +* `Insertable` no longer treats all fields as nullable for type checking. What + this means for you is that if you had an impl like `impl + AsExpression, DB> for CustomType` in your code base, you can + remove the `Nullable` portion (Unless you are using it with fields that are + actually nullable) + +* Connections will now explicitly set the session time zone to UTC when the + connection is established + +## [0.14.1] - 2017-07-10 + +### Changed + +* The return type of `sum` and `avg` is now always considered to be `Nullable`, + as these functions return `NULL` when against on an empty table. + +## [0.14.0] - 2017-07-04 + +### Added + +* Added support for joining between more than two tables. The query builder can + now be used to join between any number of tables in a single query. See the + documentation for [`JoinDsl`][join-dsl-0.14.0] for details + +[join-dsl-0.14.0]: https://docs.diesel.rs/diesel/prelude/trait.JoinDsl.html + +* Added support for the [PostgreSQL network types][pg-network-0.14.0] `MACADDR`. + +* Added support for the Numeric datatypes, using the [BigDecimal crate][bigdecimal-0.14.0]. + +* Added a function which maps to SQL `NOT`. See [the docs][not-0.14.0] for more + details. + +* Added the [`insert_default_values`][insert-default-0.14.0] function. + +[pg-network-0.14.0]: https://www.postgresql.org/docs/9.6/static/datatype-net-types.html +[not-0.14.0]: https://docs.diesel.rs/diesel/expression/dsl/fn.not.html +[insert-default-0.14.0]: https://docs.diesel.rs/diesel/fn.insert_default_values.html +[bigdecimal-0.14.0]: https://crates.io/crates/bigdecimal + +* Added `diesel_prefix_operator!` which behaves identically to + `diesel_postfix_operator!` (previously `postfix_predicate!`), but for + operators like `NOT` which use prefix notation. + +### Changed + +* `infix_predicate!` and `infix_expression!` have been renamed to + `diesel_infix_operator!`. + +* `postfix_predicate!` and `postfix_expression!` have been renamed to + `diesel_postfix_operator!`. + +* Trait bounds along the lines of `T: LoadDsl, U: Queryable` should be changed to `T: LoadQuery`. + +* Diesel now uses a migration to set up its timestamp helpers. To generate this + migration for your project, run `diesel database setup`. + +### Removed + +* `#[has_many]` has been removed. Its functionality is now provided by + `#[belongs_to]` on the child struct. If there is no child struct to + put `#[belongs_to]` on, you can invoke `joinable!` directly instead. + +## [0.13.0] - 2017-05-15 + +### Added + +* Added support for chrono types with SQLite. + +* Bind values can now be supplied to queries constructed using raw SQL. See [the + docs][sql-bind-0.13.0] for more details. + +[sql-bind-0.13.0]: https://docs.diesel.rs/diesel/expression/sql_literal/struct.SqlLiteral.html#method.bind + +* Added support for the [PostgreSQL network types][pg-network-0.13.0] `CIDR` and + `INET`. + +[pg-network-0.13.0]: https://www.postgresql.org/docs/9.6/static/datatype-net-types.html + +* Added support for `ILIKE` in PostgreSQL. + +* `diesel migration list` will show all migrations, marking those that have been + run. + +* `diesel migration pending` will list any migrations which have not been run. + +* Added support for numeric operations with nullable types. + +* Added [`migrations::any_pending_migrations`][pending-migrations-0.13.0]. + +[pending-migrations-0.13.0]: https://docs.diesel.rs/diesel/migrations/fn.any_pending_migrations.html + +### Fixed + +* Diesel CLI now respects the `--migration-dir` argument or the + `MIGRATION_DIRECTORY` environment variable for all commands. + +* Diesel CLI now properly escapes the database name. + +## [0.12.1] - 2017-05-07 + +### Changed + +* Locked the chrono dependency to require exactly `0.3.0` instead of a semver + restriction. This restriction is required for the 0.12 line of releases to + continue compiling, as the chrono project is including breaking changes in + patch releases. + +## [0.12.0] - 2017-03-16 + +### Added + +* Added support for the majority of PG upsert (`INSERT ON CONFLICT`). We now + support specifying the constraint, as well as `DO UPDATE` in addition to `DO + NOTHING`. See [the module docs][upsert-0.12.0] for details. + +[upsert-0.12.0]: https://docs.diesel.rs/diesel/pg/upsert/index.html + +* Added support for the SQL concatenation operator `||`. See [the docs for + `.concat`][concat-0.12.0] for more details. + +[concat-0.12.0]: https://docs.diesel.rs/diesel/expression/expression_methods/text_expression_methods/trait.TextExpressionMethods.html#method.concat + +* Added support for the PostgreSQL [`Money` type][pg-money-0.12.0]. + +[pg-money-0.12.0]: https://www.postgresql.org/docs/9.6/static/datatype-money.html + +* Diesel CLI: Added `db` as an alias for `database`, so you can now write `diesel db setup` (which is almost 40% faster!). + +* The `table!` macro now allows you to use types from crates outside of Diesel. + You can specify where types should be imported from by doing: `table! { use + some_modules::*; foo { columns... }`. Not specifying any any modules is + equivalent to `use diesel::types::*;`. + +### Fixed + +* `diesel_codegen` will provide a more useful error message when it encounters + an unsupported type that contains a space in MySQL. + +* `#[derive(AsChangeset)]` will now respect custom `#[primary_key]` annotations, + and avoid setting those columns. + +### Removed + +* `WithDsl` and `Aliased` have been removed. They were a feature that was + actually closer to a cross join than the names implied, and wasn't fully + thought out. The functionality they provided will return as joins are further + revamped. + +* The internal use macro `select_column_workaround!` has been removed. If you + were relying on this internal macro, you can simply delete the line that was + calling it. + +* Columns from the right side of a left join will now need to have `.nullable()` + explicitly called to be passed to `.select`. This allows it to compose better + with functions that don't normally take nullable columns (e.g. + `lower(name).nullable()`). + +## [0.11.4] - 2017-02-21 + +### Fixed + +* Corrected a memory safety violation when using MySQL. + +## 0.11.3 - 2017-02-21 + +* No changes + +## [0.11.2] - 2017-02-19 + +### Changed + +* `pq-sys` and `mysqlclient-sys` will no longer attempt to generate bindings at + compile time. Generating the bindings required a bleeding edge version of + clang, which caused too many issues. + +## [0.11.1] - 2017-02-17 + +### Fixed + +* `.on_conflict_do_nothing()` now interacts with slices properly. + +* `MysqlConnection` now implements `Send`, which is required for connection + pooling. + +## [0.11.0] - 2017-02-16 + +### Added + +* Added support for MySQL as an additional backend. Diesel CLI will install with + MySQL support by default. To enable it for Diesel and Diesel Codegen, add + `features = ["mysql"]` to Cargo.toml. See [the docs][mysql-0.11.0] for details. + +[mysql-0.11.0]: https://docs.diesel.rs/diesel/mysql/index.html + +* Added support for PG's `ON CONFLICT DO NOTHING` clause. See [the + docs][on-conflict-0.11.0] for details. + +[on-conflict-0.11.0]: https://docs.diesel.rs/diesel/pg/upsert/trait.OnConflictExtension.html#method.on_conflict_do_nothing + +* Queries constructed using [`diesel::select`][select-0.11.0] now work properly + when [boxed][boxed-0.11.0]. + +[select-0.11.0]: https://docs.rs/diesel/0.11.0/diesel/fn.select.html +[boxed-0.11.0]: https://docs.rs/diesel/0.11.0/prelude/trait.BoxedDsl.html + +* Arrays containing null are now supported. `infer_schema!` will never infer an + array that contains null, but a `table!` definition which specifies a type of + `Array>` can now be deserialized to `Vec>` + +* [`#[belongs_to]`][belongs-to-0.11.0] associations can now be self referential. + This will generate the code required for + [`belonging_to`][belonging-to-0.11.0], without generating code for performing + a join. + +[belongs-to-0.11.0]: https://docs.rs/diesel/0.11.0/diesel/associations/trait.BelongsTo.html +[belonging-to-0.11.0]: https://docs.rs/diesel/0.11.0/diesel/prelude/trait.BelongingToDsl.html#tymethod.belonging_to + +* Added support for the `rust-lang-deprecated/time` crate on PostgreSQL. To use + it, add `features = ["deprecated-time"]` + +### Changed + +* It is no longer possible to exhaustively match against + `result::ConnectionError`. + +* Updated chrono to version 0.3. + +* [`max`][max-0.11.0] and [`min`][min-0.11.0] are now always nullable. The database will + return `NULL` when the table is empty. + +[max-0.11.0]: https://docs.diesel.rs/diesel/expression/dsl/fn.max.html +[min-0.11.0]: https://docs.diesel.rs/diesel/expression/dsl/fn.min.html + +* [`now`][now-0.11.0] can now be used as an expression of type `Timestamptz`. + +[now-0.11.0]: https://docs.diesel.rs/diesel/expression/dsl/struct.now.html + +* [`Connection::transaction`][transaction-0.11.0] now returns your error + directly instead of wrapping it in `TransactionError`. It requires that the + error implement `From` + +[transaction-0.11.0]: https://docs.diesel.rs/diesel/connection/trait.Connection.html#method.transaction + +* The way tuples of columns from the right side of left outer joins interact + with `.select` has changed. If you are deserializing into an option of a tuple + (instead of a tuple of options), you will need to explicitly call + `.nullable()`. (e.g. `.select(users::name, (posts::title, + posts::body).nullable())`) + +### Removed + +* `result::TransactionError` +* `result::TransactionResult` + +## [0.10.1] - 2017-02-08 + +### Fixed + +* `infer_table_from_schema!` properly handles table names with a custom schema + specified. + +### Changed + +* Updated uuid to version 0.4. + +## [0.10.0] - 2017-02-02 + +### Added + +* Added support for the PostgreSQL [`json` and `jsonb` types][pg-json]. They can + be mapped to/from `serde_json::Value`. The `serde` feature must be enabled to + use the JSON types. + +[pg-json]: https://www.postgresql.org/docs/9.6/static/datatype-json.html + +* Added the `print-schema` command to Diesel CLI. This command will print the + output of the `infer_schema!` macro. For more information run `diesel help + print-schema`. + +### Changed + +* When possible, we will use deprecation warnings for breaking changes. + Deprecated code requires the `with-deprecated` feature, which is enabled by + default. + +* The `postgres` feature is no longer enabled by default by `diesel` or + `diesel_codegen_syntex`. Add `features = ["postgres"]` to your `Cargo.toml`. + +* The `persistable` module has been renamed to `insertable`. + +### Fixed + +* `#[derive(Insertable)]` allows fields of type `Option` to be used with + columns that are not null if they have a default value. + +### Removed + +* `diesel_codegen_syntex` is no longer supported. `diesel_codegen` can now be + used on stable Rust. + +* Dropped support for Rust 1.14 and earlier + +## [0.9.1] - 2016-12-09 + +### Fixed + +* Added missing impls for loading `chrono::NaiveDateTime` from a column of type + `Timestamptz` + +* `#[derive(AsChangeset)]` no longer assumes that `use diesel::prelude::*` has + been done. + +* `debug_sql!` can now properly be used with types from `chrono` or + `std::time`. + +* When using PostgreSQL, attempting to get the error message of a query which + could not be transmitted to the server (such as a query with greater than + 65535 bind parameters) will no longer panic. + +## [0.9.0] - 2016-12-08 + +### Added + +* Added support for SQL `NOT IN` using the `ne_any` method. + +* The `table!` macro now allows custom schemas to be specified. Example: + + ```rust + table! { + schema_1.table_1 { + id -> Integer, + } + } + ``` + + The generated module will still be called `table_1`. + +* The `infer_table_from_schema!` macro now allows custom schemas to be + specified. Example: + + ```rust + infer_table_from_schema!("dotenv:DATABASE_URL", "schema_1.table_1"); + ``` + +* The `infer_schema!` optionally allows a schema name as the second argument. Any + schemas other than `public` will be wrapped in a module with the same name as + the schema. For example, `schema_1.table_1` would be referenced as + `schema_1::table_1`. + +* Added support for batch insert on SQLite. This means that you can now pass a + slice or vector to [`diesel::insert`][insert] on all backends. + +[insert]: https://docs.diesel.rs/diesel/fn.insert.html + +* Added a function for SQL `EXISTS` expressions. See + [`diesel::expression::dsl::exists`][exists] for details. + +[exists]: https://docs.diesel.rs/diesel/expression/dsl/fn.sql.html + +* `#[derive(Identifiable)]` can be used with structs that have primary keys + other than `id`, as well as structs with composite primary keys. You can now + annotate the struct with `#[primary_key(nonstandard)]` or `#[primary_key(foo, + bar)]`. + +### Changed + +* All macros with the same name as traits we can derive (e.g. `Queryable!`) have + been renamed to `impl_Queryable!` or similar. + +### Fixed + +* `#[derive(Identifiable)]` now works on structs with lifetimes + +* Attempting to insert an empty slice will no longer panic. It does not execute + any queries, but the result will indicate that we successfully inserted 0 + rows. + +* Attempting to update a record with no changes will no longer generate invalid + SQL. The result of attempting to execute the query will still be an error, but + but it will be a `Error::QueryBuilderError`, rather than a database error. + This means that it will not abort the current transaction, and can be handled + by applications. + +* Calling `eq_any` or `ne_any` with an empty array no longer panics. + `eq_any(vec![])` will return no rows. `ne_any(vec![])` will return all rows. + +## [0.8.2] - 2016-11-22 + +### Changed + +* Fixed support for nightlies later than 2016-11-07 + +* Removed support for nightlies earlier than 2016-11-07 + +* Calls to `infer_table_from_schema!` will need to be wrapped in a module if + called more than once. This change is to work around further limitations of + the Macros 1.1 system. Example: + + ```rust + mod infer_users { + infer_table_from_schema!("dotenv:DATABASE_URL", "users"); + } + pub use self::infer_users::*; + ``` + +## [0.8.1] - 2016-11-01 + +### Added + +* SQLite date and time columns can be deserialized to/from strings. + +### Fixed + +* Fixed an issue with `diesel_codegen` on nightlies >= 2016-10-20 + +## [0.8.0] - 2016-10-10 + +### Added + +* Added partial support for composite primary keys. + +* Added support for PostgreSQL `NULLS FIRST` and `NULLS LAST` when sorting. + See https://docs.diesel.rs/diesel/prelude/trait.SortExpressionMethods.html + for details. + +* Added support for the `timestamp with time zone` type in PostgreSQL (referred + to as `diesel::types::Timestamptz`) + +* Diesel CLI can now generate bash completion. See [the readme][bash completion] + for details. + +* `infer_schema!` and `infer_table_from_schema!` can now take `"env:foo"` + instead of `env!("foo")` and `"dotenv:foo"` instead of `dotenv!("foo")`. The + use of `dotenv` requires the `dotenv` feature on `diesel_codegen`, which is + included by default. Using `env!` and `dotenv!` will no longer work with + `diesel_codegen`. They continue to work with `diesel_codegen_syntex`, but that + crate will be deprecated when Macros 1.1 is in the beta channel for Rust. + +[bash completion]: https://github.com/diesel-rs/diesel/blob/b1a0d9901f0f2a8c8d530ccba8173b57f332b891/diesel_cli/README.md#bash-completion + +### Changed + +* Structs annotated with `#[has_many]` or `#[belongs_to]` now require + `#[derive(Associations)]`. This is to allow them to work with Macros 1.1. + +* `embed_migrations!` now resolves paths relative to `Cargo.toml` instead of the + file the macro was called from. This change is required to allow this macro to + work with Macros 1.1. + +### Fixed + +* `diesel migrations run` will now respect migration directories overridden by + command line argument or environment variable +* The `infer_schema!` macro will no longer fetch views alongside with tables. + This was a source of trouble for people that had created views or are using + any extension that automatically creates views (e.g. PostGIS) + +### Changed + +* `#[changeset_for(foo)]` should now be written as + `#[derive(AsChangeset)] #[table_name="foo"]`. If you were specifying + `treat_none_as_null = "true"`, you should additionally have + `#[changeset_options(treat_none_as_null = "true")]`. +* `#[insertable_into(foo)]` should now be written as + `#[derive(Insertable)] #[table_name="foo"]`. + +## [0.7.2] - 2016-08-20 + +* Updated nightly version and syntex support. + +## [0.7.1] - 2016-08-11 + +### Changed + +* The `Copy` constraint has been removed from `Identifiable::Id`, and + `Identifiable#id` now returns `&Identifiable::Id`. + +### Fixed + +* `#[belongs_to]` now respects the `foreign_key` option when using + `diesel_codegen` or `diesel_codegen_syntex`. + +## [0.7.0] - 2016-08-01 + +### Added + +* The initial APIs have been added in the form of `#[has_many]` and + `#[belongs_to]`. See [the module documentation][associations-module] for more + information. + +* The `Insertable!` macro can now be used instead of `#[insertable_into]` for + those wishing to avoid syntax extensions from `diesel_codegen`. See + https://docs.diesel.rs/diesel/macro.Insertable!.html for details. + +* The `Queryable!` macro can now be used instead of `#[derive(Queryable)]` for + those wishing to avoid syntax extensions from `diesel_codegen`. See + https://docs.diesel.rs/diesel/macro.Queryable!.html for details. + +* The `Identifiable!` macro can now be used instead of `#[derive(Identifiable)]` for + those wishing to avoid syntax extensions from `diesel_codegen`. See + https://docs.diesel.rs/diesel/macro.Identifiable!.html for details. + +* The `AsChangeset!` macro can now be used instead of `#[changeset_for(table)]` + for those wishing to avoid syntax extensions from `diesel_codegen`. See + https://docs.diesel.rs/diesel/macro.AsChangeset!.html for details. + +* Added support for the PostgreSQL `ALL` operator. See + https://docs.diesel.rs/diesel/pg/expression/dsl/fn.all.html for details. + +* Added support for `RETURNING` expressions in `DELETE` statements. Implicitly + these queries will use `RETURNING *`. + +### Changed + +* Diesel now targets `nightly-2016-07-07`. Future releases will update to a + newer nightly version on the date that Rust releases. + +* `diesel_codegen` has been split into two crates. `diesel_codegen` and + `diesel_codegen_syntex`. See [this commit][syntex-split] for migration + information. + +* Most structs that implement `Queryable` will now also need + `#[derive(Identifiable)]`. + +* `infer_schema!` on SQLite now accepts a larger range of type names + +* `types::VarChar` is now an alias for `types::Text`. Most code should be + unaffected by this. PG array columns are treated slightly differently, + however. If you are using `varchar[]`, you should switch to `text[]` instead. + +* Struct fields annotated with `#[column_name="name"]` should be changed to + `#[column_name(name)]`. + +* The structure of `DatabaseError` has changed to hold more information. See + https://docs.diesel.rs/diesel/result/enum.Error.html and + https://docs.diesel.rs/diesel/result/trait.DatabaseErrorInformation.html for + more information + +* Structs which implement `Identifiable` can now be passed to `update` and + `delete`. This means you can now write `delete(&user).execute(&connection)` + instead of `delete(users.find(user.id)).execute(&connection)` + +[associations-module]: https://docs.diesel.rs/diesel/associations/index.html +[syntex-split]: https://github.com/diesel-rs/diesel/commit/36b8801bf5e9594443743e6a7c62e29d3dce36b7 + +### Fixed + +* `&&[T]` can now be used in queries. This allows using slices with things like + `#[insertable_into]`. + +## [0.6.1] 2016-04-14 + +### Added + +* Added the `escape` method to `Like` and `NotLike`, to specify the escape + character used in the pattern. See [EscapeExpressionMethods][escape] for + details. + +[escape]: https://docs.diesel.rs/diesel/expression/expression_methods/escape_expression_methods/trait.EscapeExpressionMethods.html + +### Fixed + +* `diesel_codegen` and `diesel_cli` now properly rely on Diesel 0.6.0. The + restriction to 0.5.0 was an oversight. + +* `infer_schema!` now properly excludes metadata tables on SQLite. + +* `infer_schema!` now properly maps types on SQLite. + +## [0.6.0] 2016-04-12 + +### Added + +* Queries can now be boxed using the `into_boxed()` method. This is useful for + conditionally modifying queries without changing the type. See + [BoxedDsl][boxed_dsl] for more details. + +* `infer_schema!` is now supported for use with SQLite3. + +* The maximum table size can be increased to 52 by enabling the `huge-tables` + feature. This feature will substantially increase compile times. + +* The `DISTINCT` keyword can now be added to queries via the `distinct()` + method. + +* `SqliteConnection` now implements `Send` + +[boxed_dsl]: https://docs.diesel.rs/diesel/prelude/trait.BoxedDsl.html + +### Changed + +* `diesel::result::Error` now implements `Send` and `Sync`. This required a + change in the return type of `ToSql` and `FromSql` to have those bounds as + well. + +* It is no longer possible to pass an owned value to `diesel::insert`. `insert` + will now give a more helpful error message when you accidentally try to pass + an owned value instead of a reference. + +### Fixed + +* `#[insertable_into]` can now be used with structs that have lifetimes with + names other than `'a'`. + +* Tables with a single column now properly return a single element tuple. E.g. + if the column was of type integer, then `users::all_columns` is now `(id,)` + and not `id`. + +* `infer_schema!` can now work with tables that have a primary key other than + `id`. + +### Removed + +* Removed the `no select` option for the `table!` macro. This was a niche + feature that didn't fit with Diesel's philosophies. You can write a function + that calls `select` for you if you need this functionality. + +## [0.5.4] 2016-03-23 + +* Updated `diesel_codegen` to allow syntex versions up to 0.30.0. + +## [0.5.3] 2016-03-12 + +### Added + +* Added helper function `diesel_manage_updated_at('TABLE_NAME')` to postgres + upon database setup. This function sets up a trigger on the specified table + that automatically updates the `updated_at` column to the `current_timestamp` + for each affected row in `UPDATE` statements. + +* Added support for explicit `RETURNING` expressions in `INSERT` and `UPDATE` + queries. Implicitly these queries will still use `RETURNING *`. + +### Fixed + +* Updated to work on nightly from early March + +## [0.5.2] 2016-02-27 + +* Updated to work on nightly from late February + +## [0.5.1] 2016-02-11 + +* Diesel CLI no longer has a hard dependency on SQLite and PostgreSQL. It + assumes both by default, but if you need to install on a system that doesn't + have one or the other, you can install it with `cargo install diesel_cli + --no-default-features --features postgres` or `cargo install diesel_cli + --no-default-features --features sqlite` + +## [0.5.0] 2016-02-05 + +### Added + +* Added support for SQLite. Diesel still uses postgres by default. To use SQLite + instead, add `default-features = false, features = ["sqlite"]` to your + Cargo.toml. You'll also want to add `default-features = false, features = + ["sqlite"]` to `diesel_codegen`. + Since SQLite is a much more limited database, it does not support our full set + of features. You can use SQLite and PostgreSQL in the same project if you + desire. + +* Added support for mapping `types::Timestamp`, `types::Date`, and `types::Time` + to/from `chrono::NaiveDateTime`, `chrono::NaiveDate`, and `chrono::NaiveTime`. + Add `features = ["chrono"]` to enable. + +* Added a `treat_none_as_null` option to `changeset_for`. When set to `true`, + a model will set a field to `Null` when an optional struct field is `None`, + instead of skipping the field entirely. The default value of the option is + `false`, as we think the current behavior is a much more common use case. + +* Added `Expression#nullable()`, to allow comparisons of not null columns with + nullable ones when required. + +* Added `sum` and `avg` functions. + +* Added the `diesel setup`, `diesel database setup`, and `diesel database + reset` commands to the CLI. + +* Added support for SQL `IN` statements through the `eq_any` method. + +* Added a top level `select` function for select statements with no from clause. + This is primarily intended to be used for testing Diesel itself, but it has + been added to the public API as it will likely be useful for third party + crates in the future. `select(foo).from(bar)` might be a supported API in the + future as an alternative to `bar.select(foo)`. + +* Added `expression::dsl::sql` as a helper function for constructing + `SqlLiteral` nodes. This is primarily intended to be used for testing Diesel + itself, but is part of the public API as an escape hatch if our query builder + DSL proves inadequate for a specific case. Use of this function in any + production code is discouraged as it is inherently unsafe and avoids real type + checking. + +### Changed + +* Moved most of our top level trait exports into a prelude module, and + re-exported our CRUD functions from the top level. + `diesel::query_builder::update` and friends are now `diesel::update`, and you + will get them by default if you import `diesel::*`. For a less aggressive + glob, you can import `diesel::prelude::*`, which will only export our traits. + +* `Connection` is now a trait instead of a struct. The struct that was + previously known as `Connection` can be found at `diesel::pg::PgConnection`. + +* Rename both the `#[derive(Queriable)]` attribute and the `Queriable` trait to + use the correct spelling `Queryable`. + +* `load` and `get_results` now return a `Vec` instead of an iterator. + +* Replaced `Connection#find(source, id)` with + `source.find(id).first(&connection)`. + +* The `debug_sql!` macro now uses \` for identifier quoting, and `?` for bind + parameters, which is closer to a "generic" backend. The previous behavior had + no identifier quoting, and used PG specific bind params. + +* Many user facing types are now generic over the backend. This includes, but is + not limited to `Queryable` and `Changeset`. This change should not have much + impact, as most impls will have been generated by diesel_codegen, and that API + has not changed. + +* The mostly internal `NativeSqlType` has been removed. It now requires a known + backend. `fn foo() where T: NativeSqlType` is now `fn foo() where + DB: HasSqlType` + +### Removed + +* `Connection#query_sql` and `Connection#query_sql_params` have been removed. + These methods were not part of the public API, and were only meant to be used + for testing Diesel itself. However, they were technically callable from any + crate, so the removal has been noted here. Their usage can be replaced with + bare `select` and `expression::dsl::sql`. + +## [0.4.1] 2016-01-11 + +### Changed + +* Diesel CLI will no longer output notices about `__diesel_schema_migrations` + already existing. + +* Relicensed under MIT/Apache dual + +## [0.4.0] 2016-01-08 + +### Added + +* Added Diesel CLI, a tool for managing your schema. + See [the readme](https://github.com/diesel-rs/diesel/blob/v0.4.0/README.md#database-migrations) + for more information. + +* Add the ability for diesel to maintain your schema for you automatically. See + the [migrations](https://docs.diesel.rs/diesel/migrations/index.html) + module for individual methods. + +* Add DebugQueryBuilder to build sql without requiring a connection. + +* Add print_sql! and debug_sql! macros to print out and return sql strings from + QueryFragments. + +### Fixed + +* `#[changeset_for]` can now be used with structs containing a `Vec`. Fixes + [#63](https://github.com/diesel-rs/diesel/issues/63). + +* No longer generate invalid SQL when an optional update field is not the first + field on a changeset. Fixes [#68](https://github.com/diesel-rs/diesel/issues/68). + +* `#[changeset_for]` can now be used with structs containing only a single field + other than `id`. Fixes [#66](https://github.com/diesel-rs/diesel/issues/66). + +* `infer_schema!` properly works with array columns. Fixes + [#65](https://github.com/diesel-rs/diesel/issues/65). + +## [0.3.0] 2015-12-04 + +### Changed + +* `#[changeset_for(table)]` now treats `Option` fields as an optional update. + Previously a field with `None` for the value would insert `NULL` into the + database field. It now does not update the field if the value is `None`. + +* `.save_changes` (generated by `#[changeset_for]`) now returns a new struct, + rather than mutating `self`. The returned struct can be any type that + implements `Queryable` for the right SQL type + +### Fixed + +* `#[derive(Queryable)]` now allows generic parameters on the struct. + +* Table definitions can now support up to 26 columns. Because this increases our + compile time by 3x, `features = ["large-tables"]` is needed to support table + definitions above 16 columns. + +### Added + +* Quickcheck is now an optional dependency. When `features = ["quickcheck"]` is + added to `Cargo.toml`, you'll gain `Arbitrary` implementations for everything + in `diesel::data_types`. + +* Added support for the SQL `MIN` function. + +* Added support for the `Numeric` data type. Since there is no Big Decimal type + in the standard library, a dumb struct has been provided which mirrors what + Postgres provides, which can be converted into whatever crate you are using. + +* Timestamp columns can now be used with `std::time::SystemTime` when compiled + with `--features unstable` + +* Implemented `Send` on `Connection` (required for R2D2 support) + +* Added `infer_schema!` and `infer_table_from_schema!`. Both macros take a + database URL, and will invoke `table!` for you automatically based on the + schema. `infer_schema!` queries for the table names, while + `infer_table_from_schema!` takes a table name as the second argument. + +## [0.2.0] - 2015-11-30 + +### Added + +* Added an `execute` method to `QueryFragment`, which is intended to replace + `Connection#execute_returning_count`. The old method still exists for use + under the hood, but has been hidden from docs and is not considered public + API. + +* Added `get_result` and `get_results`, which work similarly to `load` and + `first`, but are intended to make code read better when working with commands + like `create` and `update`. In the future, `get_result` may also check that + only a single row was affected. + +* Added [`insert`][insert], which mirrors the pattern of `update` and `delete`. + +### Changed + +* Added a hidden `__Nonexhaustive` variant to `result::Error`. This is not + intended to be something you can exhaustively match on, but I do want people + to be able to check for specific cases, so `Box` is + not an option. + +* `query_one`, `find`, and `first` now assume a single row is returned. For + cases where you actually expect 0 or 1 rows to be returned, the `optional` + method has been added to the result, in case having a `Result>` is + more idiomatic than checking for `Err(NotFound)`. + +### Deprecated + +* `Connection#insert` and `Connection#insert_returning_count` have been + deprecated in favor of [`insert`][insert] + +## 0.1.0 - 2015-11-29 + +* Initial release + +[0.2.0]: https://github.com/diesel-rs/diesel/compare/v0.1.0...v0.2.0 +[0.3.0]: https://github.com/diesel-rs/diesel/compare/v0.2.0...v0.3.0 +[0.4.0]: https://github.com/diesel-rs/diesel/compare/v0.3.0...v0.4.0 +[0.4.1]: https://github.com/diesel-rs/diesel/compare/v0.4.0...v0.4.1 +[0.5.0]: https://github.com/diesel-rs/diesel/compare/v0.4.1...v0.5.0 +[0.5.1]: https://github.com/diesel-rs/diesel/compare/v0.5.0...v0.5.1 +[0.5.2]: https://github.com/diesel-rs/diesel/compare/v0.5.1...v0.5.2 +[0.5.3]: https://github.com/diesel-rs/diesel/compare/v0.5.2...v0.5.3 +[0.5.4]: https://github.com/diesel-rs/diesel/compare/v0.5.3...v0.5.4 +[0.6.0]: https://github.com/diesel-rs/diesel/compare/v0.5.4...v0.6.0 +[0.6.1]: https://github.com/diesel-rs/diesel/compare/v0.6.0...v0.6.1 +[0.7.0]: https://github.com/diesel-rs/diesel/compare/v0.6.1...v0.7.0 +[0.7.1]: https://github.com/diesel-rs/diesel/compare/v0.7.0...v0.7.1 +[0.7.2]: https://github.com/diesel-rs/diesel/compare/v0.7.1...v0.7.2 +[0.8.0]: https://github.com/diesel-rs/diesel/compare/v0.7.2...v0.8.0 +[0.8.1]: https://github.com/diesel-rs/diesel/compare/v0.8.0...v0.8.1 +[0.8.2]: https://github.com/diesel-rs/diesel/compare/v0.8.1...v0.8.2 +[0.9.0]: https://github.com/diesel-rs/diesel/compare/v0.8.2...v0.9.0 +[0.9.1]: https://github.com/diesel-rs/diesel/compare/v0.9.0...v0.9.1 +[0.10.0]: https://github.com/diesel-rs/diesel/compare/v0.9.1...v0.10.0 +[0.10.1]: https://github.com/diesel-rs/diesel/compare/v0.10.0...v0.10.1 +[0.11.0]: https://github.com/diesel-rs/diesel/compare/v0.10.1...v0.11.0 +[0.11.1]: https://github.com/diesel-rs/diesel/compare/v0.11.0...v0.11.1 +[0.11.2]: https://github.com/diesel-rs/diesel/compare/v0.11.1...v0.11.2 +[0.11.4]: https://github.com/diesel-rs/diesel/compare/v0.11.2...v0.11.4 +[0.12.0]: https://github.com/diesel-rs/diesel/compare/v0.11.4...v0.12.0 +[0.12.1]: https://github.com/diesel-rs/diesel/compare/v0.12.0...v0.12.1 +[0.13.0]: https://github.com/diesel-rs/diesel/compare/v0.12.1...v0.13.0 +[0.14.0]: https://github.com/diesel-rs/diesel/compare/v0.13.0...v0.14.0 +[0.14.1]: https://github.com/diesel-rs/diesel/compare/v0.14.0...v0.14.1 +[0.15.0]: https://github.com/diesel-rs/diesel/compare/v0.14.1...v0.15.0 +[0.15.1]: https://github.com/diesel-rs/diesel/compare/v0.15.0...v0.15.1 +[0.15.2]: https://github.com/diesel-rs/diesel/compare/v0.15.1...v0.15.2 +[0.16.0]: https://github.com/diesel-rs/diesel/compare/v0.15.2...v0.16.0 +[0.99.0]: https://github.com/diesel-rs/diesel/compare/v0.16.0...v0.99.0 +[0.99.1]: https://github.com/diesel-rs/diesel/compare/v0.99.0...v0.99.1 +[1.0.0]: https://github.com/diesel-rs/diesel/compare/v0.99.1...v1.0.0 +[1.1.0]: https://github.com/diesel-rs/diesel/compare/v1.0.0...v1.1.0 +[1.1.1]: https://github.com/diesel-rs/diesel/compare/v1.1.0...v1.1.1 +[1.1.2]: https://github.com/diesel-rs/diesel/compare/v1.1.1...v1.1.2 +[1.2.0]: https://github.com/diesel-rs/diesel/compare/v1.1.2...v1.2.0 +[1.2.1]: https://github.com/diesel-rs/diesel/compare/v1.2.0...v1.2.1 +[1.2.2]: https://github.com/diesel-rs/diesel/compare/v1.2.1...v1.2.2 +[1.3.0]: https://github.com/diesel-rs/diesel/compare/v1.2.2...v1.3.0 +[1.3.1]: https://github.com/diesel-rs/diesel/compare/v1.3.0...v1.3.1 +[1.3.2]: https://github.com/diesel-rs/diesel/compare/v1.3.1...v1.3.2 +[1.3.3]: https://github.com/diesel-rs/diesel/compare/v1.3.2...v1.3.3 +[1.4.0]: https://github.com/diesel-rs/diesel/compare/v1.3.0...v1.4.0 +[1.4.1]: https://github.com/diesel-rs/diesel/compare/v1.4.0...v1.4.1 +[1.4.2]: https://github.com/diesel-rs/diesel/compare/v1.4.1...v1.4.2 +[1.4.3]: https://github.com/diesel-rs/diesel/compare/v1.4.2...v1.4.3 +[1.4.4]: https://github.com/diesel-rs/diesel/compare/v1.4.3...v1.4.4 diff --git a/collector/benchmarks/diesel/CONTRIBUTING.md b/collector/benchmarks/diesel/CONTRIBUTING.md new file mode 100644 index 000000000..c0d35c2e8 --- /dev/null +++ b/collector/benchmarks/diesel/CONTRIBUTING.md @@ -0,0 +1,177 @@ +# Contributing + +Thanks for your interest in contributing to Diesel! We very much look forward to +your suggestions, bug reports, and pull requests. + +We run an active [Gitter +channel](https://gitter.im/diesel-rs/diesel) where you can ask Diesel-related questions and +get help. Feel free to ask there before opening a GitHub issue or +pull request. + +*Note:* Anyone who interacts with Diesel in any space, including but not +limited to this GitHub repository, must follow our [code of +conduct](https://github.com/diesel-rs/diesel/blob/master/code_of_conduct.md). + + +## Submitting bug reports + +Have a look at our [issue tracker]. If you can't find an issue (open or closed) +describing your problem (or a very similar one) there, please open a new issue with +the following details: + +- Which versions of Rust and Diesel are you using? +- Which feature flags are you using? +- What are you trying to accomplish? +- What is the full error you are seeing? +- How can we reproduce this? + - Please quote as much of your code as needed to reproduce (best link to a + public repository or [Gist]) + - Please post as much of your database schema as is relevant to your error + +[issue tracker]: https://github.com/diesel-rs/diesel/issues +[Gist]: https://gist.github.com + +Thank you! We'll try to respond as quickly as possible. + + +## Submitting feature requests + +If you can't find an issue (open or closed) describing your idea on our [issue +tracker], open an issue. Adding answers to the following +questions in your description is +1: + +- What do you want to do, and how do you expect Diesel to support you with that? +- How might this be added to Diesel? +- What are possible alternatives? +- Are there any disadvantages? + +Thank you! We'll try to respond as quickly as possible. + + +## Contribute code to Diesel + +### Setting up Diesel locally + +1. Install Rust using [rustup], which allows you to easily switch between Rust + versions. Diesel currently supports Rust Stable, Nightly, Rust Beta. + +2. Install the system libraries needed to interface with the database systems + you wish to use. + + These are the same as when compiling Diesel. It's generally a good idea + to install _all_ drivers so you can run all tests locally. + + *Shortcut:* On macOS, you don't need to install anything to work with SQLite. + For PostgreSQL, you'll only need the server (`libpq` is installed by + default). To get started, `brew install postgresql mysql` and follow the + instructions shown to set up the database servers. +3. Clone this repository and open it in your favorite editor. +4. Create a `.env` file in this directory, and add the connection details for + your databases. + + *Additional note:* The MySQL tests currently fail when running on MySQL 5.6 + or lower. If you have 5.6 or lower installed locally and cannot upgrade for + some reason, you may want to consider setting up Docker as mentioned below. + + See [.env.sample](.env.sample) for an example that works with a trivial + local setup. + + *Note:* If you didn't specify the MySQL user to be one with elevated + permissions, you'll want to run a command like ```mysql -c "GRANT ALL ON + `diesel_%`.* TO ''@'localhost';" -uroot```, or something similar for the + user that you've specified. + + If you have [Docker](https://www.docker.com/), the following snippet might help you + to get Postgres and MySQL running (with the above `.env` file): + + ```bash + #!/usr/bin/env sh + set -e + docker run -d --name diesel.mysql -p 3306:3306 -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql + while + sleep 1; + ! echo 'CREATE DATABASE diesel_test; CREATE DATABASE diesel_unit_test;' | docker exec -i diesel.mysql mysql + do sleep 1; done + + docker run -d --name diesel.postgres -p 5432:5432 -e POSTGRES_PASSWORD=postgres postgres + while + sleep 1; + ! echo 'CREATE DATABASE diesel_test;' | docker exec -i diesel.postgres psql -U postgres + do :; done + ``` + + If you want to use docker-compose, you can execute docker-compose command like this. + + ```bash + $ docker-compose up + ``` + +5. Now, try running the test suite to confirm everything works for you locally + by executing `bin/test`. (Initially, this will take a while to compile + everything.) + +[rustup]: https://rustup.rs/ + +### Coding Style + +We follow the [Rust Style Guide](https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/guide.md), enforced using [rustfmt](https://github.com/rust-lang/rustfmt). +To run rustfmt tests locally: + +1. Use rustup to set rust toolchain to the version specified in the + [rust-toolchain file](./rust-toolchain). + +2. Install the rustfmt and clippy by running + ``` + rustup component add rustfmt-preview + rustup component add clippy-preview + ``` + +3. Run clippy using cargo from the root of your diesel repo. + ``` + cargo clippy + ``` + Each PR needs to compile without warning. + +4. Run rustfmt using cargo from the root of your diesel repo. + + To see changes that need to be made, run + + ``` + cargo fmt --all -- --check + ``` + + If all code is properly formatted (e.g. if you have not made any changes), + this should run without error or output. + If your code needs to be reformatted, + you will see a diff between your code and properly formatted code. + If you see code here that you didn't make any changes to + then you are probably running the wrong version of rustfmt. + Once you are ready to apply the formatting changes, run + + ``` + cargo fmt --all + ``` + + You won't see any output, but all your files will be corrected. + +You can also use rustfmt to make corrections or highlight issues in your editor. +Check out [their README](https://github.com/rust-lang/rustfmt) for details. + + +### Common Abbreviations + +`ST`: Sql Type. Basically always has the `NativeSqlType` constraint + +`DB`: Database. Basically always has the `Backend` constraint. + +`QS`: Query Source. Usually doesn't have a constraint, but sometimes will have `QuerySource` attached + +`PK`: Primary Key + +`Lhs`: Left Hand Side + +`Rhs`: Right Hand Side + +`Conn`: Connection + +Generally, we prefer to give our types meaningful names. `Lhs` and `Rhs` vs `T` and `U` for a binary expression, for example. diff --git a/collector/benchmarks/diesel/Cargo.lock b/collector/benchmarks/diesel/Cargo.lock new file mode 100644 index 000000000..5c53405c4 --- /dev/null +++ b/collector/benchmarks/diesel/Cargo.lock @@ -0,0 +1,590 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "aho-corasick" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7404febffaa47dac81aa44dba71523c9d069b1bdc50a77db41195149e17f68e5" +dependencies = [ + "memchr", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "bigdecimal" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc403c26e6b03005522e6e8053384c4e881dfe5b2bf041c0c2c49be33d64a539" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "chrono" +version = "0.4.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "670ad68c9088c2a963aaa298cb369688cf3f9465ce5e2d4ca10e6e0098a1ce73" +dependencies = [ + "libc", + "num-integer", + "num-traits", + "winapi", +] + +[[package]] +name = "cloudabi" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4344512281c643ae7638bbabc3af17a11307803ec8f0fcad9fae512a8bf36467" +dependencies = [ + "bitflags", +] + +[[package]] +name = "diesel" +version = "2.0.0" +dependencies = [ + "bigdecimal", + "bitflags", + "byteorder", + "cfg-if 0.1.10", + "chrono", + "diesel_derives", + "dotenv", + "ipnetwork", + "itoa", + "libc", + "libsqlite3-sys", + "mysqlclient-sys", + "num-bigint", + "num-integer", + "num-traits", + "percent-encoding", + "pq-sys", + "quickcheck", + "r2d2", + "serde_json", + "url", + "uuid", +] + +[[package]] +name = "diesel_derives" +version = "2.0.0" +dependencies = [ + "cfg-if 0.1.10", + "diesel", + "dotenv", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dotenv" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "form_urlencoded" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ece68d15c92e84fa4f19d3780f1294e5ca82a78a6d515f1efaabcc144688be00" +dependencies = [ + "matches", + "percent-encoding", +] + +[[package]] +name = "getrandom" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc587bc0ec293155d5bfa6b9891ec18a1e330c234f896ea47fbada4cadbe47e6" +dependencies = [ + "cfg-if 0.1.10", + "libc", + "wasi", +] + +[[package]] +name = "idna" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "instant" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "ipnetwork" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02c3eaab3ac0ede60ffa41add21970a7df7d91772c03383aac6c2c3d53cc716b" +dependencies = [ + "serde", +] + +[[package]] +name = "itoa" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d58d1b70b004888f764dfbf6a26a3b0342a1632d33968e4a179d8011c760614" + +[[package]] +name = "libsqlite3-sys" +version = "0.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64d31059f22935e6c31830db5249ba2b7ecd54fd73a9909286f0a67aa55c2fbd" +dependencies = [ + "pkg-config", + "vcpkg", +] + +[[package]] +name = "lock_api" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +dependencies = [ + "cfg-if 0.1.10", +] + +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + +[[package]] +name = "memchr" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ee1c47aaa256ecabcaea351eae4a9b01ef39ed810004e298d2511ed284b1525" + +[[package]] +name = "mysqlclient-sys" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e9637d93448044078aaafea7419aed69d301b4a12bcc4aa0ae856eb169bef85" +dependencies = [ + "pkg-config", + "vcpkg", +] + +[[package]] +name = "num-bigint" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e9a41747ae4633fce5adffb4d2e81ffc5e89593cb19917f8fb2cc5ff76507bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290" +dependencies = [ + "autocfg", +] + +[[package]] +name = "parking_lot" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb" +dependencies = [ + "instant", + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c361aa727dd08437f2f1447be8b59a33b0edd15e0fcee698f935613d9efbca9b" +dependencies = [ + "cfg-if 0.1.10", + "cloudabi", + "instant", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pkg-config" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3831453b3449ceb48b6d9c7ad7c96d5ea673e9b470a1dc578c2ce6521230884c" + +[[package]] +name = "ppv-lite86" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac74c624d6b2d21f425f752262f42188365d7b8ff1aff74c82e45136510a4857" + +[[package]] +name = "pq-sys" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ac25eee5a0582f45a67e837e350d784e7003bd29a5f460796772061ca49ffda" +dependencies = [ + "vcpkg", +] + +[[package]] +name = "proc-macro2" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0704ee1a7e00d7bb417d0770ea303c1bccbabf0ef1667dae92b5967f5f8a71" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "quickcheck" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f" +dependencies = [ + "env_logger", + "log", + "rand", + "rand_core", +] + +[[package]] +name = "quote" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r2d2" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "545c5bc2b880973c9c10e4067418407a0ccaa3091781d1671d46eb35107cb26f" +dependencies = [ + "log", + "parking_lot", + "scheduled-thread-pool", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "regex" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38cf2c13ed4745de91a5eb834e11c00bcc3709e773173b2ce4c56c9fbde04b9c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b181ba2dcf07aaccad5448e8ead58db5b742cf85dfe035e2227f137a539a189" + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "scheduled-thread-pool" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6f74fd1204073fa02d5d5d68bec8021be4c38690b61264b2fdb48083d0e7d7" +dependencies = [ + "parking_lot", +] + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "serde" +version = "1.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b88fa983de7720629c9387e9f517353ed404164b1e482c970a90c1a4aaf7dc1a" + +[[package]] +name = "serde_json" +version = "1.0.60" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1500e84d27fe482ed1dc791a56eddc2f230046a040fa908c08bda1d9fb615779" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "smallvec" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7acad6f34eb9e8a259d3283d1e8c1d34d7415943d4895f65cc73813c7396fc85" + +[[package]] +name = "syn" +version = "1.0.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8833e20724c24de12bbaba5ad230ea61c3eafb05b881c7c9d3cfe8638b187e68" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "tinyvec" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf8dbc19eb42fba10e8feaaec282fb50e2c14b2726d6301dbfeed0f73306a6f" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13e63ab62dbe32aeee58d1c5408d35c36c392bba5d9d3142287219721afe606" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "url" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5909f2b0817350449ed73e8bcd81c8c3c8d9a7a5d8acba4b27db277f1868976e" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "uuid" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fde2f6a4bea1d6e007c4ad38c6839fa71cbb63b6dbf5b595aa38dc9b1093c11" + +[[package]] +name = "vcpkg" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6454029bf181f092ad1b853286f23e2c507d8e8194d01d92da4a55c274a5508c" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/collector/benchmarks/diesel/Cargo.toml b/collector/benchmarks/diesel/Cargo.toml new file mode 100644 index 000000000..a52e2d521 --- /dev/null +++ b/collector/benchmarks/diesel/Cargo.toml @@ -0,0 +1,5 @@ +[workspace] +members = [ + "diesel", + "diesel_derives" +] diff --git a/collector/benchmarks/diesel/LICENSE-APACHE b/collector/benchmarks/diesel/LICENSE-APACHE new file mode 100644 index 000000000..164de5d86 --- /dev/null +++ b/collector/benchmarks/diesel/LICENSE-APACHE @@ -0,0 +1,190 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2015-2018 Sean Griffin + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/collector/benchmarks/diesel/LICENSE-MIT b/collector/benchmarks/diesel/LICENSE-MIT new file mode 100644 index 000000000..4e932e5cc --- /dev/null +++ b/collector/benchmarks/diesel/LICENSE-MIT @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2015-2018 Sean Griffin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/collector/benchmarks/diesel/README.md b/collector/benchmarks/diesel/README.md new file mode 100644 index 000000000..76f53668e --- /dev/null +++ b/collector/benchmarks/diesel/README.md @@ -0,0 +1,62 @@ +[![](https://diesel.rs/assets/images/diesel_logo_stacked_black.png)](https://diesel.rs) + +A safe, extensible ORM and Query Builder for Rust +========================================================== +[![Build Status](https://github.com/diesel-rs/diesel/workflows/CI%20Tests/badge.svg)](https://github.com/diesel-rs/diesel/actions?query=workflow%3A%22CI+Tests%22+branch%3Amaster) +[![Gitter](https://badges.gitter.im/diesel-rs/diesel.svg)](https://gitter.im/diesel-rs/diesel?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge) +[![Crates.io](https://img.shields.io/crates/v/diesel.svg)](https://crates.io/crates/diesel) + +API Documentation: [latest release](https://docs.rs/diesel) – [master branch](https://docs.diesel.rs/master/diesel/index.html) + +[Homepage](https://diesel.rs) + +Diesel gets rid of the boilerplate for database interaction and eliminates +runtime errors without sacrificing performance. It takes full advantage of +Rust's type system to create a low overhead query builder that "feels like +Rust." + +Supported databases: +1. [PostgreSQL](https://docs.diesel.rs/diesel/pg/index.html) +2. [MySQL](https://docs.diesel.rs/diesel/mysql/index.html) +3. [SQLite](https://docs.diesel.rs/diesel/sqlite/index.html) + +You can configure the database backend in `Cargo.toml`: +```toml +[dependencies] +diesel = { version = "", features = [""] } +``` + +## Getting Started + +Find our extensive Getting Started tutorial at +[https://diesel.rs/guides/getting-started](https://diesel.rs/guides/getting-started). +Guides on more specific features are coming soon. + +## Getting help +If you run into problems, Diesel has a very active Gitter room. +You can come ask for help at +[gitter.im/diesel-rs/diesel](https://gitter.im/diesel-rs/diesel). +For help with longer questions and discussion about the future of Diesel, +visit our [discourse](https://discourse.diesel.rs/) forum. + +## Code of conduct + +Anyone who interacts with Diesel in any space, including but not limited to +this GitHub repository, must follow our [code of conduct](https://github.com/diesel-rs/diesel/blob/master/code_of_conduct.md). + +## License + +Licensed under either of these: + + * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or + https://www.apache.org/licenses/LICENSE-2.0) + * MIT license ([LICENSE-MIT](LICENSE-MIT) or + https://opensource.org/licenses/MIT) + +### Contributing +Before contributing, please read the [contributors guide](https://github.com/diesel-rs/diesel/blob/master/CONTRIBUTING.md) +for useful information about setting up Diesel locally, coding style and common abbreviations. + +Unless you explicitly state otherwise, any contribution you intentionally submit +for inclusion in the work, as defined in the Apache-2.0 license, shall be +dual-licensed as above, without any additional terms or conditions. diff --git a/collector/benchmarks/diesel/clippy.toml b/collector/benchmarks/diesel/clippy.toml new file mode 100644 index 000000000..ab165574d --- /dev/null +++ b/collector/benchmarks/diesel/clippy.toml @@ -0,0 +1,10 @@ +cognitive-complexity-threshold = 30 +doc-valid-idents = [ + "MiB", "GiB", "TiB", "PiB", "EiB", + "DirectX", "OpenGL", "TrueType", + "GPLv2", "GPLv3", + "GitHub", + "IPv4", "IPv6", + "JavaScript", "NaN", "OAuth", + "SQLite", "PostgreSQL", "MySQL" +] diff --git a/collector/benchmarks/diesel/code_of_conduct.md b/collector/benchmarks/diesel/code_of_conduct.md new file mode 100644 index 000000000..0374fa064 --- /dev/null +++ b/collector/benchmarks/diesel/code_of_conduct.md @@ -0,0 +1,80 @@ +# Contributor Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting a project maintainer at: + +* Sean Griffin +* Pascal Hertleif +* Bastien Orivel + +All complaints will be reviewed and investigated and will result in a response +that is deemed necessary and appropriate to the circumstances. The project team +is obligated to maintain confidentiality with regard to the reporter of an +incident. Further details of specific enforcement policies may be posted +separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [https://contributor-covenant.org/version/1/4][version] + +[homepage]: https://contributor-covenant.org +[version]: https://contributor-covenant.org/version/1/4/ diff --git a/collector/benchmarks/diesel/diesel/Cargo.toml b/collector/benchmarks/diesel/diesel/Cargo.toml new file mode 100644 index 000000000..4fec150f3 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/Cargo.toml @@ -0,0 +1,65 @@ +[package] +name = "diesel" +version = "2.0.0" +authors = ["Sean Griffin "] +license = "MIT OR Apache-2.0" +description = "A safe, extensible ORM and Query Builder for PostgreSQL, SQLite, and MySQL" +readme = "README.md" +documentation = "https://docs.rs/diesel/" +homepage = "https://diesel.rs" +repository = "https://github.com/diesel-rs/diesel" +keywords = ["orm", "database", "blockchain", "sql"] +categories = ["database"] +edition = "2018" + +[dependencies] +byteorder = "1.0" +chrono = { version = "0.4.19", optional = true, default-features = false, features = ["clock", "std"] } +libc = { version = "0.2.0", optional = true } +libsqlite3-sys = { version = ">=0.8.0, <0.21.0", optional = true, features = ["min_sqlite_version_3_7_16"] } +mysqlclient-sys = { version = "0.2.0", optional = true } +pq-sys = { version = "0.4.0", optional = true } +quickcheck = { version = "0.9.0", optional = true } +serde_json = { version = ">=0.8.0, <2.0", optional = true } +url = { version = "2.1.0", optional = true } +percent-encoding = { version = "2.1.0", optional = true } +uuid = { version = ">=0.7.0, <0.9.0", optional = true} +ipnetwork = { version = ">=0.12.2, <0.18.0", optional = true } +num-bigint = { version = ">=0.2.0, <0.4.0", optional = true } +num-traits = { version = "0.2.0", optional = true } +num-integer = { version = "0.1.39", optional = true } +bigdecimal = { version = ">=0.0.13, < 0.3.0", optional = true } +bitflags = { version = "1.2.0", optional = true } +r2d2 = { version = ">= 0.8.0, < 0.9.0", optional = true } +itoa = "0.4.0" + +[dependencies.diesel_derives] +version = "~2.0.0" +path = "../diesel_derives" + +[dev-dependencies] +cfg-if = "0.1.10" +dotenv = "0.15" +ipnetwork = ">=0.12.2, <0.18.0" +quickcheck = "0.9" + +[features] +default = ["32-column-tables"] +extras = ["chrono", "serde_json", "uuid", "network-address", "numeric", "r2d2"] +unstable = ["diesel_derives/nightly"] +large-tables = ["32-column-tables"] +huge-tables = ["64-column-tables"] +32-column-tables = [] +64-column-tables = ["32-column-tables"] +128-column-tables = ["64-column-tables"] +postgres = ["pq-sys", "bitflags", "diesel_derives/postgres"] +sqlite = ["libsqlite3-sys", "diesel_derives/sqlite"] +mysql = ["mysqlclient-sys", "url", "percent-encoding", "diesel_derives/mysql", "bitflags"] +without-deprecated = [] +with-deprecated = [] +network-address = ["ipnetwork", "libc"] +numeric = ["num-bigint", "bigdecimal", "num-traits", "num-integer"] + +[package.metadata.docs.rs] +features = ["postgres", "mysql", "sqlite", "extras"] +no-default-features = true diff --git a/collector/benchmarks/diesel/diesel/LICENSE-APACHE b/collector/benchmarks/diesel/diesel/LICENSE-APACHE new file mode 120000 index 000000000..965b606f3 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/collector/benchmarks/diesel/diesel/LICENSE-MIT b/collector/benchmarks/diesel/diesel/LICENSE-MIT new file mode 120000 index 000000000..76219eb72 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/collector/benchmarks/diesel/diesel/README.md b/collector/benchmarks/diesel/diesel/README.md new file mode 100644 index 000000000..0e150e8d3 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/README.md @@ -0,0 +1,9 @@ +[![Diesel](https://diesel.rs/assets/images/diesel_logo_stacked_black.png)](https://diesel.rs) + +# Diesel - A safe, extensible ORM and Query Builder for Rust + +Diesel is the most productive way to interact with databases in Rust because of its safe and composable abstractions over queries. + +## Getting Started + +This is the Readme of the main crate. You can find [an extensive Getting Started tutorial](https://diesel.rs/guides/getting-started) and more information on our [website](https://diesel.rs). diff --git a/collector/benchmarks/diesel/diesel/src/associations/belongs_to.rs b/collector/benchmarks/diesel/diesel/src/associations/belongs_to.rs new file mode 100644 index 000000000..377c01ad0 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/associations/belongs_to.rs @@ -0,0 +1,178 @@ +use super::{HasTable, Identifiable}; +use crate::dsl::{Eq, EqAny, Filter, FindBy}; +use crate::expression::array_comparison::AsInExpression; +use crate::expression::AsExpression; +use crate::prelude::*; +use crate::query_dsl::methods::FilterDsl; +use crate::sql_types::SqlType; + +use std::borrow::Borrow; +use std::hash::Hash; + +/// Indicates that a type belongs to `Parent` +/// +/// Specifically, this means that this struct has fields +/// which correspond to the primary key of `Parent`. +/// This implies that a foreign key relationship exists on the tables. +/// +/// This trait is not capable of supporting composite foreign keys +pub trait BelongsTo { + /// The foreign key of this struct + type ForeignKey: Hash + ::std::cmp::Eq; + /// The database column representing the foreign key + /// of the table this struct represents + type ForeignKeyColumn: Column; + + /// Returns the foreign key for `self` + fn foreign_key(&self) -> Option<&Self::ForeignKey>; + /// Returns the foreign key column of this struct's table + fn foreign_key_column() -> Self::ForeignKeyColumn; +} + +/// The `grouped_by` function groups records by their parent. +/// +/// `grouped_by` is called on a `Vec` with a `&[Parent]`. +/// The return value will be `Vec>` indexed to match their parent. +/// Or to put it another way, the returned data can be passed to `zip`, +/// and it will be combined with its parent. +/// This function does not generate a `GROUP BY` SQL statement, +/// as it operates on data structures already loaded from the database +/// +/// **Child** refers to the "many" part of a "one to many" relationship. It "belongs to" its parent +/// **Parent** refers to the "one" part of a "one to many" relationship and can "have many" children. +/// The child always has a foreign key, which refers to its parent's primary key. +/// In the following relationship, User has many Posts, +/// so User is the parent and Posts are children. +/// +/// # Example +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # use schema::{posts, users}; +/// # +/// # #[derive(Identifiable, Queryable, PartialEq, Debug)] +/// # pub struct User { +/// # id: i32, +/// # name: String, +/// # } +/// # +/// # #[derive(Debug, PartialEq)] +/// # #[derive(Identifiable, Queryable, Associations)] +/// # #[belongs_to(User)] +/// # pub struct Post { +/// # id: i32, +/// # user_id: i32, +/// # title: String, +/// # } +/// # +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # let connection = establish_connection(); +/// let users = users::table.load::(&connection)?; +/// let posts = Post::belonging_to(&users) +/// .load::(&connection)? +/// .grouped_by(&users); +/// let data = users.into_iter().zip(posts).collect::>(); +/// +/// let expected_data = vec![ +/// ( +/// User { id: 1, name: "Sean".into() }, +/// vec![ +/// Post { id: 1, user_id: 1, title: "My first post".into() }, +/// Post { id: 2, user_id: 1, title: "About Rust".into() }, +/// ], +/// ), +/// ( +/// User { id: 2, name: "Tess".into() }, +/// vec![ +/// Post { id: 3, user_id: 2, title: "My first post too".into() }, +/// ], +/// ), +/// ]; +/// +/// assert_eq!(expected_data, data); +/// # Ok(()) +/// # } +/// ``` +/// +/// See [the module documentation] for more examples +/// +/// [the module documentation]: index.html +pub trait GroupedBy<'a, Parent>: IntoIterator + Sized { + /// See the trait documentation. + fn grouped_by(self, parents: &'a [Parent]) -> Vec>; +} + +type Id = ::Id; + +impl<'a, Parent: 'a, Child, Iter> GroupedBy<'a, Parent> for Iter +where + Iter: IntoIterator, + Child: BelongsTo, + &'a Parent: Identifiable, + Id<&'a Parent>: Borrow, +{ + fn grouped_by(self, parents: &'a [Parent]) -> Vec> { + use std::collections::HashMap; + + let id_indices: HashMap<_, _> = parents + .iter() + .enumerate() + .map(|(i, u)| (u.id(), i)) + .collect(); + let mut result = parents.iter().map(|_| Vec::new()).collect::>(); + for child in self { + if let Some(index) = child.foreign_key().map(|i| id_indices[i]) { + result[index].push(child); + } + } + result + } +} + +impl<'a, Parent, Child> BelongingToDsl<&'a Parent> for Child +where + &'a Parent: Identifiable, + Child: HasTable + BelongsTo, + Id<&'a Parent>: AsExpression<::SqlType>, + Child::Table: FilterDsl>>, + Child::ForeignKeyColumn: ExpressionMethods, + ::SqlType: SqlType, +{ + type Output = FindBy>; + + fn belonging_to(parent: &'a Parent) -> Self::Output { + FilterDsl::filter(Child::table(), Child::foreign_key_column().eq(parent.id())) + } +} + +impl<'a, Parent, Child> BelongingToDsl<&'a [Parent]> for Child +where + &'a Parent: Identifiable, + Child: HasTable + BelongsTo, + Vec>: AsInExpression<::SqlType>, + ::Table: FilterDsl>>>, + Child::ForeignKeyColumn: ExpressionMethods, + ::SqlType: SqlType, +{ + type Output = Filter>>>; + + fn belonging_to(parents: &'a [Parent]) -> Self::Output { + let ids = parents.iter().map(Identifiable::id).collect::>(); + FilterDsl::filter(Child::table(), Child::foreign_key_column().eq_any(ids)) + } +} + +impl<'a, Parent, Child> BelongingToDsl<&'a Vec> for Child +where + Child: BelongingToDsl<&'a [Parent]>, +{ + type Output = Child::Output; + + fn belonging_to(parents: &'a Vec) -> Self::Output { + Self::belonging_to(&**parents) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/associations/mod.rs b/collector/benchmarks/diesel/diesel/src/associations/mod.rs new file mode 100644 index 000000000..c04fe0442 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/associations/mod.rs @@ -0,0 +1,413 @@ +//! Traits related to relationships between multiple tables. +//! +//! Associations in Diesel are always child-to-parent. +//! You can declare an association between two records with `#[belongs_to]`. +//! Unlike other ORMs, Diesel has no concept of `has many` +//! +//! ```rust +//! # include!("../doctest_setup.rs"); +//! use schema::{posts, users}; +//! +//! #[derive(Identifiable, Queryable, PartialEq, Debug)] +//! #[table_name = "users"] +//! pub struct User { +//! id: i32, +//! name: String, +//! } +//! +//! #[derive(Identifiable, Queryable, Associations, PartialEq, Debug)] +//! #[belongs_to(User)] +//! #[table_name = "posts"] +//! pub struct Post { +//! id: i32, +//! user_id: i32, +//! title: String, +//! } +//! +//! # fn main() { +//! # run_test().unwrap(); +//! # } +//! # +//! # fn run_test() -> QueryResult<()> { +//! # let connection = establish_connection(); +//! # use self::users::dsl::*; +//! let user = users.find(2).get_result::(&connection)?; +//! let users_post = Post::belonging_to(&user) +//! .first(&connection)?; +//! let expected = Post { id: 3, user_id: 2, title: "My first post too".into() }; +//! assert_eq!(expected, users_post); +//! # Ok(()) +//! # } +//! ``` +//! +//! Note that in addition to the `#[belongs_to]` annotation, we also need to +//! `#[derive(Associations)]` +//! +//! `#[belongs_to]` is given the name of the struct that represents the parent. +//! Both the parent and child must implement [`Identifiable`]. +//! The struct given to `#[belongs_to]` must be in scope, +//! so you will need `use some_module::User` if `User` is defined in another module. +//! +//! If the parent record is generic over lifetimes, they can be written as `'_`. +//! You will also need to wrap the type in quotes until +//! `unrestricted_attribute_tokens` is stable. +//! +//! ```rust +//! # include!("../doctest_setup.rs"); +//! # use schema::{posts, users}; +//! # use std::borrow::Cow; +//! # +//! #[derive(Identifiable)] +//! #[table_name = "users"] +//! pub struct User<'a> { +//! id: i32, +//! name: Cow<'a, str>, +//! } +//! +//! #[derive(Associations)] +//! #[belongs_to(parent = "User<'_>")] +//! #[table_name = "posts"] +//! pub struct Post { +//! id: i32, +//! user_id: i32, +//! title: String, +//! } +//! # +//! # fn main() {} +//! ``` +//! +//! [`Identifiable`]: trait.Identifiable.html +//! +//! By default, Diesel assumes that your foreign keys will follow the convention `table_name_id`. +//! If your foreign key has a different name, +//! you can provide the `foreign_key` argument to `#[belongs_to]`. +//! For example, `#[belongs_to(Foo, foreign_key = "mykey")]`. +//! +//! Associated data is typically loaded in multiple queries (one query per table). +//! This is usually more efficient than using a join, +//! especially if 3 or more tables are involved. +//! For most datasets, +//! using a join to load in a single query transmits so much duplicate data +//! that it costs more time than the extra round trip would have. +//! +//! You can load the children for one or more parents using +//! [`belonging_to`] +//! +//! [`belonging_to`]: ../query_dsl/trait.BelongingToDsl.html#tymethod.belonging_to +//! +//! ```rust +//! # include!("../doctest_setup.rs"); +//! # use schema::users; +//! # use schema::posts; +//! # +//! # #[derive(Debug, PartialEq, Identifiable, Queryable)] +//! # pub struct User { +//! # id: i32, +//! # name: String, +//! # } +//! # +//! # #[derive(Debug, PartialEq, Identifiable, Queryable, Associations)] +//! # #[belongs_to(User)] +//! # pub struct Post { +//! # id: i32, +//! # user_id: i32, +//! # title: String, +//! # } +//! # +//! # fn main() { +//! # use self::users::dsl::*; +//! # let connection = establish_connection(); +//! # +//! let user = users.find(1).first::(&connection).expect("Error loading user"); +//! let post_list = Post::belonging_to(&user) +//! .load::(&connection) +//! .expect("Error loading posts"); +//! let expected = vec![ +//! Post { id: 1, user_id: 1, title: "My first post".to_string() }, +//! Post { id: 2, user_id: 1, title: "About Rust".to_string() }, +//! ]; +//! +//! assert_eq!(post_list, expected); +//! # } +//! ``` +//! +//! If you're coming from other ORMs, you'll notice that this design is quite different from most. +//! There you would have an instance method on the parent, or have the children stored somewhere on +//! the posts. This design leads to many problems, including [N+1 query +//! bugs][load-your-entire-database-into-memory-lol], and runtime errors when accessing an +//! association that isn't there. +//! +//! [load-your-entire-database-into-memory-lol]: https://stackoverflow.com/q/97197/1254484 +//! +//! In Diesel, data and its associations are considered to be separate. If you want to pass around +//! a user and all of its posts, that type is `(User, Vec)`. +//! +//! Next lets look at how to load the children for more than one parent record. +//! [`belonging_to`] can be used to load the data, but we'll also need to group it +//! with its parents. For this we use an additional method [`grouped_by`]. +//! +//! [`grouped_by`]: trait.GroupedBy.html#tymethod.grouped_by +//! [`belonging_to`]: ../query_dsl/trait.BelongingToDsl.html#tymethod.belonging_to +//! +//! ```rust +//! # include!("../doctest_setup.rs"); +//! # use schema::{posts, users}; +//! # +//! # #[derive(Identifiable, Queryable)] +//! # pub struct User { +//! # id: i32, +//! # name: String, +//! # } +//! # +//! # #[derive(Debug, PartialEq)] +//! # #[derive(Identifiable, Queryable, Associations)] +//! # #[belongs_to(User)] +//! # pub struct Post { +//! # id: i32, +//! # user_id: i32, +//! # title: String, +//! # } +//! # +//! # fn main() { +//! # run_test(); +//! # } +//! # +//! # fn run_test() -> QueryResult<()> { +//! # let connection = establish_connection(); +//! # use self::users::dsl::*; +//! # use self::posts::dsl::{posts, title}; +//! let sean = users.filter(name.eq("Sean")).first::(&connection)?; +//! let tess = users.filter(name.eq("Tess")).first::(&connection)?; +//! +//! let seans_posts = Post::belonging_to(&sean) +//! .select(title) +//! .load::(&connection)?; +//! assert_eq!(vec!["My first post", "About Rust"], seans_posts); +//! +//! // A vec or slice can be passed as well +//! let more_posts = Post::belonging_to(&vec![sean, tess]) +//! .select(title) +//! .load::(&connection)?; +//! assert_eq!(vec!["My first post", "About Rust", "My first post too"], more_posts); +//! # Ok(()) +//! # } +//! ``` +//! +//! Typically you will want to group up the children with their parents. +//! In other ORMs, this is often called a `has_many` relationship. +//! Diesel provides support for doing this grouping, once the data has been +//! loaded. +//! +//! [`grouped_by`] is called on a `Vec` with a `&[Parent]`. +//! The return value will be `Vec>` indexed to match their parent. +//! Or to put it another way, the returned data can be passed to `zip`, +//! and it will be combined with its parent. +//! +//! ```rust +//! # include!("../doctest_setup.rs"); +//! # use schema::{posts, users}; +//! # +//! # #[derive(Identifiable, Queryable, PartialEq, Debug)] +//! # pub struct User { +//! # id: i32, +//! # name: String, +//! # } +//! # +//! # #[derive(Debug, PartialEq)] +//! # #[derive(Identifiable, Queryable, Associations)] +//! # #[belongs_to(User)] +//! # pub struct Post { +//! # id: i32, +//! # user_id: i32, +//! # title: String, +//! # } +//! # +//! # fn main() { +//! # run_test(); +//! # } +//! # +//! # fn run_test() -> QueryResult<()> { +//! # let connection = establish_connection(); +//! let users = users::table.load::(&connection)?; +//! let posts = Post::belonging_to(&users) +//! .load::(&connection)? +//! .grouped_by(&users); +//! let data = users.into_iter().zip(posts).collect::>(); +//! +//! let expected_data = vec![ +//! ( +//! User { id: 1, name: "Sean".into() }, +//! vec![ +//! Post { id: 1, user_id: 1, title: "My first post".into() }, +//! Post { id: 2, user_id: 1, title: "About Rust".into() }, +//! ], +//! ), +//! ( +//! User { id: 2, name: "Tess".into() }, +//! vec![ +//! Post { id: 3, user_id: 2, title: "My first post too".into() }, +//! ], +//! ), +//! ]; +//! +//! assert_eq!(expected_data, data); +//! # Ok(()) +//! # } +//! ``` +//! +//! [`grouped_by`] can be called multiple times +//! if you have multiple children or grandchildren. +//! +//! For example, this code will load some users, +//! all of their posts, +//! and all of the comments on those posts. +//! Explicit type annotations have been added +//! to make each line a bit more clear. +//! +//! ```rust +//! # include!("../doctest_setup.rs"); +//! # use schema::{users, posts, comments}; +//! # +//! # #[derive(Debug, PartialEq, Identifiable, Queryable)] +//! # pub struct User { +//! # id: i32, +//! # name: String, +//! # } +//! # +//! # #[derive(Debug, PartialEq, Identifiable, Queryable, Associations)] +//! # #[belongs_to(User)] +//! # pub struct Post { +//! # id: i32, +//! # user_id: i32, +//! # title: String, +//! # } +//! # +//! # #[derive(Debug, PartialEq, Identifiable, Queryable, Associations)] +//! # #[belongs_to(Post)] +//! # pub struct Comment { +//! # id: i32, +//! # post_id: i32, +//! # body: String, +//! # } +//! # +//! # fn main() { +//! # let connection = establish_connection(); +//! # +//! let users: Vec = users::table.load::(&connection) +//! .expect("error loading users"); +//! let posts: Vec = Post::belonging_to(&users) +//! .load::(&connection) +//! .expect("error loading posts"); +//! let comments: Vec = Comment::belonging_to(&posts) +//! .load::(&connection) +//! .expect("Error loading comments"); +//! let grouped_comments: Vec> = comments.grouped_by(&posts); +//! let posts_and_comments: Vec)>> = posts +//! .into_iter() +//! .zip(grouped_comments) +//! .grouped_by(&users); +//! let result: Vec<(User, Vec<(Post, Vec)>)> = users +//! .into_iter() +//! .zip(posts_and_comments) +//! .collect(); +//! let expected = vec![ +//! ( +//! User { id: 1, name: "Sean".to_string() }, +//! vec![ +//! ( +//! Post { id: 1, user_id: 1, title: "My first post".to_string() }, +//! vec![ Comment { id: 1, post_id: 1, body: "Great post".to_string() } ] +//! ), +//! ( +//! Post { id: 2, user_id: 1, title: "About Rust".to_string() }, +//! vec![ +//! Comment { id: 2, post_id: 2, body: "Yay! I am learning Rust".to_string() } +//! ] +//! +//! ) +//! ] +//! ), +//! ( +//! User { id: 2, name: "Tess".to_string() }, +//! vec![ +//! ( +//! Post { id: 3, user_id: 2, title: "My first post too".to_string() }, +//! vec![ Comment { id: 3, post_id: 3, body: "I enjoyed your post".to_string() } ] +//! ) +//! ] +//! ) +//! ]; +//! +//! assert_eq!(result, expected); +//! # } +//! ``` +//! +//! And that's it. +//! It may seem odd to have load, group, and zip be explicit separate steps +//! if you are coming from another ORM. +//! However, the goal is to provide simple building blocks which can +//! be used to construct the complex behavior applications need. +mod belongs_to; + +use std::hash::Hash; + +use crate::query_source::Table; + +pub use self::belongs_to::{BelongsTo, GroupedBy}; + +#[doc(inline)] +pub use diesel_derives::Associations; + +/// This trait indicates that a struct is associated with a single database table. +/// +/// This trait is implemented by structs which implement `Identifiable`, +/// as well as database tables themselves. +pub trait HasTable { + /// The table this type is associated with. + type Table: Table; + + /// Returns the table this type is associated with. + fn table() -> Self::Table; +} + +impl<'a, T: HasTable> HasTable for &'a T { + type Table = T::Table; + + fn table() -> Self::Table { + T::table() + } +} + +/// This trait indicates that a struct represents a single row in a database table. +/// +/// This must be implemented to use associations. +/// Additionally, implementing this trait allows you to pass your struct to `update` +/// (`update(&your_struct)` is equivalent to +/// `update(YourStruct::table().find(&your_struct.primary_key())`). +/// +/// This trait is usually implemented on a reference to a struct, +/// not on the struct itself. It can be [derived](derive.Identifiable.html). +/// +pub trait Identifiable: HasTable { + /// The type of this struct's identifier. + /// + /// For single-field primary keys, this is typically `&'a i32`, or `&'a String` + /// For composite primary keys, this is typically `(&'a i32, &'a i32)` + /// or `(&'a String, &'a String)`, etc. + type Id: Hash + Eq; + + /// Returns the identifier for this record. + /// + /// This takes `self` by value, not reference. + /// This is because composite primary keys + /// are typically stored as multiple fields. + /// We could not return `&(String, String)` if each string is a separate field. + /// + /// Because of Rust's rules about specifying lifetimes, + /// this means that `Identifiable` is usually implemented on references + /// so that we have a lifetime to use for `Id`. + fn id(self) -> Self::Id; +} + +#[doc(inline)] +pub use diesel_derives::Identifiable; diff --git a/collector/benchmarks/diesel/diesel/src/backend.rs b/collector/benchmarks/diesel/diesel/src/backend.rs new file mode 100644 index 000000000..d0c025c55 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/backend.rs @@ -0,0 +1,82 @@ +//! Types which represent various database backends + +use byteorder::ByteOrder; + +use crate::query_builder::bind_collector::BindCollector; +use crate::query_builder::QueryBuilder; +use crate::sql_types::{self, HasSqlType}; + +/// A database backend +/// +/// This trait represents the concept of a backend (e.g. "MySQL" vs "SQLite"). +/// It is separate from a [`Connection`](../connection/trait.Connection.html) +/// to that backend. +/// One backend may have multiple concrete connection implementations. +/// +/// Implementations of this trait should not assume details about how the +/// connection is implemented. +/// For example, the `Pg` backend does not assume that `libpq` is being used. +/// Implementations of this trait can and should care about details of the wire +/// protocol used to communicated with the database. +pub trait Backend +where + Self: Sized, + Self: HasSqlType, + Self: HasSqlType, + Self: HasSqlType, + Self: HasSqlType, + Self: HasSqlType, + Self: HasSqlType, + Self: HasSqlType, + Self: HasSqlType, + Self: HasSqlType, + Self: HasSqlType, + Self: HasSqlType, + Self: for<'a> HasRawValue<'a>, +{ + /// The concrete `QueryBuilder` implementation for this backend. + type QueryBuilder: QueryBuilder; + /// The concrete `BindCollector` implementation for this backend. + /// + /// Most backends should use [`RawBytesBindCollector`]. + /// + /// [`RawBytesBindCollector`]: ../query_builder/bind_collector/struct.RawBytesBindCollector.html + type BindCollector: BindCollector; + /// What byte order is used to transmit integers? + /// + /// This type is only used if `RawValue` is `[u8]`. + type ByteOrder: ByteOrder; +} + +/// The raw representation of a database value given to `FromSql`. +/// +/// This trait is separate from `Backend` to imitate `type RawValue<'a>`. It +/// should only be referenced directly by implementors. Users of this type +/// should instead use the [`RawValue`](type.RawValue.html) helper type instead. +pub trait HasRawValue<'a> { + /// The actual type given to `FromSql`, with lifetimes applied. This type + /// should not be used directly. Use the [`RawValue`](type.RawValue.html) + /// helper type instead. + type RawValue; +} + +/// A trait indicating that the provided raw value uses a binary representation internally +pub trait BinaryRawValue<'a>: HasRawValue<'a> { + /// Get the underlying binary representation of the raw value + fn as_bytes(value: Self::RawValue) -> &'a [u8]; +} + +/// A helper type to get the raw representation of a database type given to +/// `FromSql`. Equivalent to `::RawValue<'a>`. +pub type RawValue<'a, DB> = >::RawValue; + +/// Does this backend support `RETURNING` clauses? +pub trait SupportsReturningClause {} +/// Does this backend support 'ON CONFLICT' clause? +pub trait SupportsOnConflictClause {} +/// Does this backend support 'WHERE' clauses on 'ON CONFLICT' clauses? +pub trait SupportsOnConflictTargetDecorations {} +/// Does this backend support the bare `DEFAULT` keyword? +pub trait SupportsDefaultKeyword {} +/// Does this backend use the standard `SAVEPOINT` syntax? +pub trait UsesAnsiSavepointSyntax {} diff --git a/collector/benchmarks/diesel/diesel/src/connection/mod.rs b/collector/benchmarks/diesel/diesel/src/connection/mod.rs new file mode 100644 index 000000000..de1cbff09 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/connection/mod.rs @@ -0,0 +1,186 @@ +//! Types related to database connections + +mod statement_cache; +mod transaction_manager; + +use std::fmt::Debug; + +use crate::backend::Backend; +use crate::deserialize::FromSqlRow; +use crate::expression::QueryMetadata; +use crate::query_builder::{AsQuery, QueryFragment, QueryId}; +use crate::result::*; + +#[doc(hidden)] +pub use self::statement_cache::{MaybeCached, StatementCache, StatementCacheKey}; +pub use self::transaction_manager::{AnsiTransactionManager, TransactionManager}; + +/// Perform simple operations on a backend. +/// +/// You should likely use [`Connection`](trait.Connection.html) instead. +pub trait SimpleConnection { + /// Execute multiple SQL statements within the same string. + /// + /// This function is used to execute migrations, + /// which may contain more than one SQL statement. + fn batch_execute(&self, query: &str) -> QueryResult<()>; +} + +/// A connection to a database +pub trait Connection: SimpleConnection + Sized + Send { + /// The backend this type connects to + type Backend: Backend; + #[doc(hidden)] + type TransactionManager: TransactionManager; + + /// Establishes a new connection to the database + /// + /// The argument to this method varies by backend. + /// See the documentation for that backend's connection class + /// for details about what it accepts. + fn establish(database_url: &str) -> ConnectionResult; + + /// Executes the given function inside of a database transaction + /// + /// If there is already an open transaction, + /// savepoints will be used instead. + /// + /// If the transaction fails to commit due to a `SerializationFailure` or a + /// `ReadOnlyTransaction` a rollback will be attempted. If the rollback succeeds, + /// the original error will be returned, otherwise the error generated by the rollback + /// will be returned. In the second case the connection should be considered broken + /// as it contains a uncommitted unabortable open transaction. + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// use diesel::result::Error; + /// + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let conn = establish_connection(); + /// conn.transaction::<_, Error, _>(|| { + /// diesel::insert_into(users) + /// .values(name.eq("Ruby")) + /// .execute(&conn)?; + /// + /// let all_names = users.select(name).load::(&conn)?; + /// assert_eq!(vec!["Sean", "Tess", "Ruby"], all_names); + /// + /// Ok(()) + /// })?; + /// + /// conn.transaction::<(), _, _>(|| { + /// diesel::insert_into(users) + /// .values(name.eq("Pascal")) + /// .execute(&conn)?; + /// + /// let all_names = users.select(name).load::(&conn)?; + /// assert_eq!(vec!["Sean", "Tess", "Ruby", "Pascal"], all_names); + /// + /// // If we want to roll back the transaction, but don't have an + /// // actual error to return, we can return `RollbackTransaction`. + /// Err(Error::RollbackTransaction) + /// }); + /// + /// let all_names = users.select(name).load::(&conn)?; + /// assert_eq!(vec!["Sean", "Tess", "Ruby"], all_names); + /// # Ok(()) + /// # } + /// ``` + fn transaction(&self, f: F) -> Result + where + F: FnOnce() -> Result, + E: From, + { + let transaction_manager = self.transaction_manager(); + transaction_manager.begin_transaction(self)?; + match f() { + Ok(value) => { + transaction_manager.commit_transaction(self)?; + Ok(value) + } + Err(e) => { + transaction_manager.rollback_transaction(self)?; + Err(e) + } + } + } + + /// Creates a transaction that will never be committed. This is useful for + /// tests. Panics if called while inside of a transaction. + fn begin_test_transaction(&self) -> QueryResult<()> { + let transaction_manager = self.transaction_manager(); + assert_eq!(transaction_manager.get_transaction_depth(), 0); + transaction_manager.begin_transaction(self) + } + + /// Executes the given function inside a transaction, but does not commit + /// it. Panics if the given function returns an error. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// use diesel::result::Error; + /// + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let conn = establish_connection(); + /// conn.test_transaction::<_, Error, _>(|| { + /// diesel::insert_into(users) + /// .values(name.eq("Ruby")) + /// .execute(&conn)?; + /// + /// let all_names = users.select(name).load::(&conn)?; + /// assert_eq!(vec!["Sean", "Tess", "Ruby"], all_names); + /// + /// Ok(()) + /// }); + /// + /// // Even though we returned `Ok`, the transaction wasn't committed. + /// let all_names = users.select(name).load::(&conn)?; + /// assert_eq!(vec!["Sean", "Tess"], all_names); + /// # Ok(()) + /// # } + /// ``` + fn test_transaction(&self, f: F) -> T + where + F: FnOnce() -> Result, + E: Debug, + { + let mut user_result = None; + let _ = self.transaction::<(), _, _>(|| { + user_result = f().ok(); + Err(Error::RollbackTransaction) + }); + user_result.expect("Transaction did not succeed") + } + + #[doc(hidden)] + fn execute(&self, query: &str) -> QueryResult; + + #[doc(hidden)] + fn load(&self, source: T) -> QueryResult> + where + T: AsQuery, + T::Query: QueryFragment + QueryId, + U: FromSqlRow, + Self::Backend: QueryMetadata; + + #[doc(hidden)] + fn execute_returning_count(&self, source: &T) -> QueryResult + where + T: QueryFragment + QueryId; + + #[doc(hidden)] + fn transaction_manager(&self) -> &Self::TransactionManager; +} diff --git a/collector/benchmarks/diesel/diesel/src/connection/statement_cache.rs b/collector/benchmarks/diesel/diesel/src/connection/statement_cache.rs new file mode 100644 index 000000000..614105a57 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/connection/statement_cache.rs @@ -0,0 +1,256 @@ +#![doc(hidden)] +//! A primer on prepared statement caching in Diesel +//! ------------------------------------------------ +//! +//! Diesel uses prepared statements for virtually all queries. This is most +//! visible in our lack of any sort of "quoting" API. Values must always be +//! transmitted as bind parameters, we do not support direct interpolation. The +//! only method in the public API that doesn't require the use of prepared +//! statements is `Connection#batch_execute`. +//! +//! In order to avoid the cost of re-parsing and planning subsequent queries, +//! Diesel caches the prepared statement whenever possible. Queries will fall +//! into one of three buckets: +//! +//! - Unsafe to cache +//! - Cached by SQL +//! - Cached by type +//! +//! A query is considered unsafe to cache if it represents a potentially +//! unbounded number of queries. This is communicated to the connection through +//! `QueryFragment#is_safe_to_cache_prepared`. While this is done as a full AST +//! pass, after monomorphisation and inlining this will usually be optimized to +//! a constant. Only boxed queries will need to do actual work to answer this +//! question. +//! +//! The majority of AST nodes are safe to cache if their components are safe to +//! cache. There are at least 4 cases where a query is unsafe to cache: +//! +//! - queries containing `IN` with bind parameters +//! - This requires 1 bind parameter per value, and is therefore unbounded +//! - `IN` with subselects are cached (assuming the subselect is safe to +//! cache) +//! - `INSERT` statements +//! - The SQL varies based on the number of rows being inserted. We could in +//! theory cache the single row case, but the cost of parsing in a write +//! query is pretty insignificant compared to reads +//! - `UPDATE` statements +//! - Technically it's bounded on "number of optional values being passed to +//! `SET` factorial" but that's still quite high, and not worth caching +//! for the same reason as single row inserts +//! - `SqlLiteral` nodes +//! - We have no way of knowing whether the SQL was generated dynamically or +//! not, so we must assume that it's unbounded +//! +//! For queries which are unsafe to cache, the statement cache will never insert +//! them. They will be prepared and immediately released after use (or in the +//! case of PG they will use the unnamed prepared statement). +//! +//! For statements which are able to be cached, we then have to determine what +//! to use as the cache key. The standard method that virtually all ORMs or +//! database access layers use in the wild is to store the statements in a +//! hash map, using the SQL as the key. +//! +//! However, the majority of queries using Diesel that are safe to cache as +//! prepared statements will be uniquely identified by their type. For these +//! queries, we can bypass the query builder entirely. Since our AST is +//! generally optimized away by the compiler, for these queries the cost of +//! fetching a prepared statement from the cache is the cost of `HashMap::get`, where the key we're fetching by is a compile time constant. For +//! these types, the AST pass to gather the bind parameters will also be +//! optimized to accessing each parameter individually. +//! +//! Determining if a query can be cached by type is the responsibility of the +//! `QueryId` trait. This trait is quite similar to `Any`, but with a few +//! differences: +//! +//! - No `'static` bound +//! - Something being a reference never changes the SQL that is generated, +//! so `&T` has the same query id as `T`. +//! - `Option` instead of `TypeId` +//! - We need to be able to constrain on this trait being implemented, but +//! not all types will actually have a static query id. Hopefully once +//! specialization is stable we can remove the `QueryId` bound and +//! specialize on it instead (or provide a blanket impl for all `T`) +//! - Implementors give a more broad type than `Self` +//! - This really only affects bind parameters. There are 6 different Rust +//! types which can be used for a parameter of type `timestamp`. The same +//! statement can be used regardless of the Rust type, so `Bound` +//! defines its `QueryId` as `Bound`. +//! +//! A type returning `Some(id)` or `None` for its query ID is based on whether +//! the SQL it generates can change without the type changing. At the moment, +//! the only type which is safe to cache as a prepared statement but does not +//! have a static query ID is something which has been boxed. +//! +//! One potential optimization that we don't perform is storing the queries +//! which are cached by type ID in a separate map. Since a type ID is a u64, +//! this would allow us to use a specialized map which knows that there will +//! never be hashing collisions (also known as a perfect hashing function), +//! which would mean lookups are always constant time. However, this would save +//! nanoseconds on an operation that will take microseconds or even +//! milliseconds. + +use std::any::TypeId; +use std::borrow::Cow; +use std::cell::{RefCell, RefMut}; +use std::collections::HashMap; +use std::hash::Hash; +use std::ops::{Deref, DerefMut}; + +use crate::backend::Backend; +use crate::query_builder::*; +use crate::result::QueryResult; + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub struct StatementCache { + pub cache: RefCell, Statement>>, +} + +#[allow(clippy::len_without_is_empty, clippy::new_without_default)] +impl StatementCache +where + DB: Backend, + DB::TypeMetadata: Clone, + DB::QueryBuilder: Default, + StatementCacheKey: Hash + Eq, +{ + pub fn new() -> Self { + StatementCache { + cache: RefCell::new(HashMap::new()), + } + } + + pub fn len(&self) -> usize { + self.cache.borrow().len() + } + + pub fn cached_statement( + &self, + source: &T, + bind_types: &[DB::TypeMetadata], + prepare_fn: F, + ) -> QueryResult> + where + T: QueryFragment + QueryId, + F: FnOnce(&str) -> QueryResult, + { + use std::collections::hash_map::Entry::{Occupied, Vacant}; + + let cache_key = StatementCacheKey::for_source(source, bind_types)?; + + if !source.is_safe_to_cache_prepared()? { + let sql = cache_key.sql(source)?; + return prepare_fn(&sql).map(MaybeCached::CannotCache); + } + + refmut_map_result(self.cache.borrow_mut(), |cache| { + match cache.entry(cache_key) { + Occupied(entry) => Ok(entry.into_mut()), + Vacant(entry) => { + let statement = { + let sql = entry.key().sql(source)?; + prepare_fn(&sql) + }; + + Ok(entry.insert(statement?)) + } + } + }) + .map(MaybeCached::Cached) + } +} + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +pub enum MaybeCached<'a, T: 'a> { + CannotCache(T), + Cached(RefMut<'a, T>), +} + +impl<'a, T> Deref for MaybeCached<'a, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + match *self { + MaybeCached::CannotCache(ref x) => x, + MaybeCached::Cached(ref x) => &**x, + } + } +} + +impl<'a, T> DerefMut for MaybeCached<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + match *self { + MaybeCached::CannotCache(ref mut x) => x, + MaybeCached::Cached(ref mut x) => &mut **x, + } + } +} + +#[doc(hidden)] +#[allow(missing_debug_implementations)] +#[derive(Hash, PartialEq, Eq)] +pub enum StatementCacheKey { + Type(TypeId), + Sql { + sql: String, + bind_types: Vec, + }, +} + +impl StatementCacheKey +where + DB: Backend, + DB::QueryBuilder: Default, + DB::TypeMetadata: Clone, +{ + pub fn for_source(source: &T, bind_types: &[DB::TypeMetadata]) -> QueryResult + where + T: QueryFragment + QueryId, + { + match T::query_id() { + Some(id) => Ok(StatementCacheKey::Type(id)), + None => { + let sql = Self::construct_sql(source)?; + Ok(StatementCacheKey::Sql { + sql: sql, + bind_types: bind_types.into(), + }) + } + } + } + + pub fn sql>(&self, source: &T) -> QueryResult> { + match *self { + StatementCacheKey::Type(_) => Self::construct_sql(source).map(Cow::Owned), + StatementCacheKey::Sql { ref sql, .. } => Ok(Cow::Borrowed(sql)), + } + } + + fn construct_sql>(source: &T) -> QueryResult { + let mut query_builder = DB::QueryBuilder::default(); + source.to_sql(&mut query_builder)?; + Ok(query_builder.finish()) + } +} + +/// Similar to `RefMut::map`, but for functions which return `Result` +/// +/// If we were in Haskell (and if `RefMut` were `Traversable`), this would just be +/// `traverse`. +fn refmut_map_result(mut refmut: RefMut, mapper: F) -> QueryResult> +where + F: FnOnce(&mut T) -> QueryResult<&mut U>, +{ + // We can't just use `RefMut::map` here, since to lift the error out of that + // closure we'd need to return *something*. + // + // Instead we will very briefly convert to a raw pointer to eliminate + // lifetimes from the equation. Ultimately the cast is safe since the input + // and output lifetimes are identical. However, without the raw pointer + // we would have two live mutable references at the same time. + let ptr = mapper(&mut *refmut).map(|x| x as *mut _)?; + Ok(RefMut::map(refmut, |_| unsafe { &mut *ptr })) +} diff --git a/collector/benchmarks/diesel/diesel/src/connection/transaction_manager.rs b/collector/benchmarks/diesel/diesel/src/connection/transaction_manager.rs new file mode 100644 index 000000000..746afb19b --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/connection/transaction_manager.rs @@ -0,0 +1,247 @@ +use crate::backend::UsesAnsiSavepointSyntax; +use crate::connection::{Connection, SimpleConnection}; +use crate::result::{DatabaseErrorKind, Error, QueryResult}; + +/// Manages the internal transaction state for a connection. +/// +/// You will not need to interact with this trait, unless you are writing an +/// implementation of [`Connection`](trait.Connection.html). +pub trait TransactionManager { + /// Begin a new transaction or savepoint + /// + /// If the transaction depth is greater than 0, + /// this should create a savepoint instead. + /// This function is expected to increment the transaction depth by 1. + fn begin_transaction(&self, conn: &Conn) -> QueryResult<()>; + + /// Rollback the inner-most transaction or savepoint + /// + /// If the transaction depth is greater than 1, + /// this should rollback to the most recent savepoint. + /// This function is expected to decrement the transaction depth by 1. + fn rollback_transaction(&self, conn: &Conn) -> QueryResult<()>; + + /// Commit the inner-most transaction or savepoint + /// + /// If the transaction depth is greater than 1, + /// this should release the most recent savepoint. + /// This function is expected to decrement the transaction depth by 1. + fn commit_transaction(&self, conn: &Conn) -> QueryResult<()>; + + /// Fetch the current transaction depth + /// + /// Used to ensure that `begin_test_transaction` is not called when already + /// inside of a transaction. + fn get_transaction_depth(&self) -> u32; +} + +use std::cell::Cell; + +/// An implementation of `TransactionManager` which can be used for backends +/// which use ANSI standard syntax for savepoints such as SQLite and PostgreSQL. +#[allow(missing_debug_implementations)] +#[derive(Default)] +pub struct AnsiTransactionManager { + transaction_depth: Cell, +} + +impl AnsiTransactionManager { + /// Create a new transaction manager + pub fn new() -> Self { + AnsiTransactionManager::default() + } + + fn change_transaction_depth(&self, by: i32, query: QueryResult<()>) -> QueryResult<()> { + if query.is_ok() { + self.transaction_depth + .set(self.transaction_depth.get() + by) + } + query + } + + /// Begin a transaction with custom SQL + /// + /// This is used by connections to implement more complex transaction APIs + /// to set things such as isolation levels. + /// Returns an error if already inside of a transaction. + pub fn begin_transaction_sql(&self, conn: &Conn, sql: &str) -> QueryResult<()> + where + Conn: SimpleConnection, + { + use crate::result::Error::AlreadyInTransaction; + + if self.transaction_depth.get() == 0 { + self.change_transaction_depth(1, conn.batch_execute(sql)) + } else { + Err(AlreadyInTransaction) + } + } +} + +impl TransactionManager for AnsiTransactionManager +where + Conn: Connection, + Conn::Backend: UsesAnsiSavepointSyntax, +{ + fn begin_transaction(&self, conn: &Conn) -> QueryResult<()> { + let transaction_depth = self.transaction_depth.get(); + self.change_transaction_depth( + 1, + if transaction_depth == 0 { + conn.batch_execute("BEGIN") + } else { + conn.batch_execute(&format!("SAVEPOINT diesel_savepoint_{}", transaction_depth)) + }, + ) + } + + fn rollback_transaction(&self, conn: &Conn) -> QueryResult<()> { + let transaction_depth = self.transaction_depth.get(); + self.change_transaction_depth( + -1, + if transaction_depth == 1 { + conn.batch_execute("ROLLBACK") + } else { + conn.batch_execute(&format!( + "ROLLBACK TO SAVEPOINT diesel_savepoint_{}", + transaction_depth - 1 + )) + }, + ) + } + + /// If the transaction fails to commit due to a `SerializationFailure` or a + /// `ReadOnlyTransaction` a rollback will be attempted. If the rollback succeeds, + /// the original error will be returned, otherwise the error generated by the rollback + /// will be returned. In the second case the connection should be considered broken + /// as it contains a uncommitted unabortable open transaction. + fn commit_transaction(&self, conn: &Conn) -> QueryResult<()> { + let transaction_depth = self.transaction_depth.get(); + if transaction_depth <= 1 { + match conn.batch_execute("COMMIT") { + // When any of these kinds of error happen on `COMMIT`, it is expected + // that a `ROLLBACK` would succeed, leaving the transaction in a non-broken state. + // If there are other such errors, it is fine to add them here. + e @ Err(Error::DatabaseError(DatabaseErrorKind::SerializationFailure, _)) + | e @ Err(Error::DatabaseError(DatabaseErrorKind::ReadOnlyTransaction, _)) => { + self.change_transaction_depth(-1, conn.batch_execute("ROLLBACK"))?; + e + } + result => self.change_transaction_depth(-1, result), + } + } else { + self.change_transaction_depth( + -1, + conn.batch_execute(&format!( + "RELEASE SAVEPOINT diesel_savepoint_{}", + transaction_depth - 1 + )), + ) + } + } + + fn get_transaction_depth(&self) -> u32 { + self.transaction_depth.get() as u32 + } +} + +#[cfg(test)] +mod test { + #[cfg(feature = "postgres")] + macro_rules! matches { + ($expression:expr, $( $pattern:pat )|+ $( if $guard: expr )?) => { + match $expression { + $( $pattern )|+ $( if $guard )? => true, + _ => false + } + } + } + + #[test] + #[cfg(feature = "postgres")] + fn transaction_depth_is_tracked_properly_on_commit_failure() { + use crate::result::DatabaseErrorKind::SerializationFailure; + use crate::result::Error::DatabaseError; + use crate::*; + use std::sync::{Arc, Barrier}; + use std::thread; + + table! { + #[sql_name = "transaction_depth_is_tracked_properly_on_commit_failure"] + serialization_example { + id -> Serial, + class -> Integer, + } + } + + let conn = crate::test_helpers::pg_connection_no_transaction(); + + sql_query("DROP TABLE IF EXISTS transaction_depth_is_tracked_properly_on_commit_failure;") + .execute(&conn) + .unwrap(); + sql_query( + r#" + CREATE TABLE transaction_depth_is_tracked_properly_on_commit_failure ( + id SERIAL PRIMARY KEY, + class INTEGER NOT NULL + ) + "#, + ) + .execute(&conn) + .unwrap(); + + insert_into(serialization_example::table) + .values(&vec![ + serialization_example::class.eq(1), + serialization_example::class.eq(2), + ]) + .execute(&conn) + .unwrap(); + + let barrier = Arc::new(Barrier::new(2)); + let threads = (1..3) + .map(|i| { + let barrier = barrier.clone(); + thread::spawn(move || { + use crate::connection::transaction_manager::AnsiTransactionManager; + use crate::connection::transaction_manager::TransactionManager; + let conn = crate::test_helpers::pg_connection_no_transaction(); + assert_eq!(0, >::get_transaction_depth(&conn.transaction_manager)); + + let result = + conn.build_transaction().serializable().run(|| { + assert_eq!(1, >::get_transaction_depth(&conn.transaction_manager)); + + let _ = serialization_example::table + .filter(serialization_example::class.eq(i)) + .count() + .execute(&conn)?; + + barrier.wait(); + + let other_i = if i == 1 { 2 } else { 1 }; + insert_into(serialization_example::table) + .values(serialization_example::class.eq(other_i)) + .execute(&conn) + }); + + assert_eq!(0, >::get_transaction_depth(&conn.transaction_manager)); + result + }) + }) + .collect::>(); + + let mut results = threads + .into_iter() + .map(|t| t.join().unwrap()) + .collect::>(); + + results.sort_by_key(|r| r.is_err()); + + assert!(matches!(results[0], Ok(_))); + assert!(matches!( + results[1], + Err(DatabaseError(SerializationFailure, _)) + )); + } +} diff --git a/collector/benchmarks/diesel/diesel/src/data_types.rs b/collector/benchmarks/diesel/diesel/src/data_types.rs new file mode 100644 index 000000000..43018e5a1 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/data_types.rs @@ -0,0 +1,7 @@ +//! Structs to represent the primitive equivalent of SQL types where +//! there is no existing Rust primitive, or where using it would be +//! confusing (such as date and time types). This module will re-export +//! all backend specific data structures when compiled against that +//! backend. +#[cfg(feature = "postgres")] +pub use crate::pg::data_types::*; diff --git a/collector/benchmarks/diesel/diesel/src/deserialize.rs b/collector/benchmarks/diesel/diesel/src/deserialize.rs new file mode 100644 index 000000000..4994e5a5d --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/deserialize.rs @@ -0,0 +1,442 @@ +//! Types and traits related to deserializing values from the database + +use std::error::Error; +use std::result; + +use crate::backend::{self, Backend}; +use crate::row::{NamedRow, Row}; +use crate::sql_types::{SingleValue, SqlType, Untyped}; + +/// A specialized result type representing the result of deserializing +/// a value from the database. +pub type Result = result::Result>; + +/// Trait indicating that a record can be queried from the database. +/// +/// Types which implement `Queryable` represent the result of a SQL query. This +/// does not necessarily mean they represent a single database table. +/// +/// Diesel represents the return type of a query as a tuple. The purpose of this +/// trait is to convert from a tuple of Rust values that have been deserialized +/// into your struct. +/// +/// This trait can be [derived](derive.Queryable.html) +/// +/// # Examples +/// +/// If we just want to map a query to our struct, we can use `derive`. +/// +/// ```rust +/// # include!("doctest_setup.rs"); +/// # +/// #[derive(Queryable, PartialEq, Debug)] +/// struct User { +/// id: i32, +/// name: String, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use schema::users::dsl::*; +/// # let connection = establish_connection(); +/// let first_user = users.first(&connection)?; +/// let expected = User { id: 1, name: "Sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +/// +/// If we want to do additional work during deserialization, we can use +/// `deserialize_as` to use a different implementation. +/// +/// ```rust +/// # include!("doctest_setup.rs"); +/// # +/// # use schema::users; +/// # use diesel::backend::{self, Backend}; +/// # use diesel::deserialize::{Queryable, FromSql}; +/// # use diesel::sql_types::Text; +/// # +/// struct LowercaseString(String); +/// +/// impl Into for LowercaseString { +/// fn into(self) -> String { +/// self.0 +/// } +/// } +/// +/// impl Queryable for LowercaseString +/// where +/// DB: Backend, +/// String: FromSql, +/// { +/// type Row = String; +/// +/// fn build(s: String) -> Self { +/// LowercaseString(s.to_lowercase()) +/// } +/// } +/// +/// #[derive(Queryable, PartialEq, Debug)] +/// struct User { +/// id: i32, +/// #[diesel(deserialize_as = "LowercaseString")] +/// name: String, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use schema::users::dsl::*; +/// # let connection = establish_connection(); +/// let first_user = users.first(&connection)?; +/// let expected = User { id: 1, name: "sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +/// +/// Alternatively, we can implement the trait for our struct manually. +/// +/// ```rust +/// # include!("doctest_setup.rs"); +/// # +/// use schema::users; +/// use diesel::deserialize::Queryable; +/// +/// # /* +/// type DB = diesel::sqlite::Sqlite; +/// # */ +/// +/// #[derive(PartialEq, Debug)] +/// struct User { +/// id: i32, +/// name: String, +/// } +/// +/// impl Queryable for User { +/// type Row = (i32, String); +/// +/// fn build(row: Self::Row) -> Self { +/// User { +/// id: row.0, +/// name: row.1.to_lowercase(), +/// } +/// } +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use schema::users::dsl::*; +/// # let connection = establish_connection(); +/// let first_user = users.first(&connection)?; +/// let expected = User { id: 1, name: "sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +pub trait Queryable +where + DB: Backend, +{ + /// The Rust type you'd like to map from. + /// + /// This is typically a tuple of all of your struct's fields. + type Row: FromStaticSqlRow; + + /// Construct an instance of this type + fn build(row: Self::Row) -> Self; +} + +#[doc(inline)] +pub use diesel_derives::Queryable; + +/// Deserializes the result of a query constructed with [`sql_query`]. +/// +/// This trait can be [derived](derive.QueryableByName.html) +/// +/// [`sql_query`]: ../fn.sql_query.html +/// +/// # Examples +/// +/// If we just want to map a query to our struct, we can use `derive`. +/// +/// ```rust +/// # include!("doctest_setup.rs"); +/// # use schema::users; +/// # use diesel::sql_query; +/// # +/// #[derive(QueryableByName, PartialEq, Debug)] +/// #[table_name = "users"] +/// struct User { +/// id: i32, +/// name: String, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # let connection = establish_connection(); +/// let first_user = sql_query("SELECT * FROM users ORDER BY id LIMIT 1") +/// .get_result(&connection)?; +/// let expected = User { id: 1, name: "Sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +/// +/// If we want to do additional work during deserialization, we can use +/// `deserialize_as` to use a different implementation. +/// +/// ```rust +/// # include!("doctest_setup.rs"); +/// # use diesel::sql_query; +/// # use schema::users; +/// # use diesel::backend::{self, Backend}; +/// # use diesel::deserialize::{self, FromSql}; +/// # +/// struct LowercaseString(String); +/// +/// impl Into for LowercaseString { +/// fn into(self) -> String { +/// self.0 +/// } +/// } +/// +/// impl FromSql for LowercaseString +/// where +/// DB: Backend, +/// String: FromSql, +/// { +/// fn from_sql(bytes: backend::RawValue) -> deserialize::Result { +/// String::from_sql(bytes) +/// .map(|s| LowercaseString(s.to_lowercase())) +/// } +/// } +/// +/// #[derive(QueryableByName, PartialEq, Debug)] +/// #[table_name = "users"] +/// struct User { +/// id: i32, +/// #[diesel(deserialize_as = "LowercaseString")] +/// name: String, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # let connection = establish_connection(); +/// let first_user = sql_query("SELECT * FROM users ORDER BY id LIMIT 1") +/// .get_result(&connection)?; +/// let expected = User { id: 1, name: "sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +pub trait QueryableByName +where + Self: Sized, + DB: Backend, +{ + /// Construct an instance of `Self` from the database row + fn build<'a>(row: &impl NamedRow<'a, DB>) -> Result; +} + +#[doc(inline)] +pub use diesel_derives::QueryableByName; + +/// Deserialize a single field of a given SQL type. +/// +/// When possible, implementations of this trait should prefer to use an +/// existing implementation, rather than reading from `bytes`. (For example, if +/// you are implementing this for an enum which is represented as an integer in +/// the database, prefer `i32::from_sql(bytes)` (or the explicit form +/// `>::from_sql(bytes)`) over reading from `bytes` +/// directly) +/// +/// Types which implement this trait should also have `#[derive(FromSqlRow)]` +/// +/// ### Backend specific details +/// +/// - For PostgreSQL, the bytes will be sent using the binary protocol, not text. +/// - For SQLite, the actual type of `DB::RawValue` is private API. All +/// implementations of this trait must be written in terms of an existing +/// primitive. +/// - For MySQL, the value of `bytes` will depend on the return value of +/// `type_metadata` for the given SQL type. See [`MysqlType`] for details. +/// - For third party backends, consult that backend's documentation. +/// +/// [`MysqlType`]: ../mysql/enum.MysqlType.html +/// +/// ### Examples +/// +/// Most implementations of this trait will be defined in terms of an existing +/// implementation. +/// +/// ```rust +/// # use diesel::backend::{self, Backend}; +/// # use diesel::sql_types::*; +/// # use diesel::deserialize::{self, FromSql, FromSqlRow}; +/// # +/// #[repr(i32)] +/// #[derive(Debug, Clone, Copy, FromSqlRow)] +/// pub enum MyEnum { +/// A = 1, +/// B = 2, +/// } +/// +/// impl FromSql for MyEnum +/// where +/// DB: Backend, +/// i32: FromSql, +/// { +/// fn from_sql(bytes: backend::RawValue) -> deserialize::Result { +/// match i32::from_sql(bytes)? { +/// 1 => Ok(MyEnum::A), +/// 2 => Ok(MyEnum::B), +/// x => Err(format!("Unrecognized variant {}", x).into()), +/// } +/// } +/// } +/// ``` +pub trait FromSql: Sized { + /// See the trait documentation. + fn from_sql(bytes: backend::RawValue) -> Result; + + /// A specialized variant of `from_sql` for handling null values. + /// + /// The default implementation returns an `UnexpectedNullError` for + /// an encountered null value and calls `Self::from_sql` otherwise + /// + /// If your custom type supports null values you need to provide a + /// custom implementation. + #[inline(always)] + fn from_nullable_sql(bytes: Option>) -> Result { + match bytes { + Some(bytes) => Self::from_sql(bytes), + None => Err(Box::new(crate::result::UnexpectedNullError)), + } + } +} + +/// Deserialize a database row into a rust data structure +/// +/// Diesel provides wild card implementations of this trait for all types +/// that implement one of the following traits: +/// * [`Queryable`](trait.Queryable.html) +/// * [`QueryableByName`](trait.QueryableByName.html) +pub trait FromSqlRow: Sized { + /// See the trait documentation. + fn build_from_row<'a>(row: &impl Row<'a, DB>) -> Result; +} + +#[doc(inline)] +pub use diesel_derives::FromSqlRow; + +/// A marker trait indicating that the corresponding type consumes a static at +/// compile time known number of field +/// +/// There is normally no need to implement this trait. Diesel provides +/// wild card impls for all types that implement `FromSql` or `Queryable` +/// where the size of `ST` is known +pub trait StaticallySizedRow: FromSqlRow { + /// The number of fields that this type will consume. + const FIELD_COUNT: usize; +} + +impl FromSqlRow for T +where + DB: Backend, + T: QueryableByName, +{ + fn build_from_row<'a>(row: &impl Row<'a, DB>) -> Result { + T::build(row) + } +} + +/// A helper trait to deserialize a statically sized row into an tuple +/// +/// **If you see an error message mentioning this trait you likly +/// trying to map the result of an query to an struct with missmatching +/// field types. Recheck your field order and the concrete field types** +/// +/// You should not need to implement this trait directly. +/// Diesel provides wild card implementations for any supported tuple size +/// and for any type that implements `FromSql`. +/// +// This is a distinct trait from `FromSqlRow` because otherwise we +// are getting conflicting implementation errors for our `FromSqlRow` +// implementation for tuples and our wild card impl for all types +// implementing `Queryable` +pub trait FromStaticSqlRow: Sized { + /// See the trait documentation + fn build_from_row<'a>(row: &impl Row<'a, DB>) -> Result; +} + +impl FromSqlRow for T +where + T: Queryable, + ST: SqlType, + DB: Backend, + T::Row: FromStaticSqlRow, +{ + fn build_from_row<'a>(row: &impl Row<'a, DB>) -> Result { + let row = >::build_from_row(row)?; + Ok(T::build(row)) + } +} + +impl FromStaticSqlRow for T +where + DB: Backend, + T: FromSql, + ST: SingleValue, +{ + fn build_from_row<'a>(row: &impl Row<'a, DB>) -> Result { + use crate::row::Field; + + let field = row.get(0).ok_or(crate::result::UnexpectedEndOfRow)?; + T::from_nullable_sql(field.value()) + } +} + +// We cannot have this impl because rustc +// then complains in third party crates that +// diesel may implement `SingleValue` for tuples +// in the future. While that is theoretically true, +// that will likly not happen in practice. +// If we get negative trait impls at some point in time +// it should be possible to make this work. +/*impl Queryable for T +where + DB: Backend, + T: FromStaticSqlRow, + ST: SingleValue, +{ + type Row = Self; + + fn build(row: Self::Row) -> Self { + row + } +}*/ + +impl StaticallySizedRow for T +where + ST: SqlType + crate::util::TupleSize, + T: Queryable, + DB: Backend, +{ + const FIELD_COUNT: usize = ::SIZE; +} diff --git a/collector/benchmarks/diesel/diesel/src/doctest_setup.rs b/collector/benchmarks/diesel/diesel/src/doctest_setup.rs new file mode 100644 index 000000000..0dfce7272 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/doctest_setup.rs @@ -0,0 +1,232 @@ +use diesel::prelude::*; +use dotenv::dotenv; + +cfg_if::cfg_if! { + if #[cfg(feature = "postgres")] { + #[allow(dead_code)] + type DB = diesel::pg::Pg; + + fn connection_no_transaction() -> PgConnection { + let connection_url = database_url_from_env("PG_DATABASE_URL"); + PgConnection::establish(&connection_url).unwrap() + } + + fn connection_no_data() -> PgConnection { + let connection = connection_no_transaction(); + connection.begin_test_transaction().unwrap(); + connection.execute("DROP TABLE IF EXISTS users CASCADE").unwrap(); + connection.execute("DROP TABLE IF EXISTS animals CASCADE").unwrap(); + connection.execute("DROP TABLE IF EXISTS posts CASCADE").unwrap(); + connection.execute("DROP TABLE IF EXISTS comments CASCADE").unwrap(); + + connection + } + + #[allow(dead_code)] + fn establish_connection() -> PgConnection { + let connection = connection_no_data(); + + connection.execute("CREATE TABLE users ( + id SERIAL PRIMARY KEY, + name VARCHAR NOT NULL + )").unwrap(); + connection.execute("INSERT INTO users (name) VALUES ('Sean'), ('Tess')").unwrap(); + + connection.execute("CREATE TABLE animals ( + id SERIAL PRIMARY KEY, + species VARCHAR NOT NULL, + legs INTEGER NOT NULL, + name VARCHAR + )").unwrap(); + connection.execute("INSERT INTO animals (species, legs, name) VALUES + ('dog', 4, 'Jack'), + ('spider', 8, null)").unwrap(); + + connection.execute("CREATE TABLE posts ( + id SERIAL PRIMARY KEY, + user_id INTEGER NOT NULL, + title VARCHAR NOT NULL + )").unwrap(); + connection.execute("INSERT INTO posts (user_id, title) VALUES + (1, 'My first post'), + (1, 'About Rust'), + (2, 'My first post too')").unwrap(); + + connection.execute("CREATE TABLE comments ( + id SERIAL PRIMARY KEY, + post_id INTEGER NOT NULL, + body VARCHAR NOT NULL + )").unwrap(); + connection.execute("INSERT INTO comments (post_id, body) VALUES + (1, 'Great post'), + (2, 'Yay! I am learning Rust'), + (3, 'I enjoyed your post')").unwrap(); + + connection + } + } else if #[cfg(feature = "sqlite")] { + #[allow(dead_code)] + type DB = diesel::sqlite::Sqlite; + + fn connection_no_data() -> SqliteConnection { + SqliteConnection::establish(":memory:").unwrap() + } + + #[allow(dead_code)] + fn establish_connection() -> SqliteConnection { + let connection = connection_no_data(); + + connection.execute("CREATE TABLE users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name VARCHAR NOT NULL + )").unwrap(); + connection.execute("INSERT INTO users (name) VALUES ('Sean'), ('Tess')").unwrap(); + + connection.execute("CREATE TABLE animals ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + species VARCHAR NOT NULL, + legs INTEGER NOT NULL, + name VARCHAR + )").unwrap(); + connection.execute("INSERT INTO animals (species, legs, name) VALUES + ('dog', 4, 'Jack'), + ('spider', 8, null)").unwrap(); + + connection.execute("CREATE TABLE posts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + title VARCHAR NOT NULL + )").unwrap(); + connection.execute("INSERT INTO posts (user_id, title) VALUES + (1, 'My first post'), + (1, 'About Rust'), + (2, 'My first post too')").unwrap(); + + connection.execute("CREATE TABLE comments ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + post_id INTEGER NOT NULL, + body VARCHAR NOT NULL + )").unwrap(); + connection.execute("INSERT INTO comments (post_id, body) VALUES + (1, 'Great post'), + (2, 'Yay! I am learning Rust'), + (3, 'I enjoyed your post')").unwrap(); + + connection + } + } else if #[cfg(feature = "mysql")] { + #[allow(dead_code)] + type DB = diesel::mysql::Mysql; + + fn connection_no_data() -> MysqlConnection { + let connection_url = database_url_from_env("MYSQL_UNIT_TEST_DATABASE_URL"); + let connection = MysqlConnection::establish(&connection_url).unwrap(); + connection.execute("DROP TABLE IF EXISTS users CASCADE").unwrap(); + connection.execute("DROP TABLE IF EXISTS animals CASCADE").unwrap(); + connection.execute("DROP TABLE IF EXISTS posts CASCADE").unwrap(); + connection.execute("DROP TABLE IF EXISTS comments CASCADE").unwrap(); + + connection + } + + #[allow(dead_code)] + fn establish_connection() -> MysqlConnection { + let connection = connection_no_data(); + + connection.execute("CREATE TABLE users ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + name TEXT NOT NULL + ) CHARACTER SET utf8mb4").unwrap(); + connection.execute("INSERT INTO users (name) VALUES ('Sean'), ('Tess')").unwrap(); + + connection.execute("CREATE TABLE animals ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + species TEXT NOT NULL, + legs INTEGER NOT NULL, + name TEXT + ) CHARACTER SET utf8mb4").unwrap(); + connection.execute("INSERT INTO animals (species, legs, name) VALUES + ('dog', 4, 'Jack'), + ('spider', 8, null)").unwrap(); + + connection.execute("CREATE TABLE posts ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + user_id INTEGER NOT NULL, + title TEXT NOT NULL + ) CHARACTER SET utf8mb4").unwrap(); + connection.execute("INSERT INTO posts (user_id, title) VALUES + (1, 'My first post'), + (1, 'About Rust'), + (2, 'My first post too')").unwrap(); + + connection.execute("CREATE TABLE comments ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + post_id INTEGER NOT NULL, + body TEXT NOT NULL + ) CHARACTER SET utf8mb4").unwrap(); + connection.execute("INSERT INTO comments (post_id, body) VALUES + (1, 'Great post'), + (2, 'Yay! I am learning Rust'), + (3, 'I enjoyed your post')").unwrap(); + + connection.begin_test_transaction().unwrap(); + connection + } + } else { + compile_error!( + "At least one backend must be used to test this crate.\n \ + Pass argument `--features \"\"` with one or more of the following backends, \ + 'mysql', 'postgres', or 'sqlite'. \n\n \ + ex. cargo test --features \"mysql postgres sqlite\"\n" + ); + } +} + +fn database_url_from_env(backend_specific_env_var: &str) -> String { + use std::env; + + dotenv().ok(); + + env::var(backend_specific_env_var) + .or_else(|_| env::var("DATABASE_URL")) + .expect("DATABASE_URL must be set in order to run tests") +} + +mod schema { + use diesel::prelude::*; + + table! { + animals { + id -> Integer, + species -> VarChar, + legs -> Integer, + name -> Nullable, + } + } + + table! { + comments { + id -> Integer, + post_id -> Integer, + body -> VarChar, + } + } + + table! { + posts { + id -> Integer, + user_id -> Integer, + title -> VarChar, + } + } + + table! { + users { + id -> Integer, + name -> VarChar, + } + } + + joinable!(posts -> users (user_id)); + allow_tables_to_appear_in_same_query!(animals, comments, posts, users); +} diff --git a/collector/benchmarks/diesel/diesel/src/expression/array_comparison.rs b/collector/benchmarks/diesel/diesel/src/expression/array_comparison.rs new file mode 100644 index 000000000..83a47ca41 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/array_comparison.rs @@ -0,0 +1,199 @@ +use crate::backend::Backend; +use crate::expression::subselect::Subselect; +use crate::expression::*; +use crate::query_builder::*; +use crate::query_builder::{BoxedSelectStatement, SelectStatement}; +use crate::result::QueryResult; +use crate::sql_types::Bool; + +#[derive(Debug, Copy, Clone, QueryId, ValidGrouping)] +pub struct In { + left: T, + values: U, +} + +#[derive(Debug, Copy, Clone, QueryId, ValidGrouping)] +pub struct NotIn { + left: T, + values: U, +} + +impl In { + pub fn new(left: T, values: U) -> Self { + In { + left: left, + values: values, + } + } +} + +impl NotIn { + pub fn new(left: T, values: U) -> Self { + NotIn { + left: left, + values: values, + } + } +} + +impl Expression for In +where + T: Expression, + U: Expression, +{ + type SqlType = Bool; +} + +impl Expression for NotIn +where + T: Expression, + U: Expression, +{ + type SqlType = Bool; +} + +impl QueryFragment for In +where + DB: Backend, + T: QueryFragment, + U: QueryFragment + MaybeEmpty, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + if self.values.is_empty() { + out.push_sql("1=0"); + } else { + self.left.walk_ast(out.reborrow())?; + out.push_sql(" IN ("); + self.values.walk_ast(out.reborrow())?; + out.push_sql(")"); + } + Ok(()) + } +} + +impl QueryFragment for NotIn +where + DB: Backend, + T: QueryFragment, + U: QueryFragment + MaybeEmpty, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + if self.values.is_empty() { + out.push_sql("1=1"); + } else { + self.left.walk_ast(out.reborrow())?; + out.push_sql(" NOT IN ("); + self.values.walk_ast(out.reborrow())?; + out.push_sql(")"); + } + Ok(()) + } +} + +impl_selectable_expression!(In); +impl_selectable_expression!(NotIn); + +pub trait AsInExpression { + type InExpression: MaybeEmpty + Expression; + + fn as_in_expression(self) -> Self::InExpression; +} + +impl AsInExpression for I +where + I: IntoIterator, + T: AsExpression, + ST: SqlType + TypedExpressionType, +{ + type InExpression = Many; + + fn as_in_expression(self) -> Self::InExpression { + let expressions = self + .into_iter() + .map(>::as_expression) + .collect(); + Many(expressions) + } +} + +pub trait MaybeEmpty { + fn is_empty(&self) -> bool; +} + +impl AsInExpression for SelectStatement +where + ST: SqlType + TypedExpressionType, + Subselect: Expression, + Self: SelectQuery, +{ + type InExpression = Subselect; + + fn as_in_expression(self) -> Self::InExpression { + Subselect::new(self) + } +} + +impl<'a, ST, QS, DB> AsInExpression for BoxedSelectStatement<'a, ST, QS, DB> +where + ST: SqlType + TypedExpressionType, + Subselect, ST>: Expression, +{ + type InExpression = Subselect; + + fn as_in_expression(self) -> Self::InExpression { + Subselect::new(self) + } +} + +#[derive(Debug, Clone, ValidGrouping)] +pub struct Many(Vec); + +impl Expression for Many { + type SqlType = T::SqlType; +} + +impl MaybeEmpty for Many { + fn is_empty(&self) -> bool { + self.0.is_empty() + } +} + +impl SelectableExpression for Many +where + Many: AppearsOnTable, + T: SelectableExpression, +{ +} + +impl AppearsOnTable for Many +where + Many: Expression, + T: AppearsOnTable, +{ +} + +impl QueryFragment for Many +where + DB: Backend, + T: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.unsafe_to_cache_prepared(); + let mut first = true; + for value in &self.0 { + if first { + first = false; + } else { + out.push_sql(", "); + } + value.walk_ast(out.reborrow())?; + } + Ok(()) + } +} + +impl QueryId for Many { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} diff --git a/collector/benchmarks/diesel/diesel/src/expression/bound.rs b/collector/benchmarks/diesel/diesel/src/expression/bound.rs new file mode 100644 index 000000000..6c3d33d97 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/bound.rs @@ -0,0 +1,55 @@ +use std::marker::PhantomData; + +use super::*; +use crate::backend::Backend; +use crate::query_builder::*; +use crate::result::QueryResult; +use crate::serialize::ToSql; +use crate::sql_types::{DieselNumericOps, HasSqlType, SqlType}; + +#[derive(Debug, Clone, Copy, DieselNumericOps)] +pub struct Bound { + item: U, + _marker: PhantomData, +} + +impl Bound { + pub fn new(item: U) -> Self { + Bound { + item: item, + _marker: PhantomData, + } + } +} + +impl Expression for Bound +where + T: SqlType + TypedExpressionType, +{ + type SqlType = T; +} + +impl QueryFragment for Bound +where + DB: Backend + HasSqlType, + U: ToSql, +{ + fn walk_ast(&self, mut pass: AstPass) -> QueryResult<()> { + pass.push_bind_param(&self.item)?; + Ok(()) + } +} + +impl QueryId for Bound { + type QueryId = Bound; + + const HAS_STATIC_QUERY_ID: bool = T::HAS_STATIC_QUERY_ID; +} + +impl SelectableExpression for Bound where Bound: AppearsOnTable {} + +impl AppearsOnTable for Bound where Bound: Expression {} + +impl ValidGrouping for Bound { + type IsAggregate = is_aggregate::Never; +} diff --git a/collector/benchmarks/diesel/diesel/src/expression/coerce.rs b/collector/benchmarks/diesel/diesel/src/expression/coerce.rs new file mode 100644 index 000000000..ecf3eacc5 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/coerce.rs @@ -0,0 +1,73 @@ +use std::marker::PhantomData; + +use crate::backend::Backend; +use crate::expression::*; +use crate::query_builder::*; +use crate::result::QueryResult; +use crate::sql_types::{DieselNumericOps, SqlType}; + +#[derive(Debug, Copy, Clone, QueryId, DieselNumericOps)] +#[doc(hidden)] +/// Coerces an expression to be another type. No checks are performed to ensure +/// that the new type is valid in all positions that the previous type was. +/// This does not perform an actual cast, it just lies to our type system. +/// +/// This is used for a few expressions where we know that the types are actually +/// always interchangeable. (Examples of this include `Timestamp` vs +/// `Timestamptz`, `VarChar` vs `Text`, and `Json` vs `Jsonb`). +/// +/// This struct should not be considered a general solution to equivalent types. +/// It is a short term workaround for expressions which are known to be commonly +/// used. +pub struct Coerce { + expr: T, + _marker: PhantomData, +} + +impl Coerce { + pub fn new(expr: T) -> Self { + Coerce { + expr: expr, + _marker: PhantomData, + } + } +} + +impl Expression for Coerce +where + T: Expression, + ST: SqlType + TypedExpressionType, +{ + type SqlType = ST; +} + +impl SelectableExpression for Coerce +where + T: SelectableExpression, + Self: Expression, +{ +} + +impl AppearsOnTable for Coerce +where + T: AppearsOnTable, + Self: Expression, +{ +} + +impl QueryFragment for Coerce +where + T: QueryFragment, + DB: Backend, +{ + fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + self.expr.walk_ast(pass) + } +} + +impl ValidGrouping for Coerce +where + T: ValidGrouping, +{ + type IsAggregate = T::IsAggregate; +} diff --git a/collector/benchmarks/diesel/diesel/src/expression/count.rs b/collector/benchmarks/diesel/diesel/src/expression/count.rs new file mode 100644 index 000000000..a941b7cd7 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/count.rs @@ -0,0 +1,73 @@ +use super::functions::sql_function; +use super::{Expression, ValidGrouping}; +use crate::backend::Backend; +use crate::query_builder::*; +use crate::result::QueryResult; +use crate::sql_types::{BigInt, DieselNumericOps, Nullable, SingleValue, SqlType}; + +sql_function! { + /// Creates a SQL `COUNT` expression + /// + /// As with most bare functions, this is not exported by default. You can import + /// it specifically as `diesel::dsl::count`, or glob import + /// `diesel::dsl::*` + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use diesel::dsl::*; + /// # + /// # fn main() { + /// # use schema::animals::dsl::*; + /// # let connection = establish_connection(); + /// assert_eq!(Ok(1), animals.select(count(name)).first(&connection)); + /// # } + /// ``` + #[aggregate] + fn count(expr: Nullable) -> BigInt; +} + +/// Creates a SQL `COUNT(*)` expression +/// +/// For selecting the count of a query, and nothing else, you can just call +/// [`count`](../query_dsl/trait.QueryDsl.html#method.count) +/// on the query instead. +/// +/// As with most bare functions, this is not exported by default. You can import +/// it specifically as `diesel::dsl::count_star`, or glob import +/// `diesel::dsl::*` +/// +/// # Examples +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # use diesel::dsl::*; +/// # +/// # fn main() { +/// # use schema::users::dsl::*; +/// # let connection = establish_connection(); +/// assert_eq!(Ok(2), users.select(count_star()).first(&connection)); +/// # } +/// ``` +pub fn count_star() -> CountStar { + CountStar +} + +#[derive(Debug, Clone, Copy, QueryId, DieselNumericOps, ValidGrouping)] +#[diesel(aggregate)] +#[doc(hidden)] +pub struct CountStar; + +impl Expression for CountStar { + type SqlType = BigInt; +} + +impl QueryFragment for CountStar { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("COUNT(*)"); + Ok(()) + } +} + +impl_selectable_expression!(CountStar); diff --git a/collector/benchmarks/diesel/diesel/src/expression/exists.rs b/collector/benchmarks/diesel/diesel/src/expression/exists.rs new file mode 100644 index 000000000..385065f6b --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/exists.rs @@ -0,0 +1,92 @@ +use crate::backend::Backend; +use crate::expression::subselect::Subselect; +use crate::expression::{AppearsOnTable, Expression, SelectableExpression, ValidGrouping}; +use crate::query_builder::*; +use crate::result::QueryResult; +use crate::sql_types::Bool; + +/// Creates a SQL `EXISTS` expression. +/// +/// The argument must be a complete SQL query. The query may reference columns +/// from the outer table. +/// +/// # Example +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # +/// # fn main() { +/// # use schema::users::dsl::*; +/// # use diesel::select; +/// # use diesel::dsl::exists; +/// # let connection = establish_connection(); +/// let sean_exists = select(exists(users.filter(name.eq("Sean")))) +/// .get_result(&connection); +/// let jim_exists = select(exists(users.filter(name.eq("Jim")))) +/// .get_result(&connection); +/// assert_eq!(Ok(true), sean_exists); +/// assert_eq!(Ok(false), jim_exists); +/// # } +/// ``` +pub fn exists(query: T) -> Exists { + Exists(Subselect::new(query)) +} + +#[derive(Clone, Copy, QueryId, Debug)] +pub struct Exists(pub Subselect); + +impl Expression for Exists +where + Subselect: Expression, +{ + type SqlType = Bool; +} + +impl ValidGrouping for Exists +where + Subselect: ValidGrouping, +{ + type IsAggregate = as ValidGrouping>::IsAggregate; +} + +#[cfg(not(feature = "unstable"))] +impl QueryFragment for Exists +where + DB: Backend, + T: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("EXISTS ("); + self.0.walk_ast(out.reborrow())?; + out.push_sql(")"); + Ok(()) + } +} + +#[cfg(feature = "unstable")] +impl QueryFragment for Exists +where + DB: Backend, + T: QueryFragment, +{ + default fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("EXISTS ("); + self.0.walk_ast(out.reborrow())?; + out.push_sql(")"); + Ok(()) + } +} + +impl SelectableExpression for Exists +where + Self: AppearsOnTable, + Subselect: SelectableExpression, +{ +} + +impl AppearsOnTable for Exists +where + Self: Expression, + Subselect: AppearsOnTable, +{ +} diff --git a/collector/benchmarks/diesel/diesel/src/expression/functions/aggregate_folding.rs b/collector/benchmarks/diesel/diesel/src/expression/functions/aggregate_folding.rs new file mode 100644 index 000000000..1896e21fe --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/functions/aggregate_folding.rs @@ -0,0 +1,68 @@ +use crate::expression::functions::sql_function; +use crate::sql_types::{Foldable, Nullable}; + +sql_function! { + /// Represents a SQL `SUM` function. This function can only take types which are + /// Foldable. + /// + /// # Examples + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # use diesel::dsl::*; + /// # + /// # fn main() { + /// # use schema::animals::dsl::*; + /// # let connection = establish_connection(); + /// assert_eq!(Ok(Some(12i64)), animals.select(sum(legs)).first(&connection)); + /// # } + /// ``` + #[aggregate] + fn sum(expr: Nullable) -> ST::Sum; +} + +sql_function! { + /// Represents a SQL `AVG` function. This function can only take types which are + /// Foldable. + /// + /// # Examples + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # use diesel::dsl::*; + /// # #[cfg(feature = "bigdecimal")] + /// # extern crate bigdecimal; + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # table! { + /// # numbers (number) { + /// # number -> Integer, + /// # } + /// # } + /// # + /// # #[cfg(all(feature = "numeric", any(feature = "postgres", not(feature = "sqlite"))))] + /// # fn run_test() -> QueryResult<()> { + /// # use bigdecimal::BigDecimal; + /// # use self::numbers::dsl::*; + /// # let conn = establish_connection(); + /// # conn.execute("DROP TABLE IF EXISTS numbers")?; + /// # conn.execute("CREATE TABLE numbers (number INTEGER)")?; + /// diesel::insert_into(numbers) + /// .values(&vec![number.eq(1), number.eq(2)]) + /// .execute(&conn)?; + /// let average = numbers.select(avg(number)).get_result(&conn)?; + /// let expected = "1.5".parse::().unwrap(); + /// assert_eq!(Some(expected), average); + /// # Ok(()) + /// # } + /// # + /// # #[cfg(not(all(feature = "numeric", any(feature = "postgres", not(feature = "sqlite")))))] + /// # fn run_test() -> QueryResult<()> { + /// # Ok(()) + /// # } + #[aggregate] + fn avg(expr: Nullable) -> ST::Avg; +} diff --git a/collector/benchmarks/diesel/diesel/src/expression/functions/aggregate_ordering.rs b/collector/benchmarks/diesel/diesel/src/expression/functions/aggregate_ordering.rs new file mode 100644 index 000000000..4b398ce09 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/functions/aggregate_ordering.rs @@ -0,0 +1,52 @@ +use crate::expression::functions::sql_function; +use crate::sql_types::{IntoNullable, Nullable, SingleValue, SqlOrd, SqlType}; + +pub trait SqlOrdAggregate: SingleValue { + type Ret: SqlType + SingleValue; +} + +impl SqlOrdAggregate for ST +where + ST: SqlOrd + SingleValue + IntoNullable, + ST::Nullable: SingleValue, +{ + type Ret = ::Nullable; +} + +sql_function! { + /// Represents a SQL `MAX` function. This function can only take types which are + /// ordered. + /// + /// # Examples + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # use diesel::dsl::*; + /// # + /// # fn main() { + /// # use schema::animals::dsl::*; + /// # let connection = establish_connection(); + /// assert_eq!(Ok(Some(8)), animals.select(max(legs)).first(&connection)); + /// # } + #[aggregate] + fn max(expr: Nullable) -> ST::Ret; +} + +sql_function! { + /// Represents a SQL `MIN` function. This function can only take types which are + /// ordered. + /// + /// # Examples + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # use diesel::dsl::*; + /// # + /// # fn main() { + /// # use schema::animals::dsl::*; + /// # let connection = establish_connection(); + /// assert_eq!(Ok(Some(4)), animals.select(min(legs)).first(&connection)); + /// # } + #[aggregate] + fn min(expr: Nullable) -> ST::Ret; +} diff --git a/collector/benchmarks/diesel/diesel/src/expression/functions/date_and_time.rs b/collector/benchmarks/diesel/diesel/src/expression/functions/date_and_time.rs new file mode 100644 index 000000000..d6cef53d2 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/functions/date_and_time.rs @@ -0,0 +1,87 @@ +use crate::backend::Backend; +use crate::expression::functions::sql_function; +#[cfg(feature = "postgres")] +use crate::expression::{coerce::Coerce, AsExpression}; +use crate::expression::{Expression, ValidGrouping}; +use crate::query_builder::*; +use crate::result::QueryResult; +use crate::sql_types::*; + +/// Represents the SQL `CURRENT_TIMESTAMP` constant. This is equivalent to the +/// `NOW()` function on backends that support it. +#[allow(non_camel_case_types)] +#[derive(Debug, Copy, Clone, QueryId, ValidGrouping)] +pub struct now; + +impl Expression for now { + type SqlType = Timestamp; +} + +impl QueryFragment for now { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("CURRENT_TIMESTAMP"); + Ok(()) + } +} + +impl_selectable_expression!(now); + +operator_allowed!(now, Add, add); +operator_allowed!(now, Sub, sub); +sql_function! { + /// Represents the SQL `DATE` function. The argument should be a Timestamp + /// expression, and the return value will be an expression of type Date. + + /// # Examples + + /// ```ignore + /// # extern crate chrono; + /// # include!("../../doctest_setup.rs"); + /// # use diesel::dsl::*; + /// # + /// # fn main() { + /// # let connection = establish_connection(); + /// let today: chrono::NaiveDate = diesel::select(date(now)).first(&connection).unwrap(); + /// # } + /// ``` + fn date(expr: Timestamp) -> Date; +} + +#[cfg(feature = "postgres")] +impl AsExpression for now { + type Expression = Coerce; + + fn as_expression(self) -> Self::Expression { + Coerce::new(self) + } +} + +#[cfg(feature = "postgres")] +impl AsExpression> for now { + type Expression = Coerce>; + + fn as_expression(self) -> Self::Expression { + Coerce::new(self) + } +} + +/// Represents the SQL `CURRENT_DATE` constant. +#[allow(non_camel_case_types)] +#[derive(Debug, Copy, Clone, QueryId, ValidGrouping)] +pub struct today; + +impl Expression for today { + type SqlType = Date; +} + +impl QueryFragment for today { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("CURRENT_DATE"); + Ok(()) + } +} + +impl_selectable_expression!(today); + +operator_allowed!(today, Add, add); +operator_allowed!(today, Sub, sub); diff --git a/collector/benchmarks/diesel/diesel/src/expression/functions/helper_types.rs b/collector/benchmarks/diesel/diesel/src/expression/functions/helper_types.rs new file mode 100644 index 000000000..0ccf8e530 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/functions/helper_types.rs @@ -0,0 +1,21 @@ +#![allow(non_camel_case_types)] + +use crate::dsl::{AsExprOf, SqlTypeOf}; +use crate::expression::grouped::Grouped; +use crate::expression::operators; +use crate::sql_types::{Bool, Nullable}; + +/// The return type of [`not(expr)`](../dsl/fn.not.html) +pub type not = operators::Not>>>; + +/// The return type of [`max(expr)`](../dsl/fn.max.html) +pub type max = super::aggregate_ordering::max::HelperType, Expr>; + +/// The return type of [`min(expr)`](../dsl/fn.min.html) +pub type min = super::aggregate_ordering::min::HelperType, Expr>; + +/// The return type of [`sum(expr)`](../dsl/fn.sum.html) +pub type sum = super::aggregate_folding::sum::HelperType, Expr>; + +/// The return type of [`avg(expr)`](../dsl/fn.avg.html) +pub type avg = super::aggregate_folding::avg::HelperType, Expr>; diff --git a/collector/benchmarks/diesel/diesel/src/expression/functions/mod.rs b/collector/benchmarks/diesel/diesel/src/expression/functions/mod.rs new file mode 100644 index 000000000..154ca87b0 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/functions/mod.rs @@ -0,0 +1,103 @@ +//! Helper macros to define custom sql functions + +#[doc(inline)] +pub use diesel_derives::sql_function_proc as sql_function; + +#[macro_export] +#[doc(hidden)] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +macro_rules! no_arg_sql_function_body_except_to_sql { + ($type_name:ident, $return_type:ty, $docs:expr) => { + #[allow(non_camel_case_types)] + #[doc=$docs] + #[derive( + Debug, + Clone, + Copy, + $crate::query_builder::QueryId, + $crate::expression::ValidGrouping + )] + pub struct $type_name; + + impl $crate::expression::Expression for $type_name { + type SqlType = $return_type; + } + + impl $crate::expression::SelectableExpression for $type_name {} + + impl $crate::expression::AppearsOnTable for $type_name {} + }; +} + +#[macro_export] +#[doc(hidden)] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +macro_rules! no_arg_sql_function_body { + ($type_name:ident, $return_type:ty, $docs:expr, $($constraint:ident)::+) => { + no_arg_sql_function_body_except_to_sql!($type_name, $return_type, $docs); + + impl $crate::query_builder::QueryFragment for $type_name where + DB: $crate::backend::Backend + $($constraint)::+, + { + fn walk_ast(&self, mut out: $crate::query_builder::AstPass) -> $crate::result::QueryResult<()> { + out.push_sql(concat!(stringify!($type_name), "()")); + Ok(()) + } + } + }; + + ($type_name:ident, $return_type:ty, $docs:expr) => { + no_arg_sql_function_body_except_to_sql!($type_name, $return_type, $docs); + + impl $crate::query_builder::QueryFragment for $type_name where + DB: $crate::backend::Backend, + { + fn walk_ast(&self, mut out: $crate::query_builder::AstPass) -> $crate::result::QueryResult<()> { + out.push_sql(concat!(stringify!($type_name), "()")); + Ok(()) + } + } + }; +} + +#[macro_export] +/// Declare a 0 argument SQL function for use in your code. This will generate a +/// unit struct, which is an expression representing calling this function. See +/// [`now`](expression/dsl/struct.now.html) for example output. `now` was +/// generated using: +/// +/// ```no_run +/// # pub use diesel::*; +/// no_arg_sql_function!(now, sql_types::Timestamp, "Represents the SQL NOW() function"); +/// # fn main() {} +/// ``` +/// +/// You can optionally pass the name of a trait, as a constraint for backends which support the +/// function. +#[deprecated( + since = "2.0.0", + note = "Use `sql_function!` instead. See `CHANGELOG.md` for migration instructions" +)] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +macro_rules! no_arg_sql_function { + ($type_name:ident, $return_type:ty) => { + no_arg_sql_function!($type_name, $return_type, ""); + }; + + ($type_name:ident, $return_type:ty, $docs:expr) => { + no_arg_sql_function_body!($type_name, $return_type, $docs); + }; + + ($type_name:ident, $return_type:ty, $docs:expr, $($constraint:ident)::+) => { + no_arg_sql_function_body!($type_name, $return_type, $docs, $($constraint)::+); + }; +} + +#[doc(hidden)] +pub mod aggregate_folding; +#[doc(hidden)] +pub mod aggregate_ordering; +#[doc(hidden)] +pub mod date_and_time; +#[doc(hidden)] +pub mod helper_types; diff --git a/collector/benchmarks/diesel/diesel/src/expression/grouped.rs b/collector/benchmarks/diesel/diesel/src/expression/grouped.rs new file mode 100644 index 000000000..bd775821b --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/grouped.rs @@ -0,0 +1,23 @@ +use crate::backend::Backend; +use crate::expression::{Expression, ValidGrouping}; +use crate::query_builder::*; +use crate::result::QueryResult; +use crate::sql_types::DieselNumericOps; + +#[derive(Debug, Copy, Clone, QueryId, Default, DieselNumericOps, ValidGrouping)] +pub struct Grouped(pub T); + +impl Expression for Grouped { + type SqlType = T::SqlType; +} + +impl, DB: Backend> QueryFragment for Grouped { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("("); + self.0.walk_ast(out.reborrow())?; + out.push_sql(")"); + Ok(()) + } +} + +impl_selectable_expression!(Grouped); diff --git a/collector/benchmarks/diesel/diesel/src/expression/helper_types.rs b/collector/benchmarks/diesel/diesel/src/expression/helper_types.rs new file mode 100644 index 000000000..3b3e8e557 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/helper_types.rs @@ -0,0 +1,120 @@ +//! The types in this module are all shorthand for `PredicateType>`. Since we often need to return concrete types, instead of +//! a boxed trait object, these can be useful for writing concise return types. +use super::array_comparison::{AsInExpression, In, NotIn}; +use super::grouped::Grouped; +use super::{AsExpression, Expression}; +use crate::sql_types; + +/// The SQL type of an expression +pub type SqlTypeOf = ::SqlType; + +/// The type of `Item` when converted to an expression with the same type as `TargetExpr` +pub type AsExpr = AsExprOf>; + +/// The type of `Item` when converted to an expression of `Type` +pub type AsExprOf = >::Expression; + +/// The return type of +/// [`lhs.eq(rhs)`](../expression_methods/trait.ExpressionMethods.html#method.eq) +pub type Eq = Grouped>>; + +/// The return type of +/// [`lhs.ne(rhs)`](../expression_methods/trait.ExpressionMethods.html#method.ne) +pub type NotEq = Grouped>>; + +/// The return type of +/// [`lhs.eq_any(rhs)`](../expression_methods/trait.ExpressionMethods.html#method.eq_any) +pub type EqAny = Grouped>>::InExpression>>; + +/// The return type of +/// [`lhs.ne_any(rhs)`](../expression_methods/trait.ExpressionMethods.html#method.ne_any) +pub type NeAny = + Grouped>>::InExpression>>; + +/// The return type of +/// [`expr.is_null()`](../expression_methods/trait.ExpressionMethods.html#method.is_null) +pub type IsNull = Grouped>; + +/// The return type of +/// [`expr.is_not_null()`](../expression_methods/trait.ExpressionMethods.html#method.is_not_null) +pub type IsNotNull = Grouped>; + +/// The return type of +/// [`lhs.gt(rhs)`](../expression_methods/trait.ExpressionMethods.html#method.gt) +pub type Gt = Grouped>>; + +/// The return type of +/// [`lhs.ge(rhs)`](../expression_methods/trait.ExpressionMethods.html#method.ge) +pub type GtEq = Grouped>>; + +/// The return type of +/// [`lhs.lt(rhs)`](../expression_methods/trait.ExpressionMethods.html#method.lt) +pub type Lt = Grouped>>; + +/// The return type of +/// [`lhs.le(rhs)`](../expression_methods/trait.ExpressionMethods.html#method.le) +pub type LtEq = Grouped>>; + +/// The return type of +/// [`lhs.between(lower, upper)`](../expression_methods/trait.ExpressionMethods.html#method.between) +pub type Between = Grouped< + super::operators::Between, AsExpr>>, +>; + +/// The return type of +/// [`lhs.not_between(lower, upper)`](../expression_methods/trait.ExpressionMethods.html#method.not_between) +pub type NotBetween = Grouped< + super::operators::NotBetween< + Lhs, + super::operators::And, AsExpr>, + >, +>; + +/// The return type of +/// [`lhs.concat(rhs)`](../expression_methods/trait.TextExpressionMethods.html#method.concat) +pub type Concat = Grouped>>; + +/// The return type of +/// [`expr.desc()`](../expression_methods/trait.ExpressionMethods.html#method.desc) +pub type Desc = super::operators::Desc; + +/// The return type of +/// [`expr.asc()`](../expression_methods/trait.ExpressionMethods.html#method.asc) +pub type Asc = super::operators::Asc; + +/// The return type of +/// [`expr.nullable()`](../expression_methods/trait.NullableExpressionMethods.html#method.nullable) +pub type Nullable = super::nullable::Nullable; + +/// The return type of +/// [`lhs.and(rhs)`](../expression_methods/trait.BoolExpressionMethods.html#method.and) +pub type And = Grouped, AsExpr>>>; + +/// The return type of +/// [`lhs.or(rhs)`](../expression_methods/trait.BoolExpressionMethods.html#method.or) +pub type Or = Grouped>>>; + +/// The return type of +/// [`lhs.escape('x')`](../expression_methods/trait.EscapeExpressionMethods.html#method.escape) +pub type Escape = Grouped< + super::operators::Escape< + ::TextExpression, + AsExprOf, + >, +>; + +/// The return type of +/// [`lhs.like(rhs)`](../expression_methods/trait.TextExpressionMethods.html#method.like) +pub type Like = Grouped>>>; + +/// The return type of +/// [`lhs.not_like(rhs)`](../expression_methods/trait.TextExpressionMethods.html#method.not_like) +pub type NotLike = Grouped>>>; + +#[doc(inline)] +pub use super::functions::helper_types::*; + +#[doc(inline)] +#[cfg(feature = "postgres")] +pub use crate::pg::expression::helper_types::*; diff --git a/collector/benchmarks/diesel/diesel/src/expression/mod.rs b/collector/benchmarks/diesel/diesel/src/expression/mod.rs new file mode 100644 index 000000000..87caafb92 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/mod.rs @@ -0,0 +1,763 @@ +//! AST types representing various typed SQL expressions. +//! +//! Almost all types implement either [`Expression`](trait.Expression.html) or +//! [`AsExpression`](trait.AsExpression.html). +//! +//! The most common expression to work with is a +//! [`Column`](../query_source/trait.Column.html). There are various methods +//! that you can call on these, found in +//! [`expression_methods`](../expression_methods). +//! +//! You can also use numeric operators such as `+` on expressions of the +//! appropriate type. +//! +//! Any primitive which implements [`ToSql`](../serialize/trait.ToSql.html) will +//! also implement [`AsExpression`](trait.AsExpression.html), allowing it to be +//! used as an argument to any of the methods described here. +#[macro_use] +#[doc(hidden)] +pub mod ops; +pub mod functions; + +#[doc(hidden)] +pub mod array_comparison; +#[doc(hidden)] +pub mod bound; +#[doc(hidden)] +pub mod coerce; +#[doc(hidden)] +pub mod count; +#[doc(hidden)] +pub mod exists; +#[doc(hidden)] +pub mod grouped; +#[doc(hidden)] +pub mod helper_types; +mod not; +#[doc(hidden)] +pub mod nullable; +#[doc(hidden)] +#[macro_use] +pub mod operators; +#[doc(hidden)] +pub mod sql_literal; +#[doc(hidden)] +pub mod subselect; + +#[doc(hidden)] +#[allow(non_camel_case_types)] +pub mod dsl { + use crate::dsl::SqlTypeOf; + + #[doc(inline)] + pub use super::count::*; + #[doc(inline)] + pub use super::exists::exists; + #[doc(inline)] + pub use super::functions::aggregate_folding::*; + #[doc(inline)] + pub use super::functions::aggregate_ordering::*; + #[doc(inline)] + pub use super::functions::date_and_time::*; + #[doc(inline)] + pub use super::not::not; + #[doc(inline)] + pub use super::sql_literal::sql; + + #[cfg(feature = "postgres")] + pub use crate::pg::expression::dsl::*; + + /// The return type of [`count(expr)`](../dsl/fn.count.html) + pub type count = super::count::count::HelperType, Expr>; + + /// The return type of [`count_star()`](../dsl/fn.count_star.html) + pub type count_star = super::count::CountStar; + + /// The return type of [`date(expr)`](../dsl/fn.date.html) + pub type date = super::functions::date_and_time::date::HelperType; +} + +#[doc(inline)] +pub use self::sql_literal::{SqlLiteral, UncheckedBind}; + +use crate::backend::Backend; +use crate::dsl::AsExprOf; +use crate::sql_types::{HasSqlType, IntoNotNullable, SingleValue, SqlType}; + +/// Represents a typed fragment of SQL. +/// +/// Apps should not need to implement this type directly, but it may be common +/// to use this in where clauses. Libraries should consider using +/// [`infix_operator!`](../macro.infix_operator.html) or +/// [`postfix_operator!`](../macro.postfix_operator.html) instead of +/// implementing this directly. +pub trait Expression { + /// The type that this expression represents in SQL + type SqlType: TypedExpressionType; +} + +/// Marker trait for possible types of [`Expression::SqlType`] +/// +/// [`Expression::SqlType`]: trait.Expression.html#associatedtype.SqlType +pub trait TypedExpressionType {} + +/// Possible types for []`Expression::SqlType`] +/// +/// [`Expression::SqlType`]: trait.Expression.html#associatedtype.SqlType +pub mod expression_types { + use super::{QueryMetadata, TypedExpressionType}; + use crate::backend::Backend; + use crate::sql_types::SingleValue; + + /// Query nodes with this expression type do not have a statically at compile + /// time known expression type. + /// + /// An example for such a query node in diesel itself, is `sql_query` as + /// we do not know which fields are returned from such a query at compile time. + /// + /// For loading values from queries returning a type of this expression, consider + /// using [`#[derive(QueryableByName)]`] on the corresponding result type. + /// + /// [`#[derive(QueryableByName)]`]: ../deserialize/derive.QueryableByName.html + #[derive(Clone, Copy, Debug)] + pub struct Untyped; + + /// Query nodes witch cannot be part of a select clause. + /// + /// If you see an error message containing `FromSqlRow` and this type + /// recheck that you have written a valid select clause + #[derive(Debug, Clone, Copy)] + pub struct NotSelectable; + + impl TypedExpressionType for Untyped {} + impl TypedExpressionType for NotSelectable {} + + impl TypedExpressionType for ST where ST: SingleValue {} + + impl QueryMetadata for DB { + fn row_metadata(_: &DB::MetadataLookup, row: &mut Vec>) { + row.push(None) + } + } +} + +impl Expression for Box { + type SqlType = T::SqlType; +} + +impl<'a, T: Expression + ?Sized> Expression for &'a T { + type SqlType = T::SqlType; +} + +/// A helper to translate type level sql type information into +/// runtime type information for specific queries +/// +/// If you do not implement a custom backend implementation +/// this trait is likely not relevant for you. +pub trait QueryMetadata: Backend { + /// The exact return value of this function is considerded to be a + /// backend specific implementation detail. You should not rely on those + /// values if you not own the corresponding backend + fn row_metadata(lookup: &Self::MetadataLookup, out: &mut Vec>); +} + +impl QueryMetadata for DB +where + DB: Backend + HasSqlType, + T: SingleValue, +{ + fn row_metadata(lookup: &Self::MetadataLookup, out: &mut Vec>) { + out.push(Some(>::metadata(lookup))) + } +} + +/// Converts a type to its representation for use in Diesel's query builder. +/// +/// This trait is used directly. Apps should typically use [`IntoSql`] instead. +/// +/// Implementations of this trait will generally do one of 3 things: +/// +/// - Return `self` for types which are already parts of Diesel's query builder +/// - Perform some implicit coercion (for example, allowing [`now`] to be used as +/// both [`Timestamp`] and [`Timestamptz`]. +/// - Indicate that the type has data which will be sent separately from the +/// query. This is generally referred as a "bind parameter". Types which +/// implement [`ToSql`] will generally implement `AsExpression` this way. +/// +/// [`IntoSql`]: trait.IntoSql.html +/// [`now`]: ../dsl/struct.now.html +/// [`Timestamp`]: ../sql_types/struct.Timestamp.html +/// [`Timestamptz`]: ../pg/types/sql_types/struct.Timestamptz.html +/// [`ToSql`]: ../serialize/trait.ToSql.html +/// +/// This trait could be [derived](derive.AsExpression.html) + +pub trait AsExpression +where + T: SqlType + TypedExpressionType, +{ + /// The expression being returned + type Expression: Expression; + + /// Perform the conversion + fn as_expression(self) -> Self::Expression; +} + +#[doc(inline)] +pub use diesel_derives::AsExpression; + +impl AsExpression for T +where + ST: SqlType + IntoNotNullable + TypedExpressionType, + ST::NotNullable: SingleValue, + self::as_expression_impl::ExpressionImplHelper::IsNull>: + self::as_expression_impl::AsExpressionHelper, + T: Expression, + T::SqlType: SqlType, +{ + type Expression = ::IsNull, + > as self::as_expression_impl::AsExpressionHelper>::Expression; + + fn as_expression(self) -> Self::Expression { + use self::as_expression_impl::AsExpressionHelper; + + let t = self::as_expression_impl::ExpressionImplHelper::< + _, + ST::IsNull, + ::IsNull, + >(self, std::marker::PhantomData); + t.as_expression() + } +} + +mod as_expression_impl { + use super::*; + use crate::sql_types::is_nullable; + + #[allow(missing_debug_implementations)] + pub struct ExpressionImplHelper( + pub T, + pub std::marker::PhantomData<(IsNullExpr, IsNullAsExpr)>, + ); + + // We could use `AsExpression` here instead of defining a new trait in theory + // in practice we hit https://github.com/rust-lang/rust/issues/77446 then + // when defining a custom type in a third party crate + pub trait AsExpressionHelper { + type Expression: Expression; + + fn as_expression(self) -> Self::Expression; + } + + // This impl is for accepting a not nullable expression in a position where + // a not nullable expression is expected + impl AsExpressionHelper + for ExpressionImplHelper + where + ST: SqlType + TypedExpressionType, + T: Expression, + { + type Expression = T; + + fn as_expression(self) -> Self::Expression { + self.0 + } + } + + // This impl is for accepting a not nullable expression in a position where + // a nullable expression is expected + impl AsExpressionHelper + for ExpressionImplHelper + where + ST: SqlType + IntoNotNullable + TypedExpressionType, + ST::NotNullable: TypedExpressionType + SqlType, + T: Expression, + super::nullable::Nullable: Expression, + { + type Expression = super::nullable::Nullable; + + fn as_expression(self) -> Self::Expression { + super::nullable::Nullable::new(self.0) + } + } + + // This impl is for accepting a nullable expression in a position where + // a nullable expression is expected + impl AsExpressionHelper + for ExpressionImplHelper + where + ST: SqlType + TypedExpressionType, + T: Expression, + { + type Expression = T; + + fn as_expression(self) -> Self::Expression { + self.0 + } + } + + // impl AsExpressionHelper for + // ExpressionImplHelper + // is missing because we don't want to accept a nullable expression in possition where + // where a not nullable expression is expected +} + +/// Converts a type to its representation for use in Diesel's query builder. +/// +/// This trait only exists to make usage of `AsExpression` more ergonomic when +/// the `SqlType` cannot be inferred. It is generally used when you need to use +/// a Rust value as the left hand side of an expression, or when you want to +/// select a constant value. +/// +/// # Example +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # use schema::users; +/// # +/// # fn main() { +/// use diesel::sql_types::Text; +/// # let conn = establish_connection(); +/// let names = users::table +/// .select("The Amazing ".into_sql::().concat(users::name)) +/// .load(&conn); +/// let expected_names = vec![ +/// "The Amazing Sean".to_string(), +/// "The Amazing Tess".to_string(), +/// ]; +/// assert_eq!(Ok(expected_names), names); +/// # } +/// ``` +pub trait IntoSql { + /// Convert `self` to an expression for Diesel's query builder. + /// + /// There is no difference in behavior between `x.into_sql::()` and + /// `AsExpression::::as_expression(x)`. + fn into_sql(self) -> AsExprOf + where + Self: AsExpression + Sized, + T: SqlType + TypedExpressionType, + { + self.as_expression() + } + + /// Convert `&self` to an expression for Diesel's query builder. + /// + /// There is no difference in behavior between `x.as_sql::()` and + /// `AsExpression::::as_expression(&x)`. + fn as_sql<'a, T>(&'a self) -> AsExprOf<&'a Self, T> + where + &'a Self: AsExpression, + T: SqlType + TypedExpressionType, + { + <&'a Self as AsExpression>::as_expression(self) + } +} + +impl IntoSql for T {} + +/// Indicates that all elements of an expression are valid given a from clause. +/// +/// This is used to ensure that `users.filter(posts::id.eq(1))` fails to +/// compile. This constraint is only used in places where the nullability of a +/// SQL type doesn't matter (everything except `select` and `returning`). For +/// places where nullability is important, `SelectableExpression` is used +/// instead. +pub trait AppearsOnTable: Expression {} + +impl AppearsOnTable for Box +where + T: AppearsOnTable, + Box: Expression, +{ +} + +impl<'a, T: ?Sized, QS> AppearsOnTable for &'a T +where + T: AppearsOnTable, + &'a T: Expression, +{ +} + +/// Indicates that an expression can be selected from a source. +/// +/// Columns will implement this for their table. Certain special types, like +/// `CountStar` and `Bound` will implement this for all sources. Most compound +/// expressions will implement this if each of their parts implement it. +/// +/// Notably, columns will not implement this trait for the right side of a left +/// join. To select a column or expression using a column from the right side of +/// a left join, you must call `.nullable()` on it. +pub trait SelectableExpression: AppearsOnTable {} + +impl SelectableExpression for Box +where + T: SelectableExpression, + Box: AppearsOnTable, +{ +} + +impl<'a, T: ?Sized, QS> SelectableExpression for &'a T +where + T: SelectableExpression, + &'a T: AppearsOnTable, +{ +} + +/// Is this expression valid for a given group by clause? +/// +/// Implementations of this trait must ensure that aggregate expressions are +/// not mixed with non-aggregate expressions. +/// +/// For generic types, you can determine if your sub-expresssions can appear +/// together using the [`MixedAggregates`] trait. +/// +/// `GroupByClause` will be a tuple containing the set of expressions appearing +/// in the `GROUP BY` portion of the query. If there is no `GROUP BY`, it will +/// be `()`. +/// +/// This trait can be [derived] +/// +/// [derived]: derive.ValidGrouping.html +/// [`MixedAggregates`]: trait.MixedAggregates.html +pub trait ValidGrouping { + /// Is this expression aggregate? + /// + /// This type should always be one of the structs in the [`is_aggregate`] + /// module. See the documentation of those structs for more details. + /// + /// [`is_aggregate`]: is_aggregate/index.html + type IsAggregate; +} + +impl + ?Sized, GB> ValidGrouping for Box { + type IsAggregate = T::IsAggregate; +} + +impl<'a, T: ValidGrouping + ?Sized, GB> ValidGrouping for &'a T { + type IsAggregate = T::IsAggregate; +} + +#[doc(inline)] +pub use diesel_derives::ValidGrouping; + +#[doc(hidden)] +pub trait IsContainedInGroupBy { + type Output; +} + +#[doc(hidden)] +#[allow(missing_debug_implementations, missing_copy_implementations)] +pub mod is_contained_in_group_by { + pub struct Yes; + pub struct No; + + pub trait IsAny { + type Output; + } + + impl IsAny for Yes { + type Output = Yes; + } + + impl IsAny for No { + type Output = Yes; + } + + impl IsAny for No { + type Output = No; + } +} + +/// Can two `IsAggregate` types appear in the same expression? +/// +/// You should never implement this trait. It will eventually become a trait +/// alias. +/// +/// [`is_aggregate::Yes`] and [`is_aggregate::No`] can only appear with +/// themselves or [`is_aggregate::Never`]. [`is_aggregate::Never`] can appear +/// with anything. +/// +/// [`is_aggregate::Yes`]: is_aggregate/struct.Yes.html +/// [`is_aggregate::No`]: is_aggregate/struct.No.html +/// [`is_aggregate::Never`]: is_aggregate/struct.Never.html +pub trait MixedAggregates { + /// What is the resulting `IsAggregate` type? + type Output; +} + +#[allow(missing_debug_implementations, missing_copy_implementations)] +/// Possible values for `ValidGrouping::IsAggregate` +pub mod is_aggregate { + use super::MixedAggregates; + + /// Yes, this expression is aggregate for the given group by clause. + pub struct Yes; + + /// No, this expression is not aggregate with the given group by clause, + /// but it might be aggregate with a different group by clause. + pub struct No; + + /// This expression is never aggregate, and can appear with any other + /// expression, regardless of whether it is aggregate. + /// + /// Examples of this are literals. `1` does not care about aggregation. + /// `foo + 1` is always valid, regardless of whether `foo` appears in the + /// group by clause or not. + pub struct Never; + + impl MixedAggregates for Yes { + type Output = Yes; + } + + impl MixedAggregates for Yes { + type Output = Yes; + } + + impl MixedAggregates for No { + type Output = No; + } + + impl MixedAggregates for No { + type Output = No; + } + + impl MixedAggregates for Never { + type Output = T; + } +} + +// Note that these docs are similar to but slightly different than the stable +// docs below. Make sure if you change these that you also change the docs +// below. +/// Trait alias to represent an expression that isn't aggregate by default. +/// +/// This alias represents a type which is not aggregate if there is no group by +/// clause. More specifically, it represents for types which implement +/// [`ValidGrouping<()>`] where `IsAggregate` is [`is_aggregate::No`] or +/// [`is_aggregate::Yes`]. +/// +/// While this trait is a useful stand-in for common cases, `T: NonAggregate` +/// cannot always be used when `T: ValidGrouping<(), IsAggregate = No>` or +/// `T: ValidGrouping<(), IsAggregate = Never>` could be. For that reason, +/// unless you need to abstract over both columns and literals, you should +/// prefer to use [`ValidGrouping<()>`] in your bounds instead. +/// +/// [`ValidGrouping<()>`]: trait.ValidGrouping.html +/// [`is_aggregate::Yes`]: is_aggregate/struct.Yes.html +/// [`is_aggregate::No`]: is_aggregate/struct.No.html +#[cfg(feature = "unstable")] +pub trait NonAggregate = ValidGrouping<()> +where + >::IsAggregate: + MixedAggregates; + +// Note that these docs are similar to but slightly different than the unstable +// docs above. Make sure if you change these that you also change the docs +// above. +/// Trait alias to represent an expression that isn't aggregate by default. +/// +/// This trait should never be implemented directly. It is replaced with a +/// trait alias when the `unstable` feature is enabled. +/// +/// This alias represents a type which is not aggregate if there is no group by +/// clause. More specifically, it represents for types which implement +/// [`ValidGrouping<()>`] where `IsAggregate` is [`is_aggregate::No`] or +/// [`is_aggregate::Yes`]. +/// +/// While this trait is a useful stand-in for common cases, `T: NonAggregate` +/// cannot always be used when `T: ValidGrouping<(), IsAggregate = No>` or +/// `T: ValidGrouping<(), IsAggregate = Never>` could be. For that reason, +/// unless you need to abstract over both columns and literals, you should +/// prefer to use [`ValidGrouping<()>`] in your bounds instead. +/// +/// [`ValidGrouping<()>`]: trait.ValidGrouping.html +/// [`is_aggregate::Yes`]: is_aggregate/struct.Yes.html +/// [`is_aggregate::No`]: is_aggregate/struct.No.html +#[cfg(not(feature = "unstable"))] +pub trait NonAggregate: ValidGrouping<()> {} + +#[cfg(not(feature = "unstable"))] +impl NonAggregate for T +where + T: ValidGrouping<()>, + T::IsAggregate: MixedAggregates, +{ +} + +use crate::query_builder::{QueryFragment, QueryId}; + +/// Helper trait used when boxing expressions. +/// +/// In Rust you cannot create a trait object with more than one trait. +/// This type has all of the additional traits you would want when using +/// `Box` as a single trait object. +/// +/// By default `BoxableExpression` is not usable in queries that have a custom +/// group by clause. Setting the generic parameters `GB` and `IsAggregate` allows +/// to configure the expression to be used with a specific group by clause. +/// +/// This is typically used as the return type of a function. +/// For cases where you want to dynamically construct a query, +/// [boxing the query] is usually more ergonomic. +/// +/// [boxing the query]: ../query_dsl/trait.QueryDsl.html#method.into_boxed +/// +/// # Examples +/// +/// ## Usage without group by clause +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # use schema::users; +/// use diesel::sql_types::Bool; +/// +/// # fn main() { +/// # run_test().unwrap(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # let conn = establish_connection(); +/// enum Search { +/// Id(i32), +/// Name(String), +/// } +/// +/// # /* +/// type DB = diesel::sqlite::Sqlite; +/// # */ +/// +/// fn find_user(search: Search) -> Box> { +/// match search { +/// Search::Id(id) => Box::new(users::id.eq(id)), +/// Search::Name(name) => Box::new(users::name.eq(name)), +/// } +/// } +/// +/// let user_one = users::table +/// .filter(find_user(Search::Id(1))) +/// .first(&conn)?; +/// assert_eq!((1, String::from("Sean")), user_one); +/// +/// let tess = users::table +/// .filter(find_user(Search::Name("Tess".into()))) +/// .first(&conn)?; +/// assert_eq!((2, String::from("Tess")), tess); +/// # Ok(()) +/// # } +/// ``` +/// +/// ## Allow usage with group by clause +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// +/// # use schema::users; +/// use diesel::sql_types::Text; +/// use diesel::dsl; +/// use diesel::expression::ValidGrouping; +/// +/// # fn main() { +/// # run_test().unwrap(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # let conn = establish_connection(); +/// enum NameOrConst { +/// Name, +/// Const(String), +/// } +/// +/// # /* +/// type DB = diesel::sqlite::Sqlite; +/// # */ +/// +/// fn selection( +/// selection: NameOrConst +/// ) -> Box< +/// dyn BoxableExpression< +/// users::table, +/// DB, +/// GB, +/// >::IsAggregate, +/// SqlType = Text +/// > +/// > +/// where +/// users::name: BoxableExpression< +/// users::table, +/// DB, +/// GB, +/// >::IsAggregate, +/// SqlType = Text +/// > + ValidGrouping, +/// { +/// match selection { +/// NameOrConst::Name => Box::new(users::name), +/// NameOrConst::Const(name) => Box::new(name.into_sql::()), +/// } +/// } +/// +/// let user_one = users::table +/// .select(selection(NameOrConst::Name)) +/// .first::(&conn)?; +/// assert_eq!(String::from("Sean"), user_one); +/// +/// let with_name = users::table +/// .group_by(users::name) +/// .select(selection(NameOrConst::Const("Jane Doe".into()))) +/// .first::(&conn)?; +/// assert_eq!(String::from("Jane Doe"), with_name); +/// # Ok(()) +/// # } +/// ``` +pub trait BoxableExpression +where + DB: Backend, + Self: Expression, + Self: SelectableExpression, + Self: QueryFragment, +{ +} + +impl BoxableExpression for T +where + DB: Backend, + T: Expression, + T: SelectableExpression, + T: ValidGrouping, + T: QueryFragment, + T::IsAggregate: MixedAggregates, +{ +} + +impl<'a, QS, ST, DB, GB, IsAggregate> QueryId + for dyn BoxableExpression + 'a +{ + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +impl<'a, QS, ST, DB, GB, IsAggregate> ValidGrouping + for dyn BoxableExpression + 'a +{ + type IsAggregate = IsAggregate; +} + +/// Converts a tuple of values into a tuple of Diesel expressions. +/// +/// This trait is similar to [`AsExpression`], but it operates on tuples. +/// The expressions must all be of the same SQL type. +/// +/// [`AsExpression`]: trait.AsExpression.html +pub trait AsExpressionList { + /// The final output expression + type Expression; + + /// Perform the conversion + fn as_expression_list(self) -> Self::Expression; +} diff --git a/collector/benchmarks/diesel/diesel/src/expression/not.rs b/collector/benchmarks/diesel/diesel/src/expression/not.rs new file mode 100644 index 000000000..96b7916c3 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/not.rs @@ -0,0 +1,31 @@ +use crate::expression::grouped::Grouped; +use crate::expression::AsExpression; +use crate::helper_types::not; +use crate::sql_types::{Bool, Nullable}; + +/// Creates a SQL `NOT` expression +/// +/// # Example +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # +/// # fn main() { +/// # use schema::users::dsl::*; +/// # let connection = establish_connection(); +/// use diesel::dsl::not; +/// +/// let users_with_name = users.select(id).filter(name.eq("Sean")); +/// let users_not_with_name = users.select(id).filter( +/// not(name.eq("Sean"))); +/// +/// assert_eq!(Ok(1), users_with_name.first(&connection)); +/// assert_eq!(Ok(2), users_not_with_name.first(&connection)); +/// # } +/// ``` +pub fn not(expr: T) -> not +where + T: AsExpression>, +{ + super::operators::Not::new(Grouped(expr.as_expression())) +} diff --git a/collector/benchmarks/diesel/diesel/src/expression/nullable.rs b/collector/benchmarks/diesel/diesel/src/expression/nullable.rs new file mode 100644 index 000000000..ee4b23481 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/nullable.rs @@ -0,0 +1,60 @@ +use crate::backend::Backend; +use crate::expression::TypedExpressionType; +use crate::expression::*; +use crate::query_builder::*; +use crate::query_source::joins::ToInnerJoin; +use crate::result::QueryResult; +use crate::sql_types::{DieselNumericOps, IntoNullable}; + +#[derive(Debug, Copy, Clone, DieselNumericOps, ValidGrouping)] +pub struct Nullable(T); + +impl Nullable { + pub fn new(expr: T) -> Self { + Nullable(expr) + } +} + +impl Expression for Nullable +where + T: Expression, + T::SqlType: IntoNullable, + ::Nullable: TypedExpressionType, +{ + type SqlType = ::Nullable; +} + +impl QueryFragment for Nullable +where + DB: Backend, + T: QueryFragment, +{ + fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + self.0.walk_ast(pass) + } +} + +/// Nullable can be used in where clauses everywhere, but can only be used in +/// select clauses for outer joins. +impl AppearsOnTable for Nullable +where + T: AppearsOnTable, + Nullable: Expression, +{ +} + +impl QueryId for Nullable { + type QueryId = T::QueryId; + + const HAS_STATIC_QUERY_ID: bool = T::HAS_STATIC_QUERY_ID; +} + +impl SelectableExpression for Nullable +where + Self: AppearsOnTable, + QS: ToInnerJoin, + T: SelectableExpression, +{ +} + +impl SelectableExpression<()> for Nullable where Self: AppearsOnTable<()> {} diff --git a/collector/benchmarks/diesel/diesel/src/expression/operators.rs b/collector/benchmarks/diesel/diesel/src/expression/operators.rs new file mode 100644 index 000000000..e68269be0 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/operators.rs @@ -0,0 +1,586 @@ +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_operator_body { + ( + notation = $notation:ident, + struct_name = $name:ident, + operator = $operator:expr, + return_ty = (ReturnBasedOnArgs), + ty_params = ($($ty_param:ident,)+), + field_names = $field_names:tt, + backend_ty_params = $backend_ty_params:tt, + backend_ty = $backend_ty:ty, + ) => { + $crate::__diesel_operator_body! { + notation = $notation, + struct_name = $name, + operator = $operator, + return_ty = (ST), + ty_params = ($($ty_param,)+), + field_names = $field_names, + backend_ty_params = $backend_ty_params, + backend_ty = $backend_ty, + expression_ty_params = (ST,), + expression_bounds = ($($ty_param: $crate::expression::Expression,)+), + } + }; + + ( + notation = $notation:ident, + struct_name = $name:ident, + operator = $operator:expr, + return_ty = ($($return_ty:tt)+), + ty_params = ($($ty_param:ident,)+), + field_names = $field_names:tt, + backend_ty_params = $backend_ty_params:tt, + backend_ty = $backend_ty:ty, + ) => { + $crate::__diesel_operator_body! { + notation = $notation, + struct_name = $name, + operator = $operator, + return_ty = ($($return_ty)*), + ty_params = ($($ty_param,)+), + field_names = $field_names, + backend_ty_params = $backend_ty_params, + backend_ty = $backend_ty, + expression_ty_params = (), + expression_bounds = ($($ty_param: $crate::expression::Expression,)+), + } + }; + + ( + notation = $notation:ident, + struct_name = $name:ident, + operator = $operator:expr, + return_ty = ($($return_ty:tt)+), + ty_params = ($($ty_param:ident,)+), + field_names = ($($field_name:ident,)+), + backend_ty_params = ($($backend_ty_param:ident,)*), + backend_ty = $backend_ty:ty, + expression_ty_params = ($($expression_ty_params:ident,)*), + expression_bounds = ($($expression_bounds:tt)*), + ) => { + #[derive( + Debug, + Clone, + Copy, + $crate::query_builder::QueryId, + $crate::sql_types::DieselNumericOps, + $crate::expression::ValidGrouping + )] + #[doc(hidden)] + pub struct $name<$($ty_param,)+> { + $(pub(crate) $field_name: $ty_param,)+ + } + + impl<$($ty_param,)+> $name<$($ty_param,)+> { + pub fn new($($field_name: $ty_param,)+) -> Self { + $name { $($field_name,)+ } + } + } + + $crate::impl_selectable_expression!($name<$($ty_param),+>); + + impl<$($ty_param,)+ $($expression_ty_params,)*> $crate::expression::Expression for $name<$($ty_param,)+> where + $($expression_bounds)* + { + type SqlType = $($return_ty)*; + } + + impl<$($ty_param,)+ $($backend_ty_param,)*> $crate::query_builder::QueryFragment<$backend_ty> + for $name<$($ty_param,)+> where + $($ty_param: $crate::query_builder::QueryFragment<$backend_ty>,)+ + $($backend_ty_param: $crate::backend::Backend,)* + { + fn walk_ast(&self, mut out: $crate::query_builder::AstPass<$backend_ty>) -> $crate::result::QueryResult<()> { + $crate::__diesel_operator_to_sql!( + notation = $notation, + operator_expr = out.push_sql($operator), + field_exprs = ($(self.$field_name.walk_ast(out.reborrow())?),+), + ); + Ok(()) + } + } + } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_operator_to_sql { + ( + notation = infix, + operator_expr = $op:expr, + field_exprs = ($left:expr, $right:expr), + ) => { + $left; + $op; + $right; + }; + + ( + notation = postfix, + operator_expr = $op:expr, + field_exprs = ($expr:expr), + ) => { + $expr; + $op; + }; + + ( + notation = prefix, + operator_expr = $op:expr, + field_exprs = ($expr:expr), + ) => { + $op; + $expr; + }; +} + +/// Useful for libraries adding support for new SQL types. Apps should never +/// need to call this. +/// +/// This will create a new type with the given name. It will implement all +/// methods needed to be used as an expression in Diesel, placing the given +/// SQL between the two elements. The third argument specifies the SQL type +/// that the operator returns. If it is not given, the type will be assumed +/// to be `Bool`. +/// +/// If the operator is specific to a single backend, you can specify this by +/// adding `backend: Pg` or similar as the last argument. +/// +/// It should be noted that the generated impls will not constrain the SQL +/// types of the arguments. You should ensure that they are of the right +/// type in your function which constructs the operator. +/// +/// Typically you would not expose the type that this generates directly. You'd +/// expose a function (or trait) used to construct the expression, and a helper +/// type which represents the return type of that function. See the source of +/// `diesel::expression::expression_methods` and +/// `diesel::expression::helper_types` for real world examples of this. +/// +/// # Examples +/// +/// # Possible invocations +/// +/// ```ignore +/// // The SQL type will be boolean. The backend will not be constrained +/// infix_operator!(Matches, " @@ "); +/// +/// // Queries which try to execute `Contains` on a backend other than Pg +/// // will fail to compile +/// infix_operator!(Contains, " @> ", backend: Pg); +/// +/// // The type of `Concat` will be `TsVector` rather than Bool +/// infix_operator!(Concat, " || ", TsVector); +/// +/// // It is perfectly fine to have multiple operators with the same SQL. +/// // Diesel will ensure that the queries are always unambiguous in which +/// // operator applies +/// infix_operator!(Or, " || ", TsQuery); +/// +/// // Specifying both the return types and the backend +/// infix_operator!(And, " && ", TsQuery, backend: Pg); +/// ``` +/// +/// ## Example usage +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # use diesel::sql_types::SqlType; +/// # use diesel::expression::TypedExpressionType; +/// # +/// # fn main() { +/// # use schema::users::dsl::*; +/// # let connection = establish_connection(); +/// diesel::infix_operator!(MyEq, " = "); +/// +/// use diesel::expression::AsExpression; +/// +/// // Normally you would put this on a trait instead +/// fn my_eq(left: T, right: U) -> MyEq where +/// T: Expression, +/// U: AsExpression, +/// ST: SqlType + TypedExpressionType, +/// { +/// MyEq::new(left, right.as_expression()) +/// } +/// +/// let users_with_name = users.select(id).filter(my_eq(name, "Sean")); +/// +/// assert_eq!(Ok(1), users_with_name.first(&connection)); +/// # } +/// ``` +#[macro_export] +macro_rules! infix_operator { + ($name:ident, $operator:expr) => { + $crate::infix_operator!($name, $operator, $crate::sql_types::Bool); + }; + + ($name:ident, $operator:expr, backend: $backend:ty) => { + $crate::infix_operator!($name, $operator, $crate::sql_types::Bool, backend: $backend); + }; + + ($name:ident, $operator:expr, $($return_ty:tt)::*) => { + $crate::__diesel_operator_body!( + notation = infix, + struct_name = $name, + operator = $operator, + return_ty = ( + $crate::sql_types::is_nullable::MaybeNullable< + $crate::sql_types::is_nullable::IsOneNullable< + ::SqlType, + ::SqlType + >, + $($return_ty)::* + > + ), + ty_params = (T, U,), + field_names = (left, right,), + backend_ty_params = (DB,), + backend_ty = DB, + expression_ty_params = (), + expression_bounds = ( + T: $crate::expression::Expression, + U: $crate::expression::Expression, + ::SqlType: $crate::sql_types::SqlType, + ::SqlType: $crate::sql_types::SqlType, + $crate::sql_types::is_nullable::IsSqlTypeNullable< + ::SqlType + >: $crate::sql_types::OneIsNullable< + $crate::sql_types::is_nullable::IsSqlTypeNullable< + ::SqlType + > + >, + $crate::sql_types::is_nullable::IsOneNullable< + ::SqlType, + ::SqlType + >: $crate::sql_types::MaybeNullableType<$($return_ty)::*>, + ), + ); + }; + + ($name:ident, $operator:expr, $return_ty:ty, backend: $backend:ty) => { + $crate::__diesel_operator_body!( + notation = infix, + struct_name = $name, + operator = $operator, + return_ty = ( + $crate::sql_types::is_nullable::MaybeNullable< + $crate::sql_types::is_nullable::IsOneNullable< + ::SqlType, + ::SqlType + >, + $return_ty, + > + ), + ty_params = (T, U,), + field_names = (left, right,), + backend_ty_params = (), + backend_ty = $backend, + expression_ty_params = (), + expression_bounds = ( + T: $crate::expression::Expression, + U: $crate::expression::Expression, + ::SqlType: $crate::sql_types::SqlType, + ::SqlType: $crate::sql_types::SqlType, + $crate::sql_types::is_nullable::IsSqlTypeNullable< + ::SqlType + >: $crate::sql_types::OneIsNullable< + $crate::sql_types::is_nullable::IsSqlTypeNullable< + ::SqlType + > + >, + $crate::sql_types::is_nullable::IsOneNullable< + ::SqlType, + ::SqlType + >: $crate::sql_types::MaybeNullableType<$return_ty>, + ), + ); + }; +} + +#[macro_export] +#[deprecated(since = "2.0.0", note = "use `diesel::infix_operator!` instead")] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +#[doc(hidden)] +macro_rules! diesel_infix_operator { + ($($args:tt)*) => { + $crate::infix_operator!($($args)*); + } +} + +/// Useful for libraries adding support for new SQL types. Apps should never +/// need to call this. +/// +/// Similar to [`infix_operator!`], but the generated type will only take +/// a single argument rather than two. The operator SQL will be placed after +/// the single argument. See [`infix_operator!`] for example usage. +/// +/// [`infix_operator!`]: macro.infix_operator.html +#[macro_export] +macro_rules! postfix_operator { + ($name:ident, $operator:expr) => { + $crate::postfix_operator!($name, $operator, $crate::sql_types::Bool); + }; + + ($name:ident, $operator:expr, backend: $backend:ty) => { + $crate::postfix_operator!($name, $operator, $crate::sql_types::Bool, backend: $backend); + }; + + ($name:ident, $operator:expr, $return_ty:ty) => { + $crate::__diesel_operator_body!( + notation = postfix, + struct_name = $name, + operator = $operator, + return_ty = ($return_ty), + ty_params = (Expr,), + field_names = (expr,), + backend_ty_params = (DB,), + backend_ty = DB, + ); + }; + + ($name:ident, $operator:expr, $return_ty:ty, backend: $backend:ty) => { + $crate::__diesel_operator_body!( + notation = postfix, + struct_name = $name, + operator = $operator, + return_ty = ($return_ty), + ty_params = (Expr,), + field_names = (expr,), + backend_ty_params = (), + backend_ty = $backend, + ); + }; +} + +#[macro_export] +#[deprecated(since = "2.0.0", note = "use `diesel::postfix_operator!` instead")] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +#[doc(hidden)] +macro_rules! diesel_postfix_operator { + ($($args:tt)*) => { + $crate::postfix_operator!($($args)*); + } +} + +/// Useful for libraries adding support for new SQL types. Apps should never +/// need to call this. +/// +/// Similar to [`infix_operator!`], but the generated type will only take +/// a single argument rather than two. The operator SQL will be placed before +/// the single argument. See [`infix_operator!`] for example usage. +/// +/// [`infix_operator!`]: macro.infix_operator.html +#[macro_export] +macro_rules! prefix_operator { + ($name:ident, $operator:expr) => { + $crate::prefix_operator!($name, $operator, $crate::sql_types::Bool); + }; + + ($name:ident, $operator:expr, backend: $backend:ty) => { + $crate::prefix_operator!($name, $operator, $crate::sql_types::Bool, backend: $backend); + }; + + ($name:ident, $operator:expr, $return_ty:ty) => { + $crate::__diesel_operator_body!( + notation = prefix, + struct_name = $name, + operator = $operator, + return_ty = ($return_ty), + ty_params = (Expr,), + field_names = (expr,), + backend_ty_params = (DB,), + backend_ty = DB, + ); + }; + + ($name:ident, $operator:expr, $return_ty:ty, backend: $backend:ty) => { + $crate::__diesel_operator_body!( + notation = prefix, + struct_name = $name, + operator = $operator, + return_ty = ($return_ty), + ty_params = (Expr,), + field_names = (expr,), + backend_ty_params = (), + backend_ty = $backend, + ); + }; +} + +#[macro_export] +#[deprecated(since = "2.0.0", note = "use `diesel::prefix_operator!` instead")] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +#[doc(hidden)] +macro_rules! diesel_prefix_operator { + ($($args:tt)*) => { + $crate::prefix_operator!($($args)*); + } +} + +infix_operator!(And, " AND "); +infix_operator!(Escape, " ESCAPE "); +infix_operator!(Eq, " = "); +infix_operator!(Gt, " > "); +infix_operator!(GtEq, " >= "); +infix_operator!(Like, " LIKE "); +infix_operator!(Lt, " < "); +infix_operator!(LtEq, " <= "); +infix_operator!(NotEq, " != "); +infix_operator!(NotLike, " NOT LIKE "); +infix_operator!(Between, " BETWEEN "); +infix_operator!(NotBetween, " NOT BETWEEN "); + +postfix_operator!(IsNull, " IS NULL"); +postfix_operator!(IsNotNull, " IS NOT NULL"); +postfix_operator!( + Asc, + " ASC ", + crate::expression::expression_types::NotSelectable +); +postfix_operator!( + Desc, + " DESC ", + crate::expression::expression_types::NotSelectable +); + +prefix_operator!( + Not, + " NOT ", + crate::sql_types::Nullable +); + +use crate::backend::Backend; +use crate::expression::{TypedExpressionType, ValidGrouping}; +use crate::insertable::{ColumnInsertValue, Insertable}; +use crate::query_builder::{QueryFragment, QueryId, ValuesClause}; +use crate::query_source::Column; +use crate::sql_types::{ + is_nullable, AllAreNullable, Bool, DieselNumericOps, MaybeNullableType, SqlType, +}; +use crate::Expression; + +impl Insertable for Eq +where + T: Column, +{ + type Values = ValuesClause, T::Table>; + + fn values(self) -> Self::Values { + ValuesClause::new(ColumnInsertValue::Expression(self.left, self.right)) + } +} + +impl<'a, T, Tab, U> Insertable for &'a Eq +where + T: Copy, + Eq: Insertable, +{ + type Values = as Insertable>::Values; + + fn values(self) -> Self::Values { + Eq::new(self.left, &self.right).values() + } +} + +#[derive(Debug, Clone, Copy, QueryId, DieselNumericOps, ValidGrouping)] +#[doc(hidden)] +pub struct Concat { + pub(crate) left: L, + pub(crate) right: R, +} + +impl Concat { + pub fn new(left: L, right: R) -> Self { + Self { left, right } + } +} + +impl crate::expression::Expression for Concat +where + L: crate::expression::Expression, + R: crate::expression::Expression, + ST: SqlType + TypedExpressionType, +{ + type SqlType = ST; +} + +impl_selectable_expression!(Concat); + +impl QueryFragment for Concat +where + L: QueryFragment, + R: QueryFragment, + DB: Backend, +{ + fn walk_ast( + &self, + mut out: crate::query_builder::AstPass, + ) -> crate::result::QueryResult<()> { + // Those brackets are required because mysql is broken + // https://github.com/diesel-rs/diesel/issues/2133#issuecomment-517432317 + out.push_sql("("); + self.left.walk_ast(out.reborrow())?; + out.push_sql(" || "); + self.right.walk_ast(out.reborrow())?; + out.push_sql(")"); + Ok(()) + } +} + +// or is different +// it only evaluates to null if both sides are null +#[derive( + Debug, + Clone, + Copy, + crate::query_builder::QueryId, + crate::sql_types::DieselNumericOps, + crate::expression::ValidGrouping, +)] +#[doc(hidden)] +pub struct Or { + pub(crate) left: T, + pub(crate) right: U, +} + +impl Or { + pub fn new(left: T, right: U) -> Self { + Or { left, right } + } +} + +impl_selectable_expression!(Or); + +impl Expression for Or +where + T: Expression, + U: Expression, + T::SqlType: SqlType, + U::SqlType: SqlType, + is_nullable::IsSqlTypeNullable: + AllAreNullable>, + is_nullable::AreAllNullable: MaybeNullableType, +{ + type SqlType = + is_nullable::MaybeNullable, Bool>; +} + +impl crate::query_builder::QueryFragment for Or +where + DB: crate::backend::Backend, + T: crate::query_builder::QueryFragment, + U: crate::query_builder::QueryFragment, +{ + fn walk_ast( + &self, + mut out: crate::query_builder::AstPass, + ) -> crate::result::QueryResult<()> { + self.left.walk_ast(out.reborrow())?; + out.push_sql(" OR "); + self.right.walk_ast(out.reborrow())?; + Ok(()) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/expression/ops/mod.rs b/collector/benchmarks/diesel/diesel/src/expression/ops/mod.rs new file mode 100644 index 000000000..580a2932d --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/ops/mod.rs @@ -0,0 +1,31 @@ +macro_rules! generic_numeric_expr_inner { + ($tpe: ident, ($($param: ident),*), $op: ident, $fn_name: ident) => { + impl ::std::ops::$op for $tpe<$($param),*> where + $tpe<$($param),*>: $crate::expression::Expression, + <$tpe<$($param),*> as $crate::Expression>::SqlType: $crate::sql_types::SqlType + $crate::sql_types::ops::$op, + <<$tpe<$($param),*> as $crate::Expression>::SqlType as $crate::sql_types::ops::$op>::Rhs: $crate::expression::TypedExpressionType, + Rhs: $crate::expression::AsExpression< + <<$tpe<$($param),*> as $crate::Expression>::SqlType as $crate::sql_types::ops::$op>::Rhs, + >, + { + type Output = $crate::expression::ops::$op; + + fn $fn_name(self, rhs: Rhs) -> Self::Output { + $crate::expression::ops::$op::new(self, rhs.as_expression()) + } + } + } +} + +macro_rules! generic_numeric_expr { + ($tpe: ident, $($param: ident),*) => { + generic_numeric_expr_inner!($tpe, ($($param),*), Add, add); + generic_numeric_expr_inner!($tpe, ($($param),*), Sub, sub); + generic_numeric_expr_inner!($tpe, ($($param),*), Div, div); + generic_numeric_expr_inner!($tpe, ($($param),*), Mul, mul); + } +} + +mod numeric; + +pub use self::numeric::{Add, Div, Mul, Sub}; diff --git a/collector/benchmarks/diesel/diesel/src/expression/ops/numeric.rs b/collector/benchmarks/diesel/diesel/src/expression/ops/numeric.rs new file mode 100644 index 000000000..1d5a5d415 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/ops/numeric.rs @@ -0,0 +1,58 @@ +use crate::backend::Backend; +use crate::expression::{Expression, TypedExpressionType, ValidGrouping}; +use crate::query_builder::*; +use crate::result::QueryResult; +use crate::sql_types; + +macro_rules! numeric_operation { + ($name:ident, $op:expr) => { + #[derive(Debug, Copy, Clone, QueryId, ValidGrouping)] + pub struct $name { + lhs: Lhs, + rhs: Rhs, + } + + impl $name { + pub fn new(left: Lhs, right: Rhs) -> Self { + $name { + lhs: left, + rhs: right, + } + } + } + + impl Expression for $name + where + Lhs: Expression, + Lhs::SqlType: sql_types::ops::$name, + Rhs: Expression, + ::Output: TypedExpressionType, + { + type SqlType = ::Output; + } + + impl QueryFragment for $name + where + DB: Backend, + Lhs: QueryFragment, + Rhs: QueryFragment, + { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("("); + self.lhs.walk_ast(out.reborrow())?; + out.push_sql($op); + self.rhs.walk_ast(out.reborrow())?; + out.push_sql(")"); + Ok(()) + } + } + + impl_selectable_expression!($name); + generic_numeric_expr!($name, A, B); + }; +} + +numeric_operation!(Add, " + "); +numeric_operation!(Sub, " - "); +numeric_operation!(Mul, " * "); +numeric_operation!(Div, " / "); diff --git a/collector/benchmarks/diesel/diesel/src/expression/sql_literal.rs b/collector/benchmarks/diesel/diesel/src/expression/sql_literal.rs new file mode 100644 index 000000000..68c0e0abe --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/sql_literal.rs @@ -0,0 +1,348 @@ +use std::marker::PhantomData; + +use crate::backend::Backend; +use crate::expression::*; +use crate::query_builder::*; +use crate::query_dsl::RunQueryDsl; +use crate::result::QueryResult; +use crate::sql_types::{DieselNumericOps, SqlType}; + +#[derive(Debug, Clone, DieselNumericOps)] +#[must_use = "Queries are only executed when calling `load`, `get_result`, or similar."] +/// Returned by the [`sql()`] function. +/// +/// [`sql()`]: ../dsl/fn.sql.html +pub struct SqlLiteral { + sql: String, + inner: T, + _marker: PhantomData, +} + +impl SqlLiteral +where + ST: TypedExpressionType, +{ + #[doc(hidden)] + pub fn new(sql: String, inner: T) -> Self { + SqlLiteral { + sql: sql, + inner: inner, + _marker: PhantomData, + } + } + + /// Bind a value for use with this SQL query. + /// + /// # Safety + /// + /// This function should be used with care, as Diesel cannot validate that + /// the value is of the right type nor can it validate that you have passed + /// the correct number of parameters. + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # table! { + /// # users { + /// # id -> Integer, + /// # name -> VarChar, + /// # } + /// # } + /// # + /// # fn main() { + /// # use self::users::dsl::*; + /// # use diesel::dsl::sql; + /// # use diesel::sql_types::{Integer, Text, Bool}; + /// # let connection = establish_connection(); + /// let seans_id = users + /// .select(id) + /// .filter(sql::("name = ").bind::("Sean")) + /// .get_result(&connection); + /// assert_eq!(Ok(1), seans_id); + /// + /// let tess_id = sql::("SELECT id FROM users WHERE name = ") + /// .bind::("Tess") + /// .get_result(&connection); + /// assert_eq!(Ok(2), tess_id); + /// # } + /// ``` + /// + /// ### Multiple Bind Params + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// + /// # table! { + /// # users { + /// # id -> Integer, + /// # name -> VarChar, + /// # } + /// # } + /// # + /// # fn main() { + /// # use self::users::dsl::*; + /// # use diesel::dsl::sql; + /// # use diesel::sql_types::{Integer, Text, Bool}; + /// # let connection = establish_connection(); + /// # diesel::insert_into(users).values(name.eq("Ryan")) + /// # .execute(&connection).unwrap(); + /// let query = users + /// .select(name) + /// .filter( + /// sql::("id > ") + /// .bind::(1) + /// .sql(" AND name <> ") + /// .bind::("Ryan") + /// ) + /// .get_results(&connection); + /// let expected = vec!["Tess".to_string()]; + /// assert_eq!(Ok(expected), query); + /// # } + /// ``` + pub fn bind(self, bind_value: U) -> UncheckedBind + where + BindST: SqlType + TypedExpressionType, + U: AsExpression, + { + UncheckedBind::new(self, bind_value.as_expression()) + } + + /// Use literal SQL in the query builder + /// + /// This function is intended for use when you need a small bit of raw SQL in + /// your query. If you want to write the entire query using raw SQL, use + /// [`sql_query`](../fn.sql_query.html) instead. + /// + /// # Safety + /// + /// This function should be used with care, as Diesel cannot validate that + /// the value is of the right type nor can it validate that you have passed + /// the correct number of parameters. + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// + /// # table! { + /// # users { + /// # id -> Integer, + /// # name -> VarChar, + /// # } + /// # } + /// # + /// # fn main() { + /// # use self::users::dsl::*; + /// # use diesel::dsl::sql; + /// # use diesel::sql_types::Bool; + /// # let connection = establish_connection(); + /// # diesel::insert_into(users).values(name.eq("Ryan")) + /// # .execute(&connection).unwrap(); + /// let query = users + /// .select(name) + /// .filter( + /// sql::("id > 1") + /// .sql(" AND name <> 'Ryan'") + /// ) + /// .get_results(&connection); + /// let expected = vec!["Tess".to_string()]; + /// assert_eq!(Ok(expected), query); + /// # } + /// ``` + pub fn sql(self, sql: &str) -> SqlLiteral { + SqlLiteral::new(sql.into(), self) + } +} + +impl Expression for SqlLiteral +where + ST: TypedExpressionType, +{ + type SqlType = ST; +} + +impl QueryFragment for SqlLiteral +where + DB: Backend, + T: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.unsafe_to_cache_prepared(); + self.inner.walk_ast(out.reborrow())?; + out.push_sql(&self.sql); + Ok(()) + } +} + +impl QueryId for SqlLiteral { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +impl Query for SqlLiteral +where + Self: Expression, +{ + type SqlType = ST; +} + +impl RunQueryDsl for SqlLiteral {} + +impl SelectableExpression for SqlLiteral where Self: Expression {} + +impl AppearsOnTable for SqlLiteral where Self: Expression {} + +impl ValidGrouping for SqlLiteral { + type IsAggregate = is_aggregate::Never; +} + +/// Use literal SQL in the query builder +/// +/// Available for when you truly cannot represent something using the expression +/// DSL. You will need to provide the SQL type of the expression, in addition to +/// the SQL. +/// +/// This function is intended for use when you need a small bit of raw SQL in +/// your query. If you want to write the entire query using raw SQL, use +/// [`sql_query`](../fn.sql_query.html) instead. +/// +/// # Safety +/// +/// The compiler will be unable to verify the correctness of the annotated type. +/// If you give the wrong type, it'll either return an error when deserializing +/// the query result or produce unexpected values. +/// +/// # Examples +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # fn main() { +/// # run_test().unwrap(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use schema::users::dsl::*; +/// # use diesel::sql_types::Bool; +/// use diesel::dsl::sql; +/// # let connection = establish_connection(); +/// let user = users.filter(sql::("name = 'Sean'")).first(&connection)?; +/// let expected = (1, String::from("Sean")); +/// assert_eq!(expected, user); +/// # Ok(()) +/// # } +/// ``` +pub fn sql(sql: &str) -> SqlLiteral +where + ST: TypedExpressionType, +{ + SqlLiteral::new(sql.into(), ()) +} + +#[derive(QueryId, Debug, Clone, Copy)] +#[must_use = "Queries are only executed when calling `load`, `get_result`, or similar."] +/// Returned by the [`SqlLiteral::bind()`] method when binding a value to a fragment of SQL. +/// +/// [`bind()`]: ./struct.SqlLiteral.html#method.bind +pub struct UncheckedBind { + query: Query, + value: Value, +} + +impl UncheckedBind +where + Query: Expression, +{ + pub(crate) fn new(query: Query, value: Value) -> Self { + UncheckedBind { query, value } + } + + /// Use literal SQL in the query builder + /// + /// This function is intended for use when you need a small bit of raw SQL in + /// your query. If you want to write the entire query using raw SQL, use + /// [`sql_query`](../fn.sql_query.html) instead. + /// + /// # Safety + /// + /// This function should be used with care, as Diesel cannot validate that + /// the value is of the right type nor can it validate that you have passed + /// the correct number of parameters. + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// + /// # table! { + /// # users { + /// # id -> Integer, + /// # name -> VarChar, + /// # } + /// # } + /// # + /// # fn main() { + /// # use self::users::dsl::*; + /// # use diesel::dsl::sql; + /// # use diesel::sql_types::{Integer, Bool}; + /// # let connection = establish_connection(); + /// # diesel::insert_into(users).values(name.eq("Ryan")) + /// # .execute(&connection).unwrap(); + /// let query = users + /// .select(name) + /// .filter( + /// sql::("id > ") + /// .bind::(1) + /// .sql(" AND name <> 'Ryan'") + /// ) + /// .get_results(&connection); + /// let expected = vec!["Tess".to_string()]; + /// assert_eq!(Ok(expected), query); + /// # } + /// ``` + pub fn sql(self, sql: &str) -> SqlLiteral { + SqlLiteral::new(sql.into(), self) + } +} + +impl Expression for UncheckedBind +where + Query: Expression, +{ + type SqlType = Query::SqlType; +} + +impl QueryFragment for UncheckedBind +where + DB: Backend, + Query: QueryFragment, + Value: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + self.query.walk_ast(out.reborrow())?; + self.value.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl Query for UncheckedBind +where + Q: Query, +{ + type SqlType = Q::SqlType; +} + +impl ValidGrouping for UncheckedBind { + type IsAggregate = is_aggregate::Never; +} + +impl SelectableExpression for UncheckedBind where + Self: AppearsOnTable +{ +} + +impl AppearsOnTable for UncheckedBind where Self: Expression {} + +impl RunQueryDsl for UncheckedBind {} diff --git a/collector/benchmarks/diesel/diesel/src/expression/subselect.rs b/collector/benchmarks/diesel/diesel/src/expression/subselect.rs new file mode 100644 index 000000000..a0bab4e8b --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression/subselect.rs @@ -0,0 +1,69 @@ +use std::marker::PhantomData; + +use crate::expression::array_comparison::MaybeEmpty; +use crate::expression::*; +use crate::query_builder::*; +use crate::result::QueryResult; +use crate::sql_types::SqlType; + +#[derive(Debug, Copy, Clone, QueryId)] +pub struct Subselect { + values: T, + _sql_type: PhantomData, +} + +impl Subselect { + pub(crate) fn new(values: T) -> Self { + Self { + values, + _sql_type: PhantomData, + } + } +} + +impl Expression for Subselect +where + ST: SqlType + TypedExpressionType, +{ + type SqlType = ST; +} + +impl MaybeEmpty for Subselect { + fn is_empty(&self) -> bool { + false + } +} + +impl SelectableExpression for Subselect +where + Subselect: AppearsOnTable, + T: ValidSubselect, +{ +} + +impl AppearsOnTable for Subselect +where + Subselect: Expression, + T: ValidSubselect, +{ +} + +// FIXME: This probably isn't sound. The subselect can reference columns from +// the outer query, and is affected by the `GROUP BY` clause of the outer query +// identically to using it outside of a subselect +impl ValidGrouping for Subselect { + type IsAggregate = is_aggregate::Never; +} + +impl QueryFragment for Subselect +where + DB: Backend, + T: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + self.values.walk_ast(out.reborrow())?; + Ok(()) + } +} + +pub trait ValidSubselect {} diff --git a/collector/benchmarks/diesel/diesel/src/expression_methods/bool_expression_methods.rs b/collector/benchmarks/diesel/diesel/src/expression_methods/bool_expression_methods.rs new file mode 100644 index 000000000..9db25db86 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression_methods/bool_expression_methods.rs @@ -0,0 +1,106 @@ +use crate::dsl; +use crate::expression::grouped::Grouped; +use crate::expression::operators::{And, Or}; +use crate::expression::{AsExpression, Expression}; +use crate::sql_types::{BoolOrNullableBool, IntoNullable, SingleValue, SqlType}; + +/// Methods present on boolean expressions +pub trait BoolExpressionMethods: Expression + Sized { + /// Creates a SQL `AND` expression + /// + /// # Example + /// + /// ``` + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::animals::dsl::*; + /// # let connection = establish_connection(); + /// # + /// diesel::insert_into(animals) + /// .values(&vec![ + /// (species.eq("ferret"), legs.eq(4), name.eq("Freddy")), + /// (species.eq("ferret"), legs.eq(4), name.eq("Jack")), + /// ]) + /// .execute(&connection)?; + /// + /// let data = animals.select((species, name)) + /// .filter(species.eq("ferret").and(name.eq("Jack"))) + /// .load(&connection)?; + /// let expected = vec![ + /// (String::from("ferret"), Some(String::from("Jack"))), + /// ]; + /// assert_eq!(expected, data); + /// # Ok(()) + /// # } + fn and(self, other: T) -> dsl::And + where + Self::SqlType: SqlType + IntoNullable, + ::Nullable: SingleValue, + T: AsExpression<::Nullable>, + And: Expression, + { + Grouped(And::new( + crate::expression::nullable::Nullable::new(self), + other.as_expression(), + )) + } + + /// Creates a SQL `OR` expression + /// + /// The result will be wrapped in parenthesis, so that precedence matches + /// that of your function calls. For example, `false.and(false.or(true))` + /// will generate the SQL `FALSE AND (FALSE OR TRUE)`, which returns `false` + /// + /// # Example + /// + /// ``` + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::animals::dsl::*; + /// # let connection = establish_connection(); + /// # + /// diesel::insert_into(animals) + /// .values(&vec![ + /// (species.eq("ferret"), legs.eq(4), name.eq("Freddy")), + /// (species.eq("ferret"), legs.eq(4), name.eq("Jack")), + /// ]) + /// .execute(&connection)?; + /// + /// let data = animals.select((species, name)) + /// .filter(species.eq("ferret").or(name.eq("Jack"))) + /// .load(&connection)?; + /// let expected = vec![ + /// (String::from("dog"), Some(String::from("Jack"))), + /// (String::from("ferret"), Some(String::from("Freddy"))), + /// (String::from("ferret"), Some(String::from("Jack"))), + /// ]; + /// assert_eq!(expected, data); + /// # Ok(()) + /// # } + fn or(self, other: T) -> dsl::Or + where + Self::SqlType: SqlType + IntoNullable, + ::Nullable: SingleValue, + T: AsExpression<::Nullable>, + Or: Expression, + { + Grouped(Or::new(self, other.as_expression())) + } +} + +impl BoolExpressionMethods for T +where + T: Expression, + T::SqlType: BoolOrNullableBool, +{ +} diff --git a/collector/benchmarks/diesel/diesel/src/expression_methods/eq_all.rs b/collector/benchmarks/diesel/diesel/src/expression_methods/eq_all.rs new file mode 100644 index 000000000..9efce1faa --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression_methods/eq_all.rs @@ -0,0 +1,73 @@ +use crate::expression::grouped::Grouped; +use crate::expression::nullable::Nullable; +use crate::expression::operators::And; +use crate::expression::Expression; +use crate::expression_methods::*; +use crate::sql_types::{self, Bool}; + +/// This method is used by `FindDsl` to work with tuples. Because we cannot +/// express this without specialization or overlapping impls, it is brute force +/// implemented on columns in the `column!` macro. +#[doc(hidden)] +pub trait EqAll { + type Output: Expression>; + + fn eq_all(self, rhs: Rhs) -> Self::Output; +} + +macro_rules! impl_eq_all { + // General case for 2+ elements + ( + ($Left1:ident, $($Left:ident,)+) + ($Right1:ident, $($Right:ident,)+) + ) => { + #[allow(non_snake_case)] + impl<$Left1, $($Left,)+ $Right1, $($Right,)+> + EqAll<($Right1, $($Right,)+)> for ($Left1, $($Left,)+) + where + $Left1: EqAll<$Right1>, + ($($Left,)+): EqAll<($($Right,)+)>, + { + type Output = Grouped>::Output>, + <($($Left,)+) as EqAll<($($Right,)+)>>::Output, + >>; + + fn eq_all(self, rhs: ($Right1, $($Right,)+)) -> Self::Output { + let ($Left1, $($Left,)+) = self; + let ($Right1, $($Right,)+) = rhs; + $Left1.eq_all($Right1).and(($($Left,)+).eq_all(($($Right,)+))) + } + } + }; + + // Special case for 1 element + ( + ($Left:ident,) ($Right:ident,) + ) => { + impl<$Left, $Right> EqAll<($Right,)> for ($Left,) + where + $Left: EqAll<$Right>, + { + type Output = <$Left as EqAll<$Right>>::Output; + + fn eq_all(self, rhs: ($Right,)) -> Self::Output { + self.0.eq_all(rhs.0) + } + } + }; +} + +macro_rules! impl_eq_all_for_all_tuples { + ($( + $unused1:tt { + $($unused2:tt -> $Left:ident, $Right:ident, $unused3:tt,)+ + } + )+) => { + $( + impl_eq_all!(($($Left,)+) ($($Right,)+)); + )+ + }; +} + +__diesel_for_each_tuple!(impl_eq_all_for_all_tuples); diff --git a/collector/benchmarks/diesel/diesel/src/expression_methods/escape_expression_methods.rs b/collector/benchmarks/diesel/diesel/src/expression_methods/escape_expression_methods.rs new file mode 100644 index 000000000..e7525e0e9 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression_methods/escape_expression_methods.rs @@ -0,0 +1,62 @@ +use crate::dsl; +use crate::expression::grouped::Grouped; +use crate::expression::operators::{Escape, Like, NotLike}; +use crate::expression::IntoSql; +use crate::sql_types::VarChar; + +/// Adds the `escape` method to `LIKE` and `NOT LIKE`. This is used to specify +/// the escape character for the pattern. +/// +/// By default, the escape character is `\` on most backends. On SQLite, +/// there is no default escape character. +/// +/// # Example +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # +/// # fn main() { +/// # use schema::users::dsl::*; +/// # use diesel::insert_into; +/// # let connection = establish_connection(); +/// # insert_into(users).values(name.eq("Ha%%0r")) +/// # .execute(&connection).unwrap(); +/// let users_with_percent = users.select(name) +/// .filter(name.like("%😀%%").escape('😀')) +/// .load(&connection); +/// let users_without_percent = users.select(name) +/// .filter(name.not_like("%a%%").escape('a')) +/// .load(&connection); +/// assert_eq!(Ok(vec![String::from("Ha%%0r")]), users_with_percent); +/// assert_eq!(Ok(vec![String::from("Sean"), String::from("Tess")]), users_without_percent); +/// # } +/// ``` +pub trait EscapeExpressionMethods: Sized { + #[doc(hidden)] + type TextExpression; + + /// See the trait documentation. + fn escape(self, _character: char) -> dsl::Escape; +} + +impl EscapeExpressionMethods for Grouped> { + type TextExpression = Like; + + fn escape(self, character: char) -> dsl::Escape { + Grouped(Escape::new( + self.0, + character.to_string().into_sql::(), + )) + } +} + +impl EscapeExpressionMethods for Grouped> { + type TextExpression = NotLike; + + fn escape(self, character: char) -> dsl::Escape { + Grouped(Escape::new( + self.0, + character.to_string().into_sql::(), + )) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/expression_methods/global_expression_methods.rs b/collector/benchmarks/diesel/diesel/src/expression_methods/global_expression_methods.rs new file mode 100644 index 000000000..5ac135d11 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression_methods/global_expression_methods.rs @@ -0,0 +1,479 @@ +use crate::dsl; +use crate::expression::array_comparison::{AsInExpression, In, NotIn}; +use crate::expression::grouped::Grouped; +use crate::expression::operators::*; +use crate::expression::{nullable, AsExpression, Expression}; +use crate::sql_types::{SingleValue, SqlType}; + +/// Methods present on all expressions, except tuples +pub trait ExpressionMethods: Expression + Sized { + /// Creates a SQL `=` expression. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let data = users.select(id).filter(name.eq("Sean")); + /// assert_eq!(Ok(1), data.first(&connection)); + /// # } + /// ``` + fn eq(self, other: T) -> dsl::Eq + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(Eq::new(self, other.as_expression())) + } + + /// Creates a SQL `!=` expression. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let data = users.select(id).filter(name.ne("Sean")); + /// assert_eq!(Ok(2), data.first(&connection)); + /// # } + /// ``` + fn ne(self, other: T) -> dsl::NotEq + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(NotEq::new(self, other.as_expression())) + } + + /// Creates a SQL `IN` statement. + /// + /// Queries using this method will not typically be + /// placed in the prepared statement cache. However, + /// in cases when a subquery is passed to the method, that + /// query will use the cache (assuming the subquery + /// itself is safe to cache). + /// On PostgreSQL, you should use + /// `eq(any())` instead. This method may change in the future to + /// automatically perform `= ANY` on PostgreSQL. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # use schema::users; + /// # use schema::posts; + /// # let connection = establish_connection(); + /// # connection.execute("INSERT INTO users (name) VALUES + /// # ('Jim')").unwrap(); + /// let data = users::table.select(users::id).filter(users::name.eq_any(vec!["Sean", "Jim"])); + /// assert_eq!(Ok(vec![1, 3]), data.load(&connection)); + /// + /// // Calling `eq_any` with an empty array is the same as doing `WHERE 1=0` + /// let data = users::table.select(users::id).filter(users::name.eq_any(Vec::::new())); + /// assert_eq!(Ok(vec![]), data.load::(&connection)); + /// + /// // Calling `eq_any` with a subquery is the same as using + /// // `WHERE {column} IN {subquery}`. + /// + /// let subquery = users::table.filter(users::name.eq("Sean")).select(users::id).into_boxed(); + /// let data = posts::table.select(posts::id).filter(posts::user_id.eq_any(subquery)); + /// assert_eq!(Ok(vec![1, 2]), data.load::(&connection)); + /// + /// # } + /// ``` + fn eq_any(self, values: T) -> dsl::EqAny + where + Self::SqlType: SqlType, + T: AsInExpression, + { + Grouped(In::new(self, values.as_in_expression())) + } + + /// Creates a SQL `NOT IN` statement. + /// + /// Queries using this method will not be + /// placed in the prepared statement cache. On PostgreSQL, you should use + /// `ne(all())` instead. This method may change in the future to + /// automatically perform `!= ALL` on PostgreSQL. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// # connection.execute("INSERT INTO users (name) VALUES + /// # ('Jim')").unwrap(); + /// let data = users.select(id).filter(name.ne_all(vec!["Sean", "Jim"])); + /// assert_eq!(Ok(vec![2]), data.load(&connection)); + /// + /// let data = users.select(id).filter(name.ne_all(vec!["Tess"])); + /// assert_eq!(Ok(vec![1, 3]), data.load(&connection)); + /// + /// // Calling `ne_any` with an empty array is the same as doing `WHERE 1=1` + /// let data = users.select(id).filter(name.ne_all(Vec::::new())); + /// assert_eq!(Ok(vec![1, 2, 3]), data.load(&connection)); + /// # } + /// ``` + fn ne_all(self, values: T) -> dsl::NeAny + where + Self::SqlType: SqlType, + T: AsInExpression, + { + Grouped(NotIn::new(self, values.as_in_expression())) + } + + /// Creates a SQL `IS NULL` expression. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::animals::dsl::*; + /// # let connection = establish_connection(); + /// # + /// let data = animals + /// .select(species) + /// .filter(name.is_null()) + /// .first::(&connection)?; + /// assert_eq!("spider", data); + /// # Ok(()) + /// # } + fn is_null(self) -> dsl::IsNull { + Grouped(IsNull::new(self)) + } + + /// Creates a SQL `IS NOT NULL` expression. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::animals::dsl::*; + /// # let connection = establish_connection(); + /// # + /// let data = animals + /// .select(species) + /// .filter(name.is_not_null()) + /// .first::(&connection)?; + /// assert_eq!("dog", data); + /// # Ok(()) + /// # } + fn is_not_null(self) -> dsl::IsNotNull { + Grouped(IsNotNull::new(self)) + } + + /// Creates a SQL `>` expression. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let data = users + /// .select(name) + /// .filter(id.gt(1)) + /// .first::(&connection)?; + /// assert_eq!("Tess", data); + /// # Ok(()) + /// # } + /// ``` + fn gt(self, other: T) -> dsl::Gt + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(Gt::new(self, other.as_expression())) + } + + /// Creates a SQL `>=` expression. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let data = users + /// .select(name) + /// .filter(id.ge(2)) + /// .first::(&connection)?; + /// assert_eq!("Tess", data); + /// # Ok(()) + /// # } + /// ``` + fn ge(self, other: T) -> dsl::GtEq + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(GtEq::new(self, other.as_expression())) + } + + /// Creates a SQL `<` expression. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let data = users + /// .select(name) + /// .filter(id.lt(2)) + /// .first::(&connection)?; + /// assert_eq!("Sean", data); + /// # Ok(()) + /// # } + /// ``` + fn lt(self, other: T) -> dsl::Lt + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(Lt::new(self, other.as_expression())) + } + + /// Creates a SQL `<=` expression. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let data = users + /// .select(name) + /// .filter(id.le(2)) + /// .first::(&connection)?; + /// assert_eq!("Sean", data); + /// # Ok(()) + /// # } + fn le(self, other: T) -> dsl::LtEq + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(LtEq::new(self, other.as_expression())) + } + + /// Creates a SQL `BETWEEN` expression using the given lower and upper + /// bounds. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # use schema::animals::dsl::*; + /// # let connection = establish_connection(); + /// # + /// let data = animals + /// .select(species) + /// .filter(legs.between(2, 6)) + /// .first(&connection); + /// # + /// assert_eq!(Ok("dog".to_string()), data); + /// # } + /// ``` + fn between(self, lower: T, upper: U) -> dsl::Between + where + Self::SqlType: SqlType, + T: AsExpression, + U: AsExpression, + { + Grouped(Between::new( + self, + And::new(lower.as_expression(), upper.as_expression()), + )) + } + + /// Creates a SQL `NOT BETWEEN` expression using the given lower and upper + /// bounds. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::animals::dsl::*; + /// # let connection = establish_connection(); + /// # + /// let data = animals + /// .select(species) + /// .filter(legs.not_between(2, 6)) + /// .first::(&connection)?; + /// assert_eq!("spider", data); + /// # Ok(()) + /// # } + fn not_between(self, lower: T, upper: U) -> dsl::NotBetween + where + Self::SqlType: SqlType, + T: AsExpression, + U: AsExpression, + { + Grouped(NotBetween::new( + self, + And::new(lower.as_expression(), upper.as_expression()), + )) + } + + /// Creates a SQL `DESC` expression, representing this expression in + /// descending order. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// # + /// let names = users + /// .select(name) + /// .order(name.desc()) + /// .load::(&connection)?; + /// assert_eq!(vec!["Tess", "Sean"], names); + /// # Ok(()) + /// # } + /// ``` + fn desc(self) -> dsl::Desc { + Desc::new(self) + } + + /// Creates a SQL `ASC` expression, representing this expression in + /// ascending order. + /// + /// This is the same as leaving the direction unspecified. It is useful if + /// you need to provide an unknown ordering, and need to box the return + /// value of a function. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use diesel::expression::expression_types::NotSelectable; + /// # + /// # fn main() { + /// # use schema::users::dsl::*; + /// # let order = "name"; + /// let ordering: Box> = + /// if order == "name" { + /// Box::new(name.desc()) + /// } else { + /// Box::new(id.asc()) + /// }; + /// # } + /// ``` + fn asc(self) -> dsl::Asc { + Asc::new(self) + } +} + +impl ExpressionMethods for T +where + T: Expression, + T::SqlType: SingleValue, +{ +} + +/// Methods present on all expressions +pub trait NullableExpressionMethods: Expression + Sized { + /// Converts this potentially non-null expression into one which is treated + /// as nullable. This method has no impact on the generated SQL, and is only + /// used to allow certain comparisons that would otherwise fail to compile. + /// + /// # Example + /// ```no_run + /// # #![allow(dead_code)] + /// # include!("../doctest_setup.rs"); + /// # use diesel::sql_types::*; + /// # use schema::users; + /// # + /// table! { + /// posts { + /// id -> Integer, + /// user_id -> Integer, + /// author_name -> Nullable, + /// } + /// } + /// # + /// # joinable!(posts -> users (user_id)); + /// # allow_tables_to_appear_in_same_query!(posts, users); + /// + /// fn main() { + /// use self::users::dsl::*; + /// use self::posts::dsl::{posts, author_name}; + /// let connection = establish_connection(); + /// + /// let data = users.inner_join(posts) + /// .filter(name.nullable().eq(author_name)) + /// .select(name) + /// .load::(&connection); + /// println!("{:?}", data); + /// } + /// ``` + fn nullable(self) -> dsl::Nullable { + nullable::Nullable::new(self) + } +} + +impl NullableExpressionMethods for T {} diff --git a/collector/benchmarks/diesel/diesel/src/expression_methods/mod.rs b/collector/benchmarks/diesel/diesel/src/expression_methods/mod.rs new file mode 100644 index 000000000..f91e5d0b8 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression_methods/mod.rs @@ -0,0 +1,26 @@ +//! Adds various methods to construct new expressions. These traits are exported +//! by default, and implemented automatically. +//! +//! You can rely on the methods provided by this trait existing on any +//! `Expression` of the appropriate type. You should not rely on the specific +//! traits existing, their names, or their organization. +mod bool_expression_methods; +mod eq_all; +mod escape_expression_methods; +mod global_expression_methods; +mod text_expression_methods; + +#[doc(inline)] +pub use self::bool_expression_methods::BoolExpressionMethods; +#[doc(hidden)] +pub use self::eq_all::EqAll; +#[doc(inline)] +pub use self::escape_expression_methods::EscapeExpressionMethods; +#[doc(inline)] +pub use self::global_expression_methods::{ExpressionMethods, NullableExpressionMethods}; +#[doc(inline)] +pub use self::text_expression_methods::TextExpressionMethods; + +#[cfg(feature = "postgres")] +#[doc(inline)] +pub use crate::pg::expression::expression_methods::*; diff --git a/collector/benchmarks/diesel/diesel/src/expression_methods/text_expression_methods.rs b/collector/benchmarks/diesel/diesel/src/expression_methods/text_expression_methods.rs new file mode 100644 index 000000000..0e8170605 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/expression_methods/text_expression_methods.rs @@ -0,0 +1,154 @@ +use crate::dsl; +use crate::expression::grouped::Grouped; +use crate::expression::operators::{Concat, Like, NotLike}; +use crate::expression::{AsExpression, Expression}; +use crate::sql_types::{Nullable, SqlType, Text}; + +/// Methods present on text expressions +pub trait TextExpressionMethods: Expression + Sized { + /// Concatenates two strings using the `||` operator. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # table! { + /// # users { + /// # id -> Integer, + /// # name -> VarChar, + /// # hair_color -> Nullable, + /// # } + /// # } + /// # + /// # fn main() { + /// # use self::users::dsl::*; + /// # use diesel::insert_into; + /// # + /// # let connection = connection_no_data(); + /// # connection.execute("CREATE TABLE users ( + /// # id INTEGER PRIMARY KEY, + /// # name VARCHAR(255) NOT NULL, + /// # hair_color VARCHAR(255) + /// # )").unwrap(); + /// # + /// # insert_into(users) + /// # .values(&vec![ + /// # (id.eq(1), name.eq("Sean"), hair_color.eq(Some("Green"))), + /// # (id.eq(2), name.eq("Tess"), hair_color.eq(None)), + /// # ]) + /// # .execute(&connection) + /// # .unwrap(); + /// # + /// let names = users.select(name.concat(" the Greatest")).load(&connection); + /// let expected_names = vec![ + /// "Sean the Greatest".to_string(), + /// "Tess the Greatest".to_string(), + /// ]; + /// assert_eq!(Ok(expected_names), names); + /// + /// // If the value is nullable, the output will be nullable + /// let names = users.select(hair_color.concat("ish")).load(&connection); + /// let expected_names = vec![ + /// Some("Greenish".to_string()), + /// None, + /// ]; + /// assert_eq!(Ok(expected_names), names); + /// # } + /// ``` + fn concat(self, other: T) -> dsl::Concat + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(Concat::new(self, other.as_expression())) + } + + /// Returns a SQL `LIKE` expression + /// + /// This method is case insensitive for SQLite and MySQL. + /// On PostgreSQL, `LIKE` is case sensitive. You may use + /// [`ilike()`](../expression_methods/trait.PgTextExpressionMethods.html#method.ilike) + /// for case insensitive comparison on PostgreSQL. + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// # + /// let starts_with_s = users + /// .select(name) + /// .filter(name.like("S%")) + /// .load::(&connection)?; + /// assert_eq!(vec!["Sean"], starts_with_s); + /// # Ok(()) + /// # } + /// ``` + fn like(self, other: T) -> dsl::Like + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(Like::new(self, other.as_expression())) + } + + /// Returns a SQL `NOT LIKE` expression + /// + /// This method is case insensitive for SQLite and MySQL. + /// On PostgreSQL `NOT LIKE` is case sensitive. You may use + /// [`not_ilike()`](../expression_methods/trait.PgTextExpressionMethods.html#method.not_ilike) + /// for case insensitive comparison on PostgreSQL. + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// # + /// let doesnt_start_with_s = users + /// .select(name) + /// .filter(name.not_like("S%")) + /// .load::(&connection)?; + /// assert_eq!(vec!["Tess"], doesnt_start_with_s); + /// # Ok(()) + /// # } + /// ``` + fn not_like(self, other: T) -> dsl::NotLike + where + Self::SqlType: SqlType, + T: AsExpression, + { + Grouped(NotLike::new(self, other.as_expression())) + } +} + +#[doc(hidden)] +/// Marker trait used to implement `TextExpressionMethods` on the appropriate +/// types. Once coherence takes associated types into account, we can remove +/// this trait. +pub trait TextOrNullableText {} + +impl TextOrNullableText for Text {} +impl TextOrNullableText for Nullable {} + +impl TextExpressionMethods for T +where + T: Expression, + T::SqlType: TextOrNullableText, +{ +} diff --git a/collector/benchmarks/diesel/diesel/src/insertable.rs b/collector/benchmarks/diesel/diesel/src/insertable.rs new file mode 100644 index 000000000..f7fa3fa8f --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/insertable.rs @@ -0,0 +1,334 @@ +use std::marker::PhantomData; + +use crate::backend::{Backend, SupportsDefaultKeyword}; +use crate::expression::grouped::Grouped; +use crate::expression::{AppearsOnTable, Expression}; +use crate::query_builder::{ + AstPass, InsertStatement, QueryFragment, UndecoratedInsertRecord, ValuesClause, +}; +use crate::query_source::{Column, Table}; +use crate::result::QueryResult; +#[cfg(feature = "sqlite")] +use crate::sqlite::Sqlite; + +/// Represents that a structure can be used to insert a new row into the +/// database. This is automatically implemented for `&[T]` and `&Vec` for +/// inserting more than one record. +/// +/// This trait can be [derived](derive.Insertable.html) +pub trait Insertable { + /// The `VALUES` clause to insert these records + /// + /// The types used here are generally internal to Diesel. + /// Implementations of this trait should use the `Values` + /// type of other `Insertable` types. + /// For example ` as Insertable>::Values`. + type Values; + + /// Construct `Self::Values` + /// + /// Implementations of this trait typically call `.values` + /// on other `Insertable` types. + fn values(self) -> Self::Values; + + /// Insert `self` into a given table. + /// + /// `foo.insert_into(table)` is identical to `insert_into(table).values(foo)`. + /// However, when inserting from a select statement, + /// this form is generally preferred. + /// + /// # Example + /// + /// ```rust + /// # include!("doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::{posts, users}; + /// # let conn = establish_connection(); + /// # diesel::delete(posts::table).execute(&conn)?; + /// users::table + /// .select(( + /// users::name.concat("'s First Post"), + /// users::id, + /// )) + /// .insert_into(posts::table) + /// .into_columns((posts::title, posts::user_id)) + /// .execute(&conn)?; + /// + /// let inserted_posts = posts::table + /// .select(posts::title) + /// .load::(&conn)?; + /// let expected = vec!["Sean's First Post", "Tess's First Post"]; + /// assert_eq!(expected, inserted_posts); + /// # Ok(()) + /// # } + /// ``` + fn insert_into(self, table: T) -> InsertStatement + where + Self: Sized, + { + crate::insert_into(table).values(self) + } +} + +#[doc(inline)] +pub use diesel_derives::Insertable; + +pub trait CanInsertInSingleQuery { + /// How many rows will this query insert? + /// + /// This function should only return `None` when the query is valid on all + /// backends, regardless of how many rows get inserted. + fn rows_to_insert(&self) -> Option; +} + +impl<'a, T, DB> CanInsertInSingleQuery for &'a T +where + T: ?Sized + CanInsertInSingleQuery, + DB: Backend, +{ + fn rows_to_insert(&self) -> Option { + (*self).rows_to_insert() + } +} + +impl<'a, T, Tab, DB> CanInsertInSingleQuery for BatchInsert<'a, T, Tab> +where + DB: Backend + SupportsDefaultKeyword, +{ + fn rows_to_insert(&self) -> Option { + Some(self.records.len()) + } +} + +impl CanInsertInSingleQuery for OwnedBatchInsert +where + DB: Backend + SupportsDefaultKeyword, +{ + fn rows_to_insert(&self) -> Option { + Some(self.values.len()) + } +} + +impl CanInsertInSingleQuery for ColumnInsertValue +where + DB: Backend, +{ + fn rows_to_insert(&self) -> Option { + Some(1) + } +} + +pub trait InsertValues: QueryFragment { + fn column_names(&self, out: AstPass) -> QueryResult<()>; +} + +#[derive(Debug, Copy, Clone)] +#[doc(hidden)] +pub enum ColumnInsertValue { + Expression(Col, Expr), + Default, +} + +impl Default for ColumnInsertValue { + fn default() -> Self { + ColumnInsertValue::Default + } +} + +impl InsertValues for ColumnInsertValue +where + DB: Backend + SupportsDefaultKeyword, + Col: Column, + Expr: Expression + AppearsOnTable<()>, + Self: QueryFragment, +{ + fn column_names(&self, mut out: AstPass) -> QueryResult<()> { + out.push_identifier(Col::NAME)?; + Ok(()) + } +} + +impl QueryFragment for ColumnInsertValue +where + DB: Backend + SupportsDefaultKeyword, + Expr: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + if let ColumnInsertValue::Expression(_, ref value) = *self { + value.walk_ast(out.reborrow())?; + } else { + out.push_sql("DEFAULT"); + } + Ok(()) + } +} + +#[cfg(feature = "sqlite")] +impl InsertValues for ColumnInsertValue +where + Col: Column, + Expr: Expression + AppearsOnTable<()>, + Self: QueryFragment, +{ + fn column_names(&self, mut out: AstPass) -> QueryResult<()> { + if let ColumnInsertValue::Expression(..) = *self { + out.push_identifier(Col::NAME)?; + } + Ok(()) + } +} + +#[cfg(feature = "sqlite")] +impl QueryFragment for ColumnInsertValue +where + Expr: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + if let ColumnInsertValue::Expression(_, ref value) = *self { + value.walk_ast(out.reborrow())?; + } + Ok(()) + } +} + +impl<'a, T, Tab> Insertable for &'a [T] +where + &'a T: UndecoratedInsertRecord, +{ + type Values = BatchInsert<'a, T, Tab>; + + fn values(self) -> Self::Values { + BatchInsert { + records: self, + _marker: PhantomData, + } + } +} + +impl<'a, T, Tab> Insertable for &'a Vec +where + &'a [T]: Insertable, +{ + type Values = <&'a [T] as Insertable>::Values; + + fn values(self) -> Self::Values { + (&**self).values() + } +} + +impl Insertable for Vec +where + T: Insertable + UndecoratedInsertRecord, +{ + type Values = OwnedBatchInsert; + + fn values(self) -> Self::Values { + OwnedBatchInsert { + values: self.into_iter().map(Insertable::values).collect(), + _marker: PhantomData, + } + } +} + +impl Insertable for Option +where + T: Insertable, + T::Values: Default, +{ + type Values = T::Values; + + fn values(self) -> Self::Values { + self.map(Insertable::values).unwrap_or_default() + } +} + +impl<'a, T, Tab> Insertable for &'a Option +where + Option<&'a T>: Insertable, +{ + type Values = as Insertable>::Values; + + fn values(self) -> Self::Values { + self.as_ref().values() + } +} + +impl Insertable for Grouped> +where + crate::expression::operators::Eq: Insertable, +{ + type Values = as Insertable>::Values; + + fn values(self) -> Self::Values { + self.0.values() + } +} + +impl<'a, L, R, Tab> Insertable for &'a Grouped> +where + &'a crate::expression::operators::Eq: Insertable, +{ + type Values = <&'a crate::expression::operators::Eq as Insertable>::Values; + + fn values(self) -> Self::Values { + self.0.values() + } +} + +#[derive(Debug, Clone, Copy)] +pub struct BatchInsert<'a, T: 'a, Tab> { + pub records: &'a [T], + _marker: PhantomData, +} + +impl<'a, T, Tab, Inner, DB> QueryFragment for BatchInsert<'a, T, Tab> +where + DB: Backend + SupportsDefaultKeyword, + &'a T: Insertable>, + ValuesClause: QueryFragment, + Inner: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + let mut records = self.records.iter().map(Insertable::values); + if let Some(record) = records.next() { + record.walk_ast(out.reborrow())?; + } + for record in records { + out.push_sql(", ("); + record.values.walk_ast(out.reborrow())?; + out.push_sql(")"); + } + Ok(()) + } +} + +#[derive(Debug)] +pub struct OwnedBatchInsert { + pub values: Vec, + _marker: PhantomData, +} + +impl QueryFragment for OwnedBatchInsert, Tab> +where + DB: Backend + SupportsDefaultKeyword, + ValuesClause: QueryFragment, + Inner: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + let mut values = self.values.iter(); + if let Some(value) = values.next() { + value.walk_ast(out.reborrow())?; + } + for value in values { + out.push_sql(", ("); + value.values.walk_ast(out.reborrow())?; + out.push_sql(")"); + } + Ok(()) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/lib.rs b/collector/benchmarks/diesel/diesel/src/lib.rs new file mode 100644 index 000000000..3e924fb11 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/lib.rs @@ -0,0 +1,368 @@ +//! # Diesel +//! +//! Diesel is an ORM and query builder designed to reduce the boilerplate for database interactions. +//! If this is your first time reading this documentation, +//! we recommend you start with the [getting started guide]. +//! We also have [many other long form guides]. +//! +//! [getting started guide]: https://diesel.rs/guides/getting-started/ +//! [many other long form guides]: https://diesel.rs/guides +//! +//! # Where to find things +//! +//! ## Declaring your schema +//! +//! For Diesel to validate your queries at compile time +//! it requires you to specify your schema in your code, +//! which you can do with [the `table!` macro][`table!`]. +//! `diesel print-schema` can be used +//! to automatically generate these macro calls +//! (by connecting to your database and querying its schema). +//! +//! [`table!`]: macro.table.html +//! +//! ## Getting started +//! +//! Queries usually start from either a table, or a function like [`update`]. +//! Those functions can be found [here](#functions). +//! +//! Diesel provides a [`prelude` module](prelude), +//! which exports most of the typically used traits and types. +//! We are conservative about what goes in this module, +//! and avoid anything which has a generic name. +//! Files which use Diesel are expected to have `use diesel::prelude::*;`. +//! +//! [`update`]: fn.update.html +//! +//! ## Constructing a query +//! +//! The tools the query builder gives you can be put into these three categories: +//! +//! - "Query builder methods" are things that map to portions of a whole query +//! (such as `ORDER` and `WHERE`). These methods usually have the same name +//! as the SQL they map to, except for `WHERE` which is called `filter` in Diesel +//! (To not conflict with the Rust keyword). +//! These methods live in [the `query_dsl` module](query_dsl). +//! - "Expression methods" are things you would call on columns +//! or other individual values. +//! These methods live in [the `expression_methods` module](expression_methods) +//! You can often find these by thinking "what would this be called" +//! if it were a method +//! and typing that into the search bar +//! (e.g. `LIKE` is called `like` in Diesel). +//! Most operators are named based on the Rust function which maps to that +//! operator in [`std::ops`][] +//! (For example `==` is called `.eq`, and `!=` is called `.ne`). +//! - "Bare functions" are normal SQL functions +//! such as `sum`. +//! They live in [the `dsl` module](dsl). +//! Diesel only supports a very small number of these functions. +//! You can declare additional functions you want to use +//! with [the `sql_function!` macro][`sql_function!`]. +//! +//! [`std::ops`]: //doc.rust-lang.org/stable/std/ops/index.html +//! [`sql_function!`]: macro.sql_function.html +//! +//! ## Serializing and Deserializing +//! +//! Types which represent the result of a SQL query implement +//! a trait called [`Queryable`]. +//! +//! Diesel maps "Rust types" (e.g. `i32`) to and from "SQL types" +//! (e.g. [`diesel::sql_types::Integer`]). +//! You can find all the types supported by Diesel in [the `sql_types` module](sql_types). +//! These types are only used to represent a SQL type. +//! You should never put them on your `Queryable` structs. +//! +//! To find all the Rust types which can be used with a given SQL type, +//! see the documentation for that SQL type. +//! +//! To find all the SQL types which can be used with a Rust type, +//! go to the docs for either [`ToSql`] or [`FromSql`], +//! go to the "Implementors" section, +//! and find the Rust type you want to use. +//! +//! [`Queryable`]: deserialize/trait.Queryable.html +//! [`diesel::sql_types::Integer`]: sql_types/struct.Integer.html +//! [`ToSql`]: serialize/trait.ToSql.html +//! [`FromSql`]: deserialize/trait.FromSql.html +//! +//! ## Getting help +//! +//! If you run into problems, Diesel has a very active Gitter room. +//! You can come ask for help at +//! [gitter.im/diesel-rs/diesel](https://gitter.im/diesel-rs/diesel) + +#![recursion_limit="1024"] +#![cfg_attr(feature = "unstable", feature(specialization, trait_alias))] +// For the `specialization` feature. +#![cfg_attr(feature = "unstable", allow(incomplete_features))] +// Built-in Lints +#![deny(warnings)] +#![warn( + missing_debug_implementations, + missing_copy_implementations, + missing_docs +)] +// Clippy lints +#![allow( + clippy::match_same_arms, + clippy::needless_doctest_main, + clippy::option_map_unwrap_or_else, + clippy::option_map_unwrap_or, + clippy::redundant_field_names, + clippy::type_complexity +)] +#![cfg_attr(test, allow(clippy::option_map_unwrap_or, clippy::result_unwrap_used))] +#![warn( + clippy::option_unwrap_used, + clippy::result_unwrap_used, + clippy::print_stdout, + clippy::wrong_pub_self_convention, + clippy::mut_mut, + clippy::non_ascii_literal, + clippy::similar_names, + clippy::unicode_not_nfc, + clippy::enum_glob_use, + clippy::if_not_else, + clippy::items_after_statements, + clippy::used_underscore_binding +)] + +#[cfg(feature = "postgres")] +#[macro_use] +extern crate bitflags; +extern crate byteorder; +extern crate diesel_derives; + +#[macro_use] +#[doc(hidden)] +pub mod macros; + +#[cfg(test)] +#[macro_use] +extern crate cfg_if; + +#[cfg(test)] +pub mod test_helpers; + +pub mod associations; +pub mod backend; +pub mod connection; +pub mod data_types; +pub mod deserialize; +#[macro_use] +pub mod expression; +pub mod expression_methods; +#[doc(hidden)] +pub mod insertable; +pub mod query_builder; +pub mod query_dsl; +pub mod query_source; +#[cfg(feature = "r2d2")] +pub mod r2d2; +pub mod result; +pub mod serialize; +pub mod upsert; +#[macro_use] +pub mod sql_types; +pub mod migration; +pub mod row; + +#[cfg(feature = "mysql")] +pub mod mysql; +#[cfg(feature = "postgres")] +pub mod pg; +#[cfg(feature = "sqlite")] +pub mod sqlite; + +mod type_impls; +mod util; + +#[doc(hidden)] +#[cfg(all(feature = "with-deprecated", not(feature = "without-deprecated")))] +#[deprecated(since = "2.0.0", note = "Use explicit macro imports instead")] +pub use diesel_derives::*; + +pub mod dsl { + //! Includes various helper types and bare functions which are named too + //! generically to be included in prelude, but are often used when using Diesel. + + #[doc(inline)] + pub use crate::helper_types::*; + + #[doc(inline)] + pub use crate::expression::dsl::*; + + #[doc(inline)] + pub use crate::query_builder::functions::{ + delete, insert_into, insert_or_ignore_into, replace_into, select, sql_query, update, + }; +} + +pub mod helper_types { + //! Provide helper types for concisely writing the return type of functions. + //! As with iterators, it is unfortunately difficult to return a partially + //! constructed query without exposing the exact implementation of the + //! function. Without higher kinded types, these various DSLs can't be + //! combined into a single trait for boxing purposes. + //! + //! All types here are in the form `>::Output`. So the return type of + //! `users.filter(first_name.eq("John")).order(last_name.asc()).limit(10)` would + //! be `Limit, Asc>>` + use super::query_builder::locking_clause as lock; + use super::query_dsl::methods::*; + use super::query_dsl::*; + use super::query_source::joins; + + #[doc(inline)] + pub use crate::expression::helper_types::*; + + /// Represents the return type of `.select(selection)` + pub type Select = >::Output; + + /// Represents the return type of `.filter(predicate)` + pub type Filter = >::Output; + + /// Represents the return type of `.filter(lhs.eq(rhs))` + pub type FindBy = Filter>; + + /// Represents the return type of `.for_update()` + pub type ForUpdate = >::Output; + + /// Represents the return type of `.for_no_key_update()` + pub type ForNoKeyUpdate = >::Output; + + /// Represents the return type of `.for_share()` + pub type ForShare = >::Output; + + /// Represents the return type of `.for_key_share()` + pub type ForKeyShare = >::Output; + + /// Represents the return type of `.skip_locked()` + pub type SkipLocked = >::Output; + + /// Represents the return type of `.no_wait()` + pub type NoWait = >::Output; + + /// Represents the return type of `.find(pk)` + pub type Find = >::Output; + + /// Represents the return type of `.or_filter(predicate)` + pub type OrFilter = >::Output; + + /// Represents the return type of `.order(ordering)` + pub type Order = >::Output; + + /// Represents the return type of `.then_order_by(ordering)` + pub type ThenOrderBy = >::Output; + + /// Represents the return type of `.limit()` + pub type Limit = ::Output; + + /// Represents the return type of `.offset()` + pub type Offset = ::Output; + + /// Represents the return type of `.inner_join(rhs)` + pub type InnerJoin = + >::Output; + + /// Represents the return type of `.inner_join(rhs.on(on))` + pub type InnerJoinOn = + >::Output; + + /// Represents the return type of `.left_join(rhs)` + pub type LeftJoin = + >::Output; + + /// Represents the return type of `.left_join(rhs.on(on))` + pub type LeftJoinOn = + >::Output; + + use super::associations::HasTable; + use super::query_builder::{AsChangeset, IntoUpdateTarget, UpdateStatement}; + /// Represents the return type of `update(lhs).set(rhs)` + pub type Update = UpdateStatement< + ::Table, + ::WhereClause, + ::Changeset, + >; + + /// Represents the return type of `.into_boxed::<'a, DB>()` + pub type IntoBoxed<'a, Source, DB> = >::Output; + + /// Represents the return type of `.distinct()` + pub type Distinct = ::Output; + + /// Represents the return type of `.distinct_on(expr)` + #[cfg(feature = "postgres")] + pub type DistinctOn = >::Output; + + /// Represents the return type of `.single_value()` + pub type SingleValue = ::Output; + + /// Represents the return type of `.nullable()` + pub type NullableSelect = ::Output; + + /// Represents the return type of `.group_by(expr)` + pub type GroupBy = >::Output; +} + +pub mod prelude { + //! Re-exports important traits and types. Meant to be glob imported when using Diesel. + + #[doc(inline)] + pub use crate::associations::{Associations, GroupedBy, Identifiable}; + #[doc(inline)] + pub use crate::connection::Connection; + #[doc(inline)] + pub use crate::deserialize::{Queryable, QueryableByName}; + #[doc(inline)] + pub use crate::expression::{ + AppearsOnTable, BoxableExpression, Expression, IntoSql, SelectableExpression, + }; + + #[doc(inline)] + pub use crate::expression::functions::sql_function; + + #[doc(inline)] + pub use crate::expression_methods::*; + #[doc(inline)] + pub use crate::insertable::Insertable; + #[doc(inline)] + pub use crate::macros::prelude::*; + #[doc(inline)] + pub use crate::query_builder::AsChangeset; + #[doc(inline)] + pub use crate::query_builder::DecoratableTarget; + #[doc(inline)] + pub use crate::query_dsl::{BelongingToDsl, JoinOnDsl, QueryDsl, RunQueryDsl, SaveChangesDsl}; + #[doc(inline)] + pub use crate::query_source::{Column, JoinTo, QuerySource, Table}; + #[doc(inline)] + pub use crate::result::{ConnectionError, ConnectionResult, OptionalExtension, QueryResult}; + + #[cfg(feature = "mysql")] + #[doc(inline)] + pub use crate::mysql::MysqlConnection; + #[cfg(feature = "postgres")] + #[doc(inline)] + pub use crate::pg::PgConnection; + #[cfg(feature = "sqlite")] + #[doc(inline)] + pub use crate::sqlite::SqliteConnection; +} + +pub use crate::prelude::*; +#[doc(inline)] +pub use crate::query_builder::debug_query; +#[doc(inline)] +pub use crate::query_builder::functions::{ + delete, insert_into, insert_or_ignore_into, replace_into, select, sql_query, update, +}; +pub use crate::result::Error::NotFound; + +pub(crate) mod diesel { + pub use super::*; +} diff --git a/collector/benchmarks/diesel/diesel/src/macros/internal.rs b/collector/benchmarks/diesel/diesel/src/macros/internal.rs new file mode 100644 index 000000000..71892ffc6 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/macros/internal.rs @@ -0,0 +1,36 @@ +/// This will implement `SelectableExpression` and `AppearsOnTable` for "simple" +/// composite nodes where the where clause is roughly `AllTyParams: +/// SelectableExpression, Self: Expression`. +/// +/// This macro is exported because we want to be able to call it from other +/// macros that are exported, but it is not part of our public API. +#[macro_export] +#[doc(hidden)] +macro_rules! impl_selectable_expression { + ($struct_name:ident) => { + $crate::impl_selectable_expression!(ty_params = (), struct_ty = $struct_name,); + }; + + ($struct_name:ident<$($ty_params:ident),+>) => { + $crate::impl_selectable_expression!( + ty_params = ($($ty_params),+), + struct_ty = $struct_name<$($ty_params),+>, + ); + }; + + (ty_params = ($($ty_params:ident),*), struct_ty = $struct_ty:ty,) => { + impl<$($ty_params,)* QS> $crate::expression::SelectableExpression + for $struct_ty where + $struct_ty: $crate::expression::AppearsOnTable, + $($ty_params: $crate::expression::SelectableExpression,)* + { + } + + impl<$($ty_params,)* QS> $crate::expression::AppearsOnTable + for $struct_ty where + $struct_ty: $crate::expression::Expression, + $($ty_params: $crate::expression::AppearsOnTable,)* + { + } + }; +} diff --git a/collector/benchmarks/diesel/diesel/src/macros/mod.rs b/collector/benchmarks/diesel/diesel/src/macros/mod.rs new file mode 100644 index 000000000..90bdaf33b --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/macros/mod.rs @@ -0,0 +1,1509 @@ +#![allow(unused_parens)] // FIXME: Remove this attribute once false positive is resolved. +#![cfg_attr(rustfmt, rustfmt_skip)] // https://github.com/rust-lang-nursery/rustfmt/issues/2755 + +pub(crate) mod prelude { + #[doc(inline)] + pub use crate::{ + allow_columns_to_appear_in_same_group_by_clause, + allow_tables_to_appear_in_same_query, + joinable, + table, + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_column { + ( + table = $table:ident, + name = $column_name:ident, + sql_name = $sql_name:expr, + ty = ($($Type:tt)*), + meta = [$($meta:tt)*], + ) => { + $($meta)* + #[allow(non_camel_case_types, dead_code)] + #[derive(Debug, Clone, Copy, $crate::query_builder::QueryId, Default)] + pub struct $column_name; + + impl $crate::expression::Expression for $column_name { + type SqlType = $($Type)*; + } + + impl $crate::query_builder::QueryFragment for $column_name where + DB: $crate::backend::Backend, + <$table as $crate::QuerySource>::FromClause: $crate::query_builder::QueryFragment, + { + #[allow(non_snake_case)] + fn walk_ast(&self, mut __out: $crate::query_builder::AstPass) -> $crate::result::QueryResult<()> { + use $crate::QuerySource; + $table.from_clause().walk_ast(__out.reborrow())?; + __out.push_sql("."); + __out.push_identifier($sql_name) + } + } + + impl $crate::SelectableExpression<$table> for $column_name { + } + + impl $crate::AppearsOnTable for $column_name where + QS: $crate::query_source::AppearsInFromClause<$table, Count=$crate::query_source::Once>, + { + } + + impl $crate::SelectableExpression< + $crate::query_source::joins::Join, + > for $column_name where + $column_name: $crate::AppearsOnTable<$crate::query_source::joins::Join>, + Self: $crate::SelectableExpression, + // If our table is on the right side of this join, only + // `Nullable` can be selected + Right: $crate::query_source::AppearsInFromClause<$table, Count=$crate::query_source::Never>, + { + } + + impl $crate::SelectableExpression< + $crate::query_source::joins::Join, + > for $column_name where + $column_name: $crate::AppearsOnTable<$crate::query_source::joins::Join>, + Left: $crate::query_source::AppearsInFromClause<$table>, + Right: $crate::query_source::AppearsInFromClause<$table>, + (Left::Count, Right::Count): $crate::query_source::Pick, + Self: $crate::SelectableExpression< + <(Left::Count, Right::Count) as $crate::query_source::Pick>::Selection, + >, + { + } + + // FIXME: Remove this when overlapping marker traits are stable + impl $crate::SelectableExpression<$crate::query_source::joins::JoinOn> for $column_name where + $column_name: $crate::SelectableExpression + $crate::AppearsOnTable<$crate::query_source::joins::JoinOn>, + { + } + + // FIXME: Remove this when overlapping marker traits are stable + impl $crate::SelectableExpression<$crate::query_builder::SelectStatement> for $column_name where + $column_name: $crate::SelectableExpression + $crate::AppearsOnTable<$crate::query_builder::SelectStatement>, + { + } + + impl<__GB> $crate::expression::ValidGrouping<__GB> for $column_name + where __GB: $crate::expression::IsContainedInGroupBy<$column_name, Output = $crate::expression::is_contained_in_group_by::Yes>, + { + type IsAggregate = $crate::expression::is_aggregate::Yes; + } + + impl $crate::expression::ValidGrouping<()> for $column_name { + type IsAggregate = $crate::expression::is_aggregate::No; + } + + impl $crate::expression::IsContainedInGroupBy<$column_name> for $column_name { + type Output = $crate::expression::is_contained_in_group_by::Yes; + } + + impl $crate::query_source::Column for $column_name { + type Table = $table; + + const NAME: &'static str = $sql_name; + } + + impl $crate::EqAll for $column_name where + T: $crate::expression::AsExpression<$($Type)*>, + $crate::dsl::Nullable<$crate::dsl::Eq<$column_name, T::Expression>>: + $crate::Expression>, + { + type Output = $crate::dsl::Nullable<$crate::dsl::Eq>; + + fn eq_all(self, rhs: T) -> Self::Output { + use $crate::expression_methods::ExpressionMethods; + use $crate::expression_methods::NullableExpressionMethods; + + self.eq(rhs).nullable() + } + } + + $crate::__diesel_generate_ops_impls_if_numeric!($column_name, $($Type)*); + $crate::__diesel_generate_ops_impls_if_date_time!($column_name, $($Type)*); + $crate::__diesel_generate_ops_impls_if_network!($column_name, $($Type)*); + } +} + +/// Specifies that a table exists, and what columns it has. This will create a +/// new public module, with the same name, as the name of the table. In this +/// module, you'll find a unit struct named `table`, and a unit struct with the +/// names of each of the columns. +/// +/// By default this allows a maximum of 32 columns per table. +/// You can increase this limit to 64 by enabling the `64-column-tables` feature. +/// You can increase it to 128 by enabling the `128-column-tables` feature. +/// You can decrease it to 16 columns, +/// which improves compilation time, +/// by disabling the default features of Diesel. +/// Note that enabling 64 column tables or larger will substantially increase +/// the compile time of Diesel. +/// +/// Example usage +/// ------------- +/// +/// ```rust +/// diesel::table! { +/// users { +/// id -> Integer, +/// name -> VarChar, +/// favorite_color -> Nullable, +/// } +/// } +/// ``` +/// +/// You may also specify a primary key if it's called something other than `id`. +/// Tables with no primary key are not supported. +/// +/// ```rust +/// diesel::table! { +/// users (non_standard_primary_key) { +/// non_standard_primary_key -> Integer, +/// name -> VarChar, +/// favorite_color -> Nullable, +/// } +/// } +/// ``` +/// +/// For tables with composite primary keys, list all of the columns in the +/// primary key. +/// +/// ```rust +/// diesel::table! { +/// followings (user_id, post_id) { +/// user_id -> Integer, +/// post_id -> Integer, +/// favorited -> Bool, +/// } +/// } +/// # fn main() { +/// # use diesel::prelude::Table; +/// # use self::followings::dsl::*; +/// # // Poor man's assert_eq! -- since this is type level this would fail +/// # // to compile if the wrong primary key were generated +/// # let (user_id {}, post_id {}) = followings.primary_key(); +/// # } +/// ``` +/// +/// If you are using types that aren't from Diesel's core types, you can specify +/// which types to import. +/// +/// ``` +/// # mod diesel_full_text_search { +/// # #[derive(diesel::sql_types::SqlType)] +/// # pub struct TsVector; +/// # } +/// +/// diesel::table! { +/// use diesel::sql_types::*; +/// # use crate::diesel_full_text_search::*; +/// # /* +/// use diesel_full_text_search::*; +/// # */ +/// +/// posts { +/// id -> Integer, +/// title -> Text, +/// keywords -> TsVector, +/// } +/// } +/// # fn main() {} +/// ``` +/// +/// If you want to add documentation to the generated code you can use the +/// following syntax: +/// +/// ``` +/// diesel::table! { +/// /// The table containing all blog posts +/// posts { +/// /// The post's unique id +/// id -> Integer, +/// /// The post's title +/// title -> Text, +/// } +/// } +/// ``` +/// +/// If you have a column with the same name as a Rust reserved keyword, you can use +/// the `sql_name` attribute like this: +/// +/// ``` +/// diesel::table! { +/// posts { +/// id -> Integer, +/// /// This column is named `mytype` but references the table `type` column. +/// #[sql_name = "type"] +/// mytype -> Text, +/// } +/// } +/// ``` +/// +/// This module will also contain several helper types: +/// +/// dsl +/// --- +/// +/// This simply re-exports the table, renamed to the same name as the module, +/// and each of the columns. This is useful to glob import when you're dealing +/// primarily with one table, to allow writing `users.filter(name.eq("Sean"))` +/// instead of `users::table.filter(users::name.eq("Sean"))`. +/// +/// `all_columns` +/// ----------- +/// +/// A constant will be assigned called `all_columns`. This is what will be +/// selected if you don't otherwise specify a select clause. It's type will be +/// `table::AllColumns`. You can also get this value from the +/// `Table::all_columns` function. +/// +/// star +/// ---- +/// +/// This will be the qualified "star" expression for this table (e.g. +/// `users.*`). Internally, we read columns by index, not by name, so this +/// column is not safe to read data out of, and it has had it's SQL type set to +/// `()` to prevent accidentally using it as such. It is sometimes useful for +/// count statements however. It can also be accessed through the `Table.star()` +/// method. +/// +/// `SqlType` +/// ------- +/// +/// A type alias called `SqlType` will be created. It will be the SQL type of +/// `all_columns`. The SQL type is needed for things like [returning boxed +/// queries][boxed_queries]. +/// +/// [boxed_queries]: query_dsl/trait.QueryDsl.html#method.into_boxed +/// +/// `BoxedQuery` +/// ---------- +/// +/// ```ignore +/// pub type BoxedQuery<'a, DB, ST = SqlType> = BoxedSelectStatement<'a, ST, table, DB>; +/// ``` +#[macro_export] +macro_rules! table { + ($($tokens:tt)*) => { + $crate::__diesel_parse_table! { + tokens = [$($tokens)*], + imports = [], + meta = [], + sql_name = unknown, + name = unknown, + schema = public, + primary_key = id, + } + } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_invalid_table_syntax { + () => { + compile_error!( + "Invalid `table!` syntax. Please see the `table!` macro docs for more info." + ); + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_parse_table { + // Found an import + ( + tokens = [use $($import:tt)::+; $($rest:tt)*], + imports = [$($imports:tt)*], + $($args:tt)* + ) => { + $crate::__diesel_parse_table! { + tokens = [$($rest)*], + imports = [$($imports)* use $($import)::+;], + $($args)* + } + }; + + // Found sql_name attribute, override whatever we had before + ( + tokens = [#[sql_name = $sql_name:expr] $($rest:tt)*], + imports = $imports:tt, + meta = $meta:tt, + sql_name = $ignore:tt, + $($args:tt)* + ) => { + $crate::__diesel_parse_table! { + tokens = [$($rest)*], + imports = $imports, + meta = $meta, + sql_name = $sql_name, + $($args)* + } + }; + + // Meta item other than sql_name, attach it to the table struct + ( + tokens = [#$new_meta:tt $($rest:tt)*], + imports = $imports:tt, + meta = [$($meta:tt)*], + $($args:tt)* + ) => { + $crate::__diesel_parse_table! { + tokens = [$($rest)*], + imports = $imports, + meta = [$($meta)* #$new_meta], + $($args)* + } + }; + + // Found a schema name, override whatever we had before + ( + tokens = [$schema:ident . $($rest:tt)*], + imports = $imports:tt, + meta = $meta:tt, + sql_name = $sql_name:tt, + name = $name:tt, + schema = $ignore:tt, + $($args:tt)* + ) => { + $crate::__diesel_parse_table! { + tokens = [$($rest)*], + imports = $imports, + meta = $meta, + sql_name = $sql_name, + name = $name, + schema = $schema, + $($args)* + } + }; + + // Found a table name, override whatever we had before + ( + tokens = [$name:ident $($rest:tt)*], + imports = $imports:tt, + meta = $meta:tt, + sql_name = $sql_name:tt, + name = $ignore:tt, + $($args:tt)* + ) => { + $crate::__diesel_parse_table! { + tokens = [$($rest)*], + imports = $imports, + meta = $meta, + sql_name = $sql_name, + name = $name, + $($args)* + } + }; + + // Found a primary key, override whatever we had before + ( + tokens = [($($pk:ident),+ $(,)*) $($rest:tt)*], + imports = $imports:tt, + meta = $meta:tt, + sql_name = $sql_name:tt, + name = $name:tt, + schema = $schema:tt, + primary_key = $ignore:tt, + $($args:tt)* + ) => { + $crate::__diesel_parse_table! { + tokens = [$($rest)*], + imports = $imports, + meta = $meta, + sql_name = $sql_name, + name = $name, + schema = $schema, + primary_key = ($($pk),+), + $($args)* + } + }; + + // Reached columns with no imports, set a default + ( + tokens = [{$($columns:tt)*}], + imports = [], + $($args:tt)* + ) => { + $crate::__diesel_parse_table! { + tokens = [{$($columns)*}], + imports = [use $crate::sql_types::*;], + $($args)* + } + }; + + // Reached columns with no sql_name, set a default + ( + tokens = [{$($columns:tt)*}], + imports = $imports:tt, + meta = $meta:tt, + sql_name = unknown, + name = $name:tt, + $($args:tt)* + ) => { + $crate::__diesel_parse_table! { + tokens = [{$($columns)*}], + imports = $imports, + meta = $meta, + sql_name = stringify!($name), + name = $name, + $($args)* + } + }; + + // Parse the columns + ( + tokens = [{$($columns:tt)*}], + $($args:tt)* + ) => { + $crate::__diesel_parse_columns! { + tokens = [$($columns)*], + table = { $($args)* }, + columns = [], + } + }; + + // Invalid syntax + ($($tokens:tt)*) => { + $crate::__diesel_invalid_table_syntax!(); + } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_parse_columns { + // No column being parsed, start a new one. + // Attempt to capture the type as separate tokens if at all possible. + ( + tokens = [ + $(#$meta:tt)* + $name:ident -> $($ty:tt)::* $(<$($ty_params:tt)::*>)*, + $($rest:tt)* + ], + $($args:tt)* + ) => { + $crate::__diesel_parse_columns! { + current_column = { + unchecked_meta = [$(#$meta)*], + name = $name, + sql_name = stringify!($name), + ty = ($($ty)::* $(<$($ty_params)::*>)*), + meta = [], + }, + tokens = [$($rest)*], + $($args)* + } + }; + + // No column being parsed, start a new one. Couldn't keep the `ty` separate. + ( + tokens = [ + $(#$meta:tt)* + $name:ident -> $ty:ty, + $($rest:tt)* + ], + $($args:tt)* + ) => { + $crate::__diesel_parse_columns! { + current_column = { + unchecked_meta = [$(#$meta)*], + name = $name, + sql_name = stringify!($name), + ty = ($ty), + meta = [], + }, + tokens = [$($rest)*], + $($args)* + } + }; + + + // Found #[sql_name] + ( + current_column = { + unchecked_meta = [#[sql_name = $sql_name:expr] $($meta:tt)*], + name = $name:tt, + sql_name = $ignore:expr, + $($current_column:tt)* + }, + $($args:tt)* + ) => { + $crate::__diesel_parse_columns! { + current_column = { + unchecked_meta = [$($meta)*], + name = $name, + sql_name = $sql_name, + $($current_column)* + }, + $($args)* + } + }; + + // Meta item other than #[sql_name] + ( + current_column = { + unchecked_meta = [#$new_meta:tt $($unchecked_meta:tt)*], + name = $name:tt, + sql_name = $sql_name:expr, + ty = $ty:tt, + meta = [$($meta:tt)*], + $($current_column:tt)* + }, + $($args:tt)* + ) => { + $crate::__diesel_parse_columns! { + current_column = { + unchecked_meta = [$($unchecked_meta)*], + name = $name, + sql_name = $sql_name, + ty = $ty, + meta = [$($meta)* #$new_meta], + $($current_column)* + }, + $($args)* + } + }; + + // Done parsing this column + ( + current_column = { + unchecked_meta = [], + $($current_column:tt)* + }, + tokens = $tokens:tt, + table = $table:tt, + columns = [$($columns:tt,)*], + $($args:tt)* + ) => { + $crate::__diesel_parse_columns! { + tokens = $tokens, + table = $table, + columns = [$($columns,)* { $($current_column)* },], + $($args)* + } + }; + + // Done parsing all columns + ( + tokens = [], + $($args:tt)* + ) => { + $crate::__diesel_table_impl!($($args)*); + }; + + // Invalid syntax + ($($tokens:tt)*) => { + $crate::__diesel_invalid_table_syntax!(); + } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_table_impl { + ( + table = { + imports = [$($imports:tt)*], + meta = [$($meta:tt)*], + sql_name = $sql_name:expr, + name = $table_name:ident, + schema = $schema:ident, + primary_key = $primary_key:tt, + }, + columns = [$({ + name = $column_name:ident, + sql_name = $column_sql_name:expr, + ty = ($($column_ty:tt)*), + $($column:tt)* + },)+], + ) => { + $($meta)* + pub mod $table_name { + #![allow(dead_code)] + $($imports)* + pub use self::columns::*; + + /// Re-exports all of the columns of this table, as well as the + /// table struct renamed to the module name. This is meant to be + /// glob imported for functions which only deal with one table. + pub mod dsl { + $($crate::static_cond! { + if $table_name == $column_name { + compile_error!(concat!( + "Column `", + stringify!($column_name), + "` cannot be named the same as its table.\n \ + You may use `#[sql_name = \"", + stringify!($column_name), + "\"]` to reference the table's `", + stringify!($column_name), + "` column. \n \ + Docs available at: `https://docs.diesel.rs/diesel/macro.table.html`\n" + )); + } else { + pub use super::columns::{$column_name}; + } + })+ + pub use super::table as $table_name; + } + + #[allow(non_upper_case_globals, dead_code)] + /// A tuple of all of the columns on this table + pub const all_columns: ($($column_name,)+) = ($($column_name,)+); + + #[allow(non_camel_case_types)] + #[derive(Debug, Clone, Copy, $crate::query_builder::QueryId)] + /// The actual table struct + /// + /// This is the type which provides the base methods of the query + /// builder, such as `.select` and `.filter`. + pub struct table; + + impl table { + #[allow(dead_code)] + /// Represents `table_name.*`, which is sometimes necessary + /// for efficient count queries. It cannot be used in place of + /// `all_columns` + pub fn star(&self) -> star { + star + } + } + + /// The SQL type of all of the columns on this table + pub type SqlType = ($($($column_ty)*,)+); + + /// Helper type for representing a boxed query from this table + pub type BoxedQuery<'a, DB, ST = SqlType> = $crate::query_builder::BoxedSelectStatement<'a, ST, table, DB>; + + $crate::__diesel_table_query_source_impl!(table, $schema, $sql_name); + + impl $crate::query_builder::AsQuery for table { + type SqlType = SqlType; + type Query = $crate::query_builder::SelectStatement; + + fn as_query(self) -> Self::Query { + $crate::query_builder::SelectStatement::simple(self) + } + } + + impl $crate::Table for table { + type PrimaryKey = $primary_key; + type AllColumns = ($($column_name,)+); + + fn primary_key(&self) -> Self::PrimaryKey { + $primary_key + } + + fn all_columns() -> Self::AllColumns { + ($($column_name,)+) + } + } + + impl $crate::associations::HasTable for table { + type Table = Self; + + fn table() -> Self::Table { + table + } + } + + impl $crate::query_builder::IntoUpdateTarget for table { + type WhereClause = <::Query as $crate::query_builder::IntoUpdateTarget>::WhereClause; + + fn into_update_target(self) -> $crate::query_builder::UpdateTarget { + use $crate::query_builder::AsQuery; + self.as_query().into_update_target() + } + } + + impl $crate::query_source::AppearsInFromClause
for table { + type Count = $crate::query_source::Once; + } + + impl $crate::query_source::AppearsInFromClause
for () { + type Count = $crate::query_source::Never; + } + + impl $crate::JoinTo<$crate::query_source::joins::Join> for table where + $crate::query_source::joins::Join: $crate::JoinTo
, + { + type FromClause = $crate::query_source::joins::Join; + type OnClause = <$crate::query_source::joins::Join as $crate::JoinTo
>::OnClause; + + fn join_target(rhs: $crate::query_source::joins::Join) -> (Self::FromClause, Self::OnClause) { + let (_, on_clause) = $crate::query_source::joins::Join::join_target(table); + (rhs, on_clause) + } + } + + impl $crate::JoinTo<$crate::query_source::joins::JoinOn> for table where + $crate::query_source::joins::JoinOn: $crate::JoinTo
, + { + type FromClause = $crate::query_source::joins::JoinOn; + type OnClause = <$crate::query_source::joins::JoinOn as $crate::JoinTo
>::OnClause; + + fn join_target(rhs: $crate::query_source::joins::JoinOn) -> (Self::FromClause, Self::OnClause) { + let (_, on_clause) = $crate::query_source::joins::JoinOn::join_target(table); + (rhs, on_clause) + } + } + + impl $crate::JoinTo<$crate::query_builder::SelectStatement> for table where + $crate::query_builder::SelectStatement: $crate::JoinTo
, + { + type FromClause = $crate::query_builder::SelectStatement; + type OnClause = <$crate::query_builder::SelectStatement as $crate::JoinTo
>::OnClause; + + fn join_target(rhs: $crate::query_builder::SelectStatement) -> (Self::FromClause, Self::OnClause) { + let (_, on_clause) = $crate::query_builder::SelectStatement::join_target(table); + (rhs, on_clause) + } + } + + impl<'a, QS, ST, DB> $crate::JoinTo<$crate::query_builder::BoxedSelectStatement<'a, QS, ST, DB>> for table where + $crate::query_builder::BoxedSelectStatement<'a, QS, ST, DB>: $crate::JoinTo
, + { + type FromClause = $crate::query_builder::BoxedSelectStatement<'a, QS, ST, DB>; + type OnClause = <$crate::query_builder::BoxedSelectStatement<'a, QS, ST, DB> as $crate::JoinTo
>::OnClause; + fn join_target(rhs: $crate::query_builder::BoxedSelectStatement<'a, QS, ST, DB>) -> (Self::FromClause, Self::OnClause) { + let (_, on_clause) = $crate::query_builder::BoxedSelectStatement::join_target(table); + (rhs, on_clause) + } + } + + // This impl should be able to live in Diesel, + // but Rust tries to recurse for no reason + impl $crate::insertable::Insertable for table + where +
::Query: $crate::insertable::Insertable, + { + type Values = <
::Query as $crate::insertable::Insertable>::Values; + + fn values(self) -> Self::Values { + use $crate::query_builder::AsQuery; + self.as_query().values() + } + } + + impl<'a, T> $crate::insertable::Insertable for &'a table + where + table: $crate::insertable::Insertable, + { + type Values =
>::Values; + + fn values(self) -> Self::Values { + (*self).values() + } + } + + /// Contains all of the columns of this table + pub mod columns { + use super::table; + $($imports)* + + #[allow(non_camel_case_types, dead_code)] + #[derive(Debug, Clone, Copy, $crate::query_builder::QueryId)] + /// Represents `table_name.*`, which is sometimes needed for + /// efficient count queries. It cannot be used in place of + /// `all_columns`, and has a `SqlType` of `()` to prevent it + /// being used that way + pub struct star; + + impl<__GB> $crate::expression::ValidGrouping<__GB> for star + where + ($($column_name,)+): $crate::expression::ValidGrouping<__GB>, + { + type IsAggregate = <($($column_name,)+) as $crate::expression::ValidGrouping<__GB>>::IsAggregate; + } + + impl $crate::Expression for star { + type SqlType = $crate::expression::expression_types::NotSelectable; + } + + impl $crate::query_builder::QueryFragment for star where +
::FromClause: $crate::query_builder::QueryFragment, + { + #[allow(non_snake_case)] + fn walk_ast(&self, mut __out: $crate::query_builder::AstPass) -> $crate::result::QueryResult<()> { + use $crate::QuerySource; + table.from_clause().walk_ast(__out.reborrow())?; + __out.push_sql(".*"); + Ok(()) + } + } + + impl $crate::SelectableExpression
for star { + } + + impl $crate::AppearsOnTable
for star { + } + + $($crate::__diesel_column! { + table = table, + name = $column_name, + sql_name = $column_sql_name, + ty = ($($column_ty)*), + $($column)* + })+ + + $crate::__diesel_valid_grouping_for_table_columns! { + primary_key = $primary_key, $($column_name,)* + } + } + } + } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_valid_grouping_for_table_columns { + (primary_key = ($primary_key: tt), $($cols: ident,)* ) => { + $crate::__diesel_valid_grouping_for_table_columns! { + primary_key = $primary_key, + $($cols,)* + } + }; + (primary_key = $primary_key: tt, $left_col: ident, $($right_col: ident,)+) => { + $( + $crate::static_cond! { + if $left_col == $primary_key { + impl $crate::expression::IsContainedInGroupBy<$right_col> for $left_col { + type Output = $crate::expression::is_contained_in_group_by::Yes; + } + } else { + impl $crate::expression::IsContainedInGroupBy<$right_col> for $left_col { + type Output = $crate::expression::is_contained_in_group_by::No; + } + } + } + + $crate::static_cond! { + if $right_col == $primary_key { + impl $crate::expression::IsContainedInGroupBy<$left_col> for $right_col { + type Output = $crate::expression::is_contained_in_group_by::Yes; + } + } else { + impl $crate::expression::IsContainedInGroupBy<$left_col> for $right_col { + type Output = $crate::expression::is_contained_in_group_by::No; + } + } + } + )* + $crate::__diesel_valid_grouping_for_table_columns! { + primary_key = $primary_key, + $($right_col,)* + } + }; + (primary_key = $primary_key: tt, $left_col: ident,) => {}; + (primary_key = $primary_key: tt) => {}; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_table_query_source_impl { + ($table_struct:ident, public, $table_name:expr) => { + impl $crate::QuerySource for $table_struct { + type FromClause = $crate::query_builder::nodes::Identifier<'static>; + type DefaultSelection = ::AllColumns; + + fn from_clause(&self) -> Self::FromClause { + $crate::query_builder::nodes::Identifier($table_name) + } + + fn default_selection(&self) -> Self::DefaultSelection { + use $crate::Table; + Self::all_columns() + } + } + }; + + ($table_struct:ident, $schema_name:ident, $table_name:expr) => { + impl $crate::QuerySource for $table_struct { + type FromClause = $crate::query_builder::nodes::InfixNode< + 'static, + $crate::query_builder::nodes::Identifier<'static>, + $crate::query_builder::nodes::Identifier<'static>, + >; + type DefaultSelection = ::AllColumns; + + fn from_clause(&self) -> Self::FromClause { + $crate::query_builder::nodes::InfixNode::new( + $crate::query_builder::nodes::Identifier(stringify!($schema_name)), + $crate::query_builder::nodes::Identifier($table_name), + ".", + ) + } + + fn default_selection(&self) -> Self::DefaultSelection { + use $crate::Table; + Self::all_columns() + } + } + }; +} + +/// Allow two tables to be referenced in a join query without providing an +/// explicit `ON` clause. +/// +/// The generated `ON` clause will always join to the primary key of the parent +/// table. This macro removes the need to call [`.on`] explicitly, you will +/// still need to invoke [`allow_tables_to_appear_in_same_query!`] for these two tables to +/// be able to use the resulting query, unless you are using `diesel print-schema` +/// which will generate it for you. +/// +/// If you are using `infer_schema!` or `diesel print-schema`, an invocation of +/// this macro will be generated for every foreign key in your database unless +/// one of the following is true: +/// +/// - The foreign key references something other than the primary key +/// - The foreign key is composite +/// - There is more than one foreign key connecting two tables +/// - The foreign key is self-referential +/// +/// # Example +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// use schema::*; +/// +/// # /* +/// joinable!(posts -> users (user_id)); +/// allow_tables_to_appear_in_same_query!(posts, users); +/// # */ +/// +/// # fn main() { +/// let implicit_on_clause = users::table.inner_join(posts::table); +/// let implicit_on_clause_sql = diesel::debug_query::(&implicit_on_clause).to_string(); +/// +/// let explicit_on_clause = users::table +/// .inner_join(posts::table.on(posts::user_id.eq(users::id))); +/// let explicit_on_clause_sql = diesel::debug_query::(&explicit_on_clause).to_string(); +/// +/// assert_eq!(implicit_on_clause_sql, explicit_on_clause_sql); +/// # } +/// +/// ``` +/// +/// In the example above, the line `joinable!(posts -> users (user_id));` +/// +/// specifies the relation of the tables and the ON clause in the following way: +/// +/// `child_table -> parent_table (foreign_key)` +/// +/// * `parent_table` is the Table with the Primary key. +/// +/// * `child_table` is the Table with the Foreign key. +/// +/// So given the Table decaration from [Associations docs](associations/index.html) +/// +/// * The parent table would be `User` +/// * The child table would be `Post` +/// * and the Foreign key would be `Post.user_id` +/// +/// For joins that do not explicitly use on clauses via [`JoinOnDsl`](prelude/trait.JoinOnDsl.html) +/// the following on clause is generated implicitly: +/// ```sql +/// post JOIN users ON posts.user_id = users.id +/// ``` +#[macro_export] +macro_rules! joinable { + ($($child:ident)::* -> $($parent:ident)::* ($source:ident)) => { + $crate::joinable_inner!($($child)::* ::table => $($parent)::* ::table : ($($child)::* ::$source = $($parent)::* ::table)); + $crate::joinable_inner!($($parent)::* ::table => $($child)::* ::table : ($($child)::* ::$source = $($parent)::* ::table)); + } +} + +#[macro_export] +#[doc(hidden)] +macro_rules! joinable_inner { + ($left_table:path => $right_table:path : ($foreign_key:path = $parent_table:path)) => { + $crate::joinable_inner!( + left_table_ty = $left_table, + right_table_ty = $right_table, + right_table_expr = $right_table, + foreign_key = $foreign_key, + primary_key_ty = <$parent_table as $crate::query_source::Table>::PrimaryKey, + primary_key_expr = + <$parent_table as $crate::query_source::Table>::primary_key(&$parent_table), + ); + }; + + ( + left_table_ty = $left_table_ty:ty, + right_table_ty = $right_table_ty:ty, + right_table_expr = $right_table_expr:expr, + foreign_key = $foreign_key:path, + primary_key_ty = $primary_key_ty:ty, + primary_key_expr = $primary_key_expr:expr, + ) => { + impl $crate::JoinTo<$right_table_ty> for $left_table_ty { + type FromClause = $right_table_ty; + type OnClause = $crate::dsl::Eq< + $crate::expression::nullable::Nullable<$foreign_key>, + $crate::expression::nullable::Nullable<$primary_key_ty>, + >; + + fn join_target(rhs: $right_table_ty) -> (Self::FromClause, Self::OnClause) { + use $crate::{ExpressionMethods, NullableExpressionMethods}; + + ( + rhs, + $foreign_key.nullable().eq($primary_key_expr.nullable()), + ) + } + } + }; +} + +/// Allow two or more tables which are otherwise unrelated to be used together +/// in a query. +/// +/// This macro must be invoked any time two tables need to appear in the same +/// query either because they are being joined together, or because one appears +/// in a subselect. When this macro is invoked with more than 2 tables, every +/// combination of those tables will be allowed to appear together. +/// +/// If you are using `diesel print-schema`, an invocation of +/// this macro will be generated for you for all tables in your schema. +/// +/// # Example +/// +/// ```ignore +/// // This would be required to do `users.inner_join(posts.inner_join(comments))` +/// allow_tables_to_appear_in_same_query!(comments, posts, users); +/// ``` +/// +/// When more than two tables are passed, the relevant code is generated for +/// every combination of those tables. This code would be equivalent to the +/// previous example. +/// +/// ```ignore +/// allow_tables_to_appear_in_same_query!(comments, posts); +/// allow_tables_to_appear_in_same_query!(comments, users); +/// allow_tables_to_appear_in_same_query!(posts, users); +/// ``` +#[macro_export] +macro_rules! allow_tables_to_appear_in_same_query { + ($left_mod:ident, $($right_mod:ident),+ $(,)*) => { + $( + impl $crate::query_source::AppearsInFromClause<$left_mod::table> + for $right_mod::table + { + type Count = $crate::query_source::Never; + } + + impl $crate::query_source::AppearsInFromClause<$right_mod::table> + for $left_mod::table + { + type Count = $crate::query_source::Never; + } + )+ + $crate::allow_tables_to_appear_in_same_query!($($right_mod,)+); + }; + + ($last_table:ident,) => {}; + + () => {}; +} + +#[doc(hidden)] +#[macro_export] +macro_rules! __diesel_impl_allow_in_same_group_by_clause { + ( + left = [$($left_path:tt)::+], + ) => {}; + ( + left = [$($left_path:tt)::+], + $($right_path:tt)::+ + ) => { + $crate::__diesel_impl_allow_in_same_group_by_clause! { + left = [$($left_path)+], + right = [$($right_path)+], + left_tbl = [], + left_path = [], + } + }; + ( + left = [$($left_path:tt)::+], + $($right_path:tt)::+, + $($other:tt)* + ) => { + $crate::__diesel_impl_allow_in_same_group_by_clause! { + left = [$($left_path)+], + right = [$($right_path)+], + left_tbl = [], + left_path = [], + } + $crate::__diesel_impl_allow_in_same_group_by_clause! { + left = [$($left_path)::+], + $($other)* + } + }; + ( + left = [$left_path_p1: tt $($left_path: tt)+], + right = [$($right_path: tt)*], + left_tbl = [$($left_tbl:tt)?], + left_path = [$($left_out_path:tt)*], + ) => { + $crate::__diesel_impl_allow_in_same_group_by_clause! { + left = [$($left_path)+], + right = [$($right_path)*], + left_tbl = [$left_path_p1], + left_path = [$($left_out_path)* $($left_tbl)?], + } + }; + ( + left = [$left_col: tt], + right = [$($right_path: tt)*], + left_tbl = [$($left_tbl:tt)?], + left_path = [$($left_out_path:tt)*], + ) => { + $crate::__diesel_impl_allow_in_same_group_by_clause! { + left = [$left_col], + right = [$($right_path)*], + left_tbl = [$($left_tbl)?], + left_path = [$($left_out_path)*], + right_tbl = [], + right_path = [], + } + }; + ( + left = [$left_col: tt ], + right = [$right_path_p1: tt $($right_path: tt)+], + left_tbl = [$($left_tbl:tt)?], + left_path = [$($left_out_path:tt)*], + right_tbl = [$($right_tbl:tt)?], + right_path = [$($right_out_path:tt)*], + ) => { + $crate::__diesel_impl_allow_in_same_group_by_clause! { + left = [$left_col], + right = [$($right_path)+], + left_tbl = [$($left_tbl)?], + left_path = [$($left_out_path)*], + right_tbl = [$right_path_p1], + right_path = [$($right_out_path)* $($right_tbl)?], + } + }; + ( + left = [$left_col: tt], + right = [$right_col: tt], + left_tbl = [$left_tbl:tt], + left_path = [$($left_begin:tt)*], + right_tbl = [$right_tbl:tt], + right_path = [$($right_begin:tt)*], + ) => { + $crate::static_cond! { + if $left_tbl != $right_tbl { + impl $crate::expression::IsContainedInGroupBy<$($left_begin ::)* $left_tbl :: $left_col> for $($right_begin ::)* $right_tbl :: $right_col { + type Output = $crate::expression::is_contained_in_group_by::No; + } + + impl $crate::expression::IsContainedInGroupBy<$($right_begin ::)* $right_tbl :: $right_col> for $($left_begin ::)* $left_tbl :: $left_col { + type Output = $crate::expression::is_contained_in_group_by::No; + } + } + } + }; + ( + left = [$left_col: tt], + right = [$right_col: tt], + left_tbl = [$($left_tbl:tt)?], + left_path = [$($left_begin:tt)*], + right_tbl = [$($right_tbl:tt)?], + right_path = [$($right_begin:tt)*], + ) => { + impl $crate::expression::IsContainedInGroupBy<$($left_begin ::)* $($left_tbl ::)? $left_col> for $($right_begin ::)* $($right_tbl ::)? $right_col { + type Output = $crate::expression::is_contained_in_group_by::No; + } + + impl $crate::expression::IsContainedInGroupBy<$($right_begin ::)* $($right_tbl ::)? $right_col> for $($left_begin ::)* $($left_tbl ::)? $left_col { + type Output = $crate::expression::is_contained_in_group_by::No; + } + }; + +} + + +/// Allow two or more columns which are otherwise unrelated to be used together +/// in a group by clause. +/// +/// This macro must be invoked any time two columns need to appear in the same +/// group by clause. When this macro is invoked with more than 2 columns, every +/// combination of those columns will be allowed to appear together. +/// +/// # Example +/// +/// ```ignore +/// // This would be required to do +/// // `users::table.inner_join(posts::table).group_by((users::name, users::hair_color, posts::id))` +/// allow_columns_to_appear_in_same_group_by_clause!(users::name, users::hair_color, posts::id); +/// ``` +/// +/// When more than two columns are passed, the relevant code is generated for +/// every combination of those columns. This code would be equivalent to the +/// previous example. +/// +/// ```ignore +/// allow_columns_to_appear_in_same_group_by_clause!(users::name, users::hair_color); +/// allow_columns_to_appear_in_same_group_by_clause!(users::name, posts::id); +/// allow_columns_to_appear_in_same_group_by_clause!(users::hair_color, posts::id); +/// ``` +#[macro_export] +macro_rules! allow_columns_to_appear_in_same_group_by_clause { + ($($left_path:tt)::+, $($right_path:tt)::+ $(,)?) => { + $crate::__diesel_impl_allow_in_same_group_by_clause! { + left = [$($left_path)::+], + $($right_path)::+, + } + }; + ($($left_path:tt)::+, $($right_path:tt)::+, $($other: tt)*) => { + $crate::__diesel_impl_allow_in_same_group_by_clause! { + left = [$($left_path)::+], + $($right_path)::+, + $($other)* + } + $crate::allow_columns_to_appear_in_same_group_by_clause! { + $($right_path)::+, + $($other)* + } + }; + ($last_col:ty,) => {}; + () => {}; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_with_dollar_sign { + ($($body:tt)*) => { + macro_rules! __with_dollar_sign { $($body)* } + __with_dollar_sign!($); + } +} + + +// The order of these modules is important (at least for those which have tests). +// Utility macros which don't call any others need to come first. +#[macro_use] +mod internal; +#[macro_use] +mod static_cond; +#[macro_use] +mod ops; +#[macro_use] +mod tuples; + +#[cfg(test)] +mod tests { + use crate::prelude::*; + + table! { + foo.bars { + id -> Integer, + baz -> Text, + } + } + + mod my_types { + #[derive(Debug, Clone, Copy, crate::sql_types::SqlType)] + pub struct MyCustomType; + } + + table! { + use crate::sql_types::*; + use crate::macros::tests::my_types::*; + + table_with_custom_types { + id -> Integer, + my_type -> MyCustomType, + } + } + + table! { + use crate::sql_types::*; + use crate::macros::tests::my_types::*; + + /// Table documentation + /// + /// some in detail documentation + table_with_custom_type_and_id (a) { + /// Column documentation + /// + /// some more details + a -> Integer, + my_type -> MyCustomType, + } + } + + #[test] + #[cfg(feature = "postgres")] + fn table_with_custom_schema() { + use crate::pg::Pg; + let expected_sql = r#"SELECT "foo"."bars"."baz" FROM "foo"."bars" -- binds: []"#; + assert_eq!( + expected_sql, + &crate::debug_query::(&bars::table.select(bars::baz)).to_string() + ); + } + + table! { + use crate::sql_types; + use crate::sql_types::*; + + table_with_arbitrarily_complex_types { + id -> sql_types::Integer, + qualified_nullable -> sql_types::Nullable, + deeply_nested_type -> Nullable>, + // This actually should work, but there appears to be a rustc bug + // on the `AsExpression` bound for `EqAll` when the ty param is a projection + // projected_type -> as sql_types::IntoNullable>::Nullable, + //random_tuple -> (Integer, Integer), + } + } + + table!( + foo { + /// Column doc + id -> Integer, + + #[sql_name = "type"] + /// Also important to document this column + mytype -> Integer, + + /// And this one + #[sql_name = "bleh"] + hey -> Integer, + } + ); + + #[test] + #[cfg(feature = "postgres")] + fn table_with_column_renaming_postgres() { + use crate::pg::Pg; + let expected_sql = + r#"SELECT "foo"."id", "foo"."type", "foo"."bleh" FROM "foo" WHERE ("foo"."type" = $1) -- binds: [1]"#; + assert_eq!( + expected_sql, + crate::debug_query::(&foo::table.filter(foo::mytype.eq(1))).to_string() + ); + } + + #[test] + #[cfg(feature = "mysql")] + fn table_with_column_renaming_mysql() { + use crate::mysql::Mysql; + let expected_sql = + r#"SELECT `foo`.`id`, `foo`.`type`, `foo`.`bleh` FROM `foo` WHERE (`foo`.`type` = ?) -- binds: [1]"#; + assert_eq!( + expected_sql, + crate::debug_query::(&foo::table.filter(foo::mytype.eq(1))).to_string() + ); + } + + #[test] + #[cfg(feature = "sqlite")] + fn table_with_column_renaming_sqlite() { + use crate::sqlite::Sqlite; + let expected_sql = + r#"SELECT `foo`.`id`, `foo`.`type`, `foo`.`bleh` FROM `foo` WHERE (`foo`.`type` = ?) -- binds: [1]"#; + assert_eq!( + expected_sql, + crate::debug_query::(&foo::table.filter(foo::mytype.eq(1))).to_string() + ); + } + + table!( + use crate::sql_types::*; + + /// Some documentation + #[sql_name="mod"] + /// Some more documentation + bar { + id -> Integer, + } + ); + + #[test] + #[cfg(feature = "postgres")] + fn table_renaming_postgres() { + use crate::pg::Pg; + let expected_sql = r#"SELECT "mod"."id" FROM "mod" -- binds: []"#; + assert_eq!( + expected_sql, + crate::debug_query::(&bar::table.select(bar::id)).to_string() + ); + } + + #[test] + #[cfg(feature = "mysql")] + fn table_renaming_mysql() { + use crate::mysql::Mysql; + let expected_sql = r#"SELECT `mod`.`id` FROM `mod` -- binds: []"#; + assert_eq!( + expected_sql, + crate::debug_query::(&bar::table.select(bar::id)).to_string() + ); + } + + #[test] + #[cfg(feature = "sqlite")] + fn table_renaming_sqlite() { + use crate::sqlite::Sqlite; + let expected_sql = r#"SELECT `mod`.`id` FROM `mod` -- binds: []"#; + assert_eq!( + expected_sql, + crate::debug_query::(&bar::table.select(bar::id)).to_string() + ); + } + + mod tests_for_allow_combined_group_by_syntax { + table! { + a(b) { + b -> Text, + c -> Text, + d -> Text, + e -> Text, + } + } + + table! { + b(a) { + a -> Text, + c -> Text, + d -> Text, + } + } + + table! { + c(a) { + a -> Text, + b -> Text, + d -> Text, + } + } + + // allow using table::collumn + allow_columns_to_appear_in_same_group_by_clause!( + a::b, b::a, a::d, + ); + + // allow using full paths + allow_columns_to_appear_in_same_group_by_clause!( + self::a::c, self::b::c, self::b::d, + ); + + use self::a::d as a_d; + use self::b::d as b_d; + use self::c::d as c_d; + + // allow using plain identifiers + allow_columns_to_appear_in_same_group_by_clause!( + a_d, b_d, c_d + ); + + // allow mixing all variants + allow_columns_to_appear_in_same_group_by_clause!( + c_d, self::b::a, a::e, + ); + } +} diff --git a/collector/benchmarks/diesel/diesel/src/macros/ops.rs b/collector/benchmarks/diesel/diesel/src/macros/ops.rs new file mode 100644 index 000000000..dac4fde71 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/macros/ops.rs @@ -0,0 +1,106 @@ +#[macro_export] +/// Implements the Rust operator for a given type. If you create a new SQL +/// function, which returns a type that you'd like to use an operator on, you +/// should invoke this macro. Unfortunately, Rust disallows us from +/// automatically implementing `Add` and other traits from `std::ops`, under its +/// orphan rules. +macro_rules! operator_allowed { + ($tpe:ty, $op:ident, $fn_name:ident) => { + impl ::std::ops::$op for $tpe + where + Rhs: $crate::expression::AsExpression< + <<$tpe as $crate::Expression>::SqlType as $crate::sql_types::ops::$op>::Rhs, + >, + { + type Output = $crate::expression::ops::$op; + + fn $fn_name(self, rhs: Rhs) -> Self::Output { + $crate::expression::ops::$op::new(self, rhs.as_expression()) + } + } + }; +} + +#[macro_export] +/// Indicates that an expression allows all numeric operators. If you create new +/// SQL functions that return a numeric type, you should invoke this macro that +/// type. Unfortunately, Rust disallows us from automatically implementing `Add` +/// for types which implement `Expression`, under its orphan rules. +macro_rules! numeric_expr { + ($tpe:ty) => { + $crate::operator_allowed!($tpe, Add, add); + $crate::operator_allowed!($tpe, Sub, sub); + $crate::operator_allowed!($tpe, Div, div); + $crate::operator_allowed!($tpe, Mul, mul); + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_generate_ops_impls_if_numeric { + ($column_name:ident, Nullable<$($inner:tt)::*>) => { $crate::__diesel_generate_ops_impls_if_numeric!($column_name, $($inner)::*); }; + + ($column_name:ident, Unsigned<$($inner:tt)::*>) => { $crate::__diesel_generate_ops_impls_if_numeric!($column_name, $($inner)::*); }; + + ($column_name:ident, SmallInt) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, Int2) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, Smallint) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, SmallSerial) => { $crate::numeric_expr!($column_name); }; + + ($column_name:ident, Integer) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, Int4) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, Serial) => { $crate::numeric_expr!($column_name); }; + + ($column_name:ident, BigInt) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, Int8) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, Bigint) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, BigSerial) => { $crate::numeric_expr!($column_name); }; + + ($column_name:ident, Float) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, Float4) => { $crate::numeric_expr!($column_name); }; + + ($column_name:ident, Double) => { $crate::numeric_expr!($column_name); }; + ($column_name:ident, Float8) => { $crate::numeric_expr!($column_name); }; + + ($column_name:ident, Numeric) => { $crate::numeric_expr!($column_name); }; + + ($column_name:ident, $non_numeric_type:ty) => {}; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! date_time_expr { + ($tpe:ty) => { + $crate::operator_allowed!($tpe, Add, add); + $crate::operator_allowed!($tpe, Sub, sub); + }; +} + +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_generate_ops_impls_if_date_time { + ($column_name:ident, Nullable<$($inner:tt)::*>) => { $crate::__diesel_generate_ops_impls_if_date_time!($column_name, $($inner)::*); }; + ($column_name:ident, Time) => { $crate::date_time_expr!($column_name); }; + ($column_name:ident, Date) => { $crate::date_time_expr!($column_name); }; + ($column_name:ident, Timestamp) => { $crate::date_time_expr!($column_name); }; + ($column_name:ident, Timestamptz) => { $crate::date_time_expr!($column_name); }; + ($column_name:ident, $non_date_time_type:ty) => {}; +} + +#[macro_export(local_inner_macros)] +#[doc(hidden)] +macro_rules! network_expr { + ($tpe:ty) => { + operator_allowed!($tpe, Add, add); + operator_allowed!($tpe, Sub, sub); + }; +} + +#[macro_export(local_inner_macros)] +#[doc(hidden)] +macro_rules! __diesel_generate_ops_impls_if_network { + ($column_name:ident, Nullable<$($inner:tt)::*>) => { __diesel_generate_ops_impls_if_network!($column_name, $($inner)::*); }; + ($column_name:ident, Cidr) => { network_expr!($column_name); }; + ($column_name:ident, Inet) => { network_expr!($column_name); }; + ($column_name:ident, $non_network_type:ty) => {}; +} diff --git a/collector/benchmarks/diesel/diesel/src/macros/static_cond.rs b/collector/benchmarks/diesel/diesel/src/macros/static_cond.rs new file mode 100644 index 000000000..d59c78ee2 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/macros/static_cond.rs @@ -0,0 +1,38 @@ +#![cfg_attr(rustfmt, rustfmt_skip)] // https://github.com/rust-lang-nursery/rustfmt/issues/2754 + +// Vendored from the static-cond crate as macro re-exports are not available in stable Rust. +// https://github.com/durka/static-cond/blob/36aa2dd/src/lib.rs +// +// Code is dual licensed under MIT/Apache-2.0 +// Copyright (c) 2016 Alex Burka +#[macro_export] +#[doc(hidden)] +macro_rules! static_cond { + // private rule to define and call the local macro + (@go $lhs:tt $rhs:tt $arm1:tt $arm2:tt) => { + // note that the inner macro has no captures (it can't, because there's no way to escape `$`) + macro_rules! __static_cond { + ($lhs $lhs) => $arm1; + ($lhs $rhs) => $arm2 + } + + __static_cond!($lhs $rhs); + }; + + // no else condition provided: fall through with empty else + (if $lhs:tt == $rhs:tt $then:tt) => { + $crate::static_cond!(if $lhs == $rhs $then else { }); + }; + (if $lhs:tt != $rhs:tt $then:tt) => { + $crate::static_cond!(if $lhs != $rhs $then else { }); + }; + + // we evaluate a conditional by generating a new macro (in an inner scope, so name shadowing is + // not a big concern) and calling it + (if $lhs:tt == $rhs:tt $then:tt else $els:tt) => { + $crate::static_cond!(@go $lhs $rhs $then $els); + }; + (if $lhs:tt != $rhs:tt $then:tt else $els:tt) => { + $crate::static_cond!(@go $lhs $rhs $els $then); + }; +} diff --git a/collector/benchmarks/diesel/diesel/src/macros/tuples.rs b/collector/benchmarks/diesel/diesel/src/macros/tuples.rs new file mode 100644 index 000000000..8a83e32a0 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/macros/tuples.rs @@ -0,0 +1,8532 @@ +#[macro_export] +#[doc(hidden)] +macro_rules! __diesel_for_each_tuple { + ($callback:ident) => { + $callback! { + 1 { + (0) -> A, SA, TA, + } + 2 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + } + 3 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + } + 4 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + } + 5 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + } + 6 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + } + 7 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + } + 8 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + } + 9 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + } + 10 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + } + 11 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + } + 12 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + } + 13 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + } + 14 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + } + 15 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + } + 16 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + } + } + + #[cfg(feature = "32-column-tables")] + $callback! { + 17 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + } + 18 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + } + 19 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + } + 20 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + } + 21 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + } + 22 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + } + 23 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + } + 24 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + } + 25 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + } + 26 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + } + 27 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + } + 28 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + } + 29 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + } + 30 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + } + 31 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + } + 32 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + } + } + + #[cfg(feature = "64-column-tables")] + $callback! { + 33 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + } + 34 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + } + 35 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + } + 36 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + } + 37 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + } + 38 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + } + 39 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + } + 40 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + } + 41 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + } + 42 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + } + 43 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + } + 44 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + } + 45 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + } + 46 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + } + 47 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + } + 48 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + } + 49 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + } + 50 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + } + 51 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + } + 52 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + } + 53 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + } + 54 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + } + 55 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + } + 56 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + } + 57 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + } + 58 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + } + 59 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + } + 60 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + } + 61 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + } + 62 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + } + 63 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + } + 64 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + } + } + + #[cfg(feature = "128-column-tables")] + $callback! { + 65 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + } + 66 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + } + 67 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + } + 68 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + } + 69 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + } + 70 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + } + 71 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + } + 72 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + } + 73 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + } + 74 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + } + 75 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + } + 76 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + } + 77 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + } + 78 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + } + 79 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + } + 80 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + } + 81 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + } + 82 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + } + 83 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + } + 84 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + } + 85 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + } + 86 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + } + 87 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + } + 88 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + } + 89 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + } + 90 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + } + 91 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + } + 92 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + } + 93 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + } + 94 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + } + 95 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + } + 96 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + } + 97 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + } + 98 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + } + 99 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + } + 100 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + } + 101 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + } + 102 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + } + 103 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + } + 104 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + } + 105 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + } + 106 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + } + 107 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + } + 108 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + } + 109 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + } + 110 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + } + 111 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + } + 112 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + } + 113 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + } + 114 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + } + 115 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + } + 116 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + (115) -> DL, SDL, TDL, + } + 117 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + (115) -> DL, SDL, TDL, + (116) -> DM, SDM, TDM, + } + 118 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + (115) -> DL, SDL, TDL, + (116) -> DM, SDM, TDM, + (117) -> DN, SDN, TDN, + } + 119 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + (115) -> DL, SDL, TDL, + (116) -> DM, SDM, TDM, + (117) -> DN, SDN, TDN, + (118) -> DO, SDO, TDO, + } + 120 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + (115) -> DL, SDL, TDL, + (116) -> DM, SDM, TDM, + (117) -> DN, SDN, TDN, + (118) -> DO, SDO, TDO, + (119) -> DP, SDP, TDP, + } + 121 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + (115) -> DL, SDL, TDL, + (116) -> DM, SDM, TDM, + (117) -> DN, SDN, TDN, + (118) -> DO, SDO, TDO, + (119) -> DP, SDP, TDP, + (120) -> DQ, SDQ, TDQ, + } + 122 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + (115) -> DL, SDL, TDL, + (116) -> DM, SDM, TDM, + (117) -> DN, SDN, TDN, + (118) -> DO, SDO, TDO, + (119) -> DP, SDP, TDP, + (120) -> DQ, SDQ, TDQ, + (121) -> DR, SDR, TDR, + } + 123 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + (115) -> DL, SDL, TDL, + (116) -> DM, SDM, TDM, + (117) -> DN, SDN, TDN, + (118) -> DO, SDO, TDO, + (119) -> DP, SDP, TDP, + (120) -> DQ, SDQ, TDQ, + (121) -> DR, SDR, TDR, + (122) -> DS, SDS, TDS, + } + 124 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + (115) -> DL, SDL, TDL, + (116) -> DM, SDM, TDM, + (117) -> DN, SDN, TDN, + (118) -> DO, SDO, TDO, + (119) -> DP, SDP, TDP, + (120) -> DQ, SDQ, TDQ, + (121) -> DR, SDR, TDR, + (122) -> DS, SDS, TDS, + (123) -> DT, SDT, TDT, + } + 125 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + (115) -> DL, SDL, TDL, + (116) -> DM, SDM, TDM, + (117) -> DN, SDN, TDN, + (118) -> DO, SDO, TDO, + (119) -> DP, SDP, TDP, + (120) -> DQ, SDQ, TDQ, + (121) -> DR, SDR, TDR, + (122) -> DS, SDS, TDS, + (123) -> DT, SDT, TDT, + (124) -> DU, SDU, TDU, + } + 126 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + (115) -> DL, SDL, TDL, + (116) -> DM, SDM, TDM, + (117) -> DN, SDN, TDN, + (118) -> DO, SDO, TDO, + (119) -> DP, SDP, TDP, + (120) -> DQ, SDQ, TDQ, + (121) -> DR, SDR, TDR, + (122) -> DS, SDS, TDS, + (123) -> DT, SDT, TDT, + (124) -> DU, SDU, TDU, + (125) -> DV, SDV, TDV, + } + 127 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + (115) -> DL, SDL, TDL, + (116) -> DM, SDM, TDM, + (117) -> DN, SDN, TDN, + (118) -> DO, SDO, TDO, + (119) -> DP, SDP, TDP, + (120) -> DQ, SDQ, TDQ, + (121) -> DR, SDR, TDR, + (122) -> DS, SDS, TDS, + (123) -> DT, SDT, TDT, + (124) -> DU, SDU, TDU, + (125) -> DV, SDV, TDV, + (126) -> DW, SDW, TDW, + } + 128 { + (0) -> A, SA, TA, + (1) -> B, SB, TB, + (2) -> C, SC, TC, + (3) -> D, SD, TD, + (4) -> E, SE, TE, + (5) -> F, SF, TF, + (6) -> G, SG, TG, + (7) -> H, SH, TH, + (8) -> I, SI, TI, + (9) -> J, SJ, TJ, + (10) -> K, SK, TK, + (11) -> L, SL, TL, + (12) -> M, SM, TM, + (13) -> N, SN, TN, + (14) -> O, SO, TO, + (15) -> P, SP, TP, + (16) -> Q, SQ, TQ, + (17) -> R, SR, TR, + (18) -> S, SS, TS, + (19) -> T, ST, TT, + (20) -> U, SU, TU, + (21) -> V, SV, TV, + (22) -> W, SW, TW, + (23) -> X, SX, TX, + (24) -> Y, SY, TY, + (25) -> Z, SZ, TZ, + (26) -> AA, SAA, TAA, + (27) -> AB, SAB, TAB, + (28) -> AC, SAC, TAC, + (29) -> AD, SAD, TAD, + (30) -> AE, SAE, TAE, + (31) -> AF, SAF, TAF, + (32) -> AG, SAG, TAG, + (33) -> AH, SAH, TAH, + (34) -> AI, SAI, TAI, + (35) -> AJ, SAJ, TAJ, + (36) -> AK, SAK, TAK, + (37) -> AL, SAL, TAL, + (38) -> AM, SAM, TAM, + (39) -> AN, SAN, TAN, + (40) -> AO, SAO, TAO, + (41) -> AP, SAP, TAP, + (42) -> AQ, SAQ, TAQ, + (43) -> AR, SAR, TAR, + (44) -> AS, SAS, TAS, + (45) -> AT, SAT, TAT, + (46) -> AU, SAU, TAU, + (47) -> AV, SAV, TAV, + (48) -> AW, SAW, TAW, + (49) -> AX, SAX, TAX, + (50) -> AY, SAY, TAY, + (51) -> AZ, SAZ, TAZ, + (52) -> BA, SBA, TBA, + (53) -> BB, SBB, TBB, + (54) -> BC, SBC, TBC, + (55) -> BD, SBD, TBD, + (56) -> BE, SBE, TBE, + (57) -> BF, SBF, TBF, + (58) -> BG, SBG, TBG, + (59) -> BH, SBH, TBH, + (60) -> BI, SBI, TBI, + (61) -> BJ, SBJ, TBJ, + (62) -> BK, SBK, TBK, + (63) -> BL, SBL, TBL, + (64) -> BM, SBM, TBM, + (65) -> BN, SBN, TBN, + (66) -> BO, SBO, TBO, + (67) -> BP, SBP, TBP, + (68) -> BQ, SBQ, TBQ, + (69) -> BR, SBR, TBR, + (70) -> BS, SBS, TBS, + (71) -> BT, SBT, TBT, + (72) -> BU, SBU, TBU, + (73) -> BV, SBV, TBV, + (74) -> BW, SBW, TBW, + (75) -> BX, SBX, TBX, + (76) -> BY, SBY, TBY, + (77) -> BZ, SBZ, TBZ, + (78) -> CA, SCA, TCA, + (79) -> CB, SCB, TCB, + (80) -> CC, SCC, TCC, + (81) -> CD, SCD, TCD, + (82) -> CE, SCE, TCE, + (83) -> CF, SCF, TCF, + (84) -> CG, SCG, TCG, + (85) -> CH, SCH, TCH, + (86) -> CI, SCI, TCI, + (87) -> CJ, SCJ, TCJ, + (88) -> CK, SCK, TCK, + (89) -> CL, SCL, TCL, + (90) -> CM, SCM, TCM, + (91) -> CN, SCN, TCN, + (92) -> CO, SCO, TCO, + (93) -> CP, SCP, TCP, + (94) -> CQ, SCQ, TCQ, + (95) -> CR, SCR, TCR, + (96) -> CS, SCS, TCS, + (97) -> CT, SCT, TCT, + (98) -> CU, SCU, TCU, + (99) -> CV, SCV, TCV, + (100) -> CW, SCW, TCW, + (101) -> CX, SCX, TCX, + (102) -> CY, SCY, TCY, + (103) -> CZ, SCZ, TCZ, + (104) -> DA, SDA, TDA, + (105) -> DB, SDB, TDB, + (106) -> DC, SDC, TDC, + (107) -> DD, SDD, TDD, + (108) -> DE, SDE, TDE, + (109) -> DF, SDF, TDF, + (110) -> DG, SDG, TDG, + (111) -> DH, SDH, TDH, + (112) -> DI, SDI, TDI, + (113) -> DJ, SDJ, TDJ, + (114) -> DK, SDK, TDK, + (115) -> DL, SDL, TDL, + (116) -> DM, SDM, TDM, + (117) -> DN, SDN, TDN, + (118) -> DO, SDO, TDO, + (119) -> DP, SDP, TDP, + (120) -> DQ, SDQ, TDQ, + (121) -> DR, SDR, TDR, + (122) -> DS, SDS, TDS, + (123) -> DT, SDT, TDT, + (124) -> DU, SDU, TDU, + (125) -> DV, SDV, TDV, + (126) -> DW, SDW, TDW, + (127) -> DX, SDX, TDX, + } + } + }; +} diff --git a/collector/benchmarks/diesel/diesel/src/migration/errors.rs b/collector/benchmarks/diesel/diesel/src/migration/errors.rs new file mode 100644 index 000000000..c0983a71e --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/migration/errors.rs @@ -0,0 +1,121 @@ +//! Error types that represent migration errors. +//! These are split into multiple segments, depending on +//! where in the migration process an error occurs. + +use std::convert::From; +use std::error::Error; +use std::path::PathBuf; +use std::{fmt, io}; + +use crate::result; + +/// Errors that occur while preparing to run migrations +#[derive(Debug)] +#[non_exhaustive] +pub enum MigrationError { + /// The migration directory wasn't found + MigrationDirectoryNotFound(PathBuf), + /// Provided migration was in an unknown format + UnknownMigrationFormat(PathBuf), + /// General system IO error + IoError(io::Error), + /// Provided migration had an incompatible version number + UnknownMigrationVersion(String), + /// No migrations had to be/ could be run + NoMigrationRun, +} + +impl Error for MigrationError {} + +impl fmt::Display for MigrationError { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match *self { + MigrationError::MigrationDirectoryNotFound(ref p) => write!( + f, + "Unable to find migrations directory in {:?} or any parent directories.", + p + ), + MigrationError::UnknownMigrationFormat(_) => write!( + f, + "Invalid migration directory, the directory's name should be \ + _, and it should only contain up.sql and down.sql." + ), + MigrationError::IoError(ref error) => write!(f, "{}", error), + MigrationError::UnknownMigrationVersion(_) => write!( + f, + "Unable to find migration version to revert in the migrations directory." + ), + MigrationError::NoMigrationRun => write!( + f, + "No migrations have been run. Did you forget `diesel migration run`?" + ), + } + } +} + +impl PartialEq for MigrationError { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + ( + &MigrationError::MigrationDirectoryNotFound(_), + &MigrationError::MigrationDirectoryNotFound(_), + ) => true, + ( + &MigrationError::UnknownMigrationFormat(ref p1), + &MigrationError::UnknownMigrationFormat(ref p2), + ) => p1 == p2, + _ => false, + } + } +} + +impl From for MigrationError { + fn from(e: io::Error) -> Self { + MigrationError::IoError(e) + } +} + +/// Errors that occur while running migrations +#[derive(Debug, PartialEq)] +#[allow(clippy::enum_variant_names)] +#[non_exhaustive] +pub enum RunMigrationsError { + /// A general migration error occured + MigrationError(MigrationError), + /// The provided migration included an invalid query + QueryError(result::Error), + /// The provided migration was empty + EmptyMigration, +} + +impl Error for RunMigrationsError {} + +impl fmt::Display for RunMigrationsError { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match *self { + RunMigrationsError::MigrationError(ref error) => write!(f, "Failed with: {}", error), + RunMigrationsError::QueryError(ref error) => write!(f, "Failed with: {}", error), + RunMigrationsError::EmptyMigration => { + write!(f, "Failed with: Attempted to run an empty migration.") + } + } + } +} + +impl From for RunMigrationsError { + fn from(e: MigrationError) -> Self { + RunMigrationsError::MigrationError(e) + } +} + +impl From for RunMigrationsError { + fn from(e: result::Error) -> Self { + RunMigrationsError::QueryError(e) + } +} + +impl From for RunMigrationsError { + fn from(e: io::Error) -> Self { + RunMigrationsError::MigrationError(e.into()) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/migration/mod.rs b/collector/benchmarks/diesel/diesel/src/migration/mod.rs new file mode 100644 index 000000000..a37068d03 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/migration/mod.rs @@ -0,0 +1,102 @@ +//! Representation of migrations + +mod errors; +pub use self::errors::{MigrationError, RunMigrationsError}; + +use crate::connection::{Connection, SimpleConnection}; +use crate::result::QueryResult; +use std::path::Path; + +/// Represents a migration that interacts with diesel +pub trait Migration { + /// Get the migration version + fn version(&self) -> &str; + /// Apply this migration + fn run(&self, conn: &dyn SimpleConnection) -> Result<(), RunMigrationsError>; + /// Revert this migration + fn revert(&self, conn: &dyn SimpleConnection) -> Result<(), RunMigrationsError>; + /// Get the migration file path + fn file_path(&self) -> Option<&Path> { + None + } +} + +impl<'a> Migration for Box { + fn version(&self) -> &str { + (&**self).version() + } + + fn run(&self, conn: &dyn SimpleConnection) -> Result<(), RunMigrationsError> { + (&**self).run(conn) + } + + fn revert(&self, conn: &dyn SimpleConnection) -> Result<(), RunMigrationsError> { + (&**self).revert(conn) + } + fn file_path(&self) -> Option<&Path> { + (&**self).file_path() + } +} + +impl<'a> Migration for &'a dyn Migration { + fn version(&self) -> &str { + (&**self).version() + } + + fn run(&self, conn: &dyn SimpleConnection) -> Result<(), RunMigrationsError> { + (&**self).run(conn) + } + + fn revert(&self, conn: &dyn SimpleConnection) -> Result<(), RunMigrationsError> { + (&**self).revert(conn) + } + fn file_path(&self) -> Option<&Path> { + (&**self).file_path() + } +} + +/// Create table statement for the `__diesel_schema_migrations` used +/// used by the postgresql, sqlite and mysql backend +pub const CREATE_MIGRATIONS_TABLE: &str = include_str!("setup_migration_table.sql"); + +/// A trait indicating that a connection could be used to manage migrations +/// +/// Only custom backend implementations need to think about this trait +pub trait MigrationConnection: Connection { + /// Setup the following table: + /// + /// ```rust + /// diesel::table! { + /// __diesel_schema_migrations(version) { + /// version -> Text, + /// /// defaults to `CURRENT_TIMESTAMP` + /// run_on -> Timestamp, + /// } + /// } + /// ``` + fn setup(&self) -> QueryResult; +} + +#[cfg(feature = "postgres")] +impl MigrationConnection for crate::pg::PgConnection { + fn setup(&self) -> QueryResult { + use crate::RunQueryDsl; + crate::sql_query(CREATE_MIGRATIONS_TABLE).execute(self) + } +} + +#[cfg(feature = "mysql")] +impl MigrationConnection for crate::mysql::MysqlConnection { + fn setup(&self) -> QueryResult { + use crate::RunQueryDsl; + crate::sql_query(CREATE_MIGRATIONS_TABLE).execute(self) + } +} + +#[cfg(feature = "sqlite")] +impl MigrationConnection for crate::sqlite::SqliteConnection { + fn setup(&self) -> QueryResult { + use crate::RunQueryDsl; + crate::sql_query(CREATE_MIGRATIONS_TABLE).execute(self) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/migration/setup_migration_table.sql b/collector/benchmarks/diesel/diesel/src/migration/setup_migration_table.sql new file mode 100644 index 000000000..6e2594703 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/migration/setup_migration_table.sql @@ -0,0 +1,4 @@ +CREATE TABLE IF NOT EXISTS __diesel_schema_migrations ( + version VARCHAR(50) PRIMARY KEY NOT NULL, + run_on TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP +); diff --git a/collector/benchmarks/diesel/diesel/src/mysql/backend.rs b/collector/benchmarks/diesel/diesel/src/mysql/backend.rs new file mode 100644 index 000000000..39482b871 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/mysql/backend.rs @@ -0,0 +1,81 @@ +//! The MySQL backend + +use byteorder::NativeEndian; + +use super::query_builder::MysqlQueryBuilder; +use super::MysqlValue; +use crate::backend::*; +use crate::query_builder::bind_collector::RawBytesBindCollector; +use crate::sql_types::TypeMetadata; + +/// The MySQL backend +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct Mysql; + +#[allow(missing_debug_implementations)] +/// Represents possible types, that can be transmitted as via the +/// Mysql wire protocol +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +#[non_exhaustive] +pub enum MysqlType { + /// A 8 bit signed integer + Tiny, + /// A 8 bit unsigned integer + UnsignedTiny, + /// A 16 bit signed integer + Short, + /// A 16 bit unsigned integer + UnsignedShort, + /// A 32 bit signed integer + Long, + /// A 32 bit unsigned integer + UnsignedLong, + /// A 64 bit signed integer + LongLong, + /// A 64 bit unsigned integer + UnsignedLongLong, + /// A 32 bit floating point number + Float, + /// A 64 bit floating point number + Double, + /// A fixed point decimal value + Numeric, + /// A datatype to store a time value + Time, + /// A datatype to store a date value + Date, + /// A datatype containing timestamp values ranging from + /// '1000-01-01 00:00:00' to '9999-12-31 23:59:59'. + DateTime, + /// A datatype containing timestamp values ranging from + /// 1970-01-01 00:00:01' UTC to '2038-01-19 03:14:07' UTC. + Timestamp, + /// A datatype for string values + String, + /// A datatype containing binary large objects + Blob, + /// A value containing a set of bit's + Bit, + /// A user defined set type + Set, + /// A user defined enum type + Enum, +} + +impl Backend for Mysql { + type QueryBuilder = MysqlQueryBuilder; + type BindCollector = RawBytesBindCollector; + type ByteOrder = NativeEndian; +} + +impl<'a> HasRawValue<'a> for Mysql { + type RawValue = MysqlValue<'a>; +} + +impl TypeMetadata for Mysql { + type TypeMetadata = MysqlType; + type MetadataLookup = (); +} + +impl SupportsDefaultKeyword for Mysql {} +impl UsesAnsiSavepointSyntax for Mysql {} diff --git a/collector/benchmarks/diesel/diesel/src/mysql/connection/bind.rs b/collector/benchmarks/diesel/diesel/src/mysql/connection/bind.rs new file mode 100644 index 000000000..c226f82f2 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/mysql/connection/bind.rs @@ -0,0 +1,1438 @@ +use mysqlclient_sys as ffi; +use std::mem; +use std::ops::Index; +use std::os::raw as libc; + +use super::stmt::Statement; +use crate::mysql::connection::stmt::StatementMetadata; +use crate::mysql::types::MYSQL_TIME; +use crate::mysql::{MysqlType, MysqlValue}; +use crate::result::QueryResult; + +pub struct Binds { + data: Vec, +} + +impl Binds { + pub fn from_input_data(input: Iter) -> QueryResult + where + Iter: IntoIterator>)>, + { + let data = input + .into_iter() + .map(BindData::for_input) + .collect::>(); + + Ok(Binds { data }) + } + + pub fn from_output_types(types: Vec>, metadata: &StatementMetadata) -> Self { + let data = metadata + .fields() + .iter() + .zip(types.into_iter().chain(std::iter::repeat(None))) + .map(|(field, tpe)| { + if let Some(tpe) = tpe { + BindData::for_output(tpe.into()) + } else { + BindData::for_output((field.field_type(), field.flags())) + } + }) + .collect(); + + Binds { data } + } + + pub fn with_mysql_binds(&mut self, f: F) -> T + where + F: FnOnce(*mut ffi::MYSQL_BIND) -> T, + { + let mut binds = self + .data + .iter_mut() + .map(|x| unsafe { x.mysql_bind() }) + .collect::>(); + f(binds.as_mut_ptr()) + } + + pub fn populate_dynamic_buffers(&mut self, stmt: &Statement) -> QueryResult<()> { + for (i, data) in self.data.iter_mut().enumerate() { + data.did_numeric_overflow_occur()?; + // This is safe because we are re-binding the invalidated buffers + // at the end of this function + unsafe { + if let Some((mut bind, offset)) = data.bind_for_truncated_data() { + stmt.fetch_column(&mut bind, i, offset)? + } else { + data.update_buffer_length() + } + } + } + + unsafe { self.with_mysql_binds(|bind_ptr| stmt.bind_result(bind_ptr)) } + } + + pub fn update_buffer_lengths(&mut self) { + for data in &mut self.data { + data.update_buffer_length(); + } + } + + pub fn len(&self) -> usize { + self.data.len() + } +} + +impl Index for Binds { + type Output = BindData; + fn index(&self, index: usize) -> &Self::Output { + &self.data[index] + } +} + +bitflags::bitflags! { + pub(crate) struct Flags: u32 { + const NOT_NULL_FLAG = 1; + const PRI_KEY_FAG = 2; + const UNIQUE_KEY_FLAG = 4; + const MULTIPLE_KEY_FLAG = 8; + const BLOB_FLAG = 16; + const UNSIGNED_FLAG = 32; + const ZEROFILL_FLAG = 64; + const BINARY_FLAG = 128; + const ENUM_FLAG = 256; + const AUTO_INCREMENT_FLAG = 512; + const TIMESTAMP_FLAG = 1024; + const SET_FLAG = 2048; + const NO_DEFAULT_VALUE_FLAG = 4096; + const ON_UPDATE_NOW_FLAG = 8192; + const NUM_FLAG = 32768; + const PART_KEY_FLAG = 16384; + const GROUP_FLAG = 32768; + const UNIQUE_FLAG = 65536; + const BINCMP_FLAG = 130_172; + const GET_FIXED_FIELDS_FLAG = (1<<18); + const FIELD_IN_PART_FUNC_FLAG = (1 << 19); + } +} + +impl From for Flags { + fn from(flags: u32) -> Self { + Flags::from_bits(flags).expect( + "We encountered a unknown type flag while parsing \ + Mysql's type information. If you see this error message \ + please open an issue at diesels github page.", + ) + } +} + +#[derive(Debug)] +pub struct BindData { + tpe: ffi::enum_field_types, + bytes: Vec, + length: libc::c_ulong, + flags: Flags, + is_null: ffi::my_bool, + is_truncated: Option, +} + +impl BindData { + fn for_input((tpe, data): (MysqlType, Option>)) -> Self { + let is_null = if data.is_none() { 1 } else { 0 }; + let bytes = data.unwrap_or_default(); + let length = bytes.len() as libc::c_ulong; + let (tpe, flags) = tpe.into(); + BindData { + tpe, + bytes, + length, + is_null, + is_truncated: None, + flags, + } + } + + fn for_output((tpe, flags): (ffi::enum_field_types, Flags)) -> Self { + let bytes = known_buffer_size_for_ffi_type(tpe) + .map(|len| vec![0; len]) + .unwrap_or_default(); + let length = bytes.len() as libc::c_ulong; + + BindData { + tpe, + bytes, + length, + is_null: 0, + is_truncated: Some(0), + flags, + } + } + + fn is_truncated(&self) -> bool { + self.is_truncated.unwrap_or(0) != 0 + } + + fn is_fixed_size_buffer(&self) -> bool { + known_buffer_size_for_ffi_type(self.tpe).is_some() + } + + pub fn value(&'_ self) -> Option> { + if self.is_null() { + None + } else { + let tpe = (self.tpe, self.flags).into(); + Some(MysqlValue::new(&self.bytes, tpe)) + } + } + + pub fn is_null(&self) -> bool { + self.is_null != 0 + } + + fn update_buffer_length(&mut self) { + use std::cmp::min; + + let actual_bytes_in_buffer = min(self.bytes.capacity(), self.length as usize); + unsafe { self.bytes.set_len(actual_bytes_in_buffer) } + } + + unsafe fn mysql_bind(&mut self) -> ffi::MYSQL_BIND { + let mut bind: ffi::MYSQL_BIND = mem::zeroed(); + bind.buffer_type = self.tpe; + bind.buffer = self.bytes.as_mut_ptr() as *mut libc::c_void; + bind.buffer_length = self.bytes.capacity() as libc::c_ulong; + bind.length = &mut self.length; + bind.is_null = &mut self.is_null; + bind.is_unsigned = self.flags.contains(Flags::UNSIGNED_FLAG) as ffi::my_bool; + + if let Some(ref mut is_truncated) = self.is_truncated { + bind.error = is_truncated; + } + + bind + } + + /// Resizes the byte buffer to fit the value of `self.length`, and returns + /// a tuple of a bind pointing at the truncated data, and the offset to use + /// in order to read the truncated data into it. + /// + /// This invalidates the bind previously returned by `mysql_bind`. Calling + /// this function is unsafe unless the binds are immediately rebound. + unsafe fn bind_for_truncated_data(&mut self) -> Option<(ffi::MYSQL_BIND, usize)> { + if self.is_truncated() { + let offset = self.bytes.capacity(); + let truncated_amount = self.length as usize - offset; + + debug_assert!( + truncated_amount > 0, + "output buffers were invalidated \ + without calling `mysql_stmt_bind_result`" + ); + self.bytes.set_len(offset); + self.bytes.reserve(truncated_amount); + self.bytes.set_len(self.length as usize); + + let mut bind = self.mysql_bind(); + bind.buffer = self.bytes[offset..].as_mut_ptr() as *mut libc::c_void; + bind.buffer_length = truncated_amount as libc::c_ulong; + Some((bind, offset)) + } else { + None + } + } + + fn did_numeric_overflow_occur(&self) -> QueryResult<()> { + use crate::result::Error::DeserializationError; + + if self.is_truncated() && self.is_fixed_size_buffer() { + Err(DeserializationError( + "Numeric overflow/underflow occurred".into(), + )) + } else { + Ok(()) + } + } +} + +impl From for (ffi::enum_field_types, Flags) { + fn from(tpe: MysqlType) -> Self { + use self::ffi::enum_field_types::*; + let mut flags = Flags::empty(); + let tpe = match tpe { + MysqlType::Tiny => MYSQL_TYPE_TINY, + MysqlType::Short => MYSQL_TYPE_SHORT, + MysqlType::Long => MYSQL_TYPE_LONG, + MysqlType::LongLong => MYSQL_TYPE_LONGLONG, + MysqlType::Float => MYSQL_TYPE_FLOAT, + MysqlType::Double => MYSQL_TYPE_DOUBLE, + MysqlType::Time => MYSQL_TYPE_TIME, + MysqlType::Date => MYSQL_TYPE_DATE, + MysqlType::DateTime => MYSQL_TYPE_DATETIME, + MysqlType::Timestamp => MYSQL_TYPE_TIMESTAMP, + MysqlType::String => MYSQL_TYPE_STRING, + MysqlType::Blob => MYSQL_TYPE_BLOB, + MysqlType::Numeric => MYSQL_TYPE_NEWDECIMAL, + MysqlType::Bit => MYSQL_TYPE_BIT, + MysqlType::UnsignedTiny => { + flags = Flags::UNSIGNED_FLAG; + MYSQL_TYPE_TINY + } + MysqlType::UnsignedShort => { + flags = Flags::UNSIGNED_FLAG; + MYSQL_TYPE_SHORT + } + MysqlType::UnsignedLong => { + flags = Flags::UNSIGNED_FLAG; + MYSQL_TYPE_LONG + } + MysqlType::UnsignedLongLong => { + flags = Flags::UNSIGNED_FLAG; + MYSQL_TYPE_LONGLONG + } + MysqlType::Set => { + flags = Flags::SET_FLAG; + MYSQL_TYPE_STRING + } + MysqlType::Enum => { + flags = Flags::ENUM_FLAG; + MYSQL_TYPE_STRING + } + }; + (tpe, flags) + } +} + +impl From<(ffi::enum_field_types, Flags)> for MysqlType { + fn from((tpe, flags): (ffi::enum_field_types, Flags)) -> Self { + use self::ffi::enum_field_types::*; + + let is_unsigned = flags.contains(Flags::UNSIGNED_FLAG); + + // https://docs.oracle.com/cd/E17952_01/mysql-8.0-en/c-api-data-structures.html + // https://dev.mysql.com/doc/dev/mysql-server/8.0.12/binary__log__types_8h.html + // https://dev.mysql.com/doc/internals/en/binary-protocol-value.html + // https://mariadb.com/kb/en/packet_bindata/ + match tpe { + MYSQL_TYPE_TINY if is_unsigned => MysqlType::UnsignedTiny, + MYSQL_TYPE_YEAR | MYSQL_TYPE_SHORT if is_unsigned => MysqlType::UnsignedShort, + MYSQL_TYPE_INT24 | MYSQL_TYPE_LONG if is_unsigned => MysqlType::UnsignedLong, + MYSQL_TYPE_LONGLONG if is_unsigned => MysqlType::UnsignedLongLong, + MYSQL_TYPE_TINY => MysqlType::Tiny, + MYSQL_TYPE_SHORT => MysqlType::Short, + MYSQL_TYPE_INT24 | MYSQL_TYPE_LONG => MysqlType::Long, + MYSQL_TYPE_LONGLONG => MysqlType::LongLong, + MYSQL_TYPE_FLOAT => MysqlType::Float, + MYSQL_TYPE_DOUBLE => MysqlType::Double, + MYSQL_TYPE_DECIMAL | MYSQL_TYPE_NEWDECIMAL => MysqlType::Numeric, + MYSQL_TYPE_BIT => MysqlType::Bit, + + MYSQL_TYPE_TIME => MysqlType::Time, + MYSQL_TYPE_DATE => MysqlType::Date, + MYSQL_TYPE_DATETIME => MysqlType::DateTime, + MYSQL_TYPE_TIMESTAMP => MysqlType::Timestamp, + // Treat json as string because even mysql 8.0 + // throws errors sometimes if we use json for json + MYSQL_TYPE_JSON => MysqlType::String, + + // The documentation states that + // MYSQL_TYPE_STRING is used for enums and sets + // but experimentation has shown that + // just any string like type works, so + // better be safe here + MYSQL_TYPE_BLOB + | MYSQL_TYPE_TINY_BLOB + | MYSQL_TYPE_MEDIUM_BLOB + | MYSQL_TYPE_LONG_BLOB + | MYSQL_TYPE_VAR_STRING + | MYSQL_TYPE_STRING + if flags.contains(Flags::ENUM_FLAG) => + { + MysqlType::Enum + } + MYSQL_TYPE_BLOB + | MYSQL_TYPE_TINY_BLOB + | MYSQL_TYPE_MEDIUM_BLOB + | MYSQL_TYPE_LONG_BLOB + | MYSQL_TYPE_VAR_STRING + | MYSQL_TYPE_STRING + if flags.contains(Flags::SET_FLAG) => + { + MysqlType::Set + } + + // "blobs" may contain binary data + // also "strings" can contain binary data + // but all only if the binary flag is set + // (see the check_all_the_types test case) + MYSQL_TYPE_BLOB + | MYSQL_TYPE_TINY_BLOB + | MYSQL_TYPE_MEDIUM_BLOB + | MYSQL_TYPE_LONG_BLOB + | MYSQL_TYPE_VAR_STRING + | MYSQL_TYPE_STRING + if flags.contains(Flags::BINARY_FLAG) => + { + MysqlType::Blob + } + + // If the binary flag is not set consider everything as string + MYSQL_TYPE_BLOB + | MYSQL_TYPE_TINY_BLOB + | MYSQL_TYPE_MEDIUM_BLOB + | MYSQL_TYPE_LONG_BLOB + | MYSQL_TYPE_VAR_STRING + | MYSQL_TYPE_STRING => MysqlType::String, + + // unsigned seems to be set for year in any case + MYSQL_TYPE_YEAR => unreachable!( + "The year type should have set the unsigned flag. If you ever \ + see this error message, something has gone very wrong. Please \ + open an issue at the diesel githup repo in this case" + ), + // Null value + MYSQL_TYPE_NULL => unreachable!( + "We ensure at the call side that we do not hit this type here. \ + If you ever see this error, something has gone very wrong. \ + Please open an issue at the diesel github repo in this case" + ), + // Those exist in libmysqlclient + // but are just not supported + // + MYSQL_TYPE_VARCHAR | MYSQL_TYPE_ENUM | MYSQL_TYPE_SET | MYSQL_TYPE_GEOMETRY => { + unimplemented!( + "Hit a type that should be unsupported in libmysqlclient. If \ + you ever see this error, they probably have added support for \ + one of those types. Please open an issue at the diesel github \ + repo in this case." + ) + } + + MYSQL_TYPE_NEWDATE + | MYSQL_TYPE_TIME2 + | MYSQL_TYPE_DATETIME2 + | MYSQL_TYPE_TIMESTAMP2 => unreachable!( + "The mysql documentation states that this types are \ + only used on server side, so if you see this error \ + something has gone wrong. Please open a issue at \ + the diesel github repo." + ), + } + } +} + +fn known_buffer_size_for_ffi_type(tpe: ffi::enum_field_types) -> Option { + use self::ffi::enum_field_types as t; + use std::mem::size_of; + + match tpe { + t::MYSQL_TYPE_TINY => Some(1), + t::MYSQL_TYPE_YEAR | t::MYSQL_TYPE_SHORT => Some(2), + t::MYSQL_TYPE_INT24 | t::MYSQL_TYPE_LONG | t::MYSQL_TYPE_FLOAT => Some(4), + t::MYSQL_TYPE_LONGLONG | t::MYSQL_TYPE_DOUBLE => Some(8), + t::MYSQL_TYPE_TIME + | t::MYSQL_TYPE_DATE + | t::MYSQL_TYPE_DATETIME + | t::MYSQL_TYPE_TIMESTAMP => Some(size_of::()), + _ => None, + } +} + +#[cfg(test)] +mod tests { + #[cfg(feature = "bigdecimal")] + use bigdecimal::FromPrimitive; + + use super::MysqlValue; + use super::*; + use crate::deserialize::FromSql; + use crate::prelude::*; + use crate::sql_types::*; + + fn to_value( + bind: &BindData, + ) -> Result> + where + T: FromSql + std::fmt::Debug, + { + let meta = (bind.tpe, bind.flags).into(); + dbg!(meta); + let value = MysqlValue::new(&bind.bytes, meta); + dbg!(T::from_sql(value)) + } + + #[cfg(feature = "extras")] + #[test] + fn check_all_the_types() { + let conn = crate::test_helpers::connection(); + + conn.execute("DROP TABLE IF EXISTS all_mysql_types CASCADE") + .unwrap(); + conn.execute( + "CREATE TABLE all_mysql_types ( + tiny_int TINYINT NOT NULL, + small_int SMALLINT NOT NULL, + medium_int MEDIUMINT NOT NULL, + int_col INTEGER NOT NULL, + big_int BIGINT NOT NULL, + unsigned_int INTEGER UNSIGNED NOT NULL, + zero_fill_int INTEGER ZEROFILL NOT NULL, + numeric_col NUMERIC(20,5) NOT NULL, + decimal_col DECIMAL(20,5) NOT NULL, + float_col FLOAT NOT NULL, + double_col DOUBLE NOT NULL, + bit_col BIT(8) NOT NULL, + date_col DATE NOT NULL, + date_time DATETIME NOT NULL, + timestamp_col TIMESTAMP NOT NULL, + time_col TIME NOT NULL, + year_col YEAR NOT NULL, + char_col CHAR(30) NOT NULL, + varchar_col VARCHAR(30) NOT NULL, + binary_col BINARY(30) NOT NULL, + varbinary_col VARBINARY(30) NOT NULL, + blob_col BLOB NOT NULL, + text_col TEXT NOT NULL, + enum_col ENUM('red', 'green', 'blue') NOT NULL, + set_col SET('one', 'two') NOT NULL, + geom GEOMETRY NOT NULL, + point_col POINT NOT NULL, + linestring_col LINESTRING NOT NULL, + polygon_col POLYGON NOT NULL, + multipoint_col MULTIPOINT NOT NULL, + multilinestring_col MULTILINESTRING NOT NULL, + multipolygon_col MULTIPOLYGON NOT NULL, + geometry_collection GEOMETRYCOLLECTION NOT NULL, + json_col JSON NOT NULL + )", + ) + .unwrap(); + conn + .execute( + "INSERT INTO all_mysql_types VALUES ( + 0, -- tiny_int + 1, -- small_int + 2, -- medium_int + 3, -- int_col + -5, -- big_int + 42, -- unsigned_int + 1, -- zero_fill_int + -999.999, -- numeric_col, + 3.14, -- decimal_col, + 1.23, -- float_col + 4.5678, -- double_col + b'10101010', -- bit_col + '1000-01-01', -- date_col + '9999-12-31 12:34:45.012345', -- date_time + '2020-01-01 10:10:10', -- timestamp_col + '23:01:01', -- time_col + 2020, -- year_col + 'abc', -- char_col + 'foo', -- varchar_col + 'a ', -- binary_col + 'a ', -- varbinary_col + 'binary', -- blob_col + 'some text whatever', -- text_col + 'red', -- enum_col + 'one', -- set_col + ST_GeomFromText('POINT(1 1)'), -- geom + ST_PointFromText('POINT(1 1)'), -- point_col + ST_LineStringFromText('LINESTRING(0 0,1 1,2 2)'), -- linestring_col + ST_PolygonFromText('POLYGON((0 0,10 0,10 10,0 10,0 0),(5 5,7 5,7 7,5 7, 5 5))'), -- polygon_col + ST_MultiPointFromText('MULTIPOINT(0 0,10 10,10 20,20 20)'), -- multipoint_col + ST_MultiLineStringFromText('MULTILINESTRING((10 48,10 21,10 0),(16 0,16 23,16 48))'), -- multilinestring_col + ST_MultiPolygonFromText('MULTIPOLYGON(((28 26,28 0,84 0,84 42,28 26),(52 18,66 23,73 9,48 6,52 18)),((59 18,67 18,67 13,59 13,59 18)))'), -- multipolygon_col + ST_GeomCollFromText('GEOMETRYCOLLECTION(POINT(1 1),LINESTRING(0 0,1 1,2 2,3 3,4 4))'), -- geometry_collection + '{\"key1\": \"value1\", \"key2\": \"value2\"}' -- json_col +)", + ) + .unwrap(); + + let mut stmt = conn + .prepare_query(&crate::sql_query( + "SELECT + tiny_int, small_int, medium_int, int_col, + big_int, unsigned_int, zero_fill_int, + numeric_col, decimal_col, float_col, double_col, bit_col, + date_col, date_time, timestamp_col, time_col, year_col, + char_col, varchar_col, binary_col, varbinary_col, blob_col, + text_col, enum_col, set_col, ST_AsText(geom), ST_AsText(point_col), ST_AsText(linestring_col), + ST_AsText(polygon_col), ST_AsText(multipoint_col), ST_AsText(multilinestring_col), + ST_AsText(multipolygon_col), ST_AsText(geometry_collection), json_col + FROM all_mysql_types", + )) + .unwrap(); + + let metadata = stmt.metadata().unwrap(); + let mut output_binds = + Binds::from_output_types(vec![None; metadata.fields().len()], &metadata); + stmt.execute_statement(&mut output_binds).unwrap(); + stmt.populate_row_buffers(&mut output_binds).unwrap(); + + let results: Vec<(BindData, &_)> = output_binds + .data + .into_iter() + .zip(metadata.fields()) + .collect::>(); + + macro_rules! matches { + ($expression:expr, $( $pattern:pat )|+ $( if $guard: expr )?) => { + match $expression { + $( $pattern )|+ $( if $guard )? => true, + _ => false + } + } + } + + let tiny_int_col = &results[0].0; + assert_eq!(tiny_int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_TINY); + assert!(tiny_int_col.flags.contains(Flags::NUM_FLAG)); + assert!(!tiny_int_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert!(matches!(to_value::(tiny_int_col), Ok(0))); + + let small_int_col = &results[1].0; + assert_eq!(small_int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_SHORT); + assert!(small_int_col.flags.contains(Flags::NUM_FLAG)); + assert!(!small_int_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert!(matches!(to_value::(small_int_col), Ok(1))); + + let medium_int_col = &results[2].0; + assert_eq!(medium_int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_INT24); + assert!(medium_int_col.flags.contains(Flags::NUM_FLAG)); + assert!(!medium_int_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert!(matches!(to_value::(medium_int_col), Ok(2))); + + let int_col = &results[3].0; + assert_eq!(int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG); + assert!(int_col.flags.contains(Flags::NUM_FLAG)); + assert!(!int_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert!(matches!(to_value::(int_col), Ok(3))); + + let big_int_col = &results[4].0; + assert_eq!(big_int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONGLONG); + assert!(big_int_col.flags.contains(Flags::NUM_FLAG)); + assert!(!big_int_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert!(matches!(to_value::(big_int_col), Ok(-5))); + + let unsigned_int_col = &results[5].0; + assert_eq!(unsigned_int_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG); + assert!(unsigned_int_col.flags.contains(Flags::NUM_FLAG)); + assert!(unsigned_int_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert!(matches!( + to_value::, u32>(unsigned_int_col), + Ok(42) + )); + + let zero_fill_int_col = &results[6].0; + assert_eq!( + zero_fill_int_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_LONG + ); + assert!(zero_fill_int_col.flags.contains(Flags::NUM_FLAG)); + assert!(zero_fill_int_col.flags.contains(Flags::ZEROFILL_FLAG)); + assert!(matches!(to_value::(zero_fill_int_col), Ok(1))); + + let numeric_col = &results[7].0; + assert_eq!( + numeric_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL + ); + assert!(numeric_col.flags.contains(Flags::NUM_FLAG)); + assert!(!numeric_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert_eq!( + to_value::(numeric_col).unwrap(), + bigdecimal::BigDecimal::from_f32(-999.999).unwrap() + ); + + let decimal_col = &results[8].0; + assert_eq!( + decimal_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_NEWDECIMAL + ); + assert!(decimal_col.flags.contains(Flags::NUM_FLAG)); + assert!(!decimal_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert_eq!( + to_value::(decimal_col).unwrap(), + bigdecimal::BigDecimal::from_f32(3.14).unwrap() + ); + + let float_col = &results[9].0; + assert_eq!(float_col.tpe, ffi::enum_field_types::MYSQL_TYPE_FLOAT); + assert!(float_col.flags.contains(Flags::NUM_FLAG)); + assert!(!float_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert_eq!(to_value::(float_col).unwrap(), 1.23); + + let double_col = &results[10].0; + assert_eq!(double_col.tpe, ffi::enum_field_types::MYSQL_TYPE_DOUBLE); + assert!(double_col.flags.contains(Flags::NUM_FLAG)); + assert!(!double_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert_eq!(to_value::(double_col).unwrap(), 4.5678); + + let bit_col = &results[11].0; + assert_eq!(bit_col.tpe, ffi::enum_field_types::MYSQL_TYPE_BIT); + assert!(!bit_col.flags.contains(Flags::NUM_FLAG)); + assert!(bit_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert!(!bit_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::>(bit_col).unwrap(), vec![170]); + + let date_col = &results[12].0; + assert_eq!(date_col.tpe, ffi::enum_field_types::MYSQL_TYPE_DATE); + assert!(!date_col.flags.contains(Flags::NUM_FLAG)); + assert_eq!( + to_value::(date_col).unwrap(), + chrono::NaiveDate::from_ymd_opt(1000, 1, 1).unwrap(), + ); + + let date_time_col = &results[13].0; + assert_eq!( + date_time_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_DATETIME + ); + assert!(!date_time_col.flags.contains(Flags::NUM_FLAG)); + assert_eq!( + to_value::(date_time_col).unwrap(), + chrono::NaiveDateTime::parse_from_str("9999-12-31 12:34:45", "%Y-%m-%d %H:%M:%S") + .unwrap() + ); + + let timestamp_col = &results[14].0; + assert_eq!( + timestamp_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_TIMESTAMP + ); + assert!(!timestamp_col.flags.contains(Flags::NUM_FLAG)); + assert_eq!( + to_value::(timestamp_col).unwrap(), + chrono::NaiveDateTime::parse_from_str("2020-01-01 10:10:10", "%Y-%m-%d %H:%M:%S") + .unwrap() + ); + + let time_col = &results[15].0; + assert_eq!(time_col.tpe, ffi::enum_field_types::MYSQL_TYPE_TIME); + assert!(!time_col.flags.contains(Flags::NUM_FLAG)); + assert_eq!( + to_value::(time_col).unwrap(), + chrono::NaiveTime::from_hms(23, 01, 01) + ); + + let year_col = &results[16].0; + assert_eq!(year_col.tpe, ffi::enum_field_types::MYSQL_TYPE_YEAR); + assert!(year_col.flags.contains(Flags::NUM_FLAG)); + assert!(year_col.flags.contains(Flags::UNSIGNED_FLAG)); + assert!(matches!(to_value::(year_col), Ok(2020))); + + let char_col = &results[17].0; + assert_eq!(char_col.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING); + assert!(!char_col.flags.contains(Flags::NUM_FLAG)); + assert!(!char_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!char_col.flags.contains(Flags::SET_FLAG)); + assert!(!char_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!char_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(char_col).unwrap(), "abc"); + + let varchar_col = &results[18].0; + assert_eq!( + varchar_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_VAR_STRING + ); + assert!(!varchar_col.flags.contains(Flags::NUM_FLAG)); + assert!(!varchar_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!varchar_col.flags.contains(Flags::SET_FLAG)); + assert!(!varchar_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!varchar_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(varchar_col).unwrap(), "foo"); + + let binary_col = &results[19].0; + assert_eq!(binary_col.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING); + assert!(!binary_col.flags.contains(Flags::NUM_FLAG)); + assert!(!binary_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!binary_col.flags.contains(Flags::SET_FLAG)); + assert!(!binary_col.flags.contains(Flags::ENUM_FLAG)); + assert!(binary_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::>(binary_col).unwrap(), + b"a \0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" + ); + + let varbinary_col = &results[20].0; + assert_eq!( + varbinary_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_VAR_STRING + ); + assert!(!varbinary_col.flags.contains(Flags::NUM_FLAG)); + assert!(!varbinary_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!varbinary_col.flags.contains(Flags::SET_FLAG)); + assert!(!varbinary_col.flags.contains(Flags::ENUM_FLAG)); + assert!(varbinary_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::>(varbinary_col).unwrap(), b"a "); + + let blob_col = &results[21].0; + assert_eq!(blob_col.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!blob_col.flags.contains(Flags::NUM_FLAG)); + assert!(blob_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!blob_col.flags.contains(Flags::SET_FLAG)); + assert!(!blob_col.flags.contains(Flags::ENUM_FLAG)); + assert!(blob_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::>(blob_col).unwrap(), b"binary"); + + let text_col = &results[22].0; + assert_eq!(text_col.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!text_col.flags.contains(Flags::NUM_FLAG)); + assert!(text_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!text_col.flags.contains(Flags::SET_FLAG)); + assert!(!text_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!text_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(text_col).unwrap(), + "some text whatever" + ); + + let enum_col = &results[23].0; + assert_eq!(enum_col.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING); + assert!(!enum_col.flags.contains(Flags::NUM_FLAG)); + assert!(!enum_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!enum_col.flags.contains(Flags::SET_FLAG)); + assert!(enum_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!enum_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(enum_col).unwrap(), "red"); + + let set_col = &results[24].0; + assert_eq!(set_col.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING); + assert!(!set_col.flags.contains(Flags::NUM_FLAG)); + assert!(!set_col.flags.contains(Flags::BLOB_FLAG)); + assert!(set_col.flags.contains(Flags::SET_FLAG)); + assert!(!set_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!set_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(set_col).unwrap(), "one"); + + let geom = &results[25].0; + assert_eq!(geom.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB); + assert!(!geom.flags.contains(Flags::NUM_FLAG)); + assert!(!geom.flags.contains(Flags::BLOB_FLAG)); + assert!(!geom.flags.contains(Flags::SET_FLAG)); + assert!(!geom.flags.contains(Flags::ENUM_FLAG)); + assert!(!geom.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(geom).unwrap(), "POINT(1 1)"); + + let point_col = &results[26].0; + assert_eq!(point_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB); + assert!(!point_col.flags.contains(Flags::NUM_FLAG)); + assert!(!point_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!point_col.flags.contains(Flags::SET_FLAG)); + assert!(!point_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!point_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(point_col).unwrap(), "POINT(1 1)"); + + let linestring_col = &results[27].0; + assert_eq!( + linestring_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB + ); + assert!(!linestring_col.flags.contains(Flags::NUM_FLAG)); + assert!(!linestring_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!linestring_col.flags.contains(Flags::SET_FLAG)); + assert!(!linestring_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!linestring_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(linestring_col).unwrap(), + "LINESTRING(0 0,1 1,2 2)" + ); + + let polygon_col = &results[28].0; + assert_eq!(polygon_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB); + assert!(!polygon_col.flags.contains(Flags::NUM_FLAG)); + assert!(!polygon_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!polygon_col.flags.contains(Flags::SET_FLAG)); + assert!(!polygon_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!polygon_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(polygon_col).unwrap(), + "POLYGON((0 0,10 0,10 10,0 10,0 0),(5 5,7 5,7 7,5 7,5 5))" + ); + + let multipoint_col = &results[29].0; + assert_eq!( + multipoint_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB + ); + assert!(!multipoint_col.flags.contains(Flags::NUM_FLAG)); + assert!(!multipoint_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!multipoint_col.flags.contains(Flags::SET_FLAG)); + assert!(!multipoint_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!multipoint_col.flags.contains(Flags::BINARY_FLAG)); + // older mysql and mariadb versions get back another encoding here + // we test for both as there seems to be no clear pattern when one or + // the other is returned + let multipoint_res = to_value::(multipoint_col).unwrap(); + assert!( + multipoint_res == "MULTIPOINT((0 0),(10 10),(10 20),(20 20))" + || multipoint_res == "MULTIPOINT(0 0,10 10,10 20,20 20)" + ); + + let multilinestring_col = &results[30].0; + assert_eq!( + multilinestring_col.tpe, + ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB + ); + assert!(!multilinestring_col.flags.contains(Flags::NUM_FLAG)); + assert!(!multilinestring_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!multilinestring_col.flags.contains(Flags::SET_FLAG)); + assert!(!multilinestring_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!multilinestring_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(multilinestring_col).unwrap(), + "MULTILINESTRING((10 48,10 21,10 0),(16 0,16 23,16 48))" + ); + + let polygon_col = &results[31].0; + assert_eq!(polygon_col.tpe, ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB); + assert!(!polygon_col.flags.contains(Flags::NUM_FLAG)); + assert!(!polygon_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!polygon_col.flags.contains(Flags::SET_FLAG)); + assert!(!polygon_col.flags.contains(Flags::ENUM_FLAG)); + assert!(!polygon_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(polygon_col).unwrap(), + "MULTIPOLYGON(((28 26,28 0,84 0,84 42,28 26),(52 18,66 23,73 9,48 6,52 18)),((59 18,67 18,67 13,59 13,59 18)))" + ); + + let geometry_collection = &results[32].0; + assert_eq!( + geometry_collection.tpe, + ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB + ); + assert!(!geometry_collection.flags.contains(Flags::NUM_FLAG)); + assert!(!geometry_collection.flags.contains(Flags::BLOB_FLAG)); + assert!(!geometry_collection.flags.contains(Flags::SET_FLAG)); + assert!(!geometry_collection.flags.contains(Flags::ENUM_FLAG)); + assert!(!geometry_collection.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(geometry_collection).unwrap(), + "GEOMETRYCOLLECTION(POINT(1 1),LINESTRING(0 0,1 1,2 2,3 3,4 4))" + ); + + let json_col = &results[33].0; + // mariadb >= 10.2 and mysql >=8.0 are supporting a json type + // from those mariadb >= 10.3 and mysql >= 8.0 are reporting + // json here, so we assert that we get back json + // mariadb 10.5 returns again blob + assert!( + json_col.tpe == ffi::enum_field_types::MYSQL_TYPE_JSON + || json_col.tpe == ffi::enum_field_types::MYSQL_TYPE_BLOB + ); + assert!(!json_col.flags.contains(Flags::NUM_FLAG)); + assert!(json_col.flags.contains(Flags::BLOB_FLAG)); + assert!(!json_col.flags.contains(Flags::SET_FLAG)); + assert!(!json_col.flags.contains(Flags::ENUM_FLAG)); + assert!(json_col.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(json_col).unwrap(), + "{\"key1\": \"value1\", \"key2\": \"value2\"}" + ); + } + + fn query_single_table( + query: &'static str, + conn: &MysqlConnection, + bind_tpe: impl Into<(ffi::enum_field_types, Flags)>, + ) -> BindData { + let mut stmt: Statement = conn.raw_connection.prepare(query).unwrap(); + + let bind = BindData::for_output(bind_tpe.into()); + + let mut binds = Binds { data: vec![bind] }; + + stmt.execute_statement(&mut binds).unwrap(); + stmt.populate_row_buffers(&mut binds).unwrap(); + + binds.data.remove(0) + } + + fn input_bind( + query: &'static str, + conn: &MysqlConnection, + id: i32, + (field, tpe): (Vec, impl Into<(ffi::enum_field_types, Flags)>), + ) { + let mut stmt = conn.raw_connection.prepare(query).unwrap(); + let length = field.len() as _; + let (tpe, flags) = tpe.into(); + + let field_bind = BindData { + tpe, + bytes: field, + length, + flags, + is_null: 0, + is_truncated: None, + }; + + let bytes = id.to_be_bytes().to_vec(); + let length = bytes.len() as _; + + let id_bind = BindData { + tpe: ffi::enum_field_types::MYSQL_TYPE_LONG, + bytes, + length, + flags: Flags::empty(), + is_null: 0, + is_truncated: None, + }; + + let binds = Binds { + data: vec![id_bind, field_bind], + }; + stmt.input_bind(binds).unwrap(); + stmt.did_an_error_occur().unwrap(); + unsafe { + stmt.execute().unwrap(); + } + } + + #[test] + fn check_json_bind() { + let conn: MysqlConnection = crate::test_helpers::connection(); + + table! { + json_test { + id -> Integer, + json_field -> Text, + } + } + + conn.execute("DROP TABLE IF EXISTS json_test CASCADE") + .unwrap(); + + conn.execute("CREATE TABLE json_test(id INTEGER PRIMARY KEY, json_field JSON NOT NULL)") + .unwrap(); + + conn.execute("INSERT INTO json_test(id, json_field) VALUES (1, '{\"key1\": \"value1\", \"key2\": \"value2\"}')").unwrap(); + + let json_col_as_json = query_single_table( + "SELECT json_field FROM json_test", + &conn, + (ffi::enum_field_types::MYSQL_TYPE_JSON, Flags::empty()), + ); + + assert_eq!(json_col_as_json.tpe, ffi::enum_field_types::MYSQL_TYPE_JSON); + assert!(!json_col_as_json.flags.contains(Flags::NUM_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::BLOB_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::SET_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::ENUM_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(&json_col_as_json).unwrap(), + "{\"key1\": \"value1\", \"key2\": \"value2\"}" + ); + + let json_col_as_text = query_single_table( + "SELECT json_field FROM json_test", + &conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()), + ); + + assert_eq!(json_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!json_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(&json_col_as_text).unwrap(), + "{\"key1\": \"value1\", \"key2\": \"value2\"}" + ); + assert_eq!(json_col_as_json.bytes, json_col_as_text.bytes); + + conn.execute("DELETE FROM json_test").unwrap(); + + input_bind( + "INSERT INTO json_test(id, json_field) VALUES (?, ?)", + &conn, + 41, + ( + b"{\"abc\": 42}".to_vec(), + MysqlType::String, + // (ffi::enum_field_types::MYSQL_TYPE_JSON, Flags::empty()), + ), + ); + + let json_col_as_json = query_single_table( + "SELECT json_field FROM json_test", + &conn, + (ffi::enum_field_types::MYSQL_TYPE_JSON, Flags::empty()), + ); + + assert_eq!(json_col_as_json.tpe, ffi::enum_field_types::MYSQL_TYPE_JSON); + assert!(!json_col_as_json.flags.contains(Flags::NUM_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::BLOB_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::SET_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::ENUM_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(&json_col_as_json).unwrap(), + "{\"abc\": 42}" + ); + + let json_col_as_text = query_single_table( + "SELECT json_field FROM json_test", + &conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()), + ); + + assert_eq!(json_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!json_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(&json_col_as_text).unwrap(), + "{\"abc\": 42}" + ); + assert_eq!(json_col_as_json.bytes, json_col_as_text.bytes); + + conn.execute("DELETE FROM json_test").unwrap(); + + input_bind( + "INSERT INTO json_test(id, json_field) VALUES (?, ?)", + &conn, + 41, + (b"{\"abca\": 42}".to_vec(), MysqlType::String), + ); + + let json_col_as_json = query_single_table( + "SELECT json_field FROM json_test", + &conn, + (ffi::enum_field_types::MYSQL_TYPE_JSON, Flags::empty()), + ); + + assert_eq!(json_col_as_json.tpe, ffi::enum_field_types::MYSQL_TYPE_JSON); + assert!(!json_col_as_json.flags.contains(Flags::NUM_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::BLOB_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::SET_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::ENUM_FLAG)); + assert!(!json_col_as_json.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(&json_col_as_json).unwrap(), + "{\"abca\": 42}" + ); + + let json_col_as_text = query_single_table( + "SELECT json_field FROM json_test", + &conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()), + ); + + assert_eq!(json_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!json_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!json_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(&json_col_as_text).unwrap(), + "{\"abca\": 42}" + ); + assert_eq!(json_col_as_json.bytes, json_col_as_text.bytes); + } + + #[test] + fn check_enum_bind() { + let conn: MysqlConnection = crate::test_helpers::connection(); + + conn.execute("DROP TABLE IF EXISTS enum_test CASCADE") + .unwrap(); + + conn.execute("CREATE TABLE enum_test(id INTEGER PRIMARY KEY, enum_field ENUM('red', 'green', 'blue') NOT NULL)") + .unwrap(); + + conn.execute("INSERT INTO enum_test(id, enum_field) VALUES (1, 'green')") + .unwrap(); + + let enum_col_as_enum: BindData = + query_single_table("SELECT enum_field FROM enum_test", &conn, MysqlType::Enum); + + assert_eq!( + enum_col_as_enum.tpe, + ffi::enum_field_types::MYSQL_TYPE_STRING + ); + assert!(!enum_col_as_enum.flags.contains(Flags::NUM_FLAG)); + assert!(!enum_col_as_enum.flags.contains(Flags::BLOB_FLAG)); + assert!(!enum_col_as_enum.flags.contains(Flags::SET_FLAG)); + assert!(enum_col_as_enum.flags.contains(Flags::ENUM_FLAG)); + assert!(!enum_col_as_enum.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(&enum_col_as_enum).unwrap(), + "green" + ); + + for tpe in &[ + ffi::enum_field_types::MYSQL_TYPE_BLOB, + ffi::enum_field_types::MYSQL_TYPE_VAR_STRING, + ffi::enum_field_types::MYSQL_TYPE_TINY_BLOB, + ffi::enum_field_types::MYSQL_TYPE_MEDIUM_BLOB, + ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB, + ] { + let enum_col_as_text = query_single_table( + "SELECT enum_field FROM enum_test", + &conn, + (*tpe, Flags::ENUM_FLAG), + ); + + assert_eq!(enum_col_as_text.tpe, *tpe); + assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(enum_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(&enum_col_as_text).unwrap(), + "green" + ); + assert_eq!(enum_col_as_enum.bytes, enum_col_as_text.bytes); + } + + let enum_col_as_text = query_single_table( + "SELECT enum_field FROM enum_test", + &conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()), + ); + + assert_eq!(enum_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!( + to_value::(&enum_col_as_text).unwrap(), + "green" + ); + assert_eq!(enum_col_as_enum.bytes, enum_col_as_text.bytes); + + conn.execute("DELETE FROM enum_test").unwrap(); + + input_bind( + "INSERT INTO enum_test(id, enum_field) VALUES (?, ?)", + &conn, + 41, + (b"blue".to_vec(), MysqlType::Enum), + ); + + let enum_col_as_enum = + query_single_table("SELECT enum_field FROM enum_test", &conn, MysqlType::Enum); + + assert_eq!( + enum_col_as_enum.tpe, + ffi::enum_field_types::MYSQL_TYPE_STRING + ); + assert!(!enum_col_as_enum.flags.contains(Flags::NUM_FLAG)); + assert!(!enum_col_as_enum.flags.contains(Flags::BLOB_FLAG)); + assert!(!enum_col_as_enum.flags.contains(Flags::SET_FLAG)); + assert!(enum_col_as_enum.flags.contains(Flags::ENUM_FLAG)); + assert!(!enum_col_as_enum.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&enum_col_as_enum).unwrap(), "blue"); + + let enum_col_as_text = query_single_table( + "SELECT enum_field FROM enum_test", + &conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::ENUM_FLAG), + ); + + assert_eq!(enum_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(enum_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&enum_col_as_text).unwrap(), "blue"); + assert_eq!(enum_col_as_enum.bytes, enum_col_as_text.bytes); + + let enum_col_as_text = query_single_table( + "SELECT enum_field FROM enum_test", + &conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::ENUM_FLAG), + ); + + assert_eq!(enum_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(enum_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&enum_col_as_text).unwrap(), "blue"); + assert_eq!(enum_col_as_enum.bytes, enum_col_as_text.bytes); + + conn.execute("DELETE FROM enum_test").unwrap(); + + input_bind( + "INSERT INTO enum_test(id, enum_field) VALUES (?, ?)", + &conn, + 41, + ( + b"red".to_vec(), + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::ENUM_FLAG), + ), + ); + + let enum_col_as_enum = + query_single_table("SELECT enum_field FROM enum_test", &conn, MysqlType::Enum); + + assert_eq!( + enum_col_as_enum.tpe, + ffi::enum_field_types::MYSQL_TYPE_STRING + ); + assert!(!enum_col_as_enum.flags.contains(Flags::NUM_FLAG)); + assert!(!enum_col_as_enum.flags.contains(Flags::BLOB_FLAG)); + assert!(!enum_col_as_enum.flags.contains(Flags::SET_FLAG)); + assert!(enum_col_as_enum.flags.contains(Flags::ENUM_FLAG)); + assert!(!enum_col_as_enum.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&enum_col_as_enum).unwrap(), "red"); + + let enum_col_as_text = query_single_table( + "SELECT enum_field FROM enum_test", + &conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::ENUM_FLAG), + ); + + assert_eq!(enum_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!enum_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(enum_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!enum_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&enum_col_as_text).unwrap(), "red"); + assert_eq!(enum_col_as_enum.bytes, enum_col_as_text.bytes); + } + + #[test] + fn check_set_bind() { + let conn: MysqlConnection = crate::test_helpers::connection(); + + conn.execute("DROP TABLE IF EXISTS set_test CASCADE") + .unwrap(); + + conn.execute("CREATE TABLE set_test(id INTEGER PRIMARY KEY, set_field SET('red', 'green', 'blue') NOT NULL)") + .unwrap(); + + conn.execute("INSERT INTO set_test(id, set_field) VALUES (1, 'green')") + .unwrap(); + + let set_col_as_set: BindData = + query_single_table("SELECT set_field FROM set_test", &conn, MysqlType::Set); + + assert_eq!(set_col_as_set.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING); + assert!(!set_col_as_set.flags.contains(Flags::NUM_FLAG)); + assert!(!set_col_as_set.flags.contains(Flags::BLOB_FLAG)); + assert!(set_col_as_set.flags.contains(Flags::SET_FLAG)); + assert!(!set_col_as_set.flags.contains(Flags::ENUM_FLAG)); + assert!(!set_col_as_set.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&set_col_as_set).unwrap(), "green"); + + for tpe in &[ + ffi::enum_field_types::MYSQL_TYPE_BLOB, + ffi::enum_field_types::MYSQL_TYPE_VAR_STRING, + ffi::enum_field_types::MYSQL_TYPE_TINY_BLOB, + ffi::enum_field_types::MYSQL_TYPE_MEDIUM_BLOB, + ffi::enum_field_types::MYSQL_TYPE_LONG_BLOB, + ] { + let set_col_as_text = query_single_table( + "SELECT set_field FROM set_test", + &conn, + (*tpe, Flags::SET_FLAG), + ); + + assert_eq!(set_col_as_text.tpe, *tpe); + assert!(!set_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(set_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&set_col_as_text).unwrap(), "green"); + assert_eq!(set_col_as_set.bytes, set_col_as_text.bytes); + } + let set_col_as_text = query_single_table( + "SELECT set_field FROM set_test", + &conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::empty()), + ); + + assert_eq!(set_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!set_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&set_col_as_text).unwrap(), "green"); + assert_eq!(set_col_as_set.bytes, set_col_as_text.bytes); + + conn.execute("DELETE FROM set_test").unwrap(); + + input_bind( + "INSERT INTO set_test(id, set_field) VALUES (?, ?)", + &conn, + 41, + (b"blue".to_vec(), MysqlType::Set), + ); + + let set_col_as_set = + query_single_table("SELECT set_field FROM set_test", &conn, MysqlType::Set); + + assert_eq!(set_col_as_set.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING); + assert!(!set_col_as_set.flags.contains(Flags::NUM_FLAG)); + assert!(!set_col_as_set.flags.contains(Flags::BLOB_FLAG)); + assert!(set_col_as_set.flags.contains(Flags::SET_FLAG)); + assert!(!set_col_as_set.flags.contains(Flags::ENUM_FLAG)); + assert!(!set_col_as_set.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&set_col_as_set).unwrap(), "blue"); + + let set_col_as_text = query_single_table( + "SELECT set_field FROM set_test", + &conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::SET_FLAG), + ); + + assert_eq!(set_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!set_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(set_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&set_col_as_text).unwrap(), "blue"); + assert_eq!(set_col_as_set.bytes, set_col_as_text.bytes); + + conn.execute("DELETE FROM set_test").unwrap(); + + input_bind( + "INSERT INTO set_test(id, set_field) VALUES (?, ?)", + &conn, + 41, + (b"red".to_vec(), MysqlType::String), + ); + + let set_col_as_set = + query_single_table("SELECT set_field FROM set_test", &conn, MysqlType::Set); + + assert_eq!(set_col_as_set.tpe, ffi::enum_field_types::MYSQL_TYPE_STRING); + assert!(!set_col_as_set.flags.contains(Flags::NUM_FLAG)); + assert!(!set_col_as_set.flags.contains(Flags::BLOB_FLAG)); + assert!(set_col_as_set.flags.contains(Flags::SET_FLAG)); + assert!(!set_col_as_set.flags.contains(Flags::ENUM_FLAG)); + assert!(!set_col_as_set.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&set_col_as_set).unwrap(), "red"); + + let set_col_as_text = query_single_table( + "SELECT set_field FROM set_test", + &conn, + (ffi::enum_field_types::MYSQL_TYPE_BLOB, Flags::SET_FLAG), + ); + + assert_eq!(set_col_as_text.tpe, ffi::enum_field_types::MYSQL_TYPE_BLOB); + assert!(!set_col_as_text.flags.contains(Flags::NUM_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::BLOB_FLAG)); + assert!(set_col_as_text.flags.contains(Flags::SET_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::ENUM_FLAG)); + assert!(!set_col_as_text.flags.contains(Flags::BINARY_FLAG)); + assert_eq!(to_value::(&set_col_as_text).unwrap(), "red"); + assert_eq!(set_col_as_set.bytes, set_col_as_text.bytes); + } +} diff --git a/collector/benchmarks/diesel/diesel/src/mysql/connection/mod.rs b/collector/benchmarks/diesel/diesel/src/mysql/connection/mod.rs new file mode 100644 index 000000000..e9aa3689d --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/mysql/connection/mod.rs @@ -0,0 +1,162 @@ +mod bind; +mod raw; +mod stmt; +mod url; + +use self::raw::RawConnection; +use self::stmt::Statement; +use self::url::ConnectionOptions; +use super::backend::Mysql; +use crate::connection::*; +use crate::deserialize::FromSqlRow; +use crate::expression::QueryMetadata; +use crate::query_builder::bind_collector::RawBytesBindCollector; +use crate::query_builder::*; +use crate::result::*; + +#[allow(missing_debug_implementations, missing_copy_implementations)] +/// A connection to a MySQL database. Connection URLs should be in the form +/// `mysql://[user[:password]@]host/database_name` +pub struct MysqlConnection { + raw_connection: RawConnection, + transaction_manager: AnsiTransactionManager, + statement_cache: StatementCache, +} + +unsafe impl Send for MysqlConnection {} + +impl SimpleConnection for MysqlConnection { + fn batch_execute(&self, query: &str) -> QueryResult<()> { + self.raw_connection + .enable_multi_statements(|| self.raw_connection.execute(query)) + } +} + +impl Connection for MysqlConnection { + type Backend = Mysql; + type TransactionManager = AnsiTransactionManager; + + fn establish(database_url: &str) -> ConnectionResult { + use crate::result::ConnectionError::CouldntSetupConfiguration; + + let raw_connection = RawConnection::new(); + let connection_options = ConnectionOptions::parse(database_url)?; + raw_connection.connect(&connection_options)?; + let conn = MysqlConnection { + raw_connection: raw_connection, + transaction_manager: AnsiTransactionManager::new(), + statement_cache: StatementCache::new(), + }; + conn.set_config_options() + .map_err(CouldntSetupConfiguration)?; + Ok(conn) + } + + #[doc(hidden)] + fn execute(&self, query: &str) -> QueryResult { + self.raw_connection + .execute(query) + .map(|_| self.raw_connection.affected_rows()) + } + + #[doc(hidden)] + fn load(&self, source: T) -> QueryResult> + where + T: AsQuery, + T::Query: QueryFragment + QueryId, + U: FromSqlRow, + Self::Backend: QueryMetadata, + { + use crate::result::Error::DeserializationError; + + let mut stmt = self.prepare_query(&source.as_query())?; + let mut metadata = Vec::new(); + Mysql::row_metadata(&(), &mut metadata); + let results = unsafe { stmt.results(metadata)? }; + results.map(|row| U::build_from_row(&row).map_err(DeserializationError)) + } + + #[doc(hidden)] + fn execute_returning_count(&self, source: &T) -> QueryResult + where + T: QueryFragment + QueryId, + { + let stmt = self.prepare_query(source)?; + unsafe { + stmt.execute()?; + } + Ok(stmt.affected_rows()) + } + + #[doc(hidden)] + fn transaction_manager(&self) -> &Self::TransactionManager { + &self.transaction_manager + } +} + +impl MysqlConnection { + fn prepare_query(&self, source: &T) -> QueryResult> + where + T: QueryFragment + QueryId, + { + let mut stmt = self + .statement_cache + .cached_statement(source, &[], |sql| self.raw_connection.prepare(sql))?; + let mut bind_collector = RawBytesBindCollector::new(); + source.collect_binds(&mut bind_collector, &())?; + let binds = bind_collector + .metadata + .into_iter() + .zip(bind_collector.binds); + stmt.bind(binds)?; + Ok(stmt) + } + + fn set_config_options(&self) -> QueryResult<()> { + self.execute("SET sql_mode=(SELECT CONCAT(@@sql_mode, ',PIPES_AS_CONCAT'))")?; + self.execute("SET time_zone = '+00:00';")?; + self.execute("SET character_set_client = 'utf8mb4'")?; + self.execute("SET character_set_connection = 'utf8mb4'")?; + self.execute("SET character_set_results = 'utf8mb4'")?; + Ok(()) + } +} + +#[cfg(test)] +mod tests { + extern crate dotenv; + + use super::*; + use std::env; + + fn connection() -> MysqlConnection { + let _ = dotenv::dotenv(); + let database_url = env::var("MYSQL_UNIT_TEST_DATABASE_URL") + .or_else(|_| env::var("MYSQL_DATABASE_URL")) + .or_else(|_| env::var("DATABASE_URL")) + .expect("DATABASE_URL must be set in order to run unit tests"); + MysqlConnection::establish(&database_url).unwrap() + } + + #[test] + fn batch_execute_handles_single_queries_with_results() { + let connection = connection(); + assert!(connection.batch_execute("SELECT 1").is_ok()); + assert!(connection.batch_execute("SELECT 1").is_ok()); + } + + #[test] + fn batch_execute_handles_multi_queries_with_results() { + let connection = connection(); + let query = "SELECT 1; SELECT 2; SELECT 3;"; + assert!(connection.batch_execute(query).is_ok()); + assert!(connection.batch_execute(query).is_ok()); + } + + #[test] + fn execute_handles_queries_which_return_results() { + let connection = connection(); + assert!(connection.execute("SELECT 1").is_ok()); + assert!(connection.execute("SELECT 1").is_ok()); + } +} diff --git a/collector/benchmarks/diesel/diesel/src/mysql/connection/raw.rs b/collector/benchmarks/diesel/diesel/src/mysql/connection/raw.rs new file mode 100644 index 000000000..be771cef2 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/mysql/connection/raw.rs @@ -0,0 +1,218 @@ +extern crate mysqlclient_sys as ffi; + +use std::ffi::CStr; +use std::os::raw as libc; +use std::ptr::{self, NonNull}; +use std::sync::Once; + +use super::stmt::Statement; +use super::url::ConnectionOptions; +use crate::result::{ConnectionError, ConnectionResult, QueryResult}; + +pub struct RawConnection(NonNull); + +impl RawConnection { + pub fn new() -> Self { + perform_thread_unsafe_library_initialization(); + let raw_connection = unsafe { ffi::mysql_init(ptr::null_mut()) }; + // We're trusting https://dev.mysql.com/doc/refman/5.7/en/mysql-init.html + // that null return always means OOM + let raw_connection = + NonNull::new(raw_connection).expect("Insufficient memory to allocate connection"); + let result = RawConnection(raw_connection); + + // This is only non-zero for unrecognized options, which should never happen. + let charset_result = unsafe { + ffi::mysql_options( + result.0.as_ptr(), + ffi::mysql_option::MYSQL_SET_CHARSET_NAME, + b"utf8mb4\0".as_ptr() as *const libc::c_void, + ) + }; + assert_eq!( + 0, charset_result, + "MYSQL_SET_CHARSET_NAME was not \ + recognized as an option by MySQL. This should never \ + happen." + ); + + result + } + + pub fn connect(&self, connection_options: &ConnectionOptions) -> ConnectionResult<()> { + let host = connection_options.host(); + let user = connection_options.user(); + let password = connection_options.password(); + let database = connection_options.database(); + let port = connection_options.port(); + let unix_socket = connection_options.unix_socket(); + + unsafe { + // Make sure you don't use the fake one! + ffi::mysql_real_connect( + self.0.as_ptr(), + host.map(CStr::as_ptr).unwrap_or_else(|| ptr::null_mut()), + user.as_ptr(), + password + .map(CStr::as_ptr) + .unwrap_or_else(|| ptr::null_mut()), + database + .map(CStr::as_ptr) + .unwrap_or_else(|| ptr::null_mut()), + u32::from(port.unwrap_or(0)), + unix_socket + .map(CStr::as_ptr) + .unwrap_or_else(|| ptr::null_mut()), + 0, + ) + }; + + let last_error_message = self.last_error_message(); + if last_error_message.is_empty() { + Ok(()) + } else { + Err(ConnectionError::BadConnection(last_error_message)) + } + } + + pub fn last_error_message(&self) -> String { + unsafe { CStr::from_ptr(ffi::mysql_error(self.0.as_ptr())) } + .to_string_lossy() + .into_owned() + } + + pub fn execute(&self, query: &str) -> QueryResult<()> { + unsafe { + // Make sure you don't use the fake one! + ffi::mysql_real_query( + self.0.as_ptr(), + query.as_ptr() as *const libc::c_char, + query.len() as libc::c_ulong, + ); + } + self.did_an_error_occur()?; + self.flush_pending_results()?; + Ok(()) + } + + pub fn enable_multi_statements(&self, f: F) -> QueryResult + where + F: FnOnce() -> QueryResult, + { + unsafe { + ffi::mysql_set_server_option( + self.0.as_ptr(), + ffi::enum_mysql_set_option::MYSQL_OPTION_MULTI_STATEMENTS_ON, + ); + } + self.did_an_error_occur()?; + + let result = f(); + + unsafe { + ffi::mysql_set_server_option( + self.0.as_ptr(), + ffi::enum_mysql_set_option::MYSQL_OPTION_MULTI_STATEMENTS_OFF, + ); + } + self.did_an_error_occur()?; + + result + } + + pub fn affected_rows(&self) -> usize { + let affected_rows = unsafe { ffi::mysql_affected_rows(self.0.as_ptr()) }; + affected_rows as usize + } + + pub fn prepare(&self, query: &str) -> QueryResult { + let stmt = unsafe { ffi::mysql_stmt_init(self.0.as_ptr()) }; + // It is documented that the only reason `mysql_stmt_init` will fail + // is because of OOM. + // https://dev.mysql.com/doc/refman/5.7/en/mysql-stmt-init.html + let stmt = NonNull::new(stmt).expect("Out of memory creating prepared statement"); + let stmt = Statement::new(stmt); + stmt.prepare(query)?; + Ok(stmt) + } + + fn did_an_error_occur(&self) -> QueryResult<()> { + use crate::result::DatabaseErrorKind; + use crate::result::Error::DatabaseError; + + let error_message = self.last_error_message(); + if error_message.is_empty() { + Ok(()) + } else { + Err(DatabaseError( + DatabaseErrorKind::__Unknown, + Box::new(error_message), + )) + } + } + + fn flush_pending_results(&self) -> QueryResult<()> { + // We may have a result to process before advancing + self.consume_current_result()?; + while self.more_results() { + self.next_result()?; + self.consume_current_result()?; + } + Ok(()) + } + + fn consume_current_result(&self) -> QueryResult<()> { + unsafe { + let res = ffi::mysql_store_result(self.0.as_ptr()); + if !res.is_null() { + ffi::mysql_free_result(res); + } + } + self.did_an_error_occur() + } + + fn more_results(&self) -> bool { + unsafe { ffi::mysql_more_results(self.0.as_ptr()) != 0 } + } + + fn next_result(&self) -> QueryResult<()> { + unsafe { ffi::mysql_next_result(self.0.as_ptr()) }; + self.did_an_error_occur() + } +} + +impl Drop for RawConnection { + fn drop(&mut self) { + unsafe { + ffi::mysql_close(self.0.as_ptr()); + } + } +} + +/// > In a non-multi-threaded environment, `mysql_init()` invokes +/// > `mysql_library_init()` automatically as necessary. However, +/// > `mysql_library_init()` is not thread-safe in a multi-threaded environment, +/// > and thus neither is `mysql_init()`. Before calling `mysql_init()`, either +/// > call `mysql_library_init()` prior to spawning any threads, or use a mutex +/// > to protect the `mysql_library_init()` call. This should be done prior to +/// > any other client library call. +/// +/// +static MYSQL_THREAD_UNSAFE_INIT: Once = Once::new(); + +fn perform_thread_unsafe_library_initialization() { + MYSQL_THREAD_UNSAFE_INIT.call_once(|| { + // mysql_library_init is defined by `#define mysql_library_init mysql_server_init` + // which isn't picked up by bindgen + let error_code = unsafe { ffi::mysql_server_init(0, ptr::null_mut(), ptr::null_mut()) }; + if error_code != 0 { + // FIXME: This is documented as Nonzero if an error occurred. + // Presumably the value has some sort of meaning that we should + // reflect in this message. We are going to panic instead of return + // an error here, since the documentation does not indicate whether + // it is safe to call this function twice if the first call failed, + // so I will assume it is not. + panic!("Unable to perform MySQL global initialization"); + } + }) +} diff --git a/collector/benchmarks/diesel/diesel/src/mysql/connection/stmt/iterator.rs b/collector/benchmarks/diesel/diesel/src/mysql/connection/stmt/iterator.rs new file mode 100644 index 000000000..584b66f94 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/mysql/connection/stmt/iterator.rs @@ -0,0 +1,122 @@ +use super::{metadata::MysqlFieldMetadata, BindData, Binds, Statement, StatementMetadata}; +use crate::mysql::{Mysql, MysqlType}; +use crate::result::QueryResult; +use crate::row::*; + +pub struct StatementIterator<'a> { + stmt: &'a mut Statement, + output_binds: Binds, + metadata: StatementMetadata, +} + +#[allow(clippy::should_implement_trait)] // don't neet `Iterator` here +impl<'a> StatementIterator<'a> { + #[allow(clippy::new_ret_no_self)] + pub fn new(stmt: &'a mut Statement, types: Vec>) -> QueryResult { + let metadata = stmt.metadata()?; + + let mut output_binds = Binds::from_output_types(types, &metadata); + + stmt.execute_statement(&mut output_binds)?; + + Ok(StatementIterator { + stmt, + output_binds, + metadata, + }) + } + + pub fn map(mut self, mut f: F) -> QueryResult> + where + F: FnMut(MysqlRow) -> QueryResult, + { + let mut results = Vec::new(); + while let Some(row) = self.next() { + results.push(f(row?)?); + } + Ok(results) + } + + fn next(&mut self) -> Option> { + match self.stmt.populate_row_buffers(&mut self.output_binds) { + Ok(Some(())) => Some(Ok(MysqlRow { + col_idx: 0, + binds: &mut self.output_binds, + metadata: &self.metadata, + })), + Ok(None) => None, + Err(e) => Some(Err(e)), + } + } +} + +#[derive(Clone)] +pub struct MysqlRow<'a> { + col_idx: usize, + binds: &'a Binds, + metadata: &'a StatementMetadata, +} + +impl<'a> Row<'a, Mysql> for MysqlRow<'a> { + type Field = MysqlField<'a>; + type InnerPartialRow = Self; + + fn field_count(&self) -> usize { + self.binds.len() + } + + fn get(&self, idx: I) -> Option + where + Self: RowIndex, + { + let idx = self.idx(idx)?; + Some(MysqlField { + bind: &self.binds[idx], + metadata: &self.metadata.fields()[idx], + }) + } + + fn partial_row(&self, range: std::ops::Range) -> PartialRow { + PartialRow::new(self, range) + } +} + +impl<'a> RowIndex for MysqlRow<'a> { + fn idx(&self, idx: usize) -> Option { + if idx < self.field_count() { + Some(idx) + } else { + None + } + } +} + +impl<'a, 'b> RowIndex<&'a str> for MysqlRow<'b> { + fn idx(&self, idx: &'a str) -> Option { + self.metadata + .fields() + .iter() + .enumerate() + .find(|(_, field_meta)| field_meta.field_name() == Some(idx)) + .map(|(idx, _)| idx) + } +} + +pub struct MysqlField<'a> { + bind: &'a BindData, + metadata: &'a MysqlFieldMetadata<'a>, +} + +impl<'a> Field<'a, Mysql> for MysqlField<'a> { + fn field_name(&self) -> Option<&'a str> { + self.metadata.field_name() + } + + fn is_null(&self) -> bool { + self.bind.is_null() + } + + fn value(&self) -> Option> { + self.bind.value() + } +} diff --git a/collector/benchmarks/diesel/diesel/src/mysql/connection/stmt/metadata.rs b/collector/benchmarks/diesel/diesel/src/mysql/connection/stmt/metadata.rs new file mode 100644 index 000000000..7a79ee92c --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/mysql/connection/stmt/metadata.rs @@ -0,0 +1,60 @@ +use std::ffi::CStr; +use std::ptr::NonNull; +use std::slice; + +use super::ffi; +use crate::mysql::connection::bind::Flags; + +pub struct StatementMetadata { + result: NonNull, +} + +impl StatementMetadata { + pub fn new(result: NonNull) -> Self { + StatementMetadata { result } + } + + pub fn fields(&'_ self) -> &'_ [MysqlFieldMetadata<'_>] { + unsafe { + let num_fields = ffi::mysql_num_fields(self.result.as_ptr()); + let field_ptr = ffi::mysql_fetch_fields(self.result.as_ptr()); + if field_ptr.is_null() { + &[] + } else { + slice::from_raw_parts(field_ptr as _, num_fields as usize) + } + } + } +} + +impl Drop for StatementMetadata { + fn drop(&mut self) { + unsafe { ffi::mysql_free_result(self.result.as_mut()) }; + } +} + +#[repr(transparent)] +pub struct MysqlFieldMetadata<'a>(ffi::MYSQL_FIELD, std::marker::PhantomData<&'a ()>); + +impl<'a> MysqlFieldMetadata<'a> { + pub fn field_name(&self) -> Option<&str> { + if self.0.name.is_null() { + None + } else { + unsafe { + Some(CStr::from_ptr(self.0.name).to_str().expect( + "Expect mysql field names to be UTF-8, because we \ + requested UTF-8 encoding on connection setup", + )) + } + } + } + + pub fn field_type(&self) -> ffi::enum_field_types { + self.0.type_ + } + + pub(crate) fn flags(&self) -> Flags { + Flags::from(self.0.flags) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/mysql/connection/stmt/mod.rs b/collector/benchmarks/diesel/diesel/src/mysql/connection/stmt/mod.rs new file mode 100644 index 000000000..7fb043327 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/mysql/connection/stmt/mod.rs @@ -0,0 +1,180 @@ +extern crate mysqlclient_sys as ffi; + +mod iterator; +mod metadata; + +use std::ffi::CStr; +use std::os::raw as libc; +use std::ptr::NonNull; + +use self::iterator::*; +use super::bind::{BindData, Binds}; +use crate::mysql::MysqlType; +use crate::result::{DatabaseErrorKind, QueryResult}; + +pub use self::metadata::StatementMetadata; + +pub struct Statement { + stmt: NonNull, + input_binds: Option, +} + +impl Statement { + pub(crate) fn new(stmt: NonNull) -> Self { + Statement { + stmt, + input_binds: None, + } + } + + pub fn prepare(&self, query: &str) -> QueryResult<()> { + unsafe { + ffi::mysql_stmt_prepare( + self.stmt.as_ptr(), + query.as_ptr() as *const libc::c_char, + query.len() as libc::c_ulong, + ); + } + self.did_an_error_occur() + } + + pub fn bind(&mut self, binds: Iter) -> QueryResult<()> + where + Iter: IntoIterator>)>, + { + let input_binds = Binds::from_input_data(binds)?; + self.input_bind(input_binds) + } + + pub(super) fn input_bind(&mut self, mut input_binds: Binds) -> QueryResult<()> { + input_binds.with_mysql_binds(|bind_ptr| { + // This relies on the invariant that the current value of `self.input_binds` + // will not change without this function being called + unsafe { + ffi::mysql_stmt_bind_param(self.stmt.as_ptr(), bind_ptr); + } + }); + self.input_binds = Some(input_binds); + self.did_an_error_occur() + } + + /// This function should be called instead of `results` on queries which + /// have no return value. It should never be called on a statement on + /// which `results` has previously been called? + pub unsafe fn execute(&self) -> QueryResult<()> { + ffi::mysql_stmt_execute(self.stmt.as_ptr()); + self.did_an_error_occur()?; + ffi::mysql_stmt_store_result(self.stmt.as_ptr()); + self.did_an_error_occur()?; + Ok(()) + } + + pub fn affected_rows(&self) -> usize { + let affected_rows = unsafe { ffi::mysql_stmt_affected_rows(self.stmt.as_ptr()) }; + affected_rows as usize + } + + /// This function should be called instead of `execute` for queries which + /// have a return value. After calling this function, `execute` can never + /// be called on this statement. + pub unsafe fn results( + &mut self, + types: Vec>, + ) -> QueryResult { + StatementIterator::new(self, types) + } + + fn last_error_message(&self) -> String { + unsafe { CStr::from_ptr(ffi::mysql_stmt_error(self.stmt.as_ptr())) } + .to_string_lossy() + .into_owned() + } + + /// If the pointers referenced by the `MYSQL_BIND` structures are invalidated, + /// you must call this function again before calling `mysql_stmt_fetch`. + pub unsafe fn bind_result(&self, binds: *mut ffi::MYSQL_BIND) -> QueryResult<()> { + ffi::mysql_stmt_bind_result(self.stmt.as_ptr(), binds); + self.did_an_error_occur() + } + + pub unsafe fn fetch_column( + &self, + bind: &mut ffi::MYSQL_BIND, + idx: usize, + offset: usize, + ) -> QueryResult<()> { + ffi::mysql_stmt_fetch_column( + self.stmt.as_ptr(), + bind, + idx as libc::c_uint, + offset as libc::c_ulong, + ); + self.did_an_error_occur() + } + + pub(super) fn metadata(&self) -> QueryResult { + use crate::result::Error::DeserializationError; + + let result_ptr = unsafe { ffi::mysql_stmt_result_metadata(self.stmt.as_ptr()) }; + self.did_an_error_occur()?; + NonNull::new(result_ptr) + .map(StatementMetadata::new) + .ok_or_else(|| DeserializationError("No metadata exists".into())) + } + + pub(super) fn did_an_error_occur(&self) -> QueryResult<()> { + use crate::result::Error::DatabaseError; + + let error_message = self.last_error_message(); + if error_message.is_empty() { + Ok(()) + } else { + Err(DatabaseError( + self.last_error_type(), + Box::new(error_message), + )) + } + } + + fn last_error_type(&self) -> DatabaseErrorKind { + let last_error_number = unsafe { ffi::mysql_stmt_errno(self.stmt.as_ptr()) }; + // These values are not exposed by the C API, but are documented + // at https://dev.mysql.com/doc/refman/8.0/en/server-error-reference.html + // and are from the ANSI SQLSTATE standard + match last_error_number { + 1062 | 1586 | 1859 => DatabaseErrorKind::UniqueViolation, + 1216 | 1217 | 1451 | 1452 | 1830 | 1834 => DatabaseErrorKind::ForeignKeyViolation, + 1792 => DatabaseErrorKind::ReadOnlyTransaction, + 1048 | 1364 => DatabaseErrorKind::NotNullViolation, + 3819 => DatabaseErrorKind::CheckViolation, + _ => DatabaseErrorKind::__Unknown, + } + } + + pub(super) fn execute_statement(&mut self, binds: &mut Binds) -> QueryResult<()> { + unsafe { + binds.with_mysql_binds(|bind_ptr| self.bind_result(bind_ptr))?; + self.execute()?; + } + Ok(()) + } + + pub(super) fn populate_row_buffers(&self, binds: &mut Binds) -> QueryResult> { + let next_row_result = unsafe { ffi::mysql_stmt_fetch(self.stmt.as_ptr()) }; + match next_row_result as libc::c_uint { + ffi::MYSQL_NO_DATA => Ok(None), + ffi::MYSQL_DATA_TRUNCATED => binds.populate_dynamic_buffers(self).map(Some), + 0 => { + binds.update_buffer_lengths(); + Ok(Some(())) + } + _error => self.did_an_error_occur().map(Some), + } + } +} + +impl Drop for Statement { + fn drop(&mut self) { + unsafe { ffi::mysql_stmt_close(self.stmt.as_ptr()) }; + } +} diff --git a/collector/benchmarks/diesel/diesel/src/mysql/connection/url.rs b/collector/benchmarks/diesel/diesel/src/mysql/connection/url.rs new file mode 100644 index 000000000..5fc84622f --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/mysql/connection/url.rs @@ -0,0 +1,229 @@ +extern crate percent_encoding; +extern crate url; + +use self::percent_encoding::percent_decode; +use self::url::{Host, Url}; +use std::collections::HashMap; +use std::ffi::{CStr, CString}; + +use crate::result::{ConnectionError, ConnectionResult}; + +pub struct ConnectionOptions { + host: Option, + user: CString, + password: Option, + database: Option, + port: Option, + unix_socket: Option, +} + +impl ConnectionOptions { + pub fn parse(database_url: &str) -> ConnectionResult { + let url = match Url::parse(database_url) { + Ok(url) => url, + Err(_) => return Err(connection_url_error()), + }; + + if url.scheme() != "mysql" { + return Err(connection_url_error()); + } + + if url.path_segments().map(Iterator::count).unwrap_or(0) > 1 { + return Err(connection_url_error()); + } + + let query_pairs = url.query_pairs().into_owned().collect::>(); + if query_pairs.get("database").is_some() { + return Err(connection_url_error()); + } + + let unix_socket = match query_pairs.get("unix_socket") { + Some(v) => Some(CString::new(v.as_bytes())?), + _ => None, + }; + + let host = match url.host() { + Some(Host::Ipv6(host)) => Some(CString::new(host.to_string())?), + Some(host) if host.to_string() == "localhost" && unix_socket != None => None, + Some(host) => Some(CString::new(host.to_string())?), + None => None, + }; + let user = decode_into_cstring(url.username())?; + let password = match url.password() { + Some(password) => Some(decode_into_cstring(password)?), + None => None, + }; + + let database = match url.path_segments().and_then(|mut iter| iter.next()) { + Some("") | None => None, + Some(segment) => Some(CString::new(segment.as_bytes())?), + }; + + Ok(ConnectionOptions { + host: host, + user: user, + password: password, + database: database, + port: url.port(), + unix_socket: unix_socket, + }) + } + + pub fn host(&self) -> Option<&CStr> { + self.host.as_ref().map(|x| &**x) + } + + pub fn user(&self) -> &CStr { + &self.user + } + + pub fn password(&self) -> Option<&CStr> { + self.password.as_ref().map(|x| &**x) + } + + pub fn database(&self) -> Option<&CStr> { + self.database.as_ref().map(|x| &**x) + } + + pub fn port(&self) -> Option { + self.port + } + + pub fn unix_socket(&self) -> Option<&CStr> { + self.unix_socket.as_ref().map(|x| &**x) + } +} + +fn decode_into_cstring(s: &str) -> ConnectionResult { + let decoded = percent_decode(s.as_bytes()) + .decode_utf8() + .map_err(|_| connection_url_error())?; + CString::new(decoded.as_bytes()).map_err(Into::into) +} + +fn connection_url_error() -> ConnectionError { + let msg = "MySQL connection URLs must be in the form \ + `mysql://[[user]:[password]@]host[:port][/database][?unix_socket=socket-path]`"; + ConnectionError::InvalidConnectionUrl(msg.into()) +} + +#[test] +fn urls_with_schemes_other_than_mysql_are_errors() { + assert!(ConnectionOptions::parse("postgres://localhost").is_err()); + assert!(ConnectionOptions::parse("http://localhost").is_err()); + assert!(ConnectionOptions::parse("file:///tmp/mysql.sock").is_err()); + assert!(ConnectionOptions::parse("socket:///tmp/mysql.sock").is_err()); + assert!(ConnectionOptions::parse("mysql://localhost?database=somedb").is_err()); + assert!(ConnectionOptions::parse("mysql://localhost").is_ok()); +} + +#[test] +fn urls_must_have_zero_or_one_path_segments() { + assert!(ConnectionOptions::parse("mysql://localhost/foo/bar").is_err()); + assert!(ConnectionOptions::parse("mysql://localhost/foo").is_ok()); +} + +#[test] +fn first_path_segment_is_treated_as_database() { + let foo_cstr = CString::new("foo").unwrap(); + let bar_cstr = CString::new("bar").unwrap(); + assert_eq!( + Some(&*foo_cstr), + ConnectionOptions::parse("mysql://localhost/foo") + .unwrap() + .database() + ); + assert_eq!( + Some(&*bar_cstr), + ConnectionOptions::parse("mysql://localhost/bar") + .unwrap() + .database() + ); + assert_eq!( + None, + ConnectionOptions::parse("mysql://localhost") + .unwrap() + .database() + ); +} + +#[test] +fn userinfo_should_be_percent_decode() { + use self::percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS}; + const USERINFO_ENCODE_SET: &AsciiSet = &CONTROLS + .add(b' ') + .add(b'"') + .add(b'<') + .add(b'>') + .add(b'`') + .add(b'#') + .add(b'?') + .add(b'{') + .add(b'}') + .add(b'/') + .add(b':') + .add(b';') + .add(b'=') + .add(b'@') + .add(b'[') + .add(b'\\') + .add(b']') + .add(b'^') + .add(b'|'); + + let username = "x#gfuL?4Zuj{n73m}eeJt0"; + let encoded_username = utf8_percent_encode(username, USERINFO_ENCODE_SET); + + let password = "x/gfuL?4Zuj{n73m}eeJt1"; + let encoded_password = utf8_percent_encode(password, USERINFO_ENCODE_SET); + + let db_url = format!( + "mysql://{}:{}@localhost/bar", + encoded_username, encoded_password + ); + let db_url = Url::parse(&db_url).unwrap(); + + let conn_opts = ConnectionOptions::parse(db_url.as_str()).unwrap(); + let username = CString::new(username.as_bytes()).unwrap(); + let password = CString::new(password.as_bytes()).unwrap(); + assert_eq!(username, conn_opts.user); + assert_eq!(password, conn_opts.password.unwrap()); +} + +#[test] +fn ipv6_host_not_wrapped_in_brackets() { + let host1 = CString::new("::1").unwrap(); + let host2 = CString::new("2001:db8:85a3::8a2e:370:7334").unwrap(); + + assert_eq!( + Some(&*host1), + ConnectionOptions::parse("mysql://[::1]").unwrap().host() + ); + assert_eq!( + Some(&*host2), + ConnectionOptions::parse("mysql://[2001:db8:85a3::8a2e:370:7334]") + .unwrap() + .host() + ); +} + +#[test] +fn unix_socket_tests() { + let unix_socket = "/var/run/mysqld.sock"; + let username = "foo"; + let password = "bar"; + let db_url = format!( + "mysql://{}:{}@localhost?unix_socket={}", + username, password, unix_socket + ); + let conn_opts = ConnectionOptions::parse(db_url.as_str()).unwrap(); + let cstring = |s| CString::new(s).unwrap(); + assert_eq!(None, conn_opts.host); + assert_eq!(None, conn_opts.port); + assert_eq!(cstring(username), conn_opts.user); + assert_eq!(cstring(password), conn_opts.password.unwrap()); + assert_eq!( + CString::new(unix_socket).unwrap(), + conn_opts.unix_socket.unwrap() + ); +} diff --git a/collector/benchmarks/diesel/diesel/src/mysql/mod.rs b/collector/benchmarks/diesel/diesel/src/mysql/mod.rs new file mode 100644 index 000000000..8f3161254 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/mysql/mod.rs @@ -0,0 +1,17 @@ +//! Provides types and functions related to working with MySQL +//! +//! Much of this module is re-exported from database agnostic locations. +//! However, if you are writing code specifically to extend Diesel on +//! MySQL, you may need to work with this module directly. + +mod backend; +mod connection; +mod value; + +mod query_builder; +pub mod types; + +pub use self::backend::{Mysql, MysqlType}; +pub use self::connection::MysqlConnection; +pub use self::query_builder::MysqlQueryBuilder; +pub use self::value::{MysqlValue, NumericRepresentation}; diff --git a/collector/benchmarks/diesel/diesel/src/mysql/query_builder/limit_offset.rs b/collector/benchmarks/diesel/diesel/src/mysql/query_builder/limit_offset.rs new file mode 100644 index 000000000..2ddb0ae3f --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/mysql/query_builder/limit_offset.rs @@ -0,0 +1,104 @@ +use crate::mysql::Mysql; +use crate::query_builder::limit_clause::{LimitClause, NoLimitClause}; +use crate::query_builder::limit_offset_clause::{BoxedLimitOffsetClause, LimitOffsetClause}; +use crate::query_builder::offset_clause::{NoOffsetClause, OffsetClause}; +use crate::query_builder::{AstPass, IntoBoxedClause, QueryFragment}; +use crate::result::QueryResult; + +impl QueryFragment for LimitOffsetClause { + fn walk_ast(&self, _out: AstPass) -> QueryResult<()> { + Ok(()) + } +} + +impl QueryFragment for LimitOffsetClause, NoOffsetClause> +where + LimitClause: QueryFragment, +{ + fn walk_ast(&self, out: AstPass) -> QueryResult<()> { + self.limit_clause.walk_ast(out)?; + Ok(()) + } +} + +impl QueryFragment for LimitOffsetClause, OffsetClause> +where + LimitClause: QueryFragment, + OffsetClause: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + self.limit_clause.walk_ast(out.reborrow())?; + self.offset_clause.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl<'a> QueryFragment for BoxedLimitOffsetClause<'a, Mysql> { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + match (self.limit.as_ref(), self.offset.as_ref()) { + (Some(limit), Some(offset)) => { + limit.walk_ast(out.reborrow())?; + offset.walk_ast(out.reborrow())?; + } + (Some(limit), None) => { + limit.walk_ast(out.reborrow())?; + } + (None, Some(offset)) => { + // Mysql requires a limit clause in front of any offset clause + // The documentation proposes the following: + // > To retrieve all rows from a certain offset up to the end of the + // > result set, you can use some large number for the second parameter. + // https://dev.mysql.com/doc/refman/8.0/en/select.html + // Therefore we just use u64::MAX as limit here + // That does not result in any limitations because mysql only supports + // up to 64TB of data per table. Assuming 1 bit per row this means + // 1024 * 1024 * 1024 * 1024 * 8 = 562.949.953.421.312 rows which is smaller + // than 2^64 = 18.446.744.073.709.551.615 + out.push_sql(" LIMIT 18446744073709551615 "); + offset.walk_ast(out.reborrow())?; + } + (None, None) => {} + } + Ok(()) + } +} + +impl<'a> IntoBoxedClause<'a, Mysql> for LimitOffsetClause { + type BoxedClause = BoxedLimitOffsetClause<'a, Mysql>; + + fn into_boxed(self) -> Self::BoxedClause { + BoxedLimitOffsetClause { + limit: None, + offset: None, + } + } +} + +impl<'a, L> IntoBoxedClause<'a, Mysql> for LimitOffsetClause, NoOffsetClause> +where + L: QueryFragment + Send + 'a, +{ + type BoxedClause = BoxedLimitOffsetClause<'a, Mysql>; + + fn into_boxed(self) -> Self::BoxedClause { + BoxedLimitOffsetClause { + limit: Some(Box::new(self.limit_clause)), + offset: None, + } + } +} + +impl<'a, L, O> IntoBoxedClause<'a, Mysql> for LimitOffsetClause, OffsetClause> +where + L: QueryFragment + Send + 'a, + O: QueryFragment + Send + 'a, +{ + type BoxedClause = BoxedLimitOffsetClause<'a, Mysql>; + + fn into_boxed(self) -> Self::BoxedClause { + BoxedLimitOffsetClause { + limit: Some(Box::new(self.limit_clause)), + offset: Some(Box::new(self.offset_clause)), + } + } +} diff --git a/collector/benchmarks/diesel/diesel/src/mysql/query_builder/mod.rs b/collector/benchmarks/diesel/diesel/src/mysql/query_builder/mod.rs new file mode 100644 index 000000000..37d73cc92 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/mysql/query_builder/mod.rs @@ -0,0 +1,41 @@ +use super::backend::Mysql; +use crate::query_builder::QueryBuilder; +use crate::result::QueryResult; + +mod limit_offset; +mod query_fragment_impls; + +/// The MySQL query builder +#[allow(missing_debug_implementations)] +#[derive(Default)] +pub struct MysqlQueryBuilder { + sql: String, +} + +impl MysqlQueryBuilder { + /// Constructs a new query builder with an empty query + pub fn new() -> Self { + MysqlQueryBuilder::default() + } +} + +impl QueryBuilder for MysqlQueryBuilder { + fn push_sql(&mut self, sql: &str) { + self.sql.push_str(sql); + } + + fn push_identifier(&mut self, identifier: &str) -> QueryResult<()> { + self.push_sql("`"); + self.push_sql(&identifier.replace("`", "``")); + self.push_sql("`"); + Ok(()) + } + + fn push_bind_param(&mut self) { + self.push_sql("?"); + } + + fn finish(self) -> String { + self.sql + } +} diff --git a/collector/benchmarks/diesel/diesel/src/mysql/query_builder/query_fragment_impls.rs b/collector/benchmarks/diesel/diesel/src/mysql/query_builder/query_fragment_impls.rs new file mode 100644 index 000000000..fe3e6fdf3 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/mysql/query_builder/query_fragment_impls.rs @@ -0,0 +1,38 @@ +use crate::mysql::Mysql; +use crate::query_builder::locking_clause::{ForShare, ForUpdate, NoModifier, NoWait, SkipLocked}; +use crate::query_builder::{AstPass, QueryFragment}; +use crate::result::QueryResult; + +impl QueryFragment for ForUpdate { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" FOR UPDATE"); + Ok(()) + } +} + +impl QueryFragment for ForShare { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" FOR SHARE"); + Ok(()) + } +} + +impl QueryFragment for NoModifier { + fn walk_ast(&self, _out: AstPass) -> QueryResult<()> { + Ok(()) + } +} + +impl QueryFragment for SkipLocked { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" SKIP LOCKED"); + Ok(()) + } +} + +impl QueryFragment for NoWait { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" NOWAIT"); + Ok(()) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/mysql/types/date_and_time.rs b/collector/benchmarks/diesel/diesel/src/mysql/types/date_and_time.rs new file mode 100644 index 000000000..093dd49fe --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/mysql/types/date_and_time.rs @@ -0,0 +1,281 @@ +use chrono::*; +use mysqlclient_sys as ffi; +use std::io::Write; +use std::os::raw as libc; +use std::{mem, slice}; + +use super::MYSQL_TIME; +use crate::deserialize::{self, FromSql}; +use crate::mysql::{Mysql, MysqlValue}; +use crate::serialize::{self, IsNull, Output, ToSql}; +use crate::sql_types::{Date, Datetime, Time, Timestamp}; + +macro_rules! mysql_time_impls { + ($ty:ty) => { + impl ToSql<$ty, Mysql> for MYSQL_TIME { + fn to_sql(&self, out: &mut Output) -> serialize::Result { + let bytes = unsafe { + let bytes_ptr = self as *const MYSQL_TIME as *const u8; + slice::from_raw_parts(bytes_ptr, mem::size_of::()) + }; + out.write_all(bytes)?; + Ok(IsNull::No) + } + } + + impl FromSql<$ty, Mysql> for MYSQL_TIME { + fn from_sql(value: MysqlValue<'_>) -> deserialize::Result { + value.time_value() + } + } + }; +} + +mysql_time_impls!(Datetime); +mysql_time_impls!(Timestamp); +mysql_time_impls!(Time); +mysql_time_impls!(Date); + +impl ToSql for NaiveDateTime { + fn to_sql(&self, out: &mut Output) -> serialize::Result { + >::to_sql(self, out) + } +} + +impl FromSql for NaiveDateTime { + fn from_sql(bytes: MysqlValue<'_>) -> deserialize::Result { + >::from_sql(bytes) + } +} + +impl ToSql for NaiveDateTime { + fn to_sql(&self, out: &mut Output) -> serialize::Result { + let mysql_time = MYSQL_TIME { + year: self.year() as libc::c_uint, + month: self.month() as libc::c_uint, + day: self.day() as libc::c_uint, + hour: self.hour() as libc::c_uint, + minute: self.minute() as libc::c_uint, + second: self.second() as libc::c_uint, + second_part: libc::c_ulong::from(self.timestamp_subsec_micros()), + neg: false, + time_type: ffi::enum_mysql_timestamp_type::MYSQL_TIMESTAMP_DATETIME, + time_zone_displacement: 0, + }; + + >::to_sql(&mysql_time, out) + } +} + +impl FromSql for NaiveDateTime { + fn from_sql(bytes: MysqlValue<'_>) -> deserialize::Result { + let mysql_time = >::from_sql(bytes)?; + + NaiveDate::from_ymd_opt( + mysql_time.year as i32, + mysql_time.month as u32, + mysql_time.day as u32, + ) + .and_then(|v| { + v.and_hms_micro_opt( + mysql_time.hour as u32, + mysql_time.minute as u32, + mysql_time.second as u32, + mysql_time.second_part as u32, + ) + }) + .ok_or_else(|| format!("Cannot parse this date: {:?}", mysql_time).into()) + } +} + +impl ToSql for NaiveTime { + fn to_sql(&self, out: &mut serialize::Output) -> serialize::Result { + let mysql_time = MYSQL_TIME { + hour: self.hour() as libc::c_uint, + minute: self.minute() as libc::c_uint, + second: self.second() as libc::c_uint, + day: 0, + month: 0, + second_part: 0, + year: 0, + neg: false, + time_type: ffi::enum_mysql_timestamp_type::MYSQL_TIMESTAMP_TIME, + time_zone_displacement: 0, + }; + + >::to_sql(&mysql_time, out) + } +} + +impl FromSql for NaiveTime { + fn from_sql(bytes: MysqlValue<'_>) -> deserialize::Result { + let mysql_time = >::from_sql(bytes)?; + NaiveTime::from_hms_opt( + mysql_time.hour as u32, + mysql_time.minute as u32, + mysql_time.second as u32, + ) + .ok_or_else(|| format!("Unable to convert {:?} to chrono", mysql_time).into()) + } +} + +impl ToSql for NaiveDate { + fn to_sql(&self, out: &mut Output) -> serialize::Result { + let mysql_time = MYSQL_TIME { + year: self.year() as libc::c_uint, + month: self.month() as libc::c_uint, + day: self.day() as libc::c_uint, + hour: 0, + minute: 0, + second: 0, + second_part: 0, + neg: false, + time_type: ffi::enum_mysql_timestamp_type::MYSQL_TIMESTAMP_DATE, + time_zone_displacement: 0, + }; + + >::to_sql(&mysql_time, out) + } +} + +impl FromSql for NaiveDate { + fn from_sql(bytes: MysqlValue<'_>) -> deserialize::Result { + let mysql_time = >::from_sql(bytes)?; + NaiveDate::from_ymd_opt( + mysql_time.year as i32, + mysql_time.month as u32, + mysql_time.day as u32, + ) + .ok_or_else(|| format!("Unable to convert {:?} to chrono", mysql_time).into()) + } +} + +#[cfg(test)] +mod tests { + extern crate chrono; + extern crate dotenv; + + use self::chrono::{Duration, NaiveDate, NaiveTime, Utc}; + use self::dotenv::dotenv; + + use crate::dsl::{now, sql}; + use crate::prelude::*; + use crate::select; + use crate::sql_types::{Date, Datetime, Time, Timestamp}; + + fn connection() -> MysqlConnection { + dotenv().ok(); + + let connection_url = ::std::env::var("MYSQL_UNIT_TEST_DATABASE_URL") + .or_else(|_| ::std::env::var("MYSQL_DATABASE_URL")) + .or_else(|_| ::std::env::var("DATABASE_URL")) + .expect("DATABASE_URL must be set in order to run tests"); + MysqlConnection::establish(&connection_url).unwrap() + } + + #[test] + fn unix_epoch_encodes_correctly() { + let connection = connection(); + let time = NaiveDate::from_ymd(1970, 1, 1).and_hms(0, 0, 0); + let query = select(sql::("CAST('1970-01-01' AS DATETIME)").eq(time)); + assert!(query.get_result::(&connection).unwrap()); + let query = select(sql::("CAST('1970-01-01' AS DATETIME)").eq(time)); + assert!(query.get_result::(&connection).unwrap()); + } + + #[test] + fn unix_epoch_decodes_correctly() { + let connection = connection(); + let time = NaiveDate::from_ymd(1970, 1, 1).and_hms(0, 0, 0); + let epoch_from_sql = + select(sql::("CAST('1970-01-01' AS DATETIME)")).get_result(&connection); + assert_eq!(Ok(time), epoch_from_sql); + let epoch_from_sql = + select(sql::("CAST('1970-01-01' AS DATETIME)")).get_result(&connection); + assert_eq!(Ok(time), epoch_from_sql); + } + + #[test] + fn times_relative_to_now_encode_correctly() { + let connection = connection(); + let time = Utc::now().naive_utc() + Duration::days(1); + let query = select(now.lt(time)); + assert!(query.get_result::(&connection).unwrap()); + + let time = Utc::now().naive_utc() - Duration::days(1); + let query = select(now.gt(time)); + assert!(query.get_result::(&connection).unwrap()); + } + + #[test] + fn times_of_day_encode_correctly() { + let connection = connection(); + + let midnight = NaiveTime::from_hms(0, 0, 0); + let query = select(sql::
for ConflictTarget> {} diff --git a/collector/benchmarks/diesel/diesel/src/pg/query_builder/query_fragment_impls.rs b/collector/benchmarks/diesel/diesel/src/pg/query_builder/query_fragment_impls.rs new file mode 100644 index 000000000..e38745b9e --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/pg/query_builder/query_fragment_impls.rs @@ -0,0 +1,54 @@ +use crate::pg::Pg; +use crate::query_builder::locking_clause::{ + ForKeyShare, ForNoKeyUpdate, ForShare, ForUpdate, NoModifier, NoWait, SkipLocked, +}; +use crate::query_builder::{AstPass, QueryFragment}; +use crate::result::QueryResult; + +impl QueryFragment for ForUpdate { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" FOR UPDATE"); + Ok(()) + } +} + +impl QueryFragment for ForNoKeyUpdate { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" FOR NO KEY UPDATE"); + Ok(()) + } +} + +impl QueryFragment for ForShare { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" FOR SHARE"); + Ok(()) + } +} + +impl QueryFragment for ForKeyShare { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" FOR KEY SHARE"); + Ok(()) + } +} + +impl QueryFragment for NoModifier { + fn walk_ast(&self, _out: AstPass) -> QueryResult<()> { + Ok(()) + } +} + +impl QueryFragment for SkipLocked { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" SKIP LOCKED"); + Ok(()) + } +} + +impl QueryFragment for NoWait { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" NOWAIT"); + Ok(()) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/pg/serialize/mod.rs b/collector/benchmarks/diesel/diesel/src/pg/serialize/mod.rs new file mode 100644 index 000000000..6bc1f4bbf --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/pg/serialize/mod.rs @@ -0,0 +1,3 @@ +mod write_tuple; + +pub use self::write_tuple::WriteTuple; diff --git a/collector/benchmarks/diesel/diesel/src/pg/serialize/write_tuple.rs b/collector/benchmarks/diesel/diesel/src/pg/serialize/write_tuple.rs new file mode 100644 index 000000000..5c2f55cdc --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/pg/serialize/write_tuple.rs @@ -0,0 +1,47 @@ +use std::io::Write; + +use crate::pg::Pg; +use crate::serialize::{self, Output}; + +/// Helper trait for writing tuples as named composite types +/// +/// This trait is essentially `ToSql>` for tuples. +/// While we can provide a valid body of `to_sql`, +/// PostgreSQL doesn't allow the use of bind parameters for unnamed composite types. +/// For this reason, we avoid implementing `ToSql` directly. +/// +/// This trait can be used by `ToSql` impls of named composite types. +/// +/// # Example +/// +/// ``` +/// # #[cfg(feature = "postgres")] +/// # mod the_impl { +/// # use diesel::prelude::*; +/// # use diesel::pg::Pg; +/// # use diesel::serialize::{self, ToSql, Output, WriteTuple}; +/// # use diesel::sql_types::{Integer, Text, SqlType}; +/// # use std::io::Write; +/// # +/// #[derive(SqlType)] +/// #[postgres(type_name = "my_type")] +/// struct MyType; +/// +/// #[derive(Debug)] +/// struct MyStruct<'a>(i32, &'a str); +/// +/// impl<'a> ToSql for MyStruct<'a> { +/// fn to_sql(&self, out: &mut Output) -> serialize::Result { +/// WriteTuple::<(Integer, Text)>::write_tuple( +/// &(self.0, self.1), +/// out, +/// ) +/// } +/// } +/// # } +/// # fn main() {} +/// ``` +pub trait WriteTuple { + /// See trait documentation. + fn write_tuple(&self, out: &mut Output) -> serialize::Result; +} diff --git a/collector/benchmarks/diesel/diesel/src/pg/transaction.rs b/collector/benchmarks/diesel/diesel/src/pg/transaction.rs new file mode 100644 index 000000000..fc49a2f06 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/pg/transaction.rs @@ -0,0 +1,401 @@ +#![allow(dead_code)] +use crate::backend::Backend; +use crate::connection::TransactionManager; +use crate::pg::Pg; +use crate::prelude::*; +use crate::query_builder::{AstPass, QueryBuilder, QueryFragment}; +use crate::result::Error; + +/// Used to build a transaction, specifying additional details. +/// +/// This struct is returned by [`.build_transaction`]. +/// See the documentation for methods on this struct for usage examples. +/// See [the PostgreSQL documentation for `SET TRANSACTION`][pg-docs] +/// for details on the behavior of each option. +/// +/// [`.build_transaction`]: struct.PgConnection.html#method.build_transaction +/// [pg-docs]: https://www.postgresql.org/docs/current/static/sql-set-transaction.html +#[allow(missing_debug_implementations)] // False positive. Connection isn't Debug. +#[derive(Clone, Copy)] +#[must_use = "Transaction builder does nothing unless you call `run` on it"] +pub struct TransactionBuilder<'a> { + connection: &'a PgConnection, + isolation_level: Option, + read_mode: Option, + deferrable: Option, +} + +impl<'a> TransactionBuilder<'a> { + pub(crate) fn new(connection: &'a PgConnection) -> Self { + Self { + connection, + isolation_level: None, + read_mode: None, + deferrable: None, + } + } + + /// Makes the transaction `READ ONLY` + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use diesel::sql_query; + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # table! { + /// # users_for_read_only { + /// # id -> Integer, + /// # name -> Text, + /// # } + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use users_for_read_only::table as users; + /// # use users_for_read_only::columns::*; + /// # let conn = connection_no_transaction(); + /// # sql_query("CREATE TABLE IF NOT EXISTS users_for_read_only ( + /// # id SERIAL PRIMARY KEY, + /// # name TEXT NOT NULL + /// # )").execute(&conn)?; + /// conn.build_transaction() + /// .read_only() + /// .run::<_, diesel::result::Error, _>(|| { + /// let read_attempt = users.select(name).load::(&conn); + /// assert!(read_attempt.is_ok()); + /// + /// let write_attempt = diesel::insert_into(users) + /// .values(name.eq("Ruby")) + /// .execute(&conn); + /// assert!(write_attempt.is_err()); + /// + /// Ok(()) + /// })?; + /// # sql_query("DROP TABLE users_for_read_only").execute(&conn)?; + /// # Ok(()) + /// # } + /// ``` + pub fn read_only(mut self) -> Self { + self.read_mode = Some(ReadMode::ReadOnly); + self + } + + /// Makes the transaction `READ WRITE` + /// + /// This is the default, unless you've changed the + /// `default_transaction_read_only` configuration parameter. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use diesel::result::Error::RollbackTransaction; + /// # use diesel::sql_query; + /// # + /// # fn main() { + /// # assert_eq!(run_test(), Err(RollbackTransaction)); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let conn = connection_no_transaction(); + /// conn.build_transaction() + /// .read_write() + /// .run(|| { + /// # sql_query("CREATE TABLE IF NOT EXISTS users ( + /// # id SERIAL PRIMARY KEY, + /// # name TEXT NOT NULL + /// # )").execute(&conn)?; + /// let read_attempt = users.select(name).load::(&conn); + /// assert!(read_attempt.is_ok()); + /// + /// let write_attempt = diesel::insert_into(users) + /// .values(name.eq("Ruby")) + /// .execute(&conn); + /// assert!(write_attempt.is_ok()); + /// + /// # Err(RollbackTransaction) + /// # /* + /// Ok(()) + /// # */ + /// }) + /// # } + /// ``` + pub fn read_write(mut self) -> Self { + self.read_mode = Some(ReadMode::ReadWrite); + self + } + + /// Makes the transaction `DEFERRABLE` + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let conn = connection_no_transaction(); + /// conn.build_transaction() + /// .deferrable() + /// .run(|| Ok(())) + /// # } + /// ``` + pub fn deferrable(mut self) -> Self { + self.deferrable = Some(Deferrable::Deferrable); + self + } + + /// Makes the transaction `NOT DEFERRABLE` + /// + /// This is the default, unless you've changed the + /// `default_transaction_deferrable` configuration parameter. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let conn = connection_no_transaction(); + /// conn.build_transaction() + /// .not_deferrable() + /// .run(|| Ok(())) + /// # } + /// ``` + pub fn not_deferrable(mut self) -> Self { + self.deferrable = Some(Deferrable::NotDeferrable); + self + } + + /// Makes the transaction `ISOLATION LEVEL READ COMMITTED` + /// + /// This is the default, unless you've changed the + /// `default_transaction_isolation_level` configuration parameter. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let conn = connection_no_transaction(); + /// conn.build_transaction() + /// .read_committed() + /// .run(|| Ok(())) + /// # } + /// ``` + pub fn read_committed(mut self) -> Self { + self.isolation_level = Some(IsolationLevel::ReadCommitted); + self + } + + /// Makes the transaction `ISOLATION LEVEL REPEATABLE READ` + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let conn = connection_no_transaction(); + /// conn.build_transaction() + /// .repeatable_read() + /// .run(|| Ok(())) + /// # } + /// ``` + pub fn repeatable_read(mut self) -> Self { + self.isolation_level = Some(IsolationLevel::RepeatableRead); + self + } + + /// Makes the transaction `ISOLATION LEVEL SERIALIZABLE` + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let conn = connection_no_transaction(); + /// conn.build_transaction() + /// .serializable() + /// .run(|| Ok(())) + /// # } + /// ``` + pub fn serializable(mut self) -> Self { + self.isolation_level = Some(IsolationLevel::Serializable); + self + } + + /// Runs the given function inside of the transaction + /// with the parameters given to this builder. + /// + /// Returns an error if the connection is already inside a transaction, + /// or if the transaction fails to commit or rollback + /// + /// If the transaction fails to commit due to a `SerializationFailure` or a + /// `ReadOnlyTransaction` a rollback will be attempted. If the rollback succeeds, + /// the original error will be returned, otherwise the error generated by the rollback + /// will be returned. In the second case the connection should be considered broken + /// as it contains a uncommitted unabortable open transaction. + pub fn run(&self, f: F) -> Result + where + F: FnOnce() -> Result, + E: From, + { + let mut query_builder = ::QueryBuilder::default(); + self.to_sql(&mut query_builder)?; + let sql = query_builder.finish(); + let transaction_manager = self.connection.transaction_manager(); + + transaction_manager.begin_transaction_sql(self.connection, &sql)?; + match f() { + Ok(value) => { + transaction_manager.commit_transaction(self.connection)?; + Ok(value) + } + Err(e) => { + transaction_manager.rollback_transaction(self.connection)?; + Err(e) + } + } + } +} + +impl<'a> QueryFragment for TransactionBuilder<'a> { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("BEGIN TRANSACTION"); + if let Some(ref isolation_level) = self.isolation_level { + isolation_level.walk_ast(out.reborrow())?; + } + if let Some(ref read_mode) = self.read_mode { + read_mode.walk_ast(out.reborrow())?; + } + if let Some(ref deferrable) = self.deferrable { + deferrable.walk_ast(out.reborrow())?; + } + Ok(()) + } +} + +#[derive(Debug, Clone, Copy)] +enum IsolationLevel { + ReadCommitted, + RepeatableRead, + Serializable, +} + +impl QueryFragment for IsolationLevel { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" ISOLATION LEVEL "); + match *self { + IsolationLevel::ReadCommitted => out.push_sql("READ COMMITTED"), + IsolationLevel::RepeatableRead => out.push_sql("REPEATABLE READ"), + IsolationLevel::Serializable => out.push_sql("SERIALIZABLE"), + } + Ok(()) + } +} + +#[derive(Debug, Clone, Copy)] +enum ReadMode { + ReadOnly, + ReadWrite, +} + +impl QueryFragment for ReadMode { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + match *self { + ReadMode::ReadOnly => out.push_sql(" READ ONLY"), + ReadMode::ReadWrite => out.push_sql(" READ WRITE"), + } + Ok(()) + } +} + +#[derive(Debug, Clone, Copy)] +enum Deferrable { + Deferrable, + NotDeferrable, +} + +impl QueryFragment for Deferrable { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + match *self { + Deferrable::Deferrable => out.push_sql(" DEFERRABLE"), + Deferrable::NotDeferrable => out.push_sql(" NOT DEFERRABLE"), + } + Ok(()) + } +} + +#[test] +fn test_transaction_builder_generates_correct_sql() { + extern crate dotenv; + + macro_rules! assert_sql { + ($query:expr, $sql:expr) => { + let mut query_builder = ::QueryBuilder::default(); + $query.to_sql(&mut query_builder).unwrap(); + let sql = query_builder.finish(); + assert_eq!(sql, $sql); + }; + } + + let database_url = dotenv::var("PG_DATABASE_URL") + .or_else(|_| dotenv::var("DATABASE_URL")) + .expect("DATABASE_URL must be set in order to run tests"); + let conn = PgConnection::establish(&database_url).unwrap(); + + let t = conn.build_transaction(); + assert_sql!(t, "BEGIN TRANSACTION"); + assert_sql!(t.read_only(), "BEGIN TRANSACTION READ ONLY"); + assert_sql!(t.read_write(), "BEGIN TRANSACTION READ WRITE"); + assert_sql!(t.deferrable(), "BEGIN TRANSACTION DEFERRABLE"); + assert_sql!(t.not_deferrable(), "BEGIN TRANSACTION NOT DEFERRABLE"); + assert_sql!( + t.read_committed(), + "BEGIN TRANSACTION ISOLATION LEVEL READ COMMITTED" + ); + assert_sql!( + t.repeatable_read(), + "BEGIN TRANSACTION ISOLATION LEVEL REPEATABLE READ" + ); + assert_sql!( + t.serializable(), + "BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE" + ); + assert_sql!( + t.serializable().deferrable().read_only(), + "BEGIN TRANSACTION ISOLATION LEVEL SERIALIZABLE READ ONLY DEFERRABLE" + ); +} diff --git a/collector/benchmarks/diesel/diesel/src/pg/types/array.rs b/collector/benchmarks/diesel/diesel/src/pg/types/array.rs new file mode 100644 index 000000000..7d799b325 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/pg/types/array.rs @@ -0,0 +1,143 @@ +use byteorder::{NetworkEndian, ReadBytesExt, WriteBytesExt}; +use std::fmt; +use std::io::Write; + +use crate::deserialize::{self, FromSql}; +use crate::pg::{Pg, PgMetadataLookup, PgTypeMetadata, PgValue}; +use crate::serialize::{self, IsNull, Output, ToSql}; +use crate::sql_types::{Array, HasSqlType, Nullable}; + +impl HasSqlType> for Pg +where + Pg: HasSqlType, +{ + fn metadata(lookup: &PgMetadataLookup) -> PgTypeMetadata { + PgTypeMetadata { + oid: >::metadata(lookup).array_oid, + array_oid: 0, + } + } +} + +impl FromSql, Pg> for Vec +where + T: FromSql, +{ + fn from_sql(value: PgValue<'_>) -> deserialize::Result { + let mut bytes = value.as_bytes(); + let num_dimensions = bytes.read_i32::()?; + let has_null = bytes.read_i32::()? != 0; + let _oid = bytes.read_i32::()?; + + if num_dimensions == 0 { + return Ok(Vec::new()); + } + + let num_elements = bytes.read_i32::()?; + let _lower_bound = bytes.read_i32::()?; + + if num_dimensions != 1 { + return Err("multi-dimensional arrays are not supported".into()); + } + + (0..num_elements) + .map(|_| { + let elem_size = bytes.read_i32::()?; + if has_null && elem_size == -1 { + T::from_nullable_sql(None) + } else { + let (elem_bytes, new_bytes) = bytes.split_at(elem_size as usize); + bytes = new_bytes; + T::from_sql(PgValue::new(elem_bytes, value.get_oid())) + } + }) + .collect() + } +} + +use crate::expression::bound::Bound; +use crate::expression::AsExpression; + +macro_rules! array_as_expression { + ($ty:ty, $sql_type:ty) => { + impl<'a, 'b, ST, T> AsExpression<$sql_type> for $ty { + type Expression = Bound<$sql_type, Self>; + + fn as_expression(self) -> Self::Expression { + Bound::new(self) + } + } + }; +} + +array_as_expression!(&'a [T], Array); +array_as_expression!(&'a [T], Nullable>); +array_as_expression!(&'a &'b [T], Array); +array_as_expression!(&'a &'b [T], Nullable>); +array_as_expression!(Vec, Array); +array_as_expression!(Vec, Nullable>); +array_as_expression!(&'a Vec, Array); +array_as_expression!(&'a Vec, Nullable>); +array_as_expression!(&'a &'b Vec, Array); +array_as_expression!(&'a &'b Vec, Nullable>); + +impl ToSql, Pg> for [T] +where + Pg: HasSqlType, + T: ToSql, +{ + fn to_sql(&self, out: &mut Output) -> serialize::Result { + let num_dimensions = 1; + out.write_i32::(num_dimensions)?; + let flags = 0; + out.write_i32::(flags)?; + let element_oid = Pg::metadata(out.metadata_lookup()).oid; + out.write_u32::(element_oid)?; + out.write_i32::(self.len() as i32)?; + let lower_bound = 1; + out.write_i32::(lower_bound)?; + + let mut buffer = out.with_buffer(Vec::new()); + for elem in self.iter() { + let is_null = elem.to_sql(&mut buffer)?; + if let IsNull::No = is_null { + out.write_i32::(buffer.len() as i32)?; + out.write_all(&buffer)?; + buffer.clear(); + } else { + // https://github.com/postgres/postgres/blob/82f8107b92c9104ec9d9465f3f6a4c6dab4c124a/src/backend/utils/adt/arrayfuncs.c#L1461 + out.write_i32::(-1)?; + } + } + + Ok(IsNull::No) + } +} + +impl ToSql>, Pg> for [T] +where + [T]: ToSql, Pg>, +{ + fn to_sql(&self, out: &mut Output) -> serialize::Result { + ToSql::, Pg>::to_sql(self, out) + } +} + +impl ToSql, Pg> for Vec +where + [T]: ToSql, Pg>, + T: fmt::Debug, +{ + fn to_sql(&self, out: &mut Output) -> serialize::Result { + (self as &[T]).to_sql(out) + } +} + +impl ToSql>, Pg> for Vec +where + Vec: ToSql, Pg>, +{ + fn to_sql(&self, out: &mut Output) -> serialize::Result { + ToSql::, Pg>::to_sql(self, out) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/pg/types/date_and_time/chrono.rs b/collector/benchmarks/diesel/diesel/src/pg/types/date_and_time/chrono.rs new file mode 100644 index 000000000..7d4d4c061 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/pg/types/date_and_time/chrono.rs @@ -0,0 +1,312 @@ +//! This module makes it possible to map `chrono::DateTime` values to postgres `Date` +//! and `Timestamp` fields. It is enabled with the `chrono` feature. + +extern crate chrono; + +use self::chrono::naive::MAX_DATE; +use self::chrono::{DateTime, Duration, Local, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc}; +use std::io::Write; + +use super::{PgDate, PgTime, PgTimestamp}; +use crate::deserialize::{self, FromSql}; +use crate::pg::{Pg, PgValue}; +use crate::serialize::{self, Output, ToSql}; +use crate::sql_types::{Date, Time, Timestamp, Timestamptz}; + +// Postgres timestamps start from January 1st 2000. +fn pg_epoch() -> NaiveDateTime { + NaiveDate::from_ymd(2000, 1, 1).and_hms(0, 0, 0) +} + +impl FromSql for NaiveDateTime { + fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { + let PgTimestamp(offset) = FromSql::::from_sql(bytes)?; + match pg_epoch().checked_add_signed(Duration::microseconds(offset)) { + Some(v) => Ok(v), + None => { + let message = "Tried to deserialize a timestamp that is too large for Chrono"; + Err(message.into()) + } + } + } +} + +impl ToSql for NaiveDateTime { + fn to_sql(&self, out: &mut Output) -> serialize::Result { + let time = match (self.signed_duration_since(pg_epoch())).num_microseconds() { + Some(time) => time, + None => { + let error_message = + format!("{:?} as microseconds is too large to fit in an i64", self); + return Err(error_message.into()); + } + }; + ToSql::::to_sql(&PgTimestamp(time), out) + } +} + +impl FromSql for NaiveDateTime { + fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { + FromSql::::from_sql(bytes) + } +} + +impl ToSql for NaiveDateTime { + fn to_sql(&self, out: &mut Output) -> serialize::Result { + ToSql::::to_sql(self, out) + } +} + +impl FromSql for DateTime { + fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { + let naive_date_time = >::from_sql(bytes)?; + Ok(DateTime::from_utc(naive_date_time, Utc)) + } +} + +impl FromSql for DateTime { + fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { + let naive_date_time = >::from_sql(bytes)?; + Ok(Local::from_utc_datetime(&Local, &naive_date_time)) + } +} + +impl ToSql for DateTime { + fn to_sql(&self, out: &mut Output) -> serialize::Result { + ToSql::::to_sql(&self.naive_utc(), out) + } +} + +fn midnight() -> NaiveTime { + NaiveTime::from_hms(0, 0, 0) +} + +impl ToSql for NaiveTime { + fn to_sql(&self, out: &mut Output) -> serialize::Result { + let duration = self.signed_duration_since(midnight()); + match duration.num_microseconds() { + Some(offset) => ToSql::::to_sql(&PgTime(offset), out), + None => unreachable!(), + } + } +} + +impl FromSql for NaiveTime { + fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { + let PgTime(offset) = FromSql::::from_sql(bytes)?; + let duration = Duration::microseconds(offset); + Ok(midnight() + duration) + } +} + +fn pg_epoch_date() -> NaiveDate { + NaiveDate::from_ymd(2000, 1, 1) +} + +impl ToSql for NaiveDate { + fn to_sql(&self, out: &mut Output) -> serialize::Result { + let days_since_epoch = self.signed_duration_since(pg_epoch_date()).num_days(); + ToSql::::to_sql(&PgDate(days_since_epoch as i32), out) + } +} + +impl FromSql for NaiveDate { + fn from_sql(bytes: PgValue<'_>) -> deserialize::Result { + let PgDate(offset) = FromSql::::from_sql(bytes)?; + match pg_epoch_date().checked_add_signed(Duration::days(i64::from(offset))) { + Some(date) => Ok(date), + None => { + let error_message = format!("Chrono can only represent dates up to {:?}", MAX_DATE); + Err(error_message.into()) + } + } + } +} + +#[cfg(test)] +mod tests { + extern crate chrono; + extern crate dotenv; + + use self::chrono::naive::MAX_DATE; + use self::chrono::{Duration, FixedOffset, NaiveDate, NaiveTime, TimeZone, Utc}; + use self::dotenv::dotenv; + + use crate::dsl::{now, sql}; + use crate::prelude::*; + use crate::select; + use crate::sql_types::{Date, Time, Timestamp, Timestamptz}; + + fn connection() -> PgConnection { + dotenv().ok(); + + let connection_url = ::std::env::var("PG_DATABASE_URL") + .or_else(|_| ::std::env::var("DATABASE_URL")) + .expect("DATABASE_URL must be set in order to run tests"); + PgConnection::establish(&connection_url).unwrap() + } + + #[test] + fn unix_epoch_encodes_correctly() { + let connection = connection(); + let time = NaiveDate::from_ymd(1970, 1, 1).and_hms(0, 0, 0); + let query = select(sql::("'1970-01-01'").eq(time)); + assert!(query.get_result::(&connection).unwrap()); + } + + #[test] + fn unix_epoch_encodes_correctly_with_utc_timezone() { + let connection = connection(); + let time = Utc.ymd(1970, 1, 1).and_hms(0, 0, 0); + let query = select(sql::("'1970-01-01Z'::timestamptz").eq(time)); + assert!(query.get_result::(&connection).unwrap()); + } + + #[test] + fn unix_epoch_encodes_correctly_with_timezone() { + let connection = connection(); + let time = FixedOffset::west(3600).ymd(1970, 1, 1).and_hms(0, 0, 0); + let query = select(sql::("'1970-01-01 01:00:00Z'::timestamptz").eq(time)); + assert!(query.get_result::(&connection).unwrap()); + } + + #[test] + fn unix_epoch_decodes_correctly() { + let connection = connection(); + let time = NaiveDate::from_ymd(1970, 1, 1).and_hms(0, 0, 0); + let epoch_from_sql = + select(sql::("'1970-01-01'::timestamp")).get_result(&connection); + assert_eq!(Ok(time), epoch_from_sql); + } + + #[test] + fn unix_epoch_decodes_correctly_with_timezone() { + let connection = connection(); + let time = Utc.ymd(1970, 1, 1).and_hms(0, 0, 0); + let epoch_from_sql = + select(sql::("'1970-01-01Z'::timestamptz")).get_result(&connection); + assert_eq!(Ok(time), epoch_from_sql); + } + + #[test] + fn times_relative_to_now_encode_correctly() { + let connection = connection(); + let time = Utc::now().naive_utc() + Duration::seconds(60); + let query = select(now.at_time_zone("utc").lt(time)); + assert!(query.get_result::(&connection).unwrap()); + + let time = Utc::now().naive_utc() - Duration::seconds(60); + let query = select(now.at_time_zone("utc").gt(time)); + assert!(query.get_result::(&connection).unwrap()); + } + + #[test] + fn times_with_timezones_round_trip_after_conversion() { + let connection = connection(); + let time = FixedOffset::east(3600).ymd(2016, 1, 2).and_hms(1, 0, 0); + let expected = NaiveDate::from_ymd(2016, 1, 1).and_hms(20, 0, 0); + let query = select(time.into_sql::().at_time_zone("EDT")); + assert_eq!(Ok(expected), query.get_result(&connection)); + } + + #[test] + fn times_of_day_encode_correctly() { + let connection = connection(); + + let midnight = NaiveTime::from_hms(0, 0, 0); + let query = select(sql::
+ Expression, + U: Query, + { + InsertStatement::new( + self.target, + self.records.with_columns(columns), + self.operator, + self.returning, + ) + } +} + +impl QueryFragment for InsertStatement +where + DB: Backend, + T: Table, + T::FromClause: QueryFragment, + U: QueryFragment + CanInsertInSingleQuery, + Op: QueryFragment, + Ret: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.unsafe_to_cache_prepared(); + + if self.records.rows_to_insert() == Some(0) { + out.push_sql("SELECT 1 FROM "); + self.target.from_clause().walk_ast(out.reborrow())?; + out.push_sql(" WHERE 1=0"); + return Ok(()); + } + + self.operator.walk_ast(out.reborrow())?; + out.push_sql(" INTO "); + self.target.from_clause().walk_ast(out.reborrow())?; + out.push_sql(" "); + self.records.walk_ast(out.reborrow())?; + self.returning.walk_ast(out.reborrow())?; + Ok(()) + } +} + +#[cfg(feature = "sqlite")] +impl<'a, T, U, Op, C> ExecuteDsl for InsertStatement, Op> +where + C: Connection, + &'a U: Insertable, + InsertStatement>::Values, Op>: QueryFragment, + T: Copy, + Op: Copy, +{ + fn execute(query: Self, conn: &C) -> QueryResult { + conn.transaction(|| { + let mut result = 0; + for record in query.records.records { + result += InsertStatement::new( + query.target, + record.values(), + query.operator, + query.returning, + ) + .execute(conn)?; + } + Ok(result) + }) + } +} + +#[cfg(feature = "sqlite")] +impl<'a, T, U, Op> Display for DebugQuery<'a, InsertStatement, Op>, Sqlite> +where + &'a U: Insertable, + for<'b> DebugQuery<'b, InsertStatement>::Values, Op>, Sqlite>: + Display, + T: Copy, + Op: Copy, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + writeln!(f, "BEGIN;")?; + for record in self.query.records.records { + let stmt = InsertStatement::new( + self.query.target, + record.values(), + self.query.operator, + self.query.returning, + ); + + writeln!(f, "{}", crate::debug_query::(&stmt))?; + } + writeln!(f, "COMMIT;")?; + Ok(()) + } +} + +#[cfg(feature = "sqlite")] +impl<'a, T, U, Op> Debug for DebugQuery<'a, InsertStatement, Op>, Sqlite> +where + &'a U: Insertable, + for<'b> DebugQuery<'b, InsertStatement>::Values, Op>, Sqlite>: + Display, + T: Copy, + Op: Copy, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut statements = Vec::with_capacity(self.query.records.records.len() + 2); + statements.push("BEGIN".into()); + for record in self.query.records.records { + let stmt = InsertStatement::new( + self.query.target, + record.values(), + self.query.operator, + self.query.returning, + ); + statements.push(format!("{}", crate::debug_query::(&stmt))); + } + statements.push("COMMIT".into()); + + f.debug_struct("Query") + .field("sql", &statements) + .field("binds", &[] as &[i32; 0]) + .finish() + } +} + +#[cfg(feature = "sqlite")] +impl ExecuteDsl + for InsertStatement, T>, Op> +where + C: Connection, + InsertStatement, Op>: QueryFragment, + T: Copy, + Op: Copy, +{ + fn execute(query: Self, conn: &C) -> QueryResult { + conn.transaction(|| { + let mut result = 0; + for value in query.records.values { + result += + InsertStatement::new(query.target, value, query.operator, query.returning) + .execute(conn)?; + } + Ok(result) + }) + } +} + +#[cfg(feature = "sqlite")] +impl<'a, T, U, Op> Display + for DebugQuery<'a, InsertStatement, T>, Op>, Sqlite> +where + for<'b> DebugQuery<'b, InsertStatement, Op>, Sqlite>: Display, + T: Copy, + Op: Copy, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + writeln!(f, "BEGIN;")?; + for value in &self.query.records.values { + let stmt = InsertStatement::new( + self.query.target, + value, + self.query.operator, + self.query.returning, + ); + + writeln!(f, "{}", crate::debug_query::(&stmt))?; + } + writeln!(f, "COMMIT;")?; + Ok(()) + } +} + +#[cfg(feature = "sqlite")] +impl<'a, T, U, Op> Debug + for DebugQuery<'a, InsertStatement, T>, Op>, Sqlite> +where + for<'b> DebugQuery<'b, InsertStatement, Op>, Sqlite>: Display, + T: Copy, + Op: Copy, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut statements = Vec::with_capacity(self.query.records.values.len() + 2); + statements.push("BEGIN".into()); + + for value in &self.query.records.values { + let stmt = InsertStatement::new( + self.query.target, + value, + self.query.operator, + self.query.returning, + ); + statements.push(format!("{}", crate::debug_query::(&stmt))); + } + statements.push("COMMIT".into()); + + f.debug_struct("Query") + .field("sql", &statements) + .field("binds", &[] as &[i32; 0]) + .finish() + } +} + +impl QueryId for InsertStatement { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +impl AsQuery for InsertStatement +where + T: Table, + InsertStatement>: Query, +{ + type SqlType = ::SqlType; + type Query = InsertStatement>; + + fn as_query(self) -> Self::Query { + self.returning(T::all_columns()) + } +} + +impl Query for InsertStatement> +where + Ret: Expression + SelectableExpression + NonAggregate, +{ + type SqlType = Ret::SqlType; +} + +impl RunQueryDsl for InsertStatement {} + +impl InsertStatement { + /// Specify what expression is returned after execution of the `insert`. + /// # Examples + /// + /// ### Inserting records: + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # #[cfg(feature = "postgres")] + /// # fn main() { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let inserted_names = diesel::insert_into(users) + /// .values(&vec![name.eq("Timmy"), name.eq("Jimmy")]) + /// .returning(name) + /// .get_results(&connection); + /// assert_eq!(Ok(vec!["Timmy".to_string(), "Jimmy".to_string()]), inserted_names); + /// # } + /// # #[cfg(not(feature = "postgres"))] + /// # fn main() {} + /// ``` + pub fn returning(self, returns: E) -> InsertStatement> + where + InsertStatement>: Query, + { + InsertStatement::new( + self.target, + self.records, + self.operator, + ReturningClause(returns), + ) + } +} + +#[derive(Debug, Copy, Clone, QueryId)] +#[doc(hidden)] +pub struct Insert; + +impl QueryFragment for Insert { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("INSERT"); + Ok(()) + } +} + +#[derive(Debug, Copy, Clone, QueryId)] +#[doc(hidden)] +pub struct InsertOrIgnore; + +#[cfg(feature = "sqlite")] +impl QueryFragment for InsertOrIgnore { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("INSERT OR IGNORE"); + Ok(()) + } +} + +#[cfg(feature = "mysql")] +impl QueryFragment for InsertOrIgnore { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("INSERT IGNORE"); + Ok(()) + } +} + +#[derive(Debug, Copy, Clone)] +#[doc(hidden)] +pub struct Replace; + +#[cfg(feature = "sqlite")] +impl QueryFragment for Replace { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("REPLACE"); + Ok(()) + } +} + +#[cfg(feature = "mysql")] +impl QueryFragment for Replace { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("REPLACE"); + Ok(()) + } +} + +/// Marker trait to indicate that no additional operations have been added +/// to a record for insert. +/// +/// This is used to prevent things like +/// `.on_conflict_do_nothing().on_conflict_do_nothing()` +/// from compiling. +pub trait UndecoratedInsertRecord
{} + +impl<'a, T, Tab> UndecoratedInsertRecord for &'a T where + T: ?Sized + UndecoratedInsertRecord +{ +} + +impl UndecoratedInsertRecord for ColumnInsertValue where T: Column {} + +impl UndecoratedInsertRecord
for [T] where T: UndecoratedInsertRecord
{} + +impl<'a, T, Table> UndecoratedInsertRecord
for BatchInsert<'a, T, Table> where + T: UndecoratedInsertRecord
+{ +} + +impl UndecoratedInsertRecord
for OwnedBatchInsert where + T: UndecoratedInsertRecord
+{ +} + +impl UndecoratedInsertRecord
for Vec where [T]: UndecoratedInsertRecord
{} + +impl UndecoratedInsertRecord for Eq where Lhs: Column {} + +impl UndecoratedInsertRecord for Option> where + Eq: UndecoratedInsertRecord +{ +} + +impl UndecoratedInsertRecord for Grouped> where Lhs: Column {} + +impl UndecoratedInsertRecord for Option>> where + Eq: UndecoratedInsertRecord +{ +} + +impl UndecoratedInsertRecord
for ValuesClause where + T: UndecoratedInsertRecord
+{ +} + +#[derive(Debug, Clone, Copy)] +#[doc(hidden)] +pub struct DefaultValues; + +impl CanInsertInSingleQuery for DefaultValues { + fn rows_to_insert(&self) -> Option { + Some(1) + } +} + +impl<'a, Tab> Insertable for &'a DefaultValues { + type Values = DefaultValues; + + fn values(self) -> Self::Values { + *self + } +} + +impl QueryFragment for DefaultValues +where + DB: Backend + Any, +{ + #[cfg(feature = "mysql")] + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + // This can be less hacky once stabilization lands + if TypeId::of::() == TypeId::of::() { + out.push_sql("() VALUES ()"); + } else { + out.push_sql("DEFAULT VALUES"); + } + Ok(()) + } + + #[cfg(not(feature = "mysql"))] + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("DEFAULT VALUES"); + Ok(()) + } +} + +#[doc(hidden)] +#[derive(Debug, Clone, Copy)] +pub struct ValuesClause { + pub values: T, + _marker: PhantomData, +} + +impl Default for ValuesClause { + fn default() -> Self { + Self::new(T::default()) + } +} + +impl ValuesClause { + pub(crate) fn new(values: T) -> Self { + Self { + values, + _marker: PhantomData, + } + } +} + +impl CanInsertInSingleQuery for ValuesClause +where + DB: Backend, + T: CanInsertInSingleQuery, +{ + fn rows_to_insert(&self) -> Option { + self.values.rows_to_insert() + } +} + +impl QueryFragment for ValuesClause +where + DB: Backend, + Tab: Table, + T: InsertValues, + DefaultValues: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + if self.values.is_noop()? { + DefaultValues.walk_ast(out)?; + } else { + out.push_sql("("); + self.values.column_names(out.reborrow())?; + out.push_sql(") VALUES ("); + self.values.walk_ast(out.reborrow())?; + out.push_sql(")"); + } + Ok(()) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/limit_clause.rs b/collector/benchmarks/diesel/diesel/src/query_builder/limit_clause.rs new file mode 100644 index 000000000..fd83de786 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/limit_clause.rs @@ -0,0 +1,11 @@ +simple_clause!( + /// A query node indicating the absence of a limit clause + /// + /// This type is only relevant for implementing custom backends + NoLimitClause, + /// A query node representing a limit clause + /// + /// This type is only relevant for implementing custom backends + LimitClause, + " LIMIT " +); diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/limit_offset_clause.rs b/collector/benchmarks/diesel/diesel/src/query_builder/limit_offset_clause.rs new file mode 100644 index 000000000..e167284b7 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/limit_offset_clause.rs @@ -0,0 +1,24 @@ +use super::QueryFragment; +use crate::query_builder::QueryId; + +/// A helper query node that contains both limit and offset clauses +/// +/// This type is only relevant for implementing custom backends +#[derive(Debug, Clone, Copy, QueryId)] +pub struct LimitOffsetClause { + /// The limit clause + pub limit_clause: Limit, + /// The offset clause + pub offset_clause: Offset, +} + +/// A boxed variant of [`LimitOffsetClause`](../struct.LimitOffsetClause.html) +/// +/// This type is only relevant for implementing custom backends +#[allow(missing_debug_implementations)] +pub struct BoxedLimitOffsetClause<'a, DB> { + /// The limit clause + pub limit: Option + Send + 'a>>, + /// The offset clause + pub offset: Option + Send + 'a>>, +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/locking_clause.rs b/collector/benchmarks/diesel/diesel/src/query_builder/locking_clause.rs new file mode 100644 index 000000000..8605ac930 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/locking_clause.rs @@ -0,0 +1,61 @@ +use crate::backend::Backend; +use crate::query_builder::{AstPass, QueryFragment, QueryId}; +use crate::result::QueryResult; + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct NoLockingClause; + +impl QueryFragment for NoLockingClause { + fn walk_ast(&self, _: AstPass) -> QueryResult<()> { + Ok(()) + } +} + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct LockingClause { + pub(crate) lock_mode: LockMode, + modifier: Modifier, +} + +impl LockingClause { + pub(crate) fn new(lock_mode: LockMode, modifier: Modifier) -> Self { + LockingClause { + lock_mode, + modifier, + } + } +} + +impl, M: QueryFragment> QueryFragment + for LockingClause +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + self.lock_mode.walk_ast(out.reborrow())?; + self.modifier.walk_ast(out.reborrow()) + } +} + +// `LockMode` parameters +// All the different types of row locks that can be acquired. +#[derive(Debug, Clone, Copy, QueryId)] +pub struct ForUpdate; + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct ForNoKeyUpdate; + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct ForShare; + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct ForKeyShare; + +// Modifiers +// To be used in conjunction with a lock mode. +#[derive(Debug, Clone, Copy, QueryId)] +pub struct NoModifier; + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct SkipLocked; + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct NoWait; diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/mod.rs b/collector/benchmarks/diesel/diesel/src/query_builder/mod.rs new file mode 100644 index 000000000..5c8f44dcb --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/mod.rs @@ -0,0 +1,336 @@ +//! Contains traits responsible for the actual construction of SQL statements +//! +//! The types in this module are part of Diesel's public API, but are generally +//! only useful for implementing Diesel plugins. Applications should generally +//! not need to care about the types inside of this module. + +#[macro_use] +mod query_id; +#[macro_use] +mod clause_macro; + +mod ast_pass; +pub mod bind_collector; +mod debug_query; +mod delete_statement; +mod distinct_clause; +#[doc(hidden)] +pub mod functions; +mod group_by_clause; +mod insert_statement; +pub(crate) mod limit_clause; +pub(crate) mod limit_offset_clause; +pub(crate) mod locking_clause; +#[doc(hidden)] +pub mod nodes; +pub(crate) mod offset_clause; +mod order_clause; +mod returning_clause; +pub(crate) mod select_clause; +mod select_statement; +mod sql_query; +mod update_statement; +pub(crate) mod upsert; +mod where_clause; + +pub use self::ast_pass::AstPass; +pub use self::bind_collector::BindCollector; +pub use self::debug_query::DebugQuery; +pub use self::delete_statement::{BoxedDeleteStatement, DeleteStatement}; +#[doc(inline)] +pub use self::insert_statement::{ + IncompleteInsertStatement, InsertStatement, UndecoratedInsertRecord, ValuesClause, +}; +pub use self::query_id::QueryId; +#[doc(inline)] +pub use self::select_clause::{ + IntoBoxedSelectClause, SelectClauseExpression, SelectClauseQueryFragment, +}; +#[doc(hidden)] +pub use self::select_statement::{BoxedSelectStatement, SelectStatement}; +pub use self::sql_query::{BoxedSqlQuery, SqlQuery}; +#[doc(inline)] +pub use self::update_statement::{ + AsChangeset, BoxedUpdateStatement, IntoUpdateTarget, UpdateStatement, UpdateTarget, +}; +pub use self::upsert::on_conflict_target_decorations::DecoratableTarget; + +pub use self::limit_clause::{LimitClause, NoLimitClause}; +pub use self::limit_offset_clause::{BoxedLimitOffsetClause, LimitOffsetClause}; +pub use self::offset_clause::{NoOffsetClause, OffsetClause}; + +pub(crate) use self::insert_statement::ColumnList; + +use std::error::Error; + +use crate::backend::Backend; +use crate::result::QueryResult; + +#[doc(hidden)] +pub type Binds = Vec>>; +/// A specialized Result type used with the query builder. +pub type BuildQueryResult = Result<(), Box>; + +/// Constructs a SQL query from a Diesel AST. +/// +/// The only reason you should ever need to interact with this trait is if you +/// are extending Diesel with support for a new backend. Plugins which extend +/// the query builder with new capabilities will interact with [`AstPass`] +/// instead. +/// +/// [`AstPass`]: struct.AstPass.html +pub trait QueryBuilder { + /// Add `sql` to the end of the query being constructed. + fn push_sql(&mut self, sql: &str); + + /// Quote `identifier`, and add it to the end of the query being + /// constructed. + fn push_identifier(&mut self, identifier: &str) -> QueryResult<()>; + + /// Add a placeholder for a bind parameter to the end of the query being + /// constructed. + fn push_bind_param(&mut self); + + /// Increases the internal counter for bind parameters without adding the + /// bind parameter itself to the query + fn push_bind_param_value_only(&mut self) {} + + /// Returns the constructed SQL query. + fn finish(self) -> String; +} + +/// A complete SQL query with a return type. +/// +/// This can be a select statement, or a command such as `update` or `insert` +/// with a `RETURNING` clause. Unlike [`Expression`], types implementing this +/// trait are guaranteed to be executable on their own. +/// +/// A type which doesn't implement this trait may still represent a complete SQL +/// query. For example, an `INSERT` statement without a `RETURNING` clause will +/// not implement this trait, but can still be executed. +/// +/// [`Expression`]: ../expression/trait.Expression.html +pub trait Query { + /// The SQL type that this query represents. + /// + /// This is the SQL type of the `SELECT` clause for select statements, and + /// the SQL type of the `RETURNING` clause for insert, update, or delete + /// statements. + type SqlType; +} + +impl<'a, T: Query> Query for &'a T { + type SqlType = T::SqlType; +} + +/// Indicates that a type is a `SELECT` statement. +/// +/// This trait differs from `Query` in two ways: +/// - It is implemented only for select statements, rather than all queries +/// which return a value. +/// - It has looser constraints. A type implementing `SelectQuery` is known to +/// be potentially valid if used as a subselect, but it is not necessarily +/// able to be executed. +pub trait SelectQuery { + /// The SQL type of the `SELECT` clause + type SqlType; +} + +/// An untyped fragment of SQL. +/// +/// This may be a complete SQL command (such as an update statement without a +/// `RETURNING` clause), or a subsection (such as our internal types used to +/// represent a `WHERE` clause). Implementations of [`ExecuteDsl`] and +/// [`LoadQuery`] will generally require that this trait be implemented. +/// +/// [`ExecuteDsl`]: ../query_dsl/methods/trait.ExecuteDsl.html +/// [`LoadQuery`]: ../query_dsl/methods/trait.LoadQuery.html +pub trait QueryFragment { + /// Walk over this `QueryFragment` for all passes. + /// + /// This method is where the actual behavior of an AST node is implemented. + /// This method will contain the behavior required for all possible AST + /// passes. See [`AstPass`] for more details. + /// + /// [`AstPass`]: struct.AstPass.html + fn walk_ast(&self, pass: AstPass) -> QueryResult<()>; + + /// Converts this `QueryFragment` to its SQL representation. + /// + /// This method should only be called by implementations of `Connection`. + fn to_sql(&self, out: &mut DB::QueryBuilder) -> QueryResult<()> { + self.walk_ast(AstPass::to_sql(out)) + } + + /// Serializes all bind parameters in this query. + /// + /// A bind parameter is a value which is sent separately from the query + /// itself. It is represented in SQL with a placeholder such as `?` or `$1`. + /// + /// This method should only be called by implementations of `Connection`. + fn collect_binds( + &self, + out: &mut DB::BindCollector, + metadata_lookup: &DB::MetadataLookup, + ) -> QueryResult<()> { + self.walk_ast(AstPass::collect_binds(out, metadata_lookup)) + } + + /// Is this query safe to store in the prepared statement cache? + /// + /// In order to keep our prepared statement cache at a reasonable size, we + /// avoid caching any queries which represent a potentially unbounded number + /// of SQL queries. Generally this will only return `true` for queries for + /// which `to_sql` will always construct exactly identical SQL. + /// + /// Some examples of where this method will return `false` are: + /// + /// - `SqlLiteral` (We don't know if the SQL was constructed dynamically, so + /// we must assume that it was) + /// - `In` and `NotIn` (Each value requires a separate bind param + /// placeholder) + /// + /// This method should only be called by implementations of `Connection`. + fn is_safe_to_cache_prepared(&self) -> QueryResult { + let mut result = true; + self.walk_ast(AstPass::is_safe_to_cache_prepared(&mut result))?; + Ok(result) + } + + #[doc(hidden)] + /// Does walking this AST have any effect? + fn is_noop(&self) -> QueryResult { + let mut result = true; + self.walk_ast(AstPass::is_noop(&mut result))?; + Ok(result) + } +} + +impl QueryFragment for Box +where + DB: Backend, + T: QueryFragment, +{ + fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + QueryFragment::walk_ast(&**self, pass) + } +} + +impl<'a, T: ?Sized, DB> QueryFragment for &'a T +where + DB: Backend, + T: QueryFragment, +{ + fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + QueryFragment::walk_ast(&**self, pass) + } +} + +impl QueryFragment for () { + fn walk_ast(&self, _: AstPass) -> QueryResult<()> { + Ok(()) + } +} + +impl QueryFragment for Option +where + DB: Backend, + T: QueryFragment, +{ + fn walk_ast(&self, out: AstPass) -> QueryResult<()> { + match *self { + Some(ref c) => c.walk_ast(out), + None => Ok(()), + } + } +} + +/// A trait used to construct type erased boxed variant of the current query node +/// +/// Mainly useful for implementing third party backends +pub trait IntoBoxedClause<'a, DB> { + /// Resulting type + type BoxedClause; + + /// Convert the given query node in it's boxed representation + fn into_boxed(self) -> Self::BoxedClause; +} + +/// Types that can be converted into a complete, typed SQL query. +/// +/// This is used internally to automatically add the right select clause when +/// none is specified, or to automatically add `RETURNING *` in certain contexts. +/// +/// A type which implements this trait is guaranteed to be valid for execution. +pub trait AsQuery { + /// The SQL type of `Self::Query` + type SqlType; + + /// What kind of query does this type represent? + type Query: Query; + + /// Converts a type which semantically represents a SQL query into the + /// actual query being executed. See the trait level docs for more. + fn as_query(self) -> Self::Query; +} + +impl AsQuery for T { + type SqlType = ::SqlType; + type Query = Self; + + fn as_query(self) -> Self::Query { + self + } +} + +/// Takes a query `QueryFragment` expression as an argument and returns a type +/// that implements `fmt::Display` and `fmt::Debug` to show the query. +/// +/// The `Display` implementation will show the exact query being sent to the +/// server, with a comment showing the values of the bind parameters. The +/// `Debug` implementation will include the same information in a more +/// structured form, and respects pretty printing. +/// +/// # Example +/// +/// ### Returning SQL from a count statement: +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # +/// # use diesel::*; +/// # use schema::*; +/// # +/// # fn main() { +/// # use schema::users::dsl::*; +/// let sql = debug_query::(&users.count()).to_string(); +/// # if cfg!(feature = "postgres") { +/// # assert_eq!(sql, r#"SELECT COUNT(*) FROM "users" -- binds: []"#); +/// # } else { +/// assert_eq!(sql, "SELECT COUNT(*) FROM `users` -- binds: []"); +/// # } +/// +/// let query = users.find(1); +/// let debug = debug_query::(&query); +/// # if cfg!(feature = "postgres") { +/// # assert_eq!(debug.to_string(), "SELECT \"users\".\"id\", \"users\".\"name\" \ +/// # FROM \"users\" WHERE (\"users\".\"id\" = $1) -- binds: [1]"); +/// # } else { +/// assert_eq!(debug.to_string(), "SELECT `users`.`id`, `users`.`name` FROM `users` \ +/// WHERE (`users`.`id` = ?) -- binds: [1]"); +/// # } +/// +/// let debug = format!("{:?}", debug); +/// # if !cfg!(feature = "postgres") { // Escaping that string is a pain +/// let expected = "Query { \ +/// sql: \"SELECT `users`.`id`, `users`.`name` FROM `users` WHERE \ +/// (`users`.`id` = ?)\", \ +/// binds: [1] \ +/// }"; +/// assert_eq!(debug, expected); +/// # } +/// # } +/// ``` +pub fn debug_query(query: &T) -> DebugQuery { + DebugQuery::new(query) +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/nodes/mod.rs b/collector/benchmarks/diesel/diesel/src/query_builder/nodes/mod.rs new file mode 100644 index 000000000..7a1787095 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/nodes/mod.rs @@ -0,0 +1,43 @@ +use crate::backend::Backend; +use crate::query_builder::*; +use crate::result::QueryResult; + +#[derive(Debug, Copy, Clone)] +pub struct Identifier<'a>(pub &'a str); + +impl<'a, DB: Backend> QueryFragment for Identifier<'a> { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_identifier(self.0) + } +} + +#[derive(Debug, Copy, Clone)] +pub struct InfixNode<'a, T, U> { + lhs: T, + rhs: U, + middle: &'a str, +} + +impl<'a, T, U> InfixNode<'a, T, U> { + pub fn new(lhs: T, rhs: U, middle: &'a str) -> Self { + InfixNode { + lhs: lhs, + rhs: rhs, + middle: middle, + } + } +} + +impl<'a, T, U, DB> QueryFragment for InfixNode<'a, T, U> +where + DB: Backend, + T: QueryFragment, + U: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + self.lhs.walk_ast(out.reborrow())?; + out.push_sql(self.middle); + self.rhs.walk_ast(out.reborrow())?; + Ok(()) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/offset_clause.rs b/collector/benchmarks/diesel/diesel/src/query_builder/offset_clause.rs new file mode 100644 index 000000000..3cb72e09f --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/offset_clause.rs @@ -0,0 +1,11 @@ +simple_clause!( + /// A query node indicating the absence of an offset clause + /// + /// This type is only relevant for implementing custom backends + NoOffsetClause, + /// A query node representing an offset clause + /// + /// This type is only relevant for implementing custom backends + OffsetClause, + " OFFSET " +); diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/order_clause.rs b/collector/benchmarks/diesel/diesel/src/query_builder/order_clause.rs new file mode 100644 index 000000000..deea8dc71 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/order_clause.rs @@ -0,0 +1,20 @@ +simple_clause!(NoOrderClause, OrderClause, " ORDER BY "); + +impl<'a, DB, Expr> Into + Send + 'a>>> for OrderClause +where + DB: Backend, + Expr: QueryFragment + Send + 'a, +{ + fn into(self) -> Option + Send + 'a>> { + Some(Box::new(self.0)) + } +} + +impl<'a, DB> Into + Send + 'a>>> for NoOrderClause +where + DB: Backend, +{ + fn into(self) -> Option + Send + 'a>> { + None + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/query_id.rs b/collector/benchmarks/diesel/diesel/src/query_builder/query_id.rs new file mode 100644 index 000000000..aa2ccb957 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/query_id.rs @@ -0,0 +1,144 @@ +use super::QueryFragment; +use std::any::{Any, TypeId}; + +/// Uniquely identifies queries by their type for the purpose of prepared +/// statement caching. +/// +/// All types which implement `QueryFragment` should also implement this trait +/// (It is not an actual supertrait of `QueryFragment` for boxing purposes). +/// +/// See the documentation of [the `QueryId` type] and [`HAS_STATIC_QUERY_ID`] +/// for more details. +/// +/// [the `QueryId` type]: #associatedtype.QueryId +/// [`HAS_STATIC_QUERY_ID`]: #associatedconstant.HAS_STATIC_QUERY_ID +/// +/// ### Deriving +/// +/// This trait can [be automatically derived](derive.QueryId.html) +/// by Diesel. +/// For example, given this struct: +/// +/// If the SQL generated by a struct is not uniquely identifiable by its type, +/// meaning that `HAS_STATIC_QUERY_ID` should always be false, +/// you should not derive this trait. +/// In that case you should manually implement it instead. +pub trait QueryId { + /// A type which uniquely represents `Self` in a SQL query. + /// + /// Typically this will be a re-construction of `Self` using the `QueryId` + /// type of each of your type parameters. For example, the type `And` would have `type QueryId = And`. + /// + /// The exception to this is when one of your type parameters does not + /// affect whether the same prepared statement can be used or not. For + /// example, a bind parameter is represented as `Bound`. + /// The actual Rust type we are serializing does not matter for the purposes + /// of prepared statement reuse, but a query which has identical SQL but + /// different types for its bind parameters requires a new prepared + /// statement. For this reason, `Bound` would have `type QueryId = + /// Bound`. + /// + /// If `HAS_STATIC_QUERY_ID` is `false`, you can put any type here + /// (typically `()`). + type QueryId: Any; + + /// Can the SQL generated by `Self` be uniquely identified by its type? + /// + /// Typically this question can be answered by looking at whether + /// `unsafe_to_cache_prepared` is called in your implementation of + /// `QueryFragment::walk_ast`. In Diesel itself, the only type which has + /// `false` here, but is potentially safe to store in the prepared statement + /// cache is a boxed query. + const HAS_STATIC_QUERY_ID: bool = true; + + /// Returns the type id of `Self::QueryId` if `Self::HAS_STATIC_QUERY_ID`. + /// Returns `None` otherwise. + /// + /// You should never need to override this method. + fn query_id() -> Option { + if Self::HAS_STATIC_QUERY_ID { + Some(TypeId::of::()) + } else { + None + } + } +} + +#[doc(inline)] +pub use diesel_derives::QueryId; + +impl QueryId for () { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = true; +} + +impl QueryId for Box { + type QueryId = T::QueryId; + + const HAS_STATIC_QUERY_ID: bool = T::HAS_STATIC_QUERY_ID; +} + +impl<'a, T: QueryId + ?Sized> QueryId for &'a T { + type QueryId = T::QueryId; + + const HAS_STATIC_QUERY_ID: bool = T::HAS_STATIC_QUERY_ID; +} + +impl QueryId for dyn QueryFragment { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +#[cfg(test)] +#[allow(unused_parens)] // FIXME: Remove this attribute once false positive is resolved. +mod tests { + use std::any::TypeId; + + use super::QueryId; + use crate::prelude::*; + + table! { + users { + id -> Integer, + name -> VarChar, + } + } + + fn query_id(_: T) -> Option { + T::query_id() + } + + #[test] + fn queries_with_no_dynamic_elements_have_a_static_id() { + use self::users::dsl::*; + assert!(query_id(users).is_some()); + assert!(query_id(users.select(name)).is_some()); + assert!(query_id(users.filter(name.eq("Sean"))).is_some()); + } + + #[test] + fn queries_with_different_types_have_different_ids() { + let id1 = query_id(users::table.select(users::name)); + let id2 = query_id(users::table.select(users::id)); + assert_ne!(id1, id2); + } + + #[test] + fn bind_params_use_only_sql_type_for_query_id() { + use self::users::dsl::*; + let id1 = query_id(users.filter(name.eq("Sean"))); + let id2 = query_id(users.filter(name.eq("Tess".to_string()))); + + assert_eq!(id1, id2); + } + + #[test] + #[cfg(features = "postgres")] + fn boxed_queries_do_not_have_static_query_id() { + use pg::Pg; + assert!(query_id(users::table.into_boxed::()).is_none()); + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/returning_clause.rs b/collector/benchmarks/diesel/diesel/src/query_builder/returning_clause.rs new file mode 100644 index 000000000..a76d3d4e7 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/returning_clause.rs @@ -0,0 +1,8 @@ +use crate::backend::SupportsReturningClause; + +simple_clause!( + NoReturningClause, + ReturningClause, + " RETURNING ", + backend_bounds = SupportsReturningClause +); diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/select_clause.rs b/collector/benchmarks/diesel/diesel/src/query_builder/select_clause.rs new file mode 100644 index 000000000..fea6ed318 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/select_clause.rs @@ -0,0 +1,110 @@ +use crate::backend::Backend; +use crate::expression::{Expression, SelectableExpression}; +use crate::query_builder::*; +use crate::query_source::QuerySource; + +#[derive(Debug, Clone, Copy, QueryId)] +pub struct DefaultSelectClause; +#[derive(Debug, Clone, Copy, QueryId)] +pub struct SelectClause(pub T); + +/// Specialised variant of `Expression` for select clause types +/// +/// The difference to the normal `Expression` trait is the query source (`QS`) +/// generic type parameter. This allows to access the query source in generic code. +pub trait SelectClauseExpression { + /// The expression represented by the given select clause + type Selection: SelectableExpression; + /// SQL type of the select clause + type SelectClauseSqlType; +} + +impl SelectClauseExpression for SelectClause +where + T: SelectableExpression, +{ + type Selection = T; + type SelectClauseSqlType = T::SqlType; +} + +impl SelectClauseExpression for DefaultSelectClause +where + QS: QuerySource, +{ + type Selection = QS::DefaultSelection; + type SelectClauseSqlType = ::SqlType; +} + +/// Specialised variant of `QueryFragment` for select clause types +/// +/// The difference to the normal `QueryFragment` trait is the query source (`QS`) +/// generic type parameter. +pub trait SelectClauseQueryFragment { + /// Walk over this `SelectClauseQueryFragment` for all passes. + /// + /// This method is where the actual behavior of an select clause is implemented. + /// This method will contain the behavior required for all possible AST + /// passes. See [`AstPass`] for more details. + /// + /// [`AstPass`]: struct.AstPass.html + fn walk_ast(&self, source: &QS, pass: AstPass) -> QueryResult<()>; +} + +impl SelectClauseQueryFragment for SelectClause +where + DB: Backend, + T: QueryFragment, +{ + fn walk_ast(&self, _: &QS, pass: AstPass) -> QueryResult<()> { + self.0.walk_ast(pass) + } +} + +impl SelectClauseQueryFragment for DefaultSelectClause +where + DB: Backend, + QS: QuerySource, + QS::DefaultSelection: QueryFragment, +{ + fn walk_ast(&self, source: &QS, pass: AstPass) -> QueryResult<()> { + source.default_selection().walk_ast(pass) + } +} + +/// An internal helper trait to convert different select clauses +/// into their boxed counter part. +/// +/// You normally don't need this trait, at least as long as you +/// don't implement your own select clause representation +pub trait IntoBoxedSelectClause<'a, DB, QS> { + /// The sql type of the select clause + type SqlType; + + /// Convert the select clause into a the boxed representation + fn into_boxed(self, source: &QS) -> Box + Send + 'a>; +} + +impl<'a, DB, T, QS> IntoBoxedSelectClause<'a, DB, QS> for SelectClause +where + T: QueryFragment + SelectableExpression + Send + 'a, + DB: Backend, +{ + type SqlType = T::SqlType; + + fn into_boxed(self, _source: &QS) -> Box + Send + 'a> { + Box::new(self.0) + } +} + +impl<'a, DB, QS> IntoBoxedSelectClause<'a, DB, QS> for DefaultSelectClause +where + QS: QuerySource, + QS::DefaultSelection: QueryFragment + Send + 'a, + DB: Backend, +{ + type SqlType = ::SqlType; + + fn into_boxed(self, source: &QS) -> Box + Send + 'a> { + Box::new(source.default_selection()) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/select_statement/boxed.rs b/collector/benchmarks/diesel/diesel/src/query_builder/select_statement/boxed.rs new file mode 100644 index 000000000..db02a2538 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/select_statement/boxed.rs @@ -0,0 +1,392 @@ +use std::marker::PhantomData; + +use crate::backend::Backend; +use crate::dsl::AsExprOf; +use crate::expression::subselect::ValidSubselect; +use crate::expression::*; +use crate::insertable::Insertable; +use crate::query_builder::distinct_clause::DistinctClause; +use crate::query_builder::group_by_clause::ValidGroupByClause; +use crate::query_builder::insert_statement::InsertFromSelect; +use crate::query_builder::limit_clause::LimitClause; +use crate::query_builder::limit_offset_clause::BoxedLimitOffsetClause; +use crate::query_builder::offset_clause::OffsetClause; +use crate::query_builder::order_clause::OrderClause; +use crate::query_builder::where_clause::*; +use crate::query_builder::*; +use crate::query_dsl::methods::*; +use crate::query_dsl::*; +use crate::query_source::joins::*; +use crate::query_source::{QuerySource, Table}; +use crate::result::QueryResult; +use crate::sql_types::{BigInt, BoolOrNullableBool, IntoNullable}; + +#[allow(missing_debug_implementations)] +pub struct BoxedSelectStatement<'a, ST, QS, DB, GB = ()> { + select: Box + Send + 'a>, + from: QS, + distinct: Box + Send + 'a>, + where_clause: BoxedWhereClause<'a, DB>, + order: Option + Send + 'a>>, + limit_offset: BoxedLimitOffsetClause<'a, DB>, + group_by: Box + Send + 'a>, + _marker: PhantomData<(ST, GB)>, +} + +impl<'a, ST, QS, DB, GB> BoxedSelectStatement<'a, ST, QS, DB, GB> { + #[allow(clippy::too_many_arguments)] + pub fn new( + select: S, + from: QS, + distinct: Box + Send + 'a>, + where_clause: BoxedWhereClause<'a, DB>, + order: Option + Send + 'a>>, + limit_offset: BoxedLimitOffsetClause<'a, DB>, + group_by: G, + ) -> Self + where + DB: Backend, + G: ValidGroupByClause + QueryFragment + Send + 'a, + S: IntoBoxedSelectClause<'a, DB, QS> + SelectClauseExpression, + S::Selection: ValidGrouping, + { + BoxedSelectStatement { + select: select.into_boxed(&from), + from, + distinct, + where_clause, + order, + limit_offset, + group_by: Box::new(group_by), + _marker: PhantomData, + } + } +} + +impl<'a, ST, QS, DB, GB> BoxedSelectStatement<'a, ST, QS, DB, GB> { + pub(crate) fn build_query( + &self, + mut out: AstPass, + where_clause_handler: impl Fn(&BoxedWhereClause<'a, DB>, AstPass) -> QueryResult<()>, + ) -> QueryResult<()> + where + DB: Backend, + QS: QuerySource, + QS::FromClause: QueryFragment, + BoxedLimitOffsetClause<'a, DB>: QueryFragment, + { + out.push_sql("SELECT "); + self.distinct.walk_ast(out.reborrow())?; + self.select.walk_ast(out.reborrow())?; + out.push_sql(" FROM "); + self.from.from_clause().walk_ast(out.reborrow())?; + where_clause_handler(&self.where_clause, out.reborrow())?; + self.group_by.walk_ast(out.reborrow())?; + + if let Some(ref order) = self.order { + out.push_sql(" ORDER BY "); + order.walk_ast(out.reborrow())?; + } + self.limit_offset.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl<'a, ST, QS, DB, GB> Query for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + DB: Backend, +{ + type SqlType = ST; +} + +impl<'a, ST, QS, DB, GB> SelectQuery for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + DB: Backend, +{ + type SqlType = ST; +} + +impl<'a, ST, QS, QS2, DB, GB> ValidSubselect for BoxedSelectStatement<'a, ST, QS, DB, GB> where + Self: Query +{ +} + +impl<'a, ST, QS, DB, GB> QueryFragment for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + DB: Backend, + QS: QuerySource, + QS::FromClause: QueryFragment, + BoxedLimitOffsetClause<'a, DB>: QueryFragment, +{ + fn walk_ast(&self, out: AstPass) -> QueryResult<()> { + self.build_query(out, |where_clause, out| where_clause.walk_ast(out)) + } +} + +impl<'a, ST, DB, GB> QueryFragment for BoxedSelectStatement<'a, ST, (), DB, GB> +where + DB: Backend, + BoxedLimitOffsetClause<'a, DB>: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("SELECT "); + self.distinct.walk_ast(out.reborrow())?; + self.select.walk_ast(out.reborrow())?; + self.where_clause.walk_ast(out.reborrow())?; + self.group_by.walk_ast(out.reborrow())?; + self.order.walk_ast(out.reborrow())?; + self.limit_offset.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl<'a, ST, QS, DB, GB> QueryId for BoxedSelectStatement<'a, ST, QS, DB, GB> { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +impl<'a, ST, QS, DB, Rhs, Kind, On, GB> InternalJoinDsl + for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + BoxedSelectStatement<'a, ST, JoinOn, On>, DB, GB>: AsQuery, +{ + type Output = BoxedSelectStatement<'a, ST, JoinOn, On>, DB, GB>; + + fn join(self, rhs: Rhs, kind: Kind, on: On) -> Self::Output { + BoxedSelectStatement { + select: self.select, + from: Join::new(self.from, rhs, kind).on(on), + distinct: self.distinct, + where_clause: self.where_clause, + order: self.order, + limit_offset: self.limit_offset, + group_by: self.group_by, + _marker: PhantomData, + } + } +} + +impl<'a, ST, QS, DB, GB> DistinctDsl for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + DB: Backend, + DistinctClause: QueryFragment, +{ + type Output = Self; + + fn distinct(mut self) -> Self::Output { + self.distinct = Box::new(DistinctClause); + self + } +} + +impl<'a, ST, QS, DB, Selection, GB> SelectDsl + for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + DB: Backend, + Selection: SelectableExpression + QueryFragment + ValidGrouping + Send + 'a, +{ + type Output = BoxedSelectStatement<'a, Selection::SqlType, QS, DB, GB>; + + fn select(self, selection: Selection) -> Self::Output { + BoxedSelectStatement { + select: Box::new(selection), + from: self.from, + distinct: self.distinct, + where_clause: self.where_clause, + order: self.order, + limit_offset: self.limit_offset, + group_by: self.group_by, + _marker: PhantomData, + } + } +} + +impl<'a, ST, QS, DB, Predicate, GB> FilterDsl + for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + BoxedWhereClause<'a, DB>: WhereAnd>, + Predicate: AppearsOnTable + NonAggregate, + Predicate::SqlType: BoolOrNullableBool, +{ + type Output = Self; + + fn filter(mut self, predicate: Predicate) -> Self::Output { + self.where_clause = self.where_clause.and(predicate); + self + } +} + +impl<'a, ST, QS, DB, Predicate, GB> OrFilterDsl + for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + BoxedWhereClause<'a, DB>: WhereOr>, + Predicate: AppearsOnTable + NonAggregate, + Predicate::SqlType: BoolOrNullableBool, +{ + type Output = Self; + + fn or_filter(mut self, predicate: Predicate) -> Self::Output { + self.where_clause = self.where_clause.or(predicate); + self + } +} + +impl<'a, ST, QS, DB, GB> LimitDsl for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + DB: Backend, + LimitClause>: QueryFragment, +{ + type Output = Self; + + fn limit(mut self, limit: i64) -> Self::Output { + self.limit_offset.limit = Some(Box::new(LimitClause(limit.into_sql::()))); + self + } +} + +impl<'a, ST, QS, DB, GB> OffsetDsl for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + DB: Backend, + OffsetClause>: QueryFragment, +{ + type Output = Self; + + fn offset(mut self, offset: i64) -> Self::Output { + self.limit_offset.offset = Some(Box::new(OffsetClause(offset.into_sql::()))); + self + } +} + +impl<'a, ST, QS, DB, Order, GB> OrderDsl for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + DB: Backend, + Order: QueryFragment + AppearsOnTable + Send + 'a, +{ + type Output = Self; + + fn order(mut self, order: Order) -> Self::Output { + self.order = OrderClause(order).into(); + self + } +} + +impl<'a, ST, QS, DB, Order, GB> ThenOrderDsl for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + DB: Backend + 'a, + Order: QueryFragment + AppearsOnTable + Send + 'a, +{ + type Output = Self; + + fn then_order_by(mut self, order: Order) -> Self::Output { + self.order = match self.order { + Some(old) => Some(Box::new((old, order))), + None => Some(Box::new(order)), + }; + self + } +} + +impl<'a, ST, QS, DB, Rhs> JoinTo for BoxedSelectStatement<'a, ST, QS, DB, ()> +where + QS: JoinTo, +{ + type FromClause = QS::FromClause; + type OnClause = QS::OnClause; + + fn join_target(rhs: Rhs) -> (Self::FromClause, Self::OnClause) { + QS::join_target(rhs) + } +} + +impl<'a, ST, QS, DB, GB> QueryDsl for BoxedSelectStatement<'a, ST, QS, DB, GB> {} + +impl<'a, ST, QS, DB, Conn, GB> RunQueryDsl for BoxedSelectStatement<'a, ST, QS, DB, GB> {} + +impl<'a, ST, QS, DB, T, GB> Insertable for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + T: Table, + Self: Query, + >::IsAggregate: + MixedAggregates, +{ + type Values = InsertFromSelect; + + fn values(self) -> Self::Values { + InsertFromSelect::new(self) + } +} + +impl<'a, 'b, ST, QS, DB, T, GB> Insertable for &'b BoxedSelectStatement<'a, ST, QS, DB, GB> +where + T: Table, + Self: Query, + >::IsAggregate: + MixedAggregates, +{ + type Values = InsertFromSelect; + + fn values(self) -> Self::Values { + InsertFromSelect::new(self) + } +} + +impl<'a, ST, QS, DB, GB> SelectNullableDsl for BoxedSelectStatement<'a, ST, QS, DB, GB> +where + ST: IntoNullable, +{ + type Output = BoxedSelectStatement<'a, ST::Nullable, QS, DB>; + + fn nullable(self) -> Self::Output { + BoxedSelectStatement { + select: self.select, + from: self.from, + distinct: self.distinct, + where_clause: self.where_clause, + order: self.order, + limit_offset: self.limit_offset, + group_by: self.group_by, + _marker: PhantomData, + } + } +} + +#[cfg(test)] +mod tests { + use crate::prelude::*; + + table! { + users { + id -> Integer, + } + } + + fn assert_send(_: T) + where + T: Send, + { + } + + macro_rules! assert_boxed_query_send { + ($backend:ty) => {{ + assert_send(users::table.into_boxed::<$backend>()); + assert_send( + users::table + .filter(users::id.eq(10)) + .into_boxed::<$backend>(), + ); + };}; + } + + #[test] + fn boxed_is_send() { + #[cfg(feature = "postgres")] + assert_boxed_query_send!(crate::pg::Pg); + + #[cfg(feature = "sqlite")] + assert_boxed_query_send!(crate::sqlite::Sqlite); + + #[cfg(feature = "mysql")] + assert_boxed_query_send!(crate::mysql::Mysql); + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/select_statement/dsl_impls.rs b/collector/benchmarks/diesel/diesel/src/query_builder/select_statement/dsl_impls.rs new file mode 100644 index 000000000..dcdad056c --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/select_statement/dsl_impls.rs @@ -0,0 +1,487 @@ +use super::BoxedSelectStatement; +use crate::associations::HasTable; +use crate::backend::Backend; +use crate::dsl::AsExprOf; +use crate::expression::nullable::Nullable; +use crate::expression::*; +use crate::insertable::Insertable; +use crate::query_builder::distinct_clause::*; +use crate::query_builder::group_by_clause::*; +use crate::query_builder::insert_statement::InsertFromSelect; +use crate::query_builder::limit_clause::*; +use crate::query_builder::limit_offset_clause::{BoxedLimitOffsetClause, LimitOffsetClause}; +use crate::query_builder::locking_clause::*; +use crate::query_builder::offset_clause::*; +use crate::query_builder::order_clause::*; +use crate::query_builder::select_clause::*; +use crate::query_builder::update_statement::*; +use crate::query_builder::where_clause::*; +use crate::query_builder::{ + AsQuery, IntoBoxedClause, Query, QueryFragment, SelectQuery, SelectStatement, +}; +use crate::query_dsl::boxed_dsl::BoxedDsl; +use crate::query_dsl::methods::*; +use crate::query_dsl::*; +use crate::query_source::joins::{Join, JoinOn, JoinTo}; +use crate::query_source::QuerySource; +use crate::sql_types::{BigInt, BoolOrNullableBool}; + +impl InternalJoinDsl + for SelectStatement +where + SelectStatement, On>, S, D, W, O, LOf, G, LC>: AsQuery, +{ + type Output = SelectStatement, On>, S, D, W, O, LOf, G, LC>; + + fn join(self, rhs: Rhs, kind: Kind, on: On) -> Self::Output { + SelectStatement::new( + self.select, + Join::new(self.from, rhs, kind).on(on), + self.distinct, + self.where_clause, + self.order, + self.limit_offset, + self.group_by, + self.locking, + ) + } +} + +impl SelectDsl + for SelectStatement +where + G: ValidGroupByClause, + Selection: SelectableExpression + ValidGrouping, + SelectStatement, D, W, O, LOf, G, LC>: SelectQuery, +{ + type Output = SelectStatement, D, W, O, LOf, G, LC>; + + fn select(self, selection: Selection) -> Self::Output { + SelectStatement::new( + SelectClause(selection), + self.from, + self.distinct, + self.where_clause, + self.order, + self.limit_offset, + self.group_by, + self.locking, + ) + } +} + +impl DistinctDsl for SelectStatement +where + Self: SelectQuery, + SelectStatement: SelectQuery, +{ + type Output = SelectStatement; + + fn distinct(self) -> Self::Output { + SelectStatement::new( + self.select, + self.from, + DistinctClause, + self.where_clause, + self.order, + self.limit_offset, + self.group_by, + self.locking, + ) + } +} + +impl FilterDsl + for SelectStatement +where + Predicate: Expression + NonAggregate, + Predicate::SqlType: BoolOrNullableBool, + W: WhereAnd, +{ + type Output = SelectStatement; + + fn filter(self, predicate: Predicate) -> Self::Output { + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause.and(predicate), + self.order, + self.limit_offset, + self.group_by, + self.locking, + ) + } +} + +impl OrFilterDsl + for SelectStatement +where + Predicate: Expression + NonAggregate, + Predicate::SqlType: BoolOrNullableBool, + W: WhereOr, +{ + type Output = SelectStatement; + + fn or_filter(self, predicate: Predicate) -> Self::Output { + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause.or(predicate), + self.order, + self.limit_offset, + self.group_by, + self.locking, + ) + } +} + +use crate::dsl::Filter; +use crate::expression_methods::EqAll; +use crate::query_source::Table; + +impl FindDsl for SelectStatement +where + F: Table, + F::PrimaryKey: EqAll, + Self: FilterDsl<>::Output>, +{ + type Output = Filter>::Output>; + + fn find(self, id: PK) -> Self::Output { + let primary_key = self.from.primary_key(); + FilterDsl::filter(self, primary_key.eq_all(id)) + } +} + +impl OrderDsl + for SelectStatement +where + Expr: AppearsOnTable, + Self: SelectQuery, + SelectStatement, LOf, G, LC>: SelectQuery, +{ + type Output = SelectStatement, LOf, G, LC>; + + fn order(self, expr: Expr) -> Self::Output { + let order = OrderClause(expr); + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause, + order, + self.limit_offset, + self.group_by, + self.locking, + ) + } +} + +impl ThenOrderDsl + for SelectStatement, LOf, G, LC> +where + Expr: AppearsOnTable, +{ + type Output = SelectStatement, LOf, G, LC>; + + fn then_order_by(self, expr: Expr) -> Self::Output { + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause, + OrderClause((self.order.0, expr)), + self.limit_offset, + self.group_by, + self.locking, + ) + } +} + +impl ThenOrderDsl + for SelectStatement +where + Expr: Expression, + Self: OrderDsl, +{ + type Output = crate::dsl::Order; + + fn then_order_by(self, expr: Expr) -> Self::Output { + self.order_by(expr) + } +} + +#[doc(hidden)] +pub type Limit = AsExprOf; + +impl LimitDsl + for SelectStatement, G, LC> +where + Self: SelectQuery, + SelectStatement, Of>, G, LC>: + SelectQuery, +{ + type Output = SelectStatement, Of>, G, LC>; + + fn limit(self, limit: i64) -> Self::Output { + let limit_clause = LimitClause(limit.into_sql::()); + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause, + self.order, + LimitOffsetClause { + limit_clause, + offset_clause: self.limit_offset.offset_clause, + }, + self.group_by, + self.locking, + ) + } +} + +#[doc(hidden)] +pub type Offset = Limit; + +impl OffsetDsl + for SelectStatement, G, LC> +where + Self: SelectQuery, + SelectStatement>, G, LC>: + SelectQuery, +{ + type Output = SelectStatement>, G, LC>; + + fn offset(self, offset: i64) -> Self::Output { + let offset_clause = OffsetClause(offset.into_sql::()); + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause, + self.order, + LimitOffsetClause { + limit_clause: self.limit_offset.limit_clause, + offset_clause, + }, + self.group_by, + self.locking, + ) + } +} + +impl GroupByDsl for SelectStatement +where + SelectStatement>: SelectQuery, + Expr: Expression, +{ + type Output = SelectStatement>; + + fn group_by(self, expr: Expr) -> Self::Output { + let group_by = GroupByClause(expr); + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause, + self.order, + self.limit_offset, + group_by, + self.locking, + ) + } +} + +impl LockingDsl + for SelectStatement +{ + type Output = SelectStatement< + F, + S, + NoDistinctClause, + W, + O, + LOf, + NoGroupByClause, + LockingClause, + >; + + fn with_lock(self, lock: Lock) -> Self::Output { + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause, + self.order, + self.limit_offset, + self.group_by, + LockingClause::new(lock, NoModifier), + ) + } +} + +impl ModifyLockDsl + for SelectStatement> +{ + type Output = SelectStatement>; + + fn modify_lock(self, modifier: Modifier) -> Self::Output { + SelectStatement::new( + self.select, + self.from, + self.distinct, + self.where_clause, + self.order, + self.limit_offset, + self.group_by, + LockingClause::new(self.locking.lock_mode, modifier), + ) + } +} + +impl<'a, F, S, D, W, O, LOf, G, DB> BoxedDsl<'a, DB> for SelectStatement +where + Self: AsQuery, + DB: Backend, + S: IntoBoxedSelectClause<'a, DB, F> + SelectClauseExpression, + S::Selection: ValidGrouping, + D: QueryFragment + Send + 'a, + W: Into>, + O: Into + Send + 'a>>>, + LOf: IntoBoxedClause<'a, DB, BoxedClause = BoxedLimitOffsetClause<'a, DB>>, + G: ValidGroupByClause + QueryFragment + Send + 'a, +{ + type Output = BoxedSelectStatement<'a, S::SqlType, F, DB, G::Expressions>; + + fn internal_into_boxed(self) -> Self::Output { + BoxedSelectStatement::new( + self.select, + self.from, + Box::new(self.distinct), + self.where_clause.into(), + self.order.into(), + self.limit_offset.into_boxed(), + self.group_by, + ) + } +} + +impl HasTable for SelectStatement +where + F: HasTable, +{ + type Table = F::Table; + + fn table() -> Self::Table { + F::table() + } +} + +impl IntoUpdateTarget for SelectStatement +where + SelectStatement: HasTable, + W: ValidWhereClause, +{ + type WhereClause = W; + + fn into_update_target(self) -> UpdateTarget { + UpdateTarget { + table: Self::table(), + where_clause: self.where_clause, + } + } +} + +// FIXME: Should we disable joining when `.group_by` has been called? Are there +// any other query methods where a join no longer has the same semantics as +// joining on just the table? +impl JoinTo for SelectStatement +where + F: JoinTo, +{ + type FromClause = F::FromClause; + type OnClause = F::OnClause; + + fn join_target(rhs: Rhs) -> (Self::FromClause, Self::OnClause) { + F::join_target(rhs) + } +} + +impl QueryDsl for SelectStatement {} + +impl RunQueryDsl + for SelectStatement +{ +} + +impl Insertable for SelectStatement +where + Tab: Table, + Self: Query, + >::IsAggregate: + MixedAggregates, +{ + type Values = InsertFromSelect; + + fn values(self) -> Self::Values { + InsertFromSelect::new(self) + } +} + +impl<'a, F, S, D, W, O, LOf, G, LC, Tab> Insertable + for &'a SelectStatement +where + Tab: Table, + Self: Query, + >::IsAggregate: + MixedAggregates, +{ + type Values = InsertFromSelect; + + fn values(self) -> Self::Values { + InsertFromSelect::new(self) + } +} + +impl<'a, F, S, D, W, O, LOf, G> SelectNullableDsl + for SelectStatement, D, W, O, LOf, G> +{ + type Output = SelectStatement>, D, W, O, LOf, G>; + + fn nullable(self) -> Self::Output { + SelectStatement::new( + SelectClause(Nullable::new(self.select.0)), + self.from, + self.distinct, + self.where_clause, + self.order, + self.limit_offset, + self.group_by, + self.locking, + ) + } +} + +impl<'a, F, D, W, O, LOf, G> SelectNullableDsl + for SelectStatement +where + F: QuerySource, +{ + type Output = SelectStatement>, D, W, O, LOf, G>; + + fn nullable(self) -> Self::Output { + SelectStatement::new( + SelectClause(Nullable::new(self.from.default_selection())), + self.from, + self.distinct, + self.where_clause, + self.order, + self.limit_offset, + self.group_by, + self.locking, + ) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/select_statement/mod.rs b/collector/benchmarks/diesel/diesel/src/query_builder/select_statement/mod.rs new file mode 100644 index 000000000..0998377b9 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/select_statement/mod.rs @@ -0,0 +1,215 @@ +//! Within this module, types commonly use the following abbreviations: +//! +//! F: From Clause +//! S: Select Clause +//! D: Distinct Clause +//! W: Where Clause +//! O: Order By Clause +//! L: Limit Clause +//! Of: Offset Clause +//! G: Group By Clause +//! LC: For Update Clause +#![allow(missing_docs)] // The missing_docs lint triggers even though this is hidden + +mod boxed; +mod dsl_impls; + +pub use self::boxed::BoxedSelectStatement; + +use super::distinct_clause::NoDistinctClause; +use super::group_by_clause::*; +use super::limit_clause::NoLimitClause; +use super::locking_clause::NoLockingClause; +use super::offset_clause::NoOffsetClause; +use super::order_clause::NoOrderClause; +use super::select_clause::*; +use super::where_clause::*; +use super::{AstPass, Query, QueryFragment}; +use crate::backend::Backend; +use crate::expression::subselect::ValidSubselect; +use crate::expression::*; +use crate::query_builder::limit_offset_clause::LimitOffsetClause; +use crate::query_builder::{QueryId, SelectQuery}; +use crate::query_source::joins::{AppendSelection, Inner, Join}; +use crate::query_source::*; +use crate::result::QueryResult; + +#[derive(Debug, Clone, Copy, QueryId)] +#[doc(hidden)] +#[must_use = "Queries are only executed when calling `load`, `get_result` or similar."] +pub struct SelectStatement< + From, + Select = DefaultSelectClause, + Distinct = NoDistinctClause, + Where = NoWhereClause, + Order = NoOrderClause, + LimitOffset = LimitOffsetClause, + GroupBy = NoGroupByClause, + Locking = NoLockingClause, +> { + pub(crate) select: Select, + pub(crate) from: From, + pub(crate) distinct: Distinct, + pub(crate) where_clause: Where, + pub(crate) order: Order, + pub(crate) limit_offset: LimitOffset, + pub(crate) group_by: GroupBy, + pub(crate) locking: Locking, +} + +impl SelectStatement { + #[allow(clippy::too_many_arguments)] + pub fn new( + select: S, + from: F, + distinct: D, + where_clause: W, + order: O, + limit_offset: LOf, + group_by: G, + locking: LC, + ) -> Self { + SelectStatement { + select, + from, + distinct, + where_clause, + order, + limit_offset, + group_by, + locking, + } + } +} + +impl SelectStatement { + pub fn simple(from: F) -> Self { + SelectStatement::new( + DefaultSelectClause, + from, + NoDistinctClause, + NoWhereClause, + NoOrderClause, + LimitOffsetClause { + limit_clause: NoLimitClause, + offset_clause: NoOffsetClause, + }, + NoGroupByClause, + NoLockingClause, + ) + } +} + +impl Query for SelectStatement +where + G: ValidGroupByClause, + S: SelectClauseExpression, + S::Selection: ValidGrouping, + W: ValidWhereClause, +{ + type SqlType = S::SelectClauseSqlType; +} + +impl SelectQuery for SelectStatement +where + S: SelectClauseExpression, +{ + type SqlType = S::SelectClauseSqlType; +} + +impl QueryFragment for SelectStatement +where + DB: Backend, + S: SelectClauseQueryFragment, + F: QuerySource, + F::FromClause: QueryFragment, + D: QueryFragment, + W: QueryFragment, + O: QueryFragment, + LOf: QueryFragment, + G: QueryFragment, + LC: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("SELECT "); + self.distinct.walk_ast(out.reborrow())?; + self.select.walk_ast(&self.from, out.reborrow())?; + out.push_sql(" FROM "); + self.from.from_clause().walk_ast(out.reborrow())?; + self.where_clause.walk_ast(out.reborrow())?; + self.group_by.walk_ast(out.reborrow())?; + self.order.walk_ast(out.reborrow())?; + self.limit_offset.walk_ast(out.reborrow())?; + self.locking.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl QueryFragment for SelectStatement<(), S, D, W, O, LOf, G, LC> +where + DB: Backend, + S: SelectClauseQueryFragment<(), DB>, + D: QueryFragment, + W: QueryFragment, + O: QueryFragment, + LOf: QueryFragment, + G: QueryFragment, + LC: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("SELECT "); + self.distinct.walk_ast(out.reborrow())?; + self.select.walk_ast(&(), out.reborrow())?; + self.where_clause.walk_ast(out.reborrow())?; + self.group_by.walk_ast(out.reborrow())?; + self.order.walk_ast(out.reborrow())?; + self.limit_offset.walk_ast(out.reborrow())?; + self.locking.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl ValidSubselect + for SelectStatement +where + Self: SelectQuery, + W: ValidWhereClause>, +{ +} + +/// Allow `SelectStatement` to act as if it were `From` as long as +/// no other query methods have been called on it +impl AppearsInFromClause for SelectStatement +where + From: AppearsInFromClause, +{ + type Count = From::Count; +} + +impl QuerySource for SelectStatement +where + From: QuerySource, + From::DefaultSelection: SelectableExpression, +{ + type FromClause = From::FromClause; + type DefaultSelection = From::DefaultSelection; + + fn from_clause(&self) -> Self::FromClause { + self.from.from_clause() + } + + fn default_selection(&self) -> Self::DefaultSelection { + self.from.default_selection() + } +} + +impl AppendSelection for SelectStatement +where + From: AppendSelection, +{ + type Output = From::Output; + + fn append_selection(&self, selection: Selection) -> Self::Output { + self.from.append_selection(selection) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/sql_query.rs b/collector/benchmarks/diesel/diesel/src/query_builder/sql_query.rs new file mode 100644 index 000000000..f838cc3c5 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/sql_query.rs @@ -0,0 +1,256 @@ +use std::marker::PhantomData; + +use super::Query; +use crate::backend::Backend; +use crate::connection::Connection; +use crate::query_builder::{AstPass, QueryFragment, QueryId}; +use crate::query_dsl::RunQueryDsl; +use crate::result::QueryResult; +use crate::serialize::ToSql; +use crate::sql_types::{HasSqlType, Untyped}; + +#[derive(Debug, Clone)] +#[must_use = "Queries are only executed when calling `load`, `get_result` or similar."] +/// The return value of `sql_query`. +/// +/// Unlike most queries in Diesel, `SqlQuery` loads its data by column name, +/// rather than by index. This means that you cannot deserialize this query into +/// a tuple, and any structs used must implement `QueryableByName`. +/// +/// See [`sql_query`](../fn.sql_query.html) for examples. +pub struct SqlQuery { + inner: Inner, + query: String, +} + +impl SqlQuery { + pub(crate) fn new(inner: Inner, query: String) -> Self { + SqlQuery { inner, query } + } + + /// Bind a value for use with this SQL query. The given query should have + /// placeholders that vary based on the database type, + /// like [SQLite Parameter](https://sqlite.org/lang_expr.html#varparam) syntax, + /// [PostgreSQL PREPARE syntax](https://www.postgresql.org/docs/current/sql-prepare.html), + /// or [MySQL bind syntax](https://dev.mysql.com/doc/refman/8.0/en/mysql-stmt-bind-param.html). + /// + /// # Safety + /// + /// This function should be used with care, as Diesel cannot validate that + /// the value is of the right type nor can it validate that you have passed + /// the correct number of parameters. + /// + /// # Example + /// + /// ``` + /// # include!("../doctest_setup.rs"); + /// # + /// # use schema::users; + /// # + /// # #[derive(QueryableByName, Debug, PartialEq)] + /// # #[table_name="users"] + /// # struct User { + /// # id: i32, + /// # name: String, + /// # } + /// # + /// # fn main() { + /// # use diesel::sql_query; + /// # use diesel::sql_types::{Integer, Text}; + /// # + /// # let connection = establish_connection(); + /// # diesel::insert_into(users::table) + /// # .values(users::name.eq("Jim")) + /// # .execute(&connection).unwrap(); + /// # #[cfg(feature = "postgres")] + /// # let users = sql_query("SELECT * FROM users WHERE id > $1 AND name != $2"); + /// # #[cfg(not(feature = "postgres"))] + /// let users = sql_query("SELECT * FROM users WHERE id > ? AND name <> ?") + /// # ; + /// # let users = users + /// .bind::(1) + /// .bind::("Tess") + /// .get_results(&connection); + /// let expected_users = vec![ + /// User { id: 3, name: "Jim".into() }, + /// ]; + /// assert_eq!(Ok(expected_users), users); + /// # } + /// ``` + pub fn bind(self, value: Value) -> UncheckedBind { + UncheckedBind::new(self, value) + } + + /// Internally boxes future calls on `bind` and `sql` so that they don't + /// change the type. + /// + /// This allows doing things you otherwise couldn't do, e.g. `bind`ing in a + /// loop. + pub fn into_boxed<'f, DB: Backend>(self) -> BoxedSqlQuery<'f, DB, Self> { + BoxedSqlQuery::new(self) + } + + /// Appends a piece of SQL code at the end. + pub fn sql>(mut self, sql: T) -> Self { + self.query += sql.as_ref(); + self + } +} + +impl QueryFragment for SqlQuery +where + DB: Backend, + Inner: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.unsafe_to_cache_prepared(); + self.inner.walk_ast(out.reborrow())?; + out.push_sql(&self.query); + Ok(()) + } +} + +impl QueryId for SqlQuery { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +impl Query for SqlQuery { + type SqlType = Untyped; +} + +impl RunQueryDsl for SqlQuery {} + +#[derive(Debug, Clone, Copy)] +#[must_use = "Queries are only executed when calling `load`, `get_result` or similar."] +pub struct UncheckedBind { + query: Query, + value: Value, + _marker: PhantomData, +} + +impl UncheckedBind { + pub fn new(query: Query, value: Value) -> Self { + UncheckedBind { + query, + value, + _marker: PhantomData, + } + } + + pub fn bind(self, value: Value2) -> UncheckedBind { + UncheckedBind::new(self, value) + } + + pub fn into_boxed<'f, DB: Backend>(self) -> BoxedSqlQuery<'f, DB, Self> { + BoxedSqlQuery::new(self) + } + + pub fn sql>(self, sql: T) -> SqlQuery { + SqlQuery::new(self, sql.into()) + } +} + +impl QueryId for UncheckedBind +where + Query: QueryId, + ST: QueryId, +{ + type QueryId = UncheckedBind; + + const HAS_STATIC_QUERY_ID: bool = Query::HAS_STATIC_QUERY_ID && ST::HAS_STATIC_QUERY_ID; +} + +impl QueryFragment for UncheckedBind +where + DB: Backend + HasSqlType, + Query: QueryFragment, + Value: ToSql, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + self.query.walk_ast(out.reborrow())?; + out.push_bind_param_value_only(&self.value)?; + Ok(()) + } +} + +impl Query for UncheckedBind { + type SqlType = Untyped; +} + +impl RunQueryDsl for UncheckedBind {} + +#[must_use = "Queries are only executed when calling `load`, `get_result`, or similar."] +/// See [`SqlQuery::into_boxed`]. +/// +/// [`SqlQuery::into_boxed`]: ./struct.SqlQuery.html#method.into_boxed +#[allow(missing_debug_implementations)] +pub struct BoxedSqlQuery<'f, DB: Backend, Query> { + query: Query, + sql: String, + binds: Vec) -> QueryResult<()> + 'f>>, +} + +impl<'f, DB: Backend, Query> BoxedSqlQuery<'f, DB, Query> { + pub(crate) fn new(query: Query) -> Self { + BoxedSqlQuery { + query, + sql: "".to_string(), + binds: vec![], + } + } + + /// See [`SqlQuery::bind`]. + /// + /// [`SqlQuery::bind`]: ./struct.SqlQuery.html#method.bind + pub fn bind(mut self, b: Value) -> Self + where + DB: HasSqlType, + Value: ToSql + 'f, + { + self.binds + .push(Box::new(move |mut out| out.push_bind_param_value_only(&b))); + self + } + + /// See [`SqlQuery::sql`]. + /// + /// [`SqlQuery::sql`]: ./struct.SqlQuery.html#method.sql + pub fn sql>(mut self, sql: T) -> Self { + self.sql += sql.as_ref(); + self + } +} + +impl QueryFragment for BoxedSqlQuery<'_, DB, Query> +where + DB: Backend, + Query: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.unsafe_to_cache_prepared(); + self.query.walk_ast(out.reborrow())?; + out.push_sql(&self.sql); + + for b in &self.binds { + b(out.reborrow())?; + } + Ok(()) + } +} + +impl QueryId for BoxedSqlQuery<'_, DB, Query> { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +impl Query for BoxedSqlQuery<'_, DB, Q> +where + DB: Backend, +{ + type SqlType = Untyped; +} + +impl RunQueryDsl for BoxedSqlQuery<'_, Conn::Backend, Query> {} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/update_statement/changeset.rs b/collector/benchmarks/diesel/diesel/src/query_builder/update_statement/changeset.rs new file mode 100644 index 000000000..75b7d3234 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/update_statement/changeset.rs @@ -0,0 +1,82 @@ +use crate::backend::Backend; +use crate::expression::grouped::Grouped; +use crate::expression::operators::Eq; +use crate::expression::AppearsOnTable; +use crate::query_builder::*; +use crate::query_source::{Column, QuerySource}; +use crate::result::QueryResult; + +/// Types which can be passed to +/// [`update.set`](struct.UpdateStatement.html#method.set). +/// +/// This trait can be [derived](derive.AsChangeset.html) +pub trait AsChangeset { + /// The table which `Self::Changeset` will be updating + type Target: QuerySource; + + /// The update statement this type represents + type Changeset; + + /// Convert `self` into the actual update statement being executed + fn as_changeset(self) -> Self::Changeset; +} + +#[doc(inline)] +pub use diesel_derives::AsChangeset; + +impl AsChangeset for Option { + type Target = T::Target; + type Changeset = Option; + + fn as_changeset(self) -> Self::Changeset { + self.map(AsChangeset::as_changeset) + } +} + +impl AsChangeset for Eq +where + Left: Column, + Right: AppearsOnTable, +{ + type Target = Left::Table; + type Changeset = Assign; + + fn as_changeset(self) -> Self::Changeset { + Assign { + _column: self.left, + expr: self.right, + } + } +} + +impl AsChangeset for Grouped> +where + Eq: AsChangeset, +{ + type Target = as AsChangeset>::Target; + + type Changeset = as AsChangeset>::Changeset; + + fn as_changeset(self) -> Self::Changeset { + self.0.as_changeset() + } +} + +#[derive(Debug, Clone, Copy)] +pub struct Assign { + _column: Col, + expr: Expr, +} + +impl QueryFragment for Assign +where + DB: Backend, + T: Column, + U: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_identifier(T::NAME)?; + out.push_sql(" = "); + QueryFragment::walk_ast(&self.expr, out) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/update_statement/mod.rs b/collector/benchmarks/diesel/diesel/src/query_builder/update_statement/mod.rs new file mode 100644 index 000000000..dd1d44359 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/update_statement/mod.rs @@ -0,0 +1,288 @@ +pub mod changeset; +pub mod target; + +pub use self::changeset::AsChangeset; +pub use self::target::{IntoUpdateTarget, UpdateTarget}; + +use crate::backend::Backend; +use crate::dsl::{Filter, IntoBoxed}; +use crate::expression::{ + is_aggregate, AppearsOnTable, Expression, MixedAggregates, SelectableExpression, ValidGrouping, +}; +use crate::query_builder::returning_clause::*; +use crate::query_builder::where_clause::*; +use crate::query_builder::*; +use crate::query_dsl::methods::{BoxedDsl, FilterDsl}; +use crate::query_dsl::RunQueryDsl; +use crate::query_source::Table; +use crate::result::Error::QueryBuilderError; +use crate::result::QueryResult; + +impl UpdateStatement { + pub(crate) fn new(target: UpdateTarget) -> Self { + UpdateStatement { + table: target.table, + where_clause: target.where_clause, + values: SetNotCalled, + returning: NoReturningClause, + } + } + + /// Provides the `SET` clause of the `UPDATE` statement. + /// + /// See [`update`](../fn.update.html) for usage examples, or [the update + /// guide](https://diesel.rs/guides/all-about-updates/) for a more exhaustive + /// set of examples. + pub fn set(self, values: V) -> UpdateStatement + where + T: Table, + V: changeset::AsChangeset, + UpdateStatement: AsQuery, + { + UpdateStatement { + table: self.table, + where_clause: self.where_clause, + values: values.as_changeset(), + returning: self.returning, + } + } +} + +#[derive(Debug, Copy, Clone)] +#[must_use = "Queries are only executed when calling `load`, `get_result` or similar."] +/// Represents a complete `UPDATE` statement. +/// +/// See [`update`](../fn.update.html) for usage examples, or [the update +/// guide](https://diesel.rs/guides/all-about-updates/) for a more exhaustive +/// set of examples. +pub struct UpdateStatement { + table: T, + where_clause: U, + values: V, + returning: Ret, +} + +/// An `UPDATE` statement with a boxed `WHERE` clause. +pub type BoxedUpdateStatement<'a, DB, T, V = SetNotCalled, Ret = NoReturningClause> = + UpdateStatement, V, Ret>; + +impl UpdateStatement { + /// Adds the given predicate to the `WHERE` clause of the statement being + /// constructed. + /// + /// If there is already a `WHERE` clause, the predicate will be appended + /// with `AND`. There is no difference in behavior between + /// `update(table.filter(x))` and `update(table).filter(x)`. + /// + /// # Example + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # fn main() { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let updated_rows = diesel::update(users) + /// .set(name.eq("Jim")) + /// .filter(name.eq("Sean")) + /// .execute(&connection); + /// assert_eq!(Ok(1), updated_rows); + /// + /// let expected_names = vec!["Jim".to_string(), "Tess".to_string()]; + /// let names = users.select(name).order(id).load(&connection); + /// + /// assert_eq!(Ok(expected_names), names); + /// # } + /// ``` + pub fn filter(self, predicate: Predicate) -> Filter + where + Self: FilterDsl, + { + FilterDsl::filter(self, predicate) + } + + /// Boxes the `WHERE` clause of this update statement. + /// + /// This is useful for cases where you want to conditionally modify a query, + /// but need the type to remain the same. The backend must be specified as + /// part of this. It is not possible to box a query and have it be useable + /// on multiple backends. + /// + /// A boxed query will incur a minor performance penalty, as the query builder + /// can no longer be inlined by the compiler. For most applications this cost + /// will be minimal. + /// + /// ### Example + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use std::collections::HashMap; + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// # let mut params = HashMap::new(); + /// # params.insert("tess_has_been_a_jerk", false); + /// let mut query = diesel::update(users) + /// .set(name.eq("Jerk")) + /// .into_boxed(); + /// + /// if !params["tess_has_been_a_jerk"] { + /// query = query.filter(name.ne("Tess")); + /// } + /// + /// let updated_rows = query.execute(&connection)?; + /// assert_eq!(1, updated_rows); + /// + /// let expected_names = vec!["Jerk", "Tess"]; + /// let names = users.select(name).order(id).load::(&connection)?; + /// + /// assert_eq!(expected_names, names); + /// # Ok(()) + /// # } + /// ``` + pub fn into_boxed<'a, DB>(self) -> IntoBoxed<'a, Self, DB> + where + DB: Backend, + Self: BoxedDsl<'a, DB>, + { + BoxedDsl::internal_into_boxed(self) + } +} + +impl FilterDsl for UpdateStatement +where + U: WhereAnd, + Predicate: AppearsOnTable, +{ + type Output = UpdateStatement; + + fn filter(self, predicate: Predicate) -> Self::Output { + UpdateStatement { + table: self.table, + where_clause: self.where_clause.and(predicate), + values: self.values, + returning: self.returning, + } + } +} + +impl<'a, T, U, V, Ret, DB> BoxedDsl<'a, DB> for UpdateStatement +where + U: Into>, +{ + type Output = BoxedUpdateStatement<'a, DB, T, V, Ret>; + + fn internal_into_boxed(self) -> Self::Output { + UpdateStatement { + table: self.table, + where_clause: self.where_clause.into(), + values: self.values, + returning: self.returning, + } + } +} + +impl QueryFragment for UpdateStatement +where + DB: Backend, + T: Table, + T::FromClause: QueryFragment, + U: QueryFragment, + V: QueryFragment, + Ret: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + if self.values.is_noop()? { + return Err(QueryBuilderError( + "There are no changes to save. This query cannot be built".into(), + )); + } + + out.unsafe_to_cache_prepared(); + out.push_sql("UPDATE "); + self.table.from_clause().walk_ast(out.reborrow())?; + out.push_sql(" SET "); + self.values.walk_ast(out.reborrow())?; + self.where_clause.walk_ast(out.reborrow())?; + self.returning.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl QueryId for UpdateStatement { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +impl AsQuery for UpdateStatement +where + T: Table, + UpdateStatement>: Query, + T::AllColumns: ValidGrouping<()>, + >::IsAggregate: + MixedAggregates, +{ + type SqlType = ::SqlType; + type Query = UpdateStatement>; + + fn as_query(self) -> Self::Query { + self.returning(T::all_columns()) + } +} + +impl Query for UpdateStatement> +where + T: Table, + Ret: Expression + SelectableExpression + ValidGrouping<()>, + Ret::IsAggregate: MixedAggregates, +{ + type SqlType = Ret::SqlType; +} + +impl RunQueryDsl for UpdateStatement {} + +impl UpdateStatement { + /// Specify what expression is returned after execution of the `update`. + /// # Examples + /// + /// ### Updating a single record: + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # #[cfg(feature = "postgres")] + /// # fn main() { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let updated_name = diesel::update(users.filter(id.eq(1))) + /// .set(name.eq("Dean")) + /// .returning(name) + /// .get_result(&connection); + /// assert_eq!(Ok("Dean".to_string()), updated_name); + /// # } + /// # #[cfg(not(feature = "postgres"))] + /// # fn main() {} + /// ``` + pub fn returning(self, returns: E) -> UpdateStatement> + where + T: Table, + UpdateStatement>: Query, + { + UpdateStatement { + table: self.table, + where_clause: self.where_clause, + values: self.values, + returning: ReturningClause(returns), + } + } +} + +/// Indicates that you have not yet called `.set` on an update statement +#[derive(Debug, Clone, Copy)] +pub struct SetNotCalled; diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/update_statement/target.rs b/collector/benchmarks/diesel/diesel/src/query_builder/update_statement/target.rs new file mode 100644 index 000000000..a76132503 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/update_statement/target.rs @@ -0,0 +1,47 @@ +use crate::associations::{HasTable, Identifiable}; +use crate::dsl::Find; +use crate::query_dsl::methods::FindDsl; +use crate::query_source::Table; + +#[doc(hidden)] +#[derive(Debug)] +pub struct UpdateTarget { + pub table: Table, + pub where_clause: WhereClause, +} + +/// A type which can be passed to [`update`] or [`delete`]. +/// +/// Apps will never need to implement this type directly. There are three kinds +/// which implement this trait. Tables, queries which have only had `filter` +/// called on them, and types which implement `Identifiable`. +/// +/// When a table is passed to `update`, every row in the table will be updated. +/// You can scope this down by calling [`filter`] which will +/// result in `UPDATE your_table SET ... WHERE args_to_filter`. Passing a type +/// which implements `Identifiable` is the same as passing +/// `SomeStruct::table().find(some_struct)`. +/// +/// [`update`]: ../fn.update.html +/// [`delete`]: ../fn.delete.html +/// [`filter`]: struct.UpdateStatement.html#method.filter +pub trait IntoUpdateTarget: HasTable { + /// What is the `WHERE` clause of this target? + type WhereClause; + + /// Decomposes `self` into the table and where clause. + fn into_update_target(self) -> UpdateTarget; +} + +impl IntoUpdateTarget for T +where + T: Identifiable
, + Tab: Table + FindDsl, + Find: IntoUpdateTarget
, +{ + type WhereClause = V; + + fn into_update_target(self) -> UpdateTarget { + T::table().find(self.id()).into_update_target() + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/upsert/into_conflict_clause.rs b/collector/benchmarks/diesel/diesel/src/query_builder/upsert/into_conflict_clause.rs new file mode 100644 index 000000000..892886b5e --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/upsert/into_conflict_clause.rs @@ -0,0 +1,124 @@ +use crate::insertable::{BatchInsert, OwnedBatchInsert}; +use crate::query_builder::insert_statement::InsertFromSelect; +#[cfg(feature = "sqlite")] +use crate::query_builder::where_clause::{BoxedWhereClause, WhereClause}; +#[cfg(any(feature = "sqlite", feature = "postgres"))] +use crate::query_builder::{AstPass, QueryFragment}; +use crate::query_builder::{BoxedSelectStatement, Query, SelectStatement, ValuesClause}; +#[cfg(any(feature = "sqlite", feature = "postgres"))] +use crate::result::QueryResult; + +pub trait IntoConflictValueClause { + type ValueClause; + + fn into_value_clause(self) -> Self::ValueClause; +} + +#[derive(Debug, Clone, Copy)] +pub struct OnConflictSelectWrapper(S); + +impl Query for OnConflictSelectWrapper +where + Q: Query, +{ + type SqlType = Q::SqlType; +} + +#[cfg(feature = "postgres")] +impl QueryFragment for OnConflictSelectWrapper +where + S: QueryFragment, +{ + fn walk_ast(&self, out: AstPass) -> QueryResult<()> { + self.0.walk_ast(out) + } +} + +// The corresponding impl for`NoWhereClause` is missing because of +// https://www.sqlite.org/lang_UPSERT.html (Parsing Ambiguity) +#[cfg(feature = "sqlite")] +impl QueryFragment + for OnConflictSelectWrapper, O, LOf, G, LC>> +where + SelectStatement, O, LOf, G, LC>: QueryFragment, +{ + fn walk_ast(&self, out: AstPass) -> QueryResult<()> { + self.0.walk_ast(out) + } +} + +#[cfg(feature = "sqlite")] +impl<'a, ST, QS> QueryFragment + for OnConflictSelectWrapper> +where + BoxedSelectStatement<'a, ST, QS, crate::sqlite::Sqlite>: QueryFragment, + QS: crate::query_source::QuerySource, + QS::FromClause: QueryFragment, +{ + fn walk_ast(&self, pass: AstPass) -> QueryResult<()> { + // https://www.sqlite.org/lang_UPSERT.html (Parsing Ambiguity) + self.0.build_query(pass, |where_clause, mut pass| { + match where_clause { + BoxedWhereClause::None => pass.push_sql(" WHERE 1=1 "), + w => w.walk_ast(pass.reborrow())?, + } + Ok(()) + }) + } +} + +impl IntoConflictValueClause for ValuesClause { + type ValueClause = Self; + + fn into_value_clause(self) -> Self::ValueClause { + self + } +} + +impl<'a, Inner, Tab> IntoConflictValueClause for BatchInsert<'a, Inner, Tab> { + type ValueClause = Self; + + fn into_value_clause(self) -> Self::ValueClause { + self + } +} + +impl IntoConflictValueClause for OwnedBatchInsert { + type ValueClause = Self; + + fn into_value_clause(self) -> Self::ValueClause { + self + } +} + +impl IntoConflictValueClause + for InsertFromSelect, Columns> +{ + type ValueClause = InsertFromSelect< + OnConflictSelectWrapper>, + Columns, + >; + + fn into_value_clause(self) -> Self::ValueClause { + let InsertFromSelect { columns, query } = self; + InsertFromSelect { + query: OnConflictSelectWrapper(query), + columns, + } + } +} + +impl<'a, ST, QS, DB, Columns> IntoConflictValueClause + for InsertFromSelect, Columns> +{ + type ValueClause = + InsertFromSelect>, Columns>; + + fn into_value_clause(self) -> Self::ValueClause { + let InsertFromSelect { columns, query } = self; + InsertFromSelect { + query: OnConflictSelectWrapper(query), + columns, + } + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/upsert/mod.rs b/collector/benchmarks/diesel/diesel/src/query_builder/upsert/mod.rs new file mode 100644 index 000000000..25122db3b --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/upsert/mod.rs @@ -0,0 +1,5 @@ +pub(crate) mod into_conflict_clause; +pub(crate) mod on_conflict_actions; +pub(crate) mod on_conflict_clause; +pub(crate) mod on_conflict_target; +pub(crate) mod on_conflict_target_decorations; diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_actions.rs b/collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_actions.rs new file mode 100644 index 000000000..3a3d71f6c --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_actions.rs @@ -0,0 +1,84 @@ +use crate::backend::{Backend, SupportsOnConflictClause}; +use crate::expression::{AppearsOnTable, Expression}; +use crate::query_builder::*; +use crate::query_source::*; +use crate::result::QueryResult; + +#[doc(hidden)] +#[derive(Debug, Clone, Copy)] +pub struct DoNothing; + +impl QueryFragment for DoNothing +where + DB: Backend + SupportsOnConflictClause, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" DO NOTHING"); + Ok(()) + } +} + +#[doc(hidden)] +#[derive(Debug, Clone, Copy)] +pub struct DoUpdate { + changeset: T, +} + +impl DoUpdate { + pub(crate) fn new(changeset: T) -> Self { + DoUpdate { changeset } + } +} + +impl QueryFragment for DoUpdate +where + DB: Backend + SupportsOnConflictClause, + T: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.unsafe_to_cache_prepared(); + if self.changeset.is_noop()? { + out.push_sql(" DO NOTHING"); + } else { + out.push_sql(" DO UPDATE SET "); + self.changeset.walk_ast(out.reborrow())?; + } + Ok(()) + } +} + +#[doc(hidden)] +#[derive(Debug, Clone, Copy)] +pub struct Excluded(T); + +impl Excluded { + pub(crate) fn new(t: T) -> Self { + Excluded(t) + } +} + +impl QueryFragment for Excluded +where + DB: Backend + SupportsOnConflictClause, + T: Column, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql("excluded."); + out.push_identifier(T::NAME)?; + Ok(()) + } +} + +impl Expression for Excluded +where + T: Expression, +{ + type SqlType = T::SqlType; +} + +impl AppearsOnTable for Excluded +where + T: Column, + Excluded: Expression, +{ +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_clause.rs b/collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_clause.rs new file mode 100644 index 000000000..95246cdd7 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_clause.rs @@ -0,0 +1,57 @@ +use super::on_conflict_actions::*; +use super::on_conflict_target::*; +use crate::backend::{Backend, SupportsOnConflictClause}; +use crate::insertable::*; +use crate::query_builder::*; +use crate::result::QueryResult; + +#[doc(hidden)] +#[derive(Debug, Clone, Copy)] +pub struct OnConflictValues { + values: Values, + target: Target, + action: Action, +} + +impl OnConflictValues { + pub(crate) fn do_nothing(values: Values) -> Self { + Self::new(values, NoConflictTarget, DoNothing) + } +} + +impl OnConflictValues { + pub(crate) fn new(values: Values, target: Target, action: Action) -> Self { + OnConflictValues { + values, + target, + action, + } + } +} + +impl CanInsertInSingleQuery + for OnConflictValues +where + DB: Backend + SupportsOnConflictClause, + Values: CanInsertInSingleQuery, +{ + fn rows_to_insert(&self) -> Option { + self.values.rows_to_insert() + } +} + +impl QueryFragment for OnConflictValues +where + DB: Backend + SupportsOnConflictClause, + Values: QueryFragment, + Target: QueryFragment, + Action: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + self.values.walk_ast(out.reborrow())?; + out.push_sql(" ON CONFLICT"); + self.target.walk_ast(out.reborrow())?; + self.action.walk_ast(out.reborrow())?; + Ok(()) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_target.rs b/collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_target.rs new file mode 100644 index 000000000..45028d46c --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_target.rs @@ -0,0 +1,106 @@ +use crate::backend::{Backend, SupportsOnConflictClause}; +use crate::expression::SqlLiteral; +use crate::query_builder::*; +use crate::query_source::Column; +use crate::result::QueryResult; + +#[doc(hidden)] +pub trait OnConflictTarget
{} + +#[doc(hidden)] +#[derive(Debug, Clone, Copy)] +pub struct NoConflictTarget; + +impl QueryFragment for NoConflictTarget +where + DB: Backend + SupportsOnConflictClause, +{ + fn walk_ast(&self, _: AstPass) -> QueryResult<()> { + Ok(()) + } +} + +impl
OnConflictTarget
for NoConflictTarget {} + +#[doc(hidden)] +#[derive(Debug, Clone, Copy)] +pub struct ConflictTarget(pub T); + +impl QueryFragment for ConflictTarget +where + DB: Backend + SupportsOnConflictClause, + T: Column, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" ("); + out.push_identifier(T::NAME)?; + out.push_sql(")"); + Ok(()) + } +} + +impl OnConflictTarget for ConflictTarget where T: Column {} + +impl QueryFragment for ConflictTarget> +where + DB: Backend + SupportsOnConflictClause, + SqlLiteral: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" "); + self.0.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl OnConflictTarget for ConflictTarget> {} + +impl QueryFragment for ConflictTarget<(T,)> +where + DB: Backend + SupportsOnConflictClause, + T: Column, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" ("); + out.push_identifier(T::NAME)?; + out.push_sql(")"); + Ok(()) + } +} + +impl OnConflictTarget for ConflictTarget<(T,)> where T: Column {} + +macro_rules! on_conflict_tuples { + ($( + $Tuple:tt { + $(($idx:tt) -> $T:ident, $ST:ident, $TT:ident,)* + } + )+) => { + $( + impl<_DB, _T, $($T),*> QueryFragment<_DB> for ConflictTarget<(_T, $($T),*)> where + _DB: Backend + SupportsOnConflictClause, + _T: Column, + $($T: Column,)* + { + fn walk_ast(&self, mut out: AstPass<_DB>) -> QueryResult<()> { + out.push_sql(" ("); + out.push_identifier(_T::NAME)?; + $( + out.push_sql(", "); + out.push_identifier($T::NAME)?; + )* + out.push_sql(")"); + Ok(()) + } + } + + impl<_T, $($T),*> OnConflictTarget<_T::Table> for ConflictTarget<(_T, $($T),*)> where + _T: Column, + $($T: Column,)* + { + } + )* + } +} + +__diesel_for_each_tuple!(on_conflict_tuples); diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_target_decorations.rs b/collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_target_decorations.rs new file mode 100644 index 000000000..a510723ee --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/upsert/on_conflict_target_decorations.rs @@ -0,0 +1,71 @@ +use crate::backend::{Backend, SupportsOnConflictClause, SupportsOnConflictTargetDecorations}; +use crate::expression::Expression; +use crate::query_builder::upsert::on_conflict_target::{ConflictTarget, NoConflictTarget}; +use crate::query_builder::where_clause::{NoWhereClause, WhereAnd, WhereClause}; +use crate::query_builder::{AstPass, QueryFragment, QueryResult}; +use crate::sql_types::BoolOrNullableBool; + +pub trait UndecoratedConflictTarget {} + +impl UndecoratedConflictTarget for NoConflictTarget {} +impl UndecoratedConflictTarget for ConflictTarget {} + +/// Interface to add information to conflict targets. +/// Designed to be open for further additions to conflict targets like constraints +pub trait DecoratableTarget

{ + /// Output type of filter_target operation + type FilterOutput; + /// equivalent to filter of FilterDsl but aimed at conflict targets + fn filter_target(self, predicate: P) -> Self::FilterOutput; +} + +#[derive(Debug)] +pub struct DecoratedConflictTarget { + target: T, + where_clause: U, +} + +impl DecoratableTarget

for T +where + P: Expression, + P::SqlType: BoolOrNullableBool, + T: UndecoratedConflictTarget, +{ + type FilterOutput = DecoratedConflictTarget>; + + fn filter_target(self, predicate: P) -> Self::FilterOutput { + DecoratedConflictTarget { + target: self, + where_clause: NoWhereClause.and(predicate), + } + } +} + +impl DecoratableTarget

for DecoratedConflictTarget +where + P: Expression, + P::SqlType: BoolOrNullableBool, + U: WhereAnd

, +{ + type FilterOutput = DecoratedConflictTarget>::Output>; + + fn filter_target(self, predicate: P) -> Self::FilterOutput { + DecoratedConflictTarget { + target: self.target, + where_clause: self.where_clause.and(predicate), + } + } +} + +impl QueryFragment for DecoratedConflictTarget +where + T: QueryFragment, + U: QueryFragment, + DB: Backend + SupportsOnConflictClause + SupportsOnConflictTargetDecorations, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + self.target.walk_ast(out.reborrow())?; + self.where_clause.walk_ast(out.reborrow())?; + Ok(()) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_builder/where_clause.rs b/collector/benchmarks/diesel/diesel/src/query_builder/where_clause.rs new file mode 100644 index 000000000..ac53f0da4 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_builder/where_clause.rs @@ -0,0 +1,190 @@ +use super::*; +use crate::backend::Backend; +use crate::expression::grouped::Grouped; +use crate::expression::operators::{And, Or}; +use crate::expression::*; +use crate::result::QueryResult; +use crate::sql_types::BoolOrNullableBool; + +/// Add `Predicate` to the current `WHERE` clause, joining with `AND` if +/// applicable. +pub trait WhereAnd { + /// What is the type of the resulting `WHERE` clause? + type Output; + + /// See the trait-level docs. + fn and(self, predicate: Predicate) -> Self::Output; +} + +/// Add `Predicate` to the current `WHERE` clause, joining with `OR` if +/// applicable. +pub trait WhereOr { + /// What is the type of the resulting `WHERE` clause? + type Output; + + /// See the trait-level docs. + fn or(self, predicate: Predicate) -> Self::Output; +} + +/// Represents that a query has no `WHERE` clause. +#[derive(Debug, Clone, Copy, QueryId)] +pub struct NoWhereClause; + +impl QueryFragment for NoWhereClause { + fn walk_ast(&self, _: AstPass) -> QueryResult<()> { + Ok(()) + } +} + +impl WhereAnd for NoWhereClause +where + Predicate: Expression, + Predicate::SqlType: BoolOrNullableBool, +{ + type Output = WhereClause; + + fn and(self, predicate: Predicate) -> Self::Output { + WhereClause(predicate) + } +} + +impl WhereOr for NoWhereClause +where + Predicate: Expression, + Predicate::SqlType: BoolOrNullableBool, +{ + type Output = WhereClause; + + fn or(self, predicate: Predicate) -> Self::Output { + WhereClause(predicate) + } +} + +impl<'a, DB> Into> for NoWhereClause { + fn into(self) -> BoxedWhereClause<'a, DB> { + BoxedWhereClause::None + } +} + +/// The `WHERE` clause of a query. +#[derive(Debug, Clone, Copy, QueryId)] +pub struct WhereClause(Expr); + +impl QueryFragment for WhereClause +where + DB: Backend, + Expr: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" WHERE "); + self.0.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl WhereAnd for WhereClause +where + Expr: Expression, + Expr::SqlType: BoolOrNullableBool, + Predicate: Expression, + Predicate::SqlType: BoolOrNullableBool, +{ + type Output = WhereClause>>; + + fn and(self, predicate: Predicate) -> Self::Output { + WhereClause(Grouped(And::new(self.0, predicate))) + } +} + +impl WhereOr for WhereClause +where + Expr: Expression, + Expr::SqlType: BoolOrNullableBool, + Predicate: Expression, + Predicate::SqlType: BoolOrNullableBool, +{ + type Output = WhereClause>>; + + fn or(self, predicate: Predicate) -> Self::Output { + WhereClause(Grouped(Or::new(self.0, predicate))) + } +} + +impl<'a, DB, Predicate> Into> for WhereClause +where + DB: Backend, + Predicate: QueryFragment + Send + 'a, +{ + fn into(self) -> BoxedWhereClause<'a, DB> { + BoxedWhereClause::Where(Box::new(self.0)) + } +} + +/// Marker trait indicating that a `WHERE` clause is valid for a given query +/// source. +pub trait ValidWhereClause {} + +impl ValidWhereClause for NoWhereClause {} + +impl ValidWhereClause for WhereClause where Expr: AppearsOnTable {} + +#[allow(missing_debug_implementations)] // We can't... +pub enum BoxedWhereClause<'a, DB> { + Where(Box + Send + 'a>), + None, +} + +impl<'a, DB> QueryFragment for BoxedWhereClause<'a, DB> +where + DB: Backend, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + match *self { + BoxedWhereClause::Where(ref where_clause) => { + out.push_sql(" WHERE "); + where_clause.walk_ast(out) + } + BoxedWhereClause::None => Ok(()), + } + } +} + +impl<'a, DB> QueryId for BoxedWhereClause<'a, DB> { + type QueryId = (); + + const HAS_STATIC_QUERY_ID: bool = false; +} + +impl<'a, DB, Predicate> WhereAnd for BoxedWhereClause<'a, DB> +where + DB: Backend + 'a, + Predicate: QueryFragment + Send + 'a, +{ + type Output = Self; + + fn and(self, predicate: Predicate) -> Self::Output { + use self::BoxedWhereClause::Where; + + match self { + Where(where_clause) => Where(Box::new(Grouped(And::new(where_clause, predicate)))), + BoxedWhereClause::None => Where(Box::new(predicate)), + } + } +} + +impl<'a, DB, Predicate> WhereOr for BoxedWhereClause<'a, DB> +where + DB: Backend + 'a, + Predicate: QueryFragment + Send + 'a, +{ + type Output = Self; + + fn or(self, predicate: Predicate) -> Self::Output { + use self::BoxedWhereClause::Where; + + match self { + Where(where_clause) => Where(Box::new(Grouped(Or::new(where_clause, predicate)))), + BoxedWhereClause::None => Where(Box::new(predicate)), + } + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/belonging_to_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/belonging_to_dsl.rs new file mode 100644 index 000000000..1c00c6447 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/belonging_to_dsl.rs @@ -0,0 +1,54 @@ +/// Constructs a query that finds record(s) based on directional association with other record(s). +/// +/// # Example +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # use schema::{posts, users}; +/// # +/// # #[derive(Identifiable, Queryable)] +/// # pub struct User { +/// # id: i32, +/// # name: String, +/// # } +/// # +/// # #[derive(Debug, PartialEq)] +/// # #[derive(Identifiable, Queryable, Associations)] +/// # #[belongs_to(User)] +/// # pub struct Post { +/// # id: i32, +/// # user_id: i32, +/// # title: String, +/// # } +/// # +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # let connection = establish_connection(); +/// # use self::users::dsl::*; +/// # use self::posts::dsl::{posts, title}; +/// let sean = users.filter(name.eq("Sean")).first::(&connection)?; +/// let tess = users.filter(name.eq("Tess")).first::(&connection)?; +/// +/// let seans_posts = Post::belonging_to(&sean) +/// .select(title) +/// .load::(&connection)?; +/// assert_eq!(vec!["My first post", "About Rust"], seans_posts); +/// +/// // A vec or slice can be passed as well +/// let more_posts = Post::belonging_to(&vec![sean, tess]) +/// .select(title) +/// .load::(&connection)?; +/// assert_eq!(vec!["My first post", "About Rust", "My first post too"], more_posts); +/// # Ok(()) +/// # } +/// ``` +pub trait BelongingToDsl { + /// The query returned by `belonging_to` + type Output; + + /// Get the record(s) belonging to record(s) `other` + fn belonging_to(other: T) -> Self::Output; +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/boxed_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/boxed_dsl.rs new file mode 100644 index 000000000..701bcce37 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/boxed_dsl.rs @@ -0,0 +1,36 @@ +use crate::dsl; +use crate::expression::TypedExpressionType; +use crate::expression::ValidGrouping; +use crate::query_builder::AsQuery; +use crate::query_builder::SelectStatement; +use crate::query_source::Table; +use crate::Expression; + +/// The `into_boxed` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `into_boxed` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait BoxedDsl<'a, DB> { + /// The return type of `internal_into_boxed` + type Output; + + /// See the trait documentation. + fn internal_into_boxed(self) -> dsl::IntoBoxed<'a, Self, DB>; +} + +impl<'a, T, DB> BoxedDsl<'a, DB> for T +where + T: Table + AsQuery>, + SelectStatement: BoxedDsl<'a, DB>, + T::DefaultSelection: Expression + ValidGrouping<()>, + T::SqlType: TypedExpressionType, +{ + type Output = dsl::IntoBoxed<'a, SelectStatement, DB>; + + fn internal_into_boxed(self) -> Self::Output { + self.as_query().internal_into_boxed() + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/distinct_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/distinct_dsl.rs new file mode 100644 index 000000000..85dbd2e4c --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/distinct_dsl.rs @@ -0,0 +1,68 @@ +use crate::dsl; +#[cfg(feature = "postgres")] +use crate::expression::SelectableExpression; +use crate::expression::TypedExpressionType; +use crate::expression::ValidGrouping; +use crate::query_builder::AsQuery; +use crate::query_builder::SelectStatement; +use crate::query_source::Table; +use crate::Expression; + +/// The `distinct` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `distinct` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait DistinctDsl { + /// The type returned by `.distinct` + type Output; + + /// See the trait documentation. + fn distinct(self) -> dsl::Distinct; +} + +impl DistinctDsl for T +where + T: Table + AsQuery>, + T::DefaultSelection: Expression + ValidGrouping<()>, + T::SqlType: TypedExpressionType, +{ + type Output = dsl::Distinct>; + + fn distinct(self) -> dsl::Distinct> { + self.as_query().distinct() + } +} + +/// The `distinct_on` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `distinct_on` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +#[cfg(feature = "postgres")] +pub trait DistinctOnDsl { + /// The type returned by `.distinct_on` + type Output; + + /// See the trait documentation + fn distinct_on(self, selection: Selection) -> dsl::DistinctOn; +} + +#[cfg(feature = "postgres")] +impl DistinctOnDsl for T +where + Selection: SelectableExpression, + T: Table + AsQuery>, + T::DefaultSelection: Expression + ValidGrouping<()>, + T::SqlType: TypedExpressionType, +{ + type Output = dsl::DistinctOn, Selection>; + + fn distinct_on(self, selection: Selection) -> dsl::DistinctOn { + self.as_query().distinct_on(selection) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/filter_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/filter_dsl.rs new file mode 100644 index 000000000..9247b6abd --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/filter_dsl.rs @@ -0,0 +1,85 @@ +use crate::dsl::{Filter, OrFilter}; +use crate::expression_methods::*; +use crate::query_source::*; + +/// The `filter` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `filter` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait FilterDsl { + /// The type returned by `.filter`. + type Output; + + /// See the trait documentation. + fn filter(self, predicate: Predicate) -> Self::Output; +} + +impl FilterDsl for T +where + T: Table, + T::Query: FilterDsl, +{ + type Output = Filter; + + fn filter(self, predicate: Predicate) -> Self::Output { + self.as_query().filter(predicate) + } +} + +/// The `find` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `find` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait FindDsl { + /// The type returned by `.find`. + type Output; + + /// See the trait documentation. + fn find(self, id: PK) -> Self::Output; +} + +impl FindDsl for T +where + T: Table + FilterDsl<<::PrimaryKey as EqAll>::Output>, + T::PrimaryKey: EqAll, +{ + type Output = Filter>::Output>; + + fn find(self, id: PK) -> Self::Output { + let primary_key = self.primary_key(); + self.filter(primary_key.eq_all(id)) + } +} + +/// The `or_filter` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `or_filter` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait OrFilterDsl { + /// The type returned by `.filter`. + type Output; + + /// See the trait documentation. + fn or_filter(self, predicate: Predicate) -> Self::Output; +} + +impl OrFilterDsl for T +where + T: Table, + T::Query: OrFilterDsl, +{ + type Output = OrFilter; + + fn or_filter(self, predicate: Predicate) -> Self::Output { + self.as_query().or_filter(predicate) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/group_by_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/group_by_dsl.rs new file mode 100644 index 000000000..517ec2b32 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/group_by_dsl.rs @@ -0,0 +1,35 @@ +use crate::dsl; +use crate::expression::Expression; +use crate::expression::TypedExpressionType; +use crate::expression::ValidGrouping; +use crate::query_builder::{AsQuery, SelectStatement}; +use crate::query_source::Table; + +/// The `group_by` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `group_by` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait GroupByDsl { + /// The type returned by `.group_by` + type Output; + + /// See the trait documentation. + fn group_by(self, expr: Expr) -> dsl::GroupBy; +} + +impl GroupByDsl for T +where + Expr: Expression, + T: Table + AsQuery>, + T::DefaultSelection: Expression + ValidGrouping<()>, + T::SqlType: TypedExpressionType, +{ + type Output = dsl::GroupBy, Expr>; + + fn group_by(self, expr: Expr) -> dsl::GroupBy { + self.as_query().group_by(expr) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/join_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/join_dsl.rs new file mode 100644 index 000000000..31b740dfc --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/join_dsl.rs @@ -0,0 +1,80 @@ +use crate::query_builder::AsQuery; +use crate::query_source::joins::OnClauseWrapper; +use crate::query_source::{JoinTo, QuerySource, Table}; + +#[doc(hidden)] +/// `JoinDsl` support trait to emulate associated type constructors +pub trait InternalJoinDsl { + type Output; + + fn join(self, rhs: Rhs, kind: Kind, on: On) -> Self::Output; +} + +impl InternalJoinDsl for T +where + T: Table + AsQuery, + T::Query: InternalJoinDsl, +{ + type Output = >::Output; + + fn join(self, rhs: Rhs, kind: Kind, on: On) -> Self::Output { + self.as_query().join(rhs, kind, on) + } +} + +#[doc(hidden)] +/// `JoinDsl` support trait to emulate associated type constructors and grab +/// the known on clause from the associations API +pub trait JoinWithImplicitOnClause { + type Output; + + fn join_with_implicit_on_clause(self, rhs: Rhs, kind: Kind) -> Self::Output; +} + +impl JoinWithImplicitOnClause for Lhs +where + Lhs: JoinTo, + Lhs: InternalJoinDsl<>::FromClause, Kind, >::OnClause>, +{ + type Output = >::Output; + + fn join_with_implicit_on_clause(self, rhs: Rhs, kind: Kind) -> Self::Output { + let (from, on) = Lhs::join_target(rhs); + self.join(from, kind, on) + } +} + +/// Specify the `ON` clause for a join statement. This will override +/// any implicit `ON` clause that would come from [`joinable!`] +/// +/// [`joinable!`]: ../macro.joinable.html +/// +/// # Example +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # use schema::{users, posts}; +/// # +/// # fn main() { +/// # let connection = establish_connection(); +/// let data = users::table +/// .left_join(posts::table.on( +/// users::id.eq(posts::user_id).and( +/// posts::title.eq("My first post")) +/// )) +/// .select((users::name, posts::title.nullable())) +/// .load(&connection); +/// let expected = vec![ +/// ("Sean".to_string(), Some("My first post".to_string())), +/// ("Tess".to_string(), None), +/// ]; +/// assert_eq!(Ok(expected), data); +/// # } +pub trait JoinOnDsl: Sized { + /// See the trait documentation. + fn on(self, on: On) -> OnClauseWrapper { + OnClauseWrapper::new(self, on) + } +} + +impl JoinOnDsl for T {} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/limit_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/limit_dsl.rs new file mode 100644 index 000000000..b9f94dfb8 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/limit_dsl.rs @@ -0,0 +1,28 @@ +use crate::query_source::Table; + +/// The `limit` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `limit` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait LimitDsl { + /// The type returned by `.limit` + type Output; + + /// See the trait documentation + fn limit(self, limit: i64) -> Self::Output; +} + +impl LimitDsl for T +where + T: Table, + T::Query: LimitDsl, +{ + type Output = ::Output; + + fn limit(self, limit: i64) -> Self::Output { + self.as_query().limit(limit) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/load_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/load_dsl.rs new file mode 100644 index 000000000..21246de94 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/load_dsl.rs @@ -0,0 +1,57 @@ +use super::RunQueryDsl; +use crate::backend::Backend; +use crate::connection::Connection; +use crate::deserialize::FromSqlRow; +use crate::expression::QueryMetadata; +use crate::query_builder::{AsQuery, QueryFragment, QueryId}; +use crate::result::QueryResult; + +/// The `load` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`RunQueryDsl`]. However, you may need a where clause on this trait +/// to call `load` from generic code. +/// +/// [`RunQueryDsl`]: ../trait.RunQueryDsl.html +pub trait LoadQuery: RunQueryDsl { + /// Load this query + fn internal_load(self, conn: &Conn) -> QueryResult>; +} + +impl LoadQuery for T +where + Conn: Connection, + T: AsQuery + RunQueryDsl, + T::Query: QueryFragment + QueryId, + U: FromSqlRow, + Conn::Backend: QueryMetadata, +{ + fn internal_load(self, conn: &Conn) -> QueryResult> { + conn.load(self) + } +} + +/// The `execute` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`RunQueryDsl`]. However, you may need a where clause on this trait +/// to call `execute` from generic code. +/// +/// [`RunQueryDsl`]: ../trait.RunQueryDsl.html +pub trait ExecuteDsl, DB: Backend = ::Backend>: + Sized +{ + /// Execute this command + fn execute(query: Self, conn: &Conn) -> QueryResult; +} + +impl ExecuteDsl for T +where + Conn: Connection, + DB: Backend, + T: QueryFragment + QueryId, +{ + fn execute(query: Self, conn: &Conn) -> QueryResult { + conn.execute_returning_count(&query) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/locking_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/locking_dsl.rs new file mode 100644 index 000000000..c0de6e17f --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/locking_dsl.rs @@ -0,0 +1,55 @@ +use crate::expression::TypedExpressionType; +use crate::expression::ValidGrouping; +use crate::query_builder::AsQuery; +use crate::query_builder::SelectStatement; +use crate::query_source::Table; +use crate::Expression; + +/// Methods related to locking select statements +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `for_update` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait LockingDsl { + /// The type returned by `set_lock`. See [`dsl::ForUpdate`] and friends for + /// convenient access to this type. + /// + /// [`dsl::ForUpdate`]: ../../dsl/type.ForUpdate.html + type Output; + + /// See the trait level documentation + fn with_lock(self, lock: Lock) -> Self::Output; +} + +impl LockingDsl for T +where + T: Table + AsQuery>, + T::DefaultSelection: Expression + ValidGrouping<()>, + T::SqlType: TypedExpressionType, +{ + type Output = as LockingDsl>::Output; + + fn with_lock(self, lock: Lock) -> Self::Output { + self.as_query().with_lock(lock) + } +} + +/// Methods related to modifiers on locking select statements +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `skip_locked` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait ModifyLockDsl { + /// The type returned by `modify_lock`. See [`dsl::SkipLocked`] and friends + /// for convenient access to this type. + /// + /// [`dsl::SkipLocked`]: ../../dsl/type.SkipLocked.html + type Output; + + /// See the trait level documentation + fn modify_lock(self, modifier: Modifier) -> Self::Output; +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/mod.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/mod.rs new file mode 100644 index 000000000..d3c45ff47 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/mod.rs @@ -0,0 +1,1354 @@ +//! Traits that construct SELECT statements +//! +//! Traits in this module have methods that generally map to the keyword for the corresponding clause in SQL, +//! unless it conflicts with a Rust keyword (such as `WHERE`/`where`). +//! +//! Methods for constructing queries lives on the [`QueryDsl`] trait. +//! Methods for executing queries live on [`RunQueryDsl`]. +//! +//! See also [`expression_methods`][expression_methods] and [`dsl`][dsl]. +//! +//! [expression_methods]: ../expression_methods/index.html +//! [dsl]: ../dsl/index.html +//! [`QueryDsl`]: trait.QueryDsl.html +//! [`RunQueryDsl`]: trait.RunQueryDsl.html + +use crate::backend::Backend; +use crate::connection::Connection; +use crate::expression::count::CountStar; +use crate::expression::Expression; +use crate::helper_types::*; +use crate::query_builder::locking_clause as lock; +use crate::query_source::{joins, Table}; +use crate::result::{first_or_not_found, QueryResult}; + +mod belonging_to_dsl; +#[doc(hidden)] +pub mod boxed_dsl; +mod distinct_dsl; +#[doc(hidden)] +pub mod filter_dsl; +mod group_by_dsl; +mod join_dsl; +#[doc(hidden)] +pub mod limit_dsl; +#[doc(hidden)] +pub mod load_dsl; +mod locking_dsl; +mod nullable_select_dsl; +mod offset_dsl; +mod order_dsl; +mod save_changes_dsl; +#[doc(hidden)] +pub mod select_dsl; +mod single_value_dsl; + +pub use self::belonging_to_dsl::BelongingToDsl; +pub use self::join_dsl::{InternalJoinDsl, JoinOnDsl, JoinWithImplicitOnClause}; +#[doc(hidden)] +pub use self::load_dsl::LoadQuery; +pub use self::save_changes_dsl::{SaveChangesDsl, UpdateAndFetchResults}; + +/// The traits used by `QueryDsl`. +/// +/// Each trait in this module represents exactly one method from `QueryDsl`. +/// Apps should general rely on `QueryDsl` directly, rather than these traits. +/// However, generic code may need to include a where clause that references +/// these traits. +pub mod methods { + pub use super::boxed_dsl::BoxedDsl; + pub use super::distinct_dsl::*; + #[doc(inline)] + pub use super::filter_dsl::*; + pub use super::group_by_dsl::GroupByDsl; + pub use super::limit_dsl::LimitDsl; + pub use super::load_dsl::{ExecuteDsl, LoadQuery}; + pub use super::locking_dsl::{LockingDsl, ModifyLockDsl}; + pub use super::nullable_select_dsl::SelectNullableDsl; + pub use super::offset_dsl::OffsetDsl; + pub use super::order_dsl::{OrderDsl, ThenOrderDsl}; + pub use super::select_dsl::SelectDsl; + pub use super::single_value_dsl::SingleValueDsl; +} + +/// Methods used to construct select statements. +pub trait QueryDsl: Sized { + /// Adds the `DISTINCT` keyword to a query. + /// + /// This method will override any previous distinct clause that was present. + /// For example, on PostgreSQL, `foo.distinct_on(bar).distinct()` will + /// create the same query as `foo.distinct()`. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// # connection.execute("DELETE FROM users").unwrap(); + /// diesel::insert_into(users) + /// .values(&vec![name.eq("Sean"); 3]) + /// .execute(&connection)?; + /// let names = users.select(name).load::(&connection)?; + /// let distinct_names = users.select(name).distinct().load::(&connection)?; + /// + /// assert_eq!(vec!["Sean"; 3], names); + /// assert_eq!(vec!["Sean"; 1], distinct_names); + /// # Ok(()) + /// # } + /// ``` + fn distinct(self) -> Distinct + where + Self: methods::DistinctDsl, + { + methods::DistinctDsl::distinct(self) + } + + /// Adds the `DISTINCT ON` clause to a query. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::animals; + /// # + /// # #[derive(Queryable, Debug, PartialEq)] + /// # struct Animal { + /// # species: String, + /// # name: Option, + /// # legs: i32, + /// # } + /// # + /// # impl Animal { + /// # fn new>(species: S, name: Option<&str>, legs: i32) -> Self { + /// # Animal { + /// # species: species.into(), + /// # name: name.map(Into::into), + /// # legs + /// # } + /// # } + /// # } + /// # + /// # fn main() { + /// # use self::animals::dsl::*; + /// # let connection = establish_connection(); + /// # connection.execute("DELETE FROM animals").unwrap(); + /// diesel::insert_into(animals) + /// .values(&vec![ + /// (species.eq("dog"), name.eq(Some("Jack")), legs.eq(4)), + /// (species.eq("dog"), name.eq(None), legs.eq(4)), + /// (species.eq("spider"), name.eq(None), legs.eq(8)), + /// ]) + /// .execute(&connection) + /// .unwrap(); + /// let all_animals = animals.select((species, name, legs)).load(&connection); + /// let distinct_animals = animals.select((species, name, legs)).distinct_on(species).load(&connection); + /// + /// assert_eq!(Ok(vec![Animal::new("dog", Some("Jack"), 4), + /// Animal::new("dog", None, 4), + /// Animal::new("spider", None, 8)]), all_animals); + /// assert_eq!(Ok(vec![Animal::new("dog", Some("Jack"), 4), + /// Animal::new("spider", None, 8)]), distinct_animals); + /// # } + /// ``` + #[cfg(feature = "postgres")] + fn distinct_on(self, expr: Expr) -> DistinctOn + where + Self: methods::DistinctOnDsl, + { + methods::DistinctOnDsl::distinct_on(self, expr) + } + + // FIXME: Needs usage example and doc rewrite + /// Adds a `SELECT` clause to the query. + /// + /// If there was already a select clause present, it will be overridden. + /// For example, `foo.select(bar).select(baz)` will produce the same + /// query as `foo.select(baz)`. + /// + /// By default, the select clause will be roughly equivalent to `SELECT *` + /// (however, Diesel will list all columns to ensure that they are in the + /// order we expect). + /// + /// `select` has slightly stricter bounds on its arguments than other + /// methods. In particular, when used with a left outer join, `.nullable` + /// must be called on columns that come from the right side of a join. It + /// can be called on the column itself, or on an expression containing that + /// column. `title.nullable()`, `lower(title).nullable()`, and `(id, + /// title).nullable()` would all be valid. + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::users; + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use self::users::dsl::*; + /// # let connection = establish_connection(); + /// // By default, all columns will be selected + /// let all_users = users.load::<(i32, String)>(&connection)?; + /// assert_eq!(vec![(1, String::from("Sean")), (2, String::from("Tess"))], all_users); + /// + /// let all_names = users.select(name).load::(&connection)?; + /// assert_eq!(vec!["Sean", "Tess"], all_names); + /// # Ok(()) + /// # } + /// ``` + /// + /// ### When used with a left join + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::{users, posts}; + /// # + /// # #[derive(Queryable, PartialEq, Eq, Debug)] + /// # struct User { + /// # id: i32, + /// # name: String, + /// # } + /// # + /// # impl User { + /// # fn new(id: i32, name: &str) -> Self { + /// # User { + /// # id, + /// # name: name.into(), + /// # } + /// # } + /// # } + /// # + /// # #[derive(Queryable, PartialEq, Eq, Debug)] + /// # struct Post { + /// # id: i32, + /// # user_id: i32, + /// # title: String, + /// # } + /// # + /// # impl Post { + /// # fn new(id: i32, user_id: i32, title: &str) -> Self { + /// # Post { + /// # id, + /// # user_id, + /// # title: title.into(), + /// # } + /// # } + /// # } + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # let connection = establish_connection(); + /// # connection.execute("DELETE FROM posts")?; + /// # diesel::insert_into(posts::table) + /// # .values((posts::user_id.eq(1), posts::title.eq("Sean's Post"))) + /// # .execute(&connection)?; + /// # let post_id = posts::table.select(posts::id) + /// # .first::(&connection)?; + /// let join = users::table.left_join(posts::table); + /// + /// // By default, all columns from both tables are selected + /// let all_data = join.load::<(User, Option)>(&connection)?; + /// let expected_data = vec![ + /// (User::new(1, "Sean"), Some(Post::new(post_id, 1, "Sean's Post"))), + /// (User::new(2, "Tess"), None), + /// ]; + /// assert_eq!(expected_data, all_data); + /// + /// // Since `posts` is on the right side of a left join, `.nullable` is + /// // needed. + /// let names_and_titles = join.select((users::name, posts::title.nullable())) + /// .load::<(String, Option)>(&connection)?; + /// let expected_data = vec![ + /// (String::from("Sean"), Some(String::from("Sean's Post"))), + /// (String::from("Tess"), None), + /// ]; + /// assert_eq!(expected_data, names_and_titles); + /// # Ok(()) + /// # } + /// ``` + fn select(self, selection: Selection) -> Select + where + Selection: Expression, + Self: methods::SelectDsl, + { + methods::SelectDsl::select(self, selection) + } + + /// Get the count of a query. This is equivalent to `.select(count_star())` + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let count = users.count().get_result(&connection); + /// assert_eq!(Ok(2), count); + /// # } + /// ``` + fn count(self) -> Select + where + Self: methods::SelectDsl, + { + use crate::dsl::count_star; + + QueryDsl::select(self, count_star()) + } + + /// Join two tables using a SQL `INNER JOIN`. + /// + /// If you have invoked [`joinable!`] for the two tables, you can pass that + /// table directly. Otherwise you will need to use [`.on`] to specify the `ON` + /// clause. + /// + /// [`joinable!`]: ../macro.joinable.html + /// [`.on`]: trait.JoinOnDsl.html#method.on + /// + /// You can join to as many tables as you'd like in a query, with the + /// restriction that no table can appear in the query more than once. The reason + /// for this restriction is that one of the appearances would require aliasing, + /// and we do not currently have a fleshed out story for dealing with table + /// aliases. + /// + /// You will also need to call [`allow_tables_to_appear_in_same_query!`]. + /// If you are using `diesel print-schema`, this will + /// have been generated for you. + /// See the documentation for [`allow_tables_to_appear_in_same_query!`] for + /// details. + /// + /// Diesel expects multi-table joins to be semantically grouped based on the + /// relationships. For example, `users.inner_join(posts.inner_join(comments))` + /// is not the same as `users.inner_join(posts).inner_join(comments)`. The first + /// would deserialize into `(User, (Post, Comment))` and generate the following + /// SQL: + /// + /// ```sql + /// SELECT * FROM users + /// INNER JOIN posts ON posts.user_id = users.id + /// INNER JOIN comments ON comments.post_id = posts.id + /// ``` + /// + /// While the second query would deserialize into `(User, Post, Comment)` and + /// generate the following SQL: + /// + /// ```sql + /// SELECT * FROM users + /// INNER JOIN posts ON posts.user_id = users.id + /// INNER JOIN comments ON comments.user_id = users.id + /// ``` + /// + /// [associations]: ../associations/index.html + /// [`allow_tables_to_appear_in_same_query!`]: ../macro.allow_tables_to_appear_in_same_query.html + /// + /// # Examples + /// + /// ### With implicit `ON` clause + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::{users, posts}; + /// # /* + /// joinable!(posts -> users (user_id)); + /// allow_tables_to_appear_in_same_query!(users, posts); + /// # */ + /// + /// # fn main() { + /// # use self::users::dsl::{users, name}; + /// # use self::posts::dsl::{posts, user_id, title}; + /// # let connection = establish_connection(); + /// let data = users.inner_join(posts) + /// .select((name, title)) + /// .load(&connection); + /// + /// let expected_data = vec![ + /// (String::from("Sean"), String::from("My first post")), + /// (String::from("Sean"), String::from("About Rust")), + /// (String::from("Tess"), String::from("My first post too")), + /// ]; + /// assert_eq!(Ok(expected_data), data); + /// # } + /// ``` + /// + /// ### With explicit `ON` clause + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::{users, posts}; + /// # + /// # /* + /// allow_tables_to_appear_in_same_query!(users, posts); + /// # */ + /// + /// # fn main() { + /// # use self::users::dsl::{users, name}; + /// # use self::posts::dsl::{posts, user_id, title}; + /// # let connection = establish_connection(); + /// diesel::insert_into(posts) + /// .values(&vec![ + /// (user_id.eq(1), title.eq("Sean's post")), + /// (user_id.eq(2), title.eq("Sean is a jerk")), + /// ]) + /// .execute(&connection) + /// .unwrap(); + /// + /// let data = users + /// .inner_join(posts.on(title.like(name.concat("%")))) + /// .select((name, title)) + /// .load(&connection); + /// let expected_data = vec![ + /// (String::from("Sean"), String::from("Sean's post")), + /// (String::from("Sean"), String::from("Sean is a jerk")), + /// ]; + /// assert_eq!(Ok(expected_data), data); + /// # } + /// ``` + fn inner_join(self, rhs: Rhs) -> InnerJoin + where + Self: JoinWithImplicitOnClause, + { + self.join_with_implicit_on_clause(rhs, joins::Inner) + } + + /// Join two tables using a SQL `LEFT OUTER JOIN`. + /// + /// Behaves similarly to [`inner_join`], but will produce a left join + /// instead. See [`inner_join`] for usage examples. + /// + /// [`inner_join`]: #method.inner_join + fn left_outer_join(self, rhs: Rhs) -> LeftJoin + where + Self: JoinWithImplicitOnClause, + { + self.join_with_implicit_on_clause(rhs, joins::LeftOuter) + } + + /// Alias for [`left_outer_join`]. + /// + /// [`left_outer_join`]: #method.left_outer_join + fn left_join(self, rhs: Rhs) -> LeftJoin + where + Self: JoinWithImplicitOnClause, + { + self.left_outer_join(rhs) + } + + /// Adds to the `WHERE` clause of a query. + /// + /// If there is already a `WHERE` clause, the result will be `old AND new`. + /// + /// # Example: + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let seans_id = users.filter(name.eq("Sean")).select(id) + /// .first(&connection); + /// assert_eq!(Ok(1), seans_id); + /// let tess_id = users.filter(name.eq("Tess")).select(id) + /// .first(&connection); + /// assert_eq!(Ok(2), tess_id); + /// # } + /// ``` + fn filter(self, predicate: Predicate) -> Filter + where + Self: methods::FilterDsl, + { + methods::FilterDsl::filter(self, predicate) + } + + /// Adds to the `WHERE` clause of a query using `OR` + /// + /// If there is already a `WHERE` clause, the result will be `(old OR new)`. + /// Calling `foo.filter(bar).or_filter(baz)` + /// is identical to `foo.filter(bar.or(baz))`. + /// However, the second form is much harder to do dynamically. + /// + /// # Example: + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::animals::dsl::*; + /// # let connection = establish_connection(); + /// # diesel::delete(animals).execute(&connection)?; + /// diesel::insert_into(animals) + /// .values(&vec![ + /// (species.eq("cat"), legs.eq(4), name.eq("Sinatra")), + /// (species.eq("dog"), legs.eq(3), name.eq("Fido")), + /// (species.eq("spider"), legs.eq(8), name.eq("Charlotte")), + /// ]) + /// .execute(&connection)?; + /// + /// let good_animals = animals + /// .filter(name.eq("Fido")) + /// .or_filter(legs.eq(4)) + /// .select(name) + /// .get_results::>(&connection)?; + /// let expected = vec![ + /// Some(String::from("Sinatra")), + /// Some(String::from("Fido")), + /// ]; + /// assert_eq!(expected, good_animals); + /// # Ok(()) + /// # } + /// ``` + fn or_filter(self, predicate: Predicate) -> OrFilter + where + Self: methods::OrFilterDsl, + { + methods::OrFilterDsl::or_filter(self, predicate) + } + + /// Attempts to find a single record from the given table by primary key. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # use schema::users::dsl::*; + /// # use diesel::result::Error::NotFound; + /// # let connection = establish_connection(); + /// let sean = (1, "Sean".to_string()); + /// let tess = (2, "Tess".to_string()); + /// assert_eq!(Ok(sean), users.find(1).first(&connection)); + /// assert_eq!(Ok(tess), users.find(2).first(&connection)); + /// assert_eq!(Err::<(i32, String), _>(NotFound), users.find(3).first(&connection)); + /// # } + /// ``` + fn find(self, id: PK) -> Find + where + Self: methods::FindDsl, + { + methods::FindDsl::find(self, id) + } + + /// Sets the order clause of a query. + /// + /// If there was already an order clause, it will be overridden. See + /// also: + /// [`.desc()`](../expression_methods/trait.ExpressionMethods.html#method.desc) + /// and + /// [`.asc()`](../expression_methods/trait.ExpressionMethods.html#method.asc) + /// + /// Ordering by multiple columns can be achieved by passing a tuple of those + /// columns. + /// To construct an order clause of an unknown number of columns, + /// see [`QueryDsl::then_order_by`](#method.then_order_by) + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// # connection.execute("DELETE FROM users")?; + /// diesel::insert_into(users) + /// .values(&vec![ + /// name.eq("Saul"), + /// name.eq("Steve"), + /// name.eq("Stan"), + /// ]) + /// .execute(&connection)?; + /// + /// let ordered_names = users.select(name) + /// .order(name.desc()) + /// .load::(&connection)?; + /// assert_eq!(vec!["Steve", "Stan", "Saul"], ordered_names); + /// + /// diesel::insert_into(users).values(name.eq("Stan")).execute(&connection)?; + /// + /// let data = users.select((name, id)) + /// .order((name.asc(), id.desc())) + /// .load(&connection)?; + /// let expected_data = vec![ + /// (String::from("Saul"), 3), + /// (String::from("Stan"), 6), + /// (String::from("Stan"), 5), + /// (String::from("Steve"), 4), + /// ]; + /// assert_eq!(expected_data, data); + /// # Ok(()) + /// # } + /// ``` + fn order(self, expr: Expr) -> Order + where + Expr: Expression, + Self: methods::OrderDsl, + { + methods::OrderDsl::order(self, expr) + } + + /// Alias for `order` + fn order_by(self, expr: Expr) -> Order + where + Expr: Expression, + Self: methods::OrderDsl, + { + QueryDsl::order(self, expr) + } + + /// Appends to the `ORDER BY` clause of this SQL query. + /// + /// Unlike `.order`, this method will append rather than replace. + /// In other words, + /// `.order_by(foo).order_by(bar)` is equivalent to `.order_by(bar)`. + /// In contrast, + /// `.order_by(foo).then_order_by(bar)` is equivalent to `.order((foo, bar))`. + /// This method is only present on boxed queries. + /// + /// # Examples + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// # connection.execute("DELETE FROM users")?; + /// diesel::insert_into(users) + /// .values(&vec![ + /// name.eq("Saul"), + /// name.eq("Steve"), + /// name.eq("Stan"), + /// name.eq("Stan"), + /// ]) + /// .execute(&connection)?; + /// + /// let data = users.select((name, id)) + /// .order_by(name.asc()) + /// .then_order_by(id.desc()) + /// .load(&connection)?; + /// let expected_data = vec![ + /// (String::from("Saul"), 3), + /// (String::from("Stan"), 6), + /// (String::from("Stan"), 5), + /// (String::from("Steve"), 4), + /// ]; + /// assert_eq!(expected_data, data); + /// # Ok(()) + /// # } + /// ``` + fn then_order_by(self, order: Order) -> ThenOrderBy + where + Self: methods::ThenOrderDsl, + { + methods::ThenOrderDsl::then_order_by(self, order) + } + + /// Sets the limit clause of the query. + /// + /// If there was already a limit clause, it will be overridden. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::users; + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use self::users::dsl::*; + /// # let connection = establish_connection(); + /// # diesel::delete(users).execute(&connection)?; + /// # diesel::insert_into(users) + /// # .values(&vec![ + /// # name.eq("Sean"), + /// # name.eq("Bastien"), + /// # name.eq("Pascal"), + /// # ]) + /// # .execute(&connection)?; + /// # + /// // Using a limit + /// let limited = users.select(name) + /// .order(id) + /// .limit(1) + /// .load::(&connection)?; + /// + /// // Without a limit + /// let no_limit = users.select(name) + /// .order(id) + /// .load::(&connection)?; + /// + /// assert_eq!(vec!["Sean"], limited); + /// assert_eq!(vec!["Sean", "Bastien", "Pascal"], no_limit); + /// # Ok(()) + /// # } + /// ``` + fn limit(self, limit: i64) -> Limit + where + Self: methods::LimitDsl, + { + methods::LimitDsl::limit(self, limit) + } + + /// Sets the offset clause of the query. + /// + /// If there was already a offset clause, it will be overridden. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::users; + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use self::users::dsl::*; + /// # let connection = establish_connection(); + /// # diesel::delete(users).execute(&connection)?; + /// # diesel::insert_into(users) + /// # .values(&vec![ + /// # name.eq("Sean"), + /// # name.eq("Bastien"), + /// # name.eq("Pascal"), + /// # ]) + /// # .execute(&connection)?; + /// # + /// // Using an offset + /// let offset = users.select(name) + /// .order(id) + /// .limit(2) + /// .offset(1) + /// .load::(&connection)?; + /// + /// // No Offset + /// let no_offset = users.select(name) + /// .order(id) + /// .limit(2) + /// .load::(&connection)?; + /// + /// assert_eq!(vec!["Bastien", "Pascal"], offset); + /// assert_eq!(vec!["Sean", "Bastien"], no_offset); + /// # Ok(()) + /// # } + /// ``` + fn offset(self, offset: i64) -> Offset + where + Self: methods::OffsetDsl, + { + methods::OffsetDsl::offset(self, offset) + } + + /// Sets the `group by` clause of a query. + /// + /// **Note:** Queries having a `group by` clause require a custom select clause. + /// Use `QueryDsl::select()` to specify one + /// + /// If there was already a group by clause, it will be overridden. + /// Ordering by multiple columns can be achieved by passing a tuple of those + /// columns. + /// + /// Diesel follows postgresql's group by semantic, this means any column + /// appearing in a group by clause is considered to be aggregated. If a + /// primary key is part of the group by clause every column from the + /// corresponding table is considerd to be aggregated. Select clauses + /// cannot mix aggregated and non aggregated expressions. + /// + /// For group by clauses containing columns from more than one table it + /// is required to call [`allow_columns_to_appear_in_same_group_by_clause!`] + /// + /// [`allow_columns_to_appear_in_same_group_by_clause!`]: ../macro.allow_columns_to_appear_in_same_group_by_clause.html + /// + /// # Examples + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use crate::schema::{users, posts}; + /// # use diesel::dsl::count; + /// # let connection = establish_connection(); + /// let data = users::table.inner_join(posts::table) + /// .group_by(users::id) + /// .select((users::name, count(posts::id))) + /// # .order_by(users::id.asc()) + /// .load::<(String, i64)>(&connection)?; + /// + /// assert_eq!(vec![(String::from("Sean"), 2), (String::from("Tess"), 1)], data); + /// # Ok(()) + /// # } + /// ``` + fn group_by(self, group_by: GB) -> GroupBy + where + GB: Expression, + Self: methods::GroupByDsl, + { + methods::GroupByDsl::group_by(self, group_by) + } + + /// Adds `FOR UPDATE` to the end of the select statement. + /// + /// This method is only available for MySQL and PostgreSQL. SQLite does not + /// provide any form of row locking. + /// + /// Additionally, `.for_update` cannot be used on queries with a distinct + /// clause, group by clause, having clause, or any unions. Queries with + /// a `FOR UPDATE` clause cannot be boxed. + /// + /// # Example + /// + /// ```ignore + /// // Executes `SELECT * FROM users FOR UPDATE` + /// users.for_update().load(&connection) + /// ``` + fn for_update(self) -> ForUpdate + where + Self: methods::LockingDsl, + { + methods::LockingDsl::with_lock(self, lock::ForUpdate) + } + + /// Adds `FOR NO KEY UPDATE` to the end of the select statement. + /// + /// This method is only available for PostgreSQL. SQLite does not + /// provide any form of row locking, and MySQL does not support anything + /// finer than row-level locking. + /// + /// Additionally, `.for_no_key_update` cannot be used on queries with a distinct + /// clause, group by clause, having clause, or any unions. Queries with + /// a `FOR NO KEY UPDATE` clause cannot be boxed. + /// + /// # Example + /// + /// ```ignore + /// // Executes `SELECT * FROM users FOR NO KEY UPDATE` + /// users.for_no_key_update().load(&connection) + /// ``` + fn for_no_key_update(self) -> ForNoKeyUpdate + where + Self: methods::LockingDsl, + { + methods::LockingDsl::with_lock(self, lock::ForNoKeyUpdate) + } + + /// Adds `FOR SHARE` to the end of the select statement. + /// + /// This method is only available for MySQL and PostgreSQL. SQLite does not + /// provide any form of row locking. + /// + /// Additionally, `.for_share` cannot be used on queries with a distinct + /// clause, group by clause, having clause, or any unions. Queries with + /// a `FOR SHARE` clause cannot be boxed. + /// + /// # Example + /// + /// ```ignore + /// // Executes `SELECT * FROM users FOR SHARE` + /// users.for_share().load(&connection) + /// ``` + fn for_share(self) -> ForShare + where + Self: methods::LockingDsl, + { + methods::LockingDsl::with_lock(self, lock::ForShare) + } + + /// Adds `FOR KEY SHARE` to the end of the select statement. + /// + /// This method is only available for PostgreSQL. SQLite does not + /// provide any form of row locking, and MySQL does not support anything + /// finer than row-level locking. + /// + /// Additionally, `.for_key_share` cannot be used on queries with a distinct + /// clause, group by clause, having clause, or any unions. Queries with + /// a `FOR KEY SHARE` clause cannot be boxed. + /// + /// # Example + /// + /// ```ignore + /// // Executes `SELECT * FROM users FOR KEY SHARE` + /// users.for_key_share().load(&connection) + /// ``` + fn for_key_share(self) -> ForKeyShare + where + Self: methods::LockingDsl, + { + methods::LockingDsl::with_lock(self, lock::ForKeyShare) + } + + /// Adds `SKIP LOCKED` to the end of a `FOR UPDATE` clause. + /// + /// This modifier is only supported in PostgreSQL 9.5+ and MySQL 8+. + /// + /// # Example + /// + /// ```ignore + /// // Executes `SELECT * FROM users FOR UPDATE SKIP LOCKED` + /// users.for_update().skip_locked().load(&connection) + /// ``` + fn skip_locked(self) -> SkipLocked + where + Self: methods::ModifyLockDsl, + { + methods::ModifyLockDsl::modify_lock(self, lock::SkipLocked) + } + + /// Adds `NOWAIT` to the end of a `FOR UPDATE` clause. + /// + /// This modifier is only supported in PostgreSQL 9.5+ and MySQL 8+. + /// + /// # Example + /// + /// ```ignore + /// // Executes `SELECT * FROM users FOR UPDATE NOWAIT` + /// users.for_update().no_wait().load(&connection) + /// ``` + fn no_wait(self) -> NoWait + where + Self: methods::ModifyLockDsl, + { + methods::ModifyLockDsl::modify_lock(self, lock::NoWait) + } + + /// Boxes the pieces of a query into a single type. + /// + /// This is useful for cases where you want to conditionally modify a query, + /// but need the type to remain the same. The backend must be specified as + /// part of this. It is not possible to box a query and have it be useable + /// on multiple backends. + /// + /// A boxed query will incur a minor performance penalty, as the query builder + /// can no longer be inlined by the compiler. For most applications this cost + /// will be minimal. + /// + /// ### Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::users; + /// # + /// # fn main() { + /// # use std::collections::HashMap; + /// # let connection = establish_connection(); + /// # let mut params = HashMap::new(); + /// # params.insert("name", "Sean"); + /// let mut query = users::table.into_boxed(); + /// if let Some(name) = params.get("name") { + /// query = query.filter(users::name.eq(name)); + /// } + /// let users = query.load(&connection); + /// # let expected = vec![(1, String::from("Sean"))]; + /// # assert_eq!(Ok(expected), users); + /// # } + /// ``` + /// + /// Diesel queries also have a similar problem to [`Iterator`][iterator], where + /// returning them from a function requires exposing the implementation of that + /// function. The [`helper_types`][helper_types] module exists to help with this, + /// but you might want to hide the return type or have it conditionally change. + /// Boxing can achieve both. + /// + /// [iterator]: https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html + /// [helper_types]: ../helper_types/index.html + /// + /// ### Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # use schema::users; + /// # + /// # fn main() { + /// # let connection = establish_connection(); + /// fn users_by_name(name: &str) -> users::BoxedQuery { + /// users::table.filter(users::name.eq(name)).into_boxed() + /// } + /// + /// assert_eq!(Ok(1), users_by_name("Sean").select(users::id).first(&connection)); + /// assert_eq!(Ok(2), users_by_name("Tess").select(users::id).first(&connection)); + /// # } + /// ``` + fn into_boxed<'a, DB>(self) -> IntoBoxed<'a, Self, DB> + where + DB: Backend, + Self: methods::BoxedDsl<'a, DB>, + { + methods::BoxedDsl::internal_into_boxed(self) + } + + /// Wraps this select statement in parenthesis, allowing it to be used + /// as an expression. + /// + /// SQL allows queries such as `foo = (SELECT ...)`, as long as the + /// subselect returns only a single column, and 0 or 1 rows. This method + /// indicates that you expect the query to only return a single value (this + /// will be enforced by adding `LIMIT 1`). + /// + /// The SQL type of this will always be `Nullable`, as the query returns + /// `NULL` if the table is empty or it otherwise returns 0 rows. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::insert_into; + /// # use schema::users::dsl::*; + /// # use schema::posts; + /// # let connection = establish_connection(); + /// insert_into(posts::table) + /// .values(posts::user_id.eq(1)) + /// .execute(&connection)?; + /// let last_post = posts::table + /// .order(posts::id.desc()); + /// let most_recently_active_user = users.select(name) + /// .filter(id.nullable().eq(last_post.select(posts::user_id).single_value())) + /// .first::(&connection)?; + /// assert_eq!("Sean", most_recently_active_user); + /// # Ok(()) + /// # } + /// ``` + fn single_value(self) -> SingleValue + where + Self: methods::SingleValueDsl, + { + methods::SingleValueDsl::single_value(self) + } + + /// Coerce the SQL type of the select clause to it's nullable equivalent. + /// + /// This is use full for writing queries that contain subselects on non null + /// fields comparing them to nullable fields. + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # let connection = establish_connection(); + /// table! { + /// users { + /// id -> Integer, + /// name -> Text, + /// } + /// } + /// + /// table! { + /// posts { + /// id -> Integer, + /// by_user -> Nullable, + /// } + /// } + /// + /// # let _: Vec<(i32, Option)> = + /// posts::table.filter( + /// posts::by_user.eq_any(users::table.select(users::name).nullable()) + /// ).load(&connection)?; + /// # Ok(()) + /// # } + fn nullable(self) -> NullableSelect + where + Self: methods::SelectNullableDsl, + { + methods::SelectNullableDsl::nullable(self) + } +} + +impl QueryDsl for T {} + +/// Methods used to execute queries. +pub trait RunQueryDsl: Sized { + /// Executes the given command, returning the number of rows affected. + /// + /// `execute` is usually used in conjunction with [`insert_into`](../fn.insert_into.html), + /// [`update`](../fn.update.html) and [`delete`](../fn.delete.html) where the number of + /// affected rows is often enough information. + /// + /// When asking the database to return data from a query, [`load`](#method.load) should + /// probably be used instead. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::insert_into; + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let inserted_rows = insert_into(users) + /// .values(name.eq("Ruby")) + /// .execute(&connection)?; + /// assert_eq!(1, inserted_rows); + /// + /// let inserted_rows = insert_into(users) + /// .values(&vec![name.eq("Jim"), name.eq("James")]) + /// .execute(&connection)?; + /// assert_eq!(2, inserted_rows); + /// # Ok(()) + /// # } + /// ``` + fn execute(self, conn: &Conn) -> QueryResult + where + Conn: Connection, + Self: methods::ExecuteDsl, + { + methods::ExecuteDsl::execute(self, conn) + } + + /// Executes the given query, returning a `Vec` with the returned rows. + /// + /// When using the query builder, + /// the return type can be + /// a tuple of the values, + /// or a struct which implements [`Queryable`]. + /// + /// When this method is called on [`sql_query`], + /// the return type can only be a struct which implements [`QueryableByName`] + /// + /// For insert, update, and delete operations where only a count of affected is needed, + /// [`execute`] should be used instead. + /// + /// [`Queryable`]: ../deserialize/trait.Queryable.html + /// [`QueryableByName`]: ../deserialize/trait.QueryableByName.html + /// [`execute`]: fn.execute.html + /// [`sql_query`]: ../fn.sql_query.html + /// + /// # Examples + /// + /// ## Returning a single field + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::insert_into; + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let data = users.select(name) + /// .load::(&connection)?; + /// assert_eq!(vec!["Sean", "Tess"], data); + /// # Ok(()) + /// # } + /// ``` + /// + /// ## Returning a tuple + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::insert_into; + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let data = users + /// .load::<(i32, String)>(&connection)?; + /// let expected_data = vec![ + /// (1, String::from("Sean")), + /// (2, String::from("Tess")), + /// ]; + /// assert_eq!(expected_data, data); + /// # Ok(()) + /// # } + /// ``` + /// + /// ## Returning a struct + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// #[derive(Queryable, PartialEq, Debug)] + /// struct User { + /// id: i32, + /// name: String, + /// } + /// + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::insert_into; + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let data = users + /// .load::(&connection)?; + /// let expected_data = vec![ + /// User { id: 1, name: String::from("Sean"), }, + /// User { id: 2, name: String::from("Tess"), }, + /// ]; + /// assert_eq!(expected_data, data); + /// # Ok(()) + /// # } + /// ``` + fn load(self, conn: &Conn) -> QueryResult> + where + Self: LoadQuery, + { + self.internal_load(conn) + } + + /// Runs the command, and returns the affected row. + /// + /// `Err(NotFound)` will be returned if the query affected 0 rows. You can + /// call `.optional()` on the result of this if the command was optional to + /// get back a `Result>` + /// + /// When this method is called on an insert, update, or delete statement, + /// it will implicitly add a `RETURNING *` to the query, + /// unless a returning clause was already specified. + /// + /// This method only returns the first row that was affected, even if more + /// rows are affected. + /// + /// # Example + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # #[cfg(feature = "postgres")] + /// # fn run_test() -> QueryResult<()> { + /// # use diesel::{insert_into, update}; + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// let inserted_row = insert_into(users) + /// .values(name.eq("Ruby")) + /// .get_result(&connection)?; + /// assert_eq!((3, String::from("Ruby")), inserted_row); + /// + /// // This will return `NotFound`, as there is no user with ID 4 + /// let update_result = update(users.find(4)) + /// .set(name.eq("Jim")) + /// .get_result::<(i32, String)>(&connection); + /// assert_eq!(Err(diesel::NotFound), update_result); + /// # Ok(()) + /// # } + /// # + /// # #[cfg(not(feature = "postgres"))] + /// # fn run_test() -> QueryResult<()> { + /// # Ok(()) + /// # } + /// ``` + fn get_result(self, conn: &Conn) -> QueryResult + where + Self: LoadQuery, + { + first_or_not_found(self.load(conn)) + } + + /// Runs the command, returning an `Vec` with the affected rows. + /// + /// This method is an alias for [`load`], but with a name that makes more + /// sense for insert, update, and delete statements. + /// + /// [`load`]: #method.load + fn get_results(self, conn: &Conn) -> QueryResult> + where + Self: LoadQuery, + { + self.load(conn) + } + + /// Attempts to load a single record. + /// + /// This method is equivalent to `.limit(1).get_result()` + /// + /// Returns `Ok(record)` if found, and `Err(NotFound)` if no results are + /// returned. If the query truly is optional, you can call `.optional()` on + /// the result of this to get a `Result>`. + /// + /// # Example: + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # fn main() { + /// # run_test(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # use schema::users::dsl::*; + /// # let connection = establish_connection(); + /// diesel::insert_into(users) + /// .values(&vec![name.eq("Sean"), name.eq("Pascal")]) + /// .execute(&connection)?; + /// + /// let first_name = users.order(id).select(name).first(&connection); + /// assert_eq!(Ok(String::from("Sean")), first_name); + /// + /// let not_found = users + /// .filter(name.eq("Foo")) + /// .first::<(i32, String)>(&connection); + /// assert_eq!(Err(diesel::NotFound), not_found); + /// # Ok(()) + /// # } + /// ``` + fn first(self, conn: &Conn) -> QueryResult + where + Self: methods::LimitDsl, + Limit: LoadQuery, + { + methods::LimitDsl::limit(self, 1).get_result(conn) + } +} + +// Note: We could have a blanket `AsQuery` impl here, which would apply to +// everything we want it to. However, when a query is invalid, we specifically +// want the error to happen on the where clause of the method instead of trait +// resolution. Otherwise our users will get an error saying `<3 page long type>: +// ExecuteDsl is not satisfied` instead of a specific error telling them what +// part of their query is wrong. +impl RunQueryDsl for T where T: Table {} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/nullable_select_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/nullable_select_dsl.rs new file mode 100644 index 000000000..be8e4d413 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/nullable_select_dsl.rs @@ -0,0 +1,14 @@ +/// The `nullable` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However you may need a where clause on this trait +/// to call `nullable` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html#method.nullable +pub trait SelectNullableDsl { + /// The return type of `nullable` + type Output; + + /// See the trait documentation + fn nullable(self) -> Self::Output; +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/offset_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/offset_dsl.rs new file mode 100644 index 000000000..05f063651 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/offset_dsl.rs @@ -0,0 +1,28 @@ +use crate::query_source::Table; + +/// The `offset` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `offset` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait OffsetDsl { + /// The type returned by `.offset`. + type Output; + + /// See the trait documentation + fn offset(self, offset: i64) -> Self::Output; +} + +impl OffsetDsl for T +where + T: Table, + T::Query: OffsetDsl, +{ + type Output = ::Output; + + fn offset(self, offset: i64) -> Self::Output { + self.as_query().offset(offset) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/order_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/order_dsl.rs new file mode 100644 index 000000000..116e69cd9 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/order_dsl.rs @@ -0,0 +1,58 @@ +use crate::expression::Expression; +use crate::query_source::Table; + +/// The `order` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `order` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait OrderDsl { + /// The type returned by `.order`. + type Output; + + /// See the trait documentation. + fn order(self, expr: Expr) -> Self::Output; +} + +impl OrderDsl for T +where + Expr: Expression, + T: Table, + T::Query: OrderDsl, +{ + type Output = >::Output; + + fn order(self, expr: Expr) -> Self::Output { + self.as_query().order(expr) + } +} + +/// The `then_order_by` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `then_order_by` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait ThenOrderDsl { + /// The type returned by `.then_order_by`. + type Output; + + /// See the trait documentation. + fn then_order_by(self, expr: Expr) -> Self::Output; +} + +impl ThenOrderDsl for T +where + Expr: Expression, + T: Table, + T::Query: ThenOrderDsl, +{ + type Output = >::Output; + + fn then_order_by(self, expr: Expr) -> Self::Output { + self.as_query().then_order_by(expr) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/save_changes_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/save_changes_dsl.rs new file mode 100644 index 000000000..896e8b78f --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/save_changes_dsl.rs @@ -0,0 +1,155 @@ +use crate::associations::HasTable; +#[cfg(any(feature = "sqlite", feature = "mysql"))] +use crate::associations::Identifiable; +use crate::connection::Connection; +#[cfg(any(feature = "sqlite", feature = "mysql"))] +use crate::dsl::Find; +#[cfg(any(feature = "sqlite", feature = "postgres", feature = "mysql"))] +use crate::dsl::Update; +#[cfg(any(feature = "sqlite", feature = "postgres", feature = "mysql"))] +use crate::expression::{is_aggregate, MixedAggregates, ValidGrouping}; +use crate::query_builder::{AsChangeset, IntoUpdateTarget}; +#[cfg(any(feature = "sqlite", feature = "mysql"))] +use crate::query_dsl::methods::{ExecuteDsl, FindDsl}; +#[cfg(any(feature = "sqlite", feature = "postgres", feature = "mysql"))] +use crate::query_dsl::{LoadQuery, RunQueryDsl}; +use crate::result::QueryResult; +#[cfg(any(feature = "sqlite", feature = "postgres", feature = "mysql"))] +use crate::Table; + +/// A trait defining how to update a record and fetch the updated entry +/// on a certain backend. +/// +/// The only case where it is required to work with this trait is while +/// implementing a new connection type. +/// Otherwise use [`SaveChangesDsl`](trait.SaveChangesDsl.html) +/// +/// For implementing this trait for a custom backend: +/// * The `Changes` generic parameter represents the changeset that should be stored +/// * The `Output` generic parameter represents the type of the response. +pub trait UpdateAndFetchResults: Connection { + /// See the traits documentation. + fn update_and_fetch(&self, changeset: Changes) -> QueryResult; +} + +#[cfg(feature = "postgres")] +use crate::pg::PgConnection; + +#[cfg(feature = "postgres")] +impl UpdateAndFetchResults for PgConnection +where + Changes: Copy + AsChangeset::Table> + IntoUpdateTarget, + Update: LoadQuery, + ::AllColumns: ValidGrouping<()>, + <::AllColumns as ValidGrouping<()>>::IsAggregate: + MixedAggregates, +{ + fn update_and_fetch(&self, changeset: Changes) -> QueryResult { + crate::update(changeset).set(changeset).get_result(self) + } +} + +#[cfg(feature = "sqlite")] +use crate::sqlite::SqliteConnection; + +#[cfg(feature = "sqlite")] +impl UpdateAndFetchResults for SqliteConnection +where + Changes: Copy + Identifiable, + Changes: AsChangeset::Table> + IntoUpdateTarget, + Changes::Table: FindDsl, + Update: ExecuteDsl, + Find: LoadQuery, + ::AllColumns: ValidGrouping<()>, + <::AllColumns as ValidGrouping<()>>::IsAggregate: + MixedAggregates, +{ + fn update_and_fetch(&self, changeset: Changes) -> QueryResult { + crate::update(changeset).set(changeset).execute(self)?; + Changes::table().find(changeset.id()).get_result(self) + } +} + +#[cfg(feature = "mysql")] +use crate::mysql::MysqlConnection; + +#[cfg(feature = "mysql")] +impl UpdateAndFetchResults for MysqlConnection +where + Changes: Copy + Identifiable, + Changes: AsChangeset::Table> + IntoUpdateTarget, + Changes::Table: FindDsl, + Update: ExecuteDsl, + Find: LoadQuery, + ::AllColumns: ValidGrouping<()>, + <::AllColumns as ValidGrouping<()>>::IsAggregate: + MixedAggregates, +{ + fn update_and_fetch(&self, changeset: Changes) -> QueryResult { + crate::update(changeset).set(changeset).execute(self)?; + Changes::table().find(changeset.id()).get_result(self) + } +} + +/// Sugar for types which implement both `AsChangeset` and `Identifiable` +/// +/// On backends which support the `RETURNING` keyword, +/// `foo.save_changes(&conn)` is equivalent to +/// `update(&foo).set(&foo).get_result(&conn)`. +/// On other backends, two queries will be executed. +/// +/// # Example +/// +/// ```rust +/// # include!("../doctest_setup.rs"); +/// # use schema::animals; +/// # +/// #[derive(Queryable, Debug, PartialEq)] +/// struct Animal { +/// id: i32, +/// species: String, +/// legs: i32, +/// name: Option, +/// } +/// +/// #[derive(AsChangeset, Identifiable)] +/// #[table_name = "animals"] +/// struct AnimalForm<'a> { +/// id: i32, +/// name: &'a str, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use self::animals::dsl::*; +/// # let connection = establish_connection(); +/// let form = AnimalForm { id: 2, name: "Super scary" }; +/// let changed_animal = form.save_changes(&connection)?; +/// let expected_animal = Animal { +/// id: 2, +/// species: String::from("spider"), +/// legs: 8, +/// name: Some(String::from("Super scary")), +/// }; +/// assert_eq!(expected_animal, changed_animal); +/// # Ok(()) +/// # } +/// ``` +pub trait SaveChangesDsl { + /// See the trait documentation. + fn save_changes(self, connection: &Conn) -> QueryResult + where + Self: Sized, + Conn: UpdateAndFetchResults, + { + connection.update_and_fetch(self) + } +} + +impl SaveChangesDsl for T where + T: Copy + AsChangeset::Table> + IntoUpdateTarget +{ +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/select_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/select_dsl.rs new file mode 100644 index 000000000..2385f6976 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/select_dsl.rs @@ -0,0 +1,33 @@ +use crate::expression::Expression; +use crate::query_source::Table; + +/// The `select` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `select` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait SelectDsl { + // FIXME: Once we've refactored the `impl Expression` on `SelectStatement` + // to not conditionally be `sql_types::Array`, it is probably worthwhile to + // add a `: Expression` bound here. + /// The type returned by `.select` + type Output; + + /// See the trait documentation + fn select(self, selection: Selection) -> Self::Output; +} + +impl SelectDsl for T +where + Selection: Expression, + T: Table, + T::Query: SelectDsl, +{ + type Output = >::Output; + + fn select(self, selection: Selection) -> Self::Output { + self.as_query().select(selection) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_dsl/single_value_dsl.rs b/collector/benchmarks/diesel/diesel/src/query_dsl/single_value_dsl.rs new file mode 100644 index 000000000..c220b6a93 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_dsl/single_value_dsl.rs @@ -0,0 +1,34 @@ +use super::methods::LimitDsl; +use crate::dsl::Limit; +use crate::expression::grouped::Grouped; +use crate::expression::subselect::Subselect; +use crate::query_builder::SelectQuery; +use crate::sql_types::IntoNullable; + +/// The `single_value` method +/// +/// This trait should not be relied on directly by most apps. Its behavior is +/// provided by [`QueryDsl`]. However, you may need a where clause on this trait +/// to call `single_value` from generic code. +/// +/// [`QueryDsl`]: ../trait.QueryDsl.html +pub trait SingleValueDsl { + /// The type returned by `.single_value`. + type Output; + + /// See the trait documentation. + fn single_value(self) -> Self::Output; +} + +impl SingleValueDsl for T +where + Self: SelectQuery + LimitDsl, + ::SqlType: IntoNullable, +{ + type Output = + Grouped, <::SqlType as IntoNullable>::Nullable>>; + + fn single_value(self) -> Self::Output { + Grouped(Subselect::new(self.limit(1))) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_source/joins.rs b/collector/benchmarks/diesel/diesel/src/query_source/joins.rs new file mode 100644 index 000000000..76461fe4a --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_source/joins.rs @@ -0,0 +1,341 @@ +use super::{AppearsInFromClause, Plus, QuerySource}; +use crate::backend::Backend; +use crate::expression::grouped::Grouped; +use crate::expression::nullable::Nullable; +use crate::expression::SelectableExpression; +use crate::prelude::*; +use crate::query_builder::*; +use crate::query_dsl::InternalJoinDsl; +use crate::result::QueryResult; +use crate::sql_types::BoolOrNullableBool; +use crate::util::TupleAppend; + +#[derive(Debug, Clone, Copy, QueryId)] +/// A query source representing the join between two tables +pub struct Join { + left: Left, + right: Right, + kind: Kind, +} + +#[derive(Debug, Clone, Copy, QueryId)] +#[doc(hidden)] +/// A query source representing the join between two tables with an explicit +/// `ON` given. `Join` should usually be referenced instead, as all "type +/// safety" traits are implemented in terms of `Join` implementing them. +pub struct JoinOn { + join: Join, + on: On, +} + +impl Join { + pub fn new(left: Left, right: Right, kind: Kind) -> Self { + Join { + left: left, + right: right, + kind: kind, + } + } + + #[doc(hidden)] + pub fn on(self, on: On) -> JoinOn { + JoinOn { join: self, on: on } + } +} + +impl QuerySource for Join +where + Left: QuerySource + AppendSelection, + Right: QuerySource, + Left::Output: SelectableExpression, + Self: Clone, +{ + type FromClause = Self; + type DefaultSelection = Left::Output; + + fn from_clause(&self) -> Self::FromClause { + self.clone() + } + + fn default_selection(&self) -> Self::DefaultSelection { + self.left.append_selection(self.right.default_selection()) + } +} + +impl QuerySource for Join +where + Left: QuerySource + AppendSelection>, + Right: QuerySource, + Left::Output: SelectableExpression, + Self: Clone, +{ + type FromClause = Self; + type DefaultSelection = Left::Output; + + fn from_clause(&self) -> Self::FromClause { + self.clone() + } + + fn default_selection(&self) -> Self::DefaultSelection { + self.left + .append_selection(self.right.default_selection().nullable()) + } +} + +impl QuerySource for JoinOn +where + Join: QuerySource, + On: AppearsOnTable + Clone, + On::SqlType: BoolOrNullableBool, + Join::DefaultSelection: SelectableExpression, +{ + type FromClause = Grouped>; + type DefaultSelection = Join::DefaultSelection; + + fn from_clause(&self) -> Self::FromClause { + Grouped(nodes::InfixNode::new( + self.join.from_clause(), + self.on.clone(), + " ON ", + )) + } + + fn default_selection(&self) -> Self::DefaultSelection { + self.join.default_selection() + } +} + +impl QueryFragment for Join +where + DB: Backend, + Left: QuerySource, + Left::FromClause: QueryFragment, + Right: QuerySource, + Right::FromClause: QueryFragment, + Kind: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + self.left.from_clause().walk_ast(out.reborrow())?; + self.kind.walk_ast(out.reborrow())?; + out.push_sql(" JOIN "); + self.right.from_clause().walk_ast(out.reborrow())?; + Ok(()) + } +} + +/// Indicates that two tables can be joined without an explicit `ON` clause. +/// +/// Implementations of this trait are generated by invoking [`joinable!`]. +/// Implementing this trait means that you can call +/// `left_table.inner_join(right_table)`, without supplying the `ON` clause +/// explicitly. To join two tables which do not implement this trait, you will +/// need to call [`.on`]. +/// +/// See [`joinable!`] and [`inner_join`] for usage examples. +/// +/// [`joinable!`]: ../macro.joinable.html +/// [`.on`]: ../query_dsl/trait.JoinOnDsl.html#method.on +/// [`inner_join`]: ../query_dsl/trait.QueryDsl.html#method.inner_join +pub trait JoinTo { + #[doc(hidden)] + type FromClause; + #[doc(hidden)] + type OnClause; + #[doc(hidden)] + fn join_target(rhs: T) -> (Self::FromClause, Self::OnClause); +} + +#[doc(hidden)] +/// Used to ensure the sql type of `left.join(mid).join(right)` is +/// `(Left, Mid, Right)` and not `((Left, Mid), Right)`. This needs +/// to be separate from `TupleAppend` because we still want to keep +/// the column lists (which are tuples) separate. +pub trait AppendSelection { + type Output; + + fn append_selection(&self, selection: Selection) -> Self::Output; +} + +impl AppendSelection for T { + type Output = (T::AllColumns, Selection); + + fn append_selection(&self, selection: Selection) -> Self::Output { + (T::all_columns(), selection) + } +} + +impl AppendSelection for Join +where + Self: QuerySource, + ::DefaultSelection: TupleAppend, +{ + type Output = <::DefaultSelection as TupleAppend>::Output; + + fn append_selection(&self, selection: Selection) -> Self::Output { + self.default_selection().tuple_append(selection) + } +} + +impl AppendSelection for JoinOn +where + Join: AppendSelection, +{ + type Output = Join::Output; + + fn append_selection(&self, selection: Selection) -> Self::Output { + self.join.append_selection(selection) + } +} + +#[doc(hidden)] +#[derive(Debug, Clone, Copy, Default, QueryId)] +pub struct Inner; + +impl QueryFragment for Inner { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" INNER"); + Ok(()) + } +} + +#[doc(hidden)] +#[derive(Debug, Clone, Copy, Default, QueryId)] +pub struct LeftOuter; + +impl QueryFragment for LeftOuter { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + out.push_sql(" LEFT OUTER"); + Ok(()) + } +} + +impl JoinTo for Join +where + Left: JoinTo, +{ + type FromClause = Left::FromClause; + type OnClause = Left::OnClause; + + fn join_target(rhs: Right) -> (Self::FromClause, Self::OnClause) { + Left::join_target(rhs) + } +} + +impl JoinTo for JoinOn +where + Join: JoinTo, +{ + type FromClause = Join::FromClause; + type OnClause = Join::OnClause; + + fn join_target(rhs: Right) -> (Self::FromClause, Self::OnClause) { + Join::join_target(rhs) + } +} + +impl AppearsInFromClause for Join +where + Left: AppearsInFromClause, + Right: AppearsInFromClause, + Left::Count: Plus, +{ + type Count = >::Output; +} + +impl AppearsInFromClause for JoinOn +where + Join: AppearsInFromClause, +{ + type Count = Join::Count; +} + +#[doc(hidden)] +#[derive(Debug, Clone, Copy)] +pub struct OnClauseWrapper { + source: Source, + on: On, +} + +impl OnClauseWrapper { + pub fn new(source: Source, on: On) -> Self { + OnClauseWrapper { source, on } + } +} + +impl JoinTo> for Lhs +where + Lhs: Table, +{ + type FromClause = Rhs; + type OnClause = On; + + fn join_target(rhs: OnClauseWrapper) -> (Self::FromClause, Self::OnClause) { + (rhs.source, rhs.on) + } +} + +impl JoinTo for OnClauseWrapper +where + Lhs: JoinTo, +{ + type FromClause = >::FromClause; + type OnClause = >::OnClause; + + fn join_target(rhs: Rhs) -> (Self::FromClause, Self::OnClause) { + >::join_target(rhs) + } +} + +impl InternalJoinDsl for OnClauseWrapper +where + Lhs: InternalJoinDsl, +{ + type Output = OnClauseWrapper<>::Output, On2>; + + fn join(self, rhs: Rhs, kind: Kind, on: On1) -> Self::Output { + OnClauseWrapper { + source: self.source.join(rhs, kind, on), + on: self.on, + } + } +} + +impl QueryDsl for OnClauseWrapper {} + +#[doc(hidden)] +/// Convert any joins in a `FROM` clause into an inner join. +/// +/// This trait is used to determine whether +/// `Nullable: SelectableExpression`. We consider it to be +/// selectable if `T: SelectableExpression`. Since `SomeJoin` +/// may be deeply nested, we need to recursively change any appearances of +/// `LeftOuter` to `Inner` in order to perform this check. +pub trait ToInnerJoin { + type InnerJoin; +} + +impl ToInnerJoin for Join +where + Left: ToInnerJoin, + Right: ToInnerJoin, +{ + type InnerJoin = Join; +} + +impl ToInnerJoin for JoinOn +where + Join: ToInnerJoin, +{ + type InnerJoin = JoinOn; +} + +impl ToInnerJoin for SelectStatement +where + From: ToInnerJoin, +{ + type InnerJoin = SelectStatement; +} + +impl ToInnerJoin for T { + type InnerJoin = T; +} diff --git a/collector/benchmarks/diesel/diesel/src/query_source/mod.rs b/collector/benchmarks/diesel/diesel/src/query_source/mod.rs new file mode 100644 index 000000000..42b04316b --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_source/mod.rs @@ -0,0 +1,158 @@ +//! Types related to describing schema, and interactions between tables. +//! +//! Most traits in this module are derived or generated by [`table!`]. +//! +//! [`table!`]: ../macro.table.html +#[doc(hidden)] +pub mod joins; +mod peano_numbers; + +use crate::expression::{Expression, SelectableExpression, ValidGrouping}; +use crate::query_builder::*; + +pub use self::joins::JoinTo; +pub use self::peano_numbers::*; + +/// Represents a type which can appear in the `FROM` clause. Apps should not +/// need to concern themselves with this trait. +/// +/// Types which implement this trait include: +/// - Tables generated by the `table!` macro +/// - Internal structs which represent joins +/// - A select statement which has had no query builder methods called on it, +/// other than those which can affect the from clause. +pub trait QuerySource { + /// The type returned by `from_clause` + type FromClause; + /// The type returned by `default_selection` + type DefaultSelection: SelectableExpression; + + /// The actual `FROM` clause of this type. This is typically only called in + /// `QueryFragment` implementations. + fn from_clause(&self) -> Self::FromClause; + /// The default select clause of this type, which should be used if no + /// select clause was explicitly specified. This should always be a tuple of + /// all the desired columns, not `star` + fn default_selection(&self) -> Self::DefaultSelection; +} + +/// A column on a database table. Types which implement this trait should have +/// been generated by the [`table!` macro](../macro.table.html). +pub trait Column: Expression { + /// The table which this column belongs to + type Table: Table; + + /// The name of this column + const NAME: &'static str; +} + +/// A SQL database table. Types which implement this trait should have been +/// generated by the [`table!` macro](../macro.table.html). +pub trait Table: QuerySource + AsQuery + Sized { + /// The type returned by `primary_key` + type PrimaryKey: SelectableExpression + ValidGrouping<()>; + /// The type returned by `all_columns` + type AllColumns: SelectableExpression + ValidGrouping<()>; + + /// Returns the primary key of this table. + /// + /// If the table has a composite primary key, this will be a tuple. + fn primary_key(&self) -> Self::PrimaryKey; + /// Returns a tuple of all columns belonging to this table. + fn all_columns() -> Self::AllColumns; +} + +/// Determines how many times `Self` appears in `QS` +/// +/// This trait is primarily used to determine whether or not a column is +/// selectable from a given from clause. A column can be selected if its table +/// appears in the from clause *exactly once*. +/// +/// We do not allow the same table to appear in a query multiple times in any +/// context where referencing that table would be ambiguous (depending on the +/// context and backend being used, this may or may not be something that would +/// otherwise result in a runtime error). +pub trait AppearsInFromClause { + /// How many times does `Self` appear in `QS`? + type Count; +} + +#[doc(hidden)] +/// Used to determine which of two from clauses contains a given table. +/// +/// This trait can be used to emulate "or" conditions in where clauses when +/// we want a trait to be implemented with one of two type parameters. +/// +/// For example, if we wanted to write: +/// +/// ```rust,ignore +/// where +/// T: SelectableExpression | SelectableExpression, +/// ``` +/// +/// we can emulate this by writing: +/// +/// ```rust,ignore +/// where +/// Left: AppearsInFromClause, +/// Right: AppearsInFromClause, +/// (Left::Count, Right::Count): Pick, +/// T: SelectableExpression< +/// <(Left::Count, Right::Count) as Pick>::Selection, +/// >, +/// ``` +/// +/// In order to aquire the counts in the first place, we must already know +/// the table we're searching for. +pub trait Pick { + /// The selected type. + /// + /// For `(Once, Never)` this type will be `Left`. For `(Never, Once)`, this type will be + /// `Right` + type Selection; +} + +impl Pick for (Once, Never) { + type Selection = Left; +} + +impl Pick for (Never, Once) { + type Selection = Right; +} + +#[doc(hidden)] +#[allow( + non_camel_case_types, + missing_debug_implementations, + missing_copy_implementations +)] +/// Everything in this module is here to give something more helpful than: +/// +/// > (Never, Never): Pick is not satisifed +/// +/// Any of these impls can be deleted if they are getting in the way of +/// other functionality. Any code which is using these impls is already +/// failing to compile. +mod impls_which_are_only_here_to_improve_error_messages { + use super::*; + + pub struct this_table_doesnt_appear_in_the_from_clause_of_your_query; + + impl Pick for (Never, Never) { + type Selection = this_table_doesnt_appear_in_the_from_clause_of_your_query; + } + + pub struct this_table_appears_in_your_query_more_than_once_and_must_be_aliased; + + impl Pick for (MoreThanOnce, OtherCount) { + type Selection = this_table_appears_in_your_query_more_than_once_and_must_be_aliased; + } + + impl Pick for (Never, MoreThanOnce) { + type Selection = this_table_appears_in_your_query_more_than_once_and_must_be_aliased; + } + + impl Pick for (Once, MoreThanOnce) { + type Selection = this_table_appears_in_your_query_more_than_once_and_must_be_aliased; + } +} diff --git a/collector/benchmarks/diesel/diesel/src/query_source/peano_numbers.rs b/collector/benchmarks/diesel/diesel/src/query_source/peano_numbers.rs new file mode 100644 index 000000000..0b9cd0686 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/query_source/peano_numbers.rs @@ -0,0 +1,45 @@ +//! A simple implementation of peano numbers. +//! +//! This is used to enforce that columns can only be selected from a given from +//! clause if their table appears exactly one time. + +/// A table never appears in the from clause. +#[allow(missing_debug_implementations, missing_copy_implementations)] +pub struct Never; + +/// A table appears in the from clause exactly one time. +#[allow(missing_debug_implementations, missing_copy_implementations)] +pub struct Once; + +/// A table appears in the from clause two or more times. +#[allow(missing_debug_implementations, missing_copy_implementations)] +pub struct MoreThanOnce; + +/// Add two peano numbers together. +/// +/// This is used to determine the number of times a table appears in a from +/// clause when the from clause contains a join. +pub trait Plus { + /// The result of adding these numbers together + type Output; +} + +impl Plus for Never { + type Output = T; +} + +impl Plus for MoreThanOnce { + type Output = Self; +} + +impl Plus for Once { + type Output = Self; +} + +impl Plus for Once { + type Output = MoreThanOnce; +} + +impl Plus for Once { + type Output = MoreThanOnce; +} diff --git a/collector/benchmarks/diesel/diesel/src/r2d2.rs b/collector/benchmarks/diesel/diesel/src/r2d2.rs new file mode 100644 index 000000000..93863c3df --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/r2d2.rs @@ -0,0 +1,303 @@ +//! Connection pooling via r2d2. +//! +//! Note: This module requires enabling the `r2d2` feature + +extern crate r2d2; + +pub use self::r2d2::*; + +/// A re-export of [`r2d2::Error`], which is only used by methods on [`r2d2::Pool`]. +/// +/// [`r2d2::Error`]: ../../r2d2/struct.Error.html +/// [`r2d2::Pool`]: ../../r2d2/struct.Pool.html +pub type PoolError = self::r2d2::Error; + +use std::convert::Into; +use std::fmt; +use std::marker::PhantomData; + +use crate::connection::{SimpleConnection, TransactionManager}; +use crate::deserialize::FromSqlRow; +use crate::expression::QueryMetadata; +use crate::prelude::*; +use crate::query_builder::{AsQuery, QueryFragment, QueryId}; + +/// An r2d2 connection manager for use with Diesel. +/// +/// See the [r2d2 documentation] for usage examples. +/// +/// [r2d2 documentation]: ../../r2d2 +#[derive(Clone)] +pub struct ConnectionManager { + database_url: String, + _marker: PhantomData, +} + +impl fmt::Debug for ConnectionManager { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "ConnectionManager<{}>", std::any::type_name::()) + } +} + +unsafe impl Sync for ConnectionManager {} + +impl ConnectionManager { + /// Returns a new connection manager, + /// which establishes connections to the given database URL. + pub fn new>(database_url: S) -> Self { + ConnectionManager { + database_url: database_url.into(), + _marker: PhantomData, + } + } +} + +/// The error used when managing connections with `r2d2`. +#[derive(Debug)] +pub enum Error { + /// An error occurred establishing the connection + ConnectionError(ConnectionError), + + /// An error occurred pinging the database + QueryError(crate::result::Error), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Error::ConnectionError(ref e) => e.fmt(f), + Error::QueryError(ref e) => e.fmt(f), + } + } +} + +impl ::std::error::Error for Error {} + +/// A trait indicating a connection could be used inside a r2d2 pool +pub trait R2D2Connection: Connection { + /// Check if a connection is still valid + fn ping(&self) -> QueryResult<()>; +} + +#[cfg(feature = "postgres")] +impl R2D2Connection for crate::pg::PgConnection { + fn ping(&self) -> QueryResult<()> { + self.execute("SELECT 1").map(|_| ()) + } +} + +#[cfg(feature = "mysql")] +impl R2D2Connection for crate::mysql::MysqlConnection { + fn ping(&self) -> QueryResult<()> { + self.execute("SELECT 1").map(|_| ()) + } +} + +#[cfg(feature = "sqlite")] +impl R2D2Connection for crate::sqlite::SqliteConnection { + fn ping(&self) -> QueryResult<()> { + self.execute("SELECT 1").map(|_| ()) + } +} + +impl ManageConnection for ConnectionManager +where + T: R2D2Connection + Send + 'static, +{ + type Connection = T; + type Error = Error; + + fn connect(&self) -> Result { + T::establish(&self.database_url).map_err(Error::ConnectionError) + } + + fn is_valid(&self, conn: &mut T) -> Result<(), Error> { + conn.ping().map_err(Error::QueryError) + } + + fn has_broken(&self, _conn: &mut T) -> bool { + false + } +} + +impl SimpleConnection for PooledConnection +where + M: ManageConnection, + M::Connection: R2D2Connection + Send + 'static, +{ + fn batch_execute(&self, query: &str) -> QueryResult<()> { + (&**self).batch_execute(query) + } +} + +impl Connection for PooledConnection +where + M: ManageConnection, + M::Connection: Connection + R2D2Connection + Send + 'static, +{ + type Backend = ::Backend; + type TransactionManager = PooledConnectionTransactionManager; + + fn establish(_: &str) -> ConnectionResult { + Err(ConnectionError::BadConnection(String::from( + "Cannot directly establish a pooled connection", + ))) + } + + fn execute(&self, query: &str) -> QueryResult { + (&**self).execute(query) + } + + fn load(&self, source: T) -> QueryResult> + where + T: AsQuery, + T::Query: QueryFragment + QueryId, + U: FromSqlRow, + Self::Backend: QueryMetadata, + { + (&**self).load(source) + } + + fn execute_returning_count(&self, source: &T) -> QueryResult + where + T: QueryFragment + QueryId, + { + (&**self).execute_returning_count(source) + } + + fn transaction_manager(&self) -> &Self::TransactionManager { + // This is actually fine because we have an #[repr(transparent)] + // on LoggingTransactionManager, which means the layout is the same + // as the inner type + // See the ref-cast crate for a longer version: https://github.com/dtolnay/ref-cast + unsafe { + &*((&**self).transaction_manager() as *const _ as *const Self::TransactionManager) + } + } +} + +#[doc(hidden)] +#[repr(transparent)] +#[allow(missing_debug_implementations)] +pub struct PooledConnectionTransactionManager +where + M: ManageConnection, + M::Connection: Connection, +{ + inner: ::TransactionManager, +} + +impl TransactionManager> for PooledConnectionTransactionManager +where + M: ManageConnection, + PooledConnection: Connection, + M::Connection: Connection, +{ + fn begin_transaction(&self, conn: &PooledConnection) -> QueryResult<()> { + self.inner.begin_transaction(&**conn) + } + + fn rollback_transaction(&self, conn: &PooledConnection) -> QueryResult<()> { + self.inner.rollback_transaction(&**conn) + } + + fn commit_transaction(&self, conn: &PooledConnection) -> QueryResult<()> { + self.inner.commit_transaction(&**conn) + } + + fn get_transaction_depth(&self) -> u32 { + self.inner.get_transaction_depth() + } +} + +impl crate::migration::MigrationConnection for PooledConnection +where + M: ManageConnection, + M::Connection: crate::migration::MigrationConnection, + Self: Connection, +{ + fn setup(&self) -> QueryResult { + (&**self).setup() + } +} + +impl crate::query_dsl::UpdateAndFetchResults + for PooledConnection +where + M: ManageConnection, + M::Connection: crate::query_dsl::UpdateAndFetchResults, + Self: Connection, +{ + fn update_and_fetch(&self, changeset: Changes) -> QueryResult { + (&**self).update_and_fetch(changeset) + } +} + +#[cfg(test)] +mod tests { + use std::sync::mpsc; + use std::sync::Arc; + use std::thread; + + use crate::r2d2::*; + use crate::test_helpers::*; + + #[test] + fn establish_basic_connection() { + let manager = ConnectionManager::::new(database_url()); + let pool = Arc::new(Pool::builder().max_size(2).build(manager).unwrap()); + + let (s1, r1) = mpsc::channel(); + let (s2, r2) = mpsc::channel(); + + let pool1 = Arc::clone(&pool); + let t1 = thread::spawn(move || { + let conn = pool1.get().unwrap(); + s1.send(()).unwrap(); + r2.recv().unwrap(); + drop(conn); + }); + + let pool2 = Arc::clone(&pool); + let t2 = thread::spawn(move || { + let conn = pool2.get().unwrap(); + s2.send(()).unwrap(); + r1.recv().unwrap(); + drop(conn); + }); + + t1.join().unwrap(); + t2.join().unwrap(); + + pool.get().unwrap(); + } + + #[test] + fn is_valid() { + let manager = ConnectionManager::::new(database_url()); + let pool = Pool::builder() + .max_size(1) + .test_on_check_out(true) + .build(manager) + .unwrap(); + + pool.get().unwrap(); + } + + #[test] + fn pooled_connection_impls_connection() { + use crate::select; + use crate::sql_types::Text; + + let manager = ConnectionManager::::new(database_url()); + let pool = Pool::builder() + .max_size(1) + .test_on_check_out(true) + .build(manager) + .unwrap(); + let conn = pool.get().unwrap(); + + let query = select("foo".into_sql::()); + assert_eq!("foo", query.get_result::(&conn).unwrap()); + } +} diff --git a/collector/benchmarks/diesel/diesel/src/result.rs b/collector/benchmarks/diesel/diesel/src/result.rs new file mode 100644 index 000000000..38b458f3c --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/result.rs @@ -0,0 +1,369 @@ +//! Errors, type aliases, and functions related to working with `Result`. + +use std::convert::From; +use std::error::Error as StdError; +use std::ffi::NulError; +use std::fmt::{self, Display}; + +#[derive(Debug)] +#[allow(clippy::enum_variant_names)] +/// Represents all the ways that a query can fail. +/// +/// This type is not intended to be exhaustively matched, and new variants may +/// be added in the future without a major version bump. +#[non_exhaustive] +pub enum Error { + /// The query contained a nul byte. + /// + /// This should never occur in normal usage. + InvalidCString(NulError), + + /// The database returned an error. + /// + /// While Diesel prevents almost all sources of runtime errors at compile + /// time, it does not attempt to prevent 100% of them. Typically this error + /// will occur from insert or update statements due to a constraint + /// violation. + DatabaseError( + DatabaseErrorKind, + Box, + ), + + /// No rows were returned by a query expected to return at least one row. + /// + /// This variant is only returned by [`get_result`] and [`first`]. [`load`] + /// does not treat 0 rows as an error. If you would like to allow either 0 + /// or 1 rows, call [`optional`] on the result. + /// + /// [`get_result`]: ../query_dsl/trait.RunQueryDsl.html#method.get_result + /// [`first`]: ../query_dsl/trait.RunQueryDsl.html#method.first + /// [`load`]: ../query_dsl/trait.RunQueryDsl.html#method.load + /// [`optional`]: trait.OptionalExtension.html#tymethod.optional + NotFound, + + /// The query could not be constructed + /// + /// An example of when this error could occur is if you are attempting to + /// construct an update statement with no changes (e.g. all fields on the + /// struct are `None`). + QueryBuilderError(Box), + + /// An error occurred deserializing the data being sent to the database. + /// + /// Typically this error means that the stated type of the query is + /// incorrect. An example of when this error might occur in normal usage is + /// attempting to deserialize an infinite date into chrono. + DeserializationError(Box), + + /// An error occurred serializing the data being sent to the database. + /// + /// An example of when this error would be returned is if you attempted to + /// serialize a `chrono::NaiveDate` earlier than the earliest date supported + /// by PostgreSQL. + SerializationError(Box), + + /// Roll back the current transaction. + /// + /// You can return this variant inside of a transaction when you want to + /// roll it back, but have no actual error to return. Diesel will never + /// return this variant unless you gave it to us, and it can be safely + /// ignored in error handling. + RollbackTransaction, + + /// Attempted to perform an operation that cannot be done inside a transaction + /// when a transaction was already open. + AlreadyInTransaction, +} + +#[derive(Debug, Clone, Copy)] +/// The kind of database error that occurred. +/// +/// This is not meant to exhaustively cover all possible errors, but is used to +/// identify errors which are commonly recovered from programmatically. This enum +/// is not intended to be exhaustively matched, and new variants may be added in +/// the future without a major version bump. +pub enum DatabaseErrorKind { + /// A unique constraint was violated. + UniqueViolation, + + /// A foreign key constraint was violated. + ForeignKeyViolation, + + /// The query could not be sent to the database due to a protocol violation. + /// + /// An example of a case where this would occur is if you attempted to send + /// a query with more than 65000 bind parameters using PostgreSQL. + UnableToSendCommand, + + /// A serializable transaction failed to commit due to a read/write + /// dependency on a concurrent transaction. + /// + /// Corresponds to SQLSTATE code 40001 + /// + /// This error is only detected for PostgreSQL, as we do not yet support + /// transaction isolation levels for other backends. + SerializationFailure, + + /// The command could not be completed because the transaction was read + /// only. + /// + /// This error will also be returned for `SELECT` statements which attempted + /// to lock the rows. + ReadOnlyTransaction, + + /// A not null constraint was violated. + NotNullViolation, + + /// A check constraint was violated. + CheckViolation, + + #[doc(hidden)] + __Unknown, // Match against _ instead, more variants may be added in the future +} + +/// Information about an error that was returned by the database. +pub trait DatabaseErrorInformation { + /// The primary human-readable error message. Typically one line. + fn message(&self) -> &str; + + /// An optional secondary error message providing more details about the + /// problem, if it was provided by the database. Might span multiple lines. + fn details(&self) -> Option<&str>; + + /// An optional suggestion of what to do about the problem, if one was + /// provided by the database. + fn hint(&self) -> Option<&str>; + + /// The name of the table the error was associated with, if the error was + /// associated with a specific table and the backend supports retrieving + /// that information. + /// + /// Currently this method will return `None` for all backends other than + /// PostgreSQL. + fn table_name(&self) -> Option<&str>; + + /// The name of the column the error was associated with, if the error was + /// associated with a specific column and the backend supports retrieving + /// that information. + /// + /// Currently this method will return `None` for all backends other than + /// PostgreSQL. + fn column_name(&self) -> Option<&str>; + + /// The constraint that was violated if this error is a constraint violation + /// and the backend supports retrieving that information. + /// + /// Currently this method will return `None` for all backends other than + /// PostgreSQL. + fn constraint_name(&self) -> Option<&str>; +} + +impl fmt::Debug for dyn DatabaseErrorInformation + Send + Sync { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&self.message(), f) + } +} + +impl DatabaseErrorInformation for String { + fn message(&self) -> &str { + self + } + + fn details(&self) -> Option<&str> { + None + } + fn hint(&self) -> Option<&str> { + None + } + fn table_name(&self) -> Option<&str> { + None + } + fn column_name(&self) -> Option<&str> { + None + } + fn constraint_name(&self) -> Option<&str> { + None + } +} + +/// Errors which can occur during [`Connection::establish`] +/// +/// [`Connection::establish`]: ../connection/trait.Connection.html#tymethod.establish +#[derive(Debug, PartialEq)] +#[non_exhaustive] +pub enum ConnectionError { + /// The connection URL contained a `NUL` byte. + InvalidCString(NulError), + /// The database returned an error. + BadConnection(String), + /// The connection URL could not be parsed. + InvalidConnectionUrl(String), + /// Diesel could not configure the database connection. + /// + /// Diesel may try to automatically set session specific configuration + /// values, such as UTF8 encoding, or enabling the `||` operator on MySQL. + /// This variant is returned if an error occurred executing the query to set + /// those options. Diesel will never affect global configuration. + CouldntSetupConfiguration(Error), +} + +/// A specialized result type for queries. +/// +/// This type is exported by `diesel::prelude`, and is generally used by any +/// code which is interacting with Diesel. This type exists to avoid writing out +/// `diesel::result::Error`, and is otherwise a direct mapping to `Result`. +pub type QueryResult = Result; + +/// A specialized result type for establishing connections. +/// +/// This type exists to avoid writing out `diesel::result::ConnectionError`, and +/// is otherwise a direct mapping to `Result`. +pub type ConnectionResult = Result; + +/// See the [method documentation](#tymethod.optional). +pub trait OptionalExtension { + /// Converts a `QueryResult` into a `QueryResult>`. + /// + /// By default, Diesel treats 0 rows being returned from a query that is expected to return 1 + /// row as an error (e.g. the return value of [`get_result`] or [`first`]). This method will + /// handle that error, and give you back an `Option` instead. + /// + /// [`get_result`]: ../query_dsl/trait.RunQueryDsl.html#method.get_result + /// [`first`]: ../query_dsl/trait.RunQueryDsl.html#method.first + /// + /// # Example + /// + /// ```rust + /// use diesel::{QueryResult, NotFound, OptionalExtension}; + /// + /// let result: QueryResult = Ok(1); + /// assert_eq!(Ok(Some(1)), result.optional()); + /// + /// let result: QueryResult = Err(NotFound); + /// assert_eq!(Ok(None), result.optional()); + /// ``` + fn optional(self) -> Result, Error>; +} + +impl OptionalExtension for QueryResult { + fn optional(self) -> Result, Error> { + match self { + Ok(value) => Ok(Some(value)), + Err(Error::NotFound) => Ok(None), + Err(e) => Err(e), + } + } +} + +impl From for ConnectionError { + fn from(e: NulError) -> Self { + ConnectionError::InvalidCString(e) + } +} + +impl From for Error { + fn from(e: NulError) -> Self { + Error::InvalidCString(e) + } +} + +impl Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Error::InvalidCString(ref nul_err) => write!(f, "{}", nul_err), + Error::DatabaseError(_, ref e) => write!(f, "{}", e.message()), + Error::NotFound => f.write_str("Record not found"), + Error::QueryBuilderError(ref e) => e.fmt(f), + Error::DeserializationError(ref e) => e.fmt(f), + Error::SerializationError(ref e) => e.fmt(f), + Error::RollbackTransaction => write!(f, "The current transaction was aborted"), + Error::AlreadyInTransaction => write!( + f, + "Cannot perform this operation while a transaction is open", + ), + } + } +} + +impl StdError for Error { + fn cause(&self) -> Option<&dyn StdError> { + match *self { + Error::InvalidCString(ref e) => Some(e), + Error::QueryBuilderError(ref e) => Some(&**e), + Error::DeserializationError(ref e) => Some(&**e), + Error::SerializationError(ref e) => Some(&**e), + _ => None, + } + } +} + +impl Display for ConnectionError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + ConnectionError::InvalidCString(ref nul_err) => nul_err.fmt(f), + ConnectionError::BadConnection(ref s) => write!(f, "{}", s), + ConnectionError::InvalidConnectionUrl(ref s) => write!(f, "{}", s), + ConnectionError::CouldntSetupConfiguration(ref e) => e.fmt(f), + } + } +} + +impl StdError for ConnectionError { + fn cause(&self) -> Option<&dyn StdError> { + match *self { + ConnectionError::InvalidCString(ref e) => Some(e), + ConnectionError::CouldntSetupConfiguration(ref e) => Some(e), + _ => None, + } + } +} + +impl PartialEq for Error { + fn eq(&self, other: &Error) -> bool { + match (self, other) { + (&Error::InvalidCString(ref a), &Error::InvalidCString(ref b)) => a == b, + (&Error::DatabaseError(_, ref a), &Error::DatabaseError(_, ref b)) => { + a.message() == b.message() + } + (&Error::NotFound, &Error::NotFound) => true, + (&Error::RollbackTransaction, &Error::RollbackTransaction) => true, + (&Error::AlreadyInTransaction, &Error::AlreadyInTransaction) => true, + _ => false, + } + } +} + +#[cfg(test)] +#[allow(warnings)] +fn error_impls_send() { + let err: Error = unimplemented!(); + let x: &Send = &err; +} + +pub(crate) fn first_or_not_found(records: QueryResult>) -> QueryResult { + records?.into_iter().next().ok_or(Error::NotFound) +} + +/// An unexpected `NULL` was encountered during deserialization +#[derive(Debug, Clone, Copy)] +pub struct UnexpectedNullError; + +impl fmt::Display for UnexpectedNullError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Unexpected null for non-null column") + } +} + +impl StdError for UnexpectedNullError {} + +/// Expected more fields then present in the current row while deserialising results +#[derive(Debug, Clone, Copy)] +pub struct UnexpectedEndOfRow; + +impl fmt::Display for UnexpectedEndOfRow { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "Unexpected end of row") + } +} + +impl StdError for UnexpectedEndOfRow {} diff --git a/collector/benchmarks/diesel/diesel/src/row.rs b/collector/benchmarks/diesel/diesel/src/row.rs new file mode 100644 index 000000000..398372d5c --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/row.rs @@ -0,0 +1,205 @@ +//! Contains the `Row` trait + +use crate::{ + backend::{self, Backend}, + deserialize, +}; +use deserialize::FromSql; +use std::ops::Range; + +/// Representing a way to index into database rows +/// +/// * Crates using existing backends should use existing implementations of +/// this traits. Diesel provides `RowIndex` and `RowIndex<&str>` for +/// all bulit-in backends +/// +/// * Crates implementing custom backends need to provide `RowIndex` and +/// `RowIndex<&str>` impls for their [`Row`] type. +/// +/// [`Row`]: trait.Row.html +pub trait RowIndex { + /// Get the numeric index inside the current row for the provided index value + fn idx(&self, idx: I) -> Option; +} + +/// Represents a single database row. +/// +/// This trait is used as an argument to [`FromSqlRow`]. +/// +/// [`FromSqlRow`]: ../deserialize/trait.FromSqlRow.html +pub trait Row<'a, DB: Backend>: RowIndex + for<'b> RowIndex<&'b str> + Sized { + /// Field type returned by a `Row` implementation + /// + /// * Crates using existing backend should not concern themself with the + /// concrete type of this associated type. + /// + /// * Crates implementing custom backends should provide their own type + /// meeting the required trait bounds + type Field: Field<'a, DB>; + + /// Return type of `PartialRow` + /// + /// For all implementations, beside of the `Row` implementation on `PartialRow` itself + /// this should be `Self`. + #[doc(hidden)] + type InnerPartialRow: Row<'a, DB>; + + /// Get the number of fields in the current row + fn field_count(&self) -> usize; + + /// Get the field with the provided index from the row. + /// + /// Returns `None` if there is no matching field for the given index + fn get(&self, idx: I) -> Option + where + Self: RowIndex; + + /// Returns a wrapping row that allows only to access fields, where the index is part of + /// the provided range. + #[doc(hidden)] + fn partial_row(&self, range: Range) -> PartialRow; +} + +/// Represents a single field in a database row. +/// +/// This trait allows retrieving information on the name of the colum and on the value of the +/// field. +pub trait Field<'a, DB: Backend> { + /// The name of the current field + /// + /// Returns `None` if it's an unnamed field + fn field_name(&self) -> Option<&'a str>; + + /// Get the value representing the current field in the raw representation + /// as it is transmitted by the database + fn value(&self) -> Option>; + + /// Checks whether this field is null or not. + fn is_null(&self) -> bool { + self.value().is_none() + } +} + +/// A row type that wraps an inner row +/// +/// This type only allows to access fields of the inner row, whose index is +/// part of `range`. +/// +/// Indexing via `usize` starts with 0 for this row type. The index is then shifted +/// by `self.range.start` to match the corresponding field in the underlying row. +#[derive(Debug)] +#[doc(hidden)] +pub struct PartialRow<'a, R> { + inner: &'a R, + range: Range, +} + +impl<'a, R> PartialRow<'a, R> { + #[doc(hidden)] + pub fn new<'b, DB>(inner: &'a R, range: Range) -> Self + where + R: Row<'b, DB>, + DB: Backend, + { + let range_lower = std::cmp::min(range.start, inner.field_count()); + let range_upper = std::cmp::min(range.end, inner.field_count()); + Self { + inner, + range: range_lower..range_upper, + } + } +} + +impl<'a, 'b, DB, R> Row<'a, DB> for PartialRow<'b, R> +where + DB: Backend, + R: Row<'a, DB>, +{ + type Field = R::Field; + type InnerPartialRow = R; + + fn field_count(&self) -> usize { + self.range.len() + } + + fn get(&self, idx: I) -> Option + where + Self: RowIndex, + { + let idx = self.idx(idx)?; + self.inner.get(idx) + } + + fn partial_row(&self, range: Range) -> PartialRow { + let range_upper_bound = std::cmp::min(self.range.end, self.range.start + range.end); + let range = (self.range.start + range.start)..range_upper_bound; + PartialRow { + inner: self.inner, + range, + } + } +} + +impl<'a, 'b, R> RowIndex<&'a str> for PartialRow<'b, R> +where + R: RowIndex<&'a str>, +{ + fn idx(&self, idx: &'a str) -> Option { + let idx = self.inner.idx(idx)?; + if self.range.contains(&idx) { + Some(idx) + } else { + None + } + } +} + +impl<'a, R> RowIndex for PartialRow<'a, R> +where + R: RowIndex, +{ + fn idx(&self, idx: usize) -> Option { + let idx = self.inner.idx(idx + self.range.start)?; + if self.range.contains(&idx) { + Some(idx) + } else { + None + } + } +} + +/// Represents a row of a SQL query, where the values are accessed by name +/// rather than by index. +/// +/// This trait is used by implementations of +/// [`QueryableByName`](../deserialize/trait.QueryableByName.html) +pub trait NamedRow<'a, DB: Backend>: Row<'a, DB> { + /// Retrieve and deserialize a single value from the query + /// + /// Note that `ST` *must* be the exact type of the value with that name in + /// the query. The compiler will not be able to verify that you have + /// provided the correct type. If there is a mismatch, you may receive an + /// incorrect value, or a runtime error. + /// + /// If two or more fields in the query have the given name, the result of + /// this function is undefined. + fn get<'b, ST, T>(&self, column_name: &'b str) -> deserialize::Result + where + T: FromSql; +} + +impl<'a, R, DB> NamedRow<'a, DB> for R +where + R: Row<'a, DB>, + DB: Backend, +{ + fn get(&self, column_name: &str) -> deserialize::Result + where + T: FromSql, + { + let field = Row::get(self, column_name) + .ok_or_else(|| format!("Column `{}` was not present in query", column_name))?; + + T::from_nullable_sql(field.value()) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/serialize.rs b/collector/benchmarks/diesel/diesel/src/serialize.rs new file mode 100644 index 000000000..d87ca1815 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/serialize.rs @@ -0,0 +1,205 @@ +//! Types and traits related to serializing values for the database + +use std::error::Error; +use std::fmt; +use std::io::{self, Write}; +use std::ops::{Deref, DerefMut}; +use std::result; + +use crate::backend::Backend; +use crate::sql_types::TypeMetadata; + +#[cfg(feature = "postgres")] +pub use crate::pg::serialize::*; + +/// A specialized result type representing the result of serializing +/// a value for the database. +pub type Result = result::Result>; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +/// Tiny enum to make the return type of `ToSql` more descriptive +pub enum IsNull { + /// No data was written, as this type is null + Yes, + /// The value is not null + /// + /// This does not necessarily mean that any data was written to the buffer. + /// For example, an empty string has no data to be sent over the wire, but + /// also is not null. + No, +} + +/// Wraps a buffer to be written by `ToSql` with additional backend specific +/// utilities. +#[derive(Clone, Copy)] +pub struct Output<'a, T, DB> +where + DB: TypeMetadata, + DB::MetadataLookup: 'a, +{ + out: T, + metadata_lookup: Option<&'a DB::MetadataLookup>, +} + +impl<'a, T, DB: TypeMetadata> Output<'a, T, DB> { + /// Construct a new `Output` + pub fn new(out: T, metadata_lookup: &'a DB::MetadataLookup) -> Self { + Output { + out, + metadata_lookup: Some(metadata_lookup), + } + } + + /// Create a new `Output` with the given buffer + pub fn with_buffer(&self, new_out: U) -> Output<'a, U, DB> { + Output { + out: new_out, + metadata_lookup: self.metadata_lookup, + } + } + + /// Return the raw buffer this type is wrapping + pub fn into_inner(self) -> T { + self.out + } + + /// Returns the backend's mechanism for dynamically looking up type + /// metadata at runtime, if relevant for the given backend. + pub fn metadata_lookup(&self) -> &'a DB::MetadataLookup { + self.metadata_lookup.expect("Lookup is there") + } +} + +#[cfg(test)] +impl Output<'static, Vec, DB> { + /// Returns a `Output` suitable for testing `ToSql` implementations. + /// Unsafe to use for testing types which perform dynamic metadata lookup. + pub fn test() -> Self { + Self { + out: Vec::new(), + metadata_lookup: None, + } + } +} + +impl<'a, T: Write, DB: TypeMetadata> Write for Output<'a, T, DB> { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.out.write(buf) + } + + fn flush(&mut self) -> io::Result<()> { + self.out.flush() + } + + fn write_all(&mut self, buf: &[u8]) -> io::Result<()> { + self.out.write_all(buf) + } + + fn write_fmt(&mut self, fmt: fmt::Arguments) -> io::Result<()> { + self.out.write_fmt(fmt) + } +} + +impl<'a, T, DB: TypeMetadata> Deref for Output<'a, T, DB> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.out + } +} + +impl<'a, T, DB: TypeMetadata> DerefMut for Output<'a, T, DB> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.out + } +} + +impl<'a, T, U, DB> PartialEq for Output<'a, T, DB> +where + DB: TypeMetadata, + T: PartialEq, +{ + fn eq(&self, rhs: &U) -> bool { + self.out == *rhs + } +} + +impl<'a, T, DB> fmt::Debug for Output<'a, T, DB> +where + T: fmt::Debug, + DB: TypeMetadata, +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.out.fmt(f) + } +} + +/// Serializes a single value to be sent to the database. +/// +/// The output is sent as a bind parameter, and the data must be written in the +/// expected format for the given backend. +/// +/// When possible, implementations of this trait should prefer using an existing +/// implementation, rather than writing to `out` directly. (For example, if you +/// are implementing this for an enum, which is represented as an integer in the +/// database, you should use `i32::to_sql(x, out)` instead of writing to `out` +/// yourself. +/// +/// Any types which implement this trait should also [`#[derive(AsExpression)]`]. +/// +/// ### Backend specific details +/// +/// - For PostgreSQL, the bytes will be sent using the binary protocol, not text. +/// - For SQLite, all implementations should be written in terms of an existing +/// `ToSql` implementation. +/// - For MySQL, the expected bytes will depend on the return value of +/// `type_metadata` for the given SQL type. See [`MysqlType`] for details. +/// - For third party backends, consult that backend's documentation. +/// +/// [`MysqlType`]: ../mysql/enum.MysqlType.html +/// [`#[derive(AsExpression)]`]: ../expression/derive.AsExpression.html; +/// +/// ### Examples +/// +/// Most implementations of this trait will be defined in terms of an existing +/// implementation. +/// +/// ```rust +/// # use diesel::backend::Backend; +/// # use diesel::expression::AsExpression; +/// # use diesel::sql_types::*; +/// # use diesel::serialize::{self, ToSql, Output}; +/// # use std::io::Write; +/// # +/// #[repr(i32)] +/// #[derive(Debug, Clone, Copy, AsExpression)] +/// #[sql_type = "Integer"] +/// pub enum MyEnum { +/// A = 1, +/// B = 2, +/// } +/// +/// impl ToSql for MyEnum +/// where +/// DB: Backend, +/// i32: ToSql, +/// { +/// fn to_sql(&self, out: &mut Output) -> serialize::Result { +/// (*self as i32).to_sql(out) +/// } +/// } +/// ``` +pub trait ToSql: fmt::Debug { + /// See the trait documentation. + fn to_sql(&self, out: &mut Output) -> Result; +} + +impl<'a, A, T, DB> ToSql for &'a T +where + DB: Backend, + T: ToSql + ?Sized, +{ + fn to_sql(&self, out: &mut Output) -> Result { + (*self).to_sql(out) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/sql_types/fold.rs b/collector/benchmarks/diesel/diesel/src/sql_types/fold.rs new file mode 100644 index 000000000..91e0de2e8 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sql_types/fold.rs @@ -0,0 +1,47 @@ +use crate::sql_types::{self, is_nullable, SingleValue, SqlType}; + +/// Represents SQL types which can be used with `SUM` and `AVG` +pub trait Foldable: SingleValue { + /// The SQL type of `sum(this_type)` + type Sum: SqlType + SingleValue; + /// The SQL type of `avg(this_type)` + type Avg: SqlType + SingleValue; +} + +impl Foldable for sql_types::Nullable +where + T: Foldable + SqlType, +{ + type Sum = T::Sum; + type Avg = T::Avg; +} + +macro_rules! foldable_impls { + ($($Source:ty => ($SumType:ty, $AvgType:ty)),+,) => { + $( + impl Foldable for $Source { + type Sum = sql_types::Nullable<$SumType>; + type Avg = sql_types::Nullable<$AvgType>; + } + )+ + } +} + +foldable_impls! { + sql_types::SmallInt => (sql_types::BigInt, sql_types::Numeric), + sql_types::Integer => (sql_types::BigInt, sql_types::Numeric), + sql_types::BigInt => (sql_types::Numeric, sql_types::Numeric), + + sql_types::Float => (sql_types::Float, sql_types::Double), + sql_types::Double => (sql_types::Double, sql_types::Double), + sql_types::Numeric => (sql_types::Numeric, sql_types::Numeric), + + sql_types::Interval => (sql_types::Interval, sql_types::Interval), +} + +#[cfg(feature = "mysql")] +foldable_impls! { + sql_types::Unsigned => (sql_types::Unsigned, sql_types::Numeric), + sql_types::Unsigned => (sql_types::Unsigned, sql_types::Numeric), + sql_types::Unsigned => (sql_types::Numeric, sql_types::Numeric), +} diff --git a/collector/benchmarks/diesel/diesel/src/sql_types/mod.rs b/collector/benchmarks/diesel/diesel/src/sql_types/mod.rs new file mode 100644 index 000000000..db517968d --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sql_types/mod.rs @@ -0,0 +1,632 @@ +//! Types which represent a SQL data type. +//! +//! The structs in this module are *only* used as markers to represent a SQL type. +//! They should never be used in your structs. +//! If you'd like to know the rust types which can be used for a given SQL type, +//! see the documentation for that SQL type. +//! Additional types may be provided by other crates. +//! +//! To see which SQL type can be used with a given Rust type, +//! see the "Implementors" section of [`FromSql`]. +//! +//! [`FromSql`]: ../deserialize/trait.FromSql.html +//! +//! Any backend specific types are re-exported through this module + +mod fold; +pub mod ops; +mod ord; + +pub use self::fold::Foldable; +pub use self::ord::SqlOrd; + +use crate::expression::TypedExpressionType; +use crate::query_builder::QueryId; + +/// The boolean SQL type. +/// +/// On backends without a native boolean type, +/// this is emulated with the smallest supported integer. +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`bool`][bool] +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`bool`][bool] +/// +/// [bool]: https://doc.rust-lang.org/nightly/std/primitive.bool.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "16", array_oid = "1000")] +#[sqlite_type = "Integer"] +#[mysql_type = "Tiny"] +pub struct Bool; + +/// The tiny integer SQL type. +/// +/// This is only available on MySQL. +/// Keep in mind that `infer_schema!` will see `TINYINT(1)` as `Bool`, +/// not `TinyInt`. +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`i8`][i8] +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`i8`][i8] +/// +/// [i8]: https://doc.rust-lang.org/nightly/std/primitive.i8.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[mysql_type = "Tiny"] +pub struct TinyInt; +#[doc(hidden)] +pub type Tinyint = TinyInt; + +/// The small integer SQL type. +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`i16`][i16] +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`i16`][i16] +/// +/// [i16]: https://doc.rust-lang.org/nightly/std/primitive.i16.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "21", array_oid = "1005")] +#[sqlite_type = "SmallInt"] +#[mysql_type = "Short"] +pub struct SmallInt; +#[doc(hidden)] +pub type Int2 = SmallInt; +#[doc(hidden)] +pub type Smallint = SmallInt; + +/// The integer SQL type. +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`i32`][i32] +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`i32`][i32] +/// +/// [i32]: https://doc.rust-lang.org/nightly/std/primitive.i32.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "23", array_oid = "1007")] +#[sqlite_type = "Integer"] +#[mysql_type = "Long"] +pub struct Integer; +#[doc(hidden)] +pub type Int4 = Integer; + +/// The big integer SQL type. +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`i64`][i64] +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`i64`][i64] +/// +/// [i64]: https://doc.rust-lang.org/nightly/std/primitive.i64.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "20", array_oid = "1016")] +#[sqlite_type = "Long"] +#[mysql_type = "LongLong"] +pub struct BigInt; +#[doc(hidden)] +pub type Int8 = BigInt; +#[doc(hidden)] +pub type Bigint = BigInt; + +/// The float SQL type. +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`f32`][f32] +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`f32`][f32] +/// +/// [f32]: https://doc.rust-lang.org/nightly/std/primitive.f32.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "700", array_oid = "1021")] +#[sqlite_type = "Float"] +#[mysql_type = "Float"] +pub struct Float; +#[doc(hidden)] +pub type Float4 = Float; + +/// The double precision float SQL type. +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`f64`][f64] +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`f64`][f64] +/// +/// [f64]: https://doc.rust-lang.org/nightly/std/primitive.f64.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "701", array_oid = "1022")] +#[sqlite_type = "Double"] +#[mysql_type = "Double"] +pub struct Double; +#[doc(hidden)] +pub type Float8 = Double; + +/// The arbitrary precision numeric SQL type. +/// +/// This type is only supported on PostgreSQL and MySQL. +/// On SQLite, [`Double`](struct.Double.html) should be used instead. +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`bigdecimal::BigDecimal`] with `feature = ["numeric"]` +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`bigdecimal::BigDecimal`] with `feature = ["numeric"]` +/// +/// [`bigdecimal::BigDecimal`]: /bigdecimal/struct.BigDecimal.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "1700", array_oid = "1231")] +#[mysql_type = "Numeric"] +#[sqlite_type = "Double"] +pub struct Numeric; + +/// Alias for `Numeric` +pub type Decimal = Numeric; + +/// The text SQL type. +/// +/// On all backends strings must be valid UTF-8. +/// On PostgreSQL strings must not include nul bytes. +/// +/// Schema inference will treat all variants of `TEXT` as this type (e.g. +/// `VARCHAR`, `MEDIUMTEXT`, etc). +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`String`][String] +/// - [`&str`][str] +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`String`][String] +/// +/// [String]: https://doc.rust-lang.org/nightly/std/string/struct.String.html +/// [str]: https://doc.rust-lang.org/nightly/std/primitive.str.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "25", array_oid = "1009")] +#[sqlite_type = "Text"] +#[mysql_type = "String"] +pub struct Text; + +/// The SQL `VARCHAR` type +/// +/// This type is generally interchangeable with `TEXT`, so Diesel has this as an +/// alias rather than a separate type (Diesel does not currently support +/// implicit coercions). +/// +/// One notable exception to this is with arrays on PG. `TEXT[]` cannot be +/// coerced to `VARCHAR[]`. It is recommended that you always use `TEXT[]` if +/// you need a string array on PG. +pub type VarChar = Text; +#[doc(hidden)] +pub type Varchar = VarChar; +#[doc(hidden)] +pub type Char = Text; +#[doc(hidden)] +pub type Tinytext = Text; +#[doc(hidden)] +pub type Mediumtext = Text; +#[doc(hidden)] +pub type Longtext = Text; + +/// The binary SQL type. +/// +/// Schema inference will treat all variants of `BLOB` as this type (e.g. +/// `VARBINARY`, `MEDIUMBLOB`, etc). +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`Vec`][Vec] +/// - [`&[u8]`][slice] +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`Vec`][Vec] +/// +/// [Vec]: https://doc.rust-lang.org/nightly/std/vec/struct.Vec.html +/// [slice]: https://doc.rust-lang.org/nightly/std/primitive.slice.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "17", array_oid = "1001")] +#[sqlite_type = "Binary"] +#[mysql_type = "Blob"] +pub struct Binary; + +#[doc(hidden)] +pub type Tinyblob = Binary; +#[doc(hidden)] +pub type Blob = Binary; +#[doc(hidden)] +pub type Mediumblob = Binary; +#[doc(hidden)] +pub type Longblob = Binary; +#[doc(hidden)] +pub type Varbinary = Binary; +#[doc(hidden)] +pub type Bit = Binary; + +/// The date SQL type. +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`chrono::NaiveDate`][NaiveDate] with `feature = "chrono"` +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`chrono::NaiveDate`][NaiveDate] with `feature = "chrono"` +/// +/// [NaiveDate]: /chrono/naive/date/struct.NaiveDate.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "1082", array_oid = "1182")] +#[sqlite_type = "Text"] +#[mysql_type = "Date"] +pub struct Date; + +/// The interval SQL type. +/// +/// This type is currently only implemented for PostgreSQL. +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`PgInterval`] which can be constructed using [`IntervalDsl`] +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`PgInterval`] which can be constructed using [`IntervalDsl`] +/// +/// [`PgInterval`]: ../pg/data_types/struct.PgInterval.html +/// [`IntervalDsl`]: ../pg/expression/extensions/trait.IntervalDsl.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "1186", array_oid = "1187")] +pub struct Interval; + +/// The time SQL type. +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`chrono::NaiveTime`][NaiveTime] with `feature = "chrono"` +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`chrono::NaiveTime`][NaiveTime] with `feature = "chrono"` +/// +/// [NaiveTime]: /chrono/naive/time/struct.NaiveTime.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "1083", array_oid = "1183")] +#[sqlite_type = "Text"] +#[mysql_type = "Time"] +pub struct Time; + +/// The timestamp SQL type. +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - [`std::time::SystemTime`][SystemTime] (PG only) +/// - [`chrono::NaiveDateTime`][NaiveDateTime] with `feature = "chrono"` +/// - [`time::Timespec`][Timespec] with `feature = "deprecated-time"` (PG only) +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - [`std::time::SystemTime`][SystemTime] (PG only) +/// - [`chrono::NaiveDateTime`][NaiveDateTime] with `feature = "chrono"` +/// - [`time::Timespec`][Timespec] with `feature = "deprecated-time"` (PG only) +/// +/// [SystemTime]: https://doc.rust-lang.org/nightly/std/time/struct.SystemTime.html +/// [NaiveDateTime]: /chrono/naive/datetime/struct.NaiveDateTime.html +/// [Timespec]: /time/struct.Timespec.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "1114", array_oid = "1115")] +#[sqlite_type = "Text"] +#[mysql_type = "Timestamp"] +pub struct Timestamp; + +/// The JSON SQL type. This type can only be used with `feature = +/// "serde_json"` +/// +/// For postgresql you should normally prefer [`Jsonb`](struct.Jsonb.html) instead, +/// for the reasons discussed there. +/// +/// ### [`ToSql`] impls +/// +/// - [`serde_json::Value`] +/// +/// ### [`FromSql`] impls +/// +/// - [`serde_json::Value`] +/// +/// [`ToSql`]: /serialize/trait.ToSql.html +/// [`FromSql`]: /deserialize/trait.FromSql.html +/// [`serde_json::Value`]: /../serde_json/value/enum.Value.html +#[derive(Debug, Clone, Copy, Default, QueryId, SqlType)] +#[postgres(oid = "114", array_oid = "199")] +#[mysql_type = "String"] +pub struct Json; + +/// The nullable SQL type. +/// +/// This wraps another SQL type to indicate that it can be null. +/// By default all values are assumed to be `NOT NULL`. +/// +/// ### [`ToSql`](../serialize/trait.ToSql.html) impls +/// +/// - Any `T` which implements `ToSql` +/// - `Option` for any `T` which implements `ToSql` +/// +/// ### [`FromSql`](../deserialize/trait.FromSql.html) impls +/// +/// - `Option` for any `T` which implements `FromSql` +#[derive(Debug, Clone, Copy, Default)] +pub struct Nullable(ST); + +impl SqlType for Nullable +where + ST: SqlType, +{ + type IsNull = is_nullable::IsNullable; +} + +#[cfg(feature = "postgres")] +pub use crate::pg::types::sql_types::*; + +#[cfg(feature = "mysql")] +pub use crate::mysql::types::*; + +/// Indicates that a SQL type exists for a backend. +/// +/// This trait can be derived using the [`SqlType` derive](derive.SqlType.html) +/// +/// # Example +/// +/// ```rust +/// #[derive(diesel::sql_types::SqlType)] +/// #[postgres(oid = "23", array_oid = "1007")] +/// #[sqlite_type = "Integer"] +/// #[mysql_type = "Long"] +/// pub struct Integer; +/// ``` +pub trait HasSqlType: TypeMetadata { + /// Fetch the metadata for the given type + /// + /// This method may use `lookup` to do dynamic runtime lookup. Implementors + /// of this method should not do dynamic lookup unless absolutely necessary + fn metadata(lookup: &Self::MetadataLookup) -> Self::TypeMetadata; +} + +/// Information about how a backend stores metadata about given SQL types +pub trait TypeMetadata { + /// The actual type used to represent metadata. + /// + /// On PostgreSQL, this is the type's OID. + /// On MySQL and SQLite, this is an enum representing all storage classes + /// they support. + type TypeMetadata; + /// The type used for runtime lookup of metadata. + /// + /// For most backends, which don't support user defined types, this will + /// be `()`. + type MetadataLookup; +} + +/// Converts a type which may or may not be nullable into its nullable +/// representation. +pub trait IntoNullable { + /// The nullable representation of this type. + /// + /// For all types except `Nullable`, this will be `Nullable`. + type Nullable; +} + +impl IntoNullable for T +where + T: SqlType + SingleValue, +{ + type Nullable = Nullable; +} + +impl IntoNullable for Nullable +where + T: SqlType, +{ + type Nullable = Self; +} + +/// Converts a type which may or may not be nullable into its not nullable +/// representation. +pub trait IntoNotNullable { + /// The not nullable representation of this type. + /// + /// For `Nullable`, this will be `T` otherwise the type itself + type NotNullable; +} + +impl IntoNotNullable for T +where + T: SqlType, +{ + type NotNullable = T; +} + +impl IntoNotNullable for Nullable +where + T: SqlType, +{ + type NotNullable = T; +} + +/// A marker trait indicating that a SQL type represents a single value, as +/// opposed to a list of values. +/// +/// This trait should generally be implemented for all SQL types with the +/// exception of Rust tuples. If a column could have this as its type, this +/// trait should be implemented. +/// +/// # Deriving +/// +/// This trait is automatically implemented by [`#[derive(SqlType)]`] +/// +/// [`#[derive(SqlType)]`]: derive.SqlType.html +pub trait SingleValue: SqlType {} + +impl SingleValue for Nullable {} + +#[doc(inline)] +pub use diesel_derives::DieselNumericOps; +#[doc(inline)] +pub use diesel_derives::SqlType; + +/// A marker trait for SQL types +/// +/// # Deriving +/// +/// This trait is automatically implemented by [`#[derive(SqlType)]`] +/// which sets `IsNull` to [`is_nullable::NotNull`] +/// +/// [`#[derive(SqlType)]`]: derive.SqlType.html +/// [`is_nullable::NotNull`]: is_nullable/struct.NotNull.html +pub trait SqlType { + /// Is this type nullable? + /// + /// This type should always be one of the structs in the ['is_nullable`] + /// module. See the documentation of those structs for more details. + /// + /// ['is_nullable`]: is_nullable/index.html + type IsNull: OneIsNullable + OneIsNullable; +} + +/// Is one value of `IsNull` nullable? +/// +/// You should never implement this trait. +pub trait OneIsNullable { + /// See the trait documentation + type Out: OneIsNullable + OneIsNullable; +} + +/// Are both values of `IsNull` are nullable? +pub trait AllAreNullable { + /// See the trait documentation + type Out: AllAreNullable + AllAreNullable; +} + +/// A type level constructor for maybe nullable types +/// +/// Constructs either `Nullable` (for `Self` == `is_nullable::IsNullable`) +/// or `O` (for `Self` == `is_nullable::NotNull`) +pub trait MaybeNullableType { + /// See the trait documentation + type Out: SqlType + TypedExpressionType; +} + +/// Possible values for `SqlType::IsNullable` +pub mod is_nullable { + use super::*; + + /// No, this type cannot be null as it is marked as `NOT NULL` at database level + /// + /// This should be choosen for basically all manual impls of `SqlType` + /// beside implementing your own `Nullable<>` wrapper type + #[derive(Debug, Clone, Copy)] + pub struct NotNull; + + /// Yes, this type can be null + /// + /// The only diesel provided `SqlType` that uses this value is [`Nullable`] + /// + /// [`Nullable`]: ../struct.Nullable.html + #[derive(Debug, Clone, Copy)] + pub struct IsNullable; + + impl OneIsNullable for NotNull { + type Out = NotNull; + } + + impl OneIsNullable for NotNull { + type Out = IsNullable; + } + + impl OneIsNullable for IsNullable { + type Out = IsNullable; + } + + impl OneIsNullable for IsNullable { + type Out = IsNullable; + } + + impl AllAreNullable for NotNull { + type Out = NotNull; + } + + impl AllAreNullable for NotNull { + type Out = NotNull; + } + + impl AllAreNullable for IsNullable { + type Out = NotNull; + } + + impl AllAreNullable for IsNullable { + type Out = IsNullable; + } + + impl MaybeNullableType for NotNull + where + O: SqlType + TypedExpressionType, + { + type Out = O; + } + + impl MaybeNullableType for IsNullable + where + O: SqlType, + Nullable: TypedExpressionType, + { + type Out = Nullable; + } + + /// Represents the output type of [`MaybeNullableType`](../trait.MaybeNullableType.html) + pub type MaybeNullable = >::Out; + + /// Represents the output type of [`OneIsNullable`](../trait.OneIsNullable.html) + /// for two given SQL types + pub type IsOneNullable = + as OneIsNullable>>::Out; + + /// Represents the output type of [`AllAreNullable`](../trait.AllAreNullable.html) + /// for two given SQL types + pub type AreAllNullable = + as AllAreNullable>>::Out; + + /// Represents if the SQL type is nullable or not + pub type IsSqlTypeNullable = ::IsNull; +} + +/// A marker trait for accepting expressions of the type `Bool` and +/// `Nullable` in the same place +pub trait BoolOrNullableBool {} + +impl BoolOrNullableBool for Bool {} +impl BoolOrNullableBool for Nullable {} + +#[doc(inline)] +pub use crate::expression::expression_types::Untyped; diff --git a/collector/benchmarks/diesel/diesel/src/sql_types/ops.rs b/collector/benchmarks/diesel/diesel/src/sql_types/ops.rs new file mode 100644 index 000000000..f548764c6 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sql_types/ops.rs @@ -0,0 +1,184 @@ +//! Represents the output of numeric operators in SQL +//! +//! Apps should not need to concern themselves with this module. +//! If you are looking for where the actual implementation of `std::ops::Add` +//! and friends are generated for columns, see +//! [`numeric_expr!`](../../macro.numeric_expr.html). +//! +//! Crates which add new types which allow numeric operators should implement +//! these traits to specify what the output is for a given right hand side. +//! +//! Unlike the traits in `std::ops`, the right hand side is an associated type +//! rather than a type parameter. The biggest drawback of this is that any type +//! can only have one right hand type which can be added/subtracted, etc. The +//! most immediately noticeable effect of this is that you cannot add a nullable +//! number to one that is not nullable. +//! +//! The reason for this is because of the impl of `std::ops::Add` that we need +//! to be able to write. We want the right hand side to allow Rust values which +//! should be sent as bind parameters, not just other Diesel expressions. That +//! means the impl would look like this: +//! +//! ```ignore +//! impl std::ops::Add for my_column +//! where +//! T: AsExpression, +//! my_column::SqlType: diesel::ops::Add, +//! ``` +//! +//! This impl is not valid in Rust, as `ST` is not constrained by the trait or +//! the implementing type. If there were two valid types for `ST` which +//! satisfied all constraints, Rust would not know which one to use, and there +//! would be no way for the user to specify which one should be used. + +use super::*; + +/// Represents SQL types which can be added. +pub trait Add { + /// The SQL type which can be added to this one + type Rhs: SqlType; + /// The SQL type of the result of adding `Rhs` to `Self` + type Output: SqlType; +} + +/// Represents SQL types which can be subtracted. +pub trait Sub { + /// The SQL type which can be subtracted from this one + type Rhs: SqlType; + /// The SQL type of the result of subtracting `Rhs` from `Self` + type Output: SqlType; +} + +/// Represents SQL types which can be multiplied. +pub trait Mul { + /// The SQL type which this can be multiplied by + type Rhs: SqlType; + /// The SQL type of the result of multiplying `Self` by `Rhs` + type Output: SqlType; +} + +/// Represents SQL types which can be divided. +pub trait Div { + /// The SQL type which this one can be divided by + type Rhs: SqlType; + /// The SQL type of the result of dividing `Self` by `Rhs` + type Output: SqlType; +} + +macro_rules! numeric_type { + ($($tpe: ident),*) => { + $( + impl Add for $tpe { + type Rhs = $tpe; + type Output = $tpe; + } + + impl Sub for $tpe { + type Rhs = $tpe; + type Output = $tpe; + } + + impl Mul for $tpe { + type Rhs = $tpe; + type Output = $tpe; + } + + impl Div for $tpe { + type Rhs = $tpe; + type Output = $tpe; + } + )* + } +} + +numeric_type!(SmallInt, Integer, BigInt, Float, Double, Numeric); + +impl Add for Time { + type Rhs = Interval; + type Output = Time; +} + +impl Sub for Time { + type Rhs = Interval; + type Output = Time; +} + +impl Add for Date { + type Rhs = Interval; + type Output = Timestamp; +} + +impl Sub for Date { + type Rhs = Interval; + type Output = Timestamp; +} + +impl Add for Timestamp { + type Rhs = Interval; + type Output = Timestamp; +} + +impl Sub for Timestamp { + type Rhs = Interval; + type Output = Timestamp; +} + +impl Add for Interval { + type Rhs = Interval; + type Output = Interval; +} + +impl Sub for Interval { + type Rhs = Interval; + type Output = Interval; +} + +impl Mul for Interval { + type Rhs = Integer; + type Output = Interval; +} + +impl Div for Interval { + type Rhs = Integer; + type Output = Interval; +} + +impl Add for Nullable +where + T: Add + SqlType, + T::Rhs: SqlType, + T::Output: SqlType, +{ + type Rhs = Nullable; + type Output = Nullable; +} + +impl Sub for Nullable +where + T: Sub + SqlType, + T::Rhs: SqlType, + T::Output: SqlType, +{ + type Rhs = Nullable; + type Output = Nullable; +} + +impl Mul for Nullable +where + T: Mul + SqlType, + T::Rhs: SqlType, + T::Output: SqlType, +{ + type Rhs = Nullable; + type Output = Nullable; +} + +impl Div for Nullable +where + T: Div + SqlType, + T::Rhs: SqlType, + T::Output: SqlType, +{ + type Rhs = Nullable; + type Output = Nullable; +} diff --git a/collector/benchmarks/diesel/diesel/src/sql_types/ord.rs b/collector/benchmarks/diesel/diesel/src/sql_types/ord.rs new file mode 100644 index 000000000..e3ba8d3fd --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sql_types/ord.rs @@ -0,0 +1,30 @@ +use crate::sql_types::{self, is_nullable, SqlType}; + +/// Marker trait for types which can be used with `MAX` and `MIN` +pub trait SqlOrd: SqlType {} + +impl SqlOrd for sql_types::SmallInt {} +impl SqlOrd for sql_types::Integer {} +impl SqlOrd for sql_types::BigInt {} +impl SqlOrd for sql_types::Float {} +impl SqlOrd for sql_types::Double {} +impl SqlOrd for sql_types::Text {} +impl SqlOrd for sql_types::Date {} +impl SqlOrd for sql_types::Interval {} +impl SqlOrd for sql_types::Time {} +impl SqlOrd for sql_types::Timestamp {} +impl SqlOrd for sql_types::Nullable where T: SqlOrd + SqlType {} + +#[cfg(feature = "postgres")] +impl SqlOrd for sql_types::Timestamptz {} +#[cfg(feature = "postgres")] +impl SqlOrd for sql_types::Array {} + +#[cfg(feature = "mysql")] +impl SqlOrd for sql_types::Datetime {} +#[cfg(feature = "mysql")] +impl SqlOrd for sql_types::Unsigned {} +#[cfg(feature = "mysql")] +impl SqlOrd for sql_types::Unsigned {} +#[cfg(feature = "mysql")] +impl SqlOrd for sql_types::Unsigned {} diff --git a/collector/benchmarks/diesel/diesel/src/sqlite/backend.rs b/collector/benchmarks/diesel/diesel/src/sqlite/backend.rs new file mode 100644 index 000000000..1c6af1637 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sqlite/backend.rs @@ -0,0 +1,57 @@ +//! The SQLite backend +use byteorder::NativeEndian; + +use super::connection::SqliteValue; +use super::query_builder::SqliteQueryBuilder; +use crate::backend::*; +use crate::query_builder::bind_collector::RawBytesBindCollector; +use crate::sql_types::TypeMetadata; + +/// The SQLite backend +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct Sqlite; + +/// Determines how a bind parameter is given to SQLite +/// +/// Diesel deals with bind parameters after serialization as opaque blobs of +/// bytes. However, SQLite instead has several functions where it expects the +/// relevant C types. +/// +/// The variants of this struct determine what bytes are expected from +/// `ToSql` impls. +#[allow(missing_debug_implementations)] +#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)] +pub enum SqliteType { + /// Bind using `sqlite3_bind_blob` + Binary, + /// Bind using `sqlite3_bind_text` + Text, + /// `bytes` should contain an `f32` + Float, + /// `bytes` should contain an `f64` + Double, + /// `bytes` should contain an `i16` + SmallInt, + /// `bytes` should contain an `i32` + Integer, + /// `bytes` should contain an `i64` + Long, +} + +impl Backend for Sqlite { + type QueryBuilder = SqliteQueryBuilder; + type BindCollector = RawBytesBindCollector; + type ByteOrder = NativeEndian; +} + +impl<'a> HasRawValue<'a> for Sqlite { + type RawValue = SqliteValue<'a>; +} + +impl TypeMetadata for Sqlite { + type TypeMetadata = SqliteType; + type MetadataLookup = (); +} + +impl SupportsOnConflictClause for Sqlite {} +impl UsesAnsiSavepointSyntax for Sqlite {} diff --git a/collector/benchmarks/diesel/diesel/src/sqlite/connection/diesel_manage_updated_at.sql b/collector/benchmarks/diesel/diesel/src/sqlite/connection/diesel_manage_updated_at.sql new file mode 100644 index 000000000..83c3d33d4 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sqlite/connection/diesel_manage_updated_at.sql @@ -0,0 +1,11 @@ +CREATE TRIGGER __diesel_manage_updated_at_{table_name} +AFTER UPDATE ON {table_name} +FOR EACH ROW WHEN + old.updated_at IS NULL AND + new.updated_at IS NULL OR + old.updated_at == new.updated_at +BEGIN + UPDATE {table_name} + SET updated_at = CURRENT_TIMESTAMP + WHERE ROWID = new.ROWID; +END diff --git a/collector/benchmarks/diesel/diesel/src/sqlite/connection/functions.rs b/collector/benchmarks/diesel/diesel/src/sqlite/connection/functions.rs new file mode 100644 index 000000000..c3fccc9df --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sqlite/connection/functions.rs @@ -0,0 +1,170 @@ +extern crate libsqlite3_sys as ffi; + +use super::raw::RawConnection; +use super::serialized_value::SerializedValue; +use super::{Sqlite, SqliteAggregateFunction, SqliteValue}; +use crate::deserialize::{FromSqlRow, StaticallySizedRow}; +use crate::result::{DatabaseErrorKind, Error, QueryResult}; +use crate::row::{Field, PartialRow, Row, RowIndex}; +use crate::serialize::{IsNull, Output, ToSql}; +use crate::sql_types::HasSqlType; +use std::marker::PhantomData; + +pub fn register( + conn: &RawConnection, + fn_name: &str, + deterministic: bool, + mut f: F, +) -> QueryResult<()> +where + F: FnMut(&RawConnection, Args) -> Ret + Send + 'static, + Args: FromSqlRow + StaticallySizedRow, + Ret: ToSql, + Sqlite: HasSqlType, +{ + let fields_needed = Args::FIELD_COUNT; + if fields_needed > 127 { + return Err(Error::DatabaseError( + DatabaseErrorKind::UnableToSendCommand, + Box::new("SQLite functions cannot take more than 127 parameters".to_string()), + )); + } + + conn.register_sql_function(fn_name, fields_needed, deterministic, move |conn, args| { + let args = build_sql_function_args::(args)?; + + let result = f(conn, args); + + process_sql_function_result::(result) + })?; + Ok(()) +} + +pub fn register_aggregate( + conn: &RawConnection, + fn_name: &str, +) -> QueryResult<()> +where + A: SqliteAggregateFunction + 'static + Send, + Args: FromSqlRow + StaticallySizedRow, + Ret: ToSql, + Sqlite: HasSqlType, +{ + let fields_needed = Args::FIELD_COUNT; + if fields_needed > 127 { + return Err(Error::DatabaseError( + DatabaseErrorKind::UnableToSendCommand, + Box::new("SQLite functions cannot take more than 127 parameters".to_string()), + )); + } + + conn.register_aggregate_function::( + fn_name, + fields_needed, + )?; + + Ok(()) +} + +pub(crate) fn build_sql_function_args( + args: &[*mut ffi::sqlite3_value], +) -> Result +where + Args: FromSqlRow, +{ + let row = FunctionRow::new(args); + Args::build_from_row(&row).map_err(Error::DeserializationError) +} + +pub(crate) fn process_sql_function_result( + result: Ret, +) -> QueryResult +where + Ret: ToSql, + Sqlite: HasSqlType, +{ + let mut buf = Output::new(Vec::new(), &()); + let is_null = result.to_sql(&mut buf).map_err(Error::SerializationError)?; + + let bytes = if let IsNull::Yes = is_null { + None + } else { + Some(buf.into_inner()) + }; + + Ok(SerializedValue { + ty: Sqlite::metadata(&()), + data: bytes, + }) +} + +#[derive(Clone)] +struct FunctionRow<'a> { + args: &'a [*mut ffi::sqlite3_value], +} + +impl<'a> FunctionRow<'a> { + fn new(args: &'a [*mut ffi::sqlite3_value]) -> Self { + Self { args } + } +} + +impl<'a> Row<'a, Sqlite> for FunctionRow<'a> { + type Field = FunctionArgument<'a>; + type InnerPartialRow = Self; + + fn field_count(&self) -> usize { + self.args.len() + } + + fn get(&self, idx: I) -> Option + where + Self: crate::row::RowIndex, + { + let idx = self.idx(idx)?; + + self.args.get(idx).map(|arg| FunctionArgument { + arg: *arg, + p: PhantomData, + }) + } + + fn partial_row(&self, range: std::ops::Range) -> PartialRow { + PartialRow::new(self, range) + } +} + +impl<'a> RowIndex for FunctionRow<'a> { + fn idx(&self, idx: usize) -> Option { + if idx < self.args.len() { + Some(idx) + } else { + None + } + } +} + +impl<'a, 'b> RowIndex<&'a str> for FunctionRow<'b> { + fn idx(&self, _idx: &'a str) -> Option { + None + } +} + +struct FunctionArgument<'a> { + arg: *mut ffi::sqlite3_value, + p: PhantomData<&'a ()>, +} + +impl<'a> Field<'a, Sqlite> for FunctionArgument<'a> { + fn field_name(&self) -> Option<&'a str> { + None + } + + fn is_null(&self) -> bool { + self.value().is_none() + } + + fn value(&self) -> Option> { + unsafe { SqliteValue::new(self.arg) } + } +} diff --git a/collector/benchmarks/diesel/diesel/src/sqlite/connection/mod.rs b/collector/benchmarks/diesel/diesel/src/sqlite/connection/mod.rs new file mode 100644 index 000000000..a52a1f842 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sqlite/connection/mod.rs @@ -0,0 +1,517 @@ +extern crate libsqlite3_sys as ffi; + +mod functions; +#[doc(hidden)] +pub mod raw; +mod serialized_value; +mod sqlite_value; +mod statement_iterator; +mod stmt; + +pub use self::sqlite_value::SqliteValue; + +use std::os::raw as libc; + +use self::raw::RawConnection; +use self::statement_iterator::*; +use self::stmt::{Statement, StatementUse}; +use super::SqliteAggregateFunction; +use crate::connection::*; +use crate::deserialize::{FromSqlRow, StaticallySizedRow}; +use crate::expression::QueryMetadata; +use crate::query_builder::bind_collector::RawBytesBindCollector; +use crate::query_builder::*; +use crate::result::*; +use crate::serialize::ToSql; +use crate::sql_types::HasSqlType; +use crate::sqlite::Sqlite; + +/// Connections for the SQLite backend. Unlike other backends, "connection URLs" +/// for SQLite are file paths, [URIs](https://sqlite.org/uri.html), or special +/// identifiers like `:memory:`. +#[allow(missing_debug_implementations)] +pub struct SqliteConnection { + statement_cache: StatementCache, + raw_connection: RawConnection, + transaction_manager: AnsiTransactionManager, +} + +// This relies on the invariant that RawConnection or Statement are never +// leaked. If a reference to one of those was held on a different thread, this +// would not be thread safe. +unsafe impl Send for SqliteConnection {} + +impl SimpleConnection for SqliteConnection { + fn batch_execute(&self, query: &str) -> QueryResult<()> { + self.raw_connection.exec(query) + } +} + +impl Connection for SqliteConnection { + type Backend = Sqlite; + type TransactionManager = AnsiTransactionManager; + + fn establish(database_url: &str) -> ConnectionResult { + use crate::result::ConnectionError::CouldntSetupConfiguration; + + let raw_connection = RawConnection::establish(database_url)?; + let conn = Self { + statement_cache: StatementCache::new(), + raw_connection, + transaction_manager: AnsiTransactionManager::new(), + }; + conn.register_diesel_sql_functions() + .map_err(CouldntSetupConfiguration)?; + Ok(conn) + } + + #[doc(hidden)] + fn execute(&self, query: &str) -> QueryResult { + self.batch_execute(query)?; + Ok(self.raw_connection.rows_affected_by_last_query()) + } + + #[doc(hidden)] + fn load(&self, source: T) -> QueryResult> + where + T: AsQuery, + T::Query: QueryFragment + QueryId, + U: FromSqlRow, + Self::Backend: QueryMetadata, + { + let mut statement = self.prepare_query(&source.as_query())?; + let statement_use = StatementUse::new(&mut statement); + let iter = StatementIterator::new(statement_use); + iter.collect() + } + + #[doc(hidden)] + fn execute_returning_count(&self, source: &T) -> QueryResult + where + T: QueryFragment + QueryId, + { + let mut statement = self.prepare_query(source)?; + let mut statement_use = StatementUse::new(&mut statement); + statement_use.run()?; + Ok(self.raw_connection.rows_affected_by_last_query()) + } + + #[doc(hidden)] + fn transaction_manager(&self) -> &Self::TransactionManager { + &self.transaction_manager + } +} + +impl SqliteConnection { + /// Run a transaction with `BEGIN IMMEDIATE` + /// + /// This method will return an error if a transaction is already open. + /// + /// # Example + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # let conn = SqliteConnection::establish(":memory:").unwrap(); + /// conn.immediate_transaction(|| { + /// // Do stuff in a transaction + /// Ok(()) + /// }) + /// # } + /// ``` + pub fn immediate_transaction(&self, f: F) -> Result + where + F: FnOnce() -> Result, + E: From, + { + self.transaction_sql(f, "BEGIN IMMEDIATE") + } + + /// Run a transaction with `BEGIN EXCLUSIVE` + /// + /// This method will return an error if a transaction is already open. + /// + /// # Example + /// + /// ```rust + /// # include!("../../doctest_setup.rs"); + /// # + /// # fn main() { + /// # run_test().unwrap(); + /// # } + /// # + /// # fn run_test() -> QueryResult<()> { + /// # let conn = SqliteConnection::establish(":memory:").unwrap(); + /// conn.exclusive_transaction(|| { + /// // Do stuff in a transaction + /// Ok(()) + /// }) + /// # } + /// ``` + pub fn exclusive_transaction(&self, f: F) -> Result + where + F: FnOnce() -> Result, + E: From, + { + self.transaction_sql(f, "BEGIN EXCLUSIVE") + } + + fn transaction_sql(&self, f: F, sql: &str) -> Result + where + F: FnOnce() -> Result, + E: From, + { + let transaction_manager = self.transaction_manager(); + + transaction_manager.begin_transaction_sql(self, sql)?; + match f() { + Ok(value) => { + transaction_manager.commit_transaction(self)?; + Ok(value) + } + Err(e) => { + transaction_manager.rollback_transaction(self)?; + Err(e) + } + } + } + + fn prepare_query + QueryId>( + &self, + source: &T, + ) -> QueryResult> { + let mut statement = self.cached_prepared_statement(source)?; + + let mut bind_collector = RawBytesBindCollector::::new(); + source.collect_binds(&mut bind_collector, &())?; + let metadata = bind_collector.metadata; + let binds = bind_collector.binds; + for (tpe, value) in metadata.into_iter().zip(binds) { + statement.bind(tpe, value)?; + } + + Ok(statement) + } + + fn cached_prepared_statement + QueryId>( + &self, + source: &T, + ) -> QueryResult> { + self.statement_cache.cached_statement(source, &[], |sql| { + Statement::prepare(&self.raw_connection, sql) + }) + } + + #[doc(hidden)] + pub fn register_sql_function( + &self, + fn_name: &str, + deterministic: bool, + mut f: F, + ) -> QueryResult<()> + where + F: FnMut(Args) -> Ret + Send + 'static, + Args: FromSqlRow + StaticallySizedRow, + Ret: ToSql, + Sqlite: HasSqlType, + { + functions::register( + &self.raw_connection, + fn_name, + deterministic, + move |_, args| f(args), + ) + } + + #[doc(hidden)] + pub fn register_aggregate_function( + &self, + fn_name: &str, + ) -> QueryResult<()> + where + A: SqliteAggregateFunction + 'static + Send, + Args: FromSqlRow + StaticallySizedRow, + Ret: ToSql, + Sqlite: HasSqlType, + { + functions::register_aggregate::<_, _, _, _, A>(&self.raw_connection, fn_name) + } + + fn register_diesel_sql_functions(&self) -> QueryResult<()> { + use crate::sql_types::{Integer, Text}; + + functions::register::( + &self.raw_connection, + "diesel_manage_updated_at", + false, + |conn, table_name: String| { + conn.exec(&format!( + include_str!("diesel_manage_updated_at.sql"), + table_name = table_name + )) + .expect("Failed to create trigger"); + 0 // have to return *something* + }, + ) + } +} + +fn error_message(err_code: libc::c_int) -> &'static str { + ffi::code_to_str(err_code) +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::dsl::sql; + use crate::prelude::*; + use crate::sql_types::Integer; + + #[test] + fn prepared_statements_are_cached_when_run() { + let connection = SqliteConnection::establish(":memory:").unwrap(); + let query = crate::select(1.into_sql::()); + + assert_eq!(Ok(1), query.get_result(&connection)); + assert_eq!(Ok(1), query.get_result(&connection)); + assert_eq!(1, connection.statement_cache.len()); + } + + #[test] + fn sql_literal_nodes_are_not_cached() { + let connection = SqliteConnection::establish(":memory:").unwrap(); + let query = crate::select(sql::("1")); + + assert_eq!(Ok(1), query.get_result(&connection)); + assert_eq!(0, connection.statement_cache.len()); + } + + #[test] + fn queries_containing_sql_literal_nodes_are_not_cached() { + let connection = SqliteConnection::establish(":memory:").unwrap(); + let one_as_expr = 1.into_sql::(); + let query = crate::select(one_as_expr.eq(sql::("1"))); + + assert_eq!(Ok(true), query.get_result(&connection)); + assert_eq!(0, connection.statement_cache.len()); + } + + #[test] + fn queries_containing_in_with_vec_are_not_cached() { + let connection = SqliteConnection::establish(":memory:").unwrap(); + let one_as_expr = 1.into_sql::(); + let query = crate::select(one_as_expr.eq_any(vec![1, 2, 3])); + + assert_eq!(Ok(true), query.get_result(&connection)); + assert_eq!(0, connection.statement_cache.len()); + } + + #[test] + fn queries_containing_in_with_subselect_are_cached() { + let connection = SqliteConnection::establish(":memory:").unwrap(); + let one_as_expr = 1.into_sql::(); + let query = crate::select(one_as_expr.eq_any(crate::select(one_as_expr))); + + assert_eq!(Ok(true), query.get_result(&connection)); + assert_eq!(1, connection.statement_cache.len()); + } + + use crate::sql_types::Text; + sql_function!(fn fun_case(x: Text) -> Text); + + #[test] + fn register_custom_function() { + let connection = SqliteConnection::establish(":memory:").unwrap(); + fun_case::register_impl(&connection, |x: String| { + x.chars() + .enumerate() + .map(|(i, c)| { + if i % 2 == 0 { + c.to_lowercase().to_string() + } else { + c.to_uppercase().to_string() + } + }) + .collect::() + }) + .unwrap(); + + let mapped_string = crate::select(fun_case("foobar")) + .get_result::(&connection) + .unwrap(); + assert_eq!("fOoBaR", mapped_string); + } + + sql_function!(fn my_add(x: Integer, y: Integer) -> Integer); + + #[test] + fn register_multiarg_function() { + let connection = SqliteConnection::establish(":memory:").unwrap(); + my_add::register_impl(&connection, |x: i32, y: i32| x + y).unwrap(); + + let added = crate::select(my_add(1, 2)).get_result::(&connection); + assert_eq!(Ok(3), added); + } + + sql_function!(fn add_counter(x: Integer) -> Integer); + + #[test] + fn register_nondeterministic_function() { + let connection = SqliteConnection::establish(":memory:").unwrap(); + let mut y = 0; + add_counter::register_nondeterministic_impl(&connection, move |x: i32| { + y += 1; + x + y + }) + .unwrap(); + + let added = crate::select((add_counter(1), add_counter(1), add_counter(1))) + .get_result::<(i32, i32, i32)>(&connection); + assert_eq!(Ok((2, 3, 4)), added); + } + + use crate::sqlite::SqliteAggregateFunction; + + sql_function! { + #[aggregate] + fn my_sum(expr: Integer) -> Integer; + } + + #[derive(Default)] + struct MySum { + sum: i32, + } + + impl SqliteAggregateFunction for MySum { + type Output = i32; + + fn step(&mut self, expr: i32) { + self.sum += expr; + } + + fn finalize(aggregator: Option) -> Self::Output { + aggregator.map(|a| a.sum).unwrap_or_default() + } + } + + table! { + my_sum_example { + id -> Integer, + value -> Integer, + } + } + + #[test] + fn register_aggregate_function() { + use self::my_sum_example::dsl::*; + + let connection = SqliteConnection::establish(":memory:").unwrap(); + connection + .execute( + "CREATE TABLE my_sum_example (id integer primary key autoincrement, value integer)", + ) + .unwrap(); + connection + .execute("INSERT INTO my_sum_example (value) VALUES (1), (2), (3)") + .unwrap(); + + my_sum::register_impl::(&connection).unwrap(); + + let result = my_sum_example + .select(my_sum(value)) + .get_result::(&connection); + assert_eq!(Ok(6), result); + } + + #[test] + fn register_aggregate_function_returns_finalize_default_on_empty_set() { + use self::my_sum_example::dsl::*; + + let connection = SqliteConnection::establish(":memory:").unwrap(); + connection + .execute( + "CREATE TABLE my_sum_example (id integer primary key autoincrement, value integer)", + ) + .unwrap(); + + my_sum::register_impl::(&connection).unwrap(); + + let result = my_sum_example + .select(my_sum(value)) + .get_result::(&connection); + assert_eq!(Ok(0), result); + } + + sql_function! { + #[aggregate] + fn range_max(expr1: Integer, expr2: Integer, expr3: Integer) -> Nullable; + } + + #[derive(Default)] + struct RangeMax { + max_value: Option, + } + + impl SqliteAggregateFunction<(T, T, T)> for RangeMax { + type Output = Option; + + fn step(&mut self, (x0, x1, x2): (T, T, T)) { + let max = if x0 >= x1 && x0 >= x2 { + x0 + } else if x1 >= x0 && x1 >= x2 { + x1 + } else { + x2 + }; + + self.max_value = match self.max_value { + Some(current_max_value) if max > current_max_value => Some(max), + None => Some(max), + _ => self.max_value, + }; + } + + fn finalize(aggregator: Option) -> Self::Output { + aggregator?.max_value + } + } + + table! { + range_max_example { + id -> Integer, + value1 -> Integer, + value2 -> Integer, + value3 -> Integer, + } + } + + #[test] + fn register_aggregate_multiarg_function() { + use self::range_max_example::dsl::*; + + let connection = SqliteConnection::establish(":memory:").unwrap(); + connection + .execute( + r#"CREATE TABLE range_max_example ( + id integer primary key autoincrement, + value1 integer, + value2 integer, + value3 integer + )"#, + ) + .unwrap(); + connection.execute("INSERT INTO range_max_example (value1, value2, value3) VALUES (3, 2, 1), (2, 2, 2)").unwrap(); + + range_max::register_impl::, _, _, _>(&connection).unwrap(); + let result = range_max_example + .select(range_max(value1, value2, value3)) + .get_result::>(&connection) + .unwrap(); + assert_eq!(Some(3), result); + } +} diff --git a/collector/benchmarks/diesel/diesel/src/sqlite/connection/raw.rs b/collector/benchmarks/diesel/diesel/src/sqlite/connection/raw.rs new file mode 100644 index 000000000..7d2484646 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sqlite/connection/raw.rs @@ -0,0 +1,390 @@ +extern crate libsqlite3_sys as ffi; + +use std::ffi::{CStr, CString, NulError}; +use std::io::{stderr, Write}; +use std::os::raw as libc; +use std::ptr::NonNull; +use std::{mem, ptr, slice, str}; + +use super::functions::{build_sql_function_args, process_sql_function_result}; +use super::serialized_value::SerializedValue; +use super::{Sqlite, SqliteAggregateFunction}; +use crate::deserialize::FromSqlRow; +use crate::result::Error::DatabaseError; +use crate::result::*; +use crate::serialize::ToSql; +use crate::sql_types::HasSqlType; + +#[allow(missing_debug_implementations, missing_copy_implementations)] +pub struct RawConnection { + pub(crate) internal_connection: NonNull, +} + +impl RawConnection { + pub fn establish(database_url: &str) -> ConnectionResult { + let mut conn_pointer = ptr::null_mut(); + + let database_url = if database_url.starts_with("sqlite://") { + CString::new(database_url.replacen("sqlite://", "file:", 1))? + } else { + CString::new(database_url)? + }; + let flags = ffi::SQLITE_OPEN_READWRITE | ffi::SQLITE_OPEN_CREATE | ffi::SQLITE_OPEN_URI; + let connection_status = unsafe { + ffi::sqlite3_open_v2(database_url.as_ptr(), &mut conn_pointer, flags, ptr::null()) + }; + + match connection_status { + ffi::SQLITE_OK => { + let conn_pointer = unsafe { NonNull::new_unchecked(conn_pointer) }; + Ok(RawConnection { + internal_connection: conn_pointer, + }) + } + err_code => { + let message = super::error_message(err_code); + Err(ConnectionError::BadConnection(message.into())) + } + } + } + + pub fn exec(&self, query: &str) -> QueryResult<()> { + let mut err_msg = ptr::null_mut(); + let query = CString::new(query)?; + let callback_fn = None; + let callback_arg = ptr::null_mut(); + unsafe { + ffi::sqlite3_exec( + self.internal_connection.as_ptr(), + query.as_ptr(), + callback_fn, + callback_arg, + &mut err_msg, + ); + } + + if err_msg.is_null() { + Ok(()) + } else { + let msg = convert_to_string_and_free(err_msg); + let error_kind = DatabaseErrorKind::__Unknown; + Err(DatabaseError(error_kind, Box::new(msg))) + } + } + + pub fn rows_affected_by_last_query(&self) -> usize { + unsafe { ffi::sqlite3_changes(self.internal_connection.as_ptr()) as usize } + } + + pub fn register_sql_function( + &self, + fn_name: &str, + num_args: usize, + deterministic: bool, + f: F, + ) -> QueryResult<()> + where + F: FnMut(&Self, &[*mut ffi::sqlite3_value]) -> QueryResult + + Send + + 'static, + { + let fn_name = Self::get_fn_name(fn_name)?; + let flags = Self::get_flags(deterministic); + let callback_fn = Box::into_raw(Box::new(f)); + + let result = unsafe { + ffi::sqlite3_create_function_v2( + self.internal_connection.as_ptr(), + fn_name.as_ptr(), + num_args as _, + flags, + callback_fn as *mut _, + Some(run_custom_function::), + None, + None, + Some(destroy_boxed_fn::), + ) + }; + + Self::process_sql_function_result(result) + } + + pub fn register_aggregate_function( + &self, + fn_name: &str, + num_args: usize, + ) -> QueryResult<()> + where + A: SqliteAggregateFunction + 'static + Send, + Args: FromSqlRow, + Ret: ToSql, + Sqlite: HasSqlType, + { + let fn_name = Self::get_fn_name(fn_name)?; + let flags = Self::get_flags(false); + + let result = unsafe { + ffi::sqlite3_create_function_v2( + self.internal_connection.as_ptr(), + fn_name.as_ptr(), + num_args as _, + flags, + ptr::null_mut(), + None, + Some(run_aggregator_step_function::<_, _, _, _, A>), + Some(run_aggregator_final_function::<_, _, _, _, A>), + None, + ) + }; + + Self::process_sql_function_result(result) + } + + fn get_fn_name(fn_name: &str) -> Result { + Ok(CString::new(fn_name)?) + } + + fn get_flags(deterministic: bool) -> i32 { + let mut flags = ffi::SQLITE_UTF8; + if deterministic { + flags |= ffi::SQLITE_DETERMINISTIC; + } + flags + } + + fn process_sql_function_result(result: i32) -> Result<(), Error> { + if result == ffi::SQLITE_OK { + Ok(()) + } else { + let error_message = super::error_message(result); + Err(DatabaseError( + DatabaseErrorKind::__Unknown, + Box::new(error_message.to_string()), + )) + } + } +} + +impl Drop for RawConnection { + fn drop(&mut self) { + use std::thread::panicking; + + let close_result = unsafe { ffi::sqlite3_close(self.internal_connection.as_ptr()) }; + if close_result != ffi::SQLITE_OK { + let error_message = super::error_message(close_result); + if panicking() { + write!( + stderr(), + "Error closing SQLite connection: {}", + error_message + ) + .expect("Error writing to `stderr`"); + } else { + panic!("Error closing SQLite connection: {}", error_message); + } + } + } +} + +fn convert_to_string_and_free(err_msg: *const libc::c_char) -> String { + let msg = unsafe { + let bytes = CStr::from_ptr(err_msg).to_bytes(); + // sqlite is documented to return utf8 strings here + str::from_utf8_unchecked(bytes).into() + }; + unsafe { ffi::sqlite3_free(err_msg as *mut libc::c_void) }; + msg +} + +#[allow(warnings)] +extern "C" fn run_custom_function( + ctx: *mut ffi::sqlite3_context, + num_args: libc::c_int, + value_ptr: *mut *mut ffi::sqlite3_value, +) where + F: FnMut(&RawConnection, &[*mut ffi::sqlite3_value]) -> QueryResult + + Send + + 'static, +{ + static NULL_DATA_ERR: &str = "An unknown error occurred. sqlite3_user_data returned a null pointer. This should never happen."; + static NULL_CONN_ERR: &str = "An unknown error occurred. sqlite3_context_db_handle returned a null pointer. This should never happen."; + + unsafe { + let data_ptr = ffi::sqlite3_user_data(ctx); + let data_ptr = data_ptr as *mut F; + let f = match data_ptr.as_mut() { + Some(f) => f, + None => { + ffi::sqlite3_result_error( + ctx, + NULL_DATA_ERR.as_ptr() as *const _ as *const _, + NULL_DATA_ERR.len() as _, + ); + return; + } + }; + + let args = slice::from_raw_parts(value_ptr, num_args as _); + let conn = match NonNull::new(ffi::sqlite3_context_db_handle(ctx)) { + Some(conn) => RawConnection { + internal_connection: conn, + }, + None => { + ffi::sqlite3_result_error( + ctx, + NULL_DATA_ERR.as_ptr() as *const _ as *const _, + NULL_DATA_ERR.len() as _, + ); + return; + } + }; + match f(&conn, args) { + Ok(value) => value.result_of(ctx), + Err(e) => { + let msg = e.to_string(); + ffi::sqlite3_result_error(ctx, msg.as_ptr() as *const _, msg.len() as _); + } + } + + mem::forget(conn); + } +} + +// Need a custom option type here, because the std lib one does not have guarantees about the discriminate values +// See: https://github.com/rust-lang/rfcs/blob/master/text/2195-really-tagged-unions.md#opaque-tags +#[repr(u8)] +enum OptionalAggregator { + // Discriminant is 0 + None, + Some(A), +} + +#[allow(warnings)] +extern "C" fn run_aggregator_step_function( + ctx: *mut ffi::sqlite3_context, + num_args: libc::c_int, + value_ptr: *mut *mut ffi::sqlite3_value, +) where + A: SqliteAggregateFunction + 'static + Send, + Args: FromSqlRow, + Ret: ToSql, + Sqlite: HasSqlType, +{ + unsafe { + // This block of unsafe code makes the following assumptions: + // + // * sqlite3_aggregate_context allocates sizeof::> + // bytes of zeroed memory as documented here: + // https://www.sqlite.org/c3ref/aggregate_context.html + // A null pointer is returned for negative or zero sized types, + // which should be impossible in theory. We check that nevertheless + // + // * OptionalAggregator::None has a discriminant of 0 as specified by + // #[repr(u8)] + RFC 2195 + // + // * If all bytes are zero, the discriminant is also zero, so we can + // assume that we get OptionalAggregator::None in this case. This is + // not UB as we only access the discriminant here, so we do not try + // to read any other zeroed memory. After that we initialize our enum + // by writing a correct value at this location via ptr::write_unaligned + // + // * We use ptr::write_unaligned as we did not found any guarantees that + // the memory will have a correct alignment. + // (Note I(weiznich): would assume that it is aligned correctly, but we + // we cannot guarantee it, so better be safe than sorry) + let aggregate_context = ffi::sqlite3_aggregate_context( + ctx, + std::mem::size_of::>() as i32, + ); + let mut aggregate_context = NonNull::new(aggregate_context as *mut OptionalAggregator); + let aggregator = match aggregate_context.map(|a| &mut *a.as_ptr()) { + Some(&mut OptionalAggregator::Some(ref mut agg)) => agg, + Some(mut a_ptr @ &mut OptionalAggregator::None) => { + ptr::write_unaligned(a_ptr as *mut _, OptionalAggregator::Some(A::default())); + if let &mut OptionalAggregator::Some(ref mut agg) = a_ptr { + agg + } else { + unreachable!( + "We've written the aggregator above to that location, it must be there" + ) + } + } + None => { + null_aggregate_context_error(ctx); + return; + } + }; + + let mut f = |args: &[*mut ffi::sqlite3_value]| -> Result<(), Error> { + let args = build_sql_function_args::(args)?; + + Ok(aggregator.step(args)) + }; + + let args = slice::from_raw_parts(value_ptr, num_args as _); + match f(args) { + Err(e) => { + let msg = e.to_string(); + ffi::sqlite3_result_error(ctx, msg.as_ptr() as *const _, msg.len() as _); + } + _ => (), + }; + } +} + +extern "C" fn run_aggregator_final_function( + ctx: *mut ffi::sqlite3_context, +) where + A: SqliteAggregateFunction + 'static + Send, + Args: FromSqlRow, + Ret: ToSql, + Sqlite: HasSqlType, +{ + unsafe { + // Within the xFinal callback, it is customary to set nBytes to 0 so no pointless memory + // allocations occur, a null pointer is returned in this case + // See: https://www.sqlite.org/c3ref/aggregate_context.html + // + // For the reasoning about the safety of the OptionalAggregator handling + // see the comment in run_aggregator_step_function. + let aggregate_context = ffi::sqlite3_aggregate_context(ctx, 0); + let mut aggregate_context = NonNull::new(aggregate_context as *mut OptionalAggregator); + let aggregator = match aggregate_context { + Some(ref mut a) => match std::mem::replace(a.as_mut(), OptionalAggregator::None) { + OptionalAggregator::Some(agg) => Some(agg), + OptionalAggregator::None => unreachable!("We've written to the aggregator in the xStep callback. If xStep was never called, then ffi::sqlite_aggregate_context() would have returned a NULL pointer") + }, + None => None, + }; + + let result = A::finalize(aggregator); + + match process_sql_function_result::(result) { + Ok(value) => value.result_of(ctx), + Err(e) => { + let msg = e.to_string(); + ffi::sqlite3_result_error(ctx, msg.as_ptr() as *const _, msg.len() as _); + } + } + } +} + +unsafe fn null_aggregate_context_error(ctx: *mut ffi::sqlite3_context) { + static NULL_AG_CTX_ERR: &str = "An unknown error occurred. sqlite3_aggregate_context returned a null pointer. This should never happen."; + + ffi::sqlite3_result_error( + ctx, + NULL_AG_CTX_ERR.as_ptr() as *const _ as *const _, + NULL_AG_CTX_ERR.len() as _, + ); +} + +extern "C" fn destroy_boxed_fn(data: *mut libc::c_void) +where + F: FnMut(&RawConnection, &[*mut ffi::sqlite3_value]) -> QueryResult + + Send + + 'static, +{ + let ptr = data as *mut F; + unsafe { Box::from_raw(ptr) }; +} diff --git a/collector/benchmarks/diesel/diesel/src/sqlite/connection/serialized_value.rs b/collector/benchmarks/diesel/diesel/src/sqlite/connection/serialized_value.rs new file mode 100644 index 000000000..5c38f22be --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sqlite/connection/serialized_value.rs @@ -0,0 +1,113 @@ +extern crate libsqlite3_sys as ffi; + +use std::os::raw as libc; +use std::ptr::{self, NonNull}; + +use crate::sqlite::SqliteType; + +pub struct SerializedValue { + pub ty: SqliteType, + pub data: Option>, +} + +impl SerializedValue { + // We are always reading potentially misaligned pointers with + // `ptr::read_unaligned` + #[allow(clippy::cast_ptr_alignment)] + pub(crate) fn bind_to(self, stmt: NonNull, idx: libc::c_int) -> libc::c_int { + // This unsafe block assumes the following invariants: + // + // - `stmt` points to valid memory + // - If `self.ty` is anything other than `Binary` or `Text`, the appropriate + // number of bytes were written to `value` for an integer of the + // corresponding size. + unsafe { + match (self.ty, self.data) { + (_, None) => ffi::sqlite3_bind_null(stmt.as_ptr(), idx), + (SqliteType::Binary, Some(bytes)) => ffi::sqlite3_bind_blob( + stmt.as_ptr(), + idx, + bytes.as_ptr() as *const libc::c_void, + bytes.len() as libc::c_int, + ffi::SQLITE_TRANSIENT(), + ), + (SqliteType::Text, Some(bytes)) => ffi::sqlite3_bind_text( + stmt.as_ptr(), + idx, + bytes.as_ptr() as *const libc::c_char, + bytes.len() as libc::c_int, + ffi::SQLITE_TRANSIENT(), + ), + (SqliteType::Float, Some(bytes)) => { + let value = ptr::read_unaligned(bytes.as_ptr() as *const f32); + ffi::sqlite3_bind_double(stmt.as_ptr(), idx, libc::c_double::from(value)) + } + (SqliteType::Double, Some(bytes)) => { + let value = ptr::read_unaligned(bytes.as_ptr() as *const f64); + ffi::sqlite3_bind_double(stmt.as_ptr(), idx, value as libc::c_double) + } + (SqliteType::SmallInt, Some(bytes)) => { + let value = ptr::read_unaligned(bytes.as_ptr() as *const i16); + ffi::sqlite3_bind_int(stmt.as_ptr(), idx, libc::c_int::from(value)) + } + (SqliteType::Integer, Some(bytes)) => { + let value = ptr::read_unaligned(bytes.as_ptr() as *const i32); + ffi::sqlite3_bind_int(stmt.as_ptr(), idx, value as libc::c_int) + } + (SqliteType::Long, Some(bytes)) => { + let value = ptr::read_unaligned(bytes.as_ptr() as *const i64); + ffi::sqlite3_bind_int64(stmt.as_ptr(), idx, value) + } + } + } + } + + // We are always reading potentially misaligned pointers with + // `ptr::read_unaligned` + #[allow(clippy::cast_ptr_alignment)] + pub fn result_of(self, ctx: *mut ffi::sqlite3_context) { + // This unsafe block assumes the following invariants: + // + // - `ctx` points to valid memory + // - If `self.ty` is anything other than `Binary` or `Text`, the appropriate + // number of bytes were written to `self.data` for an integer of the + // corresponding size. + unsafe { + match (self.ty, self.data) { + (_, None) => ffi::sqlite3_result_null(ctx), + (SqliteType::Binary, Some(bytes)) => ffi::sqlite3_result_blob( + ctx, + bytes.as_ptr() as *const libc::c_void, + bytes.len() as libc::c_int, + ffi::SQLITE_TRANSIENT(), + ), + (SqliteType::Text, Some(bytes)) => ffi::sqlite3_result_text( + ctx, + bytes.as_ptr() as *const libc::c_char, + bytes.len() as libc::c_int, + ffi::SQLITE_TRANSIENT(), + ), + (SqliteType::Float, Some(bytes)) => { + let value = ptr::read_unaligned(bytes.as_ptr() as *const f32); + ffi::sqlite3_result_double(ctx, libc::c_double::from(value)) + } + (SqliteType::Double, Some(bytes)) => { + let value = ptr::read_unaligned(bytes.as_ptr() as *const f64); + ffi::sqlite3_result_double(ctx, value as libc::c_double) + } + (SqliteType::SmallInt, Some(bytes)) => { + let value = ptr::read_unaligned(bytes.as_ptr() as *const i16); + ffi::sqlite3_result_int(ctx, libc::c_int::from(value)) + } + (SqliteType::Integer, Some(bytes)) => { + let value = ptr::read_unaligned(bytes.as_ptr() as *const i32); + ffi::sqlite3_result_int(ctx, value as libc::c_int) + } + (SqliteType::Long, Some(bytes)) => { + let value = ptr::read_unaligned(bytes.as_ptr() as *const i64); + ffi::sqlite3_result_int64(ctx, value) + } + } + } + } +} diff --git a/collector/benchmarks/diesel/diesel/src/sqlite/connection/sqlite_value.rs b/collector/benchmarks/diesel/diesel/src/sqlite/connection/sqlite_value.rs new file mode 100644 index 000000000..60e898852 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sqlite/connection/sqlite_value.rs @@ -0,0 +1,189 @@ +extern crate libsqlite3_sys as ffi; + +use std::marker::PhantomData; +use std::os::raw as libc; +use std::ptr::NonNull; +use std::{slice, str}; + +use crate::row::*; +use crate::sqlite::{Sqlite, SqliteType}; + +/// Raw sqlite value as received from the database +/// +/// Use existing `FromSql` implementations to convert this into +/// rust values: +#[allow(missing_debug_implementations, missing_copy_implementations)] +pub struct SqliteValue<'a> { + value: NonNull, + p: PhantomData<&'a ()>, +} + +#[derive(Clone)] +pub struct SqliteRow<'a> { + stmt: NonNull, + next_col_index: libc::c_int, + p: PhantomData<&'a ()>, +} + +impl<'a> SqliteValue<'a> { + pub(crate) unsafe fn new(inner: *mut ffi::sqlite3_value) -> Option { + NonNull::new(inner) + .map(|value| SqliteValue { + value, + p: PhantomData, + }) + .and_then(|value| { + // We check here that the actual value represented by the inner + // `sqlite3_value` is not `NULL` (is sql meaning, not ptr meaning) + if value.is_null() { + None + } else { + Some(value) + } + }) + } + + pub(crate) fn read_text(&self) -> &str { + unsafe { + let ptr = ffi::sqlite3_value_text(self.value.as_ptr()); + let len = ffi::sqlite3_value_bytes(self.value.as_ptr()); + let bytes = slice::from_raw_parts(ptr as *const u8, len as usize); + // The string is guaranteed to be utf8 according to + // https://www.sqlite.org/c3ref/value_blob.html + str::from_utf8_unchecked(bytes) + } + } + + pub(crate) fn read_blob(&self) -> &[u8] { + unsafe { + let ptr = ffi::sqlite3_value_blob(self.value.as_ptr()); + let len = ffi::sqlite3_value_bytes(self.value.as_ptr()); + slice::from_raw_parts(ptr as *const u8, len as usize) + } + } + + pub(crate) fn read_integer(&self) -> i32 { + unsafe { ffi::sqlite3_value_int(self.value.as_ptr()) as i32 } + } + + pub(crate) fn read_long(&self) -> i64 { + unsafe { ffi::sqlite3_value_int64(self.value.as_ptr()) as i64 } + } + + pub(crate) fn read_double(&self) -> f64 { + unsafe { ffi::sqlite3_value_double(self.value.as_ptr()) as f64 } + } + + /// Get the type of the value as returned by sqlite + pub fn value_type(&self) -> Option { + let tpe = unsafe { ffi::sqlite3_value_type(self.value.as_ptr()) }; + match tpe { + ffi::SQLITE_TEXT => Some(SqliteType::Text), + ffi::SQLITE_INTEGER => Some(SqliteType::Long), + ffi::SQLITE_FLOAT => Some(SqliteType::Double), + ffi::SQLITE_BLOB => Some(SqliteType::Binary), + ffi::SQLITE_NULL => None, + _ => unreachable!("Sqlite docs saying this is not reachable"), + } + } + + pub(crate) fn is_null(&self) -> bool { + self.value_type().is_none() + } +} + +impl<'a> SqliteRow<'a> { + pub(crate) unsafe fn new(inner_statement: NonNull) -> Self { + SqliteRow { + stmt: inner_statement, + next_col_index: 0, + p: PhantomData, + } + } +} + +impl<'a> Row<'a, Sqlite> for SqliteRow<'a> { + type Field = SqliteField<'a>; + type InnerPartialRow = Self; + + fn field_count(&self) -> usize { + column_count(self.stmt) as usize + } + + fn get(&self, idx: I) -> Option + where + Self: RowIndex, + { + let idx = self.idx(idx)?; + Some(SqliteField { + stmt: self.stmt, + col_idx: idx as i32, + p: PhantomData, + }) + } + + fn partial_row(&self, range: std::ops::Range) -> PartialRow { + PartialRow::new(self, range) + } +} + +impl<'a> RowIndex for SqliteRow<'a> { + fn idx(&self, idx: usize) -> Option { + if idx < self.field_count() { + Some(idx) + } else { + None + } + } +} + +impl<'a, 'b> RowIndex<&'a str> for SqliteRow<'b> { + fn idx(&self, field_name: &'a str) -> Option { + (0..column_count(self.stmt)) + .find(|idx| column_name(self.stmt, *idx) == Some(field_name)) + .map(|a| a as usize) + } +} + +pub struct SqliteField<'a> { + stmt: NonNull, + col_idx: i32, + p: PhantomData<&'a ()>, +} + +impl<'a> Field<'a, Sqlite> for SqliteField<'a> { + fn field_name(&self) -> Option<&'a str> { + column_name(self.stmt, self.col_idx) + } + + fn is_null(&self) -> bool { + self.value().is_none() + } + + fn value(&self) -> Option> { + unsafe { + let ptr = ffi::sqlite3_column_value(self.stmt.as_ptr(), self.col_idx); + SqliteValue::new(ptr) + } + } +} + +fn column_name<'a>(stmt: NonNull, field_number: i32) -> Option<&'a str> { + unsafe { + let ptr = ffi::sqlite3_column_name(stmt.as_ptr(), field_number); + if ptr.is_null() { + None + } else { + Some(std::ffi::CStr::from_ptr(ptr).to_str().expect( + "The Sqlite documentation states that this is UTF8. \ + If you see this error message something has gone \ + horribliy wrong. Please open an issue at the \ + diesel repository.", + )) + } + } +} + +fn column_count(stmt: NonNull) -> i32 { + unsafe { ffi::sqlite3_column_count(stmt.as_ptr()) } +} diff --git a/collector/benchmarks/diesel/diesel/src/sqlite/connection/statement_iterator.rs b/collector/benchmarks/diesel/diesel/src/sqlite/connection/statement_iterator.rs new file mode 100644 index 000000000..91195631f --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sqlite/connection/statement_iterator.rs @@ -0,0 +1,36 @@ +use std::marker::PhantomData; + +use super::stmt::StatementUse; +use crate::deserialize::FromSqlRow; +use crate::result::Error::DeserializationError; +use crate::result::QueryResult; +use crate::sqlite::Sqlite; + +pub struct StatementIterator<'a, ST, T> { + stmt: StatementUse<'a>, + _marker: PhantomData<(ST, T)>, +} + +impl<'a, ST, T> StatementIterator<'a, ST, T> { + pub fn new(stmt: StatementUse<'a>) -> Self { + StatementIterator { + stmt: stmt, + _marker: PhantomData, + } + } +} + +impl<'a, ST, T> Iterator for StatementIterator<'a, ST, T> +where + T: FromSqlRow, +{ + type Item = QueryResult; + + fn next(&mut self) -> Option { + let row = match self.stmt.step() { + Ok(row) => row, + Err(e) => return Some(Err(e)), + }; + row.map(|row| T::build_from_row(&row).map_err(DeserializationError)) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/sqlite/connection/stmt.rs b/collector/benchmarks/diesel/diesel/src/sqlite/connection/stmt.rs new file mode 100644 index 000000000..3ca28c8ec --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sqlite/connection/stmt.rs @@ -0,0 +1,154 @@ +extern crate libsqlite3_sys as ffi; + +use std::ffi::{CStr, CString}; +use std::io::{stderr, Write}; +use std::os::raw as libc; +use std::ptr::{self, NonNull}; + +use super::raw::RawConnection; +use super::serialized_value::SerializedValue; +use super::sqlite_value::SqliteRow; +use crate::result::Error::DatabaseError; +use crate::result::*; +use crate::sqlite::SqliteType; + +pub struct Statement { + inner_statement: NonNull, + bind_index: libc::c_int, +} + +impl Statement { + pub fn prepare(raw_connection: &RawConnection, sql: &str) -> QueryResult { + let mut stmt = ptr::null_mut(); + let mut unused_portion = ptr::null(); + let prepare_result = unsafe { + ffi::sqlite3_prepare_v2( + raw_connection.internal_connection.as_ptr(), + CString::new(sql)?.as_ptr(), + sql.len() as libc::c_int, + &mut stmt, + &mut unused_portion, + ) + }; + + ensure_sqlite_ok(prepare_result, raw_connection.internal_connection.as_ptr()).map(|_| { + Statement { + inner_statement: unsafe { NonNull::new_unchecked(stmt) }, + bind_index: 0, + } + }) + } + + fn run(&mut self) -> QueryResult<()> { + self.step().map(|_| ()) + } + + pub fn bind(&mut self, tpe: SqliteType, value: Option>) -> QueryResult<()> { + self.bind_index += 1; + let value = SerializedValue { + ty: tpe, + data: value, + }; + let result = value.bind_to(self.inner_statement, self.bind_index); + + ensure_sqlite_ok(result, self.raw_connection()) + } + + fn step(&mut self) -> QueryResult> { + unsafe { + match ffi::sqlite3_step(self.inner_statement.as_ptr()) { + ffi::SQLITE_DONE => Ok(None), + ffi::SQLITE_ROW => Ok(Some(SqliteRow::new(self.inner_statement))), + _ => Err(last_error(self.raw_connection())), + } + } + } + + fn reset(&mut self) { + self.bind_index = 0; + unsafe { ffi::sqlite3_reset(self.inner_statement.as_ptr()) }; + } + + fn raw_connection(&self) -> *mut ffi::sqlite3 { + unsafe { ffi::sqlite3_db_handle(self.inner_statement.as_ptr()) } + } +} + +fn ensure_sqlite_ok(code: libc::c_int, raw_connection: *mut ffi::sqlite3) -> QueryResult<()> { + if code == ffi::SQLITE_OK { + Ok(()) + } else { + Err(last_error(raw_connection)) + } +} + +fn last_error(raw_connection: *mut ffi::sqlite3) -> Error { + let error_message = last_error_message(raw_connection); + let error_information = Box::new(error_message); + let error_kind = match last_error_code(raw_connection) { + ffi::SQLITE_CONSTRAINT_UNIQUE | ffi::SQLITE_CONSTRAINT_PRIMARYKEY => { + DatabaseErrorKind::UniqueViolation + } + ffi::SQLITE_CONSTRAINT_FOREIGNKEY => DatabaseErrorKind::ForeignKeyViolation, + ffi::SQLITE_CONSTRAINT_NOTNULL => DatabaseErrorKind::NotNullViolation, + ffi::SQLITE_CONSTRAINT_CHECK => DatabaseErrorKind::CheckViolation, + _ => DatabaseErrorKind::__Unknown, + }; + DatabaseError(error_kind, error_information) +} + +fn last_error_message(conn: *mut ffi::sqlite3) -> String { + let c_str = unsafe { CStr::from_ptr(ffi::sqlite3_errmsg(conn)) }; + c_str.to_string_lossy().into_owned() +} + +fn last_error_code(conn: *mut ffi::sqlite3) -> libc::c_int { + unsafe { ffi::sqlite3_extended_errcode(conn) } +} + +impl Drop for Statement { + fn drop(&mut self) { + use std::thread::panicking; + + let raw_connection = self.raw_connection(); + let finalize_result = unsafe { ffi::sqlite3_finalize(self.inner_statement.as_ptr()) }; + if let Err(e) = ensure_sqlite_ok(finalize_result, raw_connection) { + if panicking() { + write!( + stderr(), + "Error finalizing SQLite prepared statement: {:?}", + e + ) + .expect("Error writing to `stderr`"); + } else { + panic!("Error finalizing SQLite prepared statement: {:?}", e); + } + } + } +} + +pub struct StatementUse<'a> { + statement: &'a mut Statement, +} + +impl<'a> StatementUse<'a> { + pub fn new(statement: &'a mut Statement) -> Self { + StatementUse { + statement: statement, + } + } + + pub fn run(&mut self) -> QueryResult<()> { + self.statement.run() + } + + pub fn step(&mut self) -> QueryResult> { + self.statement.step() + } +} + +impl<'a> Drop for StatementUse<'a> { + fn drop(&mut self) { + self.statement.reset(); + } +} diff --git a/collector/benchmarks/diesel/diesel/src/sqlite/mod.rs b/collector/benchmarks/diesel/diesel/src/sqlite/mod.rs new file mode 100644 index 000000000..931785b7e --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sqlite/mod.rs @@ -0,0 +1,35 @@ +//! Provides types and functions related to working with SQLite +//! +//! Much of this module is re-exported from database agnostic locations. +//! However, if you are writing code specifically to extend Diesel on +//! SQLite, you may need to work with this module directly. + +mod backend; +mod connection; +mod types; + +pub mod query_builder; + +pub use self::backend::{Sqlite, SqliteType}; +pub use self::connection::SqliteConnection; +pub use self::connection::SqliteValue; +pub use self::query_builder::SqliteQueryBuilder; + +/// Trait for the implementation of a SQLite aggregate function +/// +/// This trait is to be used in conjunction with the `sql_function!` +/// macro for defining a custom SQLite aggregate function. See +/// the documentation [there](../prelude/macro.sql_function.html) for details. +pub trait SqliteAggregateFunction: Default { + /// The result type of the SQLite aggregate function + type Output; + + /// The `step()` method is called once for every record of the query + fn step(&mut self, args: Args); + + /// After the last row has been processed, the `finalize()` method is + /// called to compute the result of the aggregate function. If no rows + /// were processed `aggregator` will be `None` and `finalize()` can be + /// used to specify a default result + fn finalize(aggregator: Option) -> Self::Output; +} diff --git a/collector/benchmarks/diesel/diesel/src/sqlite/query_builder/limit_offset.rs b/collector/benchmarks/diesel/diesel/src/sqlite/query_builder/limit_offset.rs new file mode 100644 index 000000000..8d713a3ec --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sqlite/query_builder/limit_offset.rs @@ -0,0 +1,126 @@ +use crate::query_builder::limit_clause::{LimitClause, NoLimitClause}; +use crate::query_builder::limit_offset_clause::{BoxedLimitOffsetClause, LimitOffsetClause}; +use crate::query_builder::offset_clause::{NoOffsetClause, OffsetClause}; +use crate::query_builder::{AstPass, IntoBoxedClause, QueryFragment}; +use crate::result::QueryResult; +use crate::sqlite::Sqlite; + +impl QueryFragment for LimitOffsetClause { + fn walk_ast(&self, _out: AstPass) -> QueryResult<()> { + Ok(()) + } +} + +impl QueryFragment for LimitOffsetClause, NoOffsetClause> +where + LimitClause: QueryFragment, +{ + fn walk_ast(&self, out: AstPass) -> QueryResult<()> { + self.limit_clause.walk_ast(out)?; + Ok(()) + } +} + +impl QueryFragment for LimitOffsetClause> +where + OffsetClause: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + // Sqlite requires a limit clause in front of any offset clause + // using `LIMIT -1` is the same as not having any limit clause + // https://sqlite.org/lang_select.html + out.push_sql(" LIMIT -1 "); + self.offset_clause.walk_ast(out)?; + Ok(()) + } +} + +impl QueryFragment for LimitOffsetClause, OffsetClause> +where + LimitClause: QueryFragment, + OffsetClause: QueryFragment, +{ + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + self.limit_clause.walk_ast(out.reborrow())?; + self.offset_clause.walk_ast(out.reborrow())?; + Ok(()) + } +} + +impl<'a> QueryFragment for BoxedLimitOffsetClause<'a, Sqlite> { + fn walk_ast(&self, mut out: AstPass) -> QueryResult<()> { + match (self.limit.as_ref(), self.offset.as_ref()) { + (Some(limit), Some(offset)) => { + limit.walk_ast(out.reborrow())?; + offset.walk_ast(out.reborrow())?; + } + (Some(limit), None) => { + limit.walk_ast(out.reborrow())?; + } + (None, Some(offset)) => { + // See the `QueryFragment` implementation for `LimitOffsetClause` for details. + out.push_sql(" LIMIT -1 "); + offset.walk_ast(out.reborrow())?; + } + (None, None) => {} + } + Ok(()) + } +} + +// Have explicit impls here because we need to set `Some`/`None` for the clauses +// correspondingly, otherwise we cannot match on it in the `QueryFragment` impl +// above +impl<'a> IntoBoxedClause<'a, Sqlite> for LimitOffsetClause { + type BoxedClause = BoxedLimitOffsetClause<'a, Sqlite>; + + fn into_boxed(self) -> Self::BoxedClause { + BoxedLimitOffsetClause { + limit: None, + offset: None, + } + } +} + +impl<'a, L> IntoBoxedClause<'a, Sqlite> for LimitOffsetClause, NoOffsetClause> +where + L: QueryFragment + Send + 'a, +{ + type BoxedClause = BoxedLimitOffsetClause<'a, Sqlite>; + + fn into_boxed(self) -> Self::BoxedClause { + BoxedLimitOffsetClause { + limit: Some(Box::new(self.limit_clause)), + offset: None, + } + } +} + +impl<'a, O> IntoBoxedClause<'a, Sqlite> for LimitOffsetClause> +where + O: QueryFragment + Send + 'a, +{ + type BoxedClause = BoxedLimitOffsetClause<'a, Sqlite>; + + fn into_boxed(self) -> Self::BoxedClause { + BoxedLimitOffsetClause { + limit: None, + offset: Some(Box::new(self.offset_clause)), + } + } +} + +impl<'a, L, O> IntoBoxedClause<'a, Sqlite> for LimitOffsetClause, OffsetClause> +where + L: QueryFragment + Send + 'a, + O: QueryFragment + Send + 'a, +{ + type BoxedClause = BoxedLimitOffsetClause<'a, Sqlite>; + + fn into_boxed(self) -> Self::BoxedClause { + BoxedLimitOffsetClause { + limit: Some(Box::new(self.limit_clause)), + offset: Some(Box::new(self.offset_clause)), + } + } +} diff --git a/collector/benchmarks/diesel/diesel/src/sqlite/query_builder/mod.rs b/collector/benchmarks/diesel/diesel/src/sqlite/query_builder/mod.rs new file mode 100644 index 000000000..546c96344 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sqlite/query_builder/mod.rs @@ -0,0 +1,42 @@ +//! The SQLite query builder + +use super::backend::Sqlite; +use crate::query_builder::QueryBuilder; +use crate::result::QueryResult; + +mod limit_offset; + +/// Constructs SQL queries for use with the SQLite backend +#[allow(missing_debug_implementations)] +#[derive(Default)] +pub struct SqliteQueryBuilder { + sql: String, +} + +impl SqliteQueryBuilder { + /// Construct a new query builder with an empty query + pub fn new() -> Self { + SqliteQueryBuilder::default() + } +} + +impl QueryBuilder for SqliteQueryBuilder { + fn push_sql(&mut self, sql: &str) { + self.sql.push_str(sql); + } + + fn push_identifier(&mut self, identifier: &str) -> QueryResult<()> { + self.push_sql("`"); + self.push_sql(&identifier.replace("`", "``")); + self.push_sql("`"); + Ok(()) + } + + fn push_bind_param(&mut self) { + self.push_sql("?"); + } + + fn finish(self) -> String { + self.sql + } +} diff --git a/collector/benchmarks/diesel/diesel/src/sqlite/types/date_and_time/chrono.rs b/collector/benchmarks/diesel/diesel/src/sqlite/types/date_and_time/chrono.rs new file mode 100644 index 000000000..7d186c6f6 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/sqlite/types/date_and_time/chrono.rs @@ -0,0 +1,378 @@ +extern crate chrono; + +use self::chrono::{NaiveDate, NaiveDateTime, NaiveTime}; +use std::io::Write; + +use crate::backend; +use crate::deserialize::{self, FromSql}; +use crate::serialize::{self, Output, ToSql}; +use crate::sql_types::{Date, Text, Time, Timestamp}; +use crate::sqlite::Sqlite; + +const SQLITE_DATE_FORMAT: &str = "%F"; + +impl FromSql for NaiveDate { + fn from_sql(value: backend::RawValue) -> deserialize::Result { + let text_ptr = <*const str as FromSql>::from_sql(value)?; + let text = unsafe { &*text_ptr }; + Self::parse_from_str(text, SQLITE_DATE_FORMAT).map_err(Into::into) + } +} + +impl ToSql for NaiveDate { + fn to_sql(&self, out: &mut Output) -> serialize::Result { + let s = self.format(SQLITE_DATE_FORMAT).to_string(); + ToSql::::to_sql(&s, out) + } +} + +impl FromSql for NaiveTime { + fn from_sql(value: backend::RawValue) -> deserialize::Result { + let text_ptr = <*const str as FromSql>::from_sql(value)?; + let text = unsafe { &*text_ptr }; + let valid_time_formats = &[ + // Most likely + "%T%.f", // All other valid formats in order of documentation + "%R", "%RZ", "%T%.fZ", "%R%:z", "%T%.f%:z", + ]; + + for format in valid_time_formats { + if let Ok(time) = Self::parse_from_str(text, format) { + return Ok(time); + } + } + + Err(format!("Invalid time {}", text).into()) + } +} + +impl ToSql for NaiveTime { + fn to_sql(&self, out: &mut Output) -> serialize::Result { + let s = self.format("%T%.f").to_string(); + ToSql::::to_sql(&s, out) + } +} + +impl FromSql for NaiveDateTime { + fn from_sql(value: backend::RawValue) -> deserialize::Result { + let text_ptr = <*const str as FromSql>::from_sql(value)?; + let text = unsafe { &*text_ptr }; + + let sqlite_datetime_formats = &[ + // Most likely format + "%F %T%.f", + // Other formats in order of appearance in docs + "%F %R", + "%F %RZ", + "%F %R%:z", + "%F %T%.fZ", + "%F %T%.f%:z", + "%FT%R", + "%FT%RZ", + "%FT%R%:z", + "%FT%T%.f", + "%FT%T%.fZ", + "%FT%T%.f%:z", + ]; + + for format in sqlite_datetime_formats { + if let Ok(dt) = Self::parse_from_str(text, format) { + return Ok(dt); + } + } + + if let Ok(julian_days) = text.parse::() { + let epoch_in_julian_days = 2_440_587.5; + let seconds_in_day = 86400.0; + let timestamp = (julian_days - epoch_in_julian_days) * seconds_in_day; + let seconds = timestamp as i64; + let nanos = (timestamp.fract() * 1E9) as u32; + if let Some(timestamp) = Self::from_timestamp_opt(seconds, nanos) { + return Ok(timestamp); + } + } + + Err(format!("Invalid datetime {}", text).into()) + } +} + +impl ToSql for NaiveDateTime { + fn to_sql(&self, out: &mut Output) -> serialize::Result { + let s = self.format("%F %T%.f").to_string(); + ToSql::::to_sql(&s, out) + } +} + +#[cfg(test)] +mod tests { + extern crate chrono; + extern crate dotenv; + + use self::chrono::{Duration, NaiveDate, NaiveDateTime, NaiveTime, Timelike, Utc}; + use self::dotenv::dotenv; + + use crate::dsl::{now, sql}; + use crate::prelude::*; + use crate::select; + use crate::sql_types::{Text, Time, Timestamp}; + + sql_function!(fn datetime(x: Text) -> Timestamp); + sql_function!(fn time(x: Text) -> Time); + sql_function!(fn date(x: Text) -> Date); + + fn connection() -> SqliteConnection { + dotenv().ok(); + + let connection_url = ::std::env::var("SQLITE_DATABASE_URL") + .or_else(|_| ::std::env::var("DATABASE_URL")) + .expect("DATABASE_URL must be set in order to run tests"); + SqliteConnection::establish(&connection_url).unwrap() + } + + #[test] + fn unix_epoch_encodes_correctly() { + let connection = connection(); + let time = NaiveDate::from_ymd(1970, 1, 1).and_hms(0, 0, 0); + let query = select(datetime("1970-01-01 00:00:00.000000").eq(time)); + assert_eq!(Ok(true), query.get_result(&connection)); + } + + #[test] + fn unix_epoch_decodes_correctly_in_all_possible_formats() { + let connection = connection(); + let time = NaiveDate::from_ymd(1970, 1, 1).and_hms(0, 0, 0); + let valid_epoch_formats = vec![ + "1970-01-01 00:00", + "1970-01-01 00:00:00", + "1970-01-01 00:00:00.000", + "1970-01-01 00:00:00.000000", + "1970-01-01T00:00", + "1970-01-01T00:00:00", + "1970-01-01T00:00:00.000", + "1970-01-01T00:00:00.000000", + "1970-01-01 00:00Z", + "1970-01-01 00:00:00Z", + "1970-01-01 00:00:00.000Z", + "1970-01-01 00:00:00.000000Z", + "1970-01-01T00:00Z", + "1970-01-01T00:00:00Z", + "1970-01-01T00:00:00.000Z", + "1970-01-01T00:00:00.000000Z", + "1970-01-01 00:00+00:00", + "1970-01-01 00:00:00+00:00", + "1970-01-01 00:00:00.000+00:00", + "1970-01-01 00:00:00.000000+00:00", + "1970-01-01T00:00+00:00", + "1970-01-01T00:00:00+00:00", + "1970-01-01T00:00:00.000+00:00", + "1970-01-01T00:00:00.000000+00:00", + "1970-01-01 00:00+01:00", + "1970-01-01 00:00:00+01:00", + "1970-01-01 00:00:00.000+01:00", + "1970-01-01 00:00:00.000000+01:00", + "1970-01-01T00:00+01:00", + "1970-01-01T00:00:00+01:00", + "1970-01-01T00:00:00.000+01:00", + "1970-01-01T00:00:00.000000+01:00", + "1970-01-01T00:00-01:00", + "1970-01-01T00:00:00-01:00", + "1970-01-01T00:00:00.000-01:00", + "1970-01-01T00:00:00.000000-01:00", + "1970-01-01T00:00-01:00", + "1970-01-01T00:00:00-01:00", + "1970-01-01T00:00:00.000-01:00", + "1970-01-01T00:00:00.000000-01:00", + "2440587.5", + ]; + + for s in valid_epoch_formats { + let epoch_from_sql = + select(sql::(&format!("'{}'", s))).get_result(&connection); + assert_eq!(Ok(time), epoch_from_sql, "format {} failed", s); + } + } + + #[test] + fn times_relative_to_now_encode_correctly() { + let connection = connection(); + let time = Utc::now().naive_utc() + Duration::seconds(60); + let query = select(now.lt(time)); + assert_eq!(Ok(true), query.get_result(&connection)); + + let time = Utc::now().naive_utc() - Duration::seconds(600); + let query = select(now.gt(time)); + assert_eq!(Ok(true), query.get_result(&connection)); + } + + #[test] + fn times_of_day_encode_correctly() { + let connection = connection(); + + let midnight = NaiveTime::from_hms(0, 0, 0); + let query = select(time("00:00:00.000000").eq(midnight)); + assert!(query.get_result::(&connection).unwrap()); + + let noon = NaiveTime::from_hms(12, 0, 0); + let query = select(time("12:00:00.000000").eq(noon)); + assert!(query.get_result::(&connection).unwrap()); + + let roughly_half_past_eleven = NaiveTime::from_hms_micro(23, 37, 4, 2200); + let query = select(sql::

,)+ + { + type Table = Tab; + + fn walk_ast<__DB: Backend>(&self, mut out: AstPass<__DB>) -> QueryResult<()> { + $( + if $idx != 0 { + out.push_sql(", "); + } + self.$idx.walk_ast(out.reborrow())?; + )+ + Ok(()) + } + } + + impl<$($T: QueryId),+> QueryId for ($($T,)+) { + type QueryId = ($($T::QueryId,)+); + + const HAS_STATIC_QUERY_ID: bool = $($T::HAS_STATIC_QUERY_ID &&)+ true; + } + + const _: () = { + #[derive(ValidGrouping)] + #[diesel(foreign_derive)] + struct TupleWrapper<$($T,)*>(($($T,)*)); + }; + + impl_valid_grouping_for_tuple_of_columns!($($T,)*); + + impl<$($T,)+ Tab> UndecoratedInsertRecord for ($($T,)+) + where + $($T: UndecoratedInsertRecord,)+ + { + } + + impl<$($T,)+ __DB> CanInsertInSingleQuery<__DB> for ($($T,)+) + where + __DB: Backend, + $($T: CanInsertInSingleQuery<__DB>,)+ + { + fn rows_to_insert(&self) -> Option { + $(debug_assert_eq!(self.$idx.rows_to_insert(), Some(1));)+ + Some(1) + } + } + + impl<$($T,)+ $($ST,)+ Tab> Insertable for ($($T,)+) + where + $($T: Insertable>,)+ + { + type Values = ValuesClause<($($ST,)+), Tab>; + + fn values(self) -> Self::Values { + ValuesClause::new(($(self.$idx.values().values,)+)) + } + } + + impl<'a, $($T,)+ Tab> Insertable for &'a ($($T,)+) + where + ($(&'a $T,)+): Insertable, + { + type Values = <($(&'a $T,)+) as Insertable>::Values; + + fn values(self) -> Self::Values { + ($(&self.$idx,)+).values() + } + } + + #[allow(unused_assignments)] + impl<$($T,)+ Tab, __DB> InsertValues for ($($T,)+) + where + Tab: Table, + __DB: Backend, + $($T: InsertValues,)+ + { + fn column_names(&self, mut out: AstPass<__DB>) -> QueryResult<()> { + let mut needs_comma = false; + $( + let noop_element = self.$idx.is_noop()?; + if !noop_element { + if needs_comma { + out.push_sql(", "); + } + self.$idx.column_names(out.reborrow())?; + needs_comma = true; + } + )+ + Ok(()) + } + } + + impl<$($T,)+ QS> SelectableExpression for ($($T,)+) where + $($T: SelectableExpression,)+ + ($($T,)+): AppearsOnTable, + { + } + + impl<$($T,)+ QS> AppearsOnTable for ($($T,)+) where + $($T: AppearsOnTable,)+ + ($($T,)+): Expression, + { + } + + impl AsChangeset for ($($T,)+) where + $($T: AsChangeset,)+ + Target: QuerySource, + { + type Target = Target; + type Changeset = ($($T::Changeset,)+); + + fn as_changeset(self) -> Self::Changeset { + ($(self.$idx.as_changeset(),)+) + } + } + + impl<$($T,)+ Parent> BelongsTo for ($($T,)+) where + A: BelongsTo, + { + type ForeignKey = A::ForeignKey; + type ForeignKeyColumn = A::ForeignKeyColumn; + + fn foreign_key(&self) -> Option<&Self::ForeignKey> { + self.0.foreign_key() + } + + fn foreign_key_column() -> Self::ForeignKeyColumn { + A::foreign_key_column() + } + } + + impl<$($T,)+ Next> TupleAppend for ($($T,)+) { + type Output = ($($T,)+ Next); + + #[allow(non_snake_case)] + fn tuple_append(self, next: Next) -> Self::Output { + let ($($T,)+) = self; + ($($T,)+ next) + } + } + + impl<$($T,)+ ST> AsExpressionList for ($($T,)+) where + $($T: AsExpression,)+ + ST: SqlType + TypedExpressionType, + { + type Expression = ($($T::Expression,)+); + + fn as_expression_list(self) -> Self::Expression { + ($(self.$idx.as_expression(),)+) + } + } + + impl_sql_type!($($T,)*); + + impl<$($T,)* __DB, $($ST,)*> Queryable<($($ST,)*), __DB> for ($($T,)*) + where __DB: Backend, + Self: FromStaticSqlRow<($($ST,)*), __DB>, + { + type Row = Self; + + fn build(row: Self::Row) -> Self { + row + } + } + + impl<__T, $($ST,)* __DB> FromStaticSqlRow, __DB> for Option<__T> where + __DB: Backend, + ($($ST,)*): SqlType, + __T: FromSqlRow<($($ST,)*), __DB>, + { + + #[allow(non_snake_case, unused_variables, unused_mut)] + fn build_from_row<'a>(row: &impl Row<'a, __DB>) + -> deserialize::Result + { + match <__T as FromSqlRow<($($ST,)*), __DB>>::build_from_row(row) { + Ok(v) => Ok(Some(v)), + Err(e) if e.is::() => Ok(None), + Err(e) => Err(e) + } + } + } + + impl<__T, __DB, $($ST,)*> Queryable, __DB> for Option<__T> + where __DB: Backend, + Self: FromStaticSqlRow, __DB>, + ($($ST,)*): SqlType, + { + type Row = Self; + + fn build(row: Self::Row) -> Self { + row + } + } + + impl<$($T,)*> TupleSize for ($($T,)*) + where $($T: TupleSize,)* + { + const SIZE: usize = $($T::SIZE +)* 0; + } + + impl<$($T,)*> TupleSize for Nullable<($($T,)*)> + where $($T: TupleSize,)* + ($($T,)*): SqlType, + { + const SIZE: usize = $($T::SIZE +)* 0; + } + + impl<$($T,)* __DB> QueryMetadata<($($T,)*)> for __DB + where __DB: Backend, + $(__DB: QueryMetadata<$T>,)* + { + fn row_metadata(lookup: &Self::MetadataLookup, row: &mut Vec>) { + $( + <__DB as QueryMetadata<$T>>::row_metadata(lookup, row); + )* + } + } + + impl<$($T,)* __DB> QueryMetadata> for __DB + where __DB: Backend, + $(__DB: QueryMetadata<$T>,)* + { + fn row_metadata(lookup: &Self::MetadataLookup, row: &mut Vec>) { + $( + <__DB as QueryMetadata<$T>>::row_metadata(lookup, row); + )* + } + } + + impl<$($T,)* __DB> deserialize::QueryableByName< __DB> for ($($T,)*) + where __DB: Backend, + $($T: deserialize::QueryableByName<__DB>,)* + { + fn build<'a>(row: &impl NamedRow<'a, __DB>) -> deserialize::Result { + Ok(($( + <$T as deserialize::QueryableByName<__DB>>::build(row)?, + )*)) + } + } + + )+ + } +} + +macro_rules! impl_from_sql_row { + (($T1: ident,), ($ST1: ident,)) => { + impl<$T1, $ST1, __DB> crate::deserialize::FromStaticSqlRow<($ST1,), __DB> for ($T1,) where + __DB: Backend, + $T1: FromSqlRow<$ST1, __DB>, + { + + #[allow(non_snake_case, unused_variables, unused_mut)] + fn build_from_row<'a>(row: &impl Row<'a, __DB>) + -> deserialize::Result + { + Ok(($T1::build_from_row(row)?,)) + } + } + }; + (($T1: ident, $($T: ident,)*), ($ST1: ident, $($ST: ident,)*)) => { + impl<$T1, $($T,)* $($ST,)* __DB> FromSqlRow<($($ST,)* crate::sql_types::Untyped), __DB> for ($($T,)* $T1) + where __DB: Backend, + $T1: FromSqlRow, + $( + $T: FromSqlRow<$ST, __DB> + StaticallySizedRow<$ST, __DB>, + )* + { + #[allow(non_snake_case, unused_variables, unused_mut)] + fn build_from_row<'a>(full_row: &impl Row<'a, __DB>) + -> deserialize::Result + { + let field_count = full_row.field_count(); + + let mut static_field_count = 0; + $( + let row = full_row.partial_row(static_field_count..static_field_count + $T::FIELD_COUNT); + static_field_count += $T::FIELD_COUNT; + let $T = $T::build_from_row(&row)?; + )* + + let row = full_row.partial_row(static_field_count..field_count); + + Ok(($($T,)* $T1::build_from_row(&row)?,)) + } + } + + impl<$T1, $ST1, $($T,)* $($ST,)* __DB> FromStaticSqlRow<($($ST,)* $ST1,), __DB> for ($($T,)* $T1,) where + __DB: Backend, + $T1: FromSqlRow<$ST1, __DB>, + $( + $T: FromSqlRow<$ST, __DB> + StaticallySizedRow<$ST, __DB>, + )* + + { + + #[allow(non_snake_case, unused_variables, unused_mut)] + fn build_from_row<'a>(full_row: &impl Row<'a, __DB>) + -> deserialize::Result + { + let field_count = full_row.field_count(); + + let mut static_field_count = 0; + $( + let row = full_row.partial_row(static_field_count..static_field_count + $T::FIELD_COUNT); + static_field_count += $T::FIELD_COUNT; + let $T = $T::build_from_row(&row)?; + )* + + let row = full_row.partial_row(static_field_count..field_count); + + Ok(($($T,)* $T1::build_from_row(&row)?,)) + } + } + } +} + +macro_rules! impl_valid_grouping_for_tuple_of_columns { + ( + @build + start_ts = [$($ST: ident,)*], + ts = [$T1: ident,], + bounds = [$($bounds: tt)*], + is_aggregate = [$($is_aggregate: tt)*], + ) => { + impl<$($ST,)* Col> IsContainedInGroupByfor ($($ST,)*) + where Col: Column, + $($ST: IsContainedInGroupBy,)* + $($bounds)* + <$T1 as IsContainedInGroupBy>::Output: is_contained_in_group_by::IsAny<$($is_aggregate)*>, + { + type Output = <<$T1 as IsContainedInGroupBy>::Output as is_contained_in_group_by::IsAny<$($is_aggregate)*>>::Output; + } + }; + ( + @build + start_ts = [$($ST: ident,)*], + ts = [$T1: ident, $($T: ident,)+], + bounds = [$($bounds: tt)*], + is_aggregate = [$($is_aggregate: tt)*], + ) => { + impl_valid_grouping_for_tuple_of_columns! { + @build + start_ts = [$($ST,)*], + ts = [$($T,)*], + bounds = [ + $($bounds)* + <$T1 as IsContainedInGroupBy>::Output: is_contained_in_group_by::IsAny<$($is_aggregate)*>, + ], + is_aggregate = [ + <<$T1 as IsContainedInGroupBy>::Output as is_contained_in_group_by::IsAny<$($is_aggregate)*>>::Output + ], + } + }; + ($T1: ident, $($T: ident,)+) => { + impl_valid_grouping_for_tuple_of_columns! { + @build + start_ts = [$T1, $($T,)*], + ts = [$($T,)*], + bounds = [], + is_aggregate = [<$T1 as IsContainedInGroupBy>::Output], + } + }; + ($T1: ident,) => { + impl<$T1, Col> IsContainedInGroupByfor ($T1,) + where Col: Column, + $T1: IsContainedInGroupBy+ { + type Output = <$T1 as IsContainedInGroupBy>::Output; + } + }; +} + +macro_rules! impl_sql_type { + ( + @build + start_ts = [$($ST: ident,)*], + ts = [$T1: ident,], + bounds = [$($bounds: tt)*], + is_null = [$($is_null: tt)*], + )=> { + impl<$($ST,)*> SqlType for ($($ST,)*) + where + $($ST: SqlType,)* + $($bounds)* + $T1::IsNull: OneIsNullable<$($is_null)*>, + { + type IsNull = <$T1::IsNull as OneIsNullable<$($is_null)*>>::Out; + } + + }; + ( + @build + start_ts = [$($ST: ident,)*], + ts = [$T1: ident, $($T: ident,)+], + bounds = [$($bounds: tt)*], + is_null = [$($is_null: tt)*], + )=> { + impl_sql_type!{ + @build + start_ts = [$($ST,)*], + ts = [$($T,)*], + bounds = [$($bounds)* $T1::IsNull: OneIsNullable<$($is_null)*>,], + is_null = [<$T1::IsNull as OneIsNullable<$($is_null)*>>::Out], + } + }; + ($T1: ident, $($T: ident,)+) => { + impl_sql_type!{ + @build + start_ts = [$T1, $($T,)*], + ts = [$($T,)*], + bounds = [], + is_null = [$T1::IsNull], + } + }; + ($T1: ident,) => { + impl<$T1> SqlType for ($T1,) + where $T1: SqlType, + { + type IsNull = $T1::IsNull; + } + } +} + +__diesel_for_each_tuple!(tuple_impls); diff --git a/collector/benchmarks/diesel/diesel/src/upsert/mod.rs b/collector/benchmarks/diesel/diesel/src/upsert/mod.rs new file mode 100644 index 000000000..dc073ee70 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/upsert/mod.rs @@ -0,0 +1,27 @@ +//! Types and functions related to PG's and Sqlite's `ON CONFLICT` clause +//! +//! Upsert is currently supported by diesel for the following database systems: +//! +//! * PostgreSQL version 9.5 or newer +//! * Sqlite3 version 3.24.0 or newer +//! +//! See [the methods on `InsertStatement`](../query_builder/struct.InsertStatement.html#impl-2) +//! for usage examples. +//! +//! Constructing an upsert statement from an existing select statement +//! requires a where clause on sqlite due to a ambiguity in their +//! parser. See [the corresponding documentation](https://www.sqlite.org/lang_UPSERT.html) +//! for details. +use crate::query_builder::upsert::on_conflict_actions::Excluded; + +mod on_conflict_extension; + +pub use self::on_conflict_extension::DecoratableTarget; +pub use self::on_conflict_extension::*; +#[cfg(feature = "postgres")] +pub use crate::pg::query_builder::on_constraint::*; + +/// Represents `excluded.column` in an `ON CONFLICT DO UPDATE` clause. +pub fn excluded(excluded: T) -> Excluded { + Excluded::new(excluded) +} diff --git a/collector/benchmarks/diesel/diesel/src/upsert/on_conflict_docs_setup.rs b/collector/benchmarks/diesel/diesel/src/upsert/on_conflict_docs_setup.rs new file mode 100644 index 000000000..fa9206013 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/upsert/on_conflict_docs_setup.rs @@ -0,0 +1,9 @@ +include!("../doctest_setup.rs"); +use crate::schema::users; + +#[derive(Clone, Copy, Insertable, AsChangeset)] +#[table_name="users"] +struct User<'a> { + id: i32, + name: &'a str, +} diff --git a/collector/benchmarks/diesel/diesel/src/upsert/on_conflict_extension.rs b/collector/benchmarks/diesel/diesel/src/upsert/on_conflict_extension.rs new file mode 100644 index 000000000..50937a6e4 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/upsert/on_conflict_extension.rs @@ -0,0 +1,391 @@ +use crate::expression::Expression; +use crate::query_builder::upsert::into_conflict_clause::IntoConflictValueClause; +use crate::query_builder::upsert::on_conflict_actions::*; +use crate::query_builder::upsert::on_conflict_clause::*; +use crate::query_builder::upsert::on_conflict_target::*; +pub use crate::query_builder::upsert::on_conflict_target_decorations::DecoratableTarget; +use crate::query_builder::{AsChangeset, InsertStatement, UndecoratedInsertRecord}; +use crate::query_source::QuerySource; +use crate::sql_types::BoolOrNullableBool; + +impl InsertStatement +where + U: UndecoratedInsertRecord + IntoConflictValueClause, +{ + /// Adds `ON CONFLICT DO NOTHING` to the insert statement, without + /// specifying any columns or constraints to restrict the conflict to. + /// + /// # Examples + /// + /// ### Single Record + /// + /// ```rust + /// # include!("on_conflict_docs_setup.rs"); + /// # + /// # #[cfg(any(feature = "sqlite", feature = "postgres"))] + /// # fn main() { + /// # use self::users::dsl::*; + /// # let conn = establish_connection(); + /// # #[cfg(feature = "postgres")] + /// # conn.execute("TRUNCATE TABLE users").unwrap(); + /// # #[cfg(feature = "sqlite")] + /// # conn.execute("DELETE FROM users").unwrap(); + /// let user = User { id: 1, name: "Sean", }; + /// + /// let inserted_row_count = diesel::insert_into(users) + /// .values(&user) + /// .on_conflict_do_nothing() + /// .execute(&conn); + /// assert_eq!(Ok(1), inserted_row_count); + /// + /// let inserted_row_count = diesel::insert_into(users) + /// .values(&user) + /// .on_conflict_do_nothing() + /// .execute(&conn); + /// assert_eq!(Ok(0), inserted_row_count); + /// # } + /// # #[cfg(feature = "mysql")] + /// # fn main() {} + /// ``` + /// + /// ### Vec of Records + /// + /// ```rust + /// # include!("on_conflict_docs_setup.rs"); + /// # + /// # #[cfg(any(feature = "sqlite", feature = "postgres"))] + /// # fn main() { + /// # use self::users::dsl::*; + /// # let conn = establish_connection(); + /// # #[cfg(feature = "postgres")] + /// # conn.execute("TRUNCATE TABLE users").unwrap(); + /// # #[cfg(feature = "postgres")] + /// let user = User { id: 1, name: "Sean", }; + /// + /// # #[cfg(feature = "postgres")] + /// let inserted_row_count = diesel::insert_into(users) + /// .values(&vec![user, user]) + /// .on_conflict_do_nothing() + /// .execute(&conn); + /// # #[cfg(feature = "postgres")] + /// assert_eq!(Ok(1), inserted_row_count); + /// # } + /// # #[cfg(feature = "mysql")] + /// # fn main() {} + /// ``` + pub fn on_conflict_do_nothing( + self, + ) -> InsertStatement, Op, Ret> + { + self.replace_values(|values| OnConflictValues::do_nothing(values.into_value_clause())) + } + + /// Adds an `ON CONFLICT` to the insert statement, if a conflict occurs + /// for the given unique constraint. + /// + /// `Target` can be one of: + /// + /// - A column + /// - A tuple of columns + /// - [`on_constraint("constraint_name")`][`on_constraint`] + /// + /// # Examples + /// + /// ### Specifying a column as the target + /// + /// ```rust + /// # include!("on_conflict_docs_setup.rs"); + /// # + /// # #[cfg(any(feature = "sqlite", feature = "postgres"))] + /// # fn main() { + /// # use self::users::dsl::*; + /// # let conn = establish_connection(); + /// # #[cfg(feature = "postgres")] + /// # conn.execute("TRUNCATE TABLE users").unwrap(); + /// # #[cfg(feature = "sqlite")] + /// # conn.execute("DELETE FROM users").unwrap(); + /// conn.execute("CREATE UNIQUE INDEX users_name ON users (name)").unwrap(); + /// let user = User { id: 1, name: "Sean", }; + /// let same_name_different_id = User { id: 2, name: "Sean" }; + /// let same_id_different_name = User { id: 1, name: "Pascal" }; + /// + /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(&conn)); + /// + /// let inserted_row_count = diesel::insert_into(users) + /// .values(&same_name_different_id) + /// .on_conflict(name) + /// .do_nothing() + /// .execute(&conn); + /// assert_eq!(Ok(0), inserted_row_count); + /// + /// let pk_conflict_result = diesel::insert_into(users) + /// .values(&same_id_different_name) + /// .on_conflict(name) + /// .do_nothing() + /// .execute(&conn); + /// assert!(pk_conflict_result.is_err()); + /// # } + /// # #[cfg(feature = "mysql")] + /// # fn main() {} + /// ``` + /// + /// ### Specifying multiple columns as the target + /// + /// ```rust + /// # include!("../doctest_setup.rs"); + /// # + /// # table! { + /// # users { + /// # id -> Integer, + /// # name -> VarChar, + /// # hair_color -> VarChar, + /// # } + /// # } + /// # + /// # #[derive(Clone, Copy, Insertable)] + /// # #[table_name="users"] + /// # struct User<'a> { + /// # id: i32, + /// # name: &'a str, + /// # hair_color: &'a str, + /// # } + /// # + /// # #[cfg(any(feature = "sqlite", feature = "postgres"))] + /// # fn main() { + /// # use self::users::dsl::*; + /// use diesel::upsert::*; + /// + /// # let conn = establish_connection(); + /// # conn.execute("DROP TABLE users").unwrap(); + /// # conn.execute("CREATE TABLE users (id SERIAL PRIMARY KEY, name TEXT, hair_color TEXT)").unwrap(); + /// conn.execute("CREATE UNIQUE INDEX users_name_hair_color ON users (name, hair_color)").unwrap(); + /// let user = User { id: 1, name: "Sean", hair_color: "black" }; + /// let same_name_different_hair_color = User { id: 2, name: "Sean", hair_color: "brown" }; + /// let same_name_same_hair_color = User { id: 3, name: "Sean", hair_color: "black" }; + /// + /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(&conn)); + /// + /// let inserted_row_count = diesel::insert_into(users) + /// .values(&same_name_different_hair_color) + /// .on_conflict((name, hair_color)) + /// .do_nothing() + /// .execute(&conn); + /// assert_eq!(Ok(1), inserted_row_count); + /// + /// let inserted_row_count = diesel::insert_into(users) + /// .values(&same_name_same_hair_color) + /// .on_conflict((name, hair_color)) + /// .do_nothing() + /// .execute(&conn); + /// assert_eq!(Ok(0), inserted_row_count); + /// # } + /// # #[cfg(feature = "mysql")] + /// # fn main() {} + /// ``` + /// + /// See the documentation for [`on_constraint`] and [`do_update`] for + /// more examples. + /// + /// [`on_constraint`]: ../upsert/fn.on_constraint.html + /// [`do_update`]: ../upsert/struct.IncompleteOnConflict.html#method.do_update + pub fn on_conflict( + self, + target: Target, + ) -> IncompleteOnConflict, ConflictTarget> + where + ConflictTarget: OnConflictTarget, + { + IncompleteOnConflict { + stmt: self.replace_values(IntoConflictValueClause::into_value_clause), + target: ConflictTarget(target), + } + } +} + +impl DecoratableTarget

for IncompleteOnConflict +where + P: Expression, + P::SqlType: BoolOrNullableBool, + T: DecoratableTarget

, +{ + type FilterOutput = IncompleteOnConflict>::FilterOutput>; + fn filter_target(self, predicate: P) -> Self::FilterOutput { + IncompleteOnConflict { + stmt: self.stmt, + target: self.target.filter_target(predicate), + } + } +} + +/// A partially constructed `ON CONFLICT` clause. +#[derive(Debug, Clone, Copy)] +pub struct IncompleteOnConflict { + stmt: Stmt, + target: Target, +} + +impl IncompleteOnConflict, Target> { + /// Creates a query with `ON CONFLICT (target) DO NOTHING` + /// + /// If you want to do nothing when *any* constraint conflicts, use + /// [`on_conflict_do_nothing`] instead. See [`on_conflict`] for usage + /// examples. + /// + /// [`on_conflict_do_nothing`]: ../../query_builder/struct.InsertStatement.html#method.on_conflict_do_nothing + /// [`on_conflict`]: ../../query_builder/struct.InsertStatement.html#method.on_conflict + pub fn do_nothing(self) -> InsertStatement, Op, Ret> { + let target = self.target; + self.stmt + .replace_values(|values| OnConflictValues::new(values, target, DoNothing)) + } +} + +impl IncompleteOnConflict { + /// Used to create a query in the form `ON CONFLICT (...) DO UPDATE ...` + /// + /// Call `.set` on the result of this function with the changes you want to + /// apply. The argument to `set` can be anything that implements `AsChangeset` + /// (e.g. anything you could pass to `set` on a normal update statement). + /// + /// Note: When inserting more than one row at a time, this query can still fail + /// if the rows being inserted conflict with each other. + /// + /// # Examples + /// + /// ## Set specific value on conflict + /// + /// ```rust + /// # include!("on_conflict_docs_setup.rs"); + /// # + /// # #[cfg(any(feature = "sqlite", feature = "postgres"))] + /// # fn main() { + /// # use self::users::dsl::*; + /// # let conn = establish_connection(); + /// # #[cfg(feature = "postgres")] + /// # conn.execute("TRUNCATE TABLE users").unwrap(); + /// # #[cfg(feature = "sqlite")] + /// # conn.execute("DELETE FROM users").unwrap(); + /// let user = User { id: 1, name: "Pascal" }; + /// let user2 = User { id: 1, name: "Sean" }; + /// + /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(&conn)); + /// + /// let insert_count = diesel::insert_into(users) + /// .values(&user2) + /// .on_conflict(id) + /// .do_update() + /// .set(name.eq("I DONT KNOW ANYMORE")) + /// .execute(&conn); + /// assert_eq!(Ok(1), insert_count); + /// + /// let users_in_db = users.load(&conn); + /// assert_eq!(Ok(vec![(1, "I DONT KNOW ANYMORE".to_string())]), users_in_db); + /// # } + /// # #[cfg(feature = "mysql")] + /// # fn main() {} + /// ``` + /// + /// ## Set `AsChangeset` struct on conflict + /// + /// ```rust + /// # include!("on_conflict_docs_setup.rs"); + /// # + /// # #[cfg(any(feature = "sqlite", feature = "postgres"))] + /// # fn main() { + /// # use self::users::dsl::*; + /// # let conn = establish_connection(); + /// # #[cfg(feature = "postgres")] + /// # conn.execute("TRUNCATE TABLE users").unwrap(); + /// # #[cfg(feature = "sqlite")] + /// # conn.execute("DELETE FROM users").unwrap(); + /// let user = User { id: 1, name: "Pascal" }; + /// let user2 = User { id: 1, name: "Sean" }; + /// + /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(&conn)); + /// + /// let insert_count = diesel::insert_into(users) + /// .values(&user2) + /// .on_conflict(id) + /// .do_update() + /// .set(&user2) + /// .execute(&conn); + /// assert_eq!(Ok(1), insert_count); + /// + /// let users_in_db = users.load(&conn); + /// assert_eq!(Ok(vec![(1, "Sean".to_string())]), users_in_db); + /// # } + /// # #[cfg(feature = "mysql")] + /// # fn main() {} + /// ``` + /// + /// ## Use `excluded` to get the rejected value + /// + /// ```rust + /// # include!("on_conflict_docs_setup.rs"); + /// # + /// # #[cfg(any(feature = "sqlite", feature = "postgres"))] + /// # fn main() { + /// # use self::users::dsl::*; + /// use diesel::upsert::excluded; + /// + /// # let conn = establish_connection(); + /// # #[cfg(feature = "postgres")] + /// # conn.execute("TRUNCATE TABLE users").unwrap(); + /// let user = User { id: 1, name: "Pascal" }; + /// let user2 = User { id: 1, name: "Sean" }; + /// let user3 = User { id: 2, name: "Tess" }; + /// + /// # #[cfg(feature = "postgres")] + /// assert_eq!(Ok(1), diesel::insert_into(users).values(&user).execute(&conn)); + /// + /// #[cfg(feature = "postgres")] + /// let insert_count = diesel::insert_into(users) + /// .values(&vec![user2, user3]) + /// .on_conflict(id) + /// .do_update() + /// .set(name.eq(excluded(name))) + /// .execute(&conn); + /// # #[cfg(feature = "postgres")] + /// assert_eq!(Ok(2), insert_count); + /// + /// # #[cfg(feature = "postgres")] + /// let users_in_db = users.load(&conn); + /// # #[cfg(feature = "postgres")] + /// assert_eq!(Ok(vec![(1, "Sean".to_string()), (2, "Tess".to_string())]), users_in_db); + /// # } + /// # #[cfg(feature = "mysql")] + /// # fn main() {} + /// ``` + pub fn do_update(self) -> IncompleteDoUpdate { + IncompleteDoUpdate { + stmt: self.stmt, + target: self.target, + } + } +} + +/// A partially constructed `ON CONFLICT DO UPDATE` clause. +#[derive(Debug, Clone, Copy)] +pub struct IncompleteDoUpdate { + stmt: Stmt, + target: Target, +} + +impl IncompleteDoUpdate, Target> { + /// See [`do_update`] for usage examples. + /// + /// [`do_update`]: struct.IncompleteOnConflict.html#method.do_update + pub fn set( + self, + changes: Changes, + ) -> InsertStatement>, Op, Ret> + where + T: QuerySource, + Changes: AsChangeset, + { + let target = self.target; + self.stmt.replace_values(|values| { + OnConflictValues::new(values, target, DoUpdate::new(changes.as_changeset())) + }) + } +} diff --git a/collector/benchmarks/diesel/diesel/src/util.rs b/collector/benchmarks/diesel/diesel/src/util.rs new file mode 100644 index 000000000..ed0439d49 --- /dev/null +++ b/collector/benchmarks/diesel/diesel/src/util.rs @@ -0,0 +1,11 @@ +/// Treats tuples as a list which can be appended to. e.g. +/// `(a,).tuple_append(b) == (a, b)` +pub trait TupleAppend { + type Output; + + fn tuple_append(self, right: T) -> Self::Output; +} + +pub trait TupleSize { + const SIZE: usize; +} diff --git a/collector/benchmarks/diesel/diesel_derives/Cargo.toml b/collector/benchmarks/diesel/diesel_derives/Cargo.toml new file mode 100644 index 000000000..601aefbf4 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/Cargo.toml @@ -0,0 +1,37 @@ +[package] +name = "diesel_derives" +version = "2.0.0" +authors = ["Sean Griffin "] +license = "MIT OR Apache-2.0" +description = "You should not use this crate directly, it is internal to Diesel." +documentation = "https://diesel.rs/guides/" +homepage = "https://diesel.rs" +repository = "https://github.com/diesel-rs/diesel/tree/master/diesel_derives" +autotests = false +include = ["src/**/*", "LICENSE-*"] + +[dependencies] +syn = { version = "1.0.1", features = ["full", "fold"] } +quote = "1" +proc-macro2 = "1" + +[dev-dependencies] +cfg-if = "0.1.10" +dotenv = "0.15" + +[dev-dependencies.diesel] +version = "~2.0.0" +path = "../diesel" + +[lib] +proc-macro = true + +[[test]] +name = "tests" + +[features] +default = [] +nightly = ["proc-macro2/nightly"] +postgres = [] +sqlite = [] +mysql = [] diff --git a/collector/benchmarks/diesel/diesel_derives/LICENSE-APACHE b/collector/benchmarks/diesel/diesel_derives/LICENSE-APACHE new file mode 120000 index 000000000..965b606f3 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/LICENSE-APACHE @@ -0,0 +1 @@ +../LICENSE-APACHE \ No newline at end of file diff --git a/collector/benchmarks/diesel/diesel_derives/LICENSE-MIT b/collector/benchmarks/diesel/diesel_derives/LICENSE-MIT new file mode 120000 index 000000000..76219eb72 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/LICENSE-MIT @@ -0,0 +1 @@ +../LICENSE-MIT \ No newline at end of file diff --git a/collector/benchmarks/diesel/diesel_derives/src/as_changeset.rs b/collector/benchmarks/diesel/diesel_derives/src/as_changeset.rs new file mode 100644 index 000000000..4b8dec211 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/as_changeset.rs @@ -0,0 +1,122 @@ +use proc_macro2; +use proc_macro2::Span; +use syn; + +use diagnostic_shim::*; +use field::*; +use meta::*; +use model::*; +use util::*; + +pub fn derive(item: syn::DeriveInput) -> Result { + let treat_none_as_null = MetaItem::with_name(&item.attrs, "changeset_options") + .map(|meta| { + meta.warn_if_other_options(&["treat_none_as_null"]); + meta.required_nested_item("treat_none_as_null") + .map(|m| m.expect_bool_value()) + }) + .unwrap_or(Ok(false))?; + let model = Model::from_item(&item)?; + let struct_name = &model.name; + let table_name = model.table_name(); + + let (_, ty_generics, where_clause) = item.generics.split_for_impl(); + let mut impl_generics = item.generics.clone(); + impl_generics.params.push(parse_quote!('update)); + let (impl_generics, _, _) = impl_generics.split_for_impl(); + + let fields_for_update = model + .fields() + .iter() + .filter(|f| !model.primary_key_names.contains(&f.column_name())) + .collect::>(); + let ref_changeset_ty = fields_for_update.iter().map(|field| { + field_changeset_ty( + field, + &table_name, + treat_none_as_null, + Some(quote!(&'update)), + ) + }); + let ref_changeset_expr = fields_for_update + .iter() + .map(|field| field_changeset_expr(field, &table_name, treat_none_as_null, Some(quote!(&)))); + let direct_changeset_ty = fields_for_update + .iter() + .map(|field| field_changeset_ty(field, &table_name, treat_none_as_null, None)); + let direct_changeset_expr = fields_for_update + .iter() + .map(|field| field_changeset_expr(field, &table_name, treat_none_as_null, None)); + + if fields_for_update.is_empty() { + Span::call_site() + .error( + "Deriving `AsChangeset` on a structure that only contains the primary key isn't supported." + ) + .help("If you want to change the primary key of a row, you should do so with `.set(table::id.eq(new_id))`.") + .note("`#[derive(AsChangeset)]` never changes the primary key of a row.") + .emit(); + } + + Ok(wrap_in_dummy_mod(quote!( + use diesel::query_builder::AsChangeset; + use diesel::prelude::*; + + impl #impl_generics AsChangeset for &'update #struct_name #ty_generics + #where_clause + { + type Target = #table_name::table; + type Changeset = <(#(#ref_changeset_ty,)*) as AsChangeset>::Changeset; + + fn as_changeset(self) -> Self::Changeset { + (#(#ref_changeset_expr,)*).as_changeset() + } + } + + impl #impl_generics AsChangeset for #struct_name #ty_generics + #where_clause + { + type Target = #table_name::table; + type Changeset = <(#(#direct_changeset_ty,)*) as AsChangeset>::Changeset; + + fn as_changeset(self) -> Self::Changeset { + (#(#direct_changeset_expr,)*).as_changeset() + } + } + ))) +} + +fn field_changeset_ty( + field: &Field, + table_name: &syn::Ident, + treat_none_as_null: bool, + lifetime: Option, +) -> syn::Type { + let column_name = field.column_name(); + if !treat_none_as_null && is_option_ty(&field.ty) { + let field_ty = inner_of_option_ty(&field.ty); + parse_quote!(std::option::Option>) + } else { + let field_ty = &field.ty; + parse_quote!(diesel::dsl::Eq<#table_name::#column_name, #lifetime #field_ty>) + } +} + +fn field_changeset_expr( + field: &Field, + table_name: &syn::Ident, + treat_none_as_null: bool, + lifetime: Option, +) -> syn::Expr { + let field_access = field.name.access(); + let column_name = field.column_name(); + if !treat_none_as_null && is_option_ty(&field.ty) { + if lifetime.is_some() { + parse_quote!(self#field_access.as_ref().map(|x| #table_name::#column_name.eq(x))) + } else { + parse_quote!(self#field_access.map(|x| #table_name::#column_name.eq(x))) + } + } else { + parse_quote!(#table_name::#column_name.eq(#lifetime self#field_access)) + } +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/as_expression.rs b/collector/benchmarks/diesel/diesel_derives/src/as_expression.rs new file mode 100644 index 000000000..427819e80 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/as_expression.rs @@ -0,0 +1,115 @@ +use proc_macro2; +use syn; + +use meta::*; +use util::*; + +pub fn derive(item: syn::DeriveInput) -> Result { + let flags = + MetaItem::with_name(&item.attrs, "diesel").unwrap_or_else(|| MetaItem::empty("diesel")); + let is_sized = !flags.has_flag("not_sized"); + + let sql_types = MetaItem::all_with_name(&item.attrs, "sql_type"); + let any_sql_types = !sql_types.is_empty(); + let sql_types = sql_types + .into_iter() + .filter_map(|attr| attr.ty_value().map_err(Diagnostic::emit).ok()); + + let (impl_generics, ..) = item.generics.split_for_impl(); + let lifetimes = item.generics.lifetimes().collect::>(); + let ty_params = item.generics.type_params().collect::>(); + let struct_ty = ty_for_foreign_derive(&item, &flags)?; + + let tokens = sql_types.map(|sql_type| { + let lifetimes = &lifetimes; + let ty_params = &ty_params; + let tokens = quote!( + impl<'expr, #(#lifetimes,)* #(#ty_params,)*> AsExpression<#sql_type> + for &'expr #struct_ty + { + type Expression = Bound<#sql_type, Self>; + + fn as_expression(self) -> Self::Expression { + Bound::new(self) + } + } + + impl<'expr, #(#lifetimes,)* #(#ty_params,)*> AsExpression> + for &'expr #struct_ty + { + type Expression = Bound, Self>; + + fn as_expression(self) -> Self::Expression { + Bound::new(self) + } + } + + impl<'expr2, 'expr, #(#lifetimes,)* #(#ty_params,)*> AsExpression<#sql_type> + for &'expr2 &'expr #struct_ty + { + type Expression = Bound<#sql_type, Self>; + + fn as_expression(self) -> Self::Expression { + Bound::new(self) + } + } + + impl<'expr2, 'expr, #(#lifetimes,)* #(#ty_params,)*> AsExpression> + for &'expr2 &'expr #struct_ty + { + type Expression = Bound, Self>; + + fn as_expression(self) -> Self::Expression { + Bound::new(self) + } + } + + impl<#(#lifetimes,)* #(#ty_params,)* __DB> diesel::serialize::ToSql, __DB> + for #struct_ty + where + __DB: diesel::backend::Backend, + Self: ToSql<#sql_type, __DB>, + { + fn to_sql(&self, out: &mut Output) -> serialize::Result { + ToSql::<#sql_type, __DB>::to_sql(self, out) + } + } + ); + if is_sized { + quote!( + #tokens + + impl#impl_generics AsExpression<#sql_type> for #struct_ty { + type Expression = Bound<#sql_type, Self>; + + fn as_expression(self) -> Self::Expression { + Bound::new(self) + } + } + + impl#impl_generics AsExpression> for #struct_ty { + type Expression = Bound, Self>; + + fn as_expression(self) -> Self::Expression { + Bound::new(self) + } + } + ) + } else { + tokens + } + }); + + if any_sql_types { + Ok(wrap_in_dummy_mod(quote! { + use diesel::expression::AsExpression; + use diesel::expression::bound::Bound; + use diesel::sql_types::Nullable; + use diesel::serialize::{self, ToSql, Output}; + + #(#tokens)* + })) + } else { + Ok(quote!()) + } +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/associations.rs b/collector/benchmarks/diesel/diesel_derives/src/associations.rs new file mode 100644 index 000000000..ea529fa5e --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/associations.rs @@ -0,0 +1,206 @@ +use proc_macro2; +use proc_macro2::Span; +use syn; +use syn::fold::Fold; +use syn::spanned::Spanned; + +use diagnostic_shim::*; +use meta::*; +use model::*; +use util::*; + +pub fn derive(item: syn::DeriveInput) -> Result { + let model = Model::from_item(&item)?; + let tokens = MetaItem::all_with_name(&item.attrs, "belongs_to") + .into_iter() + .filter_map( + |attr| match derive_belongs_to(&model, &item.generics, attr) { + Ok(t) => Some(t), + Err(e) => { + e.emit(); + None + } + }, + ); + + Ok(wrap_in_dummy_mod(quote!(#(#tokens)*))) +} + +fn derive_belongs_to( + model: &Model, + generics: &syn::Generics, + meta: MetaItem, +) -> Result { + let AssociationOptions { + parent_struct, + foreign_key, + } = AssociationOptions::from_meta(meta)?; + let (_, ty_generics, _) = generics.split_for_impl(); + + let foreign_key_field = model.find_column(&foreign_key)?; + let struct_name = &model.name; + let foreign_key_access = foreign_key_field.name.access(); + let foreign_key_ty = &foreign_key_field.ty; + let table_name = model.table_name(); + + let mut generics = generics.clone(); + + let parent_struct = ReplacePathLifetimes::new(|i, span| { + let letter = char::from(b'b' + i as u8); + let lifetime = syn::Lifetime::new(&format!("'__{}", letter), span); + generics.params.push(parse_quote!(#lifetime)); + lifetime + }) + .fold_type_path(parent_struct); + + generics.params.push(parse_quote!(__FK)); + { + let where_clause = generics.where_clause.get_or_insert(parse_quote!(where)); + where_clause + .predicates + .push(parse_quote!(__FK: std::hash::Hash + std::cmp::Eq)); + where_clause.predicates.push( + parse_quote!(for<'__a> &'__a #foreign_key_ty: std::convert::Into<::std::option::Option<&'__a __FK>>), + ); + where_clause.predicates.push( + parse_quote!(for<'__a> &'__a #parent_struct: diesel::associations::Identifiable), + ); + } + + let foreign_key_expr = quote!(std::convert::Into::into(&self#foreign_key_access)); + let foreign_key_ty = quote!(__FK); + + let (impl_generics, _, where_clause) = generics.split_for_impl(); + + Ok(quote! { + impl #impl_generics diesel::associations::BelongsTo<#parent_struct> + for #struct_name #ty_generics + #where_clause + { + type ForeignKey = #foreign_key_ty; + type ForeignKeyColumn = #table_name::#foreign_key; + + fn foreign_key(&self) -> std::option::Option<&Self::ForeignKey> { + #foreign_key_expr + } + + fn foreign_key_column() -> Self::ForeignKeyColumn { + #table_name::#foreign_key + } + } + }) +} + +struct AssociationOptions { + parent_struct: syn::TypePath, + foreign_key: syn::Ident, +} + +impl AssociationOptions { + fn from_meta(meta: MetaItem) -> Result { + let parent_struct = meta + .nested()? + .find(|m| m.path().is_ok() || m.name().is_ident("parent")) + .ok_or_else(|| meta.span()) + .and_then(|m| { + m.path() + .map(|i| parse_quote!(#i)) + .or_else(|_| m.ty_value()) + .map_err(|_| m.span()) + }) + .and_then(|ty| match ty { + syn::Type::Path(ty_path) => Ok(ty_path), + _ => Err(ty.span()), + }) + .map_err(|span| { + span.error("Expected a struct name") + .help("e.g. `#[belongs_to(User)]` or `#[belongs_to(parent = \"User<'_>\")]") + })?; + let foreign_key = { + let parent_struct_name = parent_struct + .path + .segments + .last() + .expect("paths always have at least one segment"); + meta.nested_item("foreign_key")? + .map(|i| i.ident_value()) + .unwrap_or_else(|| Ok(infer_foreign_key(&parent_struct_name.ident)))? + }; + + let (unrecognized_paths, unrecognized_options): (Vec<_>, Vec<_>) = meta + .nested()? + .skip(1) + .filter(|n| !n.name().is_ident("foreign_key")) + .partition(|item| item.path().is_ok()); + + if !unrecognized_paths.is_empty() { + let parent_path_string = path_to_string(&parent_struct.path); + let unrecognized_path_strings: Vec<_> = unrecognized_paths + .iter() + .filter_map(|item| item.path().as_ref().map(path_to_string).ok()) + .collect(); + + meta.span() + .warning(format!( + "belongs_to takes a single parent. Change\n\ + \tbelongs_to({}, {})\n\ + to\n\ + \tbelongs_to({})\n\ + {}", + parent_path_string, + unrecognized_path_strings.join(","), + parent_path_string, + unrecognized_path_strings + .iter() + .map(|path| format!("\tbelongs_to({})", path)) + .collect::>() + .join("\n") + )) + .emit(); + } + + for ignored in unrecognized_options { + ignored + .span() + .warning(format!( + "Unrecognized option {}", + path_to_string(&ignored.name()) + )) + .emit(); + } + + Ok(Self { + parent_struct, + foreign_key, + }) + } +} + +fn infer_foreign_key(name: &syn::Ident) -> syn::Ident { + let snake_case = camel_to_snake(&name.to_string()); + syn::Ident::new(&format!("{}_id", snake_case), name.span()) +} + +struct ReplacePathLifetimes { + count: usize, + f: F, +} + +impl ReplacePathLifetimes { + fn new(f: F) -> Self { + Self { count: 0, f } + } +} + +impl Fold for ReplacePathLifetimes +where + F: FnMut(usize, Span) -> syn::Lifetime, +{ + fn fold_lifetime(&mut self, mut lt: syn::Lifetime) -> syn::Lifetime { + if lt.ident == "_" { + lt = (self.f)(self.count, lt.span()); + self.count += 1; + } + lt + } +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/diagnostic_shim.rs b/collector/benchmarks/diesel/diesel_derives/src/diagnostic_shim.rs new file mode 100644 index 000000000..8392f8088 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/diagnostic_shim.rs @@ -0,0 +1,87 @@ +use proc_macro2::Span; + +pub trait EmitErrorExt { + fn emit_error(self) -> Option; +} + +impl EmitErrorExt for Result { + fn emit_error(self) -> Option { + self.map_err(Diagnostic::emit).ok() + } +} + +pub trait DiagnosticShim { + fn error>(self, msg: T) -> Diagnostic; + fn warning>(self, msg: T) -> Diagnostic; +} + +#[cfg(feature = "nightly")] +impl DiagnosticShim for Span { + fn error>(self, msg: T) -> Diagnostic { + self.unstable().error(msg) + } + + fn warning>(self, msg: T) -> Diagnostic { + self.unstable().warning(msg) + } +} + +#[cfg(not(feature = "nightly"))] +impl DiagnosticShim for Span { + fn error>(self, msg: T) -> Diagnostic { + Diagnostic::error(msg) + } + + fn warning>(self, msg: T) -> Diagnostic { + Diagnostic::warning(msg) + } +} + +#[cfg(feature = "nightly")] +pub use proc_macro::Diagnostic; + +#[cfg(not(feature = "nightly"))] +pub struct Diagnostic { + message: String, + level: Level, +} + +#[cfg(not(feature = "nightly"))] +impl Diagnostic { + fn error>(msg: T) -> Self { + Diagnostic { + message: msg.into(), + level: Level::Error, + } + } + + fn warning>(msg: T) -> Self { + Diagnostic { + message: msg.into(), + level: Level::Warning, + } + } + + pub fn help>(mut self, msg: T) -> Self { + self.message.push_str("\n"); + self.message.push_str(&msg.into()); + self + } + + pub fn note(self, msg: &str) -> Self { + self.help(msg) + } + + pub fn emit(self) { + match self.level { + Level::Error => panic!("{}", self.message), + Level::Warning => println!("{}", self.message), + } + } +} + +#[cfg(not(feature = "nightly"))] +enum Level { + Warning, + Error, +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/diesel_numeric_ops.rs b/collector/benchmarks/diesel/diesel_derives/src/diesel_numeric_ops.rs new file mode 100644 index 000000000..b8e021990 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/diesel_numeric_ops.rs @@ -0,0 +1,83 @@ +use proc_macro2; +use syn; + +use util::*; + +pub fn derive(mut item: syn::DeriveInput) -> Result { + let struct_name = &item.ident; + + { + let where_clause = item + .generics + .where_clause + .get_or_insert(parse_quote!(where)); + where_clause.predicates.push(parse_quote!(Self: Expression)); + where_clause.predicates.push_punct(Default::default()); + } + let (_, ty_generics, where_clause) = item.generics.split_for_impl(); + let mut impl_generics = item.generics.clone(); + impl_generics.params.push(parse_quote!(__Rhs)); + let (impl_generics, _, _) = impl_generics.split_for_impl(); + + Ok(wrap_in_dummy_mod(quote! { + use diesel::expression::{ops, Expression, AsExpression}; + use diesel::sql_types::ops::{Add, Sub, Mul, Div}; + use diesel::sql_types::{SqlType, SingleValue}; + + impl #impl_generics ::std::ops::Add<__Rhs> for #struct_name #ty_generics + #where_clause + Self: Expression, + ::SqlType: Add, + <::SqlType as Add>::Rhs: SqlType + SingleValue, + __Rhs: AsExpression<<::SqlType as Add>::Rhs>, + { + type Output = ops::Add; + + fn add(self, rhs: __Rhs) -> Self::Output { + ops::Add::new(self, rhs.as_expression()) + } + } + + impl #impl_generics ::std::ops::Sub<__Rhs> for #struct_name #ty_generics + #where_clause + Self: Expression, + ::SqlType: Sub, + <::SqlType as Sub>::Rhs: SqlType + SingleValue, + __Rhs: AsExpression<<::SqlType as Sub>::Rhs>, + { + type Output = ops::Sub; + + fn sub(self, rhs: __Rhs) -> Self::Output { + ops::Sub::new(self, rhs.as_expression()) + } + } + + impl #impl_generics ::std::ops::Mul<__Rhs> for #struct_name #ty_generics + #where_clause + Self: Expression, + ::SqlType: Mul, + <::SqlType as Mul>::Rhs: SqlType + SingleValue, + __Rhs: AsExpression<<::SqlType as Mul>::Rhs>, + { + type Output = ops::Mul; + + fn mul(self, rhs: __Rhs) -> Self::Output { + ops::Mul::new(self, rhs.as_expression()) + } + } + + impl #impl_generics ::std::ops::Div<__Rhs> for #struct_name #ty_generics + #where_clause + Self: Expression, + ::SqlType: Div, + <::SqlType as Div>::Rhs: SqlType + SingleValue, + __Rhs: AsExpression<<::SqlType as Div>::Rhs>, + { + type Output = ops::Div; + + fn div(self, rhs: __Rhs) -> Self::Output { + ops::Div::new(self, rhs.as_expression()) + } + } + })) +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/field.rs b/collector/benchmarks/diesel/diesel_derives/src/field.rs new file mode 100644 index 000000000..0e1efe25e --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/field.rs @@ -0,0 +1,125 @@ +use proc_macro2::{self, Ident, Span}; +use quote::ToTokens; +use std::borrow::Cow; +use syn; +use syn::spanned::Spanned; + +use meta::*; +use util::*; + +pub struct Field { + pub ty: syn::Type, + pub name: FieldName, + pub span: Span, + pub sql_type: Option, + pub flags: MetaItem, + column_name_from_attribute: Option, +} + +impl Field { + pub fn from_struct_field(field: &syn::Field, index: usize) -> Self { + let column_name_from_attribute = + MetaItem::with_name(&field.attrs, "column_name").map(|m| m.expect_ident_value()); + let name = match field.ident.clone() { + Some(mut x) => { + // https://github.com/rust-lang/rust/issues/47983#issuecomment-362817105 + let span = x.span(); + x.set_span(fix_span(span, Span::call_site())); + FieldName::Named(x) + } + None => FieldName::Unnamed(syn::Index { + index: index as u32, + // https://github.com/rust-lang/rust/issues/47312 + span: Span::call_site(), + }), + }; + let sql_type = MetaItem::with_name(&field.attrs, "sql_type") + .and_then(|m| m.ty_value().map_err(Diagnostic::emit).ok()); + let flags = MetaItem::with_name(&field.attrs, "diesel") + .unwrap_or_else(|| MetaItem::empty("diesel")); + let span = field.span(); + + Self { + ty: field.ty.clone(), + column_name_from_attribute, + name, + sql_type, + flags, + span, + } + } + + pub fn column_name(&self) -> syn::Ident { + self.column_name_from_attribute + .clone() + .unwrap_or_else(|| match self.name { + FieldName::Named(ref x) => x.clone(), + _ => { + self.span + .error( + "All fields of tuple structs must be annotated with `#[column_name]`", + ) + .emit(); + Ident::new("unknown_column", self.span) + } + }) + } + + pub fn has_flag(&self, flag: &str) -> bool { + self.flags.has_flag(flag) + } + + pub fn ty_for_serialize(&self) -> Result, Diagnostic> { + if let Some(meta) = self.flags.nested_item("serialize_as")? { + let ty = meta.ty_value()?; + Ok(Some(ty)) + } else { + Ok(None) + } + } + + pub fn ty_for_deserialize(&self) -> Result, Diagnostic> { + if let Some(meta) = self.flags.nested_item("deserialize_as")? { + meta.ty_value().map(Cow::Owned) + } else { + Ok(Cow::Borrowed(&self.ty)) + } + } +} + +pub enum FieldName { + Named(syn::Ident), + Unnamed(syn::Index), +} + +impl FieldName { + pub fn assign(&self, expr: syn::Expr) -> syn::FieldValue { + let span = self.span(); + // Parens are to work around https://github.com/rust-lang/rust/issues/47311 + let tokens = quote_spanned!(span=> #self: (#expr)); + parse_quote!(#tokens) + } + + pub fn access(&self) -> proc_macro2::TokenStream { + let span = self.span(); + // Span of the dot is important due to + // https://github.com/rust-lang/rust/issues/47312 + quote_spanned!(span=> .#self) + } + + pub fn span(&self) -> Span { + match *self { + FieldName::Named(ref x) => x.span(), + FieldName::Unnamed(ref x) => x.span, + } + } +} + +impl ToTokens for FieldName { + fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) { + match *self { + FieldName::Named(ref x) => x.to_tokens(tokens), + FieldName::Unnamed(ref x) => x.to_tokens(tokens), + } + } +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/from_sql_row.rs b/collector/benchmarks/diesel/diesel_derives/src/from_sql_row.rs new file mode 100644 index 000000000..c7edb9b7d --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/from_sql_row.rs @@ -0,0 +1,44 @@ +use proc_macro2::*; +use syn; + +use meta::*; +use util::*; + +pub fn derive(mut item: syn::DeriveInput) -> Result { + let flags = + MetaItem::with_name(&item.attrs, "diesel").unwrap_or_else(|| MetaItem::empty("diesel")); + let struct_ty = ty_for_foreign_derive(&item, &flags)?; + + item.generics.params.push(parse_quote!(__ST)); + item.generics.params.push(parse_quote!(__DB)); + { + let where_clause = item + .generics + .where_clause + .get_or_insert(parse_quote!(where)); + where_clause + .predicates + .push(parse_quote!(__DB: diesel::backend::Backend)); + where_clause + .predicates + .push(parse_quote!(__ST: diesel::sql_types::SingleValue)); + where_clause + .predicates + .push(parse_quote!(Self: FromSql<__ST, __DB>)); + } + let (impl_generics, _, where_clause) = item.generics.split_for_impl(); + + Ok(wrap_in_dummy_mod(quote! { + use diesel::deserialize::{FromSql, Queryable}; + + impl #impl_generics Queryable<__ST, __DB> for #struct_ty + #where_clause + { + type Row = Self; + + fn build(row: Self::Row) -> Self { + row + } + } + })) +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/identifiable.rs b/collector/benchmarks/diesel/diesel_derives/src/identifiable.rs new file mode 100644 index 000000000..eb43fa816 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/identifiable.rs @@ -0,0 +1,47 @@ +use proc_macro2; +use syn; + +use model::*; +use util::*; + +pub fn derive(item: syn::DeriveInput) -> Result { + let model = Model::from_item(&item)?; + let struct_name = &model.name; + let table_name = model.table_name(); + + let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); + let mut ref_generics = item.generics.clone(); + ref_generics.params.push(parse_quote!('ident)); + let (ref_generics, ..) = ref_generics.split_for_impl(); + + let (field_ty, field_access): (Vec<_>, Vec<_>) = model + .primary_key_names + .iter() + .filter_map(|pk| model.find_column(pk).emit_error()) + .map(|f| (&f.ty, f.name.access())) + .unzip(); + + Ok(wrap_in_dummy_mod(quote! { + use diesel::associations::{HasTable, Identifiable}; + + impl #impl_generics HasTable for #struct_name #ty_generics + #where_clause + { + type Table = #table_name::table; + + fn table() -> Self::Table { + #table_name::table + } + } + + impl #ref_generics Identifiable for &'ident #struct_name #ty_generics + #where_clause + { + type Id = (#(&'ident #field_ty),*); + + fn id(self) -> Self::Id { + (#(&self#field_access),*) + } + } + })) +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/insertable.rs b/collector/benchmarks/diesel/diesel_derives/src/insertable.rs new file mode 100644 index 000000000..d6a7374f1 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/insertable.rs @@ -0,0 +1,183 @@ +use proc_macro2; +use proc_macro2::Span; +use syn; + +use field::*; +use model::*; +use util::*; + +pub fn derive(item: syn::DeriveInput) -> Result { + let model = Model::from_item(&item)?; + + if model.fields().is_empty() { + return Err(Span::call_site() + .error("Cannot derive Insertable for unit structs") + .help(format!( + "Use `insert_into({}::table).default_values()` if you want `DEFAULT VALUES`", + model.table_name() + ))); + } + + let table_name = &model.table_name(); + let struct_name = &item.ident; + + let (_, ty_generics, where_clause) = item.generics.split_for_impl(); + let mut impl_generics = item.generics.clone(); + impl_generics.params.push(parse_quote!('insert)); + let (impl_generics, ..) = impl_generics.split_for_impl(); + + let mut generate_borrowed_insert = true; + + let mut direct_field_ty = Vec::with_capacity(model.fields().len()); + let mut direct_field_assign = Vec::with_capacity(model.fields().len()); + let mut ref_field_ty = Vec::with_capacity(model.fields().len()); + let mut ref_field_assign = Vec::with_capacity(model.fields().len()); + + for field in model.fields() { + let serialize_as = field.ty_for_serialize()?; + let embed = field.has_flag("embed"); + + match (serialize_as, embed) { + (None, true) => { + direct_field_ty.push(field_ty_embed(field, None)); + direct_field_assign.push(field_expr_embed(field, None)); + ref_field_ty.push(field_ty_embed(field, Some(quote!(&'insert)))); + ref_field_assign.push(field_expr_embed(field, Some(quote!(&)))); + } + (None, false) => { + direct_field_ty.push(field_ty(field, table_name, None)); + direct_field_assign.push(field_expr(field, table_name, None)); + ref_field_ty.push(field_ty(field, table_name, Some(quote!(&'insert)))); + ref_field_assign.push(field_expr(field, table_name, Some(quote!(&)))); + } + (Some(ty), false) => { + direct_field_ty.push(field_ty_serialize_as(field, table_name, &ty)); + direct_field_assign.push(field_expr_serialize_as(field, table_name, &ty)); + + generate_borrowed_insert = false; // as soon as we hit one field with #[diesel(serialize_as)] there is no point in generating the impl of Insertable for borrowed structs + } + (Some(_), true) => { + return Err(field + .flags + .span() + .error("`#[diesel(embed)]` cannot be combined with `#[diesel(serialize_as)]`")) + } + } + } + + let insert_owned = quote! { + impl #impl_generics Insertable<#table_name::table> for #struct_name #ty_generics + #where_clause + { + type Values = <(#(#direct_field_ty,)*) as Insertable<#table_name::table>>::Values; + + fn values(self) -> Self::Values { + (#(#direct_field_assign,)*).values() + } + } + }; + + let insert_borrowed = if generate_borrowed_insert { + quote! { + impl #impl_generics Insertable<#table_name::table> + for &'insert #struct_name #ty_generics + #where_clause + { + type Values = <(#(#ref_field_ty,)*) as Insertable<#table_name::table>>::Values; + + fn values(self) -> Self::Values { + (#(#ref_field_assign,)*).values() + } + } + + impl #impl_generics UndecoratedInsertRecord<#table_name::table> + for #struct_name #ty_generics + #where_clause + { + } + } + } else { + quote! {} + }; + + Ok(wrap_in_dummy_mod(quote! { + use diesel::insertable::Insertable; + use diesel::query_builder::UndecoratedInsertRecord; + use diesel::prelude::*; + + #insert_owned + + #insert_borrowed + })) +} + +fn field_ty_embed(field: &Field, lifetime: Option) -> syn::Type { + let field_ty = &field.ty; + + parse_quote!(#lifetime #field_ty) +} + +fn field_expr_embed(field: &Field, lifetime: Option) -> syn::Expr { + let field_access = field.name.access(); + + parse_quote!(#lifetime self#field_access) +} + +fn field_ty_serialize_as(field: &Field, table_name: &syn::Ident, ty: &syn::Type) -> syn::Type { + let inner_ty = inner_of_option_ty(&ty); + let column_name = field.column_name(); + + parse_quote!( + std::option::Option> + ) +} + +fn field_expr_serialize_as(field: &Field, table_name: &syn::Ident, ty: &syn::Type) -> syn::Expr { + let field_access = field.name.access(); + let column_name = field.column_name(); + let column: syn::Expr = parse_quote!(#table_name::#column_name); + + if is_option_ty(&ty) { + parse_quote!(self#field_access.map(|x| #column.eq(::std::convert::Into::<#ty>::into(x)))) + } else { + parse_quote!(std::option::Option::Some(#column.eq(::std::convert::Into::<#ty>::into(self#field_access)))) + } +} + +fn field_ty( + field: &Field, + table_name: &syn::Ident, + lifetime: Option, +) -> syn::Type { + let inner_ty = inner_of_option_ty(&field.ty); + let column_name = field.column_name(); + + parse_quote!( + std::option::Option> + ) +} + +fn field_expr( + field: &Field, + table_name: &syn::Ident, + lifetime: Option, +) -> syn::Expr { + let field_access = field.name.access(); + let column_name = field.column_name(); + let column: syn::Expr = parse_quote!(#table_name::#column_name); + if is_option_ty(&field.ty) { + if lifetime.is_some() { + parse_quote!(self#field_access.as_ref().map(|x| #column.eq(x))) + } else { + parse_quote!(self#field_access.map(|x| #column.eq(x))) + } + } else { + parse_quote!(std::option::Option::Some(#column.eq(#lifetime self#field_access))) + } +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/lib.rs b/collector/benchmarks/diesel/diesel_derives/src/lib.rs new file mode 100644 index 000000000..0e4b60ed0 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/lib.rs @@ -0,0 +1,1175 @@ +#![recursion_limit = "1024"] +// Built-in Lints +#![deny(warnings, missing_copy_implementations)] +// Clippy lints +#![allow( + clippy::needless_doctest_main, + clippy::needless_pass_by_value, + clippy::option_map_unwrap_or_else, + clippy::option_map_unwrap_or +)] +#![warn( + clippy::wrong_pub_self_convention, + clippy::mut_mut, + clippy::non_ascii_literal, + clippy::similar_names, + clippy::unicode_not_nfc, + clippy::if_not_else, + clippy::items_after_statements, + clippy::used_underscore_binding +)] +#![cfg_attr(feature = "nightly", feature(proc_macro_diagnostic, proc_macro_span))] + +extern crate proc_macro; +extern crate proc_macro2; +#[macro_use] +extern crate quote; +#[macro_use] +extern crate syn; + +use proc_macro::TokenStream; + +mod diagnostic_shim; +mod field; +mod meta; +mod model; +mod resolved_at_shim; +mod util; + +mod as_changeset; +mod as_expression; +mod associations; +mod diesel_numeric_ops; +mod from_sql_row; +mod identifiable; +mod insertable; +mod query_id; +mod queryable; +mod queryable_by_name; +mod sql_function; +mod sql_type; +mod valid_grouping; + +use diagnostic_shim::*; + +/// Implements `AsChangeset` +/// +/// To implement `AsChangeset` this derive needs to know the corresponding table +/// type. By default it uses the `snake_case` type name with an added `s`. +/// It is possible to change this default by using `#[table_name = "something"]`. +/// In both cases the module for that table must be in scope. +/// For example, to derive this for a struct called `User`, you will +/// likely need a line such as `use schema::users;` +/// +/// If a field name of your struct differs +/// from the name of the corresponding column, you can annotate the field with +/// `#[column_name = "some_column_name"]`. +/// +/// By default, any `Option` fields on the struct are skipped if their value is +/// `None`. If you would like to assign `NULL` to the field instead, you can +/// annotate your struct with `#[changeset_options(treat_none_as_null = +/// "true")]`. +/// +/// # Attributes +/// +/// ## Optional type attributes +/// +/// * `#[table_name = "some_table"]`, specifies the table for which the +/// current type is a changeset. Requires that `some_table` is in scope. +/// If this attribute is not used, the type name converted to +/// `snake_case` with an added `s` is used as table name +/// * `#[changeset_options(treat_none_as_null = "true")]`, specifies that +/// the derive should threat `None` values as `NULL`. By default +/// `Option::::None` is just skipped. To insert a `NULL` using default +/// behavior use `Option::>::Some(None)` +/// +/// ## Optional field attributes +/// +/// * `#[column_name = "some_column_name"]`, overrides the column name +/// of the current field to `some_column_name`. By default the field +/// name is used as column name. +#[proc_macro_derive( + AsChangeset, + attributes(table_name, primary_key, column_name, changeset_options) +)] +pub fn derive_as_changeset(input: TokenStream) -> TokenStream { + expand_proc_macro(input, as_changeset::derive) +} + +/// Implements all required variants of `AsExpression` +/// +/// This derive will generate the following impls: +/// +/// - `impl AsExpression for YourType` +/// - `impl AsExpression> for YourType` +/// - `impl AsExpression for &'a YourType` +/// - `impl AsExpression> for &'a YourType` +/// - `impl AsExpression for &'a &'b YourType` +/// - `impl AsExpression> for &'a &'b YourType` +/// +/// If your type is unsized, +/// you can specify this by adding the annotation `#[diesel(not_sized)]` +/// as attribute on the type. This will skip the impls for non-reference types. +/// +/// # Attributes: +/// +/// ## Required type attributes +/// +/// * `#[sql_type = "SqlType"]`, to specify the sql type of the +/// generated implementations. If the attribute exists multiple times +/// impls for each sql type are generated. +/// +/// ## Optional type attribute +/// +/// * `#[diesel(not_sized)]`, to skip generating impls that require +/// that the type is `Sized` +#[proc_macro_derive(AsExpression, attributes(diesel, sql_type))] +pub fn derive_as_expression(input: TokenStream) -> TokenStream { + expand_proc_macro(input, as_expression::derive) +} + +/// Implement required traits for the associations API +/// +/// This derive implement support for diesels associations api. Check the +/// module level documentation of the `diesel::associations` module for details. +/// +/// # Attributes +/// +/// # Required type attributes +/// +/// * `#[belongs_to(User)]`, to specify a child-to-parent relation ship +/// between the current type and the specified parent type (`User`). +/// If this attribute is given multiple times, multiple relation ships +/// are generated. +/// * `#[belongs_to(User, foreign_key = "mykey")]`, variant of the attribute +/// above. Allows to specify the name of the foreign key. If the foreign key +/// is not specified explicitly, the remote lower case type name with an +/// appended `_id` is used as foreign key name. (`user_id` in this example +/// case) +/// +/// # Optional type attributes +/// +/// * `#[table_name = "some_table_name"]` specifies the table this +/// type belongs to. Requires that `some_table_name` is in scope. +/// If this attribute is not used, the type name converted to +/// `snake_case` with an added `s` is used as table name +/// +/// # Optional field attributes +/// +/// * `#[column_name = "some_table_name"]`, overrides the column the current +/// field maps to to `some_table_name`. By default the field name is used +/// as column name. Only useful for the foreign key field. +/// +#[proc_macro_derive(Associations, attributes(belongs_to, column_name, table_name))] +pub fn derive_associations(input: TokenStream) -> TokenStream { + expand_proc_macro(input, associations::derive) +} + +/// Implement numeric operators for the current query node +#[proc_macro_derive(DieselNumericOps)] +pub fn derive_diesel_numeric_ops(input: TokenStream) -> TokenStream { + expand_proc_macro(input, diesel_numeric_ops::derive) +} + +/// Implements `Queryable` for primitive types +/// +/// This derive is mostly useful to implement support deserializing +/// into rust types not supported by diesel itself. +/// +/// There are no options or special considerations needed for this derive. +#[proc_macro_derive(FromSqlRow, attributes(diesel))] +pub fn derive_from_sql_row(input: TokenStream) -> TokenStream { + expand_proc_macro(input, from_sql_row::derive) +} + +/// Implements `Identifiable` for references of the current type +/// +/// By default, the primary key field is assumed to be a single field called `id`. +/// If it's not, you can put `#[primary_key(your_id)]` on your struct. +/// If you have a composite primary key, the syntax is `#[primary_key(id1, id2)]`. +/// +/// By default, `#[derive(Identifiable)]` will assume that your table +/// name is the plural form of your struct name. +/// Diesel uses very simple pluralization rules. +/// It only adds an `s` to the end, and converts `CamelCase` to `snake_case`. +/// If your table name does not follow this convention +/// or the plural form isn't just an `s`, +/// you can specify the table name with `#[table_name = "some_table_name"]`. +/// In both cases the module for that table must be in scope. +/// For example, to derive this for a struct called `User`, you will +/// likely need a line such as `use schema::users;` +/// Our rules for inferring table names is considered public API. +/// It will never change without a major version bump. +/// +/// # Attributes +/// +/// ## Optional type attributes +/// +/// * `#[table_name = "some_table_name"]` specifies the table this +/// type belongs to. Requires that `some_table_name` is in scope. +/// If this attribute is not used, the type name converted to +/// `snake_case` with an added `s` is used as table name +/// * `#[primary_key(id1, id2)]` to specify the struct field that +/// that corresponds to the primary key. If not used, `id` will be +/// assumed as primary key field +/// +/// +#[proc_macro_derive(Identifiable, attributes(table_name, primary_key, column_name))] +pub fn derive_identifiable(input: TokenStream) -> TokenStream { + expand_proc_macro(input, identifiable::derive) +} + +/// Implements `Insertable` +/// +/// To implement `Insertable` this derive needs to know the corresponding table +/// type. By default it uses the `snake_case` type name with an added `s`. +/// It is possible to change this default by using `#[table_name = "something"]`. +/// In both cases the module for that table must be in scope. +/// For example, to derive this for a struct called `User`, you will +/// likely need a line such as `use schema::users;` +/// +/// If a field name of your +/// struct differs from the name of the corresponding column, +/// you can annotate the field with `#[column_name = "some_column_name"]`. +/// +/// Your struct can also contain fields which implement `Insertable`. This is +/// useful when you want to have one field map to more than one column (for +/// example, an enum that maps to a label and a value column). Add +/// `#[diesel(embed)]` to any such fields. +/// +/// To provide custom serialization behavior for a field, you can use +/// `#[diesel(serialize_as = "SomeType")]`. If this attribute is present, Diesel +/// will call `.into` on the corresponding field and serialize the instance of `SomeType`, +/// rather than the actual field on your struct. This can be used to add custom behavior for a +/// single field, or use types that are otherwise unsupported by Diesel. +/// Using `#[diesel(serialize_as)]` is **incompatible** with `#[diesel(embed)]`. +/// Normally, Diesel produces two implementations of the `Insertable` trait for your +/// struct using this derive: one for an owned version and one for a borrowed version. +/// Using `#[diesel(serialize_as)]` implies a conversion using `.into` which consumes the underlying value. +/// Hence, once you use `#[diesel(serialize_as)]`, Diesel can no longer insert borrowed +/// versions of your struct. +/// +/// # Attributes +/// +/// ## Optional type attributes +/// +/// * `#[table_name = "some_table_name"]`, specifies the table this type +/// is insertable into. Requires that `some_table_name` is in scope. +/// If this attribute is not used, the type name converted to +/// `snake_case` with an added `s` is used as table name +/// +/// ## Optional field attributes +/// +/// * `#[column_name = "some_table_name"]`, overrides the column the current +/// field maps to `some_table_name`. By default the field name is used +/// as column name +/// * `#[diesel(embed)]`, specifies that the current field maps not only +/// to single database field, but is a struct that implements `Insertable` +/// * `#[diesel(serialize_as = "SomeType")]`, instead of serializing the actual +/// field type, Diesel will convert the field into `SomeType` using `.into` and +/// serialize that instead. By default this derive will serialize directly using +/// the actual field type. +/// +/// # Examples +/// +/// If we want to customize the serialization during insert, we can use `#[diesel(serialize_as)]`. +/// +/// ```rust +/// # extern crate diesel; +/// # extern crate dotenv; +/// # include!("../../diesel/src/doctest_setup.rs"); +/// # use diesel::{prelude::*, serialize::{ToSql, Output, self}, deserialize::{FromSqlRow}, expression::AsExpression, sql_types, backend::Backend}; +/// # use schema::users; +/// # use std::io::Write; +/// # +/// #[derive(Debug, FromSqlRow, AsExpression)] +/// #[sql_type = "sql_types::Text"] +/// struct UppercaseString(pub String); +/// +/// impl Into for String { +/// fn into(self) -> UppercaseString { +/// UppercaseString(self.to_uppercase()) +/// } +/// } +/// +/// impl ToSql for UppercaseString +/// where +/// DB: Backend, +/// String: ToSql, +/// { +/// fn to_sql(&self, out: &mut Output) -> serialize::Result { +/// self.0.to_sql(out) +/// } +/// } +/// +/// #[derive(Insertable, PartialEq, Debug)] +/// #[table_name = "users"] +/// struct InsertableUser { +/// id: i32, +/// #[diesel(serialize_as = "UppercaseString")] +/// name: String, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use schema::users::dsl::*; +/// # let connection = connection_no_data(); +/// # connection.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name VARCHAR(255) NOT NULL)").unwrap(); +/// let user = InsertableUser { +/// id: 1, +/// name: "thomas".to_string(), +/// }; +/// +/// diesel::insert_into(users) +/// .values(user) +/// .execute(&connection) +/// .unwrap(); +/// +/// assert_eq!( +/// Ok("THOMAS".to_string()), +/// users.select(name).first(&connection) +/// ); +/// # Ok(()) +/// # } +/// ``` + +#[proc_macro_derive(Insertable, attributes(table_name, column_name, diesel))] +pub fn derive_insertable(input: TokenStream) -> TokenStream { + expand_proc_macro(input, insertable::derive) +} + +#[doc(hidden)] +#[proc_macro_derive(NonAggregate)] +pub fn derive_non_aggregate(input: TokenStream) -> TokenStream { + eprintln!( + "#[derive(NonAggregate)] is deprecated. Please use \ + `#[derive(ValidGrouping)]` instead.)" + ); + expand_proc_macro(input, valid_grouping::derive) +} + +/// Implements `QueryId` +/// +/// For example, given this struct: +/// +/// ```rust +/// # extern crate diesel; +/// #[derive(diesel::query_builder::QueryId)] +/// pub struct And { +/// left: Left, +/// right: Right, +/// } +/// ``` +/// +/// the following implementation will be generated +/// +/// ```rust +/// # extern crate diesel; +/// # struct And(Left, Right); +/// # use diesel::query_builder::QueryId; +/// impl QueryId for And +/// where +/// Left: QueryId, +/// Right: QueryId, +/// { +/// type QueryId = And; +/// +/// const HAS_STATIC_QUERY_ID: bool = Left::HAS_STATIC_QUERY_ID && Right::HAS_STATIC_QUERY_ID; +/// } +/// ``` +/// +/// If the SQL generated by a struct is not uniquely identifiable by its type, +/// meaning that `HAS_STATIC_QUERY_ID` should always be false, +/// you should not derive this trait. +/// In that case you should implement it manually instead. +#[proc_macro_derive(QueryId)] +pub fn derive_query_id(input: TokenStream) -> TokenStream { + expand_proc_macro(input, query_id::derive) +} + +/// Implements `Queryable` to load the result of statically typed queries +/// +/// This trait can only be derived for structs, not enums. +/// +/// **When this trait is derived, it will assume that the order of fields on your +/// struct match the order of the fields in the query. This means that field +/// order is significant if you are using `#[derive(Queryable)]`. Field name has +/// no effect.** +/// +/// To provide custom deserialization behavior for a field, you can use +/// `#[diesel(deserialize_as = "SomeType")]`. If this attribute is present, Diesel +/// will deserialize the corresponding field into `SomeType`, rather than the +/// actual field type on your struct and then call `.into` to convert it to the +/// actual field type. This can be used to add custom behavior for a +/// single field, or use types that are otherwise unsupported by Diesel. +/// +/// # Attributes +/// +/// ## Optional field attributes: +/// +/// * `#[diesel(deserialize_as = "Type")]`, instead of deserializing directly +/// into the field type, the implementation will deserialize into `Type`. +/// Then `Type` is converted via `.into()` into the field type. By default +/// this derive will deserialize directly into the field type +/// +/// +/// # Examples +/// +/// If we just want to map a query to our struct, we can use `derive`. +/// +/// ```rust +/// # extern crate diesel; +/// # extern crate dotenv; +/// # include!("../../diesel/src/doctest_setup.rs"); +/// # +/// #[derive(Queryable, PartialEq, Debug)] +/// struct User { +/// id: i32, +/// name: String, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use schema::users::dsl::*; +/// # let connection = establish_connection(); +/// let first_user = users.first(&connection)?; +/// let expected = User { id: 1, name: "Sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +/// +/// If we want to do additional work during deserialization, we can use +/// `deserialize_as` to use a different implementation. +/// +/// ```rust +/// # extern crate diesel; +/// # extern crate dotenv; +/// # include!("../../diesel/src/doctest_setup.rs"); +/// # +/// # use schema::users; +/// # use diesel::backend::{self, Backend}; +/// # use diesel::deserialize::{Queryable, FromSql}; +/// # use diesel::sql_types::Text; +/// # +/// struct LowercaseString(String); +/// +/// impl Into for LowercaseString { +/// fn into(self) -> String { +/// self.0 +/// } +/// } +/// +/// impl Queryable for LowercaseString +/// where +/// DB: Backend, +/// String: FromSql +/// { +/// +/// type Row = String; +/// +/// fn build(s: String) -> Self { +/// LowercaseString(s.to_lowercase()) +/// } +/// } +/// +/// #[derive(Queryable, PartialEq, Debug)] +/// struct User { +/// id: i32, +/// #[diesel(deserialize_as = "LowercaseString")] +/// name: String, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use schema::users::dsl::*; +/// # let connection = establish_connection(); +/// let first_user = users.first(&connection)?; +/// let expected = User { id: 1, name: "sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +/// +/// Alternatively, we can implement the trait for our struct manually. +/// +/// ```rust +/// # extern crate diesel; +/// # extern crate dotenv; +/// # include!("../../diesel/src/doctest_setup.rs"); +/// # +/// use schema::users; +/// use diesel::deserialize::{Queryable, FromSqlRow}; +/// use diesel::row::Row; +/// +/// # /* +/// type DB = diesel::sqlite::Sqlite; +/// # */ +/// +/// #[derive(PartialEq, Debug)] +/// struct User { +/// id: i32, +/// name: String, +/// } +/// +/// impl Queryable for User +/// where +/// (i32, String): FromSqlRow, +/// { +/// type Row = (i32, String); +/// +/// fn build((id, name): Self::Row) -> Self { +/// User { id, name: name.to_lowercase() } +/// } +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # use schema::users::dsl::*; +/// # let connection = establish_connection(); +/// let first_user = users.first(&connection)?; +/// let expected = User { id: 1, name: "sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +#[proc_macro_derive(Queryable, attributes(column_name, diesel))] +pub fn derive_queryable(input: TokenStream) -> TokenStream { + expand_proc_macro(input, queryable::derive) +} + +/// Implements `QueryableByName` for untyped sql queries, such as that one generated +/// by `sql_query` +/// +/// To derive this trait, Diesel needs to know the SQL type of each field. You +/// can do this by either annotating your struct with `#[table_name = +/// "some_table"]` (in which case the SQL type will be +/// `diesel::dsl::SqlTypeOf`), or by annotating each +/// field with `#[sql_type = "SomeType"]`. +/// +/// If you are using `#[table_name]`, the module for that table must be in +/// scope. For example, to derive this for a struct called `User`, you will +/// likely need a line such as `use schema::users;` +/// +/// If the name of a field on your struct is different than the column in your +/// `table!` declaration, or if you are deriving this trait on a tuple struct, +/// you can annotate the field with `#[column_name = "some_column"]`. For tuple +/// structs, all fields must have this annotation. +/// +/// If a field is another struct which implements `QueryableByName`, +/// instead of a column, you can annotate that struct with `#[diesel(embed)]`. +/// Then all fields contained by that inner struct are loaded into +/// the embedded struct. +/// +/// To provide custom deserialization behavior for a field, you can use +/// `#[diesel(deserialize_as = "SomeType")]`. If this attribute is present, Diesel +/// will deserialize the corresponding field into `SomeType`, rather than the +/// actual field type on your struct and then call `.into` to convert it to the +/// actual field type. This can be used to add custom behavior for a +/// single field, or use types that are otherwise unsupported by Diesel. +/// +/// # Attributes +/// +/// ## Type attributes +/// +/// * `#[table_name = "some_table"]`, to specify that this type contains +/// columns for the specified table. If no field attributes are specified +/// the derive will use the sql type of the corresponding column. +/// +/// ## Field attributes +/// * `#[column_name = "some_column"]`, overrides the column name for +/// a given field. If not set, the name of the field is used as column +/// name. This attribute is required on tuple structs, if +/// `#[table_name = "some_table"]` is used, otherwise it's optional. +/// * `#[sql_type = "SomeType"]`, assumes `SomeType` as sql type of the +/// corresponding field. This attributes has precedence over all other +/// variants to specify the sql type. +/// * `#[diesel(deserialize_as = "Type")]`, instead of deserializing directly +/// into the field type, the implementation will deserialize into `Type`. +/// Then `Type` is converted via `.into()` into the field type. By default +/// this derive will deserialize directly into the field type +/// * `#[diesel(embed)]`, specifies that the current field maps not only +/// single database column, but is a type that implements +/// `QueryableByName` on it's own +/// +/// /// # Examples +/// +/// If we just want to map a query to our struct, we can use `derive`. +/// +/// ```rust +/// # extern crate diesel; +/// # extern crate dotenv; +/// # include!("../../diesel/src/doctest_setup.rs"); +/// # use schema::users; +/// # use diesel::sql_query; +/// # +/// #[derive(QueryableByName, PartialEq, Debug)] +/// #[table_name = "users"] +/// struct User { +/// id: i32, +/// name: String, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # let connection = establish_connection(); +/// let first_user = sql_query("SELECT * FROM users ORDER BY id LIMIT 1") +/// .get_result(&connection)?; +/// let expected = User { id: 1, name: "Sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +/// +/// If we want to do additional work during deserialization, we can use +/// `deserialize_as` to use a different implementation. +/// +/// ```rust +/// # extern crate diesel; +/// # extern crate dotenv; +/// # include!("../../diesel/src/doctest_setup.rs"); +/// # use diesel::sql_query; +/// # use schema::users; +/// # use diesel::backend::{self, Backend}; +/// # use diesel::deserialize::{self, FromSql}; +/// # +/// struct LowercaseString(String); +/// +/// impl Into for LowercaseString { +/// fn into(self) -> String { +/// self.0 +/// } +/// } +/// +/// impl FromSql for LowercaseString +/// where +/// DB: Backend, +/// String: FromSql, +/// { +/// fn from_sql(bytes: backend::RawValue) -> deserialize::Result { +/// String::from_sql(bytes) +/// .map(|s| LowercaseString(s.to_lowercase())) +/// } +/// } +/// +/// #[derive(QueryableByName, PartialEq, Debug)] +/// #[table_name = "users"] +/// struct User { +/// id: i32, +/// #[diesel(deserialize_as = "LowercaseString")] +/// name: String, +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # let connection = establish_connection(); +/// let first_user = sql_query("SELECT * FROM users ORDER BY id LIMIT 1") +/// .get_result(&connection)?; +/// let expected = User { id: 1, name: "sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +/// +/// The custom derive generates impls similar to the follownig one +/// +/// ```rust +/// # extern crate diesel; +/// # extern crate dotenv; +/// # include!("../../diesel/src/doctest_setup.rs"); +/// # use schema::users; +/// # use diesel::sql_query; +/// # use diesel::deserialize::{self, QueryableByName, FromSql}; +/// # use diesel::row::NamedRow; +/// # use diesel::backend::Backend; +/// # +/// #[derive(PartialEq, Debug)] +/// struct User { +/// id: i32, +/// name: String, +/// } +/// +/// impl QueryableByName for User +/// where +/// DB: Backend, +/// i32: FromSql, DB>, +/// String: FromSql, DB>, +/// { +/// fn build<'a>(row: &impl NamedRow<'a, DB>) -> deserialize::Result { +/// let id = NamedRow::get::, _>(row, "id")?; +/// let name = NamedRow::get::, _>(row, "name")?; +/// +/// Ok(Self { id, name }) +/// } +/// } +/// +/// # fn main() { +/// # run_test(); +/// # } +/// # +/// # fn run_test() -> QueryResult<()> { +/// # let connection = establish_connection(); +/// let first_user = sql_query("SELECT * FROM users ORDER BY id LIMIT 1") +/// .get_result(&connection)?; +/// let expected = User { id: 1, name: "Sean".into() }; +/// assert_eq!(expected, first_user); +/// # Ok(()) +/// # } +/// ``` +#[proc_macro_derive(QueryableByName, attributes(table_name, column_name, sql_type, diesel))] +pub fn derive_queryable_by_name(input: TokenStream) -> TokenStream { + expand_proc_macro(input, queryable_by_name::derive) +} + +/// Implement necessary traits for adding a new sql type +/// +/// This trait implements all necessary traits to define a +/// new sql type. This is useful for adding support for unsupported +/// or custom types on sql side. The sql type will be usable for +/// all backends you specified via the attributes listed below. +/// +/// This derive will implement `NotNull`, `HasSqlType` and `SingleValue`. +/// When using this deriving, +/// you need to specify how the type is represented on various backends. +/// You don't need to specify every backend, +/// only the ones supported by your type. +/// +/// For PostgreSQL, add `#[postgres(type_name = "pg_type_name")]` +/// or `#[postgres(oid = "some_oid", array_oid = "some_oid")]` for +/// builtin types. +/// For MySQL, specify which variant of `MysqlType` should be used +/// by adding `#[mysql_type = "Variant"]`. +/// For SQLite, specify which variant of `SqliteType` should be used +/// by adding `#[sqlite_type = "Variant"]`. +/// +/// # Attributes +/// +/// ## Type attributes +/// +/// * `#[postgres(type_name = "TypeName")]` specifies support for +/// a postgresql type with the name `TypeName`. Prefer this variant +/// for types with no stable OID (== everything but the builtin types) +/// * `#[postgres(oid = 42, array_oid = 142)]`, specifies support for a +/// postgresql type with the given `oid` and `array_oid`. This variant +/// should only be used with types that have a stable OID. +/// * `#[sqlite_type = "TypeName"]`, specifies support for a sqlite type +/// with the given name. `TypeName` needs to be one of the possible values +/// in `SqliteType` +/// * `#[mysql_type = "TypeName"]`, specifies support for a mysql type +/// with the given name. `TypeName` needs to be one of the possible values +/// in `MysqlType` +#[proc_macro_derive(SqlType, attributes(postgres, sqlite_type, mysql_type))] +pub fn derive_sql_type(input: TokenStream) -> TokenStream { + expand_proc_macro(input, sql_type::derive) +} + +/// Implements `ValidGrouping` +/// +/// This trait can be automatically derived for structs with no type parameters +/// which are never aggregate, as well as for structs which are `NonAggregate` +/// when all type parameters are `NonAggregate`. For example: +/// +/// ```ignore +/// #[derive(ValidGrouping)] +/// struct LiteralOne; +/// +/// #[derive(ValidGrouping)] +/// struct Plus(Lhs, Rhs); +/// +/// // The following impl will be generated: +/// +/// impl ValidGrouping for LiteralOne { +/// type IsAggregate = is_aggregate::Never; +/// } +/// +/// impl ValidGrouping for Plus +/// where +/// Lhs: ValidGrouping, +/// Rhs: ValidGrouping, +/// Lhs::IsAggregate: MixedAggregates, +/// { +/// type IsAggregate = >::Output; +/// } +/// ``` +/// +/// For types which are always considered aggregate (such as an aggregate +/// function), annotate your struct with `#[diesel(aggregate)]` to set `IsAggregate` +/// explicitly to `is_aggregate::Yes`. +/// +/// # Attributes +/// +/// ## Optional type attributes +/// +/// * `#[diesel(aggregate)]` for cases where the type represents an aggregating +/// SQL expression +#[proc_macro_derive(ValidGrouping, attributes(diesel))] +pub fn derive_valid_grouping(input: TokenStream) -> TokenStream { + expand_proc_macro(input, valid_grouping::derive) +} + +/// Declare a sql function for use in your code. +/// +/// Diesel only provides support for a very small number of SQL functions. +/// This macro enables you to add additional functions from the SQL standard, +/// as well as any custom functions your application might have. +/// +/// The syntax for this macro is very similar to that of a normal Rust function, +/// except the argument and return types will be the SQL types being used. +/// Typically these types will come from [`diesel::sql_types`](../diesel/sql_types/index.html) +/// +/// This macro will generate two items. A function with the name that you've +/// given, and a module with a helper type representing the return type of your +/// function. For example, this invocation: +/// +/// ```ignore +/// sql_function!(fn lower(x: Text) -> Text); +/// ``` +/// +/// will generate this code: +/// +/// ```ignore +/// pub fn lower(x: X) -> lower::HelperType { +/// ... +/// } +/// +/// pub(crate) mod lower { +/// pub type HelperType = ...; +/// } +/// ``` +/// +/// If you are using this macro for part of a library, where the function is +/// part of your public API, it is highly recommended that you re-export this +/// helper type with the same name as your function. This is the standard +/// structure: +/// +/// ```ignore +/// pub mod functions { +/// use super::types::*; +/// use diesel::sql_types::*; +/// +/// sql_function! { +/// /// Represents the Pg `LENGTH` function used with `tsvector`s. +/// fn length(x: TsVector) -> Integer; +/// } +/// } +/// +/// pub mod helper_types { +/// /// The return type of `length(expr)` +/// pub type Length = functions::length::HelperType; +/// } +/// +/// pub mod dsl { +/// pub use functions::*; +/// pub use helper_types::*; +/// } +/// ``` +/// +/// Most attributes given to this macro will be put on the generated function +/// (including doc comments). +/// +/// # Adding Doc Comments +/// +/// ```no_run +/// # extern crate diesel; +/// # use diesel::*; +/// # +/// # table! { crates { id -> Integer, name -> VarChar, } } +/// # +/// use diesel::sql_types::Text; +/// +/// sql_function! { +/// /// Represents the `canon_crate_name` SQL function, created in +/// /// migration .... +/// fn canon_crate_name(a: Text) -> Text; +/// } +/// +/// # fn main() { +/// # use self::crates::dsl::*; +/// let target_name = "diesel"; +/// crates.filter(canon_crate_name(name).eq(canon_crate_name(target_name))); +/// // This will generate the following SQL +/// // SELECT * FROM crates WHERE canon_crate_name(crates.name) = canon_crate_name($1) +/// # } +/// ``` +/// +/// # Special Attributes +/// +/// There are a handful of special attributes that Diesel will recognize. They +/// are: +/// +/// - `#[aggregate]` +/// - Indicates that this is an aggregate function, and that `NonAggregate` +/// should not be implemented. +/// - `#[sql_name="name"]` +/// - The SQL to be generated is different than the Rust name of the function. +/// This can be used to represent functions which can take many argument +/// types, or to capitalize function names. +/// +/// Functions can also be generic. Take the definition of `sum` for an example +/// of all of this: +/// +/// ```no_run +/// # extern crate diesel; +/// # use diesel::*; +/// # +/// # table! { crates { id -> Integer, name -> VarChar, } } +/// # +/// use diesel::sql_types::{Foldable, Nullable}; +/// +/// sql_function! { +/// #[aggregate] +/// #[sql_name = "SUM"] +/// fn sum(expr: Nullable) -> ST::Sum; +/// } +/// +/// # fn main() { +/// # use self::crates::dsl::*; +/// crates.select(sum(id)); +/// # } +/// ``` +/// +/// # SQL Functions without Arguments +/// +/// A common example is ordering a query using the `RANDOM()` sql function, +/// which can be implemented using `sql_function!` like this: +/// +/// ```rust +/// # extern crate diesel; +/// # use diesel::*; +/// # +/// # table! { crates { id -> Integer, name -> VarChar, } } +/// # +/// sql_function!(fn random() -> Text); +/// +/// # fn main() { +/// # use self::crates::dsl::*; +/// crates.order(random()); +/// # } +/// ``` +/// +/// # Use with SQLite +/// +/// On most backends, the implementation of the function is defined in a +/// migration using `CREATE FUNCTION`. On SQLite, the function is implemented in +/// Rust instead. You must call `register_impl` or +/// `register_nondeterministic_impl` with every connection before you can use +/// the function. +/// +/// These functions will only be generated if the `sqlite` feature is enabled, +/// and the function is not generic. Generic functions and variadic functions +/// are not supported on SQLite. +/// +/// ```rust +/// # extern crate diesel; +/// # use diesel::*; +/// # +/// # #[cfg(feature = "sqlite")] +/// # fn main() { +/// # run_test().unwrap(); +/// # } +/// # +/// # #[cfg(not(feature = "sqlite"))] +/// # fn main() { +/// # } +/// # +/// use diesel::sql_types::{Integer, Double}; +/// sql_function!(fn add_mul(x: Integer, y: Integer, z: Double) -> Double); +/// +/// # #[cfg(feature = "sqlite")] +/// # fn run_test() -> Result<(), Box<::std::error::Error>> { +/// let connection = SqliteConnection::establish(":memory:")?; +/// +/// add_mul::register_impl(&connection, |x: i32, y: i32, z: f64| { +/// (x + y) as f64 * z +/// })?; +/// +/// let result = select(add_mul(1, 2, 1.5)) +/// .get_result::(&connection)?; +/// assert_eq!(4.5, result); +/// # Ok(()) +/// # } +/// ``` +/// +/// ## Custom Aggregate Functions +/// +/// Custom aggregate functions can be created in SQLite by adding an `#[aggregate]` +/// attribute inside of `sql_function`. `register_impl` needs to be called on +/// the generated function with a type implementing the +/// [SqliteAggregateFunction](../diesel/sqlite/trait.SqliteAggregateFunction.html) +/// trait as a type parameter as shown in the examples below. +/// +/// ```rust +/// # extern crate diesel; +/// # use diesel::*; +/// # +/// # #[cfg(feature = "sqlite")] +/// # fn main() { +/// # run().unwrap(); +/// # } +/// # +/// # #[cfg(not(feature = "sqlite"))] +/// # fn main() { +/// # } +/// use diesel::sql_types::Integer; +/// # #[cfg(feature = "sqlite")] +/// use diesel::sqlite::SqliteAggregateFunction; +/// +/// sql_function! { +/// #[aggregate] +/// fn my_sum(x: Integer) -> Integer; +/// } +/// +/// #[derive(Default)] +/// struct MySum { sum: i32 } +/// +/// # #[cfg(feature = "sqlite")] +/// impl SqliteAggregateFunction for MySum { +/// type Output = i32; +/// +/// fn step(&mut self, expr: i32) { +/// self.sum += expr; +/// } +/// +/// fn finalize(aggregator: Option) -> Self::Output { +/// aggregator.map(|a| a.sum).unwrap_or_default() +/// } +/// } +/// # table! { +/// # players { +/// # id -> Integer, +/// # score -> Integer, +/// # } +/// # } +/// +/// # #[cfg(feature = "sqlite")] +/// fn run() -> Result<(), Box> { +/// # use self::players::dsl::*; +/// let connection = SqliteConnection::establish(":memory:")?; +/// # connection.execute("create table players (id integer primary key autoincrement, score integer)").unwrap(); +/// # connection.execute("insert into players (score) values (10), (20), (30)").unwrap(); +/// +/// my_sum::register_impl::(&connection)?; +/// +/// let total_score = players.select(my_sum(score)) +/// .get_result::(&connection)?; +/// +/// println!("The total score of all the players is: {}", total_score); +/// +/// # assert_eq!(60, total_score); +/// Ok(()) +/// } +/// ``` +/// +/// With multiple function arguments the arguments are passed as a tuple to `SqliteAggregateFunction` +/// +/// ```rust +/// # extern crate diesel; +/// # use diesel::*; +/// # +/// # #[cfg(feature = "sqlite")] +/// # fn main() { +/// # run().unwrap(); +/// # } +/// # +/// # #[cfg(not(feature = "sqlite"))] +/// # fn main() { +/// # } +/// use diesel::sql_types::{Float, Nullable}; +/// # #[cfg(feature = "sqlite")] +/// use diesel::sqlite::SqliteAggregateFunction; +/// +/// sql_function! { +/// #[aggregate] +/// fn range_max(x0: Float, x1: Float) -> Nullable; +/// } +/// +/// #[derive(Default)] +/// struct RangeMax { max_value: Option } +/// +/// # #[cfg(feature = "sqlite")] +/// impl SqliteAggregateFunction<(T, T)> for RangeMax { +/// type Output = Option; +/// +/// fn step(&mut self, (x0, x1): (T, T)) { +/// # let max = if x0 >= x1 { +/// # x0 +/// # } else { +/// # x1 +/// # }; +/// # +/// # self.max_value = match self.max_value { +/// # Some(current_max_value) if max > current_max_value => Some(max), +/// # None => Some(max), +/// # _ => self.max_value, +/// # }; +/// // Compare self.max_value to x0 and x1 +/// } +/// +/// fn finalize(aggregator: Option) -> Self::Output { +/// aggregator?.max_value +/// } +/// } +/// # table! { +/// # student_avgs { +/// # id -> Integer, +/// # s1_avg -> Float, +/// # s2_avg -> Float, +/// # } +/// # } +/// +/// # #[cfg(feature = "sqlite")] +/// fn run() -> Result<(), Box> { +/// # use self::student_avgs::dsl::*; +/// let connection = SqliteConnection::establish(":memory:")?; +/// # connection.execute("create table student_avgs (id integer primary key autoincrement, s1_avg float, s2_avg float)").unwrap(); +/// # connection.execute("insert into student_avgs (s1_avg, s2_avg) values (85.5, 90), (79.8, 80.1)").unwrap(); +/// +/// range_max::register_impl::, _, _>(&connection)?; +/// +/// let result = student_avgs.select(range_max(s1_avg, s2_avg)) +/// .get_result::>(&connection)?; +/// +/// if let Some(max_semeseter_avg) = result { +/// println!("The largest semester average is: {}", max_semeseter_avg); +/// } +/// +/// # assert_eq!(Some(90f32), result); +/// Ok(()) +/// } +/// ``` +#[proc_macro] +pub fn sql_function_proc(input: TokenStream) -> TokenStream { + expand_proc_macro(input, sql_function::expand) +} + +fn expand_proc_macro( + input: TokenStream, + f: fn(T) -> Result, +) -> TokenStream { + let item = syn::parse(input).unwrap(); + match f(item) { + Ok(x) => x.into(), + Err(e) => { + e.emit(); + "".parse().unwrap() + } + } +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/meta.rs b/collector/benchmarks/diesel/diesel_derives/src/meta.rs new file mode 100644 index 000000000..9d045de26 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/meta.rs @@ -0,0 +1,272 @@ +use proc_macro2::{Ident, Span}; +use syn; +use syn::fold::Fold; +use syn::spanned::Spanned; + +use resolved_at_shim::*; +use util::*; + +pub struct MetaItem { + meta: syn::Meta, +} + +pub(crate) fn path_to_string(path: &syn::Path) -> String { + path.segments + .iter() + .map(|s| s.ident.to_string()) + .collect::>() + .join("::") +} + +impl MetaItem { + pub fn all_with_name(attrs: &[syn::Attribute], name: &str) -> Vec { + attrs + .iter() + .filter_map(|attr| { + attr.parse_meta() + .ok() + .map(|m| FixSpan(attr.pound_token.spans[0]).fold_meta(m)) + }) + .filter(|m| m.path().is_ident(name)) + .map(|meta| Self { meta }) + .collect() + } + + pub fn with_name(attrs: &[syn::Attribute], name: &str) -> Option { + Self::all_with_name(attrs, name).pop() + } + + pub fn empty(name: &str) -> Self { + Self { + meta: syn::Meta::List(syn::MetaList { + path: syn::Path::from(Ident::new(name, Span::call_site())), + paren_token: Default::default(), + nested: Default::default(), + }), + } + } + + pub fn nested_item(&self, name: &str) -> Result, Diagnostic> { + self.nested() + .map(|mut i| i.find(|n| n.name().is_ident(name))) + } + + pub fn required_nested_item(&self, name: &str) -> Result { + self.nested_item(name)?.ok_or_else(|| { + self.span() + .error(format!("Missing required option `{}`", name)) + }) + } + + pub fn expect_bool_value(&self) -> bool { + match self.str_value().as_ref().map(String::as_str) { + Ok("true") => true, + Ok("false") => false, + _ => { + self.span() + .error(format!( + "`{0}` must be in the form `{0} = \"true\"`", + path_to_string(&self.name()) + )) + .emit(); + false + } + } + } + + pub fn expect_ident_value(&self) -> syn::Ident { + self.ident_value().unwrap_or_else(|e| { + e.emit(); + self.name().segments.first().unwrap().ident.clone() + }) + } + + pub fn ident_value(&self) -> Result { + let maybe_attr = self.nested().ok().and_then(|mut n| n.next()); + let maybe_path = maybe_attr.as_ref().and_then(|m| m.path().ok()); + match maybe_path { + Some(x) => { + self.span() + .warning(format!( + "The form `{0}(value)` is deprecated. Use `{0} = \"value\"` instead", + path_to_string(&self.name()), + )) + .emit(); + Ok(x.segments.first().unwrap().ident.clone()) + } + _ => Ok(syn::Ident::new( + &self.str_value()?, + self.value_span().resolved_at(Span::call_site()), + )), + } + } + + pub fn expect_path(&self) -> syn::Path { + self.path().unwrap_or_else(|e| { + e.emit(); + self.name() + }) + } + + pub fn path(&self) -> Result { + use syn::Meta::*; + + match self.meta { + Path(ref x) => Ok(x.clone()), + _ => { + let meta = &self.meta; + Err(self.span().error(format!( + "Expected `{}` found `{}`", + path_to_string(&self.name()), + quote!(#meta) + ))) + } + } + } + + pub fn nested(&self) -> Result { + use syn::Meta::*; + + match self.meta { + List(ref list) => Ok(Nested(list.nested.iter())), + _ => Err(self.span().error(format!( + "`{0}` must be in the form `{0}(...)`", + path_to_string(&self.name()) + ))), + } + } + + pub fn name(&self) -> syn::Path { + self.meta.path().clone() + } + + pub fn has_flag(&self, flag: &str) -> bool { + self.nested() + .map(|mut n| { + n.any(|m| match m.path() { + Ok(word) => word.is_ident(flag), + Err(_) => false, + }) + }) + .unwrap_or_else(|e| { + e.emit(); + false + }) + } + + pub fn ty_value(&self) -> Result { + let str = self.lit_str_value()?; + str.parse() + .map_err(|_| str.span().error("Invalid Rust type")) + } + + pub fn expect_str_value(&self) -> String { + self.str_value().unwrap_or_else(|e| { + e.emit(); + path_to_string(&self.name()) + }) + } + + pub fn str_value(&self) -> Result { + self.lit_str_value().map(syn::LitStr::value) + } + + fn lit_str_value(&self) -> Result<&syn::LitStr, Diagnostic> { + use syn::Lit::*; + + match *self.lit_value()? { + Str(ref s) => Ok(s), + _ => Err(self.span().error(format!( + "`{0}` must be in the form `{0} = \"value\"`", + path_to_string(&self.name()) + ))), + } + } + + pub fn expect_int_value(&self) -> u64 { + self.int_value().emit_error().unwrap_or(0) + } + + pub fn int_value(&self) -> Result { + use syn::Lit::*; + + let error = self.value_span().error("Expected a number"); + + match *self.lit_value()? { + Str(ref s) => s.value().parse().map_err(|_| error), + Int(ref i) => i.base10_parse().map_err(|_| error), + _ => Err(error), + } + } + + fn lit_value(&self) -> Result<&syn::Lit, Diagnostic> { + use syn::Meta::*; + + match self.meta { + NameValue(ref name_value) => Ok(&name_value.lit), + _ => Err(self.span().error(format!( + "`{0}` must be in the form `{0} = \"value\"`", + path_to_string(&self.name()) + ))), + } + } + + pub fn warn_if_other_options(&self, options: &[&str]) { + let nested = match self.nested() { + Ok(x) => x, + Err(_) => return, + }; + let unrecognized_options = + nested.filter(|n| !options.iter().any(|&o| n.name().is_ident(o))); + for ignored in unrecognized_options { + ignored + .span() + .warning(format!( + "Option {} has no effect", + path_to_string(&ignored.name()) + )) + .emit(); + } + } + + fn value_span(&self) -> Span { + use syn::Meta::*; + + match self.meta { + Path(ref path) => path.span(), + List(ref meta) => meta.nested.span(), + NameValue(ref meta) => meta.lit.span(), + } + } + + pub fn span(&self) -> Span { + self.meta.span() + } +} + +pub struct Nested<'a>(syn::punctuated::Iter<'a, syn::NestedMeta>); + +impl<'a> Iterator for Nested<'a> { + type Item = MetaItem; + + fn next(&mut self) -> Option { + use syn::NestedMeta::*; + + match self.0.next() { + Some(&Meta(ref item)) => Some(MetaItem { meta: item.clone() }), + Some(_) => self.next(), + None => None, + } + } +} + +/// If the given span is affected by +/// , +/// returns the span of the pound token +struct FixSpan(Span); + +impl Fold for FixSpan { + fn fold_span(&mut self, span: Span) -> Span { + fix_span(span, self.0) + } +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/model.rs b/collector/benchmarks/diesel/diesel_derives/src/model.rs new file mode 100644 index 000000000..b3c526d19 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/model.rs @@ -0,0 +1,112 @@ +use proc_macro2::{Ident, Span}; +use syn; + +use diagnostic_shim::*; +use field::*; +use meta::*; +use resolved_at_shim::*; + +pub struct Model { + pub name: syn::Ident, + pub primary_key_names: Vec, + table_name_from_attribute: Option, + fields: Vec, +} + +impl Model { + pub fn from_item(item: &syn::DeriveInput) -> Result { + let table_name_from_attribute = + MetaItem::with_name(&item.attrs, "table_name").map(|m| m.expect_ident_value()); + let primary_key_names = MetaItem::with_name(&item.attrs, "primary_key") + .map(|m| { + Ok(m.nested()? + .map(|m| m.expect_path().segments.first().unwrap().ident.clone()) + .collect()) + }) + .unwrap_or_else(|| Ok(vec![Ident::new("id", Span::call_site())]))?; + let fields = fields_from_item_data(&item.data)?; + Ok(Self { + name: item.ident.clone(), + table_name_from_attribute, + primary_key_names, + fields, + }) + } + + pub fn table_name(&self) -> syn::Ident { + self.table_name_from_attribute.clone().unwrap_or_else(|| { + syn::Ident::new( + &infer_table_name(&self.name.to_string()), + self.name.span().resolved_at(Span::call_site()), + ) + }) + } + + pub fn fields(&self) -> &[Field] { + &self.fields + } + + pub fn find_column(&self, column_name: &syn::Ident) -> Result<&Field, Diagnostic> { + self.fields() + .iter() + .find(|f| &f.column_name() == column_name) + .ok_or_else(|| { + column_name + .span() + .error(format!("No field with column name {}", column_name)) + }) + } + + pub fn has_table_name_attribute(&self) -> bool { + self.table_name_from_attribute.is_some() + } +} + +pub fn camel_to_snake(name: &str) -> String { + let mut result = String::with_capacity(name.len()); + result.push_str(&name[..1].to_lowercase()); + for character in name[1..].chars() { + if character.is_uppercase() { + result.push('_'); + for lowercase in character.to_lowercase() { + result.push(lowercase); + } + } else { + result.push(character); + } + } + result +} + +fn infer_table_name(name: &str) -> String { + let mut result = camel_to_snake(name); + result.push('s'); + result +} + +fn fields_from_item_data(data: &syn::Data) -> Result, Diagnostic> { + use syn::Data::*; + + let struct_data = match *data { + Struct(ref d) => d, + _ => return Err(Span::call_site().error("This derive can only be used on structs")), + }; + Ok(struct_data + .fields + .iter() + .enumerate() + .map(|(i, f)| Field::from_struct_field(f, i)) + .collect()) +} + +#[test] +fn infer_table_name_pluralizes_and_downcases() { + assert_eq!("foos", &infer_table_name("Foo")); + assert_eq!("bars", &infer_table_name("Bar")); +} + +#[test] +fn infer_table_name_properly_handles_underscores() { + assert_eq!("foo_bars", &infer_table_name("FooBar")); + assert_eq!("foo_bar_bazs", &infer_table_name("FooBarBaz")); +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/query_id.rs b/collector/benchmarks/diesel/diesel_derives/src/query_id.rs new file mode 100644 index 000000000..bfa561ca0 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/query_id.rs @@ -0,0 +1,37 @@ +use proc_macro2; +use syn; + +use util::*; + +pub fn derive(mut item: syn::DeriveInput) -> Result { + for ty_param in item.generics.type_params_mut() { + ty_param.bounds.push(parse_quote!(QueryId)); + } + let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); + + let struct_name = &item.ident; + let lifetimes = item.generics.lifetimes(); + let query_id_ty_params = item + .generics + .type_params() + .map(|ty_param| &ty_param.ident) + .map(|ty_param| quote!(<#ty_param as QueryId>::QueryId)); + let has_static_query_id = item + .generics + .type_params() + .map(|ty_param| &ty_param.ident) + .map(|ty_param| quote!(<#ty_param as QueryId>::HAS_STATIC_QUERY_ID)); + + Ok(wrap_in_dummy_mod(quote! { + use diesel::query_builder::QueryId; + + #[allow(non_camel_case_types)] + impl #impl_generics QueryId for #struct_name #ty_generics + #where_clause + { + type QueryId = #struct_name<#(#lifetimes,)* #(#query_id_ty_params,)*>; + + const HAS_STATIC_QUERY_ID: bool = #(#has_static_query_id &&)* true; + } + })) +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/queryable.rs b/collector/benchmarks/diesel/diesel_derives/src/queryable.rs new file mode 100644 index 000000000..388a2f880 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/queryable.rs @@ -0,0 +1,63 @@ +use proc_macro2; +use syn; + +use field::Field; +use model::*; +use util::*; + +pub fn derive(item: syn::DeriveInput) -> Result { + let model = Model::from_item(&item)?; + + let struct_name = &item.ident; + let field_ty = model + .fields() + .iter() + .map(Field::ty_for_deserialize) + .collect::, _>>()?; + let field_ty = &field_ty; + let build_expr = model.fields().iter().enumerate().map(|(i, f)| { + let i = syn::Index::from(i); + f.name.assign(parse_quote!(row.#i.into())) + }); + let sql_type = (0..model.fields().len()) + .map(|i| { + let i = syn::Ident::new(&format!("__ST{}", i), proc_macro2::Span::call_site()); + quote!(#i) + }) + .collect::>(); + let sql_type = &sql_type; + + let (_, ty_generics, _) = item.generics.split_for_impl(); + let mut generics = item.generics.clone(); + generics + .params + .push(parse_quote!(__DB: diesel::backend::Backend)); + for id in 0..model.fields().len() { + let ident = syn::Ident::new(&format!("__ST{}", id), proc_macro2::Span::call_site()); + generics.params.push(parse_quote!(#ident)); + } + { + let where_clause = generics.where_clause.get_or_insert(parse_quote!(where)); + where_clause + .predicates + .push(parse_quote!((#(#field_ty,)*): FromStaticSqlRow<(#(#sql_type,)*), __DB>)); + } + let (impl_generics, _, where_clause) = generics.split_for_impl(); + + Ok(wrap_in_dummy_mod(quote! { + use diesel::deserialize::{FromStaticSqlRow, Queryable}; + use diesel::row::{Row, Field}; + + impl #impl_generics Queryable<(#(#sql_type,)*), __DB> for #struct_name #ty_generics + #where_clause + { + type Row = (#(#field_ty,)*); + + fn build(row: Self::Row) -> Self { + Self { + #(#build_expr,)* + } + } + } + })) +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/queryable_by_name.rs b/collector/benchmarks/diesel/diesel_derives/src/queryable_by_name.rs new file mode 100644 index 000000000..bc954d5b7 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/queryable_by_name.rs @@ -0,0 +1,120 @@ +use proc_macro2::{self, Ident, Span}; +use syn; + +use field::*; +use model::*; +use util::*; + +pub fn derive(item: syn::DeriveInput) -> Result { + let model = Model::from_item(&item)?; + + let struct_name = &item.ident; + let fields = model.fields().iter().map(get_ident).collect::>(); + let field_names = model.fields().iter().map(|f| &f.name).collect::>(); + + let initial_field_expr = model + .fields() + .iter() + .map(|f| { + if f.has_flag("embed") { + let field_ty = &f.ty; + Ok(quote!(<#field_ty as QueryableByName<__DB>>::build( + row, + )?)) + } else { + let name = f.column_name(); + let field_ty = &f.ty; + let deserialize_ty = f.ty_for_deserialize()?; + Ok(quote!( + { + let field = diesel::row::NamedRow::get(row, stringify!(#name))?; + <#deserialize_ty as Into<#field_ty>>::into(field) + } + )) + } + }) + .collect::, Diagnostic>>()?; + + let (_, ty_generics, ..) = item.generics.split_for_impl(); + let mut generics = item.generics.clone(); + generics + .params + .push(parse_quote!(__DB: diesel::backend::Backend)); + + for field in model.fields() { + let where_clause = generics.where_clause.get_or_insert(parse_quote!(where)); + let field_ty = field.ty_for_deserialize()?; + if field.has_flag("embed") { + where_clause + .predicates + .push(parse_quote!(#field_ty: QueryableByName<__DB>)); + } else { + let st = sql_type(field, &model); + where_clause + .predicates + .push(parse_quote!(#field_ty: diesel::deserialize::FromSql<#st, __DB>)); + } + } + + let (impl_generics, _, where_clause) = generics.split_for_impl(); + + Ok(wrap_in_dummy_mod(quote! { + use diesel::deserialize::{self, QueryableByName}; + use diesel::row::{NamedRow}; + use diesel::sql_types::Untyped; + + impl #impl_generics QueryableByName<__DB> + for #struct_name #ty_generics + #where_clause + { + fn build<'__a>(row: &impl NamedRow<'__a, __DB>) -> deserialize::Result + { + + + #( + let mut #fields = #initial_field_expr; + )* + deserialize::Result::Ok(Self { + #( + #field_names: #fields, + )* + }) + } + } + })) +} + +fn get_ident(field: &Field) -> Ident { + match &field.name { + FieldName::Named(n) => n.clone(), + FieldName::Unnamed(i) => Ident::new(&format!("field_{}", i.index), Span::call_site()), + } +} + +fn sql_type(field: &Field, model: &Model) -> syn::Type { + let table_name = model.table_name(); + let column_name = field.column_name(); + + match field.sql_type { + Some(ref st) => st.clone(), + None => { + if model.has_table_name_attribute() { + parse_quote!(diesel::dsl::SqlTypeOf<#table_name::#column_name>) + } else { + let field_name = match field.name { + FieldName::Named(ref x) => x.clone(), + _ => Ident::new("field", Span::call_site()), + }; + field + .span + .error(format!("Cannot determine the SQL type of {}", field_name)) + .help( + "Your struct must either be annotated with `#[table_name = \"foo\"]` \ + or have all of its fields annotated with `#[sql_type = \"Integer\"]`", + ) + .emit(); + parse_quote!(()) + } + } + } +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/resolved_at_shim.rs b/collector/benchmarks/diesel/diesel_derives/src/resolved_at_shim.rs new file mode 100644 index 000000000..280873344 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/resolved_at_shim.rs @@ -0,0 +1,19 @@ +use proc_macro2::Span; + +pub trait ResolvedAtExt { + fn resolved_at(self, span: Span) -> Span; +} + +#[cfg(feature = "nightly")] +impl ResolvedAtExt for Span { + fn resolved_at(self, span: Span) -> Span { + self.unstable().resolved_at(span.unstable()).into() + } +} + +#[cfg(not(feature = "nightly"))] +impl ResolvedAtExt for Span { + fn resolved_at(self, _: Span) -> Span { + self + } +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/sql_function.rs b/collector/benchmarks/diesel/diesel_derives/src/sql_function.rs new file mode 100644 index 000000000..92813b570 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/sql_function.rs @@ -0,0 +1,384 @@ +use proc_macro2::*; +use quote::ToTokens; +use syn::parse::{self, Parse, ParseStream}; +use syn::punctuated::Punctuated; + +use meta::*; +use util::*; + +// Extremely curious why this triggers on a nearly branchless function +#[allow(clippy::cognitive_complexity)] +// for loop comes from `quote!` +#[allow(clippy::for_loop_over_option)] +// https://github.com/rust-lang/rust-clippy/issues/3768 +#[allow(clippy::useless_let_if_seq)] +pub(crate) fn expand(input: SqlFunctionDecl) -> Result { + let SqlFunctionDecl { + mut attributes, + fn_token, + fn_name, + mut generics, + args, + return_type, + } = input; + + let sql_name = MetaItem::with_name(&attributes, "sql_name") + .map(|m| m.str_value()) + .unwrap_or_else(|| Ok(fn_name.to_string()))?; + let is_aggregate = MetaItem::with_name(&attributes, "aggregate").is_some(); + + attributes.retain(|attr| { + attr.parse_meta() + .map(|m| !m.path().is_ident("sql_name") && !m.path().is_ident("aggregate")) + .unwrap_or(true) + }); + + let args = &args; + let (ref arg_name, ref arg_type): (Vec<_>, Vec<_>) = args + .iter() + .map(|StrictFnArg { name, ty, .. }| (name, ty)) + .unzip(); + let arg_struct_assign = args.iter().map( + |StrictFnArg { + name, colon_token, .. + }| { + let name2 = name.clone(); + quote!(#name #colon_token #name2.as_expression()) + }, + ); + + let type_args = &generics + .type_params() + .map(|type_param| type_param.ident.clone()) + .collect::>(); + + for StrictFnArg { name, .. } in args { + generics.params.push(parse_quote!(#name)); + } + + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + // Even if we force an empty where clause, it still won't print the where + // token with no bounds. + let where_clause = where_clause + .map(|w| quote!(#w)) + .unwrap_or_else(|| quote!(where)); + + let mut generics_with_internal = generics.clone(); + generics_with_internal + .params + .push(parse_quote!(__DieselInternal)); + let (impl_generics_internal, _, _) = generics_with_internal.split_for_impl(); + + let sql_type; + let numeric_derive; + + if arg_name.is_empty() { + sql_type = None; + // FIXME: We can always derive once trivial bounds are stable + numeric_derive = None; + } else { + sql_type = Some(quote!((#(#arg_name),*): Expression,)); + numeric_derive = Some(quote!(#[derive(diesel::sql_types::DieselNumericOps)])); + } + + let args_iter = args.iter(); + let mut tokens = quote! { + use diesel::{self, QueryResult}; + use diesel::expression::{AsExpression, Expression, SelectableExpression, AppearsOnTable, ValidGrouping}; + use diesel::query_builder::{QueryFragment, AstPass}; + use diesel::sql_types::*; + use super::*; + + #[derive(Debug, Clone, Copy, diesel::query_builder::QueryId)] + #numeric_derive + pub struct #fn_name #ty_generics { + #(pub(in super) #args_iter,)* + #(pub(in super) #type_args: ::std::marker::PhantomData<#type_args>,)* + } + + pub type HelperType #ty_generics = #fn_name < + #(#type_args,)* + #(<#arg_name as AsExpression<#arg_type>>::Expression,)* + >; + + impl #impl_generics Expression for #fn_name #ty_generics + #where_clause + #sql_type + { + type SqlType = #return_type; + } + + // __DieselInternal is what we call QS normally + impl #impl_generics_internal SelectableExpression<__DieselInternal> + for #fn_name #ty_generics + #where_clause + #(#arg_name: SelectableExpression<__DieselInternal>,)* + Self: AppearsOnTable<__DieselInternal>, + { + } + + // __DieselInternal is what we call QS normally + impl #impl_generics_internal AppearsOnTable<__DieselInternal> + for #fn_name #ty_generics + #where_clause + #(#arg_name: AppearsOnTable<__DieselInternal>,)* + Self: Expression, + { + } + + // __DieselInternal is what we call DB normally + impl #impl_generics_internal QueryFragment<__DieselInternal> + for #fn_name #ty_generics + where + __DieselInternal: diesel::backend::Backend, + for<'a> (#(&'a #arg_name),*): QueryFragment<__DieselInternal>, + { + fn walk_ast(&self, mut out: AstPass<__DieselInternal>) -> QueryResult<()> { + out.push_sql(concat!(#sql_name, "(")); + (#(&self.#arg_name,)*).walk_ast(out.reborrow())?; + out.push_sql(")"); + Ok(()) + } + } + }; + + if is_aggregate { + tokens = quote! { + #tokens + + impl #impl_generics_internal ValidGrouping<__DieselInternal> + for #fn_name #ty_generics + { + type IsAggregate = diesel::expression::is_aggregate::Yes; + } + }; + if cfg!(feature = "sqlite") && type_args.is_empty() { + tokens = quote! { + #tokens + + use diesel::sqlite::{Sqlite, SqliteConnection}; + use diesel::serialize::ToSql; + use diesel::deserialize::{FromSqlRow, StaticallySizedRow}; + use diesel::sqlite::SqliteAggregateFunction; + use diesel::sql_types::IntoNullable; + }; + + match arg_name.len() { + x if x > 1 => { + tokens = quote! { + #tokens + + #[allow(dead_code)] + /// Registers an implementation for this aggregate function on the given connection + /// + /// This function must be called for every `SqliteConnection` before + /// this SQL function can be used on SQLite. The implementation must be + /// deterministic (returns the same result given the same arguments). + pub fn register_impl( + conn: &SqliteConnection + ) -> QueryResult<()> + where + A: SqliteAggregateFunction<(#(#arg_name,)*)> + Send + 'static, + A::Output: ToSql<#return_type, Sqlite>, + (#(#arg_name,)*): FromSqlRow<(#(#arg_type,)*), Sqlite> + + StaticallySizedRow<(#(#arg_type,)*), Sqlite>, + { + conn.register_aggregate_function::<(#(#arg_type,)*), #return_type, _, _, A>(#sql_name) + } + }; + } + x if x == 1 => { + let arg_name = arg_name[0]; + let arg_type = arg_type[0]; + + tokens = quote! { + #tokens + + #[allow(dead_code)] + /// Registers an implementation for this aggregate function on the given connection + /// + /// This function must be called for every `SqliteConnection` before + /// this SQL function can be used on SQLite. The implementation must be + /// deterministic (returns the same result given the same arguments). + pub fn register_impl( + conn: &SqliteConnection + ) -> QueryResult<()> + where + A: SqliteAggregateFunction<#arg_name> + Send + 'static, + A::Output: ToSql<#return_type, Sqlite>, + #arg_name: FromSqlRow<#arg_type, Sqlite> + + StaticallySizedRow<#arg_type, Sqlite>, + { + conn.register_aggregate_function::<#arg_type, #return_type, _, _, A>(#sql_name) + } + }; + } + _ => (), + } + } + } else { + tokens = quote! { + #tokens + + #[derive(ValidGrouping)] + pub struct __Derived<#(#arg_name,)*>(#(#arg_name,)*); + + impl #impl_generics_internal ValidGrouping<__DieselInternal> + for #fn_name #ty_generics + where + __Derived<#(#arg_name,)*>: ValidGrouping<__DieselInternal>, + { + type IsAggregate = <__Derived<#(#arg_name,)*> as ValidGrouping<__DieselInternal>>::IsAggregate; + } + }; + + if cfg!(feature = "sqlite") && type_args.is_empty() && !arg_name.is_empty() { + tokens = quote! { + #tokens + + use diesel::sqlite::{Sqlite, SqliteConnection}; + use diesel::serialize::ToSql; + use diesel::deserialize::{FromSqlRow, StaticallySizedRow}; + + #[allow(dead_code)] + /// Registers an implementation for this function on the given connection + /// + /// This function must be called for every `SqliteConnection` before + /// this SQL function can be used on SQLite. The implementation must be + /// deterministic (returns the same result given the same arguments). If + /// the function is nondeterministic, call + /// `register_nondeterministic_impl` instead. + pub fn register_impl( + conn: &SqliteConnection, + f: F, + ) -> QueryResult<()> + where + F: Fn(#(#arg_name,)*) -> Ret + Send + 'static, + (#(#arg_name,)*): FromSqlRow<(#(#arg_type,)*), Sqlite> + + StaticallySizedRow<(#(#arg_type,)*), Sqlite>, + Ret: ToSql<#return_type, Sqlite>, + { + conn.register_sql_function::<(#(#arg_type,)*), #return_type, _, _, _>( + #sql_name, + true, + move |(#(#arg_name,)*)| f(#(#arg_name,)*), + ) + } + + #[allow(dead_code)] + /// Registers an implementation for this function on the given connection + /// + /// This function must be called for every `SqliteConnection` before + /// this SQL function can be used on SQLite. + /// `register_nondeterministic_impl` should only be used if your + /// function can return different results with the same arguments (e.g. + /// `random`). If your function is deterministic, you should call + /// `register_impl` instead. + pub fn register_nondeterministic_impl( + conn: &SqliteConnection, + mut f: F, + ) -> QueryResult<()> + where + F: FnMut(#(#arg_name,)*) -> Ret + Send + 'static, + (#(#arg_name,)*): FromSqlRow<(#(#arg_type,)*), Sqlite> + + StaticallySizedRow<(#(#arg_type,)*), Sqlite>, + Ret: ToSql<#return_type, Sqlite>, + { + conn.register_sql_function::<(#(#arg_type,)*), #return_type, _, _, _>( + #sql_name, + false, + move |(#(#arg_name,)*)| f(#(#arg_name,)*), + ) + } + }; + } + } + + let args_iter = args.iter(); + tokens = quote! { + #(#attributes)* + #[allow(non_camel_case_types)] + pub #fn_token #fn_name #impl_generics (#(#args_iter,)*) + -> #fn_name::HelperType #ty_generics + #where_clause + #(#arg_name: ::diesel::expression::AsExpression<#arg_type>,)* + { + #fn_name::#fn_name { + #(#arg_struct_assign,)* + #(#type_args: ::std::marker::PhantomData,)* + } + } + + #[doc(hidden)] + #[allow(non_camel_case_types, non_snake_case, unused_imports)] + pub(crate) mod #fn_name { + #tokens + } + }; + + Ok(tokens) +} + +pub(crate) struct SqlFunctionDecl { + attributes: Vec, + fn_token: Token![fn], + fn_name: syn::Ident, + generics: syn::Generics, + args: Punctuated, + return_type: syn::Type, +} + +impl Parse for SqlFunctionDecl { + fn parse(input: ParseStream) -> parse::Result { + let attributes = syn::Attribute::parse_outer(input)?; + let fn_token: Token![fn] = input.parse()?; + let fn_name = syn::Ident::parse(input)?; + let generics = syn::Generics::parse(input)?; + let args; + let _paren = parenthesized!(args in input); + let args = args.parse_terminated::<_, Token![,]>(StrictFnArg::parse)?; + let return_type = if Option::]>::parse(input)?.is_some() { + syn::Type::parse(input)? + } else { + parse_quote!(diesel::expression::expression_types::NotSelectable) + }; + let _semi = Option::::parse(input)?; + + Ok(Self { + attributes, + fn_token, + fn_name, + generics, + args, + return_type, + }) + } +} + +/// Essentially the same as syn::ArgCaptured, but only allowing ident patterns +struct StrictFnArg { + name: syn::Ident, + colon_token: Token![:], + ty: syn::Type, +} + +impl Parse for StrictFnArg { + fn parse(input: ParseStream) -> parse::Result { + let name = input.parse()?; + let colon_token = input.parse()?; + let ty = input.parse()?; + Ok(Self { + name, + colon_token, + ty, + }) + } +} + +impl ToTokens for StrictFnArg { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.name.to_tokens(tokens); + self.colon_token.to_tokens(tokens); + self.name.to_tokens(tokens); + } +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/sql_type.rs b/collector/benchmarks/diesel/diesel_derives/src/sql_type.rs new file mode 100644 index 000000000..2f8ea7a2b --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/sql_type.rs @@ -0,0 +1,159 @@ +use proc_macro2; +use syn; + +use meta::*; +use util::*; + +pub fn derive(item: syn::DeriveInput) -> Result { + let struct_name = &item.ident; + let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); + + let sqlite_tokens = sqlite_tokens(&item); + let mysql_tokens = mysql_tokens(&item); + let pg_tokens = pg_tokens(&item); + + Ok(wrap_in_dummy_mod(quote! { + impl #impl_generics diesel::sql_types::SqlType + for #struct_name #ty_generics + #where_clause + { + type IsNull = diesel::sql_types::is_nullable::NotNull; + } + + impl #impl_generics diesel::sql_types::SingleValue + for #struct_name #ty_generics + #where_clause + { + } + + #sqlite_tokens + #mysql_tokens + #pg_tokens + })) +} + +fn sqlite_tokens(item: &syn::DeriveInput) -> Option { + MetaItem::with_name(&item.attrs, "sqlite_type") + .map(|attr| attr.expect_ident_value()) + .and_then(|ty| { + if cfg!(not(feature = "sqlite")) { + return None; + } + + let struct_name = &item.ident; + let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); + + Some(quote! { + impl #impl_generics diesel::sql_types::HasSqlType<#struct_name #ty_generics> + for diesel::sqlite::Sqlite + #where_clause + { + fn metadata(_: &()) -> diesel::sqlite::SqliteType { + diesel::sqlite::SqliteType::#ty + } + } + }) + }) +} + +fn mysql_tokens(item: &syn::DeriveInput) -> Option { + MetaItem::with_name(&item.attrs, "mysql_type") + .map(|attr| attr.expect_ident_value()) + .and_then(|ty| { + if cfg!(not(feature = "mysql")) { + return None; + } + + let struct_name = &item.ident; + let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); + + Some(quote! { + impl #impl_generics diesel::sql_types::HasSqlType<#struct_name #ty_generics> + for diesel::mysql::Mysql + #where_clause + { + fn metadata(_: &()) -> diesel::mysql::MysqlType { + diesel::mysql::MysqlType::#ty + } + } + }) + }) +} + +fn pg_tokens(item: &syn::DeriveInput) -> Option { + MetaItem::with_name(&item.attrs, "postgres") + .map(|attr| { + if let Some(x) = get_type_name(&attr)? { + Ok(x) + } else if let Some(x) = get_oids(&attr)? { + Ok(x) + } else { + Err(attr + .span() + .error("Missing required options") + .help("Valid options are `type_name` or `oid` and `array_oid`")) + } + }) + .and_then(|res| res.map_err(Diagnostic::emit).ok()) + .and_then(|ty| { + if cfg!(not(feature = "postgres")) { + return None; + } + + let struct_name = &item.ident; + let (impl_generics, ty_generics, where_clause) = item.generics.split_for_impl(); + + let metadata_fn = match ty { + PgType::Fixed { oid, array_oid } => quote!( + fn metadata(_: &PgMetadataLookup) -> PgTypeMetadata { + PgTypeMetadata { + oid: #oid, + array_oid: #array_oid, + } + } + ), + PgType::Lookup(type_name) => quote!( + fn metadata(lookup: &PgMetadataLookup) -> PgTypeMetadata { + lookup.lookup_type(#type_name) + } + ), + }; + + Some(quote! { + use diesel::pg::{PgMetadataLookup, PgTypeMetadata}; + + impl #impl_generics diesel::sql_types::HasSqlType<#struct_name #ty_generics> + for diesel::pg::Pg + #where_clause + { + #metadata_fn + } + }) + }) +} + +fn get_type_name(attr: &MetaItem) -> Result, Diagnostic> { + Ok(attr.nested_item("type_name")?.map(|ty| { + attr.warn_if_other_options(&["type_name"]); + PgType::Lookup(ty.expect_str_value()) + })) +} + +fn get_oids(attr: &MetaItem) -> Result, Diagnostic> { + if let Some(oid) = attr.nested_item("oid")? { + attr.warn_if_other_options(&["oid", "array_oid"]); + let array_oid = attr.required_nested_item("array_oid")?.expect_int_value(); + let oid = oid.expect_int_value(); + Ok(Some(PgType::Fixed { + oid: oid as u32, + array_oid: array_oid as u32, + })) + } else { + Ok(None) + } +} + +enum PgType { + Fixed { oid: u32, array_oid: u32 }, + Lookup(String), +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/util.rs b/collector/benchmarks/diesel/diesel_derives/src/util.rs new file mode 100644 index 000000000..057a03bec --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/util.rs @@ -0,0 +1,85 @@ +pub use diagnostic_shim::{Diagnostic, DiagnosticShim, EmitErrorExt}; + +use meta::MetaItem; +use proc_macro2::{Span, TokenStream}; +use syn::{Data, DeriveInput, GenericArgument, Type}; + +pub fn wrap_in_dummy_mod(item: TokenStream) -> TokenStream { + quote! { + #[allow(unused_imports)] + const _: () = { + // This import is not actually redundant. When using diesel_derives + // inside of diesel, `diesel` doesn't exist as an extern crate, and + // to work around that it contains a private + // `mod diesel { pub use super::*; }` that this import will then + // refer to. In all other cases, this imports refers to the extern + // crate diesel. + use diesel; + + #item + }; + } +} + +pub fn inner_of_option_ty(ty: &Type) -> &Type { + option_ty_arg(ty).unwrap_or(ty) +} + +pub fn is_option_ty(ty: &Type) -> bool { + option_ty_arg(ty).is_some() +} + +fn option_ty_arg(ty: &Type) -> Option<&Type> { + use syn::PathArguments::AngleBracketed; + + match *ty { + Type::Path(ref ty) => { + let last_segment = ty.path.segments.iter().last().unwrap(); + match last_segment.arguments { + AngleBracketed(ref args) if last_segment.ident == "Option" => { + match args.args.iter().last() { + Some(&GenericArgument::Type(ref ty)) => Some(ty), + _ => None, + } + } + _ => None, + } + } + _ => None, + } +} + +pub fn ty_for_foreign_derive(item: &DeriveInput, flags: &MetaItem) -> Result { + if flags.has_flag("foreign_derive") { + match item.data { + Data::Struct(ref body) => match body.fields.iter().next() { + Some(field) => Ok(field.ty.clone()), + None => Err(flags + .span() + .error("foreign_derive requires at least one field")), + }, + _ => Err(flags + .span() + .error("foreign_derive can only be used with structs")), + } + } else { + let ident = &item.ident; + let (_, ty_generics, ..) = item.generics.split_for_impl(); + Ok(parse_quote!(#ident #ty_generics)) + } +} + +pub fn fix_span(maybe_bad_span: Span, mut fallback: Span) -> Span { + let bad_span_debug = "#0 bytes(0..0)"; + + if format!("{:?}", fallback) == bad_span_debug { + // On recent rust nightlies, even our fallback span is bad. + fallback = Span::call_site(); + } + + if format!("{:?}", maybe_bad_span) == bad_span_debug { + fallback + } else { + maybe_bad_span + } +} diff --git a/collector/benchmarks/diesel/diesel_derives/src/valid_grouping.rs b/collector/benchmarks/diesel/diesel_derives/src/valid_grouping.rs new file mode 100644 index 000000000..45ec153d0 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/src/valid_grouping.rs @@ -0,0 +1,69 @@ +use proc_macro2::*; +use syn; + +use meta::*; +use util::*; + +pub fn derive(mut item: syn::DeriveInput) -> Result { + let flags = + MetaItem::with_name(&item.attrs, "diesel").unwrap_or_else(|| MetaItem::empty("diesel")); + let struct_ty = ty_for_foreign_derive(&item, &flags)?; + let type_params = item + .generics + .type_params() + .map(|param| param.ident.clone()) + .collect::>(); + for type_param in type_params { + let where_clause = item.generics.make_where_clause(); + where_clause + .predicates + .push(parse_quote!(#type_param: ValidGrouping<__GroupByClause>)); + } + + let is_aggregate = flags.has_flag("aggregate"); + + if is_aggregate { + item.generics.params.push(parse_quote!(__GroupByClause)); + let (impl_generics, _, where_clause) = item.generics.split_for_impl(); + Ok(wrap_in_dummy_mod(quote! { + use diesel::expression::{ValidGrouping, MixedAggregates, is_aggregate}; + + impl #impl_generics ValidGrouping<__GroupByClause> for #struct_ty + #where_clause + { + type IsAggregate = is_aggregate::Yes; + } + })) + } else { + let mut aggregates = item + .generics + .type_params() + .map(|t| parse_quote!(#t::IsAggregate)) + .collect::>() + .into_iter(); + let is_aggregate = aggregates + .next() + .map(|first| { + let where_clause = item.generics.make_where_clause(); + aggregates.fold(first, |left, right| { + where_clause.predicates.push(parse_quote!( + #left: MixedAggregates<#right> + )); + parse_quote!(<#left as MixedAggregates<#right>>::Output) + }) + }) + .unwrap_or_else(|| parse_quote!(is_aggregate::Never)); + item.generics.params.push(parse_quote!(__GroupByClause)); + let (impl_generics, _, where_clause) = item.generics.split_for_impl(); + + Ok(wrap_in_dummy_mod(quote! { + use diesel::expression::{ValidGrouping, MixedAggregates, is_aggregate}; + + impl #impl_generics ValidGrouping<__GroupByClause> for #struct_ty + #where_clause + { + type IsAggregate = #is_aggregate; + } + })) + } +} diff --git a/collector/benchmarks/diesel/diesel_derives/tests/as_changeset.rs b/collector/benchmarks/diesel/diesel_derives/tests/as_changeset.rs new file mode 100644 index 000000000..e00975100 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/tests/as_changeset.rs @@ -0,0 +1,449 @@ +use diesel::*; +use helpers::*; +use schema::*; + +#[test] +fn named_ref_struct() { + #[derive(AsChangeset)] + struct User { + name: String, + hair_color: String, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&User { + name: String::from("Jim"), + hair_color: String::from("blue"), + }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("blue"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn named_struct() { + #[derive(AsChangeset)] + struct User { + name: String, + hair_color: String, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(User { + name: String::from("Jim"), + hair_color: String::from("blue"), + }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("blue"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn with_explicit_table_name() { + #[derive(AsChangeset)] + #[table_name = "users"] + struct UserForm { + name: String, + hair_color: String, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm { + name: String::from("Jim"), + hair_color: String::from("blue"), + }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("blue"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn with_lifetime() { + #[derive(AsChangeset)] + #[table_name = "users"] + struct UserForm<'a> { + name: &'a str, + hair_color: &'a str, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm { + name: "Jim", + hair_color: "blue", + }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("blue"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn with_multiple_lifetimes() { + #[derive(AsChangeset)] + #[table_name = "users"] + struct UserForm<'a, 'b> { + name: &'a str, + hair_color: &'b str, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm { + name: "Jim", + hair_color: "blue", + }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("blue"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn with_lifetime_constraints() { + #[derive(AsChangeset)] + #[table_name = "users"] + struct UserForm<'a, 'b: 'a> { + name: &'a str, + hair_color: &'b str, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm { + name: "Jim", + hair_color: "blue", + }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("blue"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn with_explicit_column_names() { + #[derive(AsChangeset)] + #[table_name = "users"] + struct UserForm<'a> { + #[column_name = "name"] + nombre: &'a str, + #[column_name = "hair_color"] + color_de_pelo: &'a str, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm { + nombre: "Jim", + color_de_pelo: "blue", + }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("blue"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn tuple_struct() { + #[derive(AsChangeset)] + #[table_name = "users"] + struct UserForm<'a>( + #[column_name = "name"] &'a str, + #[column_name = "hair_color"] &'a str, + ); + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm("Jim", "blue")) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("blue"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn struct_containing_single_field() { + #[derive(AsChangeset)] + #[table_name = "users"] + struct UserForm<'a> { + name: &'a str, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm { name: "Jim" }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("black"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn tuple_struct_containing_single_field() { + #[derive(AsChangeset)] + #[table_name = "users"] + struct UserForm<'a>(#[column_name = "name"] &'a str); + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm("Jim")) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("black"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn primary_key_is_not_updated() { + #[derive(AsChangeset)] + #[table_name = "users"] + struct UserForm<'a> { + #[allow(dead_code)] + id: i32, + name: &'a str, + hair_color: &'a str, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm { + id: 3, + name: "Jim", + hair_color: "blue", + }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("blue"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn primary_key_is_based_on_column_name() { + #[derive(AsChangeset)] + #[table_name = "users"] + struct UserForm<'a> { + #[column_name = "id"] + _id: i32, + name: &'a str, + hair_color: &'a str, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm { + _id: 3, + name: "Jim", + hair_color: "blue", + }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("blue"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn primary_key_is_not_updated_with_custom_pk() { + #[derive(AsChangeset)] + #[table_name = "users"] + #[primary_key(name)] + struct UserForm<'a> { + #[allow(dead_code)] + name: &'a str, + hair_color: &'a str, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm { + name: "Jim", + hair_color: "blue", + }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Sean"), Some(String::from("blue"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn primary_key_is_not_updated_with_custom_composite_pk() { + #[derive(AsChangeset)] + #[table_name = "users"] + #[primary_key(id, name)] + #[allow(dead_code)] + struct UserForm<'a> { + id: i32, + name: &'a str, + hair_color: &'a str, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm { + id: 3, + name: "Jim", + hair_color: "blue", + }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Sean"), Some(String::from("blue"))), + (2, String::from("Tess"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn option_fields_are_skipped() { + #[derive(AsChangeset)] + #[table_name = "users"] + struct UserForm<'a> { + name: &'a str, + hair_color: Option<&'a str>, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm { + name: "Jim", + hair_color: Some("blue"), + }) + .execute(&connection) + .unwrap(); + update(users::table.find(2)) + .set(&UserForm { + name: "Ruby", + hair_color: None, + }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("blue"))), + (2, String::from("Ruby"), Some(String::from("brown"))), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} + +#[test] +fn option_fields_are_assigned_null_when_specified() { + #[derive(AsChangeset)] + #[table_name = "users"] + #[changeset_options(treat_none_as_null = "true")] + struct UserForm<'a> { + name: &'a str, + hair_color: Option<&'a str>, + } + + let connection = connection_with_sean_and_tess_in_users_table(); + + update(users::table.find(1)) + .set(&UserForm { + name: "Jim", + hair_color: Some("blue"), + }) + .execute(&connection) + .unwrap(); + update(users::table.find(2)) + .set(&UserForm { + name: "Ruby", + hair_color: None, + }) + .execute(&connection) + .unwrap(); + + let expected = vec![ + (1, String::from("Jim"), Some(String::from("blue"))), + (2, String::from("Ruby"), None), + ]; + let actual = users::table.order(users::id).load(&connection); + assert_eq!(Ok(expected), actual); +} diff --git a/collector/benchmarks/diesel/diesel_derives/tests/associations.rs b/collector/benchmarks/diesel/diesel_derives/tests/associations.rs new file mode 100644 index 000000000..c428ded99 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/tests/associations.rs @@ -0,0 +1,293 @@ +use diesel::*; +use helpers::*; + +type Backend = ::Backend; + +#[test] +fn simple_belongs_to() { + table! { + users { + id -> Integer, + name -> Text, + } + } + + table! { + posts { + id -> Integer, + user_id -> Integer, + title -> Text, + } + } + + allow_tables_to_appear_in_same_query!(users, posts); + + #[derive(Identifiable)] + pub struct User { + id: i32, + } + + #[derive(Associations, Identifiable)] + #[belongs_to(User)] + pub struct Post { + id: i32, + user_id: i32, + } + + joinable!(posts -> users(user_id)); + + let _can_join_tables = posts::table + .inner_join(users::table) + .select((users::id, users::name, posts::id)) + .filter( + posts::id + .eq(1) + .and(posts::user_id.eq(2)) + .and(posts::title.eq("Bar")), + ); + + let _can_reverse_join_tables = users::table + .inner_join(posts::table) + .select((posts::id, posts::user_id, posts::title)) + .filter(users::id.eq(1).and(users::name.eq("Sean"))); + + let t = User { id: 42 }; + + let belong_to = Post::belonging_to(&t); + let filter = posts::table.filter(posts::user_id.eq(42)); + + assert_eq!( + debug_query::(&belong_to).to_string(), + debug_query::(&filter).to_string() + ); +} + +#[test] +fn custom_foreign_key() { + table! { + users { + id -> Integer, + name -> Text, + } + } + + table! { + posts { + id -> Integer, + belongs_to_user -> Integer, + title -> Text, + } + } + + allow_tables_to_appear_in_same_query!(users, posts); + + #[derive(Identifiable)] + pub struct User { + id: i32, + } + + #[derive(Associations, Identifiable)] + #[belongs_to(User, foreign_key = "belongs_to_user")] + pub struct Post { + id: i32, + belongs_to_user: i32, + } + + joinable!(posts -> users(belongs_to_user)); + + let _can_join_tables = posts::table + .inner_join(users::table) + .select((users::id, users::name)) + .filter( + posts::id + .eq(1) + .and(posts::belongs_to_user.eq(2)) + .and(posts::title.eq("Bar")), + ); + + let _can_reverse_join_tables = users::table + .inner_join(posts::table) + .select((posts::id, posts::belongs_to_user, posts::title)) + .filter(users::id.eq(1).and(users::name.eq("Sean"))); + + let t = User { id: 42 }; + + let belong_to = Post::belonging_to(&t); + let filter = posts::table.filter(posts::belongs_to_user.eq(42)); + + assert_eq!( + debug_query::(&belong_to).to_string(), + debug_query::(&filter).to_string() + ); +} + +#[test] +fn self_referential() { + table! { + trees { + id -> Integer, + parent_id -> Nullable, + } + } + + #[derive(Associations, Identifiable)] + #[belongs_to(Tree, foreign_key = "parent_id")] + pub struct Tree { + id: i32, + parent_id: Option, + } + let t = Tree { + id: 42, + parent_id: None, + }; + + let belong_to = Tree::belonging_to(&t); + let filter = trees::table.filter(trees::parent_id.eq(42)); + assert_eq!( + debug_query::(&belong_to).to_string(), + debug_query::(&filter).to_string() + ); +} + +#[test] +fn multiple_associations() { + table! { + users { + id -> Integer, + } + } + + table! { + posts { + id -> Integer, + } + } + + table! { + comments { + id -> Integer, + user_id -> Integer, + post_id -> Integer, + } + } + + #[derive(Identifiable)] + struct User { + id: i32, + } + + #[derive(Identifiable)] + struct Post { + id: i32, + } + + #[derive(Identifiable, Associations)] + #[belongs_to(User)] + #[belongs_to(Post)] + struct Comment { + id: i32, + user_id: i32, + post_id: i32, + } + + let user = User { id: 1 }; + let post = Post { id: 2 }; + + let query = Comment::belonging_to(&user); + let expected = comments::table.filter(comments::user_id.eq(1)); + assert_eq!( + debug_query::(&query).to_string(), + debug_query::(&expected).to_string() + ); + let query = Comment::belonging_to(&post); + let expected = comments::table.filter(comments::post_id.eq(2)); + assert_eq!( + debug_query::(&query).to_string(), + debug_query::(&expected).to_string() + ); +} + +#[test] +fn foreign_key_field_with_column_rename() { + table! { + users { + id -> Integer, + } + } + + table! { + posts { + id -> Integer, + user_id -> Integer, + } + } + + #[derive(Identifiable, Clone, Copy)] + pub struct User { + id: i32, + } + + #[derive(Associations, Identifiable, Clone, Copy, PartialEq, Debug)] + #[belongs_to(User)] + pub struct Post { + id: i32, + #[column_name = "user_id"] + author_id: i32, + } + + let user1 = User { id: 1 }; + let user2 = User { id: 2 }; + let post1 = Post { + id: 1, + author_id: 2, + }; + let post2 = Post { + id: 2, + author_id: 1, + }; + + let query = Post::belonging_to(&user1); + let expected = posts::table.filter(posts::user_id.eq(1)); + assert_eq!( + debug_query::(&query).to_string(), + debug_query::(&expected).to_string() + ); + + let users = vec![user1, user2]; + let posts = vec![post1, post2].grouped_by(&users); + assert_eq!(vec![vec![post2], vec![post1]], posts); +} + +#[test] +fn tuple_struct() { + table! { + users { + id -> Integer, + } + } + + table! { + posts { + id -> Integer, + user_id -> Integer, + } + } + + #[derive(Identifiable)] + pub struct User { + id: i32, + } + + #[derive(Associations, Identifiable)] + #[belongs_to(User)] + pub struct Post(#[column_name = "id"] i32, #[column_name = "user_id"] i32); + + let user = User { id: 1 }; + + let query = Post::belonging_to(&user); + let expected = posts::table.filter(posts::user_id.eq(1)); + assert_eq!( + debug_query::(&query).to_string(), + debug_query::(&expected).to_string() + ); +} diff --git a/collector/benchmarks/diesel/diesel_derives/tests/helpers.rs b/collector/benchmarks/diesel/diesel_derives/tests/helpers.rs new file mode 100644 index 000000000..d2cb5d483 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/tests/helpers.rs @@ -0,0 +1,80 @@ +use diesel::prelude::*; +use diesel::sql_query; + +cfg_if! { + if #[cfg(feature = "sqlite")] { + pub type TestConnection = SqliteConnection; + + pub fn connection() -> TestConnection { + let conn = SqliteConnection::establish(":memory:").unwrap(); + sql_query("CREATE TABLE users (\ + id INTEGER PRIMARY KEY AUTOINCREMENT, \ + name VARCHAR NOT NULL, \ + hair_color VARCHAR DEFAULT 'Green')") + .execute(&conn) + .unwrap(); + conn + } + } else if #[cfg(feature = "postgres")] { + extern crate dotenv; + + pub type TestConnection = PgConnection; + + pub fn connection() -> TestConnection { + let database_url = dotenv::var("PG_DATABASE_URL") + .or_else(|_| dotenv::var("DATABASE_URL")) + .expect("DATABASE_URL must be set in order to run tests"); + let conn = PgConnection::establish(&database_url).unwrap(); + conn.begin_test_transaction().unwrap(); + sql_query("DROP TABLE IF EXISTS users CASCADE").execute(&conn).unwrap(); + sql_query("CREATE TABLE users (\ + id SERIAL PRIMARY KEY, \ + name VARCHAR NOT NULL, \ + hair_color VARCHAR DEFAULT 'Green')") + .execute(&conn) + .unwrap(); + conn + } + } else if #[cfg(feature = "mysql")] { + extern crate dotenv; + + pub type TestConnection = MysqlConnection; + + pub fn connection() -> TestConnection { + let database_url = dotenv::var("MYSQL_UNIT_TEST_DATABASE_URL") + .or_else(|_| dotenv::var("DATABASE_URL")) + .expect("DATABASE_URL must be set in order to run tests"); + let conn = MysqlConnection::establish(&database_url).unwrap(); + sql_query("DROP TABLE IF EXISTS users CASCADE").execute(&conn).unwrap(); + sql_query("CREATE TABLE users (\ + id INTEGER PRIMARY KEY AUTO_INCREMENT, \ + name TEXT NOT NULL, \ + hair_color VARCHAR(255) DEFAULT 'Green')") + .execute(&conn) + .unwrap(); + conn.begin_test_transaction().unwrap(); + conn + } + } else { + compile_error!( + "At least one backend must be used to test this crate.\n \ + Pass argument `--features \"\"` with one or more of the following backends, \ + 'mysql', 'postgres', or 'sqlite'. \n\n \ + ex. cargo test --features \"mysql postgres sqlite\"\n" + ); + } +} + +pub fn connection_with_sean_and_tess_in_users_table() -> TestConnection { + use schema::users::dsl::*; + + let connection = connection(); + ::diesel::insert_into(users) + .values(&vec![ + (id.eq(1), name.eq("Sean"), hair_color.eq("black")), + (id.eq(2), name.eq("Tess"), hair_color.eq("brown")), + ]) + .execute(&connection) + .unwrap(); + connection +} diff --git a/collector/benchmarks/diesel/diesel_derives/tests/identifiable.rs b/collector/benchmarks/diesel/diesel_derives/tests/identifiable.rs new file mode 100644 index 000000000..ea912f951 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/tests/identifiable.rs @@ -0,0 +1,152 @@ +use diesel::associations::Identifiable; + +table! { + foos { + id -> Integer, + } +} + +table! { + bars { + id -> VarChar, + } +} + +#[test] +fn derive_identifiable_on_simple_struct() { + #[derive(Identifiable)] + struct Foo { + id: i32, + #[allow(dead_code)] + foo: i32, + } + + let foo1 = Foo { id: 1, foo: 2 }; + let foo2 = Foo { id: 2, foo: 3 }; + assert_eq!(&1, foo1.id()); + assert_eq!(&2, foo2.id()); +} + +#[test] +fn derive_identifiable_on_tuple_struct() { + #[derive(Identifiable)] + struct Foo( + #[column_name = "id"] i32, + #[allow(dead_code)] + #[column_name = "lol"] + i32, + ); + + let foo1 = Foo(1, 2); + let foo2 = Foo(2, 3); + assert_eq!(&1, foo1.id()); + assert_eq!(&2, foo2.id()); +} + +#[test] +fn derive_identifiable_when_id_is_not_first_field() { + #[derive(Identifiable)] + struct Foo { + #[allow(dead_code)] + foo: i32, + id: i32, + } + + let foo1 = Foo { id: 1, foo: 2 }; + let foo2 = Foo { id: 2, foo: 3 }; + assert_eq!(&1, foo1.id()); + assert_eq!(&2, foo2.id()); +} + +#[test] +fn derive_identifiable_on_struct_with_non_integer_pk() { + #[derive(Identifiable)] + #[table_name = "bars"] + struct Foo { + id: &'static str, + #[allow(dead_code)] + foo: i32, + } + + let foo1 = Foo { id: "hi", foo: 2 }; + let foo2 = Foo { + id: "there", + foo: 3, + }; + assert_eq!(&"hi", foo1.id()); + assert_eq!(&"there", foo2.id()); +} + +#[test] +fn derive_identifiable_on_struct_with_lifetime() { + #[derive(Identifiable)] + #[table_name = "bars"] + struct Foo<'a> { + id: &'a str, + #[allow(dead_code)] + foo: i32, + } + + let foo1 = Foo { id: "hi", foo: 2 }; + let foo2 = Foo { + id: "there", + foo: 3, + }; + assert_eq!(&"hi", foo1.id()); + assert_eq!(&"there", foo2.id()); +} + +#[test] +fn derive_identifiable_with_non_standard_pk() { + #[allow(dead_code)] + #[derive(Identifiable)] + #[table_name = "bars"] + #[primary_key(foo_id)] + struct Foo<'a> { + id: i32, + foo_id: &'a str, + foo: i32, + } + + let foo1 = Foo { + id: 1, + foo_id: "hi", + foo: 2, + }; + let foo2 = Foo { + id: 2, + foo_id: "there", + foo: 3, + }; + assert_eq!(&"hi", foo1.id()); + assert_eq!(&"there", foo2.id()); +} + +#[test] +fn derive_identifiable_with_composite_pk() { + #[allow(dead_code)] + #[derive(Identifiable)] + #[table_name = "bars"] + #[primary_key(foo_id, bar_id)] + struct Foo { + id: i32, + foo_id: i32, + bar_id: i32, + foo: i32, + } + + let foo1 = Foo { + id: 1, + foo_id: 2, + bar_id: 3, + foo: 4, + }; + let foo2 = Foo { + id: 5, + foo_id: 6, + bar_id: 7, + foo: 8, + }; + assert_eq!((&2, &3), foo1.id()); + assert_eq!((&6, &7), foo2.id()); +} diff --git a/collector/benchmarks/diesel/diesel_derives/tests/insertable.rs b/collector/benchmarks/diesel/diesel_derives/tests/insertable.rs new file mode 100644 index 000000000..b03e2cbb7 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/tests/insertable.rs @@ -0,0 +1,298 @@ +use diesel::*; +use helpers::*; + +table! { + users { + id -> Integer, + name -> VarChar, + hair_color -> Nullable, + } +} + +#[test] +fn simple_struct_definition() { + #[derive(Insertable)] + #[table_name = "users"] + struct NewUser { + name: String, + hair_color: String, + } + + let conn = connection(); + let new_user = NewUser { + name: "Sean".into(), + hair_color: "Black".into(), + }; + insert_into(users::table) + .values(new_user) + .execute(&conn) + .unwrap(); + + let saved = users::table + .select((users::name, users::hair_color)) + .load::<(String, Option)>(&conn); + let expected = vec![("Sean".to_string(), Some("Black".to_string()))]; + assert_eq!(Ok(expected), saved); +} + +#[test] +fn simple_reference_definition() { + #[derive(Insertable)] + #[table_name = "users"] + struct NewUser { + name: String, + hair_color: String, + } + + let conn = connection(); + let new_user = NewUser { + name: "Sean".into(), + hair_color: "Black".into(), + }; + insert_into(users::table) + .values(&new_user) + .execute(&conn) + .unwrap(); + + let saved = users::table + .select((users::name, users::hair_color)) + .load::<(String, Option)>(&conn); + let expected = vec![("Sean".to_string(), Some("Black".to_string()))]; + assert_eq!(Ok(expected), saved); +} + +macro_rules! test_struct_definition { + ($test_name:ident, $struct_def:item) => { + #[test] + fn $test_name() { + #[derive(Insertable)] + #[table_name = "users"] + $struct_def + + let conn = connection(); + let new_user = NewUser { name: "Sean".into(), hair_color: None }; + insert_into(users::table).values(&new_user).execute(&conn).unwrap(); + + let saved = users::table.select((users::name, users::hair_color)) + .load::<(String, Option)>(&conn); + let expected = vec![("Sean".to_string(), Some("Green".to_string()))]; + assert_eq!(Ok(expected), saved); + } + } +} + +test_struct_definition! { + struct_with_option_field, + struct NewUser { + name: String, + hair_color: Option, + } +} + +test_struct_definition! { + pub_struct_definition, + pub struct NewUser { + name: String, + hair_color: Option, + } +} + +test_struct_definition! { + struct_with_pub_field, + pub struct NewUser { + pub name: String, + hair_color: Option, + } +} + +test_struct_definition! { + struct_with_pub_option_field, + pub struct NewUser { + name: String, + pub hair_color: Option, + } +} + +test_struct_definition! { + named_struct_with_borrowed_body, + struct NewUser<'a> { + name: &'a str, + hair_color: Option<&'a str>, + } +} + +#[test] +fn named_struct_with_renamed_field() { + #[derive(Insertable)] + #[table_name = "users"] + struct NewUser { + #[column_name = "name"] + my_name: String, + hair_color: String, + } + + let conn = connection(); + let new_user = NewUser { + my_name: "Sean".into(), + hair_color: "Black".into(), + }; + insert_into(users::table) + .values(&new_user) + .execute(&conn) + .unwrap(); + + let saved = users::table + .select((users::name, users::hair_color)) + .load::<(String, Option)>(&conn); + let expected = vec![("Sean".to_string(), Some("Black".to_string()))]; + assert_eq!(Ok(expected), saved); +} + +#[test] +fn named_struct_with_renamed_option_field() { + #[derive(Insertable)] + #[table_name = "users"] + struct NewUser { + #[column_name = "name"] + my_name: String, + #[column_name = "hair_color"] + my_hair_color: Option, + } + + let conn = connection(); + let new_user = NewUser { + my_name: "Sean".into(), + my_hair_color: None, + }; + insert_into(users::table) + .values(&new_user) + .execute(&conn) + .unwrap(); + + let saved = users::table + .select((users::name, users::hair_color)) + .load::<(String, Option)>(&conn); + let expected = vec![("Sean".to_string(), Some("Green".to_string()))]; + assert_eq!(Ok(expected), saved); +} + +#[test] +fn tuple_struct() { + #[derive(Insertable)] + #[table_name = "users"] + struct NewUser<'a>( + #[column_name = "name"] &'a str, + #[column_name = "hair_color"] Option<&'a str>, + ); + + let conn = connection(); + let new_user = NewUser("Sean", None); + insert_into(users::table) + .values(&new_user) + .execute(&conn) + .unwrap(); + + let saved = users::table + .select((users::name, users::hair_color)) + .load::<(String, Option)>(&conn); + let expected = vec![("Sean".to_string(), Some("Green".to_string()))]; + assert_eq!(Ok(expected), saved); +} + +#[test] +fn named_struct_with_unusual_reference_type() { + #[derive(Insertable)] + #[table_name = "users"] + struct NewUser<'a> { + name: &'a String, + hair_color: Option<&'a String>, + } + + let conn = connection(); + let sean = "Sean".to_string(); + let black = "Black".to_string(); + let new_user = NewUser { + name: &sean, + hair_color: Some(&black), + }; + insert_into(users::table) + .values(&new_user) + .execute(&conn) + .unwrap(); + + let saved = users::table + .select((users::name, users::hair_color)) + .load(&conn); + let expected = vec![(sean.clone(), Some(black.clone()))]; + assert_eq!(Ok(expected), saved); +} + +#[test] +#[cfg(all(feature = "postgres", not(feature = "sqlite")))] +fn insertable_with_slice_of_borrowed() { + table! { + posts { + id -> Serial, + tags -> Array, + } + } + + #[derive(Insertable)] + #[table_name = "posts"] + struct NewPost<'a> { + tags: &'a [&'a str], + } + + let conn = connection(); + sql_query("DROP TABLE IF EXISTS posts CASCADE") + .execute(&conn) + .unwrap(); + sql_query("CREATE TABLE posts (id SERIAL PRIMARY KEY, tags TEXT[] NOT NULL)") + .execute(&conn) + .unwrap(); + let new_post = NewPost { + tags: &["hi", "there"], + }; + insert_into(posts::table) + .values(&new_post) + .execute(&conn) + .unwrap(); + + let saved = posts::table.select(posts::tags).load::>(&conn); + let expected = vec![vec![String::from("hi"), String::from("there")]]; + assert_eq!(Ok(expected), saved); +} + +#[test] +fn embedded_struct() { + #[derive(Insertable)] + #[table_name = "users"] + struct NameAndHairColor<'a> { + name: &'a str, + hair_color: &'a str, + } + + #[derive(Insertable)] + struct User<'a> { + id: i32, + #[diesel(embed)] + name_and_hair_color: NameAndHairColor<'a>, + } + + let conn = connection(); + let new_user = User { + id: 1, + name_and_hair_color: NameAndHairColor { + name: "Sean", + hair_color: "Black", + }, + }; + insert_into(users::table) + .values(&new_user) + .execute(&conn) + .unwrap(); + + let saved = users::table.load::<(i32, String, Option)>(&conn); + let expected = vec![(1, "Sean".to_string(), Some("Black".to_string()))]; + assert_eq!(Ok(expected), saved); +} diff --git a/collector/benchmarks/diesel/diesel_derives/tests/queryable.rs b/collector/benchmarks/diesel/diesel_derives/tests/queryable.rs new file mode 100644 index 000000000..d30114bb4 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/tests/queryable.rs @@ -0,0 +1,38 @@ +use diesel::dsl::sql; +use diesel::sql_types::Integer; +use diesel::*; + +use helpers::connection; + +#[test] +fn named_struct_definition() { + #[derive(Debug, Clone, Copy, PartialEq, Eq, Queryable)] + struct MyStruct { + foo: i32, + bar: i32, + } + + let conn = connection(); + let data = select(sql::<(Integer, Integer)>("1, 2")).get_result(&conn); + assert_eq!(Ok(MyStruct { foo: 1, bar: 2 }), data); +} + +#[test] +fn tuple_struct() { + #[derive(Debug, Clone, Copy, PartialEq, Eq, Queryable)] + struct MyStruct(#[column_name = "foo"] i32, #[column_name = "bar"] i32); + + let conn = connection(); + let data = select(sql::<(Integer, Integer)>("1, 2")).get_result(&conn); + assert_eq!(Ok(MyStruct(1, 2)), data); +} + +#[test] +fn tuple_struct_without_column_name_annotations() { + #[derive(Debug, Clone, Copy, PartialEq, Eq, Queryable)] + struct MyStruct(i32, i32); + + let conn = connection(); + let data = select(sql::<(Integer, Integer)>("1, 2")).get_result(&conn); + assert_eq!(Ok(MyStruct(1, 2)), data); +} diff --git a/collector/benchmarks/diesel/diesel_derives/tests/queryable_by_name.rs b/collector/benchmarks/diesel/diesel_derives/tests/queryable_by_name.rs new file mode 100644 index 000000000..acddaf4a9 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/tests/queryable_by_name.rs @@ -0,0 +1,109 @@ +use diesel::sql_types::Integer; +use diesel::*; + +use helpers::connection; + +table! { + my_structs (foo) { + foo -> Integer, + bar -> Integer, + } +} + +#[test] +fn named_struct_definition() { + #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] + #[table_name = "my_structs"] + struct MyStruct { + foo: i32, + bar: i32, + } + + let conn = connection(); + let data = sql_query("SELECT 1 AS foo, 2 AS bar").get_result(&conn); + assert_eq!(Ok(MyStruct { foo: 1, bar: 2 }), data); +} + +#[test] +fn tuple_struct() { + #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] + #[table_name = "my_structs"] + struct MyStruct(#[column_name = "foo"] i32, #[column_name = "bar"] i32); + + let conn = connection(); + let data = sql_query("SELECT 1 AS foo, 2 AS bar").get_result(&conn); + assert_eq!(Ok(MyStruct(1, 2)), data); +} + +// FIXME: Test usage with renamed columns + +#[test] +fn struct_with_no_table() { + #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] + struct MyStructNamedSoYouCantInferIt { + #[sql_type = "Integer"] + foo: i32, + #[sql_type = "Integer"] + bar: i32, + } + + let conn = connection(); + let data = sql_query("SELECT 1 AS foo, 2 AS bar").get_result(&conn); + assert_eq!(Ok(MyStructNamedSoYouCantInferIt { foo: 1, bar: 2 }), data); +} + +#[test] +fn embedded_struct() { + #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] + #[table_name = "my_structs"] + struct A { + foo: i32, + #[diesel(embed)] + b: B, + } + + #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] + #[table_name = "my_structs"] + struct B { + bar: i32, + } + + let conn = connection(); + let data = sql_query("SELECT 1 AS foo, 2 AS bar").get_result(&conn); + assert_eq!( + Ok(A { + foo: 1, + b: B { bar: 2 }, + }), + data + ); +} + +#[test] +fn embedded_option() { + #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] + #[table_name = "my_structs"] + struct A { + foo: i32, + #[diesel(embed)] + b: Option, + } + + #[derive(Debug, Clone, Copy, PartialEq, Eq, QueryableByName)] + #[table_name = "my_structs"] + struct B { + bar: i32, + } + + let conn = connection(); + let data = sql_query("SELECT 1 AS foo, 2 AS bar").get_result(&conn); + assert_eq!( + Ok(A { + foo: 1, + b: Some(B { bar: 2 }), + }), + data + ); + let data = sql_query("SELECT 1 AS foo, NULL AS bar").get_result(&conn); + assert_eq!(Ok(A { foo: 1, b: None }), data); +} diff --git a/collector/benchmarks/diesel/diesel_derives/tests/schema.rs b/collector/benchmarks/diesel/diesel_derives/tests/schema.rs new file mode 100644 index 000000000..aa0b96519 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/tests/schema.rs @@ -0,0 +1,7 @@ +table! { + users { + id -> Integer, + name -> Text, + hair_color -> Nullable, + } +} diff --git a/collector/benchmarks/diesel/diesel_derives/tests/tests.rs b/collector/benchmarks/diesel/diesel_derives/tests/tests.rs new file mode 100644 index 000000000..636fea662 --- /dev/null +++ b/collector/benchmarks/diesel/diesel_derives/tests/tests.rs @@ -0,0 +1,16 @@ +#![deny(warnings)] + +#[macro_use] +extern crate cfg_if; +#[macro_use] +extern crate diesel; + +mod helpers; +mod schema; + +mod as_changeset; +mod associations; +mod identifiable; +mod insertable; +mod queryable; +mod queryable_by_name; diff --git a/collector/benchmarks/diesel/migrations/mysql/20170207193805_initial_schema/down.sql b/collector/benchmarks/diesel/migrations/mysql/20170207193805_initial_schema/down.sql new file mode 100644 index 000000000..dc5cd92b9 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170207193805_initial_schema/down.sql @@ -0,0 +1,6 @@ +DROP TABLE followings; +DROP TABLE special_comments; +DROP TABLE special_posts; +DROP TABLE comments; +DROP TABLE posts; +DROP TABLE users; diff --git a/collector/benchmarks/diesel/migrations/mysql/20170207193805_initial_schema/up.sql b/collector/benchmarks/diesel/migrations/mysql/20170207193805_initial_schema/up.sql new file mode 100644 index 000000000..d25a3b9b1 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170207193805_initial_schema/up.sql @@ -0,0 +1,36 @@ +CREATE TABLE users ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + name TEXT NOT NULL, + hair_color TEXT +) CHARACTER SET utf8mb4; + +CREATE TABLE posts ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + user_id INTEGER NOT NULL, + title TEXT NOT NULL, + body TEXT +) CHARACTER SET utf8mb4; + +CREATE TABLE comments ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + post_id INTEGER NOT NULL, + text TEXT NOT NULL +) CHARACTER SET utf8mb4; + +CREATE TABLE special_posts ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + user_id INTEGER NOT NULL, + title TEXT NOT NULL +) CHARACTER SET utf8mb4; + +CREATE TABLE special_comments ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + special_post_id INTEGER NOT NULL +) CHARACTER SET utf8mb4; + +CREATE TABLE followings ( + user_id INTEGER NOT NULL, + post_id INTEGER NOT NULL, + email_notifications TINYINT(1) NOT NULL DEFAULT 0, + PRIMARY KEY (user_id, post_id) +); diff --git a/collector/benchmarks/diesel/migrations/mysql/20170209180355_add_one_off_tables_from_integration_tests/down.sql b/collector/benchmarks/diesel/migrations/mysql/20170209180355_add_one_off_tables_from_integration_tests/down.sql new file mode 100644 index 000000000..db631b629 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170209180355_add_one_off_tables_from_integration_tests/down.sql @@ -0,0 +1,5 @@ +DROP TABLE numbers; +DROP TABLE precision_numbers; +DROP TABLE nullable_doubles; +DROP TABLE users_with_name_pk; +DROP TABLE points; diff --git a/collector/benchmarks/diesel/migrations/mysql/20170209180355_add_one_off_tables_from_integration_tests/up.sql b/collector/benchmarks/diesel/migrations/mysql/20170209180355_add_one_off_tables_from_integration_tests/up.sql new file mode 100644 index 000000000..cacf5aa46 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170209180355_add_one_off_tables_from_integration_tests/up.sql @@ -0,0 +1,5 @@ +CREATE TABLE numbers (n INTEGER PRIMARY KEY); +CREATE TABLE precision_numbers (n DOUBLE PRECISION NOT NULL PRIMARY KEY); +CREATE TABLE nullable_doubles (id INT PRIMARY KEY AUTO_INCREMENT, n DOUBLE PRECISION); +CREATE TABLE users_with_name_pk (name VARCHAR(50) PRIMARY KEY); +CREATE TABLE points (x INTEGER NOT NULL, y INTEGER NOT NULL, PRIMARY KEY (x, y)); diff --git a/collector/benchmarks/diesel/migrations/mysql/20170211150830_index_columns_used_in_benchmarks/down.sql b/collector/benchmarks/diesel/migrations/mysql/20170211150830_index_columns_used_in_benchmarks/down.sql new file mode 100644 index 000000000..c19dc97fe --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170211150830_index_columns_used_in_benchmarks/down.sql @@ -0,0 +1,2 @@ +DROP INDEX users_hair_color; +DROP INDEX posts_user_id; diff --git a/collector/benchmarks/diesel/migrations/mysql/20170211150830_index_columns_used_in_benchmarks/up.sql b/collector/benchmarks/diesel/migrations/mysql/20170211150830_index_columns_used_in_benchmarks/up.sql new file mode 100644 index 000000000..018068a3d --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170211150830_index_columns_used_in_benchmarks/up.sql @@ -0,0 +1,2 @@ +CREATE INDEX users_hair_color ON users (hair_color(255)); +CREATE INDEX posts_user_id ON posts (user_id); diff --git a/collector/benchmarks/diesel/migrations/mysql/20170215170122_create_trees/down.sql b/collector/benchmarks/diesel/migrations/mysql/20170215170122_create_trees/down.sql new file mode 100644 index 000000000..1135e76a6 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170215170122_create_trees/down.sql @@ -0,0 +1 @@ +DROP TABLE trees; diff --git a/collector/benchmarks/diesel/migrations/mysql/20170215170122_create_trees/up.sql b/collector/benchmarks/diesel/migrations/mysql/20170215170122_create_trees/up.sql new file mode 100644 index 000000000..4e0db46f3 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170215170122_create_trees/up.sql @@ -0,0 +1,4 @@ +CREATE TABLE trees ( + id INTEGER PRIMARY KEY, + parent_id INTEGER +); diff --git a/collector/benchmarks/diesel/migrations/mysql/20170219200159_add_foreign_key/down.sql b/collector/benchmarks/diesel/migrations/mysql/20170219200159_add_foreign_key/down.sql new file mode 100644 index 000000000..9dea4d73a --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170219200159_add_foreign_key/down.sql @@ -0,0 +1,2 @@ +DROP TABLE fk_tests; +DROP TABLE fk_inits; diff --git a/collector/benchmarks/diesel/migrations/mysql/20170219200159_add_foreign_key/up.sql b/collector/benchmarks/diesel/migrations/mysql/20170219200159_add_foreign_key/up.sql new file mode 100644 index 000000000..c1df0ccd1 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170219200159_add_foreign_key/up.sql @@ -0,0 +1,9 @@ +CREATE TABLE fk_inits ( + id INTEGER PRIMARY KEY +); + +CREATE TABLE fk_tests ( + id INTEGER PRIMARY KEY, + fk_id INTEGER NOT NULL, + FOREIGN KEY (fk_id) REFERENCES fk_inits (id) +); diff --git a/collector/benchmarks/diesel/migrations/mysql/20170407152306_add_nullable_table/down.sql b/collector/benchmarks/diesel/migrations/mysql/20170407152306_add_nullable_table/down.sql new file mode 100644 index 000000000..ff84418a4 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170407152306_add_nullable_table/down.sql @@ -0,0 +1 @@ +DROP TABLE nullable_table; diff --git a/collector/benchmarks/diesel/migrations/mysql/20170407152306_add_nullable_table/up.sql b/collector/benchmarks/diesel/migrations/mysql/20170407152306_add_nullable_table/up.sql new file mode 100644 index 000000000..74da9a32f --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170407152306_add_nullable_table/up.sql @@ -0,0 +1,4 @@ +CREATE TABLE nullable_table ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + value INTEGER +) CHARACTER SET utf8mb4; diff --git a/collector/benchmarks/diesel/migrations/mysql/20170603131224_add_likes/down.sql b/collector/benchmarks/diesel/migrations/mysql/20170603131224_add_likes/down.sql new file mode 100644 index 000000000..f7c67fce5 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170603131224_add_likes/down.sql @@ -0,0 +1 @@ +DROP TABLE likes; diff --git a/collector/benchmarks/diesel/migrations/mysql/20170603131224_add_likes/up.sql b/collector/benchmarks/diesel/migrations/mysql/20170603131224_add_likes/up.sql new file mode 100644 index 000000000..cb37d8d14 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170603131224_add_likes/up.sql @@ -0,0 +1,5 @@ +CREATE TABLE likes ( + comment_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + PRIMARY KEY (comment_id, user_id) +); diff --git a/collector/benchmarks/diesel/migrations/mysql/20170804172328_add_foreign_keys/down.sql b/collector/benchmarks/diesel/migrations/mysql/20170804172328_add_foreign_keys/down.sql new file mode 100644 index 000000000..03a1d1831 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170804172328_add_foreign_keys/down.sql @@ -0,0 +1,6 @@ +ALTER TABLE posts DROP FOREIGN KEY posts_user_id_fkey; +ALTER TABLE comments DROP FOREIGN KEY comments_post_id_fkey; +ALTER TABLE followings DROP FOREIGN KEY followings_user_id_fkey; +ALTER TABLE followings DROP FOREIGN KEY followings_post_id_fkey; +ALTER TABLE likes DROP FOREIGN KEY likes_comment_id_fkey; +ALTER TABLE likes DROP FOREIGN KEY likes_user_id_fkey; diff --git a/collector/benchmarks/diesel/migrations/mysql/20170804172328_add_foreign_keys/up.sql b/collector/benchmarks/diesel/migrations/mysql/20170804172328_add_foreign_keys/up.sql new file mode 100644 index 000000000..dee94ba36 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170804172328_add_foreign_keys/up.sql @@ -0,0 +1,6 @@ +ALTER TABLE posts ADD CONSTRAINT posts_user_id_fkey FOREIGN KEY (user_id) REFERENCES users (id); +ALTER TABLE comments ADD CONSTRAINT comments_post_id_fkey FOREIGN KEY (post_id) REFERENCES posts (id); +ALTER TABLE followings ADD CONSTRAINT followings_user_id_fkey FOREIGN KEY (user_id) REFERENCES users (id); +ALTER TABLE followings ADD CONSTRAINT followings_post_id_fkey FOREIGN KEY (post_id) REFERENCES posts (id); +ALTER TABLE likes ADD CONSTRAINT likes_comment_id_fkey FOREIGN KEY (comment_id) REFERENCES comments (id); +ALTER TABLE likes ADD CONSTRAINT likes_user_id_fkey FOREIGN KEY (user_id) REFERENCES users (id); diff --git a/collector/benchmarks/diesel/migrations/mysql/20170805195107_tables_which_make_infer_joinable_blow_up/down.sql b/collector/benchmarks/diesel/migrations/mysql/20170805195107_tables_which_make_infer_joinable_blow_up/down.sql new file mode 100644 index 000000000..568c8ed82 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170805195107_tables_which_make_infer_joinable_blow_up/down.sql @@ -0,0 +1,6 @@ +DROP TABLE self_referential_fk; +DROP TABLE fk_doesnt_reference_pk; +DROP TABLE composite_fk; +DROP TABLE multiple_fks_to_same_table; +DROP TABLE cyclic_fk_1 CASCADE; +DROP TABLE cyclic_fk_2 CASCADE; diff --git a/collector/benchmarks/diesel/migrations/mysql/20170805195107_tables_which_make_infer_joinable_blow_up/up.sql b/collector/benchmarks/diesel/migrations/mysql/20170805195107_tables_which_make_infer_joinable_blow_up/up.sql new file mode 100644 index 000000000..2cfc789c0 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170805195107_tables_which_make_infer_joinable_blow_up/up.sql @@ -0,0 +1,36 @@ +CREATE TABLE self_referential_fk ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + parent_id INTEGER NOT NULL +); + +ALTER TABLE self_referential_fk ADD CONSTRAINT self_referential_fk_parent_id_fk FOREIGN KEY (parent_id) REFERENCES self_referential_fk (id); + +CREATE TABLE fk_doesnt_reference_pk ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + random TEXT REFERENCES posts (title) +); + +CREATE TABLE composite_fk ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + post_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + FOREIGN KEY (user_id, post_id) REFERENCES followings (user_id, post_id) +); + +CREATE TABLE multiple_fks_to_same_table ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + post_id_1 INTEGER REFERENCES posts, + post_id_2 INTEGER REFERENCES posts +); + +CREATE TABLE cyclic_fk_1 ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + cyclic_fk_2_id INTEGER +); + +CREATE TABLE cyclic_fk_2 ( + id INTEGER PRIMARY KEY AUTO_INCREMENT, + cyclic_fk_1_id INTEGER REFERENCES cyclic_fk_1 (id) +); + +ALTER TABLE cyclic_fk_1 ADD CONSTRAINT cyclic_fk_1_cyclic_fk_2_id_fk FOREIGN KEY (cyclic_fk_2_id) REFERENCES cyclic_fk_2 (id); diff --git a/collector/benchmarks/diesel/migrations/mysql/20170811104602_add_blob_types/down.sql b/collector/benchmarks/diesel/migrations/mysql/20170811104602_add_blob_types/down.sql new file mode 100644 index 000000000..d322f8997 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170811104602_add_blob_types/down.sql @@ -0,0 +1 @@ +DROP TABLE all_the_blobs; diff --git a/collector/benchmarks/diesel/migrations/mysql/20170811104602_add_blob_types/up.sql b/collector/benchmarks/diesel/migrations/mysql/20170811104602_add_blob_types/up.sql new file mode 100644 index 000000000..cb9a934e4 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170811104602_add_blob_types/up.sql @@ -0,0 +1,7 @@ +CREATE TABLE all_the_blobs ( + id INTEGER PRIMARY KEY, -- Can't use a blob as a pk + tiny TINYBLOB NOT NULL, + normal BLOB NOT NULL, + medium MEDIUMBLOB NOT NULL, + big LONGBLOB NOT NULL +) diff --git a/collector/benchmarks/diesel/migrations/mysql/20170816164349_add_keywords_table/down.sql b/collector/benchmarks/diesel/migrations/mysql/20170816164349_add_keywords_table/down.sql new file mode 100644 index 000000000..20296cf53 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170816164349_add_keywords_table/down.sql @@ -0,0 +1 @@ +DROP TABLE with_keywords; diff --git a/collector/benchmarks/diesel/migrations/mysql/20170816164349_add_keywords_table/up.sql b/collector/benchmarks/diesel/migrations/mysql/20170816164349_add_keywords_table/up.sql new file mode 100644 index 000000000..fec19b939 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/20170816164349_add_keywords_table/up.sql @@ -0,0 +1,5 @@ +CREATE TABLE with_keywords ( + fn INTEGER NOT NULL PRIMARY KEY, + let INTEGER NOT NULL, + extern INTEGER NOT NULL +); diff --git a/collector/benchmarks/diesel/migrations/mysql/2020-01-25-033332_add unsigned table/down.sql b/collector/benchmarks/diesel/migrations/mysql/2020-01-25-033332_add unsigned table/down.sql new file mode 100644 index 000000000..8ebd48e8d --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/2020-01-25-033332_add unsigned table/down.sql @@ -0,0 +1 @@ +DROP TABLE unsigned_table; \ No newline at end of file diff --git a/collector/benchmarks/diesel/migrations/mysql/2020-01-25-033332_add unsigned table/up.sql b/collector/benchmarks/diesel/migrations/mysql/2020-01-25-033332_add unsigned table/up.sql new file mode 100644 index 000000000..00f52b32e --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/2020-01-25-033332_add unsigned table/up.sql @@ -0,0 +1,4 @@ +CREATE TABLE unsigned_table ( + id INTEGER UNSIGNED PRIMARY KEY AUTO_INCREMENT, + value INTEGER UNSIGNED NOT NULL +) CHARACTER SET utf8mb4; \ No newline at end of file diff --git a/collector/benchmarks/diesel/migrations/mysql/2020-02-18-111430_create_pokes/down.sql b/collector/benchmarks/diesel/migrations/mysql/2020-02-18-111430_create_pokes/down.sql new file mode 100644 index 000000000..cdadc2916 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/2020-02-18-111430_create_pokes/down.sql @@ -0,0 +1 @@ +DROP TABLE pokes; diff --git a/collector/benchmarks/diesel/migrations/mysql/2020-02-18-111430_create_pokes/up.sql b/collector/benchmarks/diesel/migrations/mysql/2020-02-18-111430_create_pokes/up.sql new file mode 100644 index 000000000..f685fd318 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/mysql/2020-02-18-111430_create_pokes/up.sql @@ -0,0 +1,5 @@ +create table pokes ( + user_id INTEGER PRIMARY KEY NOT NULL REFERENCES users(id), + poke_count INTEGER NOT NULL, + CONSTRAINT pokes_poke_count_check CHECK (poke_count > 0) +); diff --git a/collector/benchmarks/diesel/migrations/postgresql/00000000000000_diesel_initial_setup/down.sql b/collector/benchmarks/diesel/migrations/postgresql/00000000000000_diesel_initial_setup/down.sql new file mode 100644 index 000000000..8fb31a8e5 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/00000000000000_diesel_initial_setup/down.sql @@ -0,0 +1,2 @@ +DROP FUNCTION IF EXISTS diesel_manage_updated_at(_tbl regclass); +DROP FUNCTION IF EXISTS diesel_set_updated_at(); diff --git a/collector/benchmarks/diesel/migrations/postgresql/00000000000000_diesel_initial_setup/up.sql b/collector/benchmarks/diesel/migrations/postgresql/00000000000000_diesel_initial_setup/up.sql new file mode 100644 index 000000000..d68895b1a --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/00000000000000_diesel_initial_setup/up.sql @@ -0,0 +1,36 @@ +-- This file was automatically created by Diesel to setup helper functions +-- and other internal bookkeeping. This file is safe to edit, any future +-- changes will be added to existing projects as new migrations. + + + + +-- Sets up a trigger for the given table to automatically set a column called +-- `updated_at` whenever the row is modified (unless `updated_at` was included +-- in the modified columns) +-- +-- # Example +-- +-- ```sql +-- CREATE TABLE users (id SERIAL PRIMARY KEY, updated_at TIMESTAMP NOT NULL DEFAULT NOW()); +-- +-- SELECT diesel_manage_updated_at('users'); +-- ``` +CREATE OR REPLACE FUNCTION diesel_manage_updated_at(_tbl regclass) RETURNS VOID AS $$ +BEGIN + EXECUTE format('CREATE TRIGGER set_updated_at BEFORE UPDATE ON %s + FOR EACH ROW EXECUTE PROCEDURE diesel_set_updated_at()', _tbl); +END; +$$ LANGUAGE plpgsql; + +CREATE OR REPLACE FUNCTION diesel_set_updated_at() RETURNS trigger AS $$ +BEGIN + IF ( + NEW IS DISTINCT FROM OLD AND + NEW.updated_at IS NOT DISTINCT FROM OLD.updated_at + ) THEN + NEW.updated_at := current_timestamp; + END IF; + RETURN NEW; +END; +$$ LANGUAGE plpgsql; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20151219180527_create_users_and_posts_and_comments/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20151219180527_create_users_and_posts_and_comments/down.sql new file mode 100644 index 000000000..55e7a63b1 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20151219180527_create_users_and_posts_and_comments/down.sql @@ -0,0 +1,3 @@ +DROP TABLE users; +DROP TABLE posts; +DROP TABLE comments; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20151219180527_create_users_and_posts_and_comments/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20151219180527_create_users_and_posts_and_comments/up.sql new file mode 100644 index 000000000..50ec08d3d --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20151219180527_create_users_and_posts_and_comments/up.sql @@ -0,0 +1,18 @@ +CREATE TABLE users ( + id SERIAL PRIMARY KEY, + name VARCHAR NOT NULL, + hair_color VARCHAR +); + +CREATE TABLE posts ( + id SERIAL PRIMARY KEY, + user_id INTEGER NOT NULL, + title VARCHAR NOT NULL, + body TEXT +); + +CREATE TABLE comments ( + id SERIAL PRIMARY KEY, + post_id INTEGER NOT NULL, + text TEXT NOT NULL +); diff --git a/collector/benchmarks/diesel/migrations/postgresql/20160107090901_add_tags_to_posts/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20160107090901_add_tags_to_posts/down.sql new file mode 100644 index 000000000..7b003877c --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20160107090901_add_tags_to_posts/down.sql @@ -0,0 +1 @@ +ALTER TABLE posts DROP COLUMN tags; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20160107090901_add_tags_to_posts/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20160107090901_add_tags_to_posts/up.sql new file mode 100644 index 000000000..12b6189e9 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20160107090901_add_tags_to_posts/up.sql @@ -0,0 +1 @@ +ALTER TABLE posts ADD COLUMN tags varchar[] NOT NULL DEFAULT '{}'; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20160116104628_create_special_posts_and_special_comments/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20160116104628_create_special_posts_and_special_comments/down.sql new file mode 100644 index 000000000..6759f841c --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20160116104628_create_special_posts_and_special_comments/down.sql @@ -0,0 +1,2 @@ +DROP TABLE special_posts; +DROP TABLE special_comments; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20160116104628_create_special_posts_and_special_comments/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20160116104628_create_special_posts_and_special_comments/up.sql new file mode 100644 index 000000000..075e34f9b --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20160116104628_create_special_posts_and_special_comments/up.sql @@ -0,0 +1,10 @@ +CREATE TABLE special_posts ( + id SERIAL PRIMARY KEY, + user_id INTEGER NOT NULL, + title VARCHAR NOT NULL +); + +CREATE TABLE special_comments ( + id SERIAL PRIMARY KEY, + special_post_id INTEGER NOT NULL +); diff --git a/collector/benchmarks/diesel/migrations/postgresql/20160825135747_create_followings/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20160825135747_create_followings/down.sql new file mode 100644 index 000000000..331cb0c9f --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20160825135747_create_followings/down.sql @@ -0,0 +1 @@ +DROP TABLE followings; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20160825135747_create_followings/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20160825135747_create_followings/up.sql new file mode 100644 index 000000000..b68bc1215 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20160825135747_create_followings/up.sql @@ -0,0 +1,6 @@ +CREATE TABLE followings ( + user_id INTEGER NOT NULL, + post_id INTEGER NOT NULL, + email_notifications BOOLEAN NOT NULL DEFAULT 'f', + PRIMARY KEY (user_id, post_id) +); diff --git a/collector/benchmarks/diesel/migrations/postgresql/20161206123630_create_table_custom_schema/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20161206123630_create_table_custom_schema/down.sql new file mode 100644 index 000000000..53533bb59 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20161206123630_create_table_custom_schema/down.sql @@ -0,0 +1 @@ +DROP SCHEMA custom_schema CASCADE; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20161206123630_create_table_custom_schema/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20161206123630_create_table_custom_schema/up.sql new file mode 100644 index 000000000..13df19cfb --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20161206123630_create_table_custom_schema/up.sql @@ -0,0 +1,2 @@ +CREATE SCHEMA custom_schema; +CREATE TABLE custom_schema.users (id SERIAL PRIMARY KEY); diff --git a/collector/benchmarks/diesel/migrations/postgresql/2017-09-26-151545_fix_posts_tags/down.sql b/collector/benchmarks/diesel/migrations/postgresql/2017-09-26-151545_fix_posts_tags/down.sql new file mode 100644 index 000000000..748263eaf --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/2017-09-26-151545_fix_posts_tags/down.sql @@ -0,0 +1 @@ +ALTER TABLE posts ALTER COLUMN tags TYPE varchar[]; diff --git a/collector/benchmarks/diesel/migrations/postgresql/2017-09-26-151545_fix_posts_tags/up.sql b/collector/benchmarks/diesel/migrations/postgresql/2017-09-26-151545_fix_posts_tags/up.sql new file mode 100644 index 000000000..d5671d186 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/2017-09-26-151545_fix_posts_tags/up.sql @@ -0,0 +1 @@ +ALTER TABLE posts ALTER COLUMN tags TYPE text[]; diff --git a/collector/benchmarks/diesel/migrations/postgresql/2017-09-27-135928_remove_citext_table/down.sql b/collector/benchmarks/diesel/migrations/postgresql/2017-09-27-135928_remove_citext_table/down.sql new file mode 100644 index 000000000..bd603fee2 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/2017-09-27-135928_remove_citext_table/down.sql @@ -0,0 +1,2 @@ +CREATE EXTENSION IF NOT EXISTS citext; +CREATE TABLE citext_table (citext_field CITEXT PRIMARY KEY); diff --git a/collector/benchmarks/diesel/migrations/postgresql/2017-09-27-135928_remove_citext_table/up.sql b/collector/benchmarks/diesel/migrations/postgresql/2017-09-27-135928_remove_citext_table/up.sql new file mode 100644 index 000000000..e0e686efe --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/2017-09-27-135928_remove_citext_table/up.sql @@ -0,0 +1 @@ +DROP TABLE citext_table; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170209180355_add_one_off_tables_from_integration_tests/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20170209180355_add_one_off_tables_from_integration_tests/down.sql new file mode 100644 index 000000000..db631b629 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170209180355_add_one_off_tables_from_integration_tests/down.sql @@ -0,0 +1,5 @@ +DROP TABLE numbers; +DROP TABLE precision_numbers; +DROP TABLE nullable_doubles; +DROP TABLE users_with_name_pk; +DROP TABLE points; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170209180355_add_one_off_tables_from_integration_tests/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20170209180355_add_one_off_tables_from_integration_tests/up.sql new file mode 100644 index 000000000..87278ed34 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170209180355_add_one_off_tables_from_integration_tests/up.sql @@ -0,0 +1,5 @@ +CREATE TABLE numbers (n INTEGER PRIMARY KEY); +CREATE TABLE precision_numbers (n DOUBLE PRECISION NOT NULL PRIMARY KEY); +CREATE TABLE nullable_doubles (id SERIAL PRIMARY KEY, n DOUBLE PRECISION); +CREATE TABLE users_with_name_pk (name VARCHAR PRIMARY KEY); +CREATE TABLE points (x INTEGER NOT NULL, y INTEGER NOT NULL, PRIMARY KEY (x, y)); diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170211150830_index_columns_used_in_benchmarks/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20170211150830_index_columns_used_in_benchmarks/down.sql new file mode 100644 index 000000000..c19dc97fe --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170211150830_index_columns_used_in_benchmarks/down.sql @@ -0,0 +1,2 @@ +DROP INDEX users_hair_color; +DROP INDEX posts_user_id; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170211150830_index_columns_used_in_benchmarks/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20170211150830_index_columns_used_in_benchmarks/up.sql new file mode 100644 index 000000000..b8ecd60c3 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170211150830_index_columns_used_in_benchmarks/up.sql @@ -0,0 +1,2 @@ +CREATE INDEX users_hair_color ON users (hair_color); +CREATE INDEX posts_user_id ON posts (user_id); diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170215170122_create_trees/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20170215170122_create_trees/down.sql new file mode 100644 index 000000000..1135e76a6 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170215170122_create_trees/down.sql @@ -0,0 +1 @@ +DROP TABLE trees; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170215170122_create_trees/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20170215170122_create_trees/up.sql new file mode 100644 index 000000000..c9b7c62a0 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170215170122_create_trees/up.sql @@ -0,0 +1,4 @@ +CREATE TABLE trees ( + id SERIAL PRIMARY KEY, + parent_id INTEGER +); diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170219200159_add_foreign_key/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20170219200159_add_foreign_key/down.sql new file mode 100644 index 000000000..9dea4d73a --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170219200159_add_foreign_key/down.sql @@ -0,0 +1,2 @@ +DROP TABLE fk_tests; +DROP TABLE fk_inits; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170219200159_add_foreign_key/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20170219200159_add_foreign_key/up.sql new file mode 100644 index 000000000..edfa9efc0 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170219200159_add_foreign_key/up.sql @@ -0,0 +1,8 @@ +CREATE TABLE fk_inits ( + id INTEGER PRIMARY KEY +); + +CREATE TABLE fk_tests ( + id INTEGER PRIMARY KEY, + fk_id INTEGER NOT NULL REFERENCES fk_inits(id) +); diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170407152306_add_nullable_table/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20170407152306_add_nullable_table/down.sql new file mode 100644 index 000000000..ff84418a4 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170407152306_add_nullable_table/down.sql @@ -0,0 +1 @@ +DROP TABLE nullable_table; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170407152306_add_nullable_table/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20170407152306_add_nullable_table/up.sql new file mode 100644 index 000000000..f716036ba --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170407152306_add_nullable_table/up.sql @@ -0,0 +1,4 @@ +CREATE TABLE nullable_table ( + id SERIAL PRIMARY KEY, + value INTEGER +); diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170603131224_add_likes/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20170603131224_add_likes/down.sql new file mode 100644 index 000000000..f7c67fce5 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170603131224_add_likes/down.sql @@ -0,0 +1 @@ +DROP TABLE likes; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170603131224_add_likes/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20170603131224_add_likes/up.sql new file mode 100644 index 000000000..cb37d8d14 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170603131224_add_likes/up.sql @@ -0,0 +1,5 @@ +CREATE TABLE likes ( + comment_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + PRIMARY KEY (comment_id, user_id) +); diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170717152137_add_ranges/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20170717152137_add_ranges/down.sql new file mode 100644 index 000000000..3720ef73c --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170717152137_add_ranges/down.sql @@ -0,0 +1 @@ +DROP TABLE all_the_ranges diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170717152137_add_ranges/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20170717152137_add_ranges/up.sql new file mode 100644 index 000000000..655a7ac49 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170717152137_add_ranges/up.sql @@ -0,0 +1,8 @@ +CREATE TABLE all_the_ranges ( + int4 INT4RANGE PRIMARY KEY, + int8 INT8RANGE NOT NULL, + num NUMRANGE NOT NULL, + ts TSRANGE NOT NULL, + tstz TSTZRANGE NOT NULL, + date DATERANGE NOT NULL +) diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170721184144_create_citext_table/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20170721184144_create_citext_table/down.sql new file mode 100644 index 000000000..e0e686efe --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170721184144_create_citext_table/down.sql @@ -0,0 +1 @@ +DROP TABLE citext_table; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170721184144_create_citext_table/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20170721184144_create_citext_table/up.sql new file mode 100644 index 000000000..bd603fee2 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170721184144_create_citext_table/up.sql @@ -0,0 +1,2 @@ +CREATE EXTENSION IF NOT EXISTS citext; +CREATE TABLE citext_table (citext_field CITEXT PRIMARY KEY); diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170804172328_add_foreign_keys/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20170804172328_add_foreign_keys/down.sql new file mode 100644 index 000000000..f195a013e --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170804172328_add_foreign_keys/down.sql @@ -0,0 +1,6 @@ +ALTER TABLE posts DROP CONSTRAINT posts_user_id_fkey; +ALTER TABLE comments DROP CONSTRAINT comments_post_id_fkey; +ALTER TABLE followings DROP CONSTRAINT followings_user_id_fkey; +ALTER TABLE followings DROP CONSTRAINT followings_post_id_fkey; +ALTER TABLE likes DROP CONSTRAINT likes_comment_id_fkey; +ALTER TABLE likes DROP CONSTRAINT likes_user_id_fkey; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170804172328_add_foreign_keys/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20170804172328_add_foreign_keys/up.sql new file mode 100644 index 000000000..80cc1f8d6 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170804172328_add_foreign_keys/up.sql @@ -0,0 +1,6 @@ +ALTER TABLE posts ADD CONSTRAINT posts_user_id_fkey FOREIGN KEY (user_id) REFERENCES users DEFERRABLE; +ALTER TABLE comments ADD CONSTRAINT comments_post_id_fkey FOREIGN KEY (post_id) REFERENCES posts DEFERRABLE; +ALTER TABLE followings ADD CONSTRAINT followings_user_id_fkey FOREIGN KEY (user_id) REFERENCES users DEFERRABLE; +ALTER TABLE followings ADD CONSTRAINT followings_post_id_fkey FOREIGN KEY (post_id) REFERENCES posts DEFERRABLE; +ALTER TABLE likes ADD CONSTRAINT likes_comment_id_fkey FOREIGN KEY (comment_id) REFERENCES comments DEFERRABLE; +ALTER TABLE likes ADD CONSTRAINT likes_user_id_fkey FOREIGN KEY (user_id) REFERENCES users DEFERRABLE; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170805195107_tables_which_make_infer_joinable_blow_up/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20170805195107_tables_which_make_infer_joinable_blow_up/down.sql new file mode 100644 index 000000000..36f05ade1 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170805195107_tables_which_make_infer_joinable_blow_up/down.sql @@ -0,0 +1,7 @@ +DROP TABLE self_referential_fk; +DROP TABLE fk_doesnt_reference_pk; +ALTER TABLE posts DROP CONSTRAINT title_is_unique; +DROP TABLE composite_fk; +DROP TABLE multiple_fks_to_same_table; +DROP TABLE cyclic_fk_2 CASCADE; +DROP TABLE cyclic_fk_1 CASCADE; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170805195107_tables_which_make_infer_joinable_blow_up/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20170805195107_tables_which_make_infer_joinable_blow_up/up.sql new file mode 100644 index 000000000..16ffe227d --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170805195107_tables_which_make_infer_joinable_blow_up/up.sql @@ -0,0 +1,37 @@ +CREATE TABLE self_referential_fk ( + id SERIAL PRIMARY KEY, + parent_id INTEGER NOT NULL +); + +ALTER TABLE self_referential_fk ADD CONSTRAINT self_referential_fk_parent_id_fk FOREIGN KEY (parent_id) REFERENCES self_referential_fk; + +ALTER TABLE posts ADD CONSTRAINT title_is_unique UNIQUE (title); +CREATE TABLE fk_doesnt_reference_pk ( + id SERIAL PRIMARY KEY, + random TEXT REFERENCES posts (title) +); + +CREATE TABLE composite_fk ( + id SERIAL PRIMARY KEY, + post_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + FOREIGN KEY (user_id, post_id) REFERENCES followings +); + +CREATE TABLE multiple_fks_to_same_table ( + id SERIAL PRIMARY KEY, + post_id_1 INTEGER REFERENCES posts, + post_id_2 INTEGER REFERENCES posts +); + +CREATE TABLE cyclic_fk_1 ( + id SERIAL PRIMARY KEY, + cyclic_fk_2_id INTEGER +); + +CREATE TABLE cyclic_fk_2 ( + id SERIAL PRIMARY KEY, + cyclic_fk_1_id INTEGER REFERENCES cyclic_fk_1 +); + +ALTER TABLE cyclic_fk_1 ADD CONSTRAINT cyclic_fk_1_cyclic_fk_2_id_fk FOREIGN KEY (cyclic_fk_2_id) REFERENCES cyclic_fk_2; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170816164352_add_keywords_table/down.sql b/collector/benchmarks/diesel/migrations/postgresql/20170816164352_add_keywords_table/down.sql new file mode 100644 index 000000000..20296cf53 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170816164352_add_keywords_table/down.sql @@ -0,0 +1 @@ +DROP TABLE with_keywords; diff --git a/collector/benchmarks/diesel/migrations/postgresql/20170816164352_add_keywords_table/up.sql b/collector/benchmarks/diesel/migrations/postgresql/20170816164352_add_keywords_table/up.sql new file mode 100644 index 000000000..3574ab861 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/20170816164352_add_keywords_table/up.sql @@ -0,0 +1,5 @@ +CREATE TABLE with_keywords ( + fn INTEGER PRIMARY KEY, + let INTEGER NOT NULL, + extern INTEGER NOT NULL +); diff --git a/collector/benchmarks/diesel/migrations/postgresql/2020-02-18-111430_create_pokes/down.sql b/collector/benchmarks/diesel/migrations/postgresql/2020-02-18-111430_create_pokes/down.sql new file mode 100644 index 000000000..cdadc2916 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/2020-02-18-111430_create_pokes/down.sql @@ -0,0 +1 @@ +DROP TABLE pokes; diff --git a/collector/benchmarks/diesel/migrations/postgresql/2020-02-18-111430_create_pokes/up.sql b/collector/benchmarks/diesel/migrations/postgresql/2020-02-18-111430_create_pokes/up.sql new file mode 100644 index 000000000..f685fd318 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/postgresql/2020-02-18-111430_create_pokes/up.sql @@ -0,0 +1,5 @@ +create table pokes ( + user_id INTEGER PRIMARY KEY NOT NULL REFERENCES users(id), + poke_count INTEGER NOT NULL, + CONSTRAINT pokes_poke_count_check CHECK (poke_count > 0) +); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20151219180527_create_users_and_posts_and_comments/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20151219180527_create_users_and_posts_and_comments/down.sql new file mode 100644 index 000000000..55e7a63b1 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20151219180527_create_users_and_posts_and_comments/down.sql @@ -0,0 +1,3 @@ +DROP TABLE users; +DROP TABLE posts; +DROP TABLE comments; diff --git a/collector/benchmarks/diesel/migrations/sqlite/20151219180527_create_users_and_posts_and_comments/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20151219180527_create_users_and_posts_and_comments/up.sql new file mode 100644 index 000000000..e26da6e1c --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20151219180527_create_users_and_posts_and_comments/up.sql @@ -0,0 +1,18 @@ +CREATE TABLE users ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + name VARCHAR NOT NULL, + hair_color VARCHAR +); + +CREATE TABLE posts ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + user_id INTEGER NOT NULL, + title VARCHAR NOT NULL, + body TEXT +); + +CREATE TABLE comments ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + post_id INTEGER NOT NULL, + text TEXT NOT NULL +); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20160116104628_create_special_posts_and_special_comments/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20160116104628_create_special_posts_and_special_comments/down.sql new file mode 100644 index 000000000..6759f841c --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20160116104628_create_special_posts_and_special_comments/down.sql @@ -0,0 +1,2 @@ +DROP TABLE special_posts; +DROP TABLE special_comments; diff --git a/collector/benchmarks/diesel/migrations/sqlite/20160116104628_create_special_posts_and_special_comments/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20160116104628_create_special_posts_and_special_comments/up.sql new file mode 100644 index 000000000..9b860a13f --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20160116104628_create_special_posts_and_special_comments/up.sql @@ -0,0 +1,10 @@ +CREATE TABLE special_posts ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER NOT NULL, + title VARCHAR NOT NULL +); + +CREATE TABLE special_comments ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + special_post_id INTEGER NOT NULL +); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20160416143735_infer_all_the_types/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20160416143735_infer_all_the_types/down.sql new file mode 100644 index 000000000..61e4546a7 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20160416143735_infer_all_the_types/down.sql @@ -0,0 +1,4 @@ +DROP TABLE infer_all_the_ints; +DROP TABLE infer_all_the_bools; +DROP TABLE infer_all_the_strings; +DROP TABLE infer_all_the_floats; diff --git a/collector/benchmarks/diesel/migrations/sqlite/20160416143735_infer_all_the_types/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20160416143735_infer_all_the_types/up.sql new file mode 100644 index 000000000..03e7e9864 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20160416143735_infer_all_the_types/up.sql @@ -0,0 +1,46 @@ +-- Semi-exhaustive checking of many possible invocations of supported types +-- listed at https://www.sqlite.org/datatype3.html to ensure it compiles +CREATE TABLE infer_all_the_ints ( + col1 INTEGER PRIMARY KEY NOT NULL, + col2 INT NOT NULL, + col3 INTEGER NOT NULL, + col4 LOL_WHAT_EVEN_IS_THIS_TYPE_CAN_I_HAVE_A_HINT NOT NULL, + col5 SMALLINT NOT NULL, + col6 SMALLINT(2) NOT NULL, + col7 SMALL INT NOT NULL, + col8 BIGINT NOT NULL, + col9 BIGINT(4) NOT NULL, + col10 BIG INT NOT NULL, + col11 INT2 NOT NULL, + col12 INT4 NOT NULL, + col13 INT8 NOT NULL +); + +CREATE TABLE infer_all_the_bools ( + col1 TINYINT(1) PRIMARY KEY NOT NULL, + col2 TINYINT NOT NULL, + col3 TINY INT NOT NULL, + col4 BOOLEAN NOT NULL +); + +CREATE TABLE infer_all_the_strings ( + col1 CHARACTER(20) PRIMARY KEY NOT NULL, + col2 VARCHAR(255) NOT NULL, + col3 VARYING CHARACTER(255) NOT NULL, + col4 NCHAR(55) NOT NULL, + col5 NATIVE CHARACTER(70) NOT NULL, + col6 NVARCHAR(100) NOT NULL, + col7 TEXT NOT NULL, + col8 CLOB NOT NULL, + col9 BLOB NOT NULL, + col10 NOT NULL +); + +CREATE TABLE infer_all_the_floats ( + col1 REAL PRIMARY KEY NOT NULL, + col2 FLOAT NOT NULL, + col3 DOUBLE NOT NULL, + col4 DOUBLE PRECISION NOT NULL, + col5 NUMERIC NOT NULL, + col6 DECIMAL(10, 5) NOT NULL +) diff --git a/collector/benchmarks/diesel/migrations/sqlite/20160825135747_create_followings/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20160825135747_create_followings/down.sql new file mode 100644 index 000000000..331cb0c9f --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20160825135747_create_followings/down.sql @@ -0,0 +1 @@ +DROP TABLE followings; diff --git a/collector/benchmarks/diesel/migrations/sqlite/20160825135747_create_followings/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20160825135747_create_followings/up.sql new file mode 100644 index 000000000..d1ec723ba --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20160825135747_create_followings/up.sql @@ -0,0 +1,6 @@ +CREATE TABLE followings ( + user_id INTEGER NOT NULL, + post_id INTEGER NOT NULL, + email_notifications BOOLEAN NOT NULL DEFAULT 0, + PRIMARY KEY (user_id, post_id) +); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170209180355_add_one_off_tables_from_integration_tests/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20170209180355_add_one_off_tables_from_integration_tests/down.sql new file mode 100644 index 000000000..db631b629 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170209180355_add_one_off_tables_from_integration_tests/down.sql @@ -0,0 +1,5 @@ +DROP TABLE numbers; +DROP TABLE precision_numbers; +DROP TABLE nullable_doubles; +DROP TABLE users_with_name_pk; +DROP TABLE points; diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170209180355_add_one_off_tables_from_integration_tests/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20170209180355_add_one_off_tables_from_integration_tests/up.sql new file mode 100644 index 000000000..9adff778a --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170209180355_add_one_off_tables_from_integration_tests/up.sql @@ -0,0 +1,5 @@ +CREATE TABLE numbers (n INTEGER PRIMARY KEY); +CREATE TABLE precision_numbers (n DOUBLE PRECISION NOT NULL PRIMARY KEY); +CREATE TABLE nullable_doubles (id INTEGER PRIMARY KEY AUTOINCREMENT, n DOUBLE PRECISION); +CREATE TABLE users_with_name_pk (name VARCHAR PRIMARY KEY); +CREATE TABLE points (x INTEGER NOT NULL, y INTEGER NOT NULL, PRIMARY KEY (x, y)); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170211150830_index_columns_used_in_benchmarks/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20170211150830_index_columns_used_in_benchmarks/down.sql new file mode 100644 index 000000000..c19dc97fe --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170211150830_index_columns_used_in_benchmarks/down.sql @@ -0,0 +1,2 @@ +DROP INDEX users_hair_color; +DROP INDEX posts_user_id; diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170211150830_index_columns_used_in_benchmarks/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20170211150830_index_columns_used_in_benchmarks/up.sql new file mode 100644 index 000000000..b8ecd60c3 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170211150830_index_columns_used_in_benchmarks/up.sql @@ -0,0 +1,2 @@ +CREATE INDEX users_hair_color ON users (hair_color); +CREATE INDEX posts_user_id ON posts (user_id); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170215170122_create_trees/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20170215170122_create_trees/down.sql new file mode 100644 index 000000000..1135e76a6 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170215170122_create_trees/down.sql @@ -0,0 +1 @@ +DROP TABLE trees; diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170215170122_create_trees/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20170215170122_create_trees/up.sql new file mode 100644 index 000000000..1b07e5cfe --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170215170122_create_trees/up.sql @@ -0,0 +1,4 @@ +CREATE TABLE trees ( + id INTEGER PRIMARY KEY NOT NULL, + parent_id INTEGER +); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170219200159_add_foreign_key/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20170219200159_add_foreign_key/down.sql new file mode 100644 index 000000000..9dea4d73a --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170219200159_add_foreign_key/down.sql @@ -0,0 +1,2 @@ +DROP TABLE fk_tests; +DROP TABLE fk_inits; diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170219200159_add_foreign_key/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20170219200159_add_foreign_key/up.sql new file mode 100644 index 000000000..b6ebedbc2 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170219200159_add_foreign_key/up.sql @@ -0,0 +1,9 @@ +CREATE TABLE fk_inits ( + id INTEGER PRIMARY KEY +); + +CREATE TABLE fk_tests ( + id INTEGER PRIMARY KEY, + fk_id INTEGER NOT NULL, + FOREIGN KEY(fk_id) REFERENCES fk_inits(id) +); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170407152306_add_nullable_table/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20170407152306_add_nullable_table/down.sql new file mode 100644 index 000000000..ff84418a4 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170407152306_add_nullable_table/down.sql @@ -0,0 +1 @@ +DROP TABLE nullable_table; diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170407152306_add_nullable_table/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20170407152306_add_nullable_table/up.sql new file mode 100644 index 000000000..2ccb6af34 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170407152306_add_nullable_table/up.sql @@ -0,0 +1,4 @@ +CREATE TABLE nullable_table ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + value INTEGER +); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170513174919_infer_all_the_datetime_types/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20170513174919_infer_all_the_datetime_types/down.sql new file mode 100644 index 000000000..aceb295d2 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170513174919_infer_all_the_datetime_types/down.sql @@ -0,0 +1 @@ +DROP TABLE infer_all_the_datetime_types; diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170513174919_infer_all_the_datetime_types/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20170513174919_infer_all_the_datetime_types/up.sql new file mode 100644 index 000000000..63b012db9 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170513174919_infer_all_the_datetime_types/up.sql @@ -0,0 +1,6 @@ +CREATE TABLE infer_all_the_datetime_types ( + dt DATETIME PRIMARY KEY NOT NULL, + date DATE NOT NULL, + time TIME NOT NULL, + timestamp TIMESTAMP NOT NULL +); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170603131224_add_likes/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20170603131224_add_likes/down.sql new file mode 100644 index 000000000..f7c67fce5 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170603131224_add_likes/down.sql @@ -0,0 +1 @@ +DROP TABLE likes; diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170603131224_add_likes/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20170603131224_add_likes/up.sql new file mode 100644 index 000000000..cb37d8d14 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170603131224_add_likes/up.sql @@ -0,0 +1,5 @@ +CREATE TABLE likes ( + comment_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + PRIMARY KEY (comment_id, user_id) +); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170804172328_add_foreign_keys/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20170804172328_add_foreign_keys/down.sql new file mode 100644 index 000000000..4526e15ec --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170804172328_add_foreign_keys/down.sql @@ -0,0 +1,33 @@ +-- SQLite has no useful ALTER TABLE statement for this, so we must drop and +-- re-create them. Table definitions came from `SELECT sql FROM sqlite_master` + +DROP TABLE likes; +CREATE TABLE likes ( + comment_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + PRIMARY KEY (comment_id, user_id) +); + +DROP TABLE followings; +CREATE TABLE followings ( + user_id INTEGER NOT NULL, + post_id INTEGER NOT NULL, + email_notifications BOOLEAN NOT NULL DEFAULT 0, + PRIMARY KEY (user_id, post_id) +); + +DROP TABLE comments; +CREATE TABLE comments ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + post_id INTEGER NOT NULL, + text TEXT NOT NULL +); + +DROP TABLE posts; +CREATE TABLE posts ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + user_id INTEGER NOT NULL, + title VARCHAR NOT NULL, + body TEXT +); +CREATE INDEX posts_user_id ON posts (user_id); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170804172328_add_foreign_keys/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20170804172328_add_foreign_keys/up.sql new file mode 100644 index 000000000..0b8329578 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170804172328_add_foreign_keys/up.sql @@ -0,0 +1,39 @@ +-- SQLite has no useful ALTER TABLE statement for this, so we must drop and +-- re-create them. Table definitions came from `SELECT sql FROM sqlite_master` + +DROP TABLE posts; +CREATE TABLE posts ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + user_id INTEGER NOT NULL, + title VARCHAR NOT NULL, + body TEXT, + FOREIGN KEY (user_id) REFERENCES users (id) +); +CREATE INDEX posts_user_id ON posts (user_id); + +DROP TABLE comments; +CREATE TABLE comments ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + post_id INTEGER NOT NULL, + text TEXT NOT NULL, + FOREIGN KEY (post_id) REFERENCES posts (id) +); + +DROP TABLE followings; +CREATE TABLE followings ( + user_id INTEGER NOT NULL, + post_id INTEGER NOT NULL, + email_notifications BOOLEAN NOT NULL DEFAULT 0, + PRIMARY KEY (user_id, post_id), + FOREIGN KEY (user_id) REFERENCES users (id), + FOREIGN KEY (post_id) REFERENCES posts (id) +); + +DROP TABLE likes; +CREATE TABLE likes ( + comment_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + PRIMARY KEY (comment_id, user_id), + FOREIGN KEY (user_id) REFERENCES users (id), + FOREIGN KEY (comment_id) REFERENCES comments (id) +); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170805195107_tables_which_make_infer_joinable_blow_up/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20170805195107_tables_which_make_infer_joinable_blow_up/down.sql new file mode 100644 index 000000000..0813f33ee --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170805195107_tables_which_make_infer_joinable_blow_up/down.sql @@ -0,0 +1,7 @@ +DROP TABLE self_referential_fk; +DROP INDEX posts_title_is_unique; +DROP TABLE fk_doesnt_reference_pk; +DROP TABLE composite_fk; +DROP TABLE multiple_fks_to_same_table; +DROP TABLE cyclic_fk_2; +DROP TABLE cyclic_fk_1; diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170805195107_tables_which_make_infer_joinable_blow_up/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20170805195107_tables_which_make_infer_joinable_blow_up/up.sql new file mode 100644 index 000000000..ad39d3323 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170805195107_tables_which_make_infer_joinable_blow_up/up.sql @@ -0,0 +1,39 @@ +CREATE TABLE self_referential_fk ( + id INTEGER PRIMARY KEY, + parent_id INTEGER NOT NULL, + FOREIGN KEY (parent_id) REFERENCES self_referential_fk (id) +); + +CREATE UNIQUE INDEX posts_title_is_unique ON posts (title); +CREATE TABLE fk_doesnt_reference_pk ( + id INTEGER PRIMARY KEY, + random TEXT, + FOREIGN KEY (random) REFERENCES posts (title) +); + +CREATE TABLE composite_fk ( + id INTEGER PRIMARY KEY, + post_id INTEGER NOT NULL, + user_id INTEGER NOT NULL, + FOREIGN KEY (user_id, post_id) REFERENCES followings (user_id, post_id) +); + +CREATE TABLE multiple_fks_to_same_table ( + id INTEGER PRIMARY KEY, + post_id_1, + post_id_2, + FOREIGN KEY (post_id_1) REFERENCES posts (id), + FOREIGN KEY (post_id_2) REFERENCES posts (id) +); + +CREATE TABLE cyclic_fk_1 ( + id INTEGER PRIMARY KEY, + cyclic_fk_2_id, + FOREIGN KEY (cyclic_fk_2_id) REFERENCES cyclic_fk_2 (id) +); + +CREATE TABLE cyclic_fk_2 ( + id INTEGER PRIMARY KEY, + cyclic_fk_1_id, + FOREIGN KEY (cyclic_fk_1_id) REFERENCES cyclic_fk_1 (id) +); diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170816164356_add_keywords_table/down.sql b/collector/benchmarks/diesel/migrations/sqlite/20170816164356_add_keywords_table/down.sql new file mode 100644 index 000000000..20296cf53 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170816164356_add_keywords_table/down.sql @@ -0,0 +1 @@ +DROP TABLE with_keywords; diff --git a/collector/benchmarks/diesel/migrations/sqlite/20170816164356_add_keywords_table/up.sql b/collector/benchmarks/diesel/migrations/sqlite/20170816164356_add_keywords_table/up.sql new file mode 100644 index 000000000..fec19b939 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/20170816164356_add_keywords_table/up.sql @@ -0,0 +1,5 @@ +CREATE TABLE with_keywords ( + fn INTEGER NOT NULL PRIMARY KEY, + let INTEGER NOT NULL, + extern INTEGER NOT NULL +); diff --git a/collector/benchmarks/diesel/migrations/sqlite/2020-02-18-111430_create_pokes/down.sql b/collector/benchmarks/diesel/migrations/sqlite/2020-02-18-111430_create_pokes/down.sql new file mode 100644 index 000000000..cdadc2916 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/2020-02-18-111430_create_pokes/down.sql @@ -0,0 +1 @@ +DROP TABLE pokes; diff --git a/collector/benchmarks/diesel/migrations/sqlite/2020-02-18-111430_create_pokes/up.sql b/collector/benchmarks/diesel/migrations/sqlite/2020-02-18-111430_create_pokes/up.sql new file mode 100644 index 000000000..f685fd318 --- /dev/null +++ b/collector/benchmarks/diesel/migrations/sqlite/2020-02-18-111430_create_pokes/up.sql @@ -0,0 +1,5 @@ +create table pokes ( + user_id INTEGER PRIMARY KEY NOT NULL REFERENCES users(id), + poke_count INTEGER NOT NULL, + CONSTRAINT pokes_poke_count_check CHECK (poke_count > 0) +); diff --git a/collector/benchmarks/diesel/perf-config.json b/collector/benchmarks/diesel/perf-config.json new file mode 100644 index 000000000..efc44675e --- /dev/null +++ b/collector/benchmarks/diesel/perf-config.json @@ -0,0 +1,4 @@ +{ + "cargo_toml": "diesel/Cargo.toml", + "touch_file": "diesel/src/lib.rs" +}