sqlx-0.8.3/.cargo_vcs_info.json0000644000000001360000000000100120330ustar { "git": { "sha1": "28cfdbb40c4fe535721c9ee5e1583409e0cac27e" }, "path_in_vcs": "" }sqlx-0.8.3/.editorconfig000064400000000000000000000002141046102023000132750ustar 00000000000000root = true [*] charset = utf-8 end_of_line = lf insert_final_newline = true indent_style = space indent_size = 4 [*.yml] indent_size = 2 sqlx-0.8.3/.gitattributes000064400000000000000000000000231046102023000135110ustar 00000000000000* text=auto eol=lf sqlx-0.8.3/.github/ISSUE_TEMPLATE/bug_report.md000064400000000000000000000010171046102023000170370ustar 00000000000000--- name: I think I found a bug in SQLx about: Create a bug-report issue. title: '' labels: 'bug' assignees: '' --- ### Bug Description A clear and concise description of what the bug is. ### Minimal Reproduction A small code snippet or a link to a Github repo or Gist, with instructions on reproducing the bug. ### Info * SQLx version: [REQUIRED] * SQLx features enabled: [REQUIRED] * Database server and version: [REQUIRED] (MySQL / Postgres / SQLite ) * Operating system: [REQUIRED] * `rustc --version`: [REQUIRED] sqlx-0.8.3/.github/ISSUE_TEMPLATE/config.yml000064400000000000000000000012131046102023000163330ustar 00000000000000blank_issues_enabled: false contact_links: - name: Enabling some optional features adds unexpected crates to Cargo.lock url: https://github.com/launchbadge/sqlx/issues/3211 about: See this issue. - name: I have a question or problem url: https://github.com/launchbadge/sqlx/tree/main/FAQ.md about: See our FAQ. - name: I have a question or problem not covered in the FAQ url: https://github.com/launchbadge/sqlx/discussions/new?category=q-a about: Open a Q&A discussion. - name: Join SQLx's Discord url: https://discord.gg/hPm3WqA about: Join our Discord server for help, discussions and release announcements. sqlx-0.8.3/.github/ISSUE_TEMPLATE/feature_request.md000064400000000000000000000011571046102023000200770ustar 00000000000000--- name: I have a feature request for SQLx about: Create a feature-request issue. title: '' labels: 'enhancement' assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. sqlx-0.8.3/.github/pull_request_template.md000064400000000000000000000001261046102023000171230ustar 00000000000000### Does your PR solve an issue? ### Delete this text and add "fixes #(issue number)" sqlx-0.8.3/.github/workflows/examples.yml000064400000000000000000000144011046102023000165610ustar 00000000000000name: Examples on: pull_request: push: branches: - main - '*-dev' jobs: sqlx-cli: name: Build SQLx CLI runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Use latest Rust run: rustup override set stable - uses: Swatinem/rust-cache@v2 with: key: sqlx-cli - run: > cargo build -p sqlx-cli --bin sqlx --release --no-default-features --features mysql,postgres,sqlite - uses: actions/upload-artifact@v4 with: name: sqlx-cli path: target/release/sqlx mysql: name: MySQL Examples runs-on: ubuntu-latest needs: sqlx-cli services: mysql: image: mysql:latest env: MYSQL_ROOT_PASSWORD: password ports: - 3306:3306 steps: - name: Get SQLx-CLI uses: actions/download-artifact@v4 with: name: sqlx-cli # $HOME is interpreted differently by the shell path: /home/runner/.local/bin - run: | ls -R /home/runner/.local/bin chmod +x /home/runner/.local/bin/sqlx echo /home/runner/.local/bin >> $GITHUB_PATH sleep 10 - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 with: key: mysql-examples - name: Todos (Setup) working-directory: examples/mysql/todos env: DATABASE_URL: mysql://root:password@localhost:3306/todos?ssl-mode=disabled run: sqlx db setup - name: Todos (Run) env: DATABASE_URL: mysql://root:password@localhost:3306/todos?ssl-mode=disabled run: cargo run -p sqlx-example-mysql-todos postgres: name: PostgreSQL Examples runs-on: ubuntu-latest needs: sqlx-cli services: postgres: image: postgres:latest env: POSTGRES_PASSWORD: password ports: - 5432:5432 steps: - name: Get SQLx-CLI uses: actions/download-artifact@v4 with: name: sqlx-cli path: /home/runner/.local/bin - run: | ls -R /home/runner/.local/bin chmod +x $HOME/.local/bin/sqlx echo $HOME/.local/bin >> $GITHUB_PATH sleep 10 - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 with: key: pg-examples - name: Axum Social with Tests (Setup) working-directory: examples/postgres/axum-social-with-tests env: DATABASE_URL: postgres://postgres:password@localhost:5432/axum-social run: sqlx db setup - name: Axum Social with Tests (Check) env: DATABASE_URL: postgres://postgres:password@localhost:5432/axum-social run: cargo check -p sqlx-example-postgres-axum-social - name: Axum Social with Tests (Test) env: DATABASE_URL: postgres://postgres:password@localhost:5432/axum-social run: cargo test -p sqlx-example-postgres-axum-social # The Chat example has an interactive TUI which is not trivial to test automatically, # so we only check that it compiles. - name: Chat (Check) run: cargo check -p sqlx-example-postgres-chat - name: Files (Setup) working-directory: examples/postgres/files env: DATABASE_URL: postgres://postgres:password@localhost:5432/files run: sqlx db setup - name: Files (Run) env: DATABASE_URL: postgres://postgres:password@localhost:5432/files run: cargo run -p sqlx-example-postgres-files - name: JSON (Setup) working-directory: examples/postgres/json env: DATABASE_URL: postgres://postgres:password@localhost:5432/json run: sqlx db setup - name: JSON (Run) env: DATABASE_URL: postgres://postgres:password@localhost:5432/json run: cargo run -p sqlx-example-postgres-json - name: Listen (Setup) working-directory: examples/postgres/listen env: DATABASE_URL: postgres://postgres:password@localhost:5432/listen run: sqlx db create - name: Listen (Run) env: DATABASE_URL: postgres://postgres:password@localhost:5432/listen run: cargo run -p sqlx-example-postgres-listen - name: Mockable TODOs (Setup) working-directory: examples/postgres/mockable-todos env: DATABASE_URL: postgres://postgres:password@localhost:5432/mockable-todos run: sqlx db setup - name: Mockable TODOs (Run) env: DATABASE_URL: postgres://postgres:password@localhost:5432/mockable-todos run: cargo run -p sqlx-example-postgres-mockable-todos - name: TODOs (Setup) working-directory: examples/postgres/todos env: DATABASE_URL: postgres://postgres:password@localhost:5432/todos run: sqlx db setup - name: TODOs (Run) env: DATABASE_URL: postgres://postgres:password@localhost:5432/todos # TODO: test full CLI run: cargo run -p sqlx-example-postgres-todos - name: Transaction (Setup) working-directory: examples/postgres/transaction env: DATABASE_URL: postgres://postgres:password@localhost:5432/txn run: sqlx db setup - name: Transaction (Run) env: DATABASE_URL: postgres://postgres:password@localhost:5432/txn run: cargo run -p sqlx-example-postgres-transaction sqlite: name: SQLite Examples runs-on: ubuntu-latest needs: sqlx-cli steps: - name: Get SQLx-CLI uses: actions/download-artifact@v4 with: name: sqlx-cli path: /home/runner/.local/bin - run: | ls -R /home/runner/.local/bin chmod +x /home/runner/.local/bin/sqlx echo /home/runner/.local/bin >> $GITHUB_PATH - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 with: key: sqlite-examples - name: TODOs (Setup) env: DATABASE_URL: sqlite://todos.sqlite run: sqlx db setup --source=examples/sqlite/todos/migrations - name: TODOs (Run) env: DATABASE_URL: sqlite://todos.sqlite run: cargo run -p sqlx-example-sqlite-todos sqlx-0.8.3/.github/workflows/sqlx-cli.yml000064400000000000000000000046311046102023000165030ustar 00000000000000name: SQLx CLI on: pull_request: push: branches: - main - "*-dev" jobs: check: name: Check runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - run: | rustup update rustup component add clippy rustup toolchain install beta rustup component add --toolchain beta clippy - uses: Swatinem/rust-cache@v2 - run: cargo clippy --manifest-path sqlx-cli/Cargo.toml -- -D warnings # Run beta for new warnings but don't break the build. # Use a subdirectory of `target` to avoid clobbering the cache. - run: > cargo +beta clippy --manifest-path sqlx-cli/Cargo.toml --target-dir target/beta/ test: name: Test runs-on: ${{ matrix.os }} strategy: matrix: # Note: macOS-latest uses M1 Silicon (ARM64) os: - ubuntu-latest # FIXME: migrations tests fail on Windows for whatever reason # - windows-latest - macOS-13 - macOS-latest steps: - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 with: key: ${{ runner.os }}-test - run: cargo test --manifest-path sqlx-cli/Cargo.toml build: name: Build runs-on: ${{ matrix.os }} strategy: matrix: # Note: macOS-latest uses M1 Silicon (ARM64) os: - ubuntu-latest - windows-latest - macOS-13 - macOS-latest include: - os: ubuntu-latest target: x86_64-unknown-linux-musl args: --features openssl-vendored bin: target/debug/cargo-sqlx - os: windows-latest target: x86_64-pc-windows-msvc bin: target/debug/cargo-sqlx.exe - os: macOS-13 target: x86_64-apple-darwin bin: target/debug/cargo-sqlx - os: macOS-latest target: aarch64-apple-darwin bin: target/debug/cargo-sqlx steps: - uses: actions/checkout@v4 - name: Use latest Rust run: rustup override set stable - uses: Swatinem/rust-cache@v2 with: key: ${{ runner.os }}-cli - run: cargo build --manifest-path sqlx-cli/Cargo.toml --bin cargo-sqlx ${{ matrix.args }} - uses: actions/upload-artifact@v4 with: name: cargo-sqlx-${{ matrix.target }} path: ${{ matrix.bin }} sqlx-0.8.3/.github/workflows/sqlx.yml000064400000000000000000000364331046102023000157430ustar 00000000000000name: SQLx on: pull_request: push: branches: - main - "*-dev" jobs: format: name: Format runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - run: rustup component add rustfmt - run: cargo fmt --all -- --check check: name: Check runs-on: ubuntu-22.04 strategy: matrix: runtime: [async-std, tokio] tls: [native-tls, rustls, none] steps: - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 with: key: "${{ runner.os }}-check-${{ matrix.runtime }}-${{ matrix.tls }}" - run: | rustup update rustup component add clippy rustup toolchain install beta rustup component add --toolchain beta clippy - run: > cargo clippy --no-default-features --features all-databases,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }},macros -- -D warnings # Run beta for new warnings but don't break the build. # Use a subdirectory of `target` to avoid clobbering the cache. - run: > cargo +beta clippy --no-default-features --features all-databases,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }},macros --target-dir target/beta/ check-minimal-versions: name: Check build using minimal versions runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - run: rustup update - run: rustup toolchain install nightly - run: cargo +nightly generate-lockfile -Z minimal-versions - run: cargo build --all-features test: name: Unit Tests runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 with: key: ${{ runner.os }}-test - name: Install Rust run: rustup update - name: Test sqlx-core run: > cargo test -p sqlx-core --all-features - name: Test sqlx-mysql run: > cargo test -p sqlx-mysql --all-features - name: Test sqlx-postgres run: > cargo test -p sqlx-postgres --all-features - name: Test sqlx-sqlite run: > cargo test -p sqlx-sqlite --all-features - name: Test sqlx-macros-core run: > cargo test -p sqlx-macros-core --all-features # Note: use `--lib` to not run integration tests that require a DB - name: Test sqlx run: > cargo test -p sqlx --lib --all-features sqlite: name: SQLite runs-on: ubuntu-22.04 strategy: matrix: runtime: [async-std, tokio] linking: [sqlite, sqlite-unbundled] needs: check steps: - uses: actions/checkout@v4 - run: mkdir /tmp/sqlite3-lib && wget -O /tmp/sqlite3-lib/ipaddr.so https://github.com/nalgeon/sqlean/releases/download/0.15.2/ipaddr.so - uses: Swatinem/rust-cache@v2 with: key: "${{ runner.os }}-${{ matrix.linking }}-${{ matrix.runtime }}-${{ matrix.tls }}" - name: Install system sqlite library if: ${{ matrix.linking == 'sqlite-unbundled' }} run: sudo apt-get install -y libsqlite3-dev - run: echo "using ${DATABASE_URL}" # Create data dir for offline mode - run: mkdir .sqlx - run: > cargo test --no-default-features --features any,macros,${{ matrix.linking }},_unstable-all-types,runtime-${{ matrix.runtime }} -- --test-threads=1 env: DATABASE_URL: sqlite:tests/sqlite/sqlite.db SQLX_OFFLINE_DIR: .sqlx RUSTFLAGS: --cfg sqlite_ipaddr --cfg sqlite_test_sqlcipher LD_LIBRARY_PATH: /tmp/sqlite3-lib # Remove test artifacts - run: cargo clean -p sqlx # Build the macros-test in offline mode (omit DATABASE_URL) - run: > cargo build --no-default-features --test ${{ matrix.linking }}-macros --features any,macros,${{ matrix.linking }},_unstable-all-types,runtime-${{ matrix.runtime }} env: SQLX_OFFLINE: true SQLX_OFFLINE_DIR: .sqlx RUSTFLAGS: -D warnings --cfg sqlite_ipaddr LD_LIBRARY_PATH: /tmp/sqlite3-lib # Test macros in offline mode (still needs DATABASE_URL to run) - run: > cargo test --no-default-features --test ${{ matrix.linking }}-macros --features any,macros,${{ matrix.linking }},_unstable-all-types,runtime-${{ matrix.runtime }} env: DATABASE_URL: sqlite://tests/sqlite/sqlite.db SQLX_OFFLINE: true SQLX_OFFLINE_DIR: .sqlx RUSTFLAGS: --cfg sqlite_ipaddr LD_LIBRARY_PATH: /tmp/sqlite3-lib postgres: name: Postgres runs-on: ubuntu-22.04 strategy: matrix: postgres: [17, 13] runtime: [async-std, tokio] tls: [native-tls, rustls-aws-lc-rs, rustls-ring, none] needs: check steps: - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 with: key: "${{ runner.os }}-postgres-${{ matrix.runtime }}-${{ matrix.tls }}" - env: # FIXME: needed to disable `ltree` tests in Postgres 9.6 # but `PgLTree` should just fall back to text format RUSTFLAGS: -D warnings --cfg postgres_${{ matrix.postgres }} run: cargo build --features postgres,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }} - run: | docker compose -f tests/docker-compose.yml run -d -p 5432:5432 --name postgres_${{ matrix.postgres }} postgres_${{ matrix.postgres }} docker exec postgres_${{ matrix.postgres }} bash -c "until pg_isready; do sleep 1; done" # Create data dir for offline mode - run: mkdir .sqlx - run: > cargo test --no-default-features --features any,postgres,macros,migrate,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }} env: DATABASE_URL: postgres://postgres:password@localhost:5432/sqlx SQLX_OFFLINE_DIR: .sqlx # FIXME: needed to disable `ltree` tests in Postgres 9.6 # but `PgLTree` should just fall back to text format RUSTFLAGS: --cfg postgres_${{ matrix.postgres }} - if: matrix.tls != 'none' run: > cargo test --no-default-features --features any,postgres,macros,migrate,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }} env: DATABASE_URL: postgres://postgres:password@localhost:5432/sqlx?sslmode=verify-ca&sslrootcert=.%2Ftests%2Fcerts%2Fca.crt SQLX_OFFLINE_DIR: .sqlx # FIXME: needed to disable `ltree` tests in Postgres 9.6 # but `PgLTree` should just fall back to text format RUSTFLAGS: --cfg postgres_${{ matrix.postgres }} # Remove test artifacts - run: cargo clean -p sqlx # Build the macros-test in offline mode (omit DATABASE_URL) - run: > cargo build --no-default-features --test postgres-macros --features any,postgres,macros,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }} env: SQLX_OFFLINE: true SQLX_OFFLINE_DIR: .sqlx # FIXME: needed to disable `ltree` tests in Postgres 9.6 # but `PgLTree` should just fall back to text format RUSTFLAGS: -D warnings --cfg postgres_${{ matrix.postgres }} # Test macros in offline mode (still needs DATABASE_URL to run) - run: > cargo test --no-default-features --test postgres-macros --features any,postgres,macros,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }} env: DATABASE_URL: postgres://postgres:password@localhost:5432/sqlx SQLX_OFFLINE: true SQLX_OFFLINE_DIR: .sqlx # FIXME: needed to disable `ltree` tests in Postgres 9.6 # but `PgLTree` should just fall back to text format RUSTFLAGS: --cfg postgres_${{ matrix.postgres }} # client SSL authentication - run: | docker stop postgres_${{ matrix.postgres }} docker compose -f tests/docker-compose.yml run -d -p 5432:5432 --name postgres_${{ matrix.postgres }}_client_ssl postgres_${{ matrix.postgres }}_client_ssl docker exec postgres_${{ matrix.postgres }}_client_ssl bash -c "until pg_isready; do sleep 1; done" - if: matrix.tls != 'none' run: > cargo test --no-default-features --features any,postgres,macros,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }} env: DATABASE_URL: postgres://postgres@localhost:5432/sqlx?sslmode=verify-ca&sslrootcert=.%2Ftests%2Fcerts%2Fca.crt&sslkey=.%2Ftests%2Fkeys%2Fclient.key&sslcert=.%2Ftests%2Fcerts%2Fclient.crt # FIXME: needed to disable `ltree` tests in Postgres 9.6 # but `PgLTree` should just fall back to text format RUSTFLAGS: --cfg postgres_${{ matrix.postgres }}_client_ssl mysql: name: MySQL runs-on: ubuntu-22.04 strategy: matrix: mysql: [8] runtime: [async-std, tokio] tls: [native-tls, rustls-aws-lc-rs, rustls-ring, none] needs: check steps: - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 with: key: "${{ runner.os }}-mysql-${{ matrix.runtime }}-${{ matrix.tls }}" - run: cargo build --features mysql,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }} - run: docker compose -f tests/docker-compose.yml run -d -p 3306:3306 --name mysql_${{ matrix.mysql }} mysql_${{ matrix.mysql }} - run: sleep 60 # Create data dir for offline mode - run: mkdir .sqlx - run: > cargo test --no-default-features --features any,mysql,macros,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }} env: DATABASE_URL: mysql://root:password@localhost:3306/sqlx?ssl-mode=disabled SQLX_OFFLINE_DIR: .sqlx RUSTFLAGS: --cfg mysql_${{ matrix.mysql }} # MySQL 5.7 supports TLS but not TLSv1.3 as required by RusTLS. - if: ${{ !(matrix.mysql == '5_7' && matrix.tls == 'rustls') }} run: > cargo test --no-default-features --features any,mysql,macros,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }} env: DATABASE_URL: mysql://root:password@localhost:3306/sqlx SQLX_OFFLINE_DIR: .sqlx RUSTFLAGS: --cfg mysql_${{ matrix.mysql }} # Remove test artifacts - run: cargo clean -p sqlx # Build the macros-test in offline mode (omit DATABASE_URL) - run: > cargo build --no-default-features --test mysql-macros --features any,mysql,macros,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }} env: SQLX_OFFLINE: true SQLX_OFFLINE_DIR: .sqlx RUSTFLAGS: -D warnings --cfg mysql_${{ matrix.mysql }} # Test macros in offline mode (still needs DATABASE_URL to run) # MySQL 5.7 supports TLS but not TLSv1.3 as required by RusTLS. - run: > cargo test --no-default-features --test mysql-macros --features any,mysql,macros,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }} env: DATABASE_URL: mysql://root:password@localhost:3306/sqlx SQLX_OFFLINE: true SQLX_OFFLINE_DIR: .sqlx RUSTFLAGS: --cfg mysql_${{ matrix.mysql }} # client SSL authentication - if: ${{ matrix.tls != 'none' }} run: | docker stop mysql_${{ matrix.mysql }} docker compose -f tests/docker-compose.yml run -d -p 3306:3306 --name mysql_${{ matrix.mysql }}_client_ssl mysql_${{ matrix.mysql }}_client_ssl sleep 60 - if: ${{ matrix.tls != 'none' }} run: > cargo test --no-default-features --features any,mysql,macros,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }} env: DATABASE_URL: mysql://root@localhost:3306/sqlx?sslmode=verify_ca&ssl-ca=.%2Ftests%2Fcerts%2Fca.crt&ssl-key=.%2Ftests%2Fkeys%2Fclient.key&ssl-cert=.%2Ftests%2Fcerts%2Fclient.crt RUSTFLAGS: --cfg mysql_${{ matrix.mysql }} mariadb: name: MariaDB runs-on: ubuntu-22.04 strategy: matrix: mariadb: [verylatest, 11_4, 10_11, 10_4] runtime: [async-std, tokio] tls: [native-tls, rustls-aws-lc-rs, rustls-ring, none] needs: check steps: - uses: actions/checkout@v4 - uses: Swatinem/rust-cache@v2 with: key: "${{ runner.os }}-mysql-${{ matrix.runtime }}-${{ matrix.tls }}" - run: cargo build --features mysql,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }} - run: docker compose -f tests/docker-compose.yml run -d -p 3306:3306 --name mariadb_${{ matrix.mariadb }} mariadb_${{ matrix.mariadb }} - run: sleep 30 # Create data dir for offline mode - run: mkdir .sqlx - run: > cargo test --no-default-features --features any,mysql,macros,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }} env: DATABASE_URL: mysql://root:password@localhost:3306/sqlx SQLX_OFFLINE_DIR: .sqlx RUSTFLAGS: --cfg mariadb_${{ matrix.mariadb }} # Remove test artifacts - run: cargo clean -p sqlx # Build the macros-test in offline mode (omit DATABASE_URL) - run: > cargo build --no-default-features --test mysql-macros --features any,mysql,macros,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }} env: SQLX_OFFLINE: true SQLX_OFFLINE_DIR: .sqlx RUSTFLAGS: -D warnings --cfg mariadb_${{ matrix.mariadb }} # Test macros in offline mode (still needs DATABASE_URL to run) - run: > cargo test --no-default-features --test mysql-macros --features any,mysql,macros,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }} env: DATABASE_URL: mysql://root:password@localhost:3306/sqlx SQLX_OFFLINE: true SQLX_OFFLINE_DIR: .sqlx RUSTFLAGS: --cfg mariadb_${{ matrix.mariadb }} # client SSL authentication - if: ${{ matrix.tls != 'none' }} run: | docker stop mariadb_${{ matrix.mariadb }} docker compose -f tests/docker-compose.yml run -d -p 3306:3306 --name mariadb_${{ matrix.mariadb }}_client_ssl mariadb_${{ matrix.mariadb }}_client_ssl sleep 60 - if: ${{ matrix.tls != 'none' }} run: > cargo test --no-default-features --features any,mysql,macros,_unstable-all-types,runtime-${{ matrix.runtime }},tls-${{ matrix.tls }} env: DATABASE_URL: mysql://root@localhost:3306/sqlx?sslmode=verify_ca&ssl-ca=.%2Ftests%2Fcerts%2Fca.crt&ssl-key=.%2Ftests%2Fkeys%2Fclient.key&ssl-cert=.%2Ftests%2Fcerts%2Fclient.crt RUSTFLAGS: --cfg mariadb_${{ matrix.mariadb }} sqlx-0.8.3/.gitignore000064400000000000000000000005061046102023000126140ustar 00000000000000# Built artifacts target/ # Project and editor files .vscode/ .idea/ *.vim *.vi # Environment .env # Shared-memory and WAL files created by SQLite. *-shm *-wal # Integration testing extension library for SQLite. ipaddr.dylib ipaddr.so # Temporary files from running the tests locally like they would be run from CI .sqlx sqlx-0.8.3/CHANGELOG.md000064400000000000000000004070551046102023000124470ustar 00000000000000# Changelog All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## 0.8.3 - 2025-01-03 41 pull requests were merged this release cycle. ### Added * [[#3418]]: parse timezone parameter in mysql connection url [[@dojiong]] * [[#3491]]: chore: Update async-std v1.13 [[@jayvdb]] * [[#3492]]: expose relation_id and relation_attribution_no on PgColumn [[@kurtbuilds]] * [[#3493]]: doc(sqlite): document behavior for zoned date-time types [[@abonander]] * [[#3500]]: Add sqlite commit and rollback hooks [[@gridbox]] * [[#3505]]: chore(mysql): create test for passwordless auth (#3484) [[@abonander]] * [[#3507]]: Add a "sqlite-unbundled" feature that dynamically links to system libsqlite3.so library [[@lilydjwg]] * [[#3508]]: doc(sqlite): show how to turn options into a pool [[@M3t0r]] * [[#3514]]: Support PgHstore by default in macros [[@joeydewaal]] * [[#3550]]: Implement Acquire for PgListener [[@sandhose]] * [[#3551]]: Support building with rustls but native certificates [[@IlyaBizyaev]] * [[#3553]]: Add support for Postgres lquery arrays [[@philipcristiano]] * [[#3560]]: Add PgListener::next_buffered(), to support batch processing of notifications [[@chanks]] * [[#3577]]: Derive Copy where possible for database-specific types [[@veigaribo]] * [[#3579]]: Reexport AnyTypeInfoKind [[@Norlock]] * [[#3580]]: doc(mysql): document difference between `Uuid` and `uuid::fmt::Hyphenated` [[@abonander]] * [[#3583]]: feat: point [[@jayy-lmao]] * [[#3608]]: Implement AnyQueryResult for Sqlite and MySQL [[@pxp9]] * [[#3623]]: feat: add geometry line [[@jayy-lmao]] * [[#3658]]: feat: add Transaction type aliases [[@joeydewaal]] ### Changed * [[#3519]]: Remove unused dependencies from sqlx-core, sqlx-cli and sqlx-postgres [[@vsuryamurthy]] * [[#3529]]: Box Pgconnection fields [[@joeydewaal]] * [[#3548]]: Demote `.pgpass` file warning to a debug message. [[@denschub]] * [[#3585]]: Eagerly reconnect in `PgListener::try_recv` [[@swlynch99]] * [[#3596]]: Bump thiserror to v2.0.0 [[@paolobarbolini]] * [[#3605]]: Use `UNION ALL` instead of `UNION` in nullable check [[@Suficio]] * [[#3629]]: chore: remove BoxFuture's (non-breaking) [[@joeydewaal]] * [[#3632]]: Bump hashlink to v0.10 [[@paolobarbolini]] * [[#3643]]: Roll PostgreSQL 11..=15 tests to 13..=17 [[@paolobarbolini]] * [[#3648]]: close listener connection on TimedOut and BrokenPipe errors [[@DXist]] * [[#3649]]: Bump hashbrown to v0.15 [[@paolobarbolini]] ### Fixed * [[#3528]]: fix: obey `no-transaction` flag in down migrations [[@manifest]] * [[#3536]]: fix: using sqlx::test macro inside macro's [[@joeydewaal]] * [[#3545]]: fix: remove `sqlformat` [[@tbar4]] * [[#3558]]: fix: fix example code of `query_as` [[@xuehaonan27]] * [[#3566]]: Fix: Cannot query Postgres `INTERVAL[]` [[@Ddystopia]] * [[#3593]]: fix: URL decode database name when parsing connection url [[@BenoitRanque]] * [[#3601]]: Remove default-features = false from url [[@hsivonen]] * [[#3604]]: Fix mistake in sqlx::test fixtures docs [[@andreweggleston]] * [[#3612]]: fix(mysql): percent-decode database name [[@abonander]] * [[#3640]]: Dont use `EXPLAIN` in nullability check for QuestDB [[@Suficio]] [#3418]: https://github.com/launchbadge/sqlx/pull/3418 [#3478]: https://github.com/launchbadge/sqlx/pull/3478 [#3491]: https://github.com/launchbadge/sqlx/pull/3491 [#3492]: https://github.com/launchbadge/sqlx/pull/3492 [#3493]: https://github.com/launchbadge/sqlx/pull/3493 [#3500]: https://github.com/launchbadge/sqlx/pull/3500 [#3505]: https://github.com/launchbadge/sqlx/pull/3505 [#3507]: https://github.com/launchbadge/sqlx/pull/3507 [#3508]: https://github.com/launchbadge/sqlx/pull/3508 [#3514]: https://github.com/launchbadge/sqlx/pull/3514 [#3519]: https://github.com/launchbadge/sqlx/pull/3519 [#3528]: https://github.com/launchbadge/sqlx/pull/3528 [#3529]: https://github.com/launchbadge/sqlx/pull/3529 [#3536]: https://github.com/launchbadge/sqlx/pull/3536 [#3545]: https://github.com/launchbadge/sqlx/pull/3545 [#3548]: https://github.com/launchbadge/sqlx/pull/3548 [#3550]: https://github.com/launchbadge/sqlx/pull/3550 [#3551]: https://github.com/launchbadge/sqlx/pull/3551 [#3553]: https://github.com/launchbadge/sqlx/pull/3553 [#3558]: https://github.com/launchbadge/sqlx/pull/3558 [#3560]: https://github.com/launchbadge/sqlx/pull/3560 [#3566]: https://github.com/launchbadge/sqlx/pull/3566 [#3577]: https://github.com/launchbadge/sqlx/pull/3577 [#3579]: https://github.com/launchbadge/sqlx/pull/3579 [#3580]: https://github.com/launchbadge/sqlx/pull/3580 [#3583]: https://github.com/launchbadge/sqlx/pull/3583 [#3585]: https://github.com/launchbadge/sqlx/pull/3585 [#3593]: https://github.com/launchbadge/sqlx/pull/3593 [#3596]: https://github.com/launchbadge/sqlx/pull/3596 [#3601]: https://github.com/launchbadge/sqlx/pull/3601 [#3604]: https://github.com/launchbadge/sqlx/pull/3604 [#3605]: https://github.com/launchbadge/sqlx/pull/3605 [#3608]: https://github.com/launchbadge/sqlx/pull/3608 [#3612]: https://github.com/launchbadge/sqlx/pull/3612 [#3623]: https://github.com/launchbadge/sqlx/pull/3623 [#3629]: https://github.com/launchbadge/sqlx/pull/3629 [#3632]: https://github.com/launchbadge/sqlx/pull/3632 [#3640]: https://github.com/launchbadge/sqlx/pull/3640 [#3643]: https://github.com/launchbadge/sqlx/pull/3643 [#3648]: https://github.com/launchbadge/sqlx/pull/3648 [#3649]: https://github.com/launchbadge/sqlx/pull/3649 [#3658]: https://github.com/launchbadge/sqlx/pull/3658 ## 0.8.2 - 2024-09-02 10 pull requests were merged this release cycle. This release addresses a few regressions that have occurred, and refines SQLx's MSRV policy (see [the FAQ](FAQ.md)). ### Added * [[#3447]]: Clarify usage of Json/Jsonb in query macros [[@Lachstec]] ### Changed * [[#3424]]: Remove deprecated feature-names from `Cargo.toml` files in examples [[@carschandler]] ### Fixed * [[#3403]]: Fix (#3395) sqlx::test macro in 0.8 [[@joeydewaal]] * [[#3411]]: fix: Use rfc3339 to decode date from text [[@pierre-wehbe]] * [[#3453]]: fix(#3445): PgHasArrayType [[@joeydewaal]] * Fixes `#[sqlx(no_pg_array)]` being forbidden on `#[derive(Type)]` structs. * [[#3454]]: fix: non snake case warning [[@joeydewaal]] * [[#3459]]: Pgsql cube type compile fail [[@kdesjard]] * [[#3465]]: fix(postgres): max number of binds is 65535, not 32767 (regression) [[@abonander]] * [[#3467]]: fix cancellation issues with `PgListener`, `PgStream::recv()` [[@abonander]] * Fixes cryptic `unknown message: "\\0"` error * [[#3474]]: Fix try_get example in README.md [[@luveti]] [#3403]: https://github.com/launchbadge/sqlx/pull/3403 [#3411]: https://github.com/launchbadge/sqlx/pull/3411 [#3424]: https://github.com/launchbadge/sqlx/pull/3424 [#3447]: https://github.com/launchbadge/sqlx/pull/3447 [#3453]: https://github.com/launchbadge/sqlx/pull/3453 [#3454]: https://github.com/launchbadge/sqlx/pull/3454 [#3455]: https://github.com/launchbadge/sqlx/pull/3455 [#3459]: https://github.com/launchbadge/sqlx/pull/3459 [#3465]: https://github.com/launchbadge/sqlx/pull/3465 [#3467]: https://github.com/launchbadge/sqlx/pull/3467 [#3474]: https://github.com/launchbadge/sqlx/pull/3474 ## 0.8.1 - 2024-08-23 16 pull requests were merged this release cycle. This release contains a fix for [RUSTSEC-2024-0363]. Postgres users are advised to upgrade ASAP as a possible exploit has been demonstrated: MySQL and SQLite do not _appear_ to be exploitable, but upgrading is recommended nonetheless. ### Added * [[#3421]]: correct spelling of `MySqlConnectOptions::no_engine_substitution()` [[@kolinfluence]] * Deprecates `MySqlConnectOptions::no_engine_subsitution()` (oops) in favor of the correctly spelled version. ### Changed * [[#3376]]: doc: hide `spec_error` module [[@abonander]] * This is a helper module for the macros and was not meant to be exposed. * It is not expected to receive any breaking changes for the 0.8.x release, but is not designed as a public API. Use at your own risk. * [[#3382]]: feat: bumped to `libsqlite3-sys=0.30.1` to support sqlite 3.46 [[@CommanderStorm]] * [[#3385]]: chore(examples):Migrated the pg-chat example to ratatui [[@CommanderStorm]] * [[#3399]]: Upgrade to rustls 0.23 [[@djc]] * RusTLS now has pluggable cryptography providers: `ring` (the existing implementation), and `aws-lc-rs` which has optional FIPS certification. * The existing features activating RusTLS (`runtime-tokio-rustls`, `runtime-async-std-rustls`, `tls-rustls`) enable the `ring` provider of RusTLS to match the existing behavior so this _should not_ be a breaking change. * Switch to the `tls-rustls-aws-lc-rs` feature to use the `aws-lc-rs` provider. * If using `runtime-tokio-rustls` or `runtime-async-std-rustls`, this will necessitate switching to the appropriate non-legacy runtime feature: `runtime-tokio` or `runtime-async-std` * See the RusTLS README for more details: ### Fixed * [[#2786]]: fix(sqlx-cli): do not clean sqlx during prepare [[@cycraig]] * [[#3354]]: sqlite: fix inconsistent read-after-write [[@ckampfe]] * [[#3371]]: Fix encoding and decoding of MySQL enums in `sqlx::Type` [[@alu]] * [[#3374]]: fix: usage of `node12` in `SQLx` action [[@hamirmahal]] * [[#3380]]: chore: replace structopt with clap in examples [[@tottoto]] * [[#3381]]: Fix CI after Rust 1.80, remove dead feature references [[@abonander]] * [[#3384]]: chore(tests): fixed deprecation warnings [[@CommanderStorm]] * [[#3386]]: fix(dependencys):bumped cargo_metadata to `v0.18.1` to avoid yanked `v0.14.3` [[@CommanderStorm]] * [[#3389]]: fix(cli): typo in error for required DB URL [[@ods]] * [[#3417]]: Update version to 0.8 in README [[@soucosmo]] * [[#3441]]: fix: audit protocol handling [[@abonander]] * This addresses [RUSTSEC-2024-0363] and includes regression tests for MySQL, Postgres and SQLite. [#2786]: https://github.com/launchbadge/sqlx/pull/2786 [#3354]: https://github.com/launchbadge/sqlx/pull/3354 [#3371]: https://github.com/launchbadge/sqlx/pull/3371 [#3374]: https://github.com/launchbadge/sqlx/pull/3374 [#3376]: https://github.com/launchbadge/sqlx/pull/3376 [#3380]: https://github.com/launchbadge/sqlx/pull/3380 [#3381]: https://github.com/launchbadge/sqlx/pull/3381 [#3382]: https://github.com/launchbadge/sqlx/pull/3382 [#3384]: https://github.com/launchbadge/sqlx/pull/3384 [#3385]: https://github.com/launchbadge/sqlx/pull/3385 [#3386]: https://github.com/launchbadge/sqlx/pull/3386 [#3389]: https://github.com/launchbadge/sqlx/pull/3389 [#3399]: https://github.com/launchbadge/sqlx/pull/3399 [#3417]: https://github.com/launchbadge/sqlx/pull/3417 [#3421]: https://github.com/launchbadge/sqlx/pull/3421 [#3441]: https://github.com/launchbadge/sqlx/pull/3441 [RUSTSEC-2024-0363]: https://rustsec.org/advisories/RUSTSEC-2024-0363.html ## 0.8.0 - 2024-07-22 70 pull requests were merged this release cycle. [#2697] was merged the same day as release 0.7.4 and so was missed by the automatic CHANGELOG generation. ### Breaking * [[#2697]]: fix(macros): only enable chrono when time is disabled [[@saiintbrisson]] * [[#2973]]: Generic Associated Types in Database, replacing HasValueRef, HasArguments, HasStatement [[@nitn3lav]] * [[#2482]]: chore: bump syn to 2.0 [[@saiintbrisson]] * Deprecated type ascription syntax in the query macros was removed. * [[#2736]]: Fix describe on PostgreSQL views with rules [[@tsing]] * Potentially breaking: nullability inference changes for Postgres. * [[#2869]]: Implement PgHasArrayType for all references [[@tylerhawkes]] * Conflicts with existing manual implementations. * [[#2940]]: fix: Decode and Encode derives (#1031) [[@benluelo]] * Changes lifetime obligations for field types. * [[#3064]]: Sqlite explain graph [[@tyrelr]] * Potentially breaking: nullability inference changes for SQLite. * [[#3123]]: Reorder attrs in sqlx::test macro [[@bobozaur]] * Potentially breaking: attributes on `#[sqlx::test]` usages are applied in the correct order now. * [[#3126]]: Make Encode return a result [[@FSMaxB]] * [[#3130]]: Add version information for failed cli migration (#3129) [[@FlakM]] * Breaking changes to `MigrateError`. * [[#3181]]: feat: no tx migration [[@cleverjam]] * (Postgres only) migrations that should not run in a transaction can be flagged by adding `-- no-transaction` to the beginning. * Breaking change: added field to `Migration` * [[#3184]]: [BREAKING} fix(sqlite): always use `i64` as intermediate when decoding [[@abonander]] * integer decoding will now loudly error on overflow instead of silently truncating. * some usages of the query!() macros might change an i32 to an i64. * [[#3252]]: fix `#[derive(sqlx::Type)]` in Postgres [[@abonander]] * Manual implementations of PgHasArrayType for enums will conflict with the generated one. Delete the manual impl or add `#[sqlx(no_pg_array)]` where conflicts occur. * Type equality for PgTypeInfo is now schema-aware. * [[#3329]]: fix: correct handling of arrays of custom types in Postgres [[@abonander]] * Potential breaking change: `PgTypeInfo::with_name()` infers types that start with `_` to be arrays of the un-prefixed type. Wrap type names in quotes to bypass this behavior. * [[#3356]]: breaking: fix name collision in `FromRow`, return `Error::ColumnDecode` for `TryFrom` errors [[@abonander]] * Breaking behavior change: errors with `#[sqlx(try_from = "T")]` now return `Error::ColumnDecode` instead of `Error::ColumnNotFound`. * Breaking because `#[sqlx(default)]` on an individual field or the struct itself would have previously suppressed the error. This doesn't seem like good behavior as it could result in some potentially very difficult bugs. * Instead, create a wrapper implementing `From` and apply the default explicitly. * [[#3337]]: allow rename with rename_all (close #2896) [[@DirectorX]] * Changes the precedence of `#[sqlx(rename)]` and `#[sqlx(rename_all)]` to match the expected behavior (`rename` wins). * [[#3285]]: fix: use correct names for sslmode options [[@lily-mosquitoes]] * Changes the output of `ConnectOptions::to_url_lossy()` to match what parsing expects. ### Added * [[#2917]]: Add Debug impl for PgRow [[@g-bartoszek]] * [[#3113]]: feat: new derive feature flag [[@saiintbrisson]] * [[#3154]]: feat: add `MySqlTime`, audit `mysql::types` for panics [[@abonander]] * [[#3188]]: feat(cube): support postgres cube [[@jayy-lmao]] * [[#3244]]: feat: support `NonZero*` scalar types [[@AlphaKeks]] * [[#3260]]: feat: Add set_update_hook on SqliteConnection [[@gridbox]] * [[#3291]]: feat: support the Postgres Bool type for the Any driver [[@etorreborre]] * [[#3293]]: Add LICENSE-* files to crates [[@LecrisUT]] * [[#3303]]: add array support for NonZeroI* in postgres [[@JohannesIBK]] * [[#3311]]: Add example on how to use Transaction as Executor [[@Lachstec]] * [[#3343]]: Add support for PostgreSQL HSTORE data type [[@KobusEllis]] ### Changed * [[#2652]]: MySQL: Remove collation compatibility check for strings [[@alu]] * [[#2960]]: Removed `Send` trait bound from argument binding [[@bobozaur]] * [[#2970]]: refactor: lift type mappings into driver crates [[@abonander]] * [[#3148]]: Bump libsqlite3-sys to v0.28 [[@NfNitLoop]] * Note: version bumps to `libsqlite3-sys` are not considered breaking changes as per our semver guarantees. * [[#3265]]: perf: box `MySqlConnection` to reduce sizes of futures [[@stepantubanov]] * [[#3352]]: chore:added a testcase for `sqlx migrate add ...` [[@CommanderStorm]] * [[#3340]]: ci: Add job to check that sqlx builds with its declared minimum dependencies [[@iamjpotts]] ### Fixed * [[#2702]]: Constrain cyclic associated types to themselves [[@BadBastion]] * [[#2954]]: Fix several inter doc links [[@ralpha]] * [[#3073]]: feat(logging): Log slow acquires from connection pool [[@iamjpotts]] * [[#3137]]: SqliteConnectOptions::filename() memory fix (#3136) [[@hoxxep]] * [[#3138]]: PostgreSQL Bugfix: Ensure connection is usable after failed COPY inside a transaction [[@feikesteenbergen]] * [[#3146]]: fix(sqlite): delete unused `ConnectionHandleRaw` type [[@abonander]] * [[#3162]]: Drop urlencoding dependency [[@paolobarbolini]] * [[#3165]]: Bump deps that do not need code changes [[@GnomedDev]] * [[#3167]]: fix(ci): use `docker compose` instead of `docker-compose` [[@abonander]] * [[#3172]]: fix: Option decoding in any driver [[@pxp9]] * [[#3173]]: fix(postgres) : int type conversion while decoding [[@RaghavRox]] * [[#3190]]: Update time to 0.3.36 [[@BlackSoulHub]] * [[#3191]]: Fix unclean TLS shutdown [[@levkk]] * [[#3194]]: Fix leaking connections in fetch_optional (#2647) [[@danjpgriffin]] * [[#3216]]: security: bump rustls to 0.21.11 [[@toxeus]] * [[#3230]]: fix: sqlite pragma order for auto_vacuum [[@jasonish]] * [[#3233]]: fix: get_filename should not consume self [[@jasonish]] * [[#3234]]: fix(ci): pin Rust version, ditch unmaintained actions [[@abonander]] * [[#3236]]: fix: resolve `path` ownership problems when using `sqlx_macros_unstable` [[@lily-mosquitoes]] * [[#3254]]: fix: hide `sqlx_postgres::any` [[@Zarathustra2]] * [[#3266]]: ci: MariaDB - add back 11.4 and add 11.5 [[@grooverdan]] * [[#3267]]: ci: syntax fix [[@grooverdan]] * [[#3271]]: docs(sqlite): fix typo - unixtime() -> unixepoch() [[@joelkoen]] * [[#3276]]: Invert boolean for `migrate` error message. (#3275) [[@nk9]] * [[#3279]]: fix Clippy errors [[@abonander]] * [[#3288]]: fix: sqlite update_hook char types [[@jasonish]] * [[#3297]]: Pass the `persistent` query setting when preparing queries with the `Any` driver [[@etorreborre]] * [[#3298]]: Track null arguments in order to provide the appropriate type when converting them. [[@etorreborre]] * [[#3312]]: doc: Minor rust docs fixes [[@SrGesus]] * [[#3327]]: chore: fixed one usage of `select_input_type!()` being unhygenic [[@CommanderStorm]] * [[#3328]]: fix(ci): comment not separated from other characters [[@hamirmahal]] * [[#3341]]: refactor: Resolve cargo check warnings in postgres examples [[@iamjpotts]] * [[#3346]]: fix(postgres): don't panic if `M` or `C` Notice fields are not UTF-8 [[@YgorSouza]] * [[#3350]]: fix:the `json`-feature should activate `sqlx-postgres?/json` as well [[@CommanderStorm]] * [[#3353]]: fix: build script new line at eof [[@Zarthus]] * (no PR): activate `clock` and `std` features of `workspace.dependencies.chrono`. [#2482]: https://github.com/launchbadge/sqlx/pull/2482 [#2652]: https://github.com/launchbadge/sqlx/pull/2652 [#2697]: https://github.com/launchbadge/sqlx/pull/2697 [#2702]: https://github.com/launchbadge/sqlx/pull/2702 [#2736]: https://github.com/launchbadge/sqlx/pull/2736 [#2869]: https://github.com/launchbadge/sqlx/pull/2869 [#2917]: https://github.com/launchbadge/sqlx/pull/2917 [#2940]: https://github.com/launchbadge/sqlx/pull/2940 [#2954]: https://github.com/launchbadge/sqlx/pull/2954 [#2960]: https://github.com/launchbadge/sqlx/pull/2960 [#2970]: https://github.com/launchbadge/sqlx/pull/2970 [#2973]: https://github.com/launchbadge/sqlx/pull/2973 [#3064]: https://github.com/launchbadge/sqlx/pull/3064 [#3073]: https://github.com/launchbadge/sqlx/pull/3073 [#3113]: https://github.com/launchbadge/sqlx/pull/3113 [#3123]: https://github.com/launchbadge/sqlx/pull/3123 [#3126]: https://github.com/launchbadge/sqlx/pull/3126 [#3130]: https://github.com/launchbadge/sqlx/pull/3130 [#3137]: https://github.com/launchbadge/sqlx/pull/3137 [#3138]: https://github.com/launchbadge/sqlx/pull/3138 [#3146]: https://github.com/launchbadge/sqlx/pull/3146 [#3148]: https://github.com/launchbadge/sqlx/pull/3148 [#3154]: https://github.com/launchbadge/sqlx/pull/3154 [#3162]: https://github.com/launchbadge/sqlx/pull/3162 [#3165]: https://github.com/launchbadge/sqlx/pull/3165 [#3167]: https://github.com/launchbadge/sqlx/pull/3167 [#3172]: https://github.com/launchbadge/sqlx/pull/3172 [#3173]: https://github.com/launchbadge/sqlx/pull/3173 [#3181]: https://github.com/launchbadge/sqlx/pull/3181 [#3184]: https://github.com/launchbadge/sqlx/pull/3184 [#3188]: https://github.com/launchbadge/sqlx/pull/3188 [#3190]: https://github.com/launchbadge/sqlx/pull/3190 [#3191]: https://github.com/launchbadge/sqlx/pull/3191 [#3194]: https://github.com/launchbadge/sqlx/pull/3194 [#3216]: https://github.com/launchbadge/sqlx/pull/3216 [#3230]: https://github.com/launchbadge/sqlx/pull/3230 [#3233]: https://github.com/launchbadge/sqlx/pull/3233 [#3234]: https://github.com/launchbadge/sqlx/pull/3234 [#3236]: https://github.com/launchbadge/sqlx/pull/3236 [#3244]: https://github.com/launchbadge/sqlx/pull/3244 [#3252]: https://github.com/launchbadge/sqlx/pull/3252 [#3254]: https://github.com/launchbadge/sqlx/pull/3254 [#3260]: https://github.com/launchbadge/sqlx/pull/3260 [#3265]: https://github.com/launchbadge/sqlx/pull/3265 [#3266]: https://github.com/launchbadge/sqlx/pull/3266 [#3267]: https://github.com/launchbadge/sqlx/pull/3267 [#3271]: https://github.com/launchbadge/sqlx/pull/3271 [#3276]: https://github.com/launchbadge/sqlx/pull/3276 [#3279]: https://github.com/launchbadge/sqlx/pull/3279 [#3285]: https://github.com/launchbadge/sqlx/pull/3285 [#3288]: https://github.com/launchbadge/sqlx/pull/3288 [#3291]: https://github.com/launchbadge/sqlx/pull/3291 [#3293]: https://github.com/launchbadge/sqlx/pull/3293 [#3297]: https://github.com/launchbadge/sqlx/pull/3297 [#3298]: https://github.com/launchbadge/sqlx/pull/3298 [#3303]: https://github.com/launchbadge/sqlx/pull/3303 [#3311]: https://github.com/launchbadge/sqlx/pull/3311 [#3312]: https://github.com/launchbadge/sqlx/pull/3312 [#3327]: https://github.com/launchbadge/sqlx/pull/3327 [#3328]: https://github.com/launchbadge/sqlx/pull/3328 [#3329]: https://github.com/launchbadge/sqlx/pull/3329 [#3337]: https://github.com/launchbadge/sqlx/pull/3337 [#3340]: https://github.com/launchbadge/sqlx/pull/3340 [#3341]: https://github.com/launchbadge/sqlx/pull/3341 [#3343]: https://github.com/launchbadge/sqlx/pull/3343 [#3346]: https://github.com/launchbadge/sqlx/pull/3346 [#3350]: https://github.com/launchbadge/sqlx/pull/3350 [#3352]: https://github.com/launchbadge/sqlx/pull/3352 [#3353]: https://github.com/launchbadge/sqlx/pull/3353 [#3356]: https://github.com/launchbadge/sqlx/pull/3356 ## 0.7.4 - 2024-03-11 38 pull requests were merged this release cycle. This is officially the **last** release of the 0.7.x release cycle. As of this release, development of 0.8.0 has begun on `main` and only high-priority bugfixes may be backported. ### Added * [[#2891]]: feat: expose getters for connect options fields [[@saiintbrisson]] * [[#2902]]: feat: add `to_url_lossy` to connect options [[@lily-mosquitoes]] * [[#2927]]: Support `query!` for cargo-free systems [[@kshramt]] * [[#2997]]: doc(FAQ): add entry explaining prepared statements [[@abonander]] * [[#3001]]: Update README to clarify MariaDB support [[@iangilfillan]] * [[#3004]]: feat(logging): Add numeric elapsed time field elapsed_secs [[@iamjpotts]] * [[#3007]]: feat: add `raw_sql` API [[@abonander]] * This hopefully makes it easier to find how to execute statements which are not supported by the default prepared statement interfaces `query*()` and `query!()`. * Improved documentation across the board for the `query*()` functions. * Deprecated: `execute_many()` and `fetch_many()` on interfaces that use prepared statements. * Multiple SQL statements in one query string were only supported by SQLite because its prepared statement interface is the *only* way to execute SQL. All other database flavors forbid multiple statements in one prepared statement string as an extra defense against SQL injection. * The new `raw_sql` API retains this functionality because it explicitly does *not* use prepared statements. Raw or text-mode query interfaces generally allow multiple statements in one query string, and this is supported by all current databases. Due to their nature, however, one cannot use bind parameters with them. * If this change affects you, an issue is open for discussion: https://github.com/launchbadge/sqlx/issues/3108 * [[#3011]]: Added support to IpAddr with MySQL/MariaDB. [[@Icerath]] * [[#3013]]: Add default implementation for PgInterval [[@pawurb]] * [[#3018]]: Add default implementation for PgMoney [[@pawurb]] * [[#3026]]: Update docs to reflect support for MariaDB data types [[@iangilfillan]] * [[#3037]]: feat(mysql): allow to connect with mysql driver without default behavor [[@darkecho731]] ### Changed * [[#2900]]: Show latest url to docs for macro.migrate [[@Vrajs16]] * [[#2914]]: Use `create_new` instead of `atomic-file-write` [[@mattfbacon]] * [[#2926]]: docs: update example for `PgConnectOptions` [[@Fyko]] * [[#2989]]: sqlx-core: Remove dotenvy dependency [[@joshtriplett]] * [[#2996]]: chore: Update ahash to 0.8.7 [[@takenoko-gohan]] * [[#3006]]: chore(deps): Replace unmaintained tempdir crate with tempfile [[@iamjpotts]] * [[#3008]]: chore: Ignore .sqlx folder created by running ci steps locally [[@iamjpotts]] * [[#3009]]: chore(dev-deps): Upgrade env_logger from 0.9 to 0.11 [[@iamjpotts]] * [[#3010]]: chore(deps): Upgrade criterion to 0.5.1 [[@iamjpotts]] * [[#3050]]: Optimize SASL auth in sqlx-postgres [[@mirek26]] * [[#3055]]: Set TCP_NODELAY option on TCP sockets [[@mirek26]] * [[#3065]]: Improve max_lifetime handling [[@mirek26]] * [[#3072]]: Change the name of "inner" function generated by `#[sqlx::test]` [[@ciffelia]] * [[#3083]]: Remove sha1 because it's not being used in postgres [[@rafaelGuerreiro]] ### Fixed * [[#2898]]: Fixed docs [[@Vrajs16]] * [[#2905]]: fix(mysql): Close prepared statement if persistence is disabled [[@larsschumacher]] * [[#2913]]: Fix handling of deferred constraints [[@Thomasdezeeuw]] * [[#2919]]: fix duplicate "`" in FromRow "default" attribute doc comment [[@shengsheng]] * [[#2932]]: fix(postgres): avoid unnecessary flush in PgCopyIn::read_from [[@tsing]] * [[#2955]]: Minor fixes [[@Dawsoncodes]] * [[#2963]]: Fixed ReadMe badge styling [[@tadghh]] * [[#2976]]: fix: AnyRow not support PgType::Varchar [[@holicc]] * [[#3053]]: fix: do not panic when binding a large BigDecimal [[@Ekleog]] * [[#3056]]: fix: spans in sqlite tracing (#2876) [[@zoomiti]] * [[#3089]]: fix(migrate): improve error message when parsing version from filename [[@abonander]] * [[#3098]]: Migrations fixes [[@abonander]] * Unhides `sqlx::migrate::Migrator`. * Improves I/O error message when failing to read a file in `migrate!()`. [#2891]: https://github.com/launchbadge/sqlx/pull/2891 [#2898]: https://github.com/launchbadge/sqlx/pull/2898 [#2900]: https://github.com/launchbadge/sqlx/pull/2900 [#2902]: https://github.com/launchbadge/sqlx/pull/2902 [#2905]: https://github.com/launchbadge/sqlx/pull/2905 [#2913]: https://github.com/launchbadge/sqlx/pull/2913 [#2914]: https://github.com/launchbadge/sqlx/pull/2914 [#2919]: https://github.com/launchbadge/sqlx/pull/2919 [#2926]: https://github.com/launchbadge/sqlx/pull/2926 [#2927]: https://github.com/launchbadge/sqlx/pull/2927 [#2932]: https://github.com/launchbadge/sqlx/pull/2932 [#2955]: https://github.com/launchbadge/sqlx/pull/2955 [#2963]: https://github.com/launchbadge/sqlx/pull/2963 [#2976]: https://github.com/launchbadge/sqlx/pull/2976 [#2989]: https://github.com/launchbadge/sqlx/pull/2989 [#2996]: https://github.com/launchbadge/sqlx/pull/2996 [#2997]: https://github.com/launchbadge/sqlx/pull/2997 [#3001]: https://github.com/launchbadge/sqlx/pull/3001 [#3004]: https://github.com/launchbadge/sqlx/pull/3004 [#3006]: https://github.com/launchbadge/sqlx/pull/3006 [#3007]: https://github.com/launchbadge/sqlx/pull/3007 [#3008]: https://github.com/launchbadge/sqlx/pull/3008 [#3009]: https://github.com/launchbadge/sqlx/pull/3009 [#3010]: https://github.com/launchbadge/sqlx/pull/3010 [#3011]: https://github.com/launchbadge/sqlx/pull/3011 [#3013]: https://github.com/launchbadge/sqlx/pull/3013 [#3018]: https://github.com/launchbadge/sqlx/pull/3018 [#3026]: https://github.com/launchbadge/sqlx/pull/3026 [#3037]: https://github.com/launchbadge/sqlx/pull/3037 [#3050]: https://github.com/launchbadge/sqlx/pull/3050 [#3053]: https://github.com/launchbadge/sqlx/pull/3053 [#3055]: https://github.com/launchbadge/sqlx/pull/3055 [#3056]: https://github.com/launchbadge/sqlx/pull/3056 [#3065]: https://github.com/launchbadge/sqlx/pull/3065 [#3072]: https://github.com/launchbadge/sqlx/pull/3072 [#3083]: https://github.com/launchbadge/sqlx/pull/3083 [#3089]: https://github.com/launchbadge/sqlx/pull/3089 [#3098]: https://github.com/launchbadge/sqlx/pull/3098 ## 0.7.3 - 2023-11-22 38 pull requests were merged this release cycle. ### Added * [[#2478]]: feat(citext): support postgres citext [[@hgranthorner]] * [[#2545]]: Add `fixtures_path` in sqlx::test args [[@ripa1995]] * [[#2665]]: feat(mysql): support packet splitting [[@tk2217]] * [[#2752]]: Enhancement #2747 Provide `fn PgConnectOptions::get_host(&self)` [[@boris-lok]] * [[#2769]]: Customize the macro error message based on the metadata [[@Nemo157]] * [[#2793]]: derived Hash trait for PgInterval [[@yasamoka]] * [[#2801]]: derive FromRow: sqlx(default) for all fields [[@grgi]] * [[#2827]]: Add impl `FromRow` for the unit type [[@nanoqsh]] * [[#2871]]: Add `MySqlConnectOptions::get_database()` [[@shiftrightonce]] * [[#2873]]: Sqlx Cli: Added force flag to drop database for postgres [[@Vrajs16]] * [[#2894]]: feat: `Text` adapter [[@abonander]] ### Changed * [[#2701]]: Remove documentation on offline feature [[@Baptistemontan]] * [[#2713]]: Add additional info regarding using Transaction and PoolConnection as… [[@satwanjyu]] * [[#2770]]: Update README.md [[@snspinn]] * [[#2797]]: doc(mysql): document behavior regarding `BOOLEAN` and the query macros [[@abonander]] * [[#2803]]: Don't use separate temp dir for query jsons (2) [[@mattfbacon]] * [[#2819]]: postgres begin cancel safe [[@conradludgate]] * [[#2832]]: Update extra_float_digits default to 2 instead of 3 [[@brianheineman]] * [[#2865]]: Update Faq - Bulk upsert with optional fields [[@Vrajs16]] * [[#2880]]: feat: use specific message for slow query logs [[@abonander]] * [[#2882]]: Do not require db url for prepare [[@tamasfe]] * [[#2890]]: doc(sqlite): cover lack of `NUMERIC` support [[@abonander]] * [No PR]: Upgraded `libsqlite3-sys` to 0.27.0 * Note: linkage to `libsqlite3-sys` is considered semver-exempt; see the release notes for 0.7.0 below for details. ### Fixed * [[#2640]]: fix: sqlx::macro db cleanup race condition by adding a margin to current timestamp [[@fhsgoncalves]] * [[#2655]]: [fix] Urlencode when passing filenames to sqlite3 [[@uttarayan21]] * [[#2684]]: Make PgListener recover from UnexpectedEof [[@hamiltop]] * [[#2688]]: fix: Make rust_decimal and bigdecimal decoding more lenient [[@cameronbraid]] * [[#2754]]: Is tests/x.py maintained? And I tried fix it. [[@qwerty2501]] * [[#2784]]: fix: decode postgres time without subsecond [[@granddaifuku]] * [[#2806]]: Depend on version of async-std with non-private spawn-blocking [[@A248]] * [[#2820]]: fix: correct decoding of `rust_decimal::Decimal` for high-precision values [[@abonander]] * [[#2822]]: issue #2821 Update error handling logic when opening a TCP connection [[@anupj]] * [[#2826]]: chore: bump some sqlx-core dependencies [[@djc]] * [[#2838]]: Fixes rust_decimal scale for Postgres [[@jkleinknox]] * [[#2847]]: Fix comment in `sqlx migrate add` help text [[@cryeprecision]] * [[#2850]]: fix(core): avoid unncessary wakeups in `try_stream!()` [[@abonander]] * [[#2856]]: Prevent warnings running `cargo build` [[@nyurik]] * [[#2864]]: fix(sqlite): use `AtomicUsize` for thread IDs [[@abonander]] * [[#2892]]: Fixed force dropping bug [[@Vrajs16]] [#2478]: https://github.com/launchbadge/sqlx/pull/2478 [#2545]: https://github.com/launchbadge/sqlx/pull/2545 [#2640]: https://github.com/launchbadge/sqlx/pull/2640 [#2655]: https://github.com/launchbadge/sqlx/pull/2655 [#2665]: https://github.com/launchbadge/sqlx/pull/2665 [#2684]: https://github.com/launchbadge/sqlx/pull/2684 [#2688]: https://github.com/launchbadge/sqlx/pull/2688 [#2701]: https://github.com/launchbadge/sqlx/pull/2701 [#2713]: https://github.com/launchbadge/sqlx/pull/2713 [#2752]: https://github.com/launchbadge/sqlx/pull/2752 [#2754]: https://github.com/launchbadge/sqlx/pull/2754 [#2769]: https://github.com/launchbadge/sqlx/pull/2769 [#2770]: https://github.com/launchbadge/sqlx/pull/2770 [#2782]: https://github.com/launchbadge/sqlx/pull/2782 [#2784]: https://github.com/launchbadge/sqlx/pull/2784 [#2793]: https://github.com/launchbadge/sqlx/pull/2793 [#2797]: https://github.com/launchbadge/sqlx/pull/2797 [#2801]: https://github.com/launchbadge/sqlx/pull/2801 [#2803]: https://github.com/launchbadge/sqlx/pull/2803 [#2806]: https://github.com/launchbadge/sqlx/pull/2806 [#2819]: https://github.com/launchbadge/sqlx/pull/2819 [#2820]: https://github.com/launchbadge/sqlx/pull/2820 [#2822]: https://github.com/launchbadge/sqlx/pull/2822 [#2826]: https://github.com/launchbadge/sqlx/pull/2826 [#2827]: https://github.com/launchbadge/sqlx/pull/2827 [#2832]: https://github.com/launchbadge/sqlx/pull/2832 [#2838]: https://github.com/launchbadge/sqlx/pull/2838 [#2847]: https://github.com/launchbadge/sqlx/pull/2847 [#2850]: https://github.com/launchbadge/sqlx/pull/2850 [#2856]: https://github.com/launchbadge/sqlx/pull/2856 [#2864]: https://github.com/launchbadge/sqlx/pull/2864 [#2865]: https://github.com/launchbadge/sqlx/pull/2865 [#2871]: https://github.com/launchbadge/sqlx/pull/2871 [#2873]: https://github.com/launchbadge/sqlx/pull/2873 [#2880]: https://github.com/launchbadge/sqlx/pull/2880 [#2882]: https://github.com/launchbadge/sqlx/pull/2882 [#2890]: https://github.com/launchbadge/sqlx/pull/2890 [#2892]: https://github.com/launchbadge/sqlx/pull/2892 [#2894]: https://github.com/launchbadge/sqlx/pull/2894 ## 0.7.2 - 2023-09-25 23 pull requests were merged this release cycle. ### Added * [[#2121]]: Add JSON support to `FromRow` derive [[@95ulisse]] * [[#2533]]: Implement mysql_clear_password [[@ldanilek]] * [[#2538]]: cli: add --target-version CLI flags for migrate run/revert [[@inahga]] * [[#2577]]: supplement Postgres listen example with a small chat example [[@JockeM]] * [[#2602]]: Support naming migrations sequentially [[@vmax]] * [[#2634]]: Adding PgHasArrayType for &[u8;N] [[@snf]] * [[#2646]]: Support for setting client certificate and key from bytes [[@wyhaya]] * [[#2664]]: Automatically infer migration type [[@vmax]] * [[#2712]]: Add impl for `Type`, `Decode`, and `Encode` for `Box` and `Box<[u8]>` [[@grant0417]] ### Changed * [[#2650]]: Cleanup format arguments [[@nyurik]] * [[#2695]]: remove &mut PoolConnection from Executor docs [[@olback]] * This impl was removed in 0.7.0 because of coherence issues. * [[#2706]]: Clarify where optional features should be enabled [[@kryptan]] * [[#2717]]: Update README.md [[@fermanjj]] * [[#2739]]: Bump mariadb CI images + mysql unpin [[@grooverdan]] * [[#2742]]: Implemented poll_flush for Box [[@bobozaur]] * [[#2740]]: Remove sealed trait comments from documentation [[@bobozaur]] * [[#2750]]: Fix #2384, bump flume to v0.11.0 [[@madadam]] * [[#2757]]: Remove unused `remove_dir_all` crate from `sqlx-cli`, fixes RUSTSEC-2023-0018 [[@aldur]] ### Fixed * [[#2624]]: Documentation typo: BYTE -> BINARY [[@sebastianv89]] * [[#2628]]: docs: 0.7 is stable in the entire README [[@marcusirgens]] * [[#2630]]: fix(postgres): fix buffer management in PgCopyIn::read_from [[@tsing]] * [[#2651]]: Chore: Fix few build warnings, and make CI fail on warn [[@nyurik]] * [[#2670]]: fix: ignore extra fields in Postgres describe parsing [[@abonander]] * [[#2687]]: docs: Fix description of `min_connections` [[@hakoerber]] [#2121]: https://github.com/launchbadge/sqlx/pull/2121 [#2533]: https://github.com/launchbadge/sqlx/pull/2533 [#2538]: https://github.com/launchbadge/sqlx/pull/2538 [#2577]: https://github.com/launchbadge/sqlx/pull/2577 [#2602]: https://github.com/launchbadge/sqlx/pull/2602 [#2624]: https://github.com/launchbadge/sqlx/pull/2624 [#2628]: https://github.com/launchbadge/sqlx/pull/2628 [#2630]: https://github.com/launchbadge/sqlx/pull/2630 [#2634]: https://github.com/launchbadge/sqlx/pull/2634 [#2646]: https://github.com/launchbadge/sqlx/pull/2646 [#2650]: https://github.com/launchbadge/sqlx/pull/2650 [#2651]: https://github.com/launchbadge/sqlx/pull/2651 [#2664]: https://github.com/launchbadge/sqlx/pull/2664 [#2670]: https://github.com/launchbadge/sqlx/pull/2670 [#2687]: https://github.com/launchbadge/sqlx/pull/2687 [#2695]: https://github.com/launchbadge/sqlx/pull/2695 [#2706]: https://github.com/launchbadge/sqlx/pull/2706 [#2712]: https://github.com/launchbadge/sqlx/pull/2712 [#2717]: https://github.com/launchbadge/sqlx/pull/2717 [#2739]: https://github.com/launchbadge/sqlx/pull/2739 [#2740]: https://github.com/launchbadge/sqlx/pull/2740 [#2742]: https://github.com/launchbadge/sqlx/pull/2742 [#2750]: https://github.com/launchbadge/sqlx/pull/2750 [#2757]: https://github.com/launchbadge/sqlx/pull/2757 ## 0.7.1 - 2023-07-14 This release mainly addresses issues reported with the 0.7.0 release. 16 pull requests were merged this release cycle. ### Added * [[#2551]]: Introduce build_query_scalar for QueryBuilder [[@iamquang95]] * [[#2605]]: Implement Default for QueryBuilder [[@Xydez]] * [[#2616]]: feat(sqlx-core): add table function to database error [[@saiintbrisson]] * [[#2619]]: feat: allow opt-out of `PgHasArrayType` with `#[derive(sqlx::Type)]` [[@abonander]] * TL;DR: if you're getting errors from `#[derive(sqlx::Type)]` with `#[sqlx(transparent)]` regarding `PgHasArrayType` not being implemented, add `#[sqlx(no_pg_array)]` to fix. ### Changed * [[#2566]]: improve docs about migration files [[@jnnnnn]] * [[#2576]]: Major Version Update clap to 4.0 [[@titaniumtraveler]] * [[#2597]]: Bump webpki-roots to v0.24 [[@paolobarbolini]] * [[#2603]]: docs(changelog): be more verbose about offline mode breaking change [[@mrl5]] ### Fixed * [[#2553]]: Implement `Clone` for `PoolOptions` manually (#2548) [[@alilleybrinker]] * [[#2580]]: Update README.md now that 0.7.0 is no longer in alpha [[@saolof]] * [[#2585]]: Fix for Issue #2549 - cannot use feature "rust_decimal" without also using "bigdecimal" [[@deneut]] * [[#2586]]: Fix optional dependency on sqlx-macros [[@kitterion]] * [[#2593]]: Correct mention of the `tls-native-tls` in the documentation. [[@denschub]] * [[#2599]]: Remove incorrect CAST in test database cleanup for MySQL. [[@fd]] * [[#2613]]: Fix readme.md to reduce confusion about optional features (decimal->rust_decimal) [[@vabka]] * [[#2620]]: fix(sqlite/any): encode bool as integer [[@saiintbrisson]] [#2551]: https://github.com/launchbadge/sqlx/pull/2551 [#2553]: https://github.com/launchbadge/sqlx/pull/2553 [#2566]: https://github.com/launchbadge/sqlx/pull/2566 [#2576]: https://github.com/launchbadge/sqlx/pull/2576 [#2580]: https://github.com/launchbadge/sqlx/pull/2580 [#2585]: https://github.com/launchbadge/sqlx/pull/2585 [#2586]: https://github.com/launchbadge/sqlx/pull/2586 [#2593]: https://github.com/launchbadge/sqlx/pull/2593 [#2597]: https://github.com/launchbadge/sqlx/pull/2597 [#2599]: https://github.com/launchbadge/sqlx/pull/2599 [#2603]: https://github.com/launchbadge/sqlx/pull/2603 [#2605]: https://github.com/launchbadge/sqlx/pull/2605 [#2613]: https://github.com/launchbadge/sqlx/pull/2613 [#2616]: https://github.com/launchbadge/sqlx/pull/2616 [#2619]: https://github.com/launchbadge/sqlx/pull/2619 [#2620]: https://github.com/launchbadge/sqlx/pull/2620 ## 0.7.0 - 2023-06-30 At least **70 pull requests** were merged this release cycle! (The exact count is muddied with pull requests for alpha releases and such.) And we gained 43 new contributors! Thank you to everyone who helped make this release a reality. ### Breaking Many revisions were made to query analysis in the SQLite driver; these are all potentially breaking changes as they can change the output of `sqlx::query!()` _et al_. We'd like to thank [[@tyrelr]] for their numerous PRs to this area. The MSSQL driver has been removed as it was not nearly at the same maturity level as the other drivers. [As previously announced][sqlx-pro], we have plans to introduce a fully featured replacement as a premium offering, alongside drivers for other proprietary databases, with the goal to support full-time development on SQLx. If interested, please email your inquiry to sqlx@launchbadge.com. The offline mode for the queries has been changed to use a separate file per `query!()` invocation, which is intended to reduce the number of conflicts when merging branches in a project that both modified queries. This means that CLI flag `--merged` is no longer supported. See [[#2363]] for details and make sure that your `sqlx-cli` version is in sync with the `sqlx` version in your project. The type ascription override syntax for the query macros has been deprecated, as parse support for it has been removed in `syn 2.0`, which we'll be upgrading to in the next breaking release. This can be replaced with type overrides using casting syntax (`as`). See [[#2483]] for details. * [[#1946]]: Fix compile time verification performance regression for sqlite [[@liningpan]] * [[#1960]]: Fix sqlite update return and order by type inference [[@tyrelr]] * [[#1984]]: Sqlite EXPLAIN type inference improvements [[@rongcuid]] * [[#2039]]: Break drivers out into separate crates, clean up some technical debt [[@abonander]] * All deprecated items have been removed. * The `mssql` feature and associated database driver has been deleted from the source tree. It will return as part of our planned SQLx Pro offering as a from-scratch rewrite with extra features (such as TLS) and type integrations that were previously missing. * The `runtime-actix-*` features have been deleted. They were previously changed to be aliases of their `runtime-tokio-*` counterparts for backwards compatibility reasons, but their continued existence is misleading as SQLx has no special knowledge of Actix anymore. * To fix, simply replace the `runtime-actix-*` feature with its `runtime-tokio-*` equivalent. * The `git2` feature has been removed. This was a requested integration from a while ago that over time made less and less sense to be part of SQLx itself. We have to be careful with the crates we add to our public API as each one introduces yet another semver hazard. The expected replacement is to make `#[derive(sqlx::Type)]` useful enough that users can write wrapper types for whatever they want to use without SQLx needing to be specifically aware of it. * The `Executor` impls for `Transaction` and `PoolConnection` have been deleted because they cannot exist in the new crate architecture without rewriting the `Executor` trait entirely. * To fix this breakage, simply add a dereference where an `impl Executor` is expected, as they both dereference to the inner connection type which will still implement it: * `&mut transaction` -> `&mut *transaction` * `&mut connection` -> `&mut *connection` * These cannot be blanket impls as it triggers an overflow in the compiler due to the lack of lazy normalization, and the driver crates cannot provide their own impls due to the orphan rule. * We're expecting to do another major refactor of traits to incorporate generic associated types (GAT). This will mean another major release of SQLx but ideally most API usage will not need to change significantly, if at all. * The fields of `Migrator` are now `#[doc(hidden)]` and semver-exempt; they weren't meant to be public. * The `offline` feature has been removed from the `sqlx` facade crate and is enabled unconditionally as most users are expected to have enabled it anyway and disabling it doesn't seem to appreciably affect compile times. * The `decimal` feature has been renamed to `rust_decimal` to match the crate it actually provides integrations for. * `AnyDriver` and `AnyConnection` now require either `sqlx::any::install_drivers()` or `sqlx::any::install_default_drivers()` to be called at some point during the process' lifetime before the first connection is made, as the set of possible drivers is now determined at runtime. This was determined to be the least painful way to provide knowledge of database drivers to `Any` without them being hardcoded. * The `AnyEncode` trait has been removed. * [[#2109]]: feat: better database errors [[@saiintbrisson]] * [[#2094]]: Update libsqlite3-sys to 0.25.1 [[@penberg]] * Alongside this upgrade, we are now considering the linkage to `libsqlite3-sys` to be **semver-exempt**, and we reserve the right to upgrade it as necessary. If you are using `libsqlite3-sys` directly or a crate that links it such as `rusqlite`, you should pin the versions of both crates to avoid breakages from `cargo update`: ```toml [dependencies] sqlx = { version = "=0.7.0", features = ["sqlite"] } rusqlite = "=0.29.0" ``` * [[#2132]]: fix: use owned Builder pattern for ConnectOptions [[@ar3s3ru]] * [[#2253]]: Sqlite describe fixes [[@tyrelr]] * [[#2285]]: `time`: Assume UTC when decoding a DATETIME column in sqlite [[@nstinus]] * [[#2363]]: [offline] Change prepare to one-file-per-query [[@cycraig]] * [[#2387]]: PATCH: bump libsqlite3-sys to patched version [[@grantkee]] * [[#2409]]: fix(#2407): respect the HaltIfNull opcode when determining nullability [[@arlyon]] * [[#2459]]: limit the number of instructions that can be evaluated [[@tyrelr]] * [[#2467]]: Add and improve sqlite describe performance benchmarks [[@tyrelr]] * [[#2491]]: sqlite date macro support [[@Arcayr]] * Changes `OffsetDateTime` to be the first type used when deserializing a `timestamp` type. * [[#2496]]: Bump to libsqlite3-sys 0.26 [[@mdecimus]] * [[#2508]]: Sqlite analytical [[@tyrelr]] ### Added * [[#1850]]: Add client SSL authentication using key-file for Postgres, MySQL and MariaDB [[@ThibsG]] * [[#2088]]: feat: Add set_connect_options method to Pool [[@moatra]] * [[#2113]]: Expose PoolOptions for reading [[@FSMaxB]] * [[#2115]]: Allow using complex types in `try_from` when deriving `FromRow` [[@95ulisse]] * [[#2116]]: [SQLite] Add option to execute `PRAGMA optimize;` on close of a connection [[@miles170]] * [[#2189]]: Added regexp support in sqlite [[@VictorKoenders]] * [[#2224]]: Add From impls for Json [[@dbeckwith]] * [[#2256]]: add progress handler support to sqlite [[@nbaztec]] * [[#2366]]: Allow ignoring attributes for deriving FromRow [[@grgi]] * [[#2369]]: new type support in query_as [[@0xdeafbeef]] * [[#2379]]: feat: add `Connection::shrink_buffers`, `PoolConnection::close` [[@abonander]] * [[#2400]]: fix(docs): example of `sqlx_macros_unstable` in config.toml [[@df51d]] * [[#2469]]: Add Simple format for Uuid for MySQL & SQLite. [[@MidasLamb]] * [[#2483]]: chore: add deprecation notice for type ascription use [[@saiintbrisson]] * [[#2506]]: add args to query builder (#2494) [[@cemoktra]] * [[#2554]]: Impl `AsMut` for advisory lock types (#2520) [[@alilleybrinker]] * [[#2559]]: Add CLI autocompletion using clap_complete [[@titaniumtraveler]] ### Changed * [[#2185]]: Initial work to switch to `tracing` [[@CosmicHorrorDev]] * [[#2193]]: Start testing on Postgres 15 and drop Postgres 10 [[@paolobarbolini]] * We reserve the right to drop support for end-of-lifed database versions [as discussed in our FAQ][faq-db-version]. * [[#2213]]: Use `let else` statements in favor of macro [[@OverHash]] * [[#2365]]: Update dependencies [[@paolobarbolini]] * [[#2371]]: Disable rustls crate logging feature by default up to date [[@sergeiivankov]] * [[#2373]]: chore: Use tracing's fields to get structured logs [[@jaysonsantos]] * [[#2393]]: Lower default logging level for statements to Debug [[@bnoctis]] * [[#2445]]: Traverse symlinks when resolving migrations [[@tgeoghegan]] * [[#2485]]: chore(sqlx-postgres): replace `dirs` with `home` & `etcetera` [[@utkarshgupta137]] * [[#2515]]: Bump mac_address to 1.1.5 [[@repnop]] * [[#2440]]: Update rustls to 0.21, webpki-roots to 0.23 [[@SergioBenitez]] * [[#2563]]: Update rsa to 0.9 [[@paolobarbolini]] * [[#2564]]: Update bitflags to v2 [[@paolobarbolini]] * [[#2565]]: Bump indexmap and ahash [[@paolobarbolini]] * [[#2574]]: doc: make it clear that `ConnectOptions` types impl `FromStr` [[@abonander]] ### Fixed * [[#2098]]: Fix sqlite compilation [[@cycraig]] * [[#2120]]: fix logical merge conflict [[@tyrelr]] * [[#2133]]: Postgres OID resolution query does not take into account current `search_path` [[@95ulisse]] * [[#2156]]: Fixed typo. [[@cdbfoster]] * [[#2179]]: fix: ensures recover from fail with PgCopyIn [[@andyquinterom]] * [[#2200]]: Run CI on *-dev branch [[@joehillen]] * [[#2222]]: Add context to confusing sqlx prepare parse error [[@laundmo]] * [[#2271]]: feat: support calling Postgres procedures with the macros [[@bgeron]] * [[#2282]]: Don't run EXPLAIN nullability analysis on Materialize [[@benesch]] * [[#2319]]: Set whoami default-features to false [[@thedodd]] * [[#2352]]: Preparing 0.7.0-alpha.1 release [[@abonander]] * [[#2355]]: Fixed the example code for `sqlx::test` [[@kenkoooo]] * [[#2367]]: Fix sqlx-cli create, drop, migrate [[@cycraig]] * [[#2376]]: fix(pool): close when last handle is dropped, extra check in `try_acquire` [[@abonander]] * [[#2378]]: Fix README build badge [[@dbrgn]] * [[#2398]]: fix(prepare): store temporary query files inside the workspace [[@aschey]] * [[#2402]]: fix: drop old time 0.1.44 dep [[@codahale]] * [[#2413]]: fix(macros-core): use of undeclared `tracked_path` [[@df51d]] * [[#2420]]: Enable runtime-tokio feature of sqlx when building sqlx-cli [[@paolobarbolini]] * [[#2453]]: in README.md, correct spelling and grammar [[@vizvasrj]] * [[#2454]]: fix: ensure fresh test db's aren't accidentally deleted by do_cleanup [[@phlip9]] * [[#2507]]: Exposing the Oid of PostgreSQL types [[@Razican]] * [[#2519]]: Use ::std::result::Result::Ok in output.rs [[@southball]] * [[#2569]]: Fix broken links to mysql error documentation [[@titaniumtraveler]] * [[#2570]]: Add a newline to the generated JSON files [[@nyurik]] * [[#2572]]: Do not panic when `PrepareOk` fails to decode [[@stepantubanov]] * [[#2573]]: fix(sqlite) Do not drop notify mutex guard until after condvar is triggered [[@andrewwhitehead]] [sqlx-pro]: https://github.com/launchbadge/sqlx/discussions/1616 [faq-db-version]: https://github.com/launchbadge/sqlx/blob/main/FAQ.md#what-database-versions-does-sqlx-support [#1850]: https://github.com/launchbadge/sqlx/pull/1850 [#1946]: https://github.com/launchbadge/sqlx/pull/1946 [#1960]: https://github.com/launchbadge/sqlx/pull/1960 [#1984]: https://github.com/launchbadge/sqlx/pull/1984 [#2039]: https://github.com/launchbadge/sqlx/pull/2039 [#2088]: https://github.com/launchbadge/sqlx/pull/2088 [#2092]: https://github.com/launchbadge/sqlx/pull/2092 [#2094]: https://github.com/launchbadge/sqlx/pull/2094 [#2098]: https://github.com/launchbadge/sqlx/pull/2098 [#2109]: https://github.com/launchbadge/sqlx/pull/2109 [#2113]: https://github.com/launchbadge/sqlx/pull/2113 [#2115]: https://github.com/launchbadge/sqlx/pull/2115 [#2116]: https://github.com/launchbadge/sqlx/pull/2116 [#2120]: https://github.com/launchbadge/sqlx/pull/2120 [#2132]: https://github.com/launchbadge/sqlx/pull/2132 [#2133]: https://github.com/launchbadge/sqlx/pull/2133 [#2156]: https://github.com/launchbadge/sqlx/pull/2156 [#2179]: https://github.com/launchbadge/sqlx/pull/2179 [#2185]: https://github.com/launchbadge/sqlx/pull/2185 [#2189]: https://github.com/launchbadge/sqlx/pull/2189 [#2193]: https://github.com/launchbadge/sqlx/pull/2193 [#2200]: https://github.com/launchbadge/sqlx/pull/2200 [#2213]: https://github.com/launchbadge/sqlx/pull/2213 [#2222]: https://github.com/launchbadge/sqlx/pull/2222 [#2224]: https://github.com/launchbadge/sqlx/pull/2224 [#2253]: https://github.com/launchbadge/sqlx/pull/2253 [#2256]: https://github.com/launchbadge/sqlx/pull/2256 [#2271]: https://github.com/launchbadge/sqlx/pull/2271 [#2282]: https://github.com/launchbadge/sqlx/pull/2282 [#2285]: https://github.com/launchbadge/sqlx/pull/2285 [#2319]: https://github.com/launchbadge/sqlx/pull/2319 [#2352]: https://github.com/launchbadge/sqlx/pull/2352 [#2355]: https://github.com/launchbadge/sqlx/pull/2355 [#2363]: https://github.com/launchbadge/sqlx/pull/2363 [#2365]: https://github.com/launchbadge/sqlx/pull/2365 [#2366]: https://github.com/launchbadge/sqlx/pull/2366 [#2367]: https://github.com/launchbadge/sqlx/pull/2367 [#2369]: https://github.com/launchbadge/sqlx/pull/2369 [#2371]: https://github.com/launchbadge/sqlx/pull/2371 [#2373]: https://github.com/launchbadge/sqlx/pull/2373 [#2376]: https://github.com/launchbadge/sqlx/pull/2376 [#2378]: https://github.com/launchbadge/sqlx/pull/2378 [#2379]: https://github.com/launchbadge/sqlx/pull/2379 [#2387]: https://github.com/launchbadge/sqlx/pull/2387 [#2393]: https://github.com/launchbadge/sqlx/pull/2393 [#2398]: https://github.com/launchbadge/sqlx/pull/2398 [#2400]: https://github.com/launchbadge/sqlx/pull/2400 [#2402]: https://github.com/launchbadge/sqlx/pull/2402 [#2408]: https://github.com/launchbadge/sqlx/pull/2408 [#2409]: https://github.com/launchbadge/sqlx/pull/2409 [#2413]: https://github.com/launchbadge/sqlx/pull/2413 [#2420]: https://github.com/launchbadge/sqlx/pull/2420 [#2440]: https://github.com/launchbadge/sqlx/pull/2440 [#2445]: https://github.com/launchbadge/sqlx/pull/2445 [#2453]: https://github.com/launchbadge/sqlx/pull/2453 [#2454]: https://github.com/launchbadge/sqlx/pull/2454 [#2459]: https://github.com/launchbadge/sqlx/pull/2459 [#2467]: https://github.com/launchbadge/sqlx/pull/2467 [#2469]: https://github.com/launchbadge/sqlx/pull/2469 [#2483]: https://github.com/launchbadge/sqlx/pull/2483 [#2485]: https://github.com/launchbadge/sqlx/pull/2485 [#2491]: https://github.com/launchbadge/sqlx/pull/2491 [#2496]: https://github.com/launchbadge/sqlx/pull/2496 [#2506]: https://github.com/launchbadge/sqlx/pull/2506 [#2507]: https://github.com/launchbadge/sqlx/pull/2507 [#2508]: https://github.com/launchbadge/sqlx/pull/2508 [#2515]: https://github.com/launchbadge/sqlx/pull/2515 [#2519]: https://github.com/launchbadge/sqlx/pull/2519 [#2554]: https://github.com/launchbadge/sqlx/pull/2554 [#2559]: https://github.com/launchbadge/sqlx/pull/2559 [#2563]: https://github.com/launchbadge/sqlx/pull/2563 [#2564]: https://github.com/launchbadge/sqlx/pull/2564 [#2565]: https://github.com/launchbadge/sqlx/pull/2565 [#2569]: https://github.com/launchbadge/sqlx/pull/2569 [#2570]: https://github.com/launchbadge/sqlx/pull/2570 [#2572]: https://github.com/launchbadge/sqlx/pull/2572 [#2573]: https://github.com/launchbadge/sqlx/pull/2573 [#2574]: https://github.com/launchbadge/sqlx/pull/2574 ## 0.6.3 - 2023-03-21 This is a hotfix to address the breakage caused by transitive dependencies upgrading to `syn = "2"`. We set `default-features = false` for our dependency on `syn = "1"` to be good crates.io citizens, but failed to enable the features we actually used, which went undetected because we transitively depended on `syn` with the default features enabled through other crates, and so they were also on for us because features are additive. When those other dependencies upgraded to `syn = "2"` it was no longer enabling those features for us, and so compilation broke for projects that don't also depend on `syn = "1"`, transitively or otherwise. There is no PR for this fix as there was no longer a dedicated development branch for `0.6`, but discussion can be found in [issue #2418]. As of this release, the `0.7` release is in alpha and so development is no longer occurring against `0.6`. This fix will be forward-ported to `0.7`. [issue #2418]: https://github.com/launchbadge/sqlx/issues/2418 ## 0.6.2 - 2022-09-14 [25 pull requests][0.6.2-prs] were merged this release cycle. ### Added * [[#1081]]: Add `try_from` attribute for `FromRow` derive [[@zzhengzhuo]] * Exemplifies "out of sight, out of mind." It's surprisingly easy to forget about PRs when they get pushed onto the second page. We'll be sure to clean out the backlog for 0.7.0. * [[#2014]]: Support additional SQLCipher options in SQLite driver. [[@szymek156]] * [[#2052]]: Add issue templates [[@abonander]] * [[#2053]]: Add documentation for `IpAddr` support in Postgres [[@rakshith-ravi]] * [[#2062]]: Add extension support for SQLite [[@bradfier]] * [[#2063]]: customizable db locking during migration [[@fuzzbuck]] ### Changed * [[#2025]]: Bump sqlformat to 2.0 [[@NSMustache]] * [[#2056]]: chore: Switch to sha1 crate [[@stoically]] * [[#2071]]: Use cargo check consistently in `prepare` [[@cycraig]] ### Fixed * [[#1991]]: Ensure migration progress is not lost for Postgres, MySQL and SQLite. [[@crepererum]] * [[#2023]]: Fix expansion of `#[sqlx(flatten)]` for `FromRow` derive [[@RustyYato]] * [[#2028]]: Use fully qualified path when forwarding to `#[test]` from `#[sqlx::test]` [[@alexander-jackson]] * [[#2040]]: Fix typo in `FromRow` docs [[@zlidner]] * [[#2046]]: added flag for PIPES_AS_CONCAT connection setting for MySQL to fix #2034 [[@marcustut]] * [[#2055]]: Use unlock notify also on `sqlite3_exec` [[@madadam]] * [[#2057]]: Make begin,commit,rollback cancel-safe in sqlite [[@madadam]] * [[#2058]]: fix typo in documentation [[@lovasoa]] * [[#2067]]: fix(docs): close code block in query_builder.rs [[@abonander]] * [[#2069]]: Fix `prepare` race condition in workspaces [[@cycraig]]\ * NOTE: this changes the directory structure under `target/` that `cargo sqlx prepare` depends on. If you use offline mode in your workflow, please rerun `cargo install sqlx-cli` to upgrade. * [[#2072]]: SqliteConnectOptions typo [[@fasterthanlime]] * [[#2074]]: fix: mssql uses unsigned for tinyint instead of signed [[@he4d]] * [[#2081]]: close unnamed portal after each executed extended query [[@DXist]] * [[#2086]]: PgHasArrayType for transparent types fix. [[@Wopple]] * NOTE: this is a breaking change and has been postponed to 0.7.0. * [[#2089]]: fix: Remove default chrono dep on time for sqlx-cli [[@TravisWhitehead]] * [[#2091]]: Sqlite explain plan log efficiency [[@tyrelr]] [0.6.2-prs]: https://github.com/launchbadge/sqlx/pulls?q=is%3Apr+is%3Aclosed+merged%3A2022-08-04..2022-09-14+ [#1081]: https://github.com/launchbadge/sqlx/pull/1081 [#1991]: https://github.com/launchbadge/sqlx/pull/1991 [#2014]: https://github.com/launchbadge/sqlx/pull/2014 [#2023]: https://github.com/launchbadge/sqlx/pull/2023 [#2025]: https://github.com/launchbadge/sqlx/pull/2025 [#2028]: https://github.com/launchbadge/sqlx/pull/2028 [#2040]: https://github.com/launchbadge/sqlx/pull/2040 [#2046]: https://github.com/launchbadge/sqlx/pull/2046 [#2052]: https://github.com/launchbadge/sqlx/pull/2052 [#2053]: https://github.com/launchbadge/sqlx/pull/2053 [#2055]: https://github.com/launchbadge/sqlx/pull/2055 [#2056]: https://github.com/launchbadge/sqlx/pull/2056 [#2057]: https://github.com/launchbadge/sqlx/pull/2057 [#2058]: https://github.com/launchbadge/sqlx/pull/2058 [#2062]: https://github.com/launchbadge/sqlx/pull/2062 [#2063]: https://github.com/launchbadge/sqlx/pull/2063 [#2067]: https://github.com/launchbadge/sqlx/pull/2067 [#2069]: https://github.com/launchbadge/sqlx/pull/2069 [#2071]: https://github.com/launchbadge/sqlx/pull/2071 [#2072]: https://github.com/launchbadge/sqlx/pull/2072 [#2074]: https://github.com/launchbadge/sqlx/pull/2074 [#2081]: https://github.com/launchbadge/sqlx/pull/2081 [#2086]: https://github.com/launchbadge/sqlx/pull/2086 [#2089]: https://github.com/launchbadge/sqlx/pull/2089 [#2091]: https://github.com/launchbadge/sqlx/pull/2091 ## 0.6.1 - 2022-08-02 [33 pull requests][0.6.1-prs] were merged this release cycle. ### Added * [[#1495]]: Add example for manual implementation of the `FromRow` trait [[@Erik1000]] * [[#1822]]: (Postgres) Add support for `std::net::IpAddr` [[@meh]] * Decoding returns an error if the `INET` value in Postgres is a prefix and not a full address (`/32` for IPv4, `/128` for IPv6). * [[#1865]]: Add SQLite support for the `time` crate [[@johnbcodes]] * [[#1902]]: Add an example of how to use `QueryBuilder::separated()` [[@sbeckeriv]] * [[#1917]]: Added docs for `sqlx::types::Json` [[@jayy-lmao]] * [[#1919]]: Implement `Clone` for `PoolOptions` [[@Thomasdezeeuw]] * [[#1953]]: Support Rust arrays in Postgres [[@e00E]] * [[#1954]]: Add `push_tuples` for `QueryBuilder` [[@0xdeafbeef]] * [[#1959]]: Support `#[sqlx(flatten)]` attribute in `FromRow` [[@TheoOiry]] * [[#1967]]: Add example with external query files [[@JoeyMckenzie]] * [[#1985]]: Add `query_builder::Separated::push_bind_unseparated()` [[@0xdeafbeef]] * [[#2001]]: Implement `#[sqlx::test]` for general use * Includes automatic database management, migration and fixture application. * Drops support for end-of-lifed database versions, see PR for details. * [[#2005]]: `QueryBuilder` improvements [[@abonander]] * Raw SQL getters, new method to build `QueryAs` instead of `Query`. * [[#2013]]: (SQLite) Allow VFS to be set as URL query parameter [[@liningpan]] ### Changed * [[#1679]]: refactor: alias actix-* features to their equivalent tokio-* features [[@robjtede]] * [[#1906]]: replaced all uses of "uri" to "url" [[@RomainStorai]] * [[#1965]]: SQLite improvements [[@abonander]] * [[#1977]]: Docs: clarify relationship between `query_as!()` and `FromRow` [[@abonander]] * [[#2003]]: Replace `dotenv` with `dotenvy` [[@abonander]] ### Fixed * [[#1802]]: Try avoiding a full clean in `cargo sqlx prepare --merged` [[@LovecraftianHorror]] * [[#1848]]: Fix type info access in `Any` database driver [[@raviqqe]] * [[#1910]]: Set `CARGO_TARGET_DIR` when compiling queries [[@sedrik]] * [[#1915]]: Pool: fix panic when using callbacks [[@abonander]] * [[#1930]]: Don't cache SQLite connection for macros [[@LovecraftianHorror]] * [[#1948]]: Fix panic in Postgres `BYTEA` decode [[@e00E]] * [[#1955]]: Fix typo in FAQ [[@kenkoooo]] * [[#1968]]: (Postgres) don't panic if `S` or `V` notice fields are not UTF-8 [[@abonander]] * [[#1969]]: Fix sqlx-cli build [[@ivan]] * [[#1974]]: Use the `rust-cache` action for CI [[@abonander]] * [[#1988]]: Agree on a single default runtime for the whole workspace [[@crepererum]] * [[#1989]]: Fix panics in `PgListener` [[@crepererum]] * [[#1990]]: Switch `master` to `main` in docs [[@crepererum]] * The change had already been made in the repo, the docs were out of date. * [[#1993]]: Update versions in quickstart examples in README [[@UramnOIL]] [0.6.1-prs]: https://github.com/launchbadge/sqlx/pulls?page=1&q=is%3Apr+is%3Aclosed+merged%3A2022-06-17..2022-08-02 [#1906]: https://github.com/launchbadge/sqlx/pull/1906 [#1495]: https://github.com/launchbadge/sqlx/pull/1495 [#1679]: https://github.com/launchbadge/sqlx/pull/1679 [#1802]: https://github.com/launchbadge/sqlx/pull/1802 [#1822]: https://github.com/launchbadge/sqlx/pull/1822 [#1848]: https://github.com/launchbadge/sqlx/pull/1848 [#1865]: https://github.com/launchbadge/sqlx/pull/1865 [#1902]: https://github.com/launchbadge/sqlx/pull/1902 [#1910]: https://github.com/launchbadge/sqlx/pull/1910 [#1915]: https://github.com/launchbadge/sqlx/pull/1915 [#1917]: https://github.com/launchbadge/sqlx/pull/1917 [#1919]: https://github.com/launchbadge/sqlx/pull/1919 [#1930]: https://github.com/launchbadge/sqlx/pull/1930 [#1948]: https://github.com/launchbadge/sqlx/pull/1948 [#1953]: https://github.com/launchbadge/sqlx/pull/1953 [#1954]: https://github.com/launchbadge/sqlx/pull/1954 [#1955]: https://github.com/launchbadge/sqlx/pull/1955 [#1959]: https://github.com/launchbadge/sqlx/pull/1959 [#1965]: https://github.com/launchbadge/sqlx/pull/1965 [#1967]: https://github.com/launchbadge/sqlx/pull/1967 [#1968]: https://github.com/launchbadge/sqlx/pull/1968 [#1969]: https://github.com/launchbadge/sqlx/pull/1969 [#1974]: https://github.com/launchbadge/sqlx/pull/1974 [#1977]: https://github.com/launchbadge/sqlx/pull/1977 [#1985]: https://github.com/launchbadge/sqlx/pull/1985 [#1988]: https://github.com/launchbadge/sqlx/pull/1988 [#1989]: https://github.com/launchbadge/sqlx/pull/1989 [#1990]: https://github.com/launchbadge/sqlx/pull/1990 [#1993]: https://github.com/launchbadge/sqlx/pull/1993 [#2001]: https://github.com/launchbadge/sqlx/pull/2001 [#2003]: https://github.com/launchbadge/sqlx/pull/2003 [#2005]: https://github.com/launchbadge/sqlx/pull/2005 [#2013]: https://github.com/launchbadge/sqlx/pull/2013 ## 0.6.0 - 2022-06-16 This release marks the end of the 0.5.x series of releases and contains a number of breaking changes, mainly to do with backwards-incompatible dependency upgrades. As we foresee many more of these in the future, we [surveyed the community] on how to handle this; the consensus appears to be "just release breaking changes more often." As such, we expect the 0.6.x release series to be a shorter one. [39 pull requests(!)][0.6.0-prs] (not counting "prepare 0.5.12 release", of course) were merged this release cycle. ### Breaking * [[#1384]]: (Postgres) Move `server_version_num` from trait to inherent impl [[@AtkinsChang]] * [[#1426]]: Bump `ipnetwork` to 0.19 [[@paolobarbolini]] * [[#1455]]: Upgrade `time` to 0.3 [[@paolobarbolini]] * [[#1505]]: Upgrade `rustls` to 0.20 [[@paolobarbolini]] * Fortunately, future upgrades should not be breaking as `webpki` is no longer exposed in the API. * [[#1529]]: Upgrade `bigdecimal` to 0.3 [[@e00E]] * [[#1602]]: postgres: use `Oid` everywhere instead of `u32` [[@paolobarbolini]] * This drops the `Type`, `Decode`, `Encode` impls for `u32` for Postgres as it was misleading. Postgres doesn't support unsigned ints without using an extension. These impls were decoding Postgres `OID`s as bare `u32`s without any context (and trying to bind a `u32` to a query would produce an `OID` value in SQL). This changes that to use a newtype instead, for clarity. * [[#1612]]: Make all `ConnectOptions` types cloneable [[@05storm26]] * [[#1618]]: SQLite `chrono::DateTime` timezone fix [[@05storm26]] * `DateTime` will be stored in SQLite with the correct timezone instead of always in UTC. This was flagged as a "potentially breaking change" since it changes how dates are sent to SQLite. * [[#1733]]: Update `git2` to 0.14 [[@joshtriplett]] * [[#1734]]: Make `PgLTree::push()` infallible and take `PgLTreeLabel` directly [[@sebpuetz]] * [[#1785]]: Fix Rust type for SQLite `REAL` [[@pruthvikar]] * Makes the macros always map a `REAL` column to `f64` instead of `f32` as SQLite uses **only** 64-bit floats. * [[#1816]]: Improve SQLite support for sub-queries and CTEs [[@tyrelr]] * This likely will change the generated code for some invocations `sqlx::query!()` with SQLite. * [[#1821]]: Update `uuid` crate to v1 [[@paolobarbolini]] * [[#1901]]: Pool fixes and breaking changes [[@abonander]] * Renamed `PoolOptions::connect_timeout` to `acquire_timeout` for clarity. * Changed the expected signatures for `PoolOptions::after_connect`, `before_acquire`, `after_release` * Changed the signature for `Pool::close()` slightly * Now eagerly starts the pool closing, `.await`ing is only necessary if you want to ensure a graceful shutdown. * Deleted `PoolConnection::release()` which was previously deprecated in favor of `PoolConnection::detach()`. * Fixed connections getting leaked even when calling `.close()`. * [[#1748]]: Derive `PgHasArrayType` for `#[sqlx(transparent)]` types [[@carols10cents]] * This change was released with 0.5.12 but [we didn't realize it was a breaking change] at the time. It was reverted in 0.5.13 and postponed until this release. ### Added * [[#1843]]: Expose some useful methods on `PgValueRef` [[@mfreeborn]] * [[#1889]]: SQLx-CLI: add `--connect-timeout` [[@abonander]] * Adds a default 10 second connection timeout to all commands. * [[#1890]]: Added test for mssql LoginAck [[@walf443]] * [[#1891]]: Added test for mssql ProtocolInfo [[@walf443]] * [[#1892]]: Added test for mssql ReturnValue [[@walf443]] * [[#1895]]: Add support for `i16` to `Any` driver [[@EthanYuan]] * [[#1897]]: Expose `ConnectOptions` and `PoolOptions` on `Pool` and database name on `PgConnectOptions` [[@Nukesor]] ### Changed * [[#1782]]: Reuse a cached DB connection instead of always opening a new one for `sqlx-macros` [[@LovecraftianHorror]] * [[#1807]]: Bump remaining dependencies [[@paolobarbolini]] * [[#1808]]: Update to edition 2021 [[@paolobarbolini]] * Note that while SQLx [does not officially track an MSRV] and only officially supports the latest stable Rust, this effectively places a lower bound of 1.56.0 on the range of versions it may work with. * [[#1823]]: (sqlx-macros) Ignore deps when getting metadata for workspace root [[@LovecraftianHorror]] * [[#1831]]: Update `crc` to 3.0 [[@djc]] * [[#1887]]: query_as: don't stop stream after decoding error [[@lovasoa]] ### Fixed * [[#1814]]: SQLx-cli README: move `Usage` to the same level as `Install` [[@tobymurray]] * [[#1815]]: SQLx-cli README: reword "building in offline mode" [[@tobymurray]] * [[#1818]]: Trim `[]` from host string before passing to TcpStream [[@smonv]] * This fixes handling of database URLs with IPv6 hosts. * [[#1842]]: Fix usage of `serde_json` in macros [[@mfreeborn]] * [[#1855]]: Postgres: fix panics on unknown type OID when decoding [[@demurgos]] * [[#1856]]: MySQL: support COLLATE_UTF8MB4_0900_AI_CI [[@scottwey]] * Fixes the MySQL driver thinking text columns are bytestring columns when querying against a Planetscale DB. * [[#1861]]: MySQL: avoid panic when streaming packets are empty [[@e-rhodes]] * [[#1863]]: Fix nullability check for inner joins in Postgres [[@OskarPersson]] * [[#1881]]: Fix `field is never read` warnings on Postgres test [[@walf443]] * [[#1882]]: Fix `unused result must be used` warnings [[@walf443]] * [[#1888]]: Fix migration checksum comparison during `sqlx migrate info` [[@mdtusz]] * [[#1894]]: Fix typos [[@kianmeng]] [surveyed the community]: https://github.com/launchbadge/sqlx/issues/1796 [0.6.0-prs]: https://github.com/launchbadge/sqlx/pulls?page=2&q=is%3Apr+is%3Amerged+merged%3A2022-04-14..2022-06-16 [does not officially track an MSRV]: /FAQ.md#what-versions-of-rust-does-sqlx-support-what-is-sqlxs-msrv [we didn't realize it was a breaking change]: https://github.com/launchbadge/sqlx/pull/1800#issuecomment-1099898932 [#1384]: https://github.com/launchbadge/sqlx/pull/1384 [#1426]: https://github.com/launchbadge/sqlx/pull/1426 [#1455]: https://github.com/launchbadge/sqlx/pull/1455 [#1505]: https://github.com/launchbadge/sqlx/pull/1505 [#1529]: https://github.com/launchbadge/sqlx/pull/1529 [#1602]: https://github.com/launchbadge/sqlx/pull/1602 [#1612]: https://github.com/launchbadge/sqlx/pull/1612 [#1618]: https://github.com/launchbadge/sqlx/pull/1618 [#1733]: https://github.com/launchbadge/sqlx/pull/1733 [#1734]: https://github.com/launchbadge/sqlx/pull/1734 [#1782]: https://github.com/launchbadge/sqlx/pull/1782 [#1785]: https://github.com/launchbadge/sqlx/pull/1785 [#1807]: https://github.com/launchbadge/sqlx/pull/1807 [#1808]: https://github.com/launchbadge/sqlx/pull/1808 [#1814]: https://github.com/launchbadge/sqlx/pull/1814 [#1815]: https://github.com/launchbadge/sqlx/pull/1815 [#1816]: https://github.com/launchbadge/sqlx/pull/1816 [#1818]: https://github.com/launchbadge/sqlx/pull/1818 [#1821]: https://github.com/launchbadge/sqlx/pull/1821 [#1823]: https://github.com/launchbadge/sqlx/pull/1823 [#1831]: https://github.com/launchbadge/sqlx/pull/1831 [#1842]: https://github.com/launchbadge/sqlx/pull/1842 [#1843]: https://github.com/launchbadge/sqlx/pull/1843 [#1855]: https://github.com/launchbadge/sqlx/pull/1855 [#1856]: https://github.com/launchbadge/sqlx/pull/1856 [#1861]: https://github.com/launchbadge/sqlx/pull/1861 [#1863]: https://github.com/launchbadge/sqlx/pull/1863 [#1881]: https://github.com/launchbadge/sqlx/pull/1881 [#1882]: https://github.com/launchbadge/sqlx/pull/1882 [#1887]: https://github.com/launchbadge/sqlx/pull/1887 [#1888]: https://github.com/launchbadge/sqlx/pull/1888 [#1889]: https://github.com/launchbadge/sqlx/pull/1889 [#1890]: https://github.com/launchbadge/sqlx/pull/1890 [#1891]: https://github.com/launchbadge/sqlx/pull/1891 [#1892]: https://github.com/launchbadge/sqlx/pull/1892 [#1894]: https://github.com/launchbadge/sqlx/pull/1894 [#1895]: https://github.com/launchbadge/sqlx/pull/1895 [#1897]: https://github.com/launchbadge/sqlx/pull/1897 [#1901]: https://github.com/launchbadge/sqlx/pull/1901 ## 0.5.13 - 2022-04-15 This is a hotfix that reverts [#1748] as that was an accidental breaking change: the generated `PgHasArrayType` impl conflicts with manual impls of the trait. This change will have to wait for 0.6.0. ## 0.5.12 - 2022-04-13 (Yanked; use 0.5.13) [27 pull requests][0.5.12-prs] were merged this release cycle. ### Added * [[#1641]]: Postgres: Convenient wrapper for advisory locks [[@abonander]] * [[#1675]]: Add function to undo migrations [[@jdrouet]] * [[#1722]]: Postgres: implement `PgHasArrayType` for `serde_json::{Value, RawValue}` [[@abreis]] * [[#1736]]: Derive `Clone` for `MySqlArguments` and `MssqlArguments` [[@0xdeafbeef]] * [[#1748]]: Derive `PgHasArrayType` for `#[sqlx(transparent)]` types [[@carols10cents]] * [[#1754]]: Include affected rows alongside returned rows in query logging [[@david-mcgillicuddy-moixa]] * [[#1757]]: Implement `Type` for `Cow` for MySQL, MSSQL and SQLite [[@ipetkov]] * [[#1769]]: sqlx-cli: add `--source` to migration subcommands [[@pedromfedricci]] * [[#1774]]: Postgres: make `extra_float_digits` settable [[@abonander]] * Can be set to `None` for Postgres or third-party database servers that don't support the option. * [[#1776]]: Implement close-event notification for Pool [[@abonander]] * Also fixes `PgListener` preventing `Pool::close()` from resolving. * [[#1780]]: Implement query builder [[@crajcan]] * See also [[#1790]]: Document and expand query builder [[@abonander]] * [[#1781]]: Postgres: support `NUMERIC[]` using `decimal` feature [[@tm-drtina]] * [[#1784]]: SQLite: add `FromStr`, `Copy`, `PartialEq`, `Eq` impls for options enums [[@andrewwhitehead]] ### Changed * [[#1625]]: Update RustCrypto crates [[@paolobarbolini]] * [[#1725]]: Update `heck` to 0.4 [[@paolobarbolini]] * [[#1738]]: Update `regex` [[@Dylan-DPC]] * [[#1763]]: SQLite: update `libsqlite3-sys` [[@espindola]] ### Fixed * [[#1719]]: Fix a link in `query!()` docs [[@vbmade2000]] * [[#1731]]: Postgres: fix option passing logic [[@liushuyu]] * [[#1735]]: sqlx-cli: pass `DATABASE_URL` to command spawned in `prepare` [[@LovecraftianHorror]] * [[#1741]]: Postgres: fix typo in `TSTZRANGE` [[@mgrachev]] * [[#1761]]: Fix link from `QueryAs` to `query_as()` in docs [[@mgrachev]] * [[#1786]]: MySQL: silence compile warnings for unused fields [[@andrewwhitehead]] * [[#1789]]: SQLite: fix left-joins breaking `query!()` macros [[@tyrelr]] * [[#1791]]: Postgres: fix newline parsing of `.pgpass` files [[@SebastienGllmt]] * [[#1799]]: `PoolConnection`: don't leak connection permit if drop task fails to run [[@abonander]] [#1625]: https://github.com/launchbadge/sqlx/pull/1625 [#1641]: https://github.com/launchbadge/sqlx/pull/1641 [#1675]: https://github.com/launchbadge/sqlx/pull/1675 [#1719]: https://github.com/launchbadge/sqlx/pull/1719 [#1722]: https://github.com/launchbadge/sqlx/pull/1722 [#1725]: https://github.com/launchbadge/sqlx/pull/1725 [#1731]: https://github.com/launchbadge/sqlx/pull/1731 [#1735]: https://github.com/launchbadge/sqlx/pull/1735 [#1736]: https://github.com/launchbadge/sqlx/pull/1736 [#1738]: https://github.com/launchbadge/sqlx/pull/1738 [#1741]: https://github.com/launchbadge/sqlx/pull/1741 [#1748]: https://github.com/launchbadge/sqlx/pull/1748 [#1754]: https://github.com/launchbadge/sqlx/pull/1754 [#1757]: https://github.com/launchbadge/sqlx/pull/1757 [#1761]: https://github.com/launchbadge/sqlx/pull/1761 [#1763]: https://github.com/launchbadge/sqlx/pull/1763 [#1769]: https://github.com/launchbadge/sqlx/pull/1769 [#1774]: https://github.com/launchbadge/sqlx/pull/1774 [#1776]: https://github.com/launchbadge/sqlx/pull/1776 [#1780]: https://github.com/launchbadge/sqlx/pull/1780 [#1781]: https://github.com/launchbadge/sqlx/pull/1781 [#1784]: https://github.com/launchbadge/sqlx/pull/1784 [#1786]: https://github.com/launchbadge/sqlx/pull/1786 [#1789]: https://github.com/launchbadge/sqlx/pull/1789 [#1790]: https://github.com/launchbadge/sqlx/pull/1790 [#1791]: https://github.com/launchbadge/sqlx/pull/1791 [#1799]: https://github.com/launchbadge/sqlx/pull/1799 [0.5.12-prs]: https://github.com/launchbadge/sqlx/pulls?q=is%3Apr+is%3Amerged+merged%3A2022-02-19..2022-04-13 ## 0.5.11 - 2022-02-17 [20 pull requests][0.5.11-prs] were merged this release cycle. ### Added * [[#1610]]: Allow converting `AnyConnectOptions` to a specific `ConnectOptions` [[@05storm26]] * [[#1652]]: Implement `From` for `AnyConnection` [[@genusistimelord]] * [[#1658]]: Handle `SQLITE_LOCKED` [[@madadam]] * [[#1665]]: Document offline mode usage with feature flags [[@sedrik]] * [[#1680]]: Show checksum mismatches in `sqlx migrate info` [[@ifn3]] * [[#1685]]: Add tip for setting `opt-level` for `sqlx-macros` [[@LovecraftianHorror]] * [[#1687]]: Docs: `Acquire` examples and alternative [[@stoically]] * [[#1696]]: Postgres: support for `ltree` [[@cemoktra]] * [[#1710]]: Postgres: support for `lquery` [[@cemoktra]] ### Changed * [[#1605]]: Remove unused dependencies [[@paolobarbolini]] * [[#1606]]: Add target context to Postgres `NOTICE` logs [[@dbeckwith]] * [[#1684]]: Macros: Cache parsed `sqlx-data.json` instead of reparsing [[@LovecraftianHorror]] ### Fixed * [[#1608]]: Drop worker shared state in shutdown (SQLite) [[@andrewwhitehead]] * [[#1619]]: Docs(macros): remove sentences banning usage of `as _` [[@k-jun]] * [[#1626]]: Simplify `cargo-sqlx` command-line definition [[@tranzystorek-io]] * [[#1636]]: Fix and extend Postgres transaction example [[@taladar]] * [[#1657]]: Fix typo in macro docs [[@p9s]] * [[#1661]]: Fix binding `Option` for `Any` driver [[@ArGGu]] * [[#1667]]: MySQL: Avoid panicking if packet is empty [[@nappa85]] * [[#1692]]: Postgres: Fix power calculation when encoding `BigDecimal` into `NUMERIC` [[@VersBinarii]] Additionally, we have introduced two mitigations for [the issue of the cyclic dependency on `ahash`][aHash#95]: * We re-downgraded our version requirement on `indexmap` from `1.7.0` back to `1.6.2` so users can pin it to that version [as recommended in aHash#95][ahash-fix]. * [This was regressed accidentally during a sweeping dependency upgrade before the last release][indexmap-regression], sorry about that. * Thanks to the work of [@LovecraftianHorror] in [#1684], we no longer require the `preserve_order` feature of `serde_json` which gives users another place to break the cycle by simply not enabling that feature. * This may introduce extra churn in Git diffs for `sqlx-data.json`, however. If this is an issue for you but the dependency cycle isn't, you can re-enable the `preserve_order` feature: ```toml [dependencies] serde_json = { version = "1", features = ["preserve_order"] } ``` [aHash#95]: https://github.com/tkaitchuck/aHash/issues/95 [ahash-fix]: https://github.com/tkaitchuck/aHash/issues/95#issuecomment-874150078 [indexmap-regression]: https://github.com/launchbadge/sqlx/pull/1603#issuecomment-1010827637 [#1605]: https://github.com/launchbadge/sqlx/pull/1605 [#1606]: https://github.com/launchbadge/sqlx/pull/1606 [#1608]: https://github.com/launchbadge/sqlx/pull/1608 [#1610]: https://github.com/launchbadge/sqlx/pull/1610 [#1619]: https://github.com/launchbadge/sqlx/pull/1619 [#1626]: https://github.com/launchbadge/sqlx/pull/1626 [#1636]: https://github.com/launchbadge/sqlx/pull/1636 [#1652]: https://github.com/launchbadge/sqlx/pull/1652 [#1657]: https://github.com/launchbadge/sqlx/pull/1657 [#1658]: https://github.com/launchbadge/sqlx/pull/1658 [#1661]: https://github.com/launchbadge/sqlx/pull/1661 [#1665]: https://github.com/launchbadge/sqlx/pull/1665 [#1667]: https://github.com/launchbadge/sqlx/pull/1667 [#1680]: https://github.com/launchbadge/sqlx/pull/1680 [#1684]: https://github.com/launchbadge/sqlx/pull/1684 [#1685]: https://github.com/launchbadge/sqlx/pull/1685 [#1687]: https://github.com/launchbadge/sqlx/pull/1687 [#1692]: https://github.com/launchbadge/sqlx/pull/1692 [#1696]: https://github.com/launchbadge/sqlx/pull/1696 [#1710]: https://github.com/launchbadge/sqlx/pull/1710 [0.5.11-prs]: https://github.com/launchbadge/sqlx/pulls?q=is%3Apr+is%3Amerged+merged%3A2021-12-30..2022-02-17 ## 0.5.10 - 2021-12-29 [A whopping 31 pull requests][0.5.10-prs] were merged this release cycle! According to this changelog, we saw 18 new contributors! However, some of these folks may have missed getting mentioned in previous entries since we only listed highlights. To avoid anyone feeling left out, I put in the effort this time and tried to list every single one here. ### Added * [[#1228]]: Add `Pool::any_kind()` [[@nitnelave]] * [[#1343]]: Add `Encode/Decode` impl for `Cow<'_, str>` [[@Drevoed]] * [[#1474]]: Derive `Clone`, `Copy` for `AnyKind` [[@yuyawk]] * [[#1497]]: Update FAQ to explain how to configure docs.rs to build a project using SQLx [[@russweas]] * [[#1498]]: Add description of migration file structure to `migrate!()` docs [[@zbigniewzolnierowicz]] * [[#1508]]: Add `.persistent(bool)` to `QueryAs`, `QueryScalar` [[@akiradeveloper]] * [[#1514]]: Add support for serialized threading mode to SQLite [[@LLBlumire]] * [[#1523]]: Allow `rust_decimal::Decimal` in `PgRange` [[@meh]] * [[#1539]]: Support `PGOPTIONS` and adding custom configuration options in `PgConnectOptions` [[@liushuyu]] * [[#1562]]: Re-export `either::Either` used by `Executor::fetch_many()` [[@DoumanAsh]] * [[#1584]]: Add feature to use RusTLS instead of `native-tls` for `sqlx-cli` [[@SonicZentropy]] * [[#1592]]: Add `AnyConnection::kind()` [[@05storm26]] ### Changes * [[#1385]]: Rewrite Postgres array handling to reduce boilerplate and allow custom types [[@jplatte]] * [[#1479]]: Remove outdated mention of `runtime-async-std-native-tls` as the default runtime in README.md [[@yerke]] * [[#1526]]: Revise `Pool` docs in a couple places [[@abonander]] * [[#1535]]: Bump `libsqlite-sys` to `0.23.1` [[@nitsky]] * [[#1551]]: SQLite: make worker thread responsible for all FFI calls [[@abonander]] * If you were encountering segfaults with the SQLite driver, there's a good chance this will fix it! * [[#1557]]: CI: test with Postgres 14 [[@paolobarbolini]] * [[#1571]]: Make `whoami` dep optional, only pull it in for Postgres [[@joshtriplett]] * [[#1572]]: Update `rsa` crate to 0.5 [[@paolobarbolini]] * [[#1591]]: List SeaORM as an ORM option in the README [[@kunjee17]] * [[#1601]]: Update `itoa` and `dirs` [[@paolobarbolini]] ### Fixes * [[#1475]]: Fix panic when converting a negative `chrono::Duration` to `PgInterval` [[@yuyawk]] * [[#1483]]: Fix error when decoding array of custom types from Postgres [[@demurgos] * [[#1501]]: Reduce `indexmap` version requirement to `1.6.2` [[@dimfeld]] * [[#1511]]: Fix element type given to Postgres for arrays of custom enums [[@chesedo]] * [[#1517]]: Fix mismatched type errors in MySQL type tests [[@abonander]] * [[#1537]]: Fix missing re-export of `PgCopyIn` [[@akiradeveloper]] * [[#1566]]: Match `~/.pgpass` password after URL parsing and fix user and database ordering [[@D1plo1d]] * [[#1582]]: `cargo sqlx prepare`: Append to existing `RUSTFLAGS` instead of overwriting [[@tkintscher]] * [[#1587]]: SQLite: if set, send `PRAGMA key` on a new connection before anything else. [[@parazyd]] * This should fix problems with being unable to open databases using SQLCipher. [#1228]: https://github.com/launchbadge/sqlx/pull/1228 [#1343]: https://github.com/launchbadge/sqlx/pull/1343 [#1385]: https://github.com/launchbadge/sqlx/pull/1385 [#1474]: https://github.com/launchbadge/sqlx/pull/1474 [#1475]: https://github.com/launchbadge/sqlx/pull/1475 [#1479]: https://github.com/launchbadge/sqlx/pull/1479 [#1483]: https://github.com/launchbadge/sqlx/pull/1483 [#1497]: https://github.com/launchbadge/sqlx/pull/1497 [#1498]: https://github.com/launchbadge/sqlx/pull/1498 [#1501]: https://github.com/launchbadge/sqlx/pull/1501 [#1508]: https://github.com/launchbadge/sqlx/pull/1508 [#1511]: https://github.com/launchbadge/sqlx/pull/1511 [#1514]: https://github.com/launchbadge/sqlx/pull/1514 [#1517]: https://github.com/launchbadge/sqlx/pull/1517 [#1523]: https://github.com/launchbadge/sqlx/pull/1523 [#1526]: https://github.com/launchbadge/sqlx/pull/1526 [#1535]: https://github.com/launchbadge/sqlx/pull/1535 [#1537]: https://github.com/launchbadge/sqlx/pull/1537 [#1539]: https://github.com/launchbadge/sqlx/pull/1539 [#1551]: https://github.com/launchbadge/sqlx/pull/1551 [#1557]: https://github.com/launchbadge/sqlx/pull/1557 [#1562]: https://github.com/launchbadge/sqlx/pull/1562 [#1566]: https://github.com/launchbadge/sqlx/pull/1566 [#1571]: https://github.com/launchbadge/sqlx/pull/1571 [#1572]: https://github.com/launchbadge/sqlx/pull/1572 [#1582]: https://github.com/launchbadge/sqlx/pull/1582 [#1584]: https://github.com/launchbadge/sqlx/pull/1584 [#1587]: https://github.com/launchbadge/sqlx/pull/1587 [#1591]: https://github.com/launchbadge/sqlx/pull/1591 [#1592]: https://github.com/launchbadge/sqlx/pull/1592 [#1601]: https://github.com/launchbadge/sqlx/pull/1601 [0.5.10-prs]: https://github.com/launchbadge/sqlx/pulls?page=1&q=is%3Apr+merged%3A2021-10-02..2021-12-31+sort%3Acreated-asc ## 0.5.9 - 2021-10-01 A hotfix release to address the issue of the `sqlx` crate itself still depending on older versions of `sqlx-core` and `sqlx-macros`. No other changes from `0.5.8`. ## 0.5.8 - 2021-10-01 (Yanked; use 0.5.9) [A total of 24 pull requests][0.5.8-prs] were merged this release cycle! Some highlights: * [[#1289]] Support the `immutable` option on SQLite connections [[@djmarcin]] * [[#1295]] Support custom initial options for SQLite [[@ghassmo]] * Allows specifying custom `PRAGMA`s and overriding those set by SQLx. * [[#1345]] Initial support for Postgres `COPY FROM/TO`[[@montanalow], [@abonander]] * [[#1439]] Handle multiple waiting results correctly in MySQL [[@eagletmt]] [#1289]: https://github.com/launchbadge/sqlx/pull/1289 [#1295]: https://github.com/launchbadge/sqlx/pull/1295 [#1345]: https://github.com/launchbadge/sqlx/pull/1345 [#1439]: https://github.com/launchbadge/sqlx/pull/1439 [0.5.8-prs]: https://github.com/launchbadge/sqlx/pulls?q=is%3Apr+is%3Amerged+merged%3A2021-08-21..2021-10-01 ## 0.5.7 - 2021-08-20 * [[#1392]] use `resolve_path` when getting path for `include_str!()` [[@abonander]] * Fixes a regression introduced by [[#1332]]. * [[#1393]] avoid recursively spawning tasks in `PgListener::drop()` [[@abonander]] * Fixes a panic that occurs when `PgListener` is dropped in `async fn main()`. [#1392]: https://github.com/launchbadge/sqlx/pull/1392 [#1393]: https://github.com/launchbadge/sqlx/pull/1393 ## 0.5.6 - 2021-08-16 A large bugfix release, including but not limited to: * [[#1329]] Implement `MACADDR` type for Postgres [[@nomick]] * [[#1363]] Fix `PortalSuspended` for array of composite types in Postgres [[@AtkinsChang]] * [[#1320]] Reimplement `sqlx::Pool` internals using `futures-intrusive` [[@abonander]] * This addresses a number of deadlocks/stalls on acquiring connections from the pool. * [[#1332]] Macros: tell the compiler about external files/env vars to watch [[@abonander]] * Includes `sqlx build-script` to create a `build.rs` to watch `migrations/` for changes. * Nightly users can try `RUSTFLAGS=--cfg sqlx_macros_unstable` to tell the compiler to watch `migrations/` for changes instead of using a build script. * See the new section in the docs for `sqlx::migrate!()` for details. * [[#1351]] Fix a few sources of segfaults/errors in SQLite driver [[@abonander]] * Includes contributions from [[@link2ext]] and [[@madadam]]. * [[#1323]] Keep track of column typing in SQLite EXPLAIN parsing [[@marshoepial]] * This fixes errors in the macros when using `INSERT/UPDATE/DELETE ... RETURNING ...` in SQLite. [A total of 25 pull requests][0.5.6-prs] were merged this release cycle! [#1329]: https://github.com/launchbadge/sqlx/pull/1329 [#1363]: https://github.com/launchbadge/sqlx/pull/1363 [#1320]: https://github.com/launchbadge/sqlx/pull/1320 [#1332]: https://github.com/launchbadge/sqlx/pull/1332 [#1351]: https://github.com/launchbadge/sqlx/pull/1351 [#1323]: https://github.com/launchbadge/sqlx/pull/1323 [0.5.6-prs]: https://github.com/launchbadge/sqlx/pulls?q=is%3Apr+is%3Amerged+merged%3A2021-05-24..2021-08-17 ## 0.5.5 - 2021-05-24 - [[#1242]] Fix infinite loop at compile time when using query macros [[@toshokan]] [#1242]: https://github.com/launchbadge/sqlx/pull/1242 ## 0.5.4 - 2021-05-22 - [[#1235]] Fix compilation with rustls from an eager update to webpki [[@ETCaton]] [#1235]: https://github.com/launchbadge/sqlx/pull/1235 ## 0.5.3 - 2021-05-21 - [[#1211]] Even more tweaks and fixes to the Pool internals [[@abonander]] - [[#1213]] Add support for bytes and `chrono::NaiveDateTime` to `Any` [[@guylapid]] - [[#1224]] Add support for `chrono::DateTime` to `Any` with `MySQL` [[@NatPRoach]] - [[#1216]] Skip empty lines and comments in pgpass files [[@feikesteenbergen]] - [[#1218]] Add support for `PgMoney` to the compile-time type-checking [[@iamsiddhant05]] [#1211]: https://github.com/launchbadge/sqlx/pull/1211 [#1213]: https://github.com/launchbadge/sqlx/pull/1213 [#1216]: https://github.com/launchbadge/sqlx/pull/1216 [#1218]: https://github.com/launchbadge/sqlx/pull/1218 [#1224]: https://github.com/launchbadge/sqlx/pull/1224 ## 0.5.2 - 2021-04-15 - [[#1149]] Tweak and optimize Pool internals [[@abonander]] - [[#1132]] Remove `'static` bound on `Connection::transaction` [[@argv-minus-one]] - [[#1128]] Fix `-y` flag for `sqlx db reset -y` [[@qqwa]] - [[#1099]] [[#1097]] Truncate buffer when `BufStream` is dropped [[@Diggsey]] [#1132]: https://github.com/launchbadge/sqlx/pull/1132 [#1149]: https://github.com/launchbadge/sqlx/pull/1149 [#1128]: https://github.com/launchbadge/sqlx/pull/1128 [#1099]: https://github.com/launchbadge/sqlx/pull/1099 [#1097]: https://github.com/launchbadge/sqlx/issues/1097 ### PostgreSQL - [[#1170]] Remove `Self: Type` bounds in `Encode` / `Decode` implementations for arrays [[@jplatte]] Enables working around the lack of support for user-defined array types: ```rust #[derive(sqlx::Encode)] struct Foos<'a>(&'a [Foo]); impl sqlx::Type for Foos<'_> { fn type_info() -> PgTypeInfo { PgTypeInfo::with_name("_foo") } } query_as!( Whatever, "", Foos(&foo_vec) as _, ) ``` - [[#1141]] Use `u16::MAX` instead of `i16::MAX` for a check against the largest number of parameters in a query [[@crajcan]] - [[#1112]] Add support for `DOMAIN` types [[@demurgos]] - [[#1100]] Explicitly `UNLISTEN` before returning connections to the pool in `PgListener` [[@Diggsey]] [#1170]: https://github.com/launchbadge/sqlx/pull/1170 [#1141]: https://github.com/launchbadge/sqlx/pull/1141 [#1112]: https://github.com/launchbadge/sqlx/pull/1112 [#1100]: https://github.com/launchbadge/sqlx/pull/1100 ### SQLite - [[#1161]] Catch `SQLITE_MISUSE` on connection close and panic [[@link2xt]] - [[#1160]] Do not cast pointers to `i32` (cast to `usize`) [[@link2xt]] - [[#1156]] Reset the statement when `fetch_many` stream is dropped [[@link2xt]] [#1161]: https://github.com/launchbadge/sqlx/pull/1161 [#1160]: https://github.com/launchbadge/sqlx/pull/1160 [#1156]: https://github.com/launchbadge/sqlx/pull/1156 ## 0.5.1 - 2021-02-04 - Update sqlx-rt to 0.3. ## 0.5.0 - 2021-02-04 ### Changes - [[#983]] [[#1022]] Upgrade async runtime dependencies [[@seryl], [@ant32], [@jplatte], [@robjtede]] - tokio 1.0 - actix-rt 2.0 - [[#854]] Allow chaining `map` and `try_map` [[@jplatte]] Additionally enables calling these combinators with the macros: ```rust let ones: Vec = query!("SELECT 1 as foo") .map(|row| row.foo) .fetch_all(&mut conn).await?; ``` - [[#940]] Rename the `#[sqlx(rename)]` attribute used to specify the type name on the database side to `#[sqlx(type_name)]` [[@jplatte]]. - [[#976]] Rename the `DbDone` types to `DbQueryResult`. [[@jplatte]] - [[#976]] Remove the `Done` trait. The `.rows_affected()` method is now available as an inherent method on `PgQueryResult`, `MySqlQueryResult` and so on. [[@jplatte]] - [[#1007]] Remove `any::AnyType` (and replace with directly implementing `Type`) [[@jplatte]] ### Added - [[#998]] [[#821]] Add `.constraint()` to `DatabaseError` [[@fl9]] - [[#919]] For SQLite, add support for unsigned integers [[@dignifiedquire]] ### Fixes - [[#1002]] For SQLite, `GROUP BY` in `query!` caused an infinite loop at compile time. [[@pymongo]] - [[#979]] For MySQL, fix support for non-default authentication. [[@sile]] - [[#918]] Recover from dropping `wait_for_conn` inside Pool. [[@antialize]] [#821]: https://github.com/launchbadge/sqlx/issues/821 [#918]: https://github.com/launchbadge/sqlx/pull/918 [#919]: https://github.com/launchbadge/sqlx/pull/919 [#983]: https://github.com/launchbadge/sqlx/pull/983 [#940]: https://github.com/launchbadge/sqlx/pull/940 [#976]: https://github.com/launchbadge/sqlx/pull/976 [#979]: https://github.com/launchbadge/sqlx/pull/979 [#998]: https://github.com/launchbadge/sqlx/pull/998 [#983]: https://github.com/launchbadge/sqlx/pull/983 [#1002]: https://github.com/launchbadge/sqlx/pull/1002 [#1007]: https://github.com/launchbadge/sqlx/pull/1007 [#1022]: https://github.com/launchbadge/sqlx/pull/1022 ## 0.4.2 - 2020-12-19 - [[#908]] Fix `whoami` crash on FreeBSD platform [[@fundon]] [[@AldaronLau]] - [[#895]] Decrement pool size when connection is released [[@andrewwhitehead]] - [[#878]] Fix `conn.transaction` wrapper [[@hamza1311]] ```rust conn.transaction(|transaction: &mut Transaction | { // ... }); ``` - [[#874]] Recognize `1` as `true` for `SQLX_OFFLINE [[@Pleto]] - [[#747]] [[#867]] Replace `lru-cache` with `hashlink` [[@chertov]] - [[#860]] Add `rename_all` to `FromRow` and add `camelCase` and `PascalCase` [[@framp]] - [[#839]] Add (optional) support for `bstr::BStr`, `bstr::BString`, and `git2::Oid` [[@joshtriplett]] #### SQLite - [[#893]] Fix memory leak if `create_collation` fails [[@slumber]] - [[#852]] Fix potential 100% CPU usage in `fetch_one` / `fetch_optional` [[@markazmierczak]] - [[#850]] Add `synchronous` option to `SqliteConnectOptions` [[@markazmierczak]] #### PostgreSQL - [[#889]] Fix decimals (one more time) [[@slumber]] - [[#876]] Add support for `BYTEA[]` to compile-time type-checking [[@augustocdias]] - [[#845]] Fix path for `&[NaiveTime]` in `query!` macros [[@msrd0]] #### MySQL - [[#880]] Consider `utf8mb4_general_ci` as a string [[@mcronce]] [#908]: https://github.com/launchbadge/sqlx/pull/908 [#895]: https://github.com/launchbadge/sqlx/pull/895 [#893]: https://github.com/launchbadge/sqlx/pull/893 [#889]: https://github.com/launchbadge/sqlx/pull/889 [#880]: https://github.com/launchbadge/sqlx/pull/880 [#878]: https://github.com/launchbadge/sqlx/pull/878 [#876]: https://github.com/launchbadge/sqlx/pull/876 [#874]: https://github.com/launchbadge/sqlx/pull/874 [#867]: https://github.com/launchbadge/sqlx/pull/867 [#860]: https://github.com/launchbadge/sqlx/pull/860 [#854]: https://github.com/launchbadge/sqlx/pull/854 [#852]: https://github.com/launchbadge/sqlx/pull/852 [#850]: https://github.com/launchbadge/sqlx/pull/850 [#845]: https://github.com/launchbadge/sqlx/pull/845 [#839]: https://github.com/launchbadge/sqlx/pull/839 [#747]: https://github.com/launchbadge/sqlx/issues/747 ## 0.4.1 – 2020-11-13 Fix docs.rs build by enabling a runtime feature in the docs.rs metadata in `Cargo.toml`. ## 0.4.0 - 2020-11-12 - [[#774]] Fix usage of SQLx derives with other derive crates [[@NyxCode]] - [[#762]] Fix `migrate!()` (with no params) [[@esemeniuc]] - [[#755]] Add `kebab-case` to `rename_all` [[@iamsiddhant05]] - [[#735]] Support `rustls` [[@jplatte]] Adds `-native-tls` or `-rustls` on each runtime feature: ```toml # previous features = [ "runtime-async-std" ] # now features = [ "runtime-async-std-native-tls" ] ``` - [[#718]] Support tuple structs with `#[derive(FromRow)]` [[@dvermd]] #### SQLite - [[#789]] Support `$NNN` parameters [[@nitsky]] - [[#784]] Use `futures_channel::oneshot` in worker for big perf win [[@markazmierczak]] #### PostgreSQL - [[#781]] Fix decimal conversions handling of `0.01` [[@pimeys]] - [[#745]] Always prefer parsing of the non-localized notice severity field [[@dstoeckel]] - [[#742]] Enable `Vec>` with chrono [[@mrcd]] #### MySQL - [[#743]] Consider `utf8mb4_bin` as a string [[@digorithm]] - [[#739]] Fix minor protocol detail with `iteration-count` that was blocking Vitess [[@mcronce]] [#774]: https://github.com/launchbadge/sqlx/pull/774 [#789]: https://github.com/launchbadge/sqlx/pull/789 [#784]: https://github.com/launchbadge/sqlx/pull/784 [#781]: https://github.com/launchbadge/sqlx/pull/781 [#762]: https://github.com/launchbadge/sqlx/pull/762 [#755]: https://github.com/launchbadge/sqlx/pull/755 [#745]: https://github.com/launchbadge/sqlx/pull/745 [#743]: https://github.com/launchbadge/sqlx/pull/743 [#742]: https://github.com/launchbadge/sqlx/pull/742 [#735]: https://github.com/launchbadge/sqlx/pull/735 [#739]: https://github.com/launchbadge/sqlx/pull/739 [#718]: https://github.com/launchbadge/sqlx/pull/718 ## 0.4.0-beta.1 - 2020-07-27 ### Highlights - Enable compile-time type checking from cached metadata to enable building in an environment without access to a development database (e.g., Docker, CI). - Initial support for **Microsoft SQL Server**. If there is something missing that you need, open an issue. We are happy to help. - SQL migrations, both with a CLI tool and programmatically loading migrations at runtime. - Runtime-determined database driver, `Any`, to support compile-once and run with a database driver selected at runtime. - Support for user-defined types and more generally overriding the inferred Rust type from SQL with compile-time SQL verification. ### Fixed #### MySQL - [[#418]] Support zero dates and times [[@blackwolf12333]] ### Added - [[#174]] Inroduce a builder to construct connections to bypass the URL parsing ```rust // MSSQL let conn = MssqlConnectOptions::new() .host("localhost") .database("master") .username("sa") .password("Password") .connect().await?; // SQLite let conn = SqliteConnectOptions::from_str("sqlite://a.db")? .foreign_keys(false) .connect().await?; ``` - [[#127]] Get the last ID or Row ID inserted for MySQL or SQLite ```rust // MySQL let id: u64 = query!("INSERT INTO table ( col ) VALUES ( ? )", val) .execute(&mut conn).await? .last_insert_id(); // LAST_INSERT_ID() // SQLite let id: i64 = query!("INSERT INTO table ( col ) VALUES ( ?1 )", val) .execute(&mut conn).await? .last_insert_rowid(); // sqlite3_last_insert_rowid() ``` - [[#263]] Add hooks to the Pool: `after_connect`, `before_release`, and `after_acquire` ```rust // PostgreSQL let pool = PgPoolOptions::new() .after_connect(|conn| Box::pin(async move { conn.execute("SET application_name = 'your_app';").await?; conn.execute("SET search_path = 'my_schema';").await?; Ok(()) })) .connect("postgres:// …").await? ``` - [[#308]] [[#495]] Extend `derive(FromRow)` with support for `#[sqlx(default)]` on fields to allow reading in a partial query [[@OriolMunoz]] - [[#454]] [[#456]] Support `rust_decimal::Decimal` as an alternative to `bigdecimal::BigDecimal` for `NUMERIC` columns in MySQL and PostgreSQL [[@pimeys]] - [[#181]] Column names and type information is now accessible from `Row` via `Row::columns()` or `Row::column(name)` #### PostgreSQL - [[#197]] [[#271]] Add initial support for `INTERVAL` (full support pending a `time::Period` type) [[@dimtion]] #### MySQL - [[#449]] [[#450]] Support Unix Domain Sockets (UDS) for MySQL [[@pimeys]] #### SQLite - Types are now inferred for expressions. This means its now possible to use `query!` and `query_as!` for: ```rust let row = query!("SELECT 10 as _1, x + 5 as _2 FROM table").fetch_one(&mut conn).await?; assert_eq!(row._1, 10); assert_eq!(row._2, 5); // 5 + x? ``` - [[#167]] Support `foreign_keys` explicitly with a `foreign_keys(true)` method available on `SqliteConnectOptions` which is a builder for new SQLite connections (and can be passed into `PoolOptions` to build a pool). ```rust let conn = SqliteConnectOptions::new() .foreign_keys(true) // on by default .connect().await?; ``` - [[#430]] [[#438]] Add method to get the raw SQLite connection handle [[@agentsim]] ```rust // conn is `SqliteConnection` // this is not unsafe, but what you do with the handle will be let ptr: *mut libsqlite3::sqlite3 = conn.as_raw_handle(); ``` - [[#164]] Support `TIMESTAMP`, `DATETIME`, `DATE`, and `TIME` via `chrono` in SQLite [[@felipesere]] [[@meteficha]] ### Changed - `Transaction` now mutably borrows a connection instead of owning it. This enables a new (or nested) transaction to be started from `&mut conn`. - [[#145]] [[#444]] Use a least-recently-used (LRU) cache to limit the growth of the prepared statement cache for SQLite, MySQL, and PostgreSQL [[@pimeys]] #### SQLite - [[#499]] `INTEGER` now resolves to `i64` instead of `i32`, `INT4` will still resolve to `i32` ### Removed [#127]: https://github.com/launchbadge/sqlx/issues/127 [#174]: https://github.com/launchbadge/sqlx/issues/174 [#145]: https://github.com/launchbadge/sqlx/issues/145 [#164]: https://github.com/launchbadge/sqlx/issues/164 [#167]: https://github.com/launchbadge/sqlx/issues/167 [#181]: https://github.com/launchbadge/sqlx/issues/181 [#197]: https://github.com/launchbadge/sqlx/issues/197 [#263]: https://github.com/launchbadge/sqlx/issues/263 [#308]: https://github.com/launchbadge/sqlx/issues/308 [#418]: https://github.com/launchbadge/sqlx/issues/418 [#430]: https://github.com/launchbadge/sqlx/issues/430 [#449]: https://github.com/launchbadge/sqlx/issues/449 [#499]: https://github.com/launchbadge/sqlx/issues/499 [#454]: https://github.com/launchbadge/sqlx/issues/454 [#271]: https://github.com/launchbadge/sqlx/pull/271 [#444]: https://github.com/launchbadge/sqlx/pull/444 [#438]: https://github.com/launchbadge/sqlx/pull/438 [#495]: https://github.com/launchbadge/sqlx/pull/495 [#495]: https://github.com/launchbadge/sqlx/pull/495 ## 0.3.5 - 2020-05-06 ### Fixed - [[#259]] Handle percent-encoded paths for SQLite [[@g-s-k]] - [[#281]] Deallocate SQLite statements before closing the SQLite connection [[@hasali19]] - [[#284]] Fix handling of `0` for `BigDecimal` in PostgreSQL and MySQL [[@abonander]] ### Added - [[#256]] Add `query_unchecked!` and `query_file_unchecked!` with similar semantics to `query_as_unchecked!` [[@meh]] - [[#252]] [[#297]] Derive several traits for the `Json` wrapper type [[@meh]] - [[#261]] Add support for `#[sqlx(rename_all = "snake_case")]` to `#[derive(Type)]` [[@shssoichiro]] - [[#253]] Add support for UNIX domain sockets to PostgreSQL [[@Nilix007]] - [[#251]] Add support for textual JSON on MySQL [[@blackwolf12333]] - [[#275]] [[#268]] Optionally log formatted SQL queries on execution [[@shssoichiro]] - [[#267]] Support Cargo.toml relative `.env` files; allows for each crate in a workspace to use their own `.env` file and thus their own `DATABASE_URL` [[@xyzd]] [#252]: https://github.com/launchbadge/sqlx/pull/252 [#261]: https://github.com/launchbadge/sqlx/pull/261 [#256]: https://github.com/launchbadge/sqlx/pull/256 [#259]: https://github.com/launchbadge/sqlx/pull/259 [#253]: https://github.com/launchbadge/sqlx/pull/253 [#297]: https://github.com/launchbadge/sqlx/pull/297 [#251]: https://github.com/launchbadge/sqlx/pull/251 [#275]: https://github.com/launchbadge/sqlx/pull/275 [#267]: https://github.com/launchbadge/sqlx/pull/267 [#268]: https://github.com/launchbadge/sqlx/pull/268 [#281]: https://github.com/launchbadge/sqlx/pull/281 [#284]: https://github.com/launchbadge/sqlx/pull/284 ## 0.3.4 - 2020-04-10 ### Fixed - [[#241]] Type name for custom enum is not always attached to TypeInfo in PostgreSQL - [[#237]] [[#238]] User-defined type name matching is now case-insensitive in PostgreSQL [[@qtbeee]] - [[#231]] Handle empty queries (and those with comments) in SQLite - [[#228]] Provide `MapRow` implementations for functions (enables `.map(|row| ...)` over `.try_map(|row| ...)`) ### Added - [[#234]] Add support for `NUMERIC` in MySQL with the `bigdecimal` crate [[@xiaopengli89]] - [[#227]] Support `#[sqlx(rename = "new_name")]` on struct fields within a `FromRow` derive [[@sidred]] [#228]: https://github.com/launchbadge/sqlx/issues/228 [#231]: https://github.com/launchbadge/sqlx/issues/231 [#237]: https://github.com/launchbadge/sqlx/issues/237 [#241]: https://github.com/launchbadge/sqlx/issues/241 [#227]: https://github.com/launchbadge/sqlx/pull/227 [#234]: https://github.com/launchbadge/sqlx/pull/234 [#238]: https://github.com/launchbadge/sqlx/pull/238 ## 0.3.3 - 2020-04-01 ### Fixed - [[#214]] Handle percent-encoded usernames in a database URL [[@jamwaffles]] ### Changed - [[#216]] Mark `Cursor`, `Query`, `QueryAs`, `query::Map`, and `Transaction` as `#[must_use]` [[@Ace4896]] - [[#213]] Remove matches dependency and use matches macro from std [[@nrjais]] [#216]: https://github.com/launchbadge/sqlx/pull/216 [#214]: https://github.com/launchbadge/sqlx/pull/214 [#213]: https://github.com/launchbadge/sqlx/pull/213 ## 0.3.2 - 2020-03-31 ### Fixed - [[#212]] Removed sneaky `println!` in `MySqlCursor` [#212]: https://github.com/launchbadge/sqlx/issues/212 ## 0.3.1 - 2020-03-30 ### Fixed - [[#203]] Allow an empty password for MySQL - [[#204]] Regression in error reporting for invalid SQL statements on PostgreSQL - [[#200]] Fixes the incorrect handling of raw (`r#...`) fields of a struct in the `FromRow` derive [[@sidred]] [#200]: https://github.com/launchbadge/sqlx/pull/200 [#203]: https://github.com/launchbadge/sqlx/issues/203 [#204]: https://github.com/launchbadge/sqlx/issues/204 ## 0.3.0 - 2020-03-29 ### Breaking Changes - `sqlx::Row` now has a lifetime (`'c`) tied to the database connection. In effect, this means that you cannot store `Row`s or collect them into a collection. `Query` (returned from `sqlx::query()`) has `map()` which takes a function to map from the `Row` to another type to make this transition easier. In 0.2.x ```rust let rows = sqlx::query("SELECT 1") .fetch_all(&mut conn).await?; ``` In 0.3.x ```rust let values: Vec = sqlx::query("SELECT 1") .map(|row: PgRow| row.get(0)) .fetch_all(&mut conn).await?; ``` To assist with the above, `sqlx::query_as()` now supports querying directly into tuples (up to 9 elements) or struct types with a `#[derive(FromRow)]`. ```rust // This extension trait is needed until a rust bug is fixed use sqlx::postgres::PgQueryAs; let values: Vec<(i32, bool)> = sqlx::query_as("SELECT 1, false") .fetch_all(&mut conn).await?; ``` - `HasSqlType: Database` is now `T: Type` to mirror `Encode` and `Decode` - `Query::fetch` (returned from `query()`) now returns a new `Cursor` type. `Cursor` is a Stream-like type where the item type borrows into the stream (which itself borrows from connection). This means that using `query().fetch()` you can now stream directly from the database with **zero-copy** and **zero-allocation**. - Remove `PgTypeInfo::with_oid` and replace with `PgTypeInfo::with_name` ### Added - Results from the database are now zero-copy and no allocation beyond a shared read buffer for the TCP stream ( in other words, almost no per-query allocation ). Bind arguments still do allocate a buffer per query. - [[#129]] Add support for [SQLite](https://sqlite.org/index.html). Generated code should be very close to normal use of the C API. - Adds `Sqlite`, `SqliteConnection`, `SqlitePool`, and other supporting types - [[#97]] [[#134]] Add support for user-defined types. [[@Freax13]] - Rust-only domain types or transparent wrappers around SQL types. These may be used _transparently_ inplace of the SQL type. ```rust #[derive(sqlx::Type)] #[repr(transparent)] struct Meters(i32); ``` - Enumerations may be defined in Rust and can match SQL by integer discriminant or variant name. ```rust #[derive(sqlx::Type)] #[repr(i32)] // Expects a INT in SQL enum Color { Red = 1, Green = 2, Blue = 3 } ``` ```rust #[derive(sqlx::Type)] #[sqlx(rename = "TEXT")] // May also be the name of a user defined enum type #[sqlx(rename_all = "lowercase")] // similar to serde rename_all enum Color { Red, Green, Blue } // expects 'red', 'green', or 'blue' ``` - **Postgres** further supports user-defined composite types. ```rust #[derive(sqlx::Type)] #[sqlx(rename = "interface_type")] struct InterfaceType { name: String, supplier_id: i32, price: f64 } ``` - [[#98]] [[#131]] Add support for asynchronous notifications in Postgres (`LISTEN` / `NOTIFY`). [[@thedodd]] - Supports automatic reconnection on connection failure. - `PgListener` implements `Executor` and may be used to execute queries. Be careful however as if the intent is to handle and process messages rapidly you don't want to be tying up the connection for too long. Messages received during queries are buffered and will be delivered on the next call to `recv()`. ```rust let mut listener = PgListener::new(DATABASE_URL).await?; listener.listen("topic").await?; loop { let message = listener.recv().await?; println!("payload = {}", message.payload); } ``` - Add _unchecked_ variants of the query macros. These will still verify the SQL for syntactic and semantic correctness with the current database but they will not check the input or output types. This is intended as a temporary solution until `query_as!` is able to support user defined types. - `query_as_unchecked!` - `query_file_as_unchecked!` - Add support for many more types in Postgres - `JSON`, `JSONB` [[@oeb25]] - `INET`, `CIDR` [[@PoiScript]] - Arrays [[@oeb25]] - Composites ( Rust tuples or structs with a `#[derive(Type)]` ) - `NUMERIC` [[@abonander]] - `OID` (`u32`) - `"CHAR"` (`i8`) - `TIMESTAMP`, `TIMESTAMPTZ`, etc. with the `time` crate [[@utter-step]] - Enumerations ( Rust enums with a `#[derive(Type)]` ) [[@Freax13]] ### Changed - `Query` (and `QueryAs`; returned from `query()`, `query_as()`, `query!()`, and `query_as!()`) now will accept both `&mut Connection` or `&Pool` where as in 0.2.x they required `&mut &Pool`. - `Executor` now takes any value that implements `Execute` as a query. `Execute` is implemented for `Query` and `QueryAs` to mean exactly what they've meant so far, a prepared SQL query. However, `Execute` is also implemented for just `&str` which now performs a raw or unprepared SQL query. You can further use this to fetch `Row`s from the database though it is not as efficient as the prepared API (notably Postgres and MySQL send data back in TEXT mode as opposed to in BINARY mode). ```rust use sqlx::Executor; // Set the time zone parameter conn.execute("SET TIME ZONE LOCAL;").await // Demonstrate two queries at once with the raw API let mut cursor = conn.fetch("SELECT 1; SELECT 2"); let row = cursor.next().await?.unwrap(); let value: i32 = row.get(0); // 1 let row = cursor.next().await?.unwrap(); let value: i32 = row.get(0); // 2 ``` ### Removed - `Query` (returned from `query()`) no longer has `fetch_one`, `fetch_optional`, or `fetch_all`. You _must_ map the row using `map()` and then you will have a `query::Map` value that has the former methods available. ```rust let values: Vec = sqlx::query("SELECT 1") .map(|row: PgRow| row.get(0)) .fetch_all(&mut conn).await?; ``` ### Fixed - [[#62]] [[#130]] [[#135]] Remove explicit set of `IntervalStyle`. Allow usage of SQLx for CockroachDB and potentially PgBouncer. [[@bmisiak]] - [[#108]] Allow nullable and borrowed values to be used as arguments in `query!` and `query_as!`. For example, where the column would resolve to `String` in Rust (TEXT, VARCHAR, etc.), you may now use `Option`, `Option<&str>`, or `&str` instead. [[@abonander]] - [[#108]] Make unknown type errors far more informative. As an example, trying to `SELECT` a `DATE` column will now try and tell you about the `chrono` feature. [[@abonander]] ``` optional feature `chrono` required for type DATE of column #1 ("now") ``` [#62]: https://github.com/launchbadge/sqlx/issues/62 [#130]: https://github.com/launchbadge/sqlx/issues/130 [#98]: https://github.com/launchbadge/sqlx/pull/98 [#97]: https://github.com/launchbadge/sqlx/pull/97 [#134]: https://github.com/launchbadge/sqlx/pull/134 [#129]: https://github.com/launchbadge/sqlx/pull/129 [#131]: https://github.com/launchbadge/sqlx/pull/131 [#135]: https://github.com/launchbadge/sqlx/pull/135 [#108]: https://github.com/launchbadge/sqlx/pull/108 ## 0.2.6 - 2020-03-10 ### Added - [[#114]] Export `sqlx_core::Transaction` [[@thedodd]] ### Fixed - [[#125]] [[#126]] Fix statement execution in MySQL if it contains NULL statement values [[@repnop]] - [[#105]] [[#109]] Allow trailing commas in query macros [[@timmythetiny]] [#105]: https://github.com/launchbadge/sqlx/pull/105 [#109]: https://github.com/launchbadge/sqlx/pull/109 [#114]: https://github.com/launchbadge/sqlx/pull/114 [#125]: https://github.com/launchbadge/sqlx/pull/125 [#126]: https://github.com/launchbadge/sqlx/pull/126 [@timmythetiny]: https://github.com/timmythetiny [@thedodd]: https://github.com/thedodd ## 0.2.5 - 2020-02-01 ### Fixed - Fix decoding of Rows containing NULLs in Postgres [#104] - After a large review and some battle testing by [@ianthetechie](https://github.com/ianthetechie) of the `Pool`, a live leaking issue was found. This has now been fixed by [@abonander] in [#84] which included refactoring to make the pool internals less brittle (using RAII instead of manual work is one example) and to help any future contributors when changing the pool internals. - Passwords are now being percent-decoded before being presented to the server [[@repnop]] - [@100] Fix `FLOAT` and `DOUBLE` decoding in MySQL [#84]: https://github.com/launchbadge/sqlx/issues/84 [#100]: https://github.com/launchbadge/sqlx/issues/100 [#104]: https://github.com/launchbadge/sqlx/issues/104 ### Added - [[#72]] Add `PgTypeInfo::with_oid` to allow simple construction of `PgTypeInfo` which enables `HasSqlType` to be implemented by downstream consumers of SQLx [[@jplatte]] - [[#96]] Add support for returning columns from `query!` with a name of a rust keyword by using raw identifiers [[@yaahc]] - [[#71]] Implement derives for `Encode` and `Decode`. This is the first step to supporting custom types in SQLx. [[@Freax13]] [#72]: https://github.com/launchbadge/sqlx/issues/72 [#96]: https://github.com/launchbadge/sqlx/issues/96 [#71]: https://github.com/launchbadge/sqlx/issues/71 ## 0.2.4 - 2020-01-18 ### Fixed - Fix decoding of Rows containing NULLs in MySQL (and add an integration test so this doesn't break again) ## 0.2.3 - 2020-01-18 ### Fixed - Fix `query!` when used on a query that does not return results ## 0.2.2 - 2020-01-16 ### Added - [[#57]] Add support for unsigned integers and binary types in `query!` for MySQL [[@mehcode]] [#57]: https://github.com/launchbadge/sqlx/issues/57 ### Fixed - Fix stall when requesting TLS from a Postgres server that explicitly does not support TLS (such as postgres running inside docker) [[@abonander]] - [[#66]] Declare used features for `tokio` in `sqlx-macros` explicitly [#66]: https://github.com/launchbadge/sqlx/issues/66 ## 0.2.1 - 2020-01-16 ### Fixed - [[#64], [#65]] Fix decoding of Rows containing NULLs in MySQL [[@danielakhterov]] [#64]: https://github.com/launchbadge/sqlx/pull/64 [#65]: https://github.com/launchbadge/sqlx/pull/65 - [[#55]] Use a shared tokio runtime for the `query!` macro compile-time execution (under the `runtime-tokio` feature) [[@udoprog]] [#55]: https://github.com/launchbadge/sqlx/pull/55 ## 0.2.0 - 2020-01-15 ### Fixed - https://github.com/launchbadge/sqlx/issues/47 ### Added - Support Tokio through an optional `runtime-tokio` feature. - Support SQL transactions. You may now use the `begin()` function on `Pool` or `Connection` to start a new SQL transaction. This returns `sqlx::Transaction` which will `ROLLBACK` on `Drop` or can be explicitly `COMMIT` using `commit()`. - Support TLS connections. ## 0.1.4 - 2020-01-11 ### Fixed - https://github.com/launchbadge/sqlx/issues/43 - https://github.com/launchbadge/sqlx/issues/40 ### Added - Support for `SCRAM-SHA-256` authentication in Postgres [#37](https://github.com/launchbadge/sqlx/pull/37) [@danielakhterov](https://github.com/danielakhterov) - Implement `Debug` for Pool [#42](https://github.com/launchbadge/sqlx/pull/42) [@prettynatty](https://github.com/prettynatty) ## 0.1.3 - 2020-01-06 ### Fixed - https://github.com/launchbadge/sqlx/issues/30 ## 0.1.2 - 2020-01-03 ### Added - Support for Authentication in MySQL 5+ including the newer authentication schemes now default in MySQL 8: `mysql_native_password`, `sha256_password`, and `caching_sha2_password`. - [`Chrono`](https://github.com/chronotope/chrono) support for MySQL was only partially implemented (was missing `NaiveTime` and `DateTime`). - `Vec` (and `[u8]`) support for MySQL (`BLOB`) and Postgres (`BYTEA`). [@abonander]: https://github.com/abonander [@danielakhterov]: https://github.com/danielakhterov [@mehcode]: https://github.com/mehcode [@udoprog]: https://github.com/udoprog [@jplatte]: https://github.com/jplatte [@yaahc]: https://github.com/yaahc [@freax13]: https://github.com/Freax13 [@repnop]: https://github.com/repnop [@bmisiak]: https://github.com/bmisiak [@oeb25]: https://github.com/oeb25 [@poiscript]: https://github.com/PoiScript [@utter-step]: https://github.com/utter-step [@sidred]: https://github.com/sidred [@ace4896]: https://github.com/Ace4896 [@jamwaffles]: https://github.com/jamwaffles [@nrjais]: https://github.com/nrjais [@qtbeee]: https://github.com/qtbeee [@xiaopengli89]: https://github.com/xiaopengli89 [@meh]: https://github.com/meh [@shssoichiro]: https://github.com/shssoichiro [@nilix007]: https://github.com/Nilix007 [@g-s-k]: https://github.com/g-s-k [@blackwolf12333]: https://github.com/blackwolf12333 [@xyzd]: https://github.com/xyzd [@hasali19]: https://github.com/hasali19 [@oriolmunoz]: https://github.com/OriolMunoz [@pimeys]: https://github.com/pimeys [@agentsim]: https://github.com/agentsim [@meteficha]: https://github.com/meteficha [@felipesere]: https://github.com/felipesere [@dimtion]: https://github.com/dimtion [@fundon]: https://github.com/fundon [@aldaronlau]: https://github.com/AldaronLau [@andrewwhitehead]: https://github.com/andrewwhitehead [@slumber]: https://github.com/slumber [@mcronce]: https://github.com/mcronce [@hamza1311]: https://github.com/hamza1311 [@augustocdias]: https://github.com/augustocdias [@pleto]: https://github.com/Pleto [@chertov]: https://github.com/chertov [@framp]: https://github.com/framp [@markazmierczak]: https://github.com/markazmierczak [@msrd0]: https://github.com/msrd0 [@joshtriplett]: https://github.com/joshtriplett [@nyxcode]: https://github.com/NyxCode [@nitsky]: https://github.com/nitsky [@esemeniuc]: https://github.com/esemeniuc [@iamsiddhant05]: https://github.com/iamsiddhant05 [@dstoeckel]: https://github.com/dstoeckel [@mrcd]: https://github.com/mrcd [@dvermd]: https://github.com/dvermd [@seryl]: https://github.com/seryl [@ant32]: https://github.com/ant32 [@robjtede]: https://github.com/robjtede [@pymongo]: https://github.com/pymongo [@sile]: https://github.com/sile [@fl9]: https://github.com/fl9 [@antialize]: https://github.com/antialize [@dignifiedquire]: https://github.com/dignifiedquire [@argv-minus-one]: https://github.com/argv-minus-one [@qqwa]: https://github.com/qqwa [@diggsey]: https://github.com/Diggsey [@crajcan]: https://github.com/crajcan [@demurgos]: https://github.com/demurgos [@link2xt]: https://github.com/link2xt [@guylapid]: https://github.com/guylapid [@natproach]: https://github.com/NatPRoach [@feikesteenbergen]: https://github.com/feikesteenbergen [@etcaton]: https://github.com/ETCaton [@toshokan]: https://github.com/toshokan [@nomick]: https://github.com/nomick [@marshoepial]: https://github.com/marshoepial [@link2ext]: https://github.com/link2ext [@madadam]: https://github.com/madadam [@AtkinsChang]: https://github.com/AtkinsChang [@djmarcin]: https://github.com/djmarcin [@ghassmo]: https://github.com/ghassmo [@eagletmt]: https://github.com/eagletmt [@montanalow]: https://github.com/montanalow [@nitnelave]: https://github.com/nitnelave [@Drevoed]: https://github.com/Drevoed [@yuyawk]: https://github.com/yuyawk [@yerke]: https://github.com/yerke [@russweas]: https://github.com/russweas [@zbigniewzolnierowicz]: https://github.com/zbigniewzolnierowicz [@dimfeld]: https://github.com/dimfeld [@akiradeveloper]: https://github.com/akiradeveloper [@chesedo]: https://github.com/chesedo [@LLBlumire]: https://github.com/LLBlumire [@liushuyu]: https://github.com/liushuyu [@paolobarbolini]: https://github.com/paolobarbolini [@DoumanAsh]: https://github.com/DoumanAsh [@D1plo1d]: https://github.com/D1plo1d [@tkintscher]: https://github.com/tkintscher [@SonicZentropy]: https://github.com/SonicZentropy [@parazyd]: https://github.com/parazyd [@kunjee17]: https://github.com/kunjee17 [@05storm26]: https://github.com/05storm26 [@dbeckwith]: https://github.com/dbeckwith [@k-jun]: https://github.com/k-jun [@tranzystorek-io]: https://github.com/tranzystorek-io [@taladar]: https://github.com/taladar [@genusistimelord]: https://github.com/genusistimelord [@p9s]: https://github.com/p9s [@ArGGu]: https://github.com/ArGGu [@sedrik]: https://github.com/sedrik [@nappa85]: https://github.com/nappa85 [@ifn3]: https://github.com/ifn3 [@LovecraftianHorror]: https://github.com/LovecraftianHorror [@stoically]: https://github.com/stoically [@VersBinarii]: https://github.com/VersBinarii [@cemoktra]: https://github.com/cemoktra [@jdrouet]: https://github.com/jdrouet [@vbmade2000]: https://github.com/vbmade2000 [@abreis]: https://github.com/abreis [@0xdeafbeef]: https://github.com/0xdeafbeef [@Dylan-DPC]: https://github.com/Dylan-DPC [@carols10cents]: https://github.com/carols10cents [@david-mcgillicuddy-moixa]: https://github.com/david-mcgillicuddy-moixa [@ipetkov]: https://github.com/ipetkov [@pedromfedricci]: https://github.com/pedromfedricci [@tm-drtina]: https://github.com/tm-drtina [@espindola]: https://github.com/espindola [@mgrachev]: https://github.com/mgrachev [@tyrelr]: https://github.com/tyrelr [@SebastienGllmt]: https://github.com/SebastienGllmt [@e00E]: https://github.com/e00E [@sebpuetz]: https://github.com/sebpuetz [@pruthvikar]: https://github.com/pruthvikar [@tobymurray]: https://github.com/tobymurray [@djc]: https://github.com/djc [@mfreeborn]: https://github.com/mfreeborn [@scottwey]: https://github.com/scottwey [@e-rhodes]: https://github.com/e-rhodes [@OskarPersson]: https://github.com/OskarPersson [@walf443]: https://github.com/walf443 [@lovasoa]: https://github.com/lovasoa [@mdtusz]: https://github.com/mdtusz [@kianmeng]: https://github.com/kianmeng [@EthanYuan]: https://github.com/EthanYuan [@Nukesor]: https://github.com/Nukesor [@smonv]: https://github.com/smonv [@Erik1000]: https://github.com/Erik1000 [@raviqqe]: https://github.com/raviqqe [@johnbcodes]: https://github.com/johnbcodes [@sbeckeriv]: https://github.com/sbeckeriv [@RomainStorai]: https://github.com/RomainStorai [@jayy-lmao]: https://github.com/jayy-lmao [@Thomasdezeeuw]: https://github.com/Thomasdezeeuw [@kenkoooo]: https://github.com/kenkoooo [@TheoOiry]: https://github.com/TheoOiry [@JoeyMckenzie]: https://github.com/JoeyMckenzie [@ivan]: https://github.com/ivan [@crepererum]: https://github.com/crepererum [@UramnOIL]: https://github.com/UramnOIL [@liningpan]: https://github.com/liningpan [@zzhengzhuo]: https://github.com/zzhengzhuo [@crepererum]: https://github.com/crepererum [@szymek156]: https://github.com/szymek156 [@NSMustache]: https://github.com/NSMustache [@RustyYato]: https://github.com/RustyYato [@alexander-jackson]: https://github.com/alexander-jackson [@zlidner]: https://github.com/zlidner [@zlindner]: https://github.com/zlindner [@marcustut]: https://github.com/marcustut [@rakshith-ravi]: https://github.com/rakshith-ravi [@bradfier]: https://github.com/bradfier [@fuzzbuck]: https://github.com/fuzzbuck [@cycraig]: https://github.com/cycraig [@fasterthanlime]: https://github.com/fasterthanlime [@he4d]: https://github.com/he4d [@DXist]: https://github.com/DXist [@Wopple]: https://github.com/Wopple [@TravisWhitehead]: https://github.com/TravisWhitehead [@ThibsG]: https://github.com/ThibsG [@rongcuid]: https://github.com/rongcuid [@moatra]: https://github.com/moatra [@penberg]: https://github.com/penberg [@saiintbrisson]: https://github.com/saiintbrisson [@FSMaxB]: https://github.com/FSMaxB [@95ulisse]: https://github.com/95ulisse [@miles170]: https://github.com/miles170 [@ar3s3ru]: https://github.com/ar3s3ru [@cdbfoster]: https://github.com/cdbfoster [@andyquinterom]: https://github.com/andyquinterom [@CosmicHorrorDev]: https://github.com/CosmicHorrorDev [@VictorKoenders]: https://github.com/VictorKoenders [@joehillen]: https://github.com/joehillen [@OverHash]: https://github.com/OverHash [@laundmo]: https://github.com/laundmo [@nbaztec]: https://github.com/nbaztec [@bgeron]: https://github.com/bgeron [@benesch]: https://github.com/benesch [@nstinus]: https://github.com/nstinus [@grgi]: https://github.com/grgi [@sergeiivankov]: https://github.com/sergeiivankov [@jaysonsantos]: https://github.com/jaysonsantos [@dbrgn]: https://github.com/dbrgn [@grantkee]: https://github.com/grantkee [@bnoctis]: https://github.com/bnoctis [@aschey]: https://github.com/aschey [@df51d]: https://github.com/df51d [@codahale]: https://github.com/codahale [@arlyon]: https://github.com/arlyon [@SergioBenitez]: https://github.com/SergioBenitez [@tgeoghegan]: https://github.com/tgeoghegan [@vizvasrj]: https://github.com/vizvasrj [@phlip9]: https://github.com/phlip9 [@MidasLamb]: https://github.com/MidasLamb [@utkarshgupta137]: https://github.com/utkarshgupta137 [@Arcayr]: https://github.com/Arcayr [@mdecimus]: https://github.com/mdecimus [@Razican]: https://github.com/Razican [@southball]: https://github.com/southball [@alilleybrinker]: https://github.com/alilleybrinker [@titaniumtraveler]: https://github.com/titaniumtraveler [@nyurik]: https://github.com/nyurik [@stepantubanov]: https://github.com/stepantubanov [@iamquang95]: https://github.com/iamquang95 [@jnnnnn]: https://github.com/jnnnnn [@saolof]: https://github.com/saolof [@deneut]: https://github.com/deneut [@kitterion]: https://github.com/kitterion [@denschub]: https://github.com/denschub [@fd]: https://github.com/fd [@mrl5]: https://github.com/mrl5 [@Xydez]: https://github.com/Xydez [@vabka]: https://github.com/vabka [@ldanilek]: https://github.com/ldanilek [@inahga]: https://github.com/inahga [@JockeM]: https://github.com/JockeM [@vmax]: https://github.com/vmax [@sebastianv89]: https://github.com/sebastianv89 [@marcusirgens]: https://github.com/marcusirgens [@tsing]: https://github.com/tsing [@snf]: https://github.com/snf [@wyhaya]: https://github.com/wyhaya [@hakoerber]: https://github.com/hakoerber [@olback]: https://github.com/olback [@kryptan]: https://github.com/kryptan [@grant0417]: https://github.com/grant0417 [@fermanjj]: https://github.com/fermanjj [@grooverdan]: https://github.com/grooverdan [@bobozaur]: https://github.com/bobozaur [@aldur]: https://github.com/aldur [@hgranthorner]: https://github.com/hgranthorner [@ripa1995]: https://github.com/ripa1995 [@fhsgoncalves]: https://github.com/fhsgoncalves [@uttarayan21]: https://github.com/uttarayan21 [@tk2217]: https://github.com/tk2217 [@hamiltop]: https://github.com/hamiltop [@cameronbraid]: https://github.com/cameronbraid [@Baptistemontan]: https://github.com/Baptistemontan [@satwanjyu]: https://github.com/satwanjyu [@boris-lok]: https://github.com/boris-lok [@qwerty2501]: https://github.com/qwerty2501 [@Nemo157]: https://github.com/Nemo157 [@snspinn]: https://github.com/snspinn [@granddaifuku]: https://github.com/granddaifuku [@yasamoka]: https://github.com/yasamoka [@mattfbacon]: https://github.com/mattfbacon [@A248]: https://github.com/A248 [@conradludgate]: https://github.com/conradludgate [@anupj]: https://github.com/anupj [@nanoqsh]: https://github.com/nanoqsh [@brianheineman]: https://github.com/brianheineman [@jkleinknox]: https://github.com/jkleinknox [@cryeprecision]: https://github.com/cryeprecision [@Vrajs16]: https://github.com/Vrajs16 [@shiftrightonce]: https://github.com/shiftrightonce [@tamasfe]: https://github.com/tamasfe [@lily-mosquitoes]: https://github.com/lily-mosquitoes [@larsschumacher]: https://github.com/larsschumacher [@shengsheng]: https://github.com/shengsheng [@Fyko]: https://github.com/Fyko [@kshramt]: https://github.com/kshramt [@Dawsoncodes]: https://github.com/Dawsoncodes [@tadghh]: https://github.com/tadghh [@holicc]: https://github.com/holicc [@takenoko-gohan]: https://github.com/takenoko-gohan [@iangilfillan]: https://github.com/iangilfillan [@iamjpotts]: https://github.com/iamjpotts [@Icerath]: https://github.com/Icerath [@pawurb]: https://github.com/pawurb [@darkecho731]: https://github.com/darkecho731 [@mirek26]: https://github.com/mirek26 [@Ekleog]: https://github.com/Ekleog [@zoomiti]: https://github.com/zoomiti [@ciffelia]: https://github.com/ciffelia [@rafaelGuerreiro]: https://github.com/rafaelGuerreiro [@alu]: https://github.com/alu [@BadBastion]: https://github.com/BadBastion [@tylerhawkes]: https://github.com/tylerhawkes [@g-bartoszek]: https://github.com/g-bartoszek [@benluelo]: https://github.com/benluelo [@ralpha]: https://github.com/ralpha [@nitn3lav]: https://github.com/nitn3lav [@FlakM]: https://github.com/FlakM [@hoxxep]: https://github.com/hoxxep [@NfNitLoop]: https://github.com/NfNitLoop [@GnomedDev]: https://github.com/GnomedDev [@pxp9]: https://github.com/pxp9 [@RaghavRox]: https://github.com/RaghavRox [@cleverjam]: https://github.com/cleverjam [@BlackSoulHub]: https://github.com/BlackSoulHub [@levkk]: https://github.com/levkk [@danjpgriffin]: https://github.com/danjpgriffin [@toxeus]: https://github.com/toxeus [@jasonish]: https://github.com/jasonish [@AlphaKeks]: https://github.com/AlphaKeks [@Zarathustra2]: https://github.com/Zarathustra2 [@gridbox]: https://github.com/gridbox [@joelkoen]: https://github.com/joelkoen [@nk9]: https://github.com/nk9 [@etorreborre]: https://github.com/etorreborre [@LecrisUT]: https://github.com/LecrisUT [@JohannesIBK]: https://github.com/JohannesIBK [@Lachstec]: https://github.com/Lachstec [@SrGesus]: https://github.com/SrGesus [@CommanderStorm]: https://github.com/CommanderStorm [@hamirmahal]: https://github.com/hamirmahal [@DirectorX]: https://github.com/DirectorX [@KobusEllis]: https://github.com/KobusEllis [@YgorSouza]: https://github.com/YgorSouza [@Zarthus]: https://github.com/Zarthus [@ckampfe]: https://github.com/ckampfe [@tottoto]: https://github.com/tottoto [@ods]: https://github.com/ods [@soucosmo]: https://github.com/soucosmo [@kolinfluence]: https://github.com/kolinfluence [@joeydewaal]: https://github.com/joeydewaal [@pierre-wehbe]: https://github.com/pierre-wehbe [@carschandler]: https://github.com/carschandler [@kdesjard]: https://github.com/kdesjard [@luveti]: https://github.com/luveti [@dojiong]: https://github.com/dojiong [@jayvdb]: https://github.com/jayvdb [@kurtbuilds]: https://github.com/kurtbuilds [@lilydjwg]: https://github.com/lilydjwg [@M3t0r]: https://github.com/M3t0r [@vsuryamurthy]: https://github.com/vsuryamurthy [@manifest]: https://github.com/manifest [@tbar4]: https://github.com/tbar4 [@sandhose]: https://github.com/sandhose [@IlyaBizyaev]: https://github.com/IlyaBizyaev [@philipcristiano]: https://github.com/philipcristiano [@xuehaonan27]: https://github.com/xuehaonan27 [@chanks]: https://github.com/chanks [@Ddystopia]: https://github.com/Ddystopia [@veigaribo]: https://github.com/veigaribo [@Norlock]: https://github.com/Norlock [@swlynch99]: https://github.com/swlynch99 [@BenoitRanque]: https://github.com/BenoitRanque [@hsivonen]: https://github.com/hsivonen [@andreweggleston]: https://github.com/andreweggleston [@Suficio]: https://github.com/Suficio sqlx-0.8.3/CONTRIBUTING.md000064400000000000000000000033571046102023000130640ustar 00000000000000# How to contribute So, you've decided to contribute, that's great! You can use this document to figure out how and where to start. ## Getting started - Make sure you have a [GitHub account](https://github.com/join). - Take a look at [existing issues](https://github.com/launchbadge/sqlx/issues). - If you need to create an issue: - Make sure to clearly describe it. - Including steps to reproduce when it is a bug. - Include the version of SQLx used. - Include the database driver and version. - Include the database version. ## Making changes - Fork the repository on GitHub. - Create a branch on your fork. - You can usually base it on the `main` branch. - Make sure not to commit directly to `main`. - Make commits of logical and atomic units. - Make sure you have added the necessary tests for your changes. - Push your changes to a topic branch in your fork of the repository. - Submit a pull request to the original repository. ## What to work on We try to mark issues with a suggested level of experience (in Rust/SQL/SQLx). Where possible we try to spell out how to go about implementing the feature. To start with, check out: - Issues labeled as ["good first issue"](https://github.com/launchbadge/sqlx/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22). - Issues labeled as ["Easy"](https://github.com/launchbadge/sqlx/issues?q=is%3Aopen+is%3Aissue+label%3AE-easy). Additionally, it's always good to work on improving/adding examples and documentation. ## Communication If you're unsure about your contribution or simply want to ask a question about anything, you can: - Visit the [SQLx Discord server](https://discord.gg/uuruzJ7) - Discuss something directly in the [Github issue](https://github.com/launchbadge/sqlx/issues). sqlx-0.8.3/Cargo.toml0000644000000226070000000000100100400ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "sqlx" version = "0.8.3" authors = [ "Ryan Leckey ", "Austin Bonander ", "Chloe Ross ", "Daniel Akhterov ", ] description = "🧰 The Rust SQL Toolkit. An async, pure Rust SQL crate featuring compile-time checked queries without a DSL. Supports PostgreSQL, MySQL, and SQLite." documentation = "https://docs.rs/sqlx" readme = "README.md" license = "MIT OR Apache-2.0" repository = "https://github.com/launchbadge/sqlx" [package.metadata.docs.rs] features = [ "all-databases", "_unstable-all-types", ] rustdoc-args = [ "--cfg", "docsrs", ] [[test]] name = "any" path = "tests/any/any.rs" required-features = ["any"] [[test]] name = "any-pool" path = "tests/any/pool.rs" required-features = ["any"] [[test]] name = "migrate-macro" path = "tests/migrate/macro.rs" required-features = [ "macros", "migrate", ] [[test]] name = "sqlite" path = "tests/sqlite/sqlite.rs" required-features = ["sqlite"] [[test]] name = "sqlite-any" path = "tests/sqlite/any.rs" required-features = ["sqlite"] [[test]] name = "sqlite-types" path = "tests/sqlite/types.rs" required-features = ["sqlite"] [[test]] name = "sqlite-describe" path = "tests/sqlite/describe.rs" required-features = ["sqlite"] [[test]] name = "sqlite-macros" path = "tests/sqlite/macros.rs" required-features = [ "sqlite", "macros", ] [[test]] name = "sqlite-unbundled-macros" path = "tests/sqlite/macros.rs" required-features = [ "sqlite-unbundled", "macros", ] [[test]] name = "sqlite-derives" path = "tests/sqlite/derives.rs" required-features = [ "sqlite", "macros", ] [[test]] name = "sqlite-error" path = "tests/sqlite/error.rs" required-features = ["sqlite"] [[test]] name = "sqlite-sqlcipher" path = "tests/sqlite/sqlcipher.rs" required-features = ["sqlite"] [[test]] name = "sqlite-test-attr" path = "tests/sqlite/test-attr.rs" required-features = [ "sqlite", "macros", "migrate", ] [[test]] name = "sqlite-migrate" path = "tests/sqlite/migrate.rs" required-features = [ "sqlite", "macros", "migrate", ] [[test]] name = "sqlite-rustsec" path = "tests/sqlite/rustsec.rs" required-features = ["sqlite"] [[test]] name = "mysql" path = "tests/mysql/mysql.rs" required-features = ["mysql"] [[test]] name = "mysql-types" path = "tests/mysql/types.rs" required-features = ["mysql"] [[test]] name = "mysql-describe" path = "tests/mysql/describe.rs" required-features = ["mysql"] [[test]] name = "mysql-derives" path = "tests/mysql/derives.rs" required-features = [ "mysql", "derive", ] [[test]] name = "mysql-macros" path = "tests/mysql/macros.rs" required-features = [ "mysql", "macros", ] [[test]] name = "mysql-error" path = "tests/mysql/error.rs" required-features = ["mysql"] [[test]] name = "mysql-test-attr" path = "tests/mysql/test-attr.rs" required-features = [ "mysql", "macros", "migrate", ] [[test]] name = "mysql-migrate" path = "tests/mysql/migrate.rs" required-features = [ "mysql", "macros", "migrate", ] [[test]] name = "mysql-rustsec" path = "tests/mysql/rustsec.rs" required-features = ["mysql"] [[test]] name = "postgres" path = "tests/postgres/postgres.rs" required-features = ["postgres"] [[test]] name = "postgres-types" path = "tests/postgres/types.rs" required-features = ["postgres"] [[test]] name = "postgres-describe" path = "tests/postgres/describe.rs" required-features = ["postgres"] [[test]] name = "postgres-macros" path = "tests/postgres/macros.rs" required-features = [ "postgres", "macros", ] [[test]] name = "postgres-derives" path = "tests/postgres/derives.rs" required-features = [ "postgres", "macros", ] [[test]] name = "postgres-error" path = "tests/postgres/error.rs" required-features = ["postgres"] [[test]] name = "postgres-test-attr" path = "tests/postgres/test-attr.rs" required-features = [ "postgres", "macros", "migrate", ] [[test]] name = "postgres-migrate" path = "tests/postgres/migrate.rs" required-features = [ "postgres", "macros", "migrate", ] [[test]] name = "postgres-query-builder" path = "tests/postgres/query_builder.rs" required-features = ["postgres"] [[test]] name = "postgres-rustsec" path = "tests/postgres/rustsec.rs" required-features = [ "postgres", "macros", "migrate", ] [[bench]] name = "sqlite-describe" path = "benches/sqlite/describe.rs" harness = false required-features = ["sqlite"] [dependencies.sqlx-core] version = "=0.8.3" features = [ "offline", "migrate", ] [dependencies.sqlx-macros] version = "=0.8.3" optional = true [dependencies.sqlx-mysql] version = "=0.8.3" optional = true [dependencies.sqlx-postgres] version = "=0.8.3" optional = true [dependencies.sqlx-sqlite] version = "=0.8.3" optional = true [dev-dependencies.anyhow] version = "1.0.52" [dev-dependencies.async-std] version = "1.12" features = ["attributes"] [dev-dependencies.criterion] version = "0.5.1" features = ["async_tokio"] [dev-dependencies.dotenvy] version = "0.15.0" [dev-dependencies.env_logger] version = "0.11" [dev-dependencies.futures] version = "0.3.19" [dev-dependencies.hex] version = "0.4.3" [dev-dependencies.paste] version = "1.0.6" [dev-dependencies.rand] version = "0.8.4" [dev-dependencies.rand_xoshiro] version = "0.6.0" [dev-dependencies.serde] version = "1.0.132" features = ["derive"] [dev-dependencies.serde_json] version = "1.0.73" [dev-dependencies.tempfile] version = "3.10.1" [dev-dependencies.time_] version = "0.3.2" package = "time" [dev-dependencies.tokio] version = "1.15.0" features = ["full"] [dev-dependencies.trybuild] version = "1.0.53" [dev-dependencies.url] version = "2.2.2" [features] _rt-async-std = [] _rt-tokio = [] _sqlite = [] _unstable-all-types = [ "bigdecimal", "rust_decimal", "json", "time", "chrono", "ipnetwork", "mac_address", "uuid", "bit-vec", ] all-databases = [ "mysql", "sqlite", "postgres", "any", ] any = [ "sqlx-core/any", "sqlx-mysql?/any", "sqlx-postgres?/any", "sqlx-sqlite?/any", ] bigdecimal = [ "sqlx-core/bigdecimal", "sqlx-macros?/bigdecimal", "sqlx-mysql?/bigdecimal", "sqlx-postgres?/bigdecimal", ] bit-vec = [ "sqlx-core/bit-vec", "sqlx-macros?/bit-vec", "sqlx-postgres?/bit-vec", ] chrono = [ "sqlx-core/chrono", "sqlx-macros?/chrono", "sqlx-mysql?/chrono", "sqlx-postgres?/chrono", "sqlx-sqlite?/chrono", ] default = [ "any", "macros", "migrate", "json", ] derive = ["sqlx-macros/derive"] ipnetwork = [ "sqlx-core/ipnetwork", "sqlx-macros?/ipnetwork", "sqlx-postgres?/ipnetwork", ] json = [ "sqlx-macros?/json", "sqlx-mysql?/json", "sqlx-postgres?/json", "sqlx-sqlite?/json", ] mac_address = [ "sqlx-core/mac_address", "sqlx-macros?/mac_address", "sqlx-postgres?/mac_address", ] macros = [ "derive", "sqlx-macros/macros", ] migrate = [ "sqlx-core/migrate", "sqlx-macros?/migrate", "sqlx-mysql?/migrate", "sqlx-postgres?/migrate", "sqlx-sqlite?/migrate", ] mysql = [ "sqlx-mysql", "sqlx-macros?/mysql", ] postgres = [ "sqlx-postgres", "sqlx-macros?/postgres", ] regexp = ["sqlx-sqlite?/regexp"] runtime-async-std = [ "_rt-async-std", "sqlx-core/_rt-async-std", "sqlx-macros?/_rt-async-std", ] runtime-async-std-native-tls = [ "runtime-async-std", "tls-native-tls", ] runtime-async-std-rustls = [ "runtime-async-std", "tls-rustls-ring", ] runtime-tokio = [ "_rt-tokio", "sqlx-core/_rt-tokio", "sqlx-macros?/_rt-tokio", ] runtime-tokio-native-tls = [ "runtime-tokio", "tls-native-tls", ] runtime-tokio-rustls = [ "runtime-tokio", "tls-rustls-ring", ] rust_decimal = [ "sqlx-core/rust_decimal", "sqlx-macros?/rust_decimal", "sqlx-mysql?/rust_decimal", "sqlx-postgres?/rust_decimal", ] sqlite = [ "_sqlite", "sqlx-sqlite/bundled", "sqlx-macros?/sqlite", ] sqlite-unbundled = [ "_sqlite", "sqlx-sqlite/unbundled", "sqlx-macros?/sqlite-unbundled", ] time = [ "sqlx-core/time", "sqlx-macros?/time", "sqlx-mysql?/time", "sqlx-postgres?/time", "sqlx-sqlite?/time", ] tls-native-tls = [ "sqlx-core/_tls-native-tls", "sqlx-macros?/_tls-native-tls", ] tls-none = [] tls-rustls = ["tls-rustls-ring"] tls-rustls-aws-lc-rs = [ "sqlx-core/_tls-rustls-aws-lc-rs", "sqlx-macros?/_tls-rustls-aws-lc-rs", ] tls-rustls-ring = ["tls-rustls-ring-webpki"] tls-rustls-ring-native-roots = [ "sqlx-core/_tls-rustls-ring-native-roots", "sqlx-macros?/_tls-rustls-ring-native-roots", ] tls-rustls-ring-webpki = [ "sqlx-core/_tls-rustls-ring-webpki", "sqlx-macros?/_tls-rustls-ring-webpki", ] uuid = [ "sqlx-core/uuid", "sqlx-macros?/uuid", "sqlx-mysql?/uuid", "sqlx-postgres?/uuid", "sqlx-sqlite?/uuid", ] [target."cfg(sqlite_test_sqlcipher)".dev-dependencies.libsqlite3-sys] version = "0.30.1" features = ["bundled-sqlcipher"] sqlx-0.8.3/Cargo.toml.orig000064400000000000000000000256561046102023000135300ustar 00000000000000[workspace] members = [ ".", "sqlx-core", "sqlx-macros", "sqlx-macros-core", "sqlx-test", "sqlx-cli", # "sqlx-bench", "sqlx-mysql", "sqlx-postgres", "sqlx-sqlite", "examples/mysql/todos", "examples/postgres/axum-social-with-tests", "examples/postgres/chat", "examples/postgres/files", "examples/postgres/json", "examples/postgres/listen", "examples/postgres/todos", "examples/postgres/mockable-todos", "examples/postgres/transaction", "examples/sqlite/todos", ] [workspace.package] version = "0.8.3" license = "MIT OR Apache-2.0" edition = "2021" repository = "https://github.com/launchbadge/sqlx" keywords = ["database", "async", "postgres", "mysql", "sqlite"] categories = ["database", "asynchronous"] authors = [ "Ryan Leckey ", "Austin Bonander ", "Chloe Ross ", "Daniel Akhterov ", ] # TODO: enable this for 0.9.0 # rust-version = "1.80.0" [package] name = "sqlx" readme = "README.md" documentation = "https://docs.rs/sqlx" description = "🧰 The Rust SQL Toolkit. An async, pure Rust SQL crate featuring compile-time checked queries without a DSL. Supports PostgreSQL, MySQL, and SQLite." version.workspace = true license.workspace = true edition.workspace = true authors.workspace = true repository.workspace = true [package.metadata.docs.rs] features = ["all-databases", "_unstable-all-types"] rustdoc-args = ["--cfg", "docsrs"] [features] default = ["any", "macros", "migrate", "json"] derive = ["sqlx-macros/derive"] macros = ["derive", "sqlx-macros/macros"] migrate = ["sqlx-core/migrate", "sqlx-macros?/migrate", "sqlx-mysql?/migrate", "sqlx-postgres?/migrate", "sqlx-sqlite?/migrate"] # intended mainly for CI and docs all-databases = ["mysql", "sqlite", "postgres", "any"] _unstable-all-types = [ "bigdecimal", "rust_decimal", "json", "time", "chrono", "ipnetwork", "mac_address", "uuid", "bit-vec", ] # Base runtime features without TLS runtime-async-std = ["_rt-async-std", "sqlx-core/_rt-async-std", "sqlx-macros?/_rt-async-std"] runtime-tokio = ["_rt-tokio", "sqlx-core/_rt-tokio", "sqlx-macros?/_rt-tokio"] # TLS features tls-native-tls = ["sqlx-core/_tls-native-tls", "sqlx-macros?/_tls-native-tls"] tls-rustls = ["tls-rustls-ring"] # For backwards compatibility tls-rustls-aws-lc-rs = ["sqlx-core/_tls-rustls-aws-lc-rs", "sqlx-macros?/_tls-rustls-aws-lc-rs"] tls-rustls-ring = ["tls-rustls-ring-webpki"] # For backwards compatibility tls-rustls-ring-webpki = ["sqlx-core/_tls-rustls-ring-webpki", "sqlx-macros?/_tls-rustls-ring-webpki"] tls-rustls-ring-native-roots = ["sqlx-core/_tls-rustls-ring-native-roots", "sqlx-macros?/_tls-rustls-ring-native-roots"] # No-op feature used by the workflows to compile without TLS enabled. Not meant for general use. tls-none = [] # Legacy Runtime + TLS features runtime-async-std-native-tls = ["runtime-async-std", "tls-native-tls"] runtime-async-std-rustls = ["runtime-async-std", "tls-rustls-ring"] runtime-tokio-native-tls = ["runtime-tokio", "tls-native-tls"] runtime-tokio-rustls = ["runtime-tokio", "tls-rustls-ring"] # for conditional compilation _rt-async-std = [] _rt-tokio = [] _sqlite = [] # database any = ["sqlx-core/any", "sqlx-mysql?/any", "sqlx-postgres?/any", "sqlx-sqlite?/any"] postgres = ["sqlx-postgres", "sqlx-macros?/postgres"] mysql = ["sqlx-mysql", "sqlx-macros?/mysql"] sqlite = ["_sqlite", "sqlx-sqlite/bundled", "sqlx-macros?/sqlite"] sqlite-unbundled = ["_sqlite", "sqlx-sqlite/unbundled", "sqlx-macros?/sqlite-unbundled"] # types json = ["sqlx-macros?/json", "sqlx-mysql?/json", "sqlx-postgres?/json", "sqlx-sqlite?/json"] bigdecimal = ["sqlx-core/bigdecimal", "sqlx-macros?/bigdecimal", "sqlx-mysql?/bigdecimal", "sqlx-postgres?/bigdecimal"] bit-vec = ["sqlx-core/bit-vec", "sqlx-macros?/bit-vec", "sqlx-postgres?/bit-vec"] chrono = ["sqlx-core/chrono", "sqlx-macros?/chrono", "sqlx-mysql?/chrono", "sqlx-postgres?/chrono", "sqlx-sqlite?/chrono"] ipnetwork = ["sqlx-core/ipnetwork", "sqlx-macros?/ipnetwork", "sqlx-postgres?/ipnetwork"] mac_address = ["sqlx-core/mac_address", "sqlx-macros?/mac_address", "sqlx-postgres?/mac_address"] rust_decimal = ["sqlx-core/rust_decimal", "sqlx-macros?/rust_decimal", "sqlx-mysql?/rust_decimal", "sqlx-postgres?/rust_decimal"] time = ["sqlx-core/time", "sqlx-macros?/time", "sqlx-mysql?/time", "sqlx-postgres?/time", "sqlx-sqlite?/time"] uuid = ["sqlx-core/uuid", "sqlx-macros?/uuid", "sqlx-mysql?/uuid", "sqlx-postgres?/uuid", "sqlx-sqlite?/uuid"] regexp = ["sqlx-sqlite?/regexp"] [workspace.dependencies] # Core Crates sqlx-core = { version = "=0.8.3", path = "sqlx-core" } sqlx-macros-core = { version = "=0.8.3", path = "sqlx-macros-core" } sqlx-macros = { version = "=0.8.3", path = "sqlx-macros" } # Driver crates sqlx-mysql = { version = "=0.8.3", path = "sqlx-mysql" } sqlx-postgres = { version = "=0.8.3", path = "sqlx-postgres" } sqlx-sqlite = { version = "=0.8.3", path = "sqlx-sqlite" } # Facade crate (for reference from sqlx-cli) sqlx = { version = "=0.8.3", path = ".", default-features = false } # Common type integrations shared by multiple driver crates. # These are optional unless enabled in a workspace crate. bigdecimal = "0.4.0" bit-vec = "0.6.3" chrono = { version = "0.4.34", default-features = false, features = ["std", "clock"] } ipnetwork = "0.20.0" mac_address = "1.1.5" rust_decimal = { version = "1.26.1", default-features = false, features = ["std"] } time = { version = "0.3.36", features = ["formatting", "parsing", "macros"] } uuid = "1.1.2" # Common utility crates dotenvy = { version = "0.15.0", default-features = false } # Runtimes [workspace.dependencies.async-std] version = "1.12" [workspace.dependencies.tokio] version = "1" features = ["time", "net", "sync", "fs", "io-util", "rt"] default-features = false [dependencies] sqlx-core = { workspace = true, features = ["offline", "migrate"] } sqlx-macros = { workspace = true, optional = true } sqlx-mysql = { workspace = true, optional = true } sqlx-postgres = { workspace = true, optional = true } sqlx-sqlite = { workspace = true, optional = true } [dev-dependencies] anyhow = "1.0.52" time_ = { version = "0.3.2", package = "time" } futures = "0.3.19" env_logger = "0.11" async-std = { workspace = true, features = ["attributes"] } tokio = { version = "1.15.0", features = ["full"] } dotenvy = "0.15.0" trybuild = "1.0.53" sqlx-test = { path = "./sqlx-test" } paste = "1.0.6" serde = { version = "1.0.132", features = ["derive"] } serde_json = "1.0.73" url = "2.2.2" rand = "0.8.4" rand_xoshiro = "0.6.0" hex = "0.4.3" tempfile = "3.10.1" criterion = { version = "0.5.1", features = ["async_tokio"] } # If this is an unconditional dev-dependency then Cargo will *always* try to build `libsqlite3-sys`, # even when SQLite isn't the intended test target, and fail if the build environment is not set up for compiling C code. [target.'cfg(sqlite_test_sqlcipher)'.dev-dependencies] # Enable testing with SQLCipher if specifically requested. libsqlite3-sys = { version = "0.30.1", features = ["bundled-sqlcipher"] } # Common lint settings for the workspace [workspace.lints.clippy] # https://github.com/launchbadge/sqlx/issues/3440 cast_possible_truncation = 'deny' cast_possible_wrap = 'deny' cast_sign_loss = 'deny' # See `clippy.toml` disallowed_methods = 'deny' # # Any # [[test]] name = "any" path = "tests/any/any.rs" required-features = ["any"] [[test]] name = "any-pool" path = "tests/any/pool.rs" required-features = ["any"] # # Migrations # [[test]] name = "migrate-macro" path = "tests/migrate/macro.rs" required-features = ["macros", "migrate"] # # SQLite # [[test]] name = "sqlite" path = "tests/sqlite/sqlite.rs" required-features = ["sqlite"] [[test]] name = "sqlite-any" path = "tests/sqlite/any.rs" required-features = ["sqlite"] [[test]] name = "sqlite-types" path = "tests/sqlite/types.rs" required-features = ["sqlite"] [[test]] name = "sqlite-describe" path = "tests/sqlite/describe.rs" required-features = ["sqlite"] [[test]] name = "sqlite-macros" path = "tests/sqlite/macros.rs" required-features = ["sqlite", "macros"] [[test]] name = "sqlite-unbundled-macros" path = "tests/sqlite/macros.rs" required-features = ["sqlite-unbundled", "macros"] [[test]] name = "sqlite-derives" path = "tests/sqlite/derives.rs" required-features = ["sqlite", "macros"] [[test]] name = "sqlite-error" path = "tests/sqlite/error.rs" required-features = ["sqlite"] [[test]] name = "sqlite-sqlcipher" path = "tests/sqlite/sqlcipher.rs" required-features = ["sqlite"] [[test]] name = "sqlite-test-attr" path = "tests/sqlite/test-attr.rs" required-features = ["sqlite", "macros", "migrate"] [[test]] name = "sqlite-migrate" path = "tests/sqlite/migrate.rs" required-features = ["sqlite", "macros", "migrate"] [[test]] name = "sqlite-rustsec" path = "tests/sqlite/rustsec.rs" required-features = ["sqlite"] [[bench]] name = "sqlite-describe" path = "benches/sqlite/describe.rs" harness = false required-features = ["sqlite"] # # MySQL # [[test]] name = "mysql" path = "tests/mysql/mysql.rs" required-features = ["mysql"] [[test]] name = "mysql-types" path = "tests/mysql/types.rs" required-features = ["mysql"] [[test]] name = "mysql-describe" path = "tests/mysql/describe.rs" required-features = ["mysql"] [[test]] name = "mysql-derives" path = "tests/mysql/derives.rs" required-features = ["mysql", "derive"] [[test]] name = "mysql-macros" path = "tests/mysql/macros.rs" required-features = ["mysql", "macros"] [[test]] name = "mysql-error" path = "tests/mysql/error.rs" required-features = ["mysql"] [[test]] name = "mysql-test-attr" path = "tests/mysql/test-attr.rs" required-features = ["mysql", "macros", "migrate"] [[test]] name = "mysql-migrate" path = "tests/mysql/migrate.rs" required-features = ["mysql", "macros", "migrate"] [[test]] name = "mysql-rustsec" path = "tests/mysql/rustsec.rs" required-features = ["mysql"] # # PostgreSQL # [[test]] name = "postgres" path = "tests/postgres/postgres.rs" required-features = ["postgres"] [[test]] name = "postgres-types" path = "tests/postgres/types.rs" required-features = ["postgres"] [[test]] name = "postgres-describe" path = "tests/postgres/describe.rs" required-features = ["postgres"] [[test]] name = "postgres-macros" path = "tests/postgres/macros.rs" required-features = ["postgres", "macros"] [[test]] name = "postgres-derives" path = "tests/postgres/derives.rs" required-features = ["postgres", "macros"] [[test]] name = "postgres-error" path = "tests/postgres/error.rs" required-features = ["postgres"] [[test]] name = "postgres-test-attr" path = "tests/postgres/test-attr.rs" required-features = ["postgres", "macros", "migrate"] [[test]] name = "postgres-migrate" path = "tests/postgres/migrate.rs" required-features = ["postgres", "macros", "migrate"] [[test]] name = "postgres-query-builder" path = "tests/postgres/query_builder.rs" required-features = ["postgres"] [[test]] name = "postgres-rustsec" path = "tests/postgres/rustsec.rs" required-features = ["postgres", "macros", "migrate"] sqlx-0.8.3/FAQ.md000064400000000000000000000612331046102023000115610ustar 00000000000000SQLx Frequently Asked Questions =============================== ### What database versions does SQLx support? This is a difficult question to answer because it depends on which features of the databases are used and when those features were introduced. SQL databases tend to be very strongly backwards-compatible so it's likely that SQLx will work with some very old versions. TLS support is one of the features that ages most quickly with databases, since old SSL/TLS versions are deprecated over time as they become insecure due to weaknesses being discovered; this is especially important to consider when using RusTLS, as it only supports the latest TLS version for security reasons (see the question below mentioning RusTLS for details). As a rule, however, we only officially support the range of versions for each database that are still actively maintained, and will drop support for versions as they reach their end-of-life. * Postgres has a page to track these versions and give their end-of-life dates: https://www.postgresql.org/support/versioning/ * MariaDB has a similar list here (though it doesn't show the dates at which old versions were EOL'd): https://mariadb.com/kb/en/mariadb-server-release-dates/ * MySQL's equivalent page is more concerned with what platforms are supported by the newest and oldest maintained versions: https://www.mysql.com/support/supportedplatforms/database.html * However, its Wikipedia page helpfully tracks its versions and their announced EOL dates: https://en.wikipedia.org/wiki/MySQL#Release_history * SQLite is easy as only SQLite 3 is supported and the current version depends on the version of the `libsqlite3-sys` crate being used. For each database and where applicable, we test against the latest and oldest versions that we intend to support. You can see the current versions being tested against by looking at our CI config: https://github.com/launchbadge/sqlx/blob/main/.github/workflows/sqlx.yml#L168 ------------------------------------------------------------------- ### What versions of Rust does SQLx support? What is SQLx's MSRV\*? SQLx's MSRV is the second-to-latest stable release as of the beginning of the current release cycle (`0.x.0`). It will remain there until the next major release (`0.{x + 1}.0`). For example, as of the `0.8.0` release of SQLx, the latest stable Rust version was `1.79.0`, so the MSRV for the `0.8.x` release cycle of SQLx is `1.78.0`. This guarantees that SQLx will compile with a Rust version that is _at least_ six weeks old, which should be plenty of time for it to make it through any packaging system that is being actively kept up to date. We do _not_ recommend installing Rust through operating system packages, as they can often be a whole year or more out-of-date. \*Minimum Supported Rust Version [`rust-version`]: https://doc.rust-lang.org/stable/cargo/reference/manifest.html#the-rust-version-field ---------------------------------------------------------------- ### I'm getting `HandshakeFailure` or `CorruptMessage` when trying to connect to a server over TLS using RusTLS. What gives? To encourage good security practices and limit cruft, RusTLS does not support older versions of TLS or cryptographic algorithms that are considered insecure. `HandshakeFailure` is a normal error returned when RusTLS and the server cannot agree on parameters for a secure connection. Check the supported TLS versions for the database server version you're running. If it does not support TLS 1.2 or greater, then you likely will not be able to connect to it with RusTLS. The ideal solution, of course, is to upgrade your database server to a version that supports at least TLS 1.2. * MySQL: [has supported TLS 1.2 since 5.6.46](https://dev.mysql.com/doc/refman/5.6/en/encrypted-connection-protocols-ciphers.html#encrypted-connection-supported-protocols). * PostgreSQL: depends on the system OpenSSL version. * MSSQL: TLS is not supported yet. If you're running a third-party database that talks one of these protocols, consult its documentation for supported TLS versions. If you're stuck on an outdated version, which is unfortunate but tends to happen for one reason or another, try switching to the corresponding `runtime--native-tls` feature for SQLx. That will use the system APIs for TLS which tend to have much wider support. See [the `native-tls` crate docs](https://docs.rs/native-tls/latest/native_tls/) for details. The `CorruptMessage` error occurs in similar situations and many users have had success with switching to `-native-tls` to get around it. However, if you do encounter this error, please try to capture a Wireshark or `tcpdump` trace of the TLS handshake as the RusTLS folks are interested in covering cases that trigger this (as it might indicate a protocol handling bug or the server is doing something non-standard): https://github.com/rustls/rustls/issues/893 ---------------------------------------------------------------- ### How does SQLx help prevent SQL Injection? ### How do Query Parameters work? ### Why does SQLx use Prepared Statements for most queries? ### Can I Use Query Parameters to add conditional SQL to my query? ### Why can't I use DDL (e.g. `CREATE TABLE`, `ALTER TABLE`, etc.) with the `sqlx::query*()` functions or `sqlx::query*!()` macros? These questions can all be answered by a thorough explanation of prepared statements. Feel free to skip the parts you already know. Back in the day, if a web application wanted to include user input in a SQL query, a search parameter for example, it had no choice but to simply format that data into the query. PHP applications used to be full of snippets like this: ```php /* Imagine this is user input */ $city = "Munich"; /* $query = "SELECT country FROM city WHERE name='Munich'" */ $query = sprintf("SELECT country FROM city WHERE name='%s'", $city); $result = $mysqli->query($query); ``` However, this leaves the application vulnerable to [SQL injection attacks](https://en.wikipedia.org/wiki/SQL_injection), because it's trivial to craft an input string that will terminate the existing query and begin a new one, and the database won't know the difference and will execute both. As illustrated in the famous XKCD #327: Exploits of a Mom The fictional school's student database application might have contained a query that looked like this: ```php $student_name = "Robert');DROP TABLE Students;--" $query = sprintf("INSERT INTO Students (name) VALUES ('%s')", $student_name); $result = $mysqli->query($query); ``` When formatted into the middle of this query, the maliciously crafted input string closes the quotes and finishes the statement (`Robert');`), then starts another one with the nefarious payload (`DROP TABLE Students;`), and causes the rest of the original query to be ignored by starting a SQL comment (`--`). Thus, the database server sees, and executes, three separate statements like so: ```SQL INSERT INTO Students(firstname) VALUES ('Robert'); DROP TABLE Students; --'); ``` And thus the school has lost this year's student records (at least they had last years' backed up?). The original mitigation for this attack was to make sure that any untrustworthy user input was properly escaped (or "sanitized"), and many frameworks provided utility functions for this, such as PHP's [`mysqli::real_escape_string()`](https://www.php.net/manual/en/mysqli.real-escape-string.php) (not to be confused with the obsolete [`mysql_real_escape_string()`](https://www.php.net/manual/en/function.mysql-real-escape-string) or [`mysql_escape_string()`](https://www.php.net/manual/en/function.mysql-escape-string.php)). These would prefix any syntactically significant characters (in this case, quotation marks) with a backslash, so it's less likely to affect the database server's interpretation of the query: ```php $student_name = $mysqli->real_escape_string("Robert');DROP TABLE Students;--"); /* Everything is okay now as the dastardly single-quote has been inactivated by the backslash: "INSERT INTO Students (name) VALUES ('Robert\');DROP TABLE Students;--');" */ $query = sprintf("INSERT INTO Students (name) VALUES ('%s')", $student_name); ``` The database server sees the backslash and knows that the single-quote is part of the string content, not its terminating character. However, this was something that you still had to _remember_ to do, making it only half a solution. Additionally, properly escaping the string requires knowledge of the current character set of the connection which is why the `mysqli` object is a required parameter (or the receiver in object-oriented style). And you could always just forget to wrap the string parameter in quotes (`'%s'`) in the first place, which these wouldn't help with. Even when everything is working correctly, formatting dynamic data into a query still requires the database server to re-parse and generate a new query plan with every new variant--caching helps, but is not a silver bullet. #### Prepared Statements to the rescue! These solve both problems (injection and re-parsing) by **completely separating** the query from any dynamic input data. Instead of formatting data into the query, you use a (database-specific) token to signify a value that will be passed separately: ```SQL -- MySQL INSERT INTO Students (name) VALUES(?); -- Postgres and SQLite INSERT INTO Students (name) VALUES($1); ``` The database will substitute a given value when _executing_ the query, long after it's finished parsing it. The database will effectively treat the parameter as a variable. There is, by design, **no way** for a query parameter to modify the SQL of a query, unless you're using some `exec()`-like SQL function that lets you execute a string as a query, but then hopefully you know what you're doing. In fact, parsing and executing prepared statements are explicitly separate steps in pretty much every database's protocol, where the query string, without any values attached, is parsed first and given an identifier, then a separate execution step simply passes that identifier along with the values to substitute. The response from the initial parsing often contains useful metadata about the query, which SQLx's query macros use to great effect (see "How do the query macros work under the hood?" below). Unfortunately, query parameters do not appear to be standardized, as every database has a different syntax. Look through the project for specific examples for your database, and consult your database manual about prepared statements for more information. The syntax SQLite supports is effectively a superset of many databases' syntaxes, including MySQL and Postgres. To simplify our examples, we use the same syntax for Postgres and SQLite; though SQLite's syntax technically allows alphanumeric identifiers, that's not currently exposed in SQLx, and it's expected to be a numeric 1-based index like Postgres. Some databases, like MySQL and PostgreSQL, may have special statements that let the user explicitly create and execute prepared statements (often `PREPARE` and `EXECUTE`, respectively), but most of the time an application, or library like SQLx, will interact with prepared statements using specialized messages in the database's client/server protocol. Prepared statements created through this protocol may or may not be accessible using explicit SQL statements, depending on the database flavor. Since the dynamic data is handled separately, an application only needs to prepare a statement once, and then it can execute it as many times as it wants with all kinds of different data (at least of the same type and number). Prepared statements are generally tracked per-connection, so an application may need to re-prepare a statement several times over its lifetime as it opens new connections. If it uses a connection pool, ideally all connections will eventually have all statements already prepared (assuming a closed set of statements), so the overhead of parsing and generating a query plan is amortized. Query parameters are also usually transmitted in a compact binary format, which saves bandwidth over having to send them as human-readable strings. Because of the obvious security and performance benefits of prepared statements, the design of SQLx tries to make them as easy to use and transparent as possible. The `sqlx::query*()` family of functions, as well as the `sqlx::query*!()` macros, will always prefer prepared statements. This was an explicit goal from day one. SQLx will **never** substitute query parameters for values on the client-side, it will always let the database server handle that. We have concepts for making certain usage patterns easier, like expanding a dynamic list of parameters (e.g. `?, ?, ?, ?, ...`) since MySQL and SQLite don't really support arrays, but will never simply format data into a query implicitly. Our pervasive use of prepared statements can cause some problems with third-party database implementations, e.g. projects like CockroachDB or PGBouncer that support the Postgres protocol but have their own semantics. In this case, you might try setting [`.persistent(false)`](https://docs.rs/sqlx/latest/sqlx/query/struct.Query.html#method.persistent) before executing a query, which will cause the connection not to retain the prepared statement after executing it. Not all SQL statements are allowed in prepared statements, either. As a general rule, DML (Data Manipulation Language, i.e. `SELECT`, `INSERT`, `UPDATE`, `DELETE`) is allowed while DDL (Data Definition Language, e.g. `CREATE TABLE`, `ALTER TABLE`, etc.) is not. Consult your database manual for details. To execute DDL requires using a different API than `query*()` or `query*!()` in SQLx. Ideally, we'd like to encourage you to use SQLx's built-in support for migrations (though that could be better documented, we'll get to it). However, in the event that isn't feasible, or you have different needs, you can execute pretty much any statement, including multiple statements separated by semicolons (`;`), by directly invoking methods of the [`Executor` trait](https://docs.rs/sqlx/latest/sqlx/trait.Executor.html#method.execute) on any type that implements it, and passing your query string, e.g.: ```rust use sqlx::postgres::PgConnection; use sqlx::Executor; let mut conn: PgConnection = connect().await?; conn .execute( "CREATE TABLE IF NOT EXISTS StudentContactInfo (student_id INTEGER, person_name TEXT, relation TEXT, phone TEXT);\ INSERT INTO StudentContactInfo (student_id, person_name, relation, phone) \ SELECT student_id, guardian_name, guardian_relation, guardian_phone FROM Students;\ ALTER TABLE Students DROP guardian_name, guardian_relation, guardian_phone;" ) .await?; ``` This is also pending a redesign to make it easier to discover and utilize. ---------------------------------------------------------------- ### How can I do a `SELECT ... WHERE foo IN (...)` query? In the future SQLx will support binding arrays as a comma-separated list for every database, but unfortunately there's no general solution for that currently in SQLx itself. You would need to manually generate the query, at which point it cannot be used with the macros. However, **in Postgres** you can work around this limitation by binding the arrays directly and using `= ANY()`: ```rust let db: PgPool = /* ... */; let foo_ids: Vec = vec![/* ... */]; let foos = sqlx::query!( "SELECT * FROM foo WHERE id = ANY($1)", // a bug of the parameter typechecking code requires all array parameters to be slices &foo_ids[..] ) .fetch_all(&db) .await?; ``` Even when SQLx gains generic placeholder expansion for arrays, this will still be the optimal way to do it for Postgres, as comma-expansion means each possible length of the array generates a different query (and represents a combinatorial explosion if more than one array is used). Note that you can use any operator that returns a boolean, but beware that `!= ANY($1)` is **not equivalent** to `NOT IN (...)` as it effectively works like this: `lhs != ANY(rhs) -> false OR lhs != rhs[0] OR lhs != rhs[1] OR ... lhs != rhs[length(rhs) - 1]` The equivalent of `NOT IN (...)` would be `!= ALL($1)`: `lhs != ALL(rhs) -> true AND lhs != rhs[0] AND lhs != rhs[1] AND ... lhs != rhs[length(rhs) - 1]` Note that `ANY` using any operator and passed an empty array will return `false`, thus the leading `false OR ...`. Meanwhile, `ALL` with any operator and passed an empty array will return `true`, thus the leading `true AND ...`. See also: [Postgres Manual, Section 9.24: Row and Array Comparisons](https://www.postgresql.org/docs/current/functions-comparisons.html) ----- ### How can I bind an array to a `VALUES()` clause? How can I do bulk inserts? Like the above, SQLx currently does not support this in the general case right now but will in the future. However, **Postgres** also has a feature to save the day here! You can pass an array to `UNNEST()` and it will treat it as a temporary table: ```rust let foo_texts: Vec = vec![/* ... */]; sqlx::query!( // because `UNNEST()` is a generic function, Postgres needs the cast on the parameter here // in order to know what type to expect there when preparing the query "INSERT INTO foo(text_column) SELECT * FROM UNNEST($1::text[])", &foo_texts[..] ) .execute(&db) .await?; ``` `UNNEST()` can also take more than one array, in which case it'll treat each array as a column in the temporary table: ```rust // this solution currently requires each column to be its own vector // in the future we're aiming to allow binding iterators directly as arrays // so you can take a vector of structs and bind iterators mapping to each field let foo_texts: Vec = vec![/* ... */]; let foo_bools: Vec = vec![/* ... */]; let foo_ints: Vec = vec![/* ... */]; let foo_opt_texts: Vec> = vec![/* ... */]; let foo_opt_naive_dts: Vec> = vec![/* ... */] sqlx::query!( " INSERT INTO foo(text_column, bool_column, int_column, opt_text_column, opt_naive_dt_column) SELECT * FROM UNNEST($1::text[], $2::bool[], $3::int8[], $4::text[], $5::timestamp[]) ", &foo_texts[..], &foo_bools[..], &foo_ints[..], // Due to a limitation in how SQLx typechecks query parameters, `Vec>` is unable to be typechecked. // This demonstrates the explicit type override syntax, which tells SQLx not to typecheck these parameters. // See the documentation for `query!()` for more details. &foo_opt_texts as &[Option], &foo_opt_naive_dts as &[Option] ) .execute(&db) .await?; ``` Again, even with comma-expanded lists in the future this will likely still be the most performant way to run bulk inserts with Postgres--at least until we get around to implementing an interface for `COPY FROM STDIN`, though this solution with `UNNEST()` will still be more flexible as you can use it in queries that are more complex than just inserting into a table. Note that if some vectors are shorter than others, `UNNEST` will fill the corresponding columns with `NULL`s to match the longest vector. For example, if `foo_texts` is length 5, `foo_bools` is length 4, `foo_ints` is length 3, the resulting table will look like this: | Row # | `text_column` | `bool_column` | `int_column` | | ----- | -------------- | -------------- | ------------- | | 1 | `foo_texts[0]` | `foo_bools[0]` | `foo_ints[0]` | | 2 | `foo_texts[1]` | `foo_bools[1]` | `foo_ints[1]` | | 3 | `foo_texts[2]` | `foo_bools[2]` | `foo_ints[2]` | | 4 | `foo_texts[3]` | `foo_bools[3]` | `NULL` | | 5 | `foo_texts[4]` | `NULL` | `NULL` | See Also: * [Postgres Manual, Section 7.2.1.4: Table Functions](https://www.postgresql.org/docs/current/queries-table-expressions.html#QUERIES-TABLEFUNCTIONS) * [Postgres Manual, Section 9.19: Array Functions and Operators](https://www.postgresql.org/docs/current/functions-array.html) ---- ### How do I compile with the macros without needing a database, e.g. in CI? The macros support an offline mode which saves data for existing queries to a `.sqlx` directory, so the macros can just read those instead of talking to a database. See the following: * [the docs for `query!()`](https://docs.rs/sqlx/0.5.5/sqlx/macro.query.html#offline-mode-requires-the-offline-feature) * [the README for `sqlx-cli`](sqlx-cli/README.md#enable-building-in-offline-mode-with-query) To keep `.sqlx` up-to-date you need to run `cargo sqlx prepare` before every commit that adds or changes a query; you can do this with a Git pre-commit hook: ```shell $ echo "cargo sqlx prepare > /dev/null 2>&1; git add .sqlx > /dev/null" > .git/hooks/pre-commit ``` Note that this may make committing take some time as it'll cause your project to be recompiled, and as an ergonomic choice it does _not_ block committing if `cargo sqlx prepare` fails. We're working on a way for the macros to save their data to the filesystem automatically which should be part of SQLx 0.7, so your pre-commit hook would then just need to stage the changed files. This can be enabled by creating a directory and setting the `SQLX_OFFLINE_DIR` environment variable to it before compiling. However, this behaviour is not considered stable and it is still recommended to use `cargo sqlx prepare`. ---- ### How do the query macros work under the hood? The macros work by talking to the database at compile time. When a(n) SQL client asks to create a prepared statement from a query string, the response from the server typically includes information about the following: * the number of bind parameters, and their expected types if the database is capable of inferring that * the number, names and types of result columns, as well as the original table and columns names before aliasing In MySQL/MariaDB, we also get boolean flag signaling if a column is `NOT NULL`, however in Postgres and SQLite, we need to do a bit more work to determine whether a column can be `NULL` or not. After preparing, the Postgres driver will first look up the result columns in their source table and check if they have a `NOT NULL` constraint. Then, it will execute `EXPLAIN (VERBOSE, FORMAT JSON) ` to determine which columns come from half-open joins (LEFT and RIGHT joins), which makes a normally `NOT NULL` column nullable. Since the `EXPLAIN VERBOSE` format is not stable or completely documented, this inference isn't perfect. However, it does err on the side of producing false-positives (marking a column nullable when it's `NOT NULL`) to avoid errors at runtime. If you do encounter false-positives please feel free to open an issue; make sure to include your query, any relevant schema as well as the output of `EXPLAIN (VERBOSE, FORMAT JSON) ` which will make this easier to debug. The SQLite driver will pull the bytecode of the prepared statement and step through it to find any instructions that produce a null value for any column in the output. --- ### Why can't SQLx just look at my database schema/migrations and parse the SQL itself? Take a moment and think of the effort that would be required to do that. To implement this for a single database driver, SQLx would need to: * know how to parse SQL, and not just standard SQL but the specific dialect of that particular database * know how to analyze and typecheck SQL queries in the context of the original schema * if inferring schema from migrations it would need to simulate all the schema-changing effects of those migrations This is effectively reimplementing a good chunk of the database server's frontend, _and_ maintaining and ensuring correctness of that reimplementation, including bugs and idiosyncrasies, for the foreseeable future, for _every_ database we intend to support. Even Sisyphus would pity us. ---- ### Why does my project using sqlx query macros not build on docs.rs? Docs.rs doesn't have access to your database, so it needs to be provided prepared queries in a `.sqlx` directory and be instructed to set the `SQLX_OFFLINE` environment variable to true while compiling your project. Luckily for us, docs.rs creates a `DOCS_RS` environment variable that we can access in a custom build script to achieve this functionality. To do so, first, make sure that you have run `cargo sqlx prepare` to generate a `.sqlx` directory in your project. Next, create a file called `build.rs` in the root of your project directory (at the same level as `Cargo.toml`). Add the following code to it: ```rs fn main() { // When building in docs.rs, we want to set SQLX_OFFLINE mode to true if std::env::var_os("DOCS_RS").is_some() { println!("cargo:rustc-env=SQLX_OFFLINE=true"); } } ``` sqlx-0.8.3/LICENSE-APACHE000064400000000000000000000240031046102023000125460ustar 00000000000000Apache 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 APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2020 LaunchBadge, LLC 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.sqlx-0.8.3/LICENSE-MIT000064400000000000000000000020441046102023000122570ustar 00000000000000Copyright (c) 2020 LaunchBadge, LLC 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. sqlx-0.8.3/README.md000064400000000000000000000437531046102023000121160ustar 00000000000000

SQLx

🧰 The Rust SQL Toolkit


Built with ❤️ by The LaunchBadge team

Have a question? Be sure to check the FAQ first!

SQLx is an async, pure Rust SQL crate featuring compile-time checked queries without a DSL. - **Truly Asynchronous**. Built from the ground-up using async/await for maximum concurrency. - **Compile-time checked queries** (if you want). See [SQLx is not an ORM](#sqlx-is-not-an-orm). - **Database Agnostic**. Support for [PostgreSQL], [MySQL], [MariaDB], [SQLite]. - [MSSQL] was supported prior to version 0.7, but has been removed pending a full rewrite of the driver as part of our [SQLx Pro initiative]. - **Pure Rust**. The Postgres and MySQL/MariaDB drivers are written in pure Rust using **zero** unsafe†† code. - **Runtime Agnostic**. Works on different runtimes ([`async-std`] / [`tokio`] / [`actix`]) and TLS backends ([`native-tls`], [`rustls`]). † The SQLite driver uses the libsqlite3 C library as SQLite is an embedded database (the only way we could be pure Rust for SQLite is by porting _all_ of SQLite to Rust). †† SQLx uses `#![forbid(unsafe_code)]` unless the `sqlite` feature is enabled. The SQLite driver directly invokes the SQLite3 API via `libsqlite3-sys`, which requires `unsafe`. [postgresql]: http://postgresql.org/ [sqlite]: https://sqlite.org/ [mysql]: https://www.mysql.com/ [mariadb]: https://www.mariadb.org/ [mssql]: https://www.microsoft.com/en-us/sql-server [SQLx Pro initiative]: https://github.com/launchbadge/sqlx/discussions/1616 --- - Cross-platform. Being native Rust, SQLx will compile anywhere Rust is supported. - Built-in connection pooling with `sqlx::Pool`. - Row streaming. Data is read asynchronously from the database and decoded on demand. - Automatic statement preparation and caching. When using the high-level query API (`sqlx::query`), statements are prepared and cached per connection. - Simple (unprepared) query execution including fetching results into the same `Row` types used by the high-level API. Supports batch execution and returns results from all statements. - Transport Layer Security (TLS) where supported ([MySQL], [MariaDB] and [PostgreSQL]). - Asynchronous notifications using `LISTEN` and `NOTIFY` for [PostgreSQL]. - Nested transactions with support for save points. - `Any` database driver for changing the database driver at runtime. An `AnyPool` connects to the driver indicated by the URL scheme. ## Install SQLx is compatible with the [`async-std`], [`tokio`], and [`actix`] runtimes; and, the [`native-tls`] and [`rustls`] TLS backends. When adding the dependency, you must choose a runtime feature that is `runtime` + `tls`. [`async-std`]: https://github.com/async-rs/async-std [`tokio`]: https://github.com/tokio-rs/tokio [`actix`]: https://github.com/actix/actix-net [`native-tls`]: https://crates.io/crates/native-tls [`rustls`]: https://crates.io/crates/rustls ```toml # Cargo.toml [dependencies] # PICK ONE OF THE FOLLOWING: # tokio (no TLS) sqlx = { version = "0.8", features = [ "runtime-tokio" ] } # tokio + native-tls sqlx = { version = "0.8", features = [ "runtime-tokio", "tls-native-tls" ] } # tokio + rustls with ring and WebPKI CA certificates sqlx = { version = "0.8", features = [ "runtime-tokio", "tls-rustls-ring-webpki" ] } # tokio + rustls with ring and platform's native CA certificates sqlx = { version = "0.8", features = [ "runtime-tokio", "tls-rustls-ring-native-roots" ] } # tokio + rustls with aws-lc-rs sqlx = { version = "0.8", features = [ "runtime-tokio", "tls-rustls-aws-lc-rs" ] } # async-std (no TLS) sqlx = { version = "0.8", features = [ "runtime-async-std" ] } # async-std + native-tls sqlx = { version = "0.8", features = [ "runtime-async-std", "tls-native-tls" ] } # async-std + rustls with ring and WebPKI CA certificates sqlx = { version = "0.8", features = [ "runtime-async-std", "tls-rustls-ring-webpki" ] } # async-std + rustls with ring and platform's native CA certificates sqlx = { version = "0.8", features = [ "runtime-async-std", "tls-rustls-ring-native-roots" ] } # async-std + rustls with aws-lc-rs sqlx = { version = "0.8", features = [ "runtime-async-std", "tls-rustls-aws-lc-rs" ] } ``` #### Cargo Feature Flags For backward-compatibility reasons, the runtime and TLS features can either be chosen together as a single feature, or separately. For forward compatibility, you should use the separate runtime and TLS features as the combination features may be removed in the future. - `runtime-async-std`: Use the `async-std` runtime without enabling a TLS backend. - `runtime-async-std-native-tls`: Use the `async-std` runtime and `native-tls` TLS backend (SOFT-DEPRECATED). - `runtime-async-std-rustls`: Use the `async-std` runtime and `rustls` TLS backend (SOFT-DEPRECATED). - `runtime-tokio`: Use the `tokio` runtime without enabling a TLS backend. - `runtime-tokio-native-tls`: Use the `tokio` runtime and `native-tls` TLS backend (SOFT-DEPRECATED). - `runtime-tokio-rustls`: Use the `tokio` runtime and `rustls` TLS backend (SOFT-DEPRECATED). - Actix-web is fully compatible with Tokio and so a separate runtime feature is no longer needed. - `tls-native-tls`: Use the `native-tls` TLS backend (OpenSSL on *nix, SChannel on Windows, Secure Transport on macOS). - `tls-rustls`: Use the `rustls` TLS backend (cross-platform backend, only supports TLS 1.2 and 1.3). - `postgres`: Add support for the Postgres database server. - `mysql`: Add support for the MySQL/MariaDB database server. - `mssql`: Add support for the MSSQL database server. - `sqlite`: Add support for the self-contained [SQLite](https://sqlite.org/) database engine with SQLite bundled and statically-linked. - `sqlite-unbundled`: The same as above (`sqlite`), but link SQLite from the system instead of the bundled version. * Allows updating SQLite independently of SQLx or using forked versions. * You must have SQLite installed on the system or provide a path to the library at build time. See [the `rusqlite` README](https://github.com/rusqlite/rusqlite?tab=readme-ov-file#notes-on-building-rusqlite-and-libsqlite3-sys) for details. * May result in link errors if the SQLite version is too old. Version `3.20.0` or newer is recommended. * Can increase build time due to the use of bindgen. - `any`: Add support for the `Any` database driver, which can proxy to a database driver at runtime. - `derive`: Add support for the derive family macros, those are `FromRow`, `Type`, `Encode`, `Decode`. - `macros`: Add support for the `query*!` macros, which allows compile-time checked queries. - `migrate`: Add support for the migration management and `migrate!` macro, which allow compile-time embedded migrations. - `uuid`: Add support for UUID (in Postgres). - `chrono`: Add support for date and time types from `chrono`. - `time`: Add support for date and time types from `time` crate (alternative to `chrono`, which is preferred by `query!` macro, if both enabled) - `bstr`: Add support for `bstr::BString`. - `bigdecimal`: Add support for `NUMERIC` using the `bigdecimal` crate. - `rust_decimal`: Add support for `NUMERIC` using the `rust_decimal` crate. - `ipnetwork`: Add support for `INET` and `CIDR` (in postgres) using the `ipnetwork` crate. - `json`: Add support for `JSON` and `JSONB` (in postgres) using the `serde_json` crate. - Offline mode is now always enabled. See [sqlx-cli/README.md][readme-offline]. [readme-offline]: sqlx-cli/README.md#enable-building-in-offline-mode-with-query ## SQLx is not an ORM! SQLx supports **compile-time checked queries**. It does not, however, do this by providing a Rust API or DSL (domain-specific language) for building queries. Instead, it provides macros that take regular SQL as input and ensure that it is valid for your database. The way this works is that SQLx connects to your development DB at compile time to have the database itself verify (and return some info on) your SQL queries. This has some potentially surprising implications: - Since SQLx never has to parse the SQL string itself, any syntax that the development DB accepts can be used (including things added by database extensions) - Due to the different amount of information databases let you retrieve about queries, the extent of SQL verification you get from the query macros depends on the database **If you are looking for an (asynchronous) ORM,** you can check out our new [Ecosystem wiki page](https://github.com/launchbadge/sqlx/wiki/Ecosystem#orms)! [`ormx`]: https://crates.io/crates/ormx [`SeaORM`]: https://github.com/SeaQL/sea-orm ## Usage See the `examples/` folder for more in-depth usage. ### Quickstart ```rust use sqlx::postgres::PgPoolOptions; // use sqlx::mysql::MySqlPoolOptions; // etc. #[async_std::main] // Requires the `attributes` feature of `async-std` // or #[tokio::main] // or #[actix_web::main] async fn main() -> Result<(), sqlx::Error> { // Create a connection pool // for MySQL/MariaDB, use MySqlPoolOptions::new() // for SQLite, use SqlitePoolOptions::new() // etc. let pool = PgPoolOptions::new() .max_connections(5) .connect("postgres://postgres:password@localhost/test").await?; // Make a simple query to return the given parameter (use a question mark `?` instead of `$1` for MySQL/MariaDB) let row: (i64,) = sqlx::query_as("SELECT $1") .bind(150_i64) .fetch_one(&pool).await?; assert_eq!(row.0, 150); Ok(()) } ``` ### Connecting A single connection can be established using any of the database connection types and calling `connect()`. ```rust use sqlx::Connection; let conn = SqliteConnection::connect("sqlite::memory:").await?; ``` Generally, you will want to instead create a connection pool (`sqlx::Pool`) for the application to regulate how many server-side connections it's using. ```rust let pool = MySqlPool::connect("mysql://user:pass@host/database").await?; ``` ### Querying In SQL, queries can be separated into prepared (parameterized) or unprepared (simple). Prepared queries have their query plan _cached_, use a binary mode of communication (lower bandwidth and faster decoding), and utilize parameters to avoid SQL injection. Unprepared queries are simple and intended only for use where a prepared statement will not work, such as various database commands (e.g., `PRAGMA` or `SET` or `BEGIN`). SQLx supports all operations with both types of queries. In SQLx, a `&str` is treated as an unprepared query, and a `Query` or `QueryAs` struct is treated as a prepared query. ```rust // low-level, Executor trait conn.execute("BEGIN").await?; // unprepared, simple query conn.execute(sqlx::query("DELETE FROM table")).await?; // prepared, cached query ``` We should prefer to use the high-level `query` interface whenever possible. To make this easier, there are finalizers on the type to avoid the need to wrap with an executor. ```rust sqlx::query("DELETE FROM table").execute(&mut conn).await?; sqlx::query("DELETE FROM table").execute(&pool).await?; ``` The `execute` query finalizer returns the number of affected rows, if any, and drops all received results. In addition, there are `fetch`, `fetch_one`, `fetch_optional`, and `fetch_all` to receive results. The `Query` type returned from `sqlx::query` will return `Row<'conn>` from the database. Column values can be accessed by ordinal or by name with `row.get()`. As the `Row` retains an immutable borrow on the connection, only one `Row` may exist at a time. The `fetch` query finalizer returns a stream-like type that iterates through the rows in the result sets. ```rust // provides `try_next` use futures::TryStreamExt; // provides `try_get` use sqlx::Row; let mut rows = sqlx::query("SELECT * FROM users WHERE email = ?") .bind(email) .fetch(&mut conn); while let Some(row) = rows.try_next().await? { // map the row into a user-defined domain type let email: &str = row.try_get("email")?; } ``` To assist with mapping the row into a domain type, one of two idioms may be used: ```rust let mut stream = sqlx::query("SELECT * FROM users") .map(|row: PgRow| { // map the row into a user-defined domain type }) .fetch(&mut conn); ``` ```rust #[derive(sqlx::FromRow)] struct User { name: String, id: i64 } let mut stream = sqlx::query_as::<_, User>("SELECT * FROM users WHERE email = ? OR name = ?") .bind(user_email) .bind(user_name) .fetch(&mut conn); ``` Instead of a stream of results, we can use `fetch_one` or `fetch_optional` to request one required or optional result from the database. ### Compile-time verification We can use the macro, `sqlx::query!` to achieve compile-time syntactic and semantic verification of the SQL, with an output to an anonymous record type where each SQL column is a Rust field (using raw identifiers where needed). ```rust let countries = sqlx::query!( " SELECT country, COUNT(*) as count FROM users GROUP BY country WHERE organization = ? ", organization ) .fetch_all(&pool) // -> Vec<{ country: String, count: i64 }> .await?; // countries[0].country // countries[0].count ``` Differences from `query()`: - The input (or bind) parameters must be given all at once (and they are compile-time validated to be the right number and the right type). - The output type is an anonymous record. In the above example the type would be similar to: ```rust { country: String, count: i64 } ``` - The `DATABASE_URL` environment variable must be set at build time to a database which it can prepare queries against; the database does not have to contain any data but must be the same kind (MySQL, Postgres, etc.) and have the same schema as the database you will be connecting to at runtime. For convenience, you can use [a `.env` file][dotenv]1 to set DATABASE_URL so that you don't have to pass it every time: ``` DATABASE_URL=mysql://localhost/my_database ``` [dotenv]: https://github.com/dotenv-rs/dotenv#examples The biggest downside to `query!()` is that the output type cannot be named (due to Rust not officially supporting anonymous records). To address that, there is a `query_as!()` macro that is mostly identical except that you can name the output type. ```rust // no traits are needed struct Country { country: String, count: i64 } let countries = sqlx::query_as!(Country, " SELECT country, COUNT(*) as count FROM users GROUP BY country WHERE organization = ? ", organization ) .fetch_all(&pool) // -> Vec .await?; // countries[0].country // countries[0].count ``` To avoid the need of having a development database around to compile the project even when no modifications (to the database-accessing parts of the code) are done, you can enable "offline mode" to cache the results of the SQL query analysis using the `sqlx` command-line tool. See [sqlx-cli/README.md](./sqlx-cli/README.md#enable-building-in-offline-mode-with-query). Compile-time verified queries do quite a bit of work at compile time. Incremental actions like `cargo check` and `cargo build` can be significantly faster when using an optimized build by putting the following in your `Cargo.toml` (More information in the [Profiles section](https://doc.rust-lang.org/cargo/reference/profiles.html) of The Cargo Book) ```toml [profile.dev.package.sqlx-macros] opt-level = 3 ``` 1 The `dotenv` crate itself appears abandoned as of [December 2021](https://github.com/dotenv-rs/dotenv/issues/74) so we now use the `dotenvy` crate instead. The file format is the same. ## Safety This crate uses `#![forbid(unsafe_code)]` to ensure everything is implemented in 100% Safe Rust. If the `sqlite` feature is enabled, this is downgraded to `#![deny(unsafe_code)]` with `#![allow(unsafe_code)]` on the `sqlx::sqlite` module. There are several places where we interact with the C SQLite API. We try to document each call for the invariants we're assuming. We absolutely welcome auditing of, and feedback on, our unsafe code usage. ## License Licensed under either of - Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) - MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) at your option. ## Contribution Unless you explicitly state otherwise, any Contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. sqlx-0.8.3/benches/sqlite/describe.rs000064400000000000000000000117551046102023000156720ustar 00000000000000use criterion::BenchmarkId; use criterion::Criterion; use criterion::{criterion_group, criterion_main}; use sqlx::sqlite::{Sqlite, SqliteConnection}; use sqlx::{Connection, Executor}; use sqlx_test::new; // Here we have an async function to benchmark async fn do_describe_trivial(db: &std::cell::RefCell) { db.borrow_mut().describe("select 1").await.unwrap(); } async fn do_describe_recursive(db: &std::cell::RefCell) { db.borrow_mut() .describe( r#" WITH RECURSIVE schedule(begin_date) AS MATERIALIZED ( SELECT datetime('2022-10-01') WHERE datetime('2022-10-01') < datetime('2022-11-03') UNION ALL SELECT datetime(begin_date,'+1 day') FROM schedule WHERE datetime(begin_date) < datetime(?2) ) SELECT begin_date FROM schedule GROUP BY begin_date "#, ) .await .unwrap(); } async fn do_describe_insert(db: &std::cell::RefCell) { db.borrow_mut() .describe("INSERT INTO tweet (id, text) VALUES (2, 'Hello') RETURNING *") .await .unwrap(); } async fn do_describe_insert_fks(db: &std::cell::RefCell) { db.borrow_mut() .describe("insert into statements (text) values ('a') returning id") .await .unwrap(); } async fn init_connection() -> SqliteConnection { let mut conn = new::().await.unwrap(); conn.execute( r#" CREATE TEMPORARY TABLE statements ( id integer not null primary key, text text not null ); CREATE TEMPORARY TABLE votes1 (statement_id integer not null references statements(id)); CREATE TEMPORARY TABLE votes2 (statement_id integer not null references statements(id)); CREATE TEMPORARY TABLE votes3 (statement_id integer not null references statements(id)); CREATE TEMPORARY TABLE votes4 (statement_id integer not null references statements(id)); CREATE TEMPORARY TABLE votes5 (statement_id integer not null references statements(id)); CREATE TEMPORARY TABLE votes6 (statement_id integer not null references statements(id)); --CREATE TEMPORARY TABLE votes7 (statement_id integer not null references statements(id)); --CREATE TEMPORARY TABLE votes8 (statement_id integer not null references statements(id)); --CREATE TEMPORARY TABLE votes9 (statement_id integer not null references statements(id)); --CREATE TEMPORARY TABLE votes10 (statement_id integer not null references statements(id)); --CREATE TEMPORARY TABLE votes11 (statement_id integer not null references statements(id)); "#, ) .await .unwrap(); conn } fn describe_trivial(c: &mut Criterion) { let runtime = tokio::runtime::Runtime::new().unwrap(); let db = std::cell::RefCell::new(runtime.block_on(init_connection())); c.bench_with_input( BenchmarkId::new("select", "trivial"), &db, move |b, db_ref| { // Insert a call to `to_async` to convert the bencher to async mode. // The timing loops are the same as with the normal bencher. b.to_async(&runtime).iter(|| do_describe_trivial(db_ref)); }, ); } fn describe_recursive(c: &mut Criterion) { let runtime = tokio::runtime::Runtime::new().unwrap(); let db = std::cell::RefCell::new(runtime.block_on(init_connection())); c.bench_with_input( BenchmarkId::new("select", "recursive"), &db, move |b, db_ref| { // Insert a call to `to_async` to convert the bencher to async mode. // The timing loops are the same as with the normal bencher. b.to_async(&runtime).iter(|| do_describe_recursive(db_ref)); }, ); } fn describe_insert(c: &mut Criterion) { let runtime = tokio::runtime::Runtime::new().unwrap(); let db = std::cell::RefCell::new(runtime.block_on(init_connection())); c.bench_with_input( BenchmarkId::new("insert", "returning"), &db, move |b, db_ref| { // Insert a call to `to_async` to convert the bencher to async mode. // The timing loops are the same as with the normal bencher. b.to_async(&runtime).iter(|| do_describe_insert(db_ref)); }, ); } fn describe_insert_fks(c: &mut Criterion) { let runtime = tokio::runtime::Runtime::new().unwrap(); let db = std::cell::RefCell::new(runtime.block_on(init_connection())); c.bench_with_input(BenchmarkId::new("insert", "fks"), &db, move |b, db_ref| { // Insert a call to `to_async` to convert the bencher to async mode. // The timing loops are the same as with the normal bencher. b.to_async(&runtime).iter(|| do_describe_insert_fks(db_ref)); }); } criterion_group!( benches, describe_trivial, describe_recursive, describe_insert, describe_insert_fks ); criterion_main!(benches); sqlx-0.8.3/clippy.toml000064400000000000000000000006531046102023000130240ustar 00000000000000[[disallowed-methods]] path = "core::cmp::Ord::min" reason = ''' too easy to misread `x.min(y)` as "let the minimum value of `x` be `y`" when it actually means the exact opposite; use `std::cmp::min` instead. ''' [[disallowed-methods]] path = "core::cmp::Ord::max" reason = ''' too easy to misread `x.max(y)` as "let the maximum value of `x` be `y`" when it actually means the exact opposite; use `std::cmp::max` instead. ''' sqlx-0.8.3/contrib/ide/vscode/settings.json000064400000000000000000000000741046102023000170430ustar 00000000000000{ "rust-analyzer.assist.importMergeBehaviour": "last" } sqlx-0.8.3/examples/.gitignore000064400000000000000000000000261046102023000144270ustar 00000000000000sqlite/todos/todos.db sqlx-0.8.3/examples/x.py000075500000000000000000000040211046102023000132620ustar 00000000000000#!/usr/bin/env python3 import sys import os from os import path # base dir of sqlx workspace dir_workspace = path.dirname(path.dirname(path.realpath(__file__))) # dir of tests dir_tests = path.join(dir_workspace, "tests") # extend import path to tests/ sys.path.append(dir_tests) import subprocess import time import argparse from docker import start_database parser = argparse.ArgumentParser() parser.add_argument("-p", "--project") parser.add_argument("-l", "--list-projects", action="store_true") argv, unknown = parser.parse_known_args() def run(command, env=None, cwd=None, display=None): if display: print(f"\x1b[93m $ {display}\x1b[0m") else: print(f"\x1b[93m $ {command}\x1b[0m") res = subprocess.run( command.split(" "), env=dict(**os.environ, **env), cwd=cwd, ) if res.returncode != 0: sys.exit(res.returncode) def sqlx(command, url, cwd=None): run(f"cargo --quiet run -p sqlx-cli --bin sqlx -- {command}", cwd=cwd, env={"DATABASE_URL": url}, display=f"sqlx {command}") def project(name, database=None, driver=None): if argv.list_projects: print(f"{name}") return if argv.project and name != argv.project: return print(f"\x1b[2m # {name}\x1b[0m") env = {} cwd = path.join(dir_workspace, "examples", name) if database is not None: database_url = start_database(driver, database, cwd=cwd) env["DATABASE_URL"] = database_url # show the database url print(f"\x1b[94m @ {database_url}\x1b[0m") # database drop (if exists) sqlx("db drop -y", database_url, cwd=cwd) # database create sqlx("db create", database_url, cwd=cwd) # migrate sqlx("migrate run", database_url, cwd=cwd) # check run("cargo check", cwd=cwd, env=env) # todos project("mysql/todos", driver="mysql_8", database="todos") project("postgres/todos", driver="postgres_12", database="todos") project("sqlite/todos", driver="sqlite", database="todos.db") sqlx-0.8.3/gen-changelog.sh000075500000000000000000000061421046102023000136630ustar 00000000000000# Requires Github CLI and `jq` # Usage: `./gen-changelog.sh YYYY-mm-dd` # Generates changelog entries for all PRs merged on or after the given date. set -e PULLS='[]' CURSOR='null' MIN_MERGED_AT=$(date --date="$1" +%s) while true do # Use the GraphQL API to paginate merged pull requests. # The REST API doesn't allow filtering only merged pull requests. # We scan all merged pull requests from the beginning because it's not unheard of to have a very old PR finally get # merged; e.g. #1081, merged a year and a half after it was opened. if [ "$CURSOR" != "null" ]; then PAGE=$(gh api graphql -f after="$CURSOR" -f query='query($after: String) { repository(owner: "launchbadge", name: "sqlx") { pullRequests(first:100,orderBy: {field:CREATED_AT, direction:ASC},states:MERGED, after: $after) { nodes { number author { login } title url mergedAt } pageInfo { hasNextPage endCursor } } } }'); else PAGE=$(gh api graphql -f query='query { repository(owner: "launchbadge", name: "sqlx") { pullRequests(first:100,orderBy: {field:CREATED_AT, direction:ASC},states:MERGED) { nodes { number author { login } title url mergedAt } pageInfo { hasNextPage endCursor } } } }'); fi CURSOR=$(echo "$PAGE" | jq -r '.data.repository.pullRequests.pageInfo.endCursor'); HAS_NEXT_PAGE=$(echo "$PAGE" | jq '.data.repository.pullRequests.pageInfo.hasNextPage'); PULLS=$(echo "$PAGE" | jq "$PULLS + (.data.repository.pullRequests.nodes | map(select(.mergedAt | fromdate >= $MIN_MERGED_AT)))"); # can't use `"$CURSOR" == 'null'` because the last page still gives a valid cursor if ! $HAS_NEXT_PAGE; then break; fi; done COUNT=$(echo "$PULLS" | jq "length"); echo "Found $COUNT pull requests merged on or after $1\n" if [ -z $COUNT ]; then exit 0; fi; echo "Entries:" echo "$PULLS" | jq -r 'map("* [[#\(.number)]]: \(.title) [[@\(.author.login)]]") | join("\n")' echo "\nLinks:" echo "$PULLS" | jq -r 'map("[#\(.number)]: \(.url)") | join("\n")' echo "\nNew Authors:" DUPE_AUTHORS='' # Generate link entries for new authors at the end of the changelog. echo "$PULLS" | jq -r '.[].author.login' | while read author; do author_url="https://github.com/$author" author_entry="[@$author]: $author_url" # Check if the entry already exists in the changelog or in our list of new authors. if grep -qF "$author_entry" CHANGELOG.md || echo "$DUPE_AUTHORS" | grep -qF "$author_entry"; then continue; fi; DUPE_AUTHORS="$DUPE_AUTHORS$author_entry\n" echo $author_entry done sqlx-0.8.3/rust-toolchain.toml000064400000000000000000000001601046102023000144700ustar 00000000000000# Note: should NOT increase during a minor/patch release cycle [toolchain] channel = "1.78" profile = "minimal" sqlx-0.8.3/src/any/install_drivers_note.md000064400000000000000000000006511046102023000167560ustar 00000000000000 The underlying database drivers are chosen at runtime from the list set via [`install_drivers`][crate::any::install_drivers]. Any use of [`AnyConnection`] or [`AnyPool`] without this will panic. It is recommended to use [`install_default_drivers`][crate::any::install_default_drivers] to activate all currently compiled-in drivers. [`AnyConnection`]: sqlx_core::any::AnyConnection [`AnyPool`]: sqlx_core::any::AnyPool sqlx-0.8.3/src/any/mod.rs000064400000000000000000000031301046102023000133230ustar 00000000000000//! **SEE DOCUMENTATION BEFORE USE**. Runtime-generic database driver. #![doc = include_str!("install_drivers_note.md")] use std::sync::Once; pub use sqlx_core::any::driver::install_drivers; pub use sqlx_core::any::{ Any, AnyArguments, AnyConnectOptions, AnyExecutor, AnyPoolOptions, AnyQueryResult, AnyRow, AnyStatement, AnyTransactionManager, AnyTypeInfo, AnyTypeInfoKind, AnyValue, AnyValueRef, }; #[allow(deprecated)] pub use sqlx_core::any::AnyKind; pub(crate) mod reexports { /// **SEE DOCUMENTATION BEFORE USE**. Type alias for `Pool`. #[doc = include_str!("install_drivers_note.md")] pub use sqlx_core::any::AnyPool; /// **SEE DOCUMENTATION BEFORE USE**. Runtime-generic database connection. #[doc = include_str!("install_drivers_note.md")] pub use sqlx_core::any::AnyConnection; } /// Install all currently compiled-in drivers for [`AnyConnection`] to use. /// /// May be called multiple times; only the first call will install drivers, subsequent calls /// will have no effect. /// /// ### Panics /// If [`install_drivers`] has already been called *not* through this function. /// /// [`AnyConnection`]: sqlx_core::any::AnyConnection pub fn install_default_drivers() { static ONCE: Once = Once::new(); ONCE.call_once(|| { install_drivers(&[ #[cfg(feature = "mysql")] sqlx_mysql::any::DRIVER, #[cfg(feature = "postgres")] sqlx_postgres::any::DRIVER, #[cfg(feature = "_sqlite")] sqlx_sqlite::any::DRIVER, ]) .expect("non-default drivers already installed") }); } sqlx-0.8.3/src/lib.md000064400000000000000000000071761046102023000125150ustar 00000000000000The async SQL toolkit for Rust, built with ❤️ by [the LaunchBadge team]. See our [README] to get started or [browse our example projects]. Have a question? [Check our FAQ] or [open a discussion]. ### Runtime Support SQLx supports both the [Tokio] and [async-std] runtimes. You choose which runtime SQLx uses by default by enabling one of the following features: * `runtime-async-std` * `runtime-tokio` The `runtime-actix` feature also exists but is an alias of `runtime-tokio`. If more than one runtime feature is enabled, the Tokio runtime is used if a Tokio context exists on the current thread, i.e. [`tokio::runtime::Handle::try_current()`] returns `Ok`; `async-std` is used otherwise. Note that while SQLx no longer produces a compile error if zero or multiple runtime features are enabled, which is useful for libraries building on top of it, **the use of nearly any async function in the API will panic without at least one runtime feature enabled**. The chief exception is the SQLite driver, which is runtime-agnostic, including its integration with the query macros. However, [`SqlitePool`][crate::sqlite::SqlitePool] _does_ require runtime support for timeouts and spawning internal management tasks. ### TLS Support For securely communicating with SQL servers over an untrusted network connection such as the internet, you can enable Transport Layer Security (TLS) by enabling one of the following features: * `tls-native-tls`: Enables the [`native-tls`] backend which uses the OS-native TLS capabilities: * SecureTransport on macOS. * SChannel on Windows. * OpenSSL on all other platforms. * `tls-rustls`: Enables the [rustls] backend, a cross-platform TLS library. * Only supports TLS revisions 1.2 and 1.3. * If you get `HandshakeFailure` errors when using this feature, it likely means your database server does not support these newer revisions. This might be resolved by enabling or switching to the `tls-native-tls` feature. * rustls supports several providers of cryptographic primitives. The default (enabled when you use the `tls-rustls` feature or `tls-rustls-ring`) is the `ring` provider, which has fewer build-time dependencies but also has fewer features. Alternatively, you can use `tls-rustls-aws-lc-rs` to use the `aws-lc-rs` provider, which enables additional cipher suite support at the cost of more onerous build requirements (depending on platform support). If more than one TLS feature is enabled, the `tls-native-tls` feature takes precedent so that it is only necessary to enable it to see if it resolves the `HandshakeFailure` error without disabling `tls-rustls`. Consult the user manual for your database to find the TLS versions it supports. If your connection configuration requires a TLS upgrade but TLS support was not enabled, the connection attempt will return an error. The legacy runtime+TLS combination feature flags are still supported, but for forward-compatibility, use of the separate runtime and TLS feature flags is recommended. [the LaunchBadge team]: https://www.launchbadge.com [README]: https://www.github.com/launchbadge/sqlx/tree/main/README.md [browse our example projects]: https://www.github.com/launchbadge/sqlx/tree/main/examples [Check our FAQ]: https://www.github.com/launchbadge/sqlx/tree/main/FAQ.md [open a discussion]: https://github.com/launchbadge/sqlx/discussions/new?category=q-a [Tokio]: https://www.tokio.rs [async-std]: https://www.async.rs [`tokio::runtime::Handle::try_current()`]: https://docs.rs/tokio/latest/tokio/runtime/struct.Handle.html#method.try_current [`native-tls`]: https://docs.rs/native-tls/latest/native_tls/ [rustls]: https://docs.rs/rustls/latest/rustls/ sqlx-0.8.3/src/lib.rs000064400000000000000000000111121046102023000125220ustar 00000000000000#![cfg_attr(docsrs, feature(doc_cfg))] #![doc = include_str!("lib.md")] pub use sqlx_core::acquire::Acquire; pub use sqlx_core::arguments::{Arguments, IntoArguments}; pub use sqlx_core::column::Column; pub use sqlx_core::column::ColumnIndex; pub use sqlx_core::connection::{ConnectOptions, Connection}; pub use sqlx_core::database::{self, Database}; pub use sqlx_core::describe::Describe; pub use sqlx_core::executor::{Execute, Executor}; pub use sqlx_core::from_row::FromRow; pub use sqlx_core::pool::{self, Pool}; #[doc(hidden)] pub use sqlx_core::query::query_with_result as __query_with_result; pub use sqlx_core::query::{query, query_with}; pub use sqlx_core::query_as::{query_as, query_as_with}; pub use sqlx_core::query_builder::{self, QueryBuilder}; #[doc(hidden)] pub use sqlx_core::query_scalar::query_scalar_with_result as __query_scalar_with_result; pub use sqlx_core::query_scalar::{query_scalar, query_scalar_with}; pub use sqlx_core::raw_sql::{raw_sql, RawSql}; pub use sqlx_core::row::Row; pub use sqlx_core::statement::Statement; pub use sqlx_core::transaction::{Transaction, TransactionManager}; pub use sqlx_core::type_info::TypeInfo; pub use sqlx_core::types::Type; pub use sqlx_core::value::{Value, ValueRef}; pub use sqlx_core::Either; #[doc(inline)] pub use sqlx_core::error::{self, Error, Result}; #[cfg(feature = "migrate")] pub use sqlx_core::migrate; #[cfg(feature = "mysql")] #[cfg_attr(docsrs, doc(cfg(feature = "mysql")))] #[doc(inline)] pub use sqlx_mysql::{ self as mysql, MySql, MySqlConnection, MySqlExecutor, MySqlPool, MySqlTransaction, }; #[cfg(feature = "postgres")] #[cfg_attr(docsrs, doc(cfg(feature = "postgres")))] #[doc(inline)] pub use sqlx_postgres::{ self as postgres, PgConnection, PgExecutor, PgPool, PgTransaction, Postgres, }; #[cfg(feature = "_sqlite")] #[cfg_attr(docsrs, doc(cfg(feature = "_sqlite")))] #[doc(inline)] pub use sqlx_sqlite::{ self as sqlite, Sqlite, SqliteConnection, SqliteExecutor, SqlitePool, SqliteTransaction, }; #[cfg(feature = "any")] #[cfg_attr(docsrs, doc(cfg(feature = "any")))] pub use crate::any::{reexports::*, Any, AnyExecutor}; #[cfg(any(feature = "derive", feature = "macros"))] #[doc(hidden)] pub extern crate sqlx_macros; // derives #[cfg(feature = "derive")] #[doc(hidden)] pub use sqlx_macros::{FromRow, Type}; // We can't do our normal facade approach with an attribute, but thankfully we can now // have docs out-of-line quite easily. #[doc = include_str!("macros/test.md")] #[cfg(feature = "macros")] pub use sqlx_macros::test; #[doc(hidden)] #[cfg(feature = "migrate")] pub use sqlx_core::testing; #[doc(hidden)] pub use sqlx_core::rt::test_block_on; #[cfg(feature = "any")] pub mod any; #[cfg(feature = "macros")] mod macros; // macro support #[cfg(feature = "macros")] #[doc(hidden)] pub mod ty_match; #[cfg(feature = "macros")] #[doc(hidden)] pub mod spec_error; #[doc(hidden)] pub use sqlx_core::rt as __rt; /// Conversions between Rust and SQL types. /// /// To see how each SQL type maps to a Rust type, see the corresponding `types` module for each /// database: /// /// * Postgres: [postgres::types] /// * MySQL: [mysql::types] /// * SQLite: [sqlite::types] /// /// Any external types that have had [`Type`] implemented for, are re-exported in this module /// for convenience as downstream users need to use a compatible version of the external crate /// to take advantage of the implementation. /// /// [`Type`]: types::Type pub mod types { pub use sqlx_core::types::*; #[cfg(feature = "derive")] #[doc(hidden)] pub use sqlx_macros::Type; } /// Provides [`Encode`] for encoding values for the database. pub mod encode { pub use sqlx_core::encode::{Encode, IsNull}; #[cfg(feature = "derive")] #[doc(hidden)] pub use sqlx_macros::Encode; } pub use self::encode::Encode; /// Provides [`Decode`] for decoding values from the database. pub mod decode { pub use sqlx_core::decode::Decode; #[cfg(feature = "derive")] #[doc(hidden)] pub use sqlx_macros::Decode; } pub use self::decode::Decode; /// Types and traits for the `query` family of functions and macros. pub mod query { pub use sqlx_core::query::{Map, Query}; pub use sqlx_core::query_as::QueryAs; pub use sqlx_core::query_scalar::QueryScalar; } /// Convenience re-export of common traits. pub mod prelude { pub use super::Acquire; pub use super::ConnectOptions; pub use super::Connection; pub use super::Decode; pub use super::Encode; pub use super::Executor; pub use super::FromRow; pub use super::IntoArguments; pub use super::Row; pub use super::Statement; pub use super::Type; } sqlx-0.8.3/src/macros/mod.rs000064400000000000000000001102431046102023000140240ustar 00000000000000/// Statically checked SQL query with `println!()` style syntax. /// /// This expands to an instance of [`query::Map`][crate::query::Map] that outputs an ad-hoc anonymous /// struct type, if the query has at least one output column that is not `Void`, or `()` (unit) otherwise: /// /// ```rust,ignore /// # use sqlx::Connect; /// # #[cfg(all(feature = "mysql", feature = "_rt-async-std"))] /// # #[async_std::main] /// # async fn main() -> sqlx::Result<()>{ /// # let db_url = dotenvy::var("DATABASE_URL").expect("DATABASE_URL must be set"); /// # /// # if !(db_url.starts_with("mysql") || db_url.starts_with("mariadb")) { return Ok(()) } /// # let mut conn = sqlx::MySqlConnection::connect(db_url).await?; /// // let mut conn = ; /// let account = sqlx::query!("select (1) as id, 'Herp Derpinson' as name") /// .fetch_one(&mut conn) /// .await?; /// /// // anonymous struct has `#[derive(Debug)]` for convenience /// println!("{account:?}"); /// println!("{}: {}", account.id, account.name); /// /// # Ok(()) /// # } /// # /// # #[cfg(any(not(feature = "mysql"), not(feature = "_rt-async-std")))] /// # fn main() {} /// ``` /// /// The output columns will be mapped to their corresponding Rust types. /// See the documentation for your database for details: /// /// * Postgres: [crate::postgres::types] /// * MySQL: [crate::mysql::types] /// * Note: due to wire protocol limitations, the query macros do not know when /// a column should be decoded as `bool`. It will be inferred to be `i8` instead. /// See the link above for details. /// * SQLite: [crate::sqlite::types] /// /// **The method you want to call on the result depends on how many rows you're expecting.** /// /// | Number of Rows | Method to Call* | Returns | Notes | /// |----------------| ----------------------------|-----------------------------------------------------|-------| /// | None† | `.execute(...).await` | `sqlx::Result` | For `INSERT`/`UPDATE`/`DELETE` without `RETURNING`. | /// | Zero or One | `.fetch_optional(...).await`| `sqlx::Result>` | Extra rows are ignored. | /// | Exactly One | `.fetch_one(...).await` | `sqlx::Result<{adhoc struct}>` | Errors if no rows were returned. Extra rows are ignored. Aggregate queries, use this. | /// | At Least One | `.fetch(...)` | `impl Stream>` | Call `.try_next().await` to get each row result. | /// | Multiple | `.fetch_all(...)` | `sqlx::Result>` | | /// /// \* All methods accept one of `&mut {connection type}`, `&mut Transaction` or `&Pool`. /// † Only callable if the query returns no columns; otherwise it's assumed the query *may* return at least one row. /// ## Requirements /// * The `DATABASE_URL` environment variable must be set at build-time to point to a database /// server with the schema that the query string will be checked against. /// All variants of `query!()` use [dotenv]1 so this can be in a `.env` file instead. /// /// * Or, `.sqlx` must exist at the workspace root. See [Offline Mode](#offline-mode-requires-the-offline-feature) /// below. /// /// * The query must be a string literal, or concatenation of string literals using `+` (useful /// for queries generated by macro), or else it cannot be introspected (and thus cannot be dynamic /// or the result of another macro). /// /// * The `QueryAs` instance will be bound to the same database type as `query!()` was compiled /// against (e.g. you cannot build against a Postgres database and then run the query against /// a MySQL database). /// /// * The schema of the database URL (e.g. `postgres://` or `mysql://`) will be used to /// determine the database type. /// /// 1 The `dotenv` crate itself appears abandoned as of [December 2021](https://github.com/dotenv-rs/dotenv/issues/74) /// so we now use the [dotenvy] crate instead. The file format is the same. /// /// [dotenv]: https://crates.io/crates/dotenv /// [dotenvy]: https://crates.io/crates/dotenvy /// ## Query Arguments /// Like `println!()` and the other formatting macros, you can add bind parameters to your SQL /// and this macro will typecheck passed arguments and error on missing ones: /// /// ```rust,ignore /// # use sqlx::Connect; /// # #[cfg(all(feature = "mysql", feature = "_rt-async-std"))] /// # #[async_std::main] /// # async fn main() -> sqlx::Result<()>{ /// # let db_url = dotenvy::var("DATABASE_URL").expect("DATABASE_URL must be set"); /// # /// # if !(db_url.starts_with("mysql") || db_url.starts_with("mariadb")) { return Ok(()) } /// # let mut conn = sqlx::mysql::MySqlConnection::connect(db_url).await?; /// // let mut conn = ; /// let account = sqlx::query!( /// // just pretend "accounts" is a real table /// "select * from (select (1) as id, 'Herp Derpinson' as name) accounts where id = ?", /// 1i32 /// ) /// .fetch_one(&mut conn) /// .await?; /// /// println!("{account:?}"); /// println!("{}: {}", account.id, account.name); /// # Ok(()) /// # } /// # /// # #[cfg(any(not(feature = "mysql"), not(feature = "_rt-async-std")))] /// # fn main() {} /// ``` /// /// Bind parameters in the SQL string are specific to the database backend: /// /// * Postgres: `$N` where `N` is the 1-based positional argument index /// * MySQL/SQLite: `?` which matches arguments in order that it appears in the query /// /// ## Nullability: Bind Parameters /// For a given expected type `T`, both `T` and `Option` are allowed (as well as either /// behind references). `Option::None` will be bound as `NULL`, so if binding a type behind `Option` /// be sure your query can support it. /// /// Note, however, if binding in a `where` clause, that equality comparisons with `NULL` may not /// work as expected; instead you must use `IS NOT NULL` or `IS NULL` to check if a column is not /// null or is null, respectively. /// /// In Postgres and MySQL you may also use `IS [NOT] DISTINCT FROM` to compare with a possibly /// `NULL` value. In MySQL `IS NOT DISTINCT FROM` can be shortened to `<=>`. /// In SQLite you can use `IS` or `IS NOT`. Note that operator precedence may be different. /// /// ## Nullability: Output Columns /// In most cases, the database engine can tell us whether or not a column may be `NULL`, and /// the `query!()` macro adjusts the field types of the returned struct accordingly. /// /// For Postgres, this only works for columns which come directly from actual tables, /// as the implementation will need to query the table metadata to find if a given column /// has a `NOT NULL` constraint. Columns that do not have a `NOT NULL` constraint or are the result /// of an expression are assumed to be nullable and so `Option` is used instead of `T`. /// /// For MySQL, the implementation looks at [the `NOT_NULL` flag](https://dev.mysql.com/doc/dev/mysql-server/8.0.12/group__group__cs__column__definition__flags.html#ga50377f5ca5b3e92f3931a81fe7b44043) /// of [the `ColumnDefinition` structure in `COM_QUERY_OK`](https://dev.mysql.com/doc/internals/en/com-query-response.html#column-definition): /// if it is set, `T` is used; if it is not set, `Option` is used. /// /// MySQL appears to be capable of determining the nullability of a result column even if it /// is the result of an expression, depending on if the expression may in any case result in /// `NULL` which then depends on the semantics of what functions are used. Consult the MySQL /// manual for the functions you are using to find the cases in which they return `NULL`. /// /// For SQLite we perform a similar check to Postgres, looking for `NOT NULL` constraints /// on columns that come from tables. However, for SQLite we also can step through the output /// of `EXPLAIN` to identify columns that may or may not be `NULL`. /// /// To override the nullability of an output column, [see below](#type-overrides-output-columns). /// /// ## Type Overrides: Bind Parameters (Postgres only) /// For typechecking of bind parameters, casts using `as` are treated as overrides for the inferred /// types of bind parameters and no typechecking is emitted: /// /// ```rust,ignore /// #[derive(sqlx::Type)] /// #[sqlx(transparent)] /// struct MyInt4(i32); /// /// let my_int = MyInt4(1); /// /// sqlx::query!("select $1::int4 as id", my_int as MyInt4) /// ``` /// /// Using `expr as _` simply signals to the macro to not type-check that bind expression, /// and then that syntax is stripped from the expression so as to not trigger type errors. /// /// ## Type Overrides: Output Columns /// Type overrides are also available for output columns, utilizing the SQL standard's support /// for arbitrary text in column names: /// /// ##### Force Not-Null /// Selecting a column `foo as "foo!"` (Postgres / SQLite) or `` foo as `foo!` `` (MySQL) overrides /// inferred nullability and forces the column to be treated as `NOT NULL`; this is useful e.g. for /// selecting expressions in Postgres where we cannot infer nullability: /// /// ```rust,ignore /// # async fn main() { /// # let mut conn = panic!(); /// // Postgres: using a raw query string lets us use unescaped double-quotes /// // Note that this query wouldn't work in SQLite as we still don't know the exact type of `id` /// let record = sqlx::query!(r#"select 1 as "id!""#) // MySQL: use "select 1 as `id!`" instead /// .fetch_one(&mut conn) /// .await?; /// /// // For Postgres this would have been inferred to be Option instead /// assert_eq!(record.id, 1i32); /// # } /// /// ``` /// /// ##### Force Nullable /// Selecting a column `foo as "foo?"` (Postgres / SQLite) or `` foo as `foo?` `` (MySQL) overrides /// inferred nullability and forces the column to be treated as nullable; this is provided mainly /// for symmetry with `!`. /// /// ```rust,ignore /// # async fn main() { /// # let mut conn = panic!(); /// // Postgres/SQLite: /// let record = sqlx::query!(r#"select 1 as "id?""#) // MySQL: use "select 1 as `id?`" instead /// .fetch_one(&mut conn) /// .await?; /// /// // For Postgres this would have been inferred to be Option anyway /// // but this is just a basic example /// assert_eq!(record.id, Some(1i32)); /// # } /// ``` /// /// MySQL should be accurate with regards to nullability as it directly tells us when a column is /// expected to never be `NULL`. Any mistakes should be considered a bug in MySQL. /// /// However, inference in SQLite and Postgres is more fragile as it depends primarily on observing /// `NOT NULL` constraints on columns. If a `NOT NULL` column is brought in by a `LEFT JOIN` then /// that column may be `NULL` if its row does not satisfy the join condition. Similarly, a /// `FULL JOIN` or `RIGHT JOIN` may generate rows from the primary table that are all `NULL`. /// /// Unfortunately, the result of mistakes in inference is a `UnexpectedNull` error at runtime. /// /// In Postgres, we patch up this inference by analyzing `EXPLAIN VERBOSE` output (which is not /// well documented, is highly dependent on the query plan that Postgres generates, and may differ /// between releases) to find columns that are the result of left/right/full outer joins. This /// analysis errs on the side of producing false positives (marking columns nullable that are not /// in practice) but there are likely edge cases that it does not cover yet. /// /// Using `?` as an override we can fix this for columns we know to be nullable in practice: /// /// ```rust,ignore /// # async fn main() { /// # let mut conn = panic!(); /// // Ironically this is the exact column we primarily look at to determine nullability in Postgres /// let record = sqlx::query!( /// r#"select attnotnull as "attnotnull?" from (values (1)) ids left join pg_attribute on false"# /// ) /// .fetch_one(&mut conn) /// .await?; /// /// // Although we do our best, under Postgres this might have been inferred to be `bool` /// // In that case, we would have gotten an error /// assert_eq!(record.attnotnull, None); /// # } /// ``` /// /// If you find that you need to use this override, please open an issue with a query we can use /// to reproduce the problem. For Postgres users, especially helpful would be the output of /// `EXPLAIN (VERBOSE, FORMAT JSON) ` with bind parameters substituted in the query /// (as the exact value of bind parameters can change the query plan) /// and the definitions of any relevant tables (or sufficiently anonymized equivalents). /// /// ##### Force a Different/Custom Type /// Selecting a column `foo as "foo: T"` (Postgres / SQLite) or `` foo as `foo: T` `` (MySQL) /// overrides the inferred type which is useful when selecting user-defined custom types /// (dynamic type checking is still done so if the types are incompatible this will be an error /// at runtime instead of compile-time). Note that this syntax alone doesn't override inferred nullability, /// but it is compatible with the forced not-null and forced nullable annotations: /// /// ```rust,ignore /// # async fn main() { /// # let mut conn = panic!(); /// #[derive(sqlx::Type)] /// #[sqlx(transparent)] /// struct MyInt4(i32); /// /// let my_int = MyInt4(1); /// /// // Postgres/SQLite /// sqlx::query!(r#"select 1 as "id!: MyInt4""#) // MySQL: use "select 1 as `id: MyInt4`" instead /// .fetch_one(&mut conn) /// .await?; /// /// // For Postgres this would have been inferred to be `Option`, MySQL/SQLite `i32` /// // Note that while using `id: MyInt4` (without the `!`) would work the same for MySQL/SQLite, /// // Postgres would expect `Some(MyInt4(1))` and the code wouldn't compile /// assert_eq!(record.id, MyInt4(1)); /// # } /// ``` /// /// ##### Overrides cheatsheet /// /// | Syntax | Nullability | Type | /// | --------- | --------------- | ---------- | /// | `foo!` | Forced not-null | Inferred | /// | `foo?` | Forced nullable | Inferred | /// | `foo: T` | Inferred | Overridden | /// | `foo!: T` | Forced not-null | Overridden | /// | `foo?: T` | Forced nullable | Overridden | /// /// ## Offline Mode /// The macros can be configured to not require a live database connection for compilation, /// but it requires a couple extra steps: /// /// * Run `cargo install sqlx-cli`. /// * In your project with `DATABASE_URL` set (or in a `.env` file) and the database server running, /// run `cargo sqlx prepare`. /// * Check the generated `.sqlx` directory into version control. /// * Don't have `DATABASE_URL` set during compilation. /// /// Your project can now be built without a database connection (you must omit `DATABASE_URL` or /// else it will still try to connect). To update the generated file simply run `cargo sqlx prepare` /// again. /// /// To ensure that your `.sqlx` directory is kept up-to-date, both with the queries in your /// project and your database schema itself, run /// `cargo install sqlx-cli && cargo sqlx prepare --check` in your Continuous Integration script. /// /// See [the README for `sqlx-cli`](https://crates.io/crates/sqlx-cli) for more information. /// /// ## See Also /// * [`query_as!`][`crate::query_as!`] if you want to use a struct you can name, /// * [`query_file!`][`crate::query_file!`] if you want to define the SQL query out-of-line, /// * [`query_file_as!`][`crate::query_file_as!`] if you want both of the above. #[macro_export] #[cfg_attr(docsrs, doc(cfg(feature = "macros")))] macro_rules! query ( // in Rust 1.45 we can now invoke proc macros in expression position ($query:expr) => ({ $crate::sqlx_macros::expand_query!(source = $query) }); // RFC: this semantically should be `$($args:expr),*` (with `$(,)?` to allow trailing comma) // but that doesn't work in 1.45 because `expr` fragments get wrapped in a way that changes // their hygiene, which is fixed in 1.46 so this is technically just a temp. workaround. // My question is: do we care? // I was hoping using the `expr` fragment might aid code completion but it doesn't in my // experience, at least not with IntelliJ-Rust at the time of writing (version 0.3.126.3220-201) // so really the only benefit is making the macros _slightly_ self-documenting, but it's // not like it makes them magically understandable at-a-glance. ($query:expr, $($args:tt)*) => ({ $crate::sqlx_macros::expand_query!(source = $query, args = [$($args)*]) }) ); /// A variant of [`query!`][`crate::query!`] which does not check the input or output types. This still does parse /// the query to ensure it's syntactically and semantically valid for the current database. #[macro_export] #[cfg_attr(docsrs, doc(cfg(feature = "macros")))] macro_rules! query_unchecked ( ($query:expr) => ({ $crate::sqlx_macros::expand_query!(source = $query, checked = false) }); ($query:expr, $($args:tt)*) => ({ $crate::sqlx_macros::expand_query!(source = $query, args = [$($args)*], checked = false) }) ); /// A variant of [`query!`][`crate::query!`] where the SQL query is stored in a separate file. /// /// Useful for large queries and potentially cleaner than multiline strings. /// /// The syntax and requirements (see [`query!`][`crate::query!`]) are the same except the SQL /// string is replaced by a file path. /// /// The file must be relative to the project root (the directory containing `Cargo.toml`), /// unlike `include_str!()` which uses compiler internals to get the path of the file where it /// was invoked. /// /// ----- /// /// `examples/queries/account-by-id.sql`: /// ```text /// select * from (select (1) as id, 'Herp Derpinson' as name) accounts /// where id = ? /// ``` /// /// `src/my_query.rs`: /// ```rust,ignore /// # use sqlx::Connect; /// # #[cfg(all(feature = "mysql", feature = "_rt-async-std"))] /// # #[async_std::main] /// # async fn main() -> sqlx::Result<()>{ /// # let db_url = dotenvy::var("DATABASE_URL").expect("DATABASE_URL must be set"); /// # /// # if !(db_url.starts_with("mysql") || db_url.starts_with("mariadb")) { return Ok(()) } /// # let mut conn = sqlx::MySqlConnection::connect(db_url).await?; /// let account = sqlx::query_file!("tests/test-query-account-by-id.sql", 1i32) /// .fetch_one(&mut conn) /// .await?; /// /// println!("{account:?}"); /// println!("{}: {}", account.id, account.name); /// /// # Ok(()) /// # } /// # /// # #[cfg(any(not(feature = "mysql"), not(feature = "_rt-async-std")))] /// # fn main() {} /// ``` #[macro_export] #[cfg_attr(docsrs, doc(cfg(feature = "macros")))] macro_rules! query_file ( ($path:literal) => ({ $crate::sqlx_macros::expand_query!(source_file = $path) }); ($path:literal, $($args:tt)*) => ({ $crate::sqlx_macros::expand_query!(source_file = $path, args = [$($args)*]) }) ); /// A variant of [`query_file!`][`crate::query_file!`] which does not check the input or output /// types. This still does parse the query to ensure it's syntactically and semantically valid /// for the current database. #[macro_export] #[cfg_attr(docsrs, doc(cfg(feature = "macros")))] macro_rules! query_file_unchecked ( ($path:literal) => ({ $crate::sqlx_macros::expand_query!(source_file = $path, checked = false) }); ($path:literal, $($args:tt)*) => ({ $crate::sqlx_macros::expand_query!(source_file = $path, args = [$($args)*], checked = false) }) ); /// A variant of [`query!`][`crate::query!`] which takes a path to an explicitly defined struct /// as the output type. /// /// This lets you return the struct from a function or add your own trait implementations. /// /// **This macro does not use [`FromRow`][crate::FromRow]**; in fact, no trait implementations are /// required at all, though this may change in future versions. /// /// The macro maps rows using a struct literal where the names of columns in the query are expected /// to be the same as the fields of the struct (but the order does not need to be the same). /// The types of the columns are based on the query and not the corresponding fields of the struct, /// so this is type-safe as well. /// /// This enforces a few things: /// * The query must output at least one column. /// * The column names of the query must match the field names of the struct. /// * The field types must be the Rust equivalent of their SQL counterparts; see the corresponding /// module for your database for mappings: /// * Postgres: [crate::postgres::types] /// * MySQL: [crate::mysql::types] /// * Note: due to wire protocol limitations, the query macros do not know when /// a column should be decoded as `bool`. It will be inferred to be `i8` instead. /// See the link above for details. /// * SQLite: [crate::sqlite::types] /// * If a column may be `NULL`, the corresponding field's type must be wrapped in `Option<_>`. /// * Neither the query nor the struct may have unused fields. /// /// The only modification to the `query!()` syntax is that the struct name is given before the SQL /// string: /// ```rust,ignore /// # use sqlx::Connect; /// # #[cfg(all(feature = "mysql", feature = "_rt-async-std"))] /// # #[async_std::main] /// # async fn main() -> sqlx::Result<()>{ /// # let db_url = dotenvy::var("DATABASE_URL").expect("DATABASE_URL must be set"); /// # /// # if !(db_url.starts_with("mysql") || db_url.starts_with("mariadb")) { return Ok(()) } /// # let mut conn = sqlx::MySqlConnection::connect(db_url).await?; /// #[derive(Debug)] /// struct Account { /// id: i32, /// name: String /// } /// /// // let mut conn = ; /// let account = sqlx::query_as!( /// Account, /// "select * from (select (1) as id, 'Herp Derpinson' as name) accounts where id = ?", /// 1i32 /// ) /// .fetch_one(&mut conn) /// .await?; /// /// println!("{account:?}"); /// println!("{}: {}", account.id, account.name); /// /// # Ok(()) /// # } /// # /// # #[cfg(any(not(feature = "mysql"), not(feature = "_rt-async-std")))] /// # fn main() {} /// ``` /// /// **The method you want to call depends on how many rows you're expecting.** /// /// | Number of Rows | Method to Call* | Returns (`T` being the given struct) | Notes | /// |----------------| ----------------------------|----------------------------------------|-------| /// | Zero or One | `.fetch_optional(...).await`| `sqlx::Result>` | Extra rows are ignored. | /// | Exactly One | `.fetch_one(...).await` | `sqlx::Result` | Errors if no rows were returned. Extra rows are ignored. Aggregate queries, use this. | /// | At Least One | `.fetch(...)` | `impl Stream>` | Call `.try_next().await` to get each row result. | /// | Multiple | `.fetch_all(...)` | `sqlx::Result>` | | /// /// \* All methods accept one of `&mut {connection type}`, `&mut Transaction` or `&Pool`. /// (`.execute()` is omitted as this macro requires at least one column to be returned.) /// /// ### Column Type Override: Infer from Struct Field /// In addition to the column type overrides supported by [`query!`][`crate::query!`], /// [`query_as!()`][`crate::query_as!`] supports an /// additional override option: /// /// If you select a column `foo as "foo: _"` (Postgres/SQLite) or `` foo as `foo: _` `` (MySQL) /// it causes that column to be inferred based on the type of the corresponding field in the given /// record struct. Runtime type-checking is still done so an error will be emitted if the types /// are not compatible. /// /// This allows you to override the inferred type of a column to instead use a custom-defined type: /// /// ```rust,ignore /// #[derive(sqlx::Type)] /// #[sqlx(transparent)] /// struct MyInt4(i32); /// /// struct Record { /// id: MyInt4, /// } /// /// let my_int = MyInt4(1); /// /// // Postgres/SQLite /// sqlx::query_as!(Record, r#"select 1 as "id: _""#) // MySQL: use "select 1 as `id: _`" instead /// .fetch_one(&mut conn) /// .await?; /// /// assert_eq!(record.id, MyInt4(1)); /// ``` /// /// ### Troubleshooting: "error: mismatched types" /// If you get a "mismatched types" error from an invocation of this macro and the error /// isn't pointing specifically at a parameter. /// /// For example, code like this (using a Postgres database): /// /// ```rust,ignore /// struct Account { /// id: i32, /// name: Option, /// } /// /// let account = sqlx::query_as!( /// Account, /// r#"SELECT id, name from (VALUES (1, 'Herp Derpinson')) accounts(id, name)"#, /// ) /// .fetch_one(&mut conn) /// .await?; /// ``` /// /// Might produce an error like this: /// ```text,ignore /// error[E0308]: mismatched types /// --> tests/postgres/macros.rs:126:19 /// | /// 126 | let account = sqlx::query_as!( /// | ___________________^ /// 127 | | Account, /// 128 | | r#"SELECT id, name from (VALUES (1, 'Herp Derpinson')) accounts(id, name)"#, /// 129 | | ) /// | |_____^ expected `i32`, found enum `std::option::Option` /// | /// = note: expected type `i32` /// found enum `std::option::Option` /// ``` /// /// This means that you need to check that any field of the "expected" type (here, `i32`) matches /// the Rust type mapping for its corresponding SQL column (see the `types` module of your database, /// listed above, for mappings). The "found" type is the SQL->Rust mapping that the macro chose. /// /// In the above example, the returned column is inferred to be nullable because it's being /// returned from a `VALUES` statement in Postgres, so the macro inferred the field to be nullable /// and so used `Option` instead of `i32`. **In this specific case** we could use /// `select id as "id!"` to override the inferred nullability because we know in practice /// that column will never be `NULL` and it will fix the error. /// /// Nullability inference and type overrides are discussed in detail in the docs for /// [`query!`][`crate::query!`]. /// /// It unfortunately doesn't appear to be possible right now to make the error specifically mention /// the field; this probably requires the `const-panic` feature (still unstable as of Rust 1.45). #[macro_export] #[cfg_attr(docsrs, doc(cfg(feature = "macros")))] macro_rules! query_as ( ($out_struct:path, $query:expr) => ( { $crate::sqlx_macros::expand_query!(record = $out_struct, source = $query) }); ($out_struct:path, $query:expr, $($args:tt)*) => ( { $crate::sqlx_macros::expand_query!(record = $out_struct, source = $query, args = [$($args)*]) }) ); /// Combines the syntaxes of [`query_as!`][`crate::query_as!`] and [`query_file!`][`crate::query_file!`]. /// /// Enforces requirements of both macros; see them for details. /// /// ```rust,ignore /// # use sqlx::Connect; /// # #[cfg(all(feature = "mysql", feature = "_rt-async-std"))] /// # #[async_std::main] /// # async fn main() -> sqlx::Result<()>{ /// # let db_url = dotenvy::var("DATABASE_URL").expect("DATABASE_URL must be set"); /// # /// # if !(db_url.starts_with("mysql") || db_url.starts_with("mariadb")) { return Ok(()) } /// # let mut conn = sqlx::MySqlConnection::connect(db_url).await?; /// #[derive(Debug)] /// struct Account { /// id: i32, /// name: String /// } /// /// // let mut conn = ; /// let account = sqlx::query_file_as!(Account, "tests/test-query-account-by-id.sql", 1i32) /// .fetch_one(&mut conn) /// .await?; /// /// println!("{account:?}"); /// println!("{}: {}", account.id, account.name); /// /// # Ok(()) /// # } /// # /// # #[cfg(any(not(feature = "mysql"), not(feature = "_rt-async-std")))] /// # fn main() {} /// ``` #[macro_export] #[cfg_attr(docsrs, doc(cfg(feature = "macros")))] macro_rules! query_file_as ( ($out_struct:path, $path:literal) => ( { $crate::sqlx_macros::expand_query!(record = $out_struct, source_file = $path) }); ($out_struct:path, $path:literal, $($args:tt)*) => ( { $crate::sqlx_macros::expand_query!(record = $out_struct, source_file = $path, args = [$($args)*]) }) ); /// A variant of [`query_as!`][`crate::query_as!`] which does not check the input or output types. This still does parse /// the query to ensure it's syntactically and semantically valid for the current database. #[macro_export] #[cfg_attr(docsrs, doc(cfg(feature = "macros")))] macro_rules! query_as_unchecked ( ($out_struct:path, $query:expr) => ( { $crate::sqlx_macros::expand_query!(record = $out_struct, source = $query, checked = false) }); ($out_struct:path, $query:expr, $($args:tt)*) => ( { $crate::sqlx_macros::expand_query!(record = $out_struct, source = $query, args = [$($args)*], checked = false) }) ); /// A variant of [`query_file_as!`][`crate::query_file_as!`] which does not check the input or output types. This /// still does parse the query to ensure it's syntactically and semantically valid /// for the current database. #[macro_export] #[cfg_attr(docsrs, doc(cfg(feature = "macros")))] macro_rules! query_file_as_unchecked ( ($out_struct:path, $path:literal) => ( { $crate::sqlx_macros::expand_query!(record = $out_struct, source_file = $path, checked = false) }); ($out_struct:path, $path:literal, $($args:tt)*) => ( { $crate::sqlx_macros::expand_query!(record = $out_struct, source_file = $path, args = [$($args)*], checked = false) }) ); /// A variant of [`query!`][`crate::query!`] which expects a single column from the query and evaluates to an /// instance of [QueryScalar][crate::query::QueryScalar]. /// /// The name of the column is not required to be a valid Rust identifier, however you can still /// use the column type override syntax in which case the column name _does_ have to be a valid /// Rust identifier for the override to parse properly. If the override parse fails the error /// is silently ignored (we just don't have a reliable way to tell the difference). **If you're /// getting a different type than expected, please check to see if your override syntax is correct /// before opening an issue.** /// /// Wildcard overrides like in [`query_as!`][`crate::query_as!`] are also allowed, in which case the output type /// is left up to inference. /// /// See [`query!`][`crate::query!`] for more information. #[macro_export] #[cfg_attr(docsrs, doc(cfg(feature = "macros")))] macro_rules! query_scalar ( ($query:expr) => ( $crate::sqlx_macros::expand_query!(scalar = _, source = $query) ); ($query:expr, $($args:tt)*) => ( $crate::sqlx_macros::expand_query!(scalar = _, source = $query, args = [$($args)*]) ) ); /// A variant of [`query_scalar!`][`crate::query_scalar!`] which takes a file path like /// [`query_file!`][`crate::query_file!`]. #[macro_export] #[cfg_attr(docsrs, doc(cfg(feature = "macros")))] macro_rules! query_file_scalar ( ($path:literal) => ( $crate::sqlx_macros::expand_query!(scalar = _, source_file = $path) ); ($path:literal, $($args:tt)*) => ( $crate::sqlx_macros::expand_query!(scalar = _, source_file = $path, args = [$($args)*]) ) ); /// A variant of [`query_scalar!`][`crate::query_scalar!`] which does not typecheck bind parameters /// and leaves the output type to inference. /// The query itself is still checked that it is syntactically and semantically /// valid for the database, that it only produces one column and that the number of bind parameters /// is correct. /// /// For this macro variant the name of the column is irrelevant. #[macro_export] #[cfg_attr(docsrs, doc(cfg(feature = "macros")))] macro_rules! query_scalar_unchecked ( ($query:expr) => ( $crate::sqlx_macros::expand_query!(scalar = _, source = $query, checked = false) ); ($query:expr, $($args:tt)*) => ( $crate::sqlx_macros::expand_query!(scalar = _, source = $query, args = [$($args)*], checked = false) ) ); /// A variant of [`query_file_scalar!`][`crate::query_file_scalar!`] which does not typecheck bind /// parameters and leaves the output type to inference. /// The query itself is still checked that it is syntactically and /// semantically valid for the database, that it only produces one column and that the number of /// bind parameters is correct. /// /// For this macro variant the name of the column is irrelevant. #[macro_export] #[cfg_attr(docsrs, doc(cfg(feature = "macros")))] macro_rules! query_file_scalar_unchecked ( ($path:literal) => ( $crate::sqlx_macros::expand_query!(scalar = _, source_file = $path, checked = false) ); ($path:literal, $($args:tt)*) => ( $crate::sqlx_macros::expand_query!(scalar = _, source_file = $path, args = [$($args)*], checked = false) ) ); #[allow(clippy::needless_doctest_main)] /// Embeds migrations into the binary by expanding to a static instance of [Migrator][crate::migrate::Migrator]. /// /// ```rust,ignore /// sqlx::migrate!("db/migrations") /// .run(&pool) /// .await?; /// ``` /// /// ```rust,ignore /// use sqlx::migrate::Migrator; /// /// static MIGRATOR: Migrator = sqlx::migrate!(); // defaults to "./migrations" /// ``` /// /// The directory must be relative to the project root (the directory containing `Cargo.toml`), /// unlike `include_str!()` which uses compiler internals to get the path of the file where it /// was invoked. /// /// See [MigrationSource][crate::migrate::MigrationSource] for details on structure of the ./migrations directory. /// /// ## Triggering Recompilation on Migration Changes /// In some cases when making changes to embedded migrations, such as adding a new migration without /// changing any Rust source files, you might find that `cargo build` doesn't actually do anything, /// or when you do `cargo run` your application isn't applying new migrations on startup. /// /// This is because our ability to tell the compiler to watch external files for changes /// from a proc-macro is very limited. The compiler by default only re-runs proc macros when /// one or more source files have changed, because normally it shouldn't have to otherwise. SQLx is /// just weird in that external factors can change the output of proc macros, much to the chagrin of /// the compiler team and IDE plugin authors. /// /// As of 0.5.6, we emit `include_str!()` with an absolute path for each migration, but that /// only works to get the compiler to watch _existing_ migration files for changes. /// /// Our only options for telling it to watch the whole `migrations/` directory are either via the /// user creating a Cargo build script in their project, or using an unstable API on nightly /// governed by a `cfg`-flag. /// /// ##### Stable Rust: Cargo Build Script /// The only solution on stable Rust right now is to create a Cargo build script in your project /// and have it print `cargo:rerun-if-changed=migrations`: /// /// `build.rs` /// ```no_run /// fn main() { /// println!("cargo:rerun-if-changed=migrations"); /// } /// ``` /// /// You can run `sqlx migrate build-script` to generate this file automatically. /// /// See: [The Cargo Book: 3.8 Build Scripts; Outputs of the Build Script](https://doc.rust-lang.org/stable/cargo/reference/build-scripts.html#outputs-of-the-build-script) /// /// #### Nightly Rust: `cfg` Flag /// The `migrate!()` macro also listens to `--cfg sqlx_macros_unstable`, which will enable /// the `track_path` feature to directly tell the compiler to watch the `migrations/` directory: /// /// ```sh,ignore /// $ env RUSTFLAGS='--cfg sqlx_macros_unstable' cargo build /// ``` /// /// Note that this unfortunately will trigger a fully recompile of your dependency tree, at least /// for the first time you use it. It also, of course, requires using a nightly compiler. /// /// You can also set it in `build.rustflags` in `.cargo/config.toml`: /// ```toml,ignore /// [build] /// rustflags = ["--cfg=sqlx_macros_unstable"] /// ``` /// /// And then continue building and running your project normally. /// /// If you're building on nightly anyways, it would be extremely helpful to help us test /// this feature and find any bugs in it. /// /// Subscribe to [the `track_path` tracking issue](https://github.com/rust-lang/rust/issues/73921) /// for discussion and the future stabilization of this feature. /// /// For brevity and because it involves the same commitment to unstable features in `proc_macro`, /// if you're using `--cfg procmacro2_semver_exempt` it will also enable this feature /// (see [`proc-macro2` docs / Unstable Features](https://docs.rs/proc-macro2/1.0.27/proc_macro2/#unstable-features)). #[cfg(feature = "migrate")] #[macro_export] macro_rules! migrate { ($dir:literal) => {{ $crate::sqlx_macros::migrate!($dir) }}; () => {{ $crate::sqlx_macros::migrate!("./migrations") }}; } sqlx-0.8.3/src/macros/test.md000064400000000000000000000205351046102023000142040ustar 00000000000000Mark an `async fn` as a test with SQLx support. The test will automatically be executed in the async runtime according to the chosen `runtime-{async-std, tokio}` feature. If more than one runtime feature is enabled, `runtime-tokio` is preferred. By default, this behaves identically to `#[tokio::test]`1 or `#[async_std::test]`: ```rust # // Note if reading these examples directly in `test.md`: # // lines prefixed with `#` are not meant to be shown; # // they are supporting code to help the examples to compile successfully. # #[cfg(feature = "_rt-tokio")] #[sqlx::test] async fn test_async_fn() { tokio::task::yield_now().await; } ``` However, several advanced features are also supported as shown in the next section. 1`#[sqlx::test]` does not recognize any of the control arguments supported by `#[tokio::test]` as that would have complicated the implementation. If your use case requires any of those, feel free to open an issue. ### Automatic Test Database Management (requires `migrate` feature) `#[sqlx::test]` can automatically create test databases for you and provide live connections to your test. For every annotated function, a new test database is created so tests can run against a live database but are isolated from each other. This feature is activated by changing the signature of your test function. The following signatures are supported: * `async fn(Pool) -> Ret` * the `Pool`s used by all running tests share a single connection limit to avoid exceeding the server's limit. * `async fn(PoolConnection) -> Ret` * `PoolConnection`, etc. * `async fn(PoolOptions, impl ConnectOptions) -> Ret` * Where `impl ConnectOptions` is, e.g, `PgConnectOptions`, `MySqlConnectOptions`, etc. * If your test wants to create its own `Pool` (for example, to set pool callbacks or to modify `ConnectOptions`), you can use this signature. Where `DB` is a supported `Database` type and `Ret` is `()` or `Result<_, _>`. ##### Supported Databases Most of these will require you to set `DATABASE_URL` as an environment variable or in a `.env` file like `sqlx::query!()` _et al_, to give the test driver a superuser connection with which to manage test databases. | Database | Requires `DATABASE_URL` | | --- | --- | | Postgres | Yes | | MySQL | Yes | | SQLite | No2 | Test databases are automatically cleaned up as tests succeed, but failed tests will leave their databases in-place to facilitate debugging. Note that to simplify the implementation, panics are _always_ considered to be failures, even for `#[should_panic]` tests. To limit disk space usage, any previously created test databases will be deleted the next time a test binary using `#[sqlx::test]` is run. ```rust,no_run # #[cfg(all(feature = "migrate", feature = "postgres"))] # mod example { use sqlx::{PgPool, Row}; #[sqlx::test] async fn basic_test(pool: PgPool) -> sqlx::Result<()> { let mut conn = pool.acquire().await?; let foo = sqlx::query("SELECT * FROM foo") .fetch_one(&mut conn) .await?; assert_eq!(foo.get::("bar"), "foobar!"); Ok(()) } # } ``` 2 SQLite defaults to `target/sqlx/test-dbs/.sqlite` where `` is the path of the test function converted to a filesystem path (`::` replaced with `/`). ### Automatic Migrations (requires `migrate` feature) To ensure a straightforward test implementation against a fresh test database, migrations are automatically applied if a `migrations` folder is found in the same directory as `CARGO_MANIFEST_DIR` (the directory where the current crate's `Cargo.toml` resides). You can override the resolved path relative to `CARGO_MANIFEST_DIR` in the attribute (global overrides are not currently supported): ```rust,ignore # #[cfg(all(feature = "migrate", feature = "postgres"))] # mod example { use sqlx::{PgPool, Row}; #[sqlx::test(migrations = "foo_migrations")] async fn basic_test(pool: PgPool) -> sqlx::Result<()> { let mut conn = pool.acquire().await?; let foo = sqlx::query("SELECT * FROM foo") .fetch_one(&mut conn) .await?; assert_eq!(foo.get::("bar"), "foobar!"); Ok(()) } # } ``` Or if you're already embedding migrations in your main crate, you can reference them directly: `foo_crate/lib.rs` ```rust,ignore pub static MIGRATOR: sqlx::migrate::Migrator = sqlx::migrate!("foo_migrations"); ``` `foo_crate/tests/foo_test.rs` ```rust,no_run # #[cfg(all(feature = "migrate", feature = "postgres"))] # mod example { use sqlx::{PgPool, Row}; # // This is standing in for the main crate since doc examples don't support multiple crates. # mod foo_crate { # use std::borrow::Cow; # static MIGRATOR: sqlx::migrate::Migrator = sqlx::migrate::Migrator { # migrations: Cow::Borrowed(&[]), # ignore_missing: false, # locking: true, # no_tx: false # }; # } // You could also do `use foo_crate::MIGRATOR` and just refer to it as `MIGRATOR` here. #[sqlx::test(migrator = "foo_crate::MIGRATOR")] async fn basic_test(pool: PgPool) -> sqlx::Result<()> { let mut conn = pool.acquire().await?; let foo = sqlx::query("SELECT * FROM foo") .fetch_one(&mut conn) .await?; assert_eq!(foo.get::("bar"), "foobar!"); Ok(()) } # } ``` Or disable migrations processing entirely: ```rust,no_run # #[cfg(all(feature = "migrate", feature = "postgres"))] # mod example { use sqlx::{PgPool, Row}; #[sqlx::test(migrations = false)] async fn basic_test(pool: PgPool) -> sqlx::Result<()> { let mut conn = pool.acquire().await?; conn.execute("CREATE TABLE foo(bar text)").await?; let foo = sqlx::query("SELECT * FROM foo") .fetch_one(&mut conn) .await?; assert_eq!(foo.get::("bar"), "foobar!"); Ok(()) } # } ``` ### Automatic Fixture Application (requires `migrate` feature) Since tests are isolated from each other but may require data to already exist in the database to keep from growing exponentially in complexity, `#[sqlx::test]` also supports applying test fixtures, which are SQL scripts that function similarly to migrations but are solely intended to insert test data and be arbitrarily composable. Imagine a basic social app that has users, posts and comments. To test the comment routes, you'd want the database to already have users and posts in it so the comments tests don't have to duplicate that work. You can either pass a list of fixture to the attribute `fixtures` in three different operating modes: 1) Pass a list of references files in `./fixtures` (resolved as `./fixtures/{name}.sql`, `.sql` added only if extension is missing); 2) Pass a list of file paths (including associated extension), in which case they can either be absolute, or relative to the current file; 3) Pass a `path = ` parameter and a `scripts(, , ...)` parameter that are relative to the provided path (resolved as `{path}/{filename_x}.sql`, `.sql` added only if extension is missing). In any case they will be applied in the given order3: ```rust,no_run # #[cfg(all(feature = "migrate", feature = "postgres"))] # mod example { # struct App {} # fn create_app(pool: PgPool) -> App { App {} } use sqlx::PgPool; use serde_json::json; // Alternatives: // #[sqlx::test(fixtures("./fixtures/users.sql", "./fixtures/posts.sql"))] // or // #[sqlx::test(fixtures(path = "./fixtures", scripts("users", "posts")))] #[sqlx::test(fixtures("users", "posts"))] async fn test_create_comment(pool: PgPool) -> sqlx::Result<()> { // See examples/postgres/social-axum-with-tests for a more in-depth example. let mut app = create_app(pool); let comment = test_request( &mut app, "POST", "/v1/comment", json! { "postId": "1234" } ).await?; assert_eq!(comment["postId"], "1234"); Ok(()) } # } ``` Multiple `fixtures` attributes can be used to combine different operating modes. 3Ordering for test fixtures is entirely up to the application, and each test may choose which fixtures to apply and which to omit. However, since each fixture is applied separately (sent as a single command string, so wrapped in an implicit `BEGIN` and `COMMIT`), you will want to make sure to order the fixtures such that foreign key requirements are always satisfied, or else you might get errors. sqlx-0.8.3/src/spec_error.rs000064400000000000000000000045261046102023000141320ustar 00000000000000use std::any::Any; use std::error::Error; use std::fmt::{Debug, Display}; // Autoderef specialization similar to `clap::value_parser!()`. pub struct SpecErrorWrapper(pub E); pub trait SpecError: Sized { fn __sqlx_spec_error( &self, ) -> fn(SpecErrorWrapper) -> Box; } impl SpecError for &&&&SpecErrorWrapper where E: Error + Send + Sync + 'static, { fn __sqlx_spec_error( &self, ) -> fn(SpecErrorWrapper) -> Box { |e| Box::new(e.0) } } impl SpecError for &&&SpecErrorWrapper where E: Display, { fn __sqlx_spec_error( &self, ) -> fn(SpecErrorWrapper) -> Box { |e| e.0.to_string().into() } } impl SpecError for &&SpecErrorWrapper where E: Debug, { fn __sqlx_spec_error( &self, ) -> fn(SpecErrorWrapper) -> Box { |e| format!("{:?}", e.0).into() } } impl SpecError for &SpecErrorWrapper where E: Any, { fn __sqlx_spec_error( &self, ) -> fn(SpecErrorWrapper) -> Box { |_e| format!("unprintable error: {}", std::any::type_name::()).into() } } impl SpecError for SpecErrorWrapper { fn __sqlx_spec_error( &self, ) -> fn(SpecErrorWrapper) -> Box { |_e| "unprintable error: (unprintable type)".into() } } #[doc(hidden)] #[macro_export] macro_rules! __spec_error { ($e:expr) => {{ use $crate::spec_error::{SpecError, SpecErrorWrapper}; let wrapper = SpecErrorWrapper($e); let wrap_err = wrapper.__sqlx_spec_error(); wrap_err(wrapper) }}; } #[test] fn test_spec_error() { #[derive(Debug)] struct DebugError; struct AnyError; let _e: Box = __spec_error!(std::io::Error::from(std::io::ErrorKind::Unsupported)); let _e: Box = __spec_error!("displayable error"); let _e: Box = __spec_error!(DebugError); let _e: Box = __spec_error!(AnyError); let _e: Box = __spec_error!(&1i32); } sqlx-0.8.3/src/ty_match.rs000064400000000000000000000107221046102023000135720ustar 00000000000000use std::marker::PhantomData; // These types allow the `query!()` and friends to compare a given parameter's type to // an expected parameter type even if the former is behind a reference or in `Option`. // For query parameters, Postgres gives us a single type ID which we convert to an "expected" or // preferred Rust type, but there can actually be several types that are compatible for a given type // in input position. E.g. for an expected parameter of `String`, we want to accept `String`, // `Option`, `&str` and `Option<&str>`. And for the best compiler errors we don't just // want an `IsCompatible` trait (at least not without `#[on_unimplemented]` which is unstable // for the foreseeable future). // We can do this by using autoref (for method calls, the compiler adds reference ops until // it finds a matching impl) with impls that technically don't overlap as a hacky form of // specialization (but this works only if all types are statically known, i.e. we're not in a // generic context; this should suit 99% of use cases for the macros). #[allow(clippy::just_underscores_and_digits)] pub fn same_type(_1: &T, _2: &T) {} pub struct WrapSame(PhantomData, PhantomData); impl WrapSame { pub fn new(_arg: &U) -> Self { WrapSame(PhantomData, PhantomData) } } pub trait WrapSameExt: Sized { type Wrapped; fn wrap_same(self) -> Self::Wrapped { panic!("only for type resolution") } } impl WrapSameExt for WrapSame> { type Wrapped = Option; } impl WrapSameExt for &'_ WrapSame { type Wrapped = T; } pub struct MatchBorrow(PhantomData, PhantomData); impl MatchBorrow { pub fn new(t: T, _u: &U) -> (T, Self) { (t, MatchBorrow(PhantomData, PhantomData)) } } pub trait MatchBorrowExt: Sized { type Matched; fn match_borrow(self) -> Self::Matched { panic!("only for type resolution") } } impl<'a> MatchBorrowExt for MatchBorrow, Option> { type Matched = Option<&'a str>; } impl<'a> MatchBorrowExt for MatchBorrow, Option>> { type Matched = Option<&'a [u8]>; } impl<'a> MatchBorrowExt for MatchBorrow, Option<&'a String>> { type Matched = Option<&'a str>; } impl<'a> MatchBorrowExt for MatchBorrow, Option<&'a Vec>> { type Matched = Option<&'a [u8]>; } impl<'a> MatchBorrowExt for MatchBorrow<&'a str, String> { type Matched = &'a str; } impl<'a> MatchBorrowExt for MatchBorrow<&'a [u8], Vec> { type Matched = &'a [u8]; } impl MatchBorrowExt for MatchBorrow<&'_ T, T> { type Matched = T; } impl MatchBorrowExt for MatchBorrow<&'_ &'_ T, T> { type Matched = T; } impl MatchBorrowExt for MatchBorrow { type Matched = T; } impl MatchBorrowExt for MatchBorrow { type Matched = T; } impl MatchBorrowExt for MatchBorrow, Option> { type Matched = Option; } impl MatchBorrowExt for MatchBorrow, Option> { type Matched = Option; } impl MatchBorrowExt for MatchBorrow, Option<&'_ T>> { type Matched = Option; } impl MatchBorrowExt for MatchBorrow, Option<&'_ &'_ T>> { type Matched = Option; } impl MatchBorrowExt for &'_ MatchBorrow { type Matched = U; } pub fn conjure_value() -> T { panic!() } pub fn dupe_value(_t: &T) -> T { panic!() } #[test] fn test_dupe_value() { let val = &(String::new(),); if false { let _: i32 = dupe_value(&0i32); let _: String = dupe_value(&String::new()); let _: String = dupe_value(&val.0); } } #[test] fn test_wrap_same() { if false { let _: i32 = WrapSame::::new(&0i32).wrap_same(); let _: i32 = WrapSame::::new(&"hello, world!").wrap_same(); let _: Option = WrapSame::::new(&Some(String::new())).wrap_same(); } } #[test] fn test_match_borrow() { if false { let (_, match_borrow) = MatchBorrow::new("", &String::new()); let _: &str = match_borrow.match_borrow(); let (_, match_borrow) = MatchBorrow::new(&&0i64, &0i64); let _: i64 = match_borrow.match_borrow(); let (_, match_borrow) = MatchBorrow::new(&0i64, &0i64); let _: i64 = match_borrow.match_borrow(); let (_, match_borrow) = MatchBorrow::new(0i64, &0i64); let _: i64 = match_borrow.match_borrow(); } } sqlx-0.8.3/tests/.dockerignore000064400000000000000000000001141046102023000144350ustar 00000000000000* !certs/* !keys/* !mysql/my.cnf !mssql/*.sh !postgres/pg_hba.conf !*/*.sql sqlx-0.8.3/tests/.env000064400000000000000000000001021046102023000125470ustar 00000000000000# environment values for docker-compose COMPOSE_PROJECT_NAME=sqlx sqlx-0.8.3/tests/.gitignore000064400000000000000000000000221046102023000137470ustar 00000000000000!.env __pycache__ sqlx-0.8.3/tests/README.md000064400000000000000000000014071046102023000132460ustar 00000000000000 ### Running Tests SQLx uses docker to run many compatible database systems for integration testing. You'll need to [install docker](https://docs.docker.com/engine/) to run the full suite. You can validate your docker installation with: $ docker run hello-world Start the databases with `docker-compose` before running tests: $ docker-compose up Run all tests against all supported databases using: $ ./x.py If you see test failures, or want to run a more specific set of tests against a specific database, you can specify both the features to be tests and the DATABASE_URL. e.g. $ DATABASE_URL=mysql://root:password@127.0.0.1:49183/sqlx cargo test --no-default-features --features macros,offline,any,all-types,mysql,runtime-async-std-native-tls sqlx-0.8.3/tests/any/any.rs000064400000000000000000000063401046102023000137140ustar 00000000000000use sqlx::any::AnyRow; use sqlx::{Any, Connection, Executor, Row}; use sqlx_test::new; #[sqlx_macros::test] async fn it_connects() -> anyhow::Result<()> { sqlx::any::install_default_drivers(); let mut conn = new::().await?; let value = sqlx::query("select 1 + 5") .try_map(|row: AnyRow| row.try_get::(0)) .fetch_one(&mut conn) .await?; assert_eq!(6i32, value); conn.close().await?; Ok(()) } #[sqlx_macros::test] async fn it_pings() -> anyhow::Result<()> { sqlx::any::install_default_drivers(); let mut conn = new::().await?; conn.ping().await?; Ok(()) } #[sqlx_macros::test] async fn it_executes_with_pool() -> anyhow::Result<()> { sqlx::any::install_default_drivers(); let pool = sqlx_test::pool::().await?; let rows = pool.fetch_all("SELECT 1; SElECT 2").await?; assert_eq!(rows.len(), 2); Ok(()) } #[sqlx_macros::test] async fn it_does_not_stop_stream_after_decoding_error() -> anyhow::Result<()> { use futures::stream::StreamExt; sqlx::any::install_default_drivers(); // see https://github.com/launchbadge/sqlx/issues/1884 let pool = sqlx_test::pool::().await?; #[derive(Debug, PartialEq)] struct MyType; impl<'a> sqlx::FromRow<'a, AnyRow> for MyType { fn from_row(row: &'a AnyRow) -> sqlx::Result { let n = row.try_get::(0)?; if n == 1 { Err(sqlx::Error::RowNotFound) } else { Ok(MyType) } } } let rows = sqlx::query_as("SELECT 0 UNION ALL SELECT 1 UNION ALL SELECT 2") .fetch(&pool) .map(|r| r.ok()) .collect::>() .await; assert_eq!(rows, vec![Some(MyType), None, Some(MyType)]); Ok(()) } #[sqlx_macros::test] async fn it_gets_by_name() -> anyhow::Result<()> { sqlx::any::install_default_drivers(); let mut conn = new::().await?; let row = conn.fetch_one("SELECT 1 as _1").await?; let val: i32 = row.get("_1"); assert_eq!(val, 1); Ok(()) } #[sqlx_macros::test] async fn it_can_fail_and_recover() -> anyhow::Result<()> { sqlx::any::install_default_drivers(); let mut conn = new::().await?; for i in 0..10 { // make a query that will fail let res = conn .execute("INSERT INTO not_found (column) VALUES (10)") .await; assert!(res.is_err()); // now try and use the connection let val: i32 = conn .fetch_one(&*format!("SELECT {i}")) .await? .get_unchecked(0); assert_eq!(val, i); } Ok(()) } #[sqlx_macros::test] async fn it_can_fail_and_recover_with_pool() -> anyhow::Result<()> { sqlx::any::install_default_drivers(); let pool = sqlx_test::pool::().await?; for i in 0..10 { // make a query that will fail let res = pool .execute("INSERT INTO not_found (column) VALUES (10)") .await; assert!(res.is_err()); // now try and use the connection let val: i32 = pool .fetch_one(&*format!("SELECT {i}")) .await? .get_unchecked(0); assert_eq!(val, i); } Ok(()) } sqlx-0.8.3/tests/any/pool.rs000064400000000000000000000200301046102023000140660ustar 00000000000000use sqlx::any::{AnyConnectOptions, AnyPoolOptions}; use sqlx::Executor; use std::sync::{ atomic::{AtomicI32, AtomicUsize, Ordering}, Arc, Mutex, }; use std::time::Duration; #[sqlx_macros::test] async fn pool_should_invoke_after_connect() -> anyhow::Result<()> { sqlx::any::install_default_drivers(); let counter = Arc::new(AtomicUsize::new(0)); let pool = AnyPoolOptions::new() .after_connect({ let counter = counter.clone(); move |_conn, _meta| { let counter = counter.clone(); Box::pin(async move { counter.fetch_add(1, Ordering::SeqCst); Ok(()) }) } }) .connect(&dotenvy::var("DATABASE_URL")?) .await?; let _ = pool.acquire().await?; let _ = pool.acquire().await?; let _ = pool.acquire().await?; let _ = pool.acquire().await?; // since connections are released asynchronously, // `.after_connect()` may be called more than once assert!(counter.load(Ordering::SeqCst) >= 1); Ok(()) } // https://github.com/launchbadge/sqlx/issues/527 #[sqlx_macros::test] async fn pool_should_be_returned_failed_transactions() -> anyhow::Result<()> { sqlx::any::install_default_drivers(); let pool = AnyPoolOptions::new() .max_connections(2) .acquire_timeout(Duration::from_secs(3)) .connect(&dotenvy::var("DATABASE_URL")?) .await?; let query = "blah blah"; let mut tx = pool.begin().await?; let res = sqlx::query(query).execute(&mut *tx).await; assert!(res.is_err()); drop(tx); let mut tx = pool.begin().await?; let res = sqlx::query(query).execute(&mut *tx).await; assert!(res.is_err()); drop(tx); let mut tx = pool.begin().await?; let res = sqlx::query(query).execute(&mut *tx).await; assert!(res.is_err()); drop(tx); Ok(()) } #[sqlx_macros::test] async fn test_pool_callbacks() -> anyhow::Result<()> { sqlx::any::install_default_drivers(); #[derive(sqlx::FromRow, Debug, PartialEq, Eq)] struct ConnStats { id: i32, before_acquire_calls: i32, after_release_calls: i32, } sqlx_test::setup_if_needed(); let conn_options: AnyConnectOptions = std::env::var("DATABASE_URL")?.parse()?; let current_id = AtomicI32::new(0); let pool = AnyPoolOptions::new() .max_connections(1) .acquire_timeout(Duration::from_secs(5)) .after_connect(move |conn, meta| { assert_eq!(meta.age, Duration::ZERO); assert_eq!(meta.idle_for, Duration::ZERO); let id = current_id.fetch_add(1, Ordering::AcqRel); Box::pin(async move { let statement = format!( // language=SQL r#" CREATE TEMPORARY TABLE conn_stats( id int primary key, before_acquire_calls int default 0, after_release_calls int default 0 ); INSERT INTO conn_stats(id) VALUES ({}); "#, // Until we have generalized bind parameters id ); conn.execute(&statement[..]).await?; Ok(()) }) }) .before_acquire(|conn, meta| { // `age` and `idle_for` should both be nonzero assert_ne!(meta.age, Duration::ZERO); assert_ne!(meta.idle_for, Duration::ZERO); Box::pin(async move { // MySQL and MariaDB don't support UPDATE ... RETURNING sqlx::query( r#" UPDATE conn_stats SET before_acquire_calls = before_acquire_calls + 1 "#, ) .execute(&mut *conn) .await?; let stats: ConnStats = sqlx::query_as("SELECT * FROM conn_stats") .fetch_one(conn) .await?; // For even IDs, cap by the number of before_acquire calls. // Ignore the check for odd IDs. Ok((stats.id & 1) == 1 || stats.before_acquire_calls < 3) }) }) .after_release(|conn, meta| { // `age` should be nonzero but `idle_for` should be zero. assert_ne!(meta.age, Duration::ZERO); assert_eq!(meta.idle_for, Duration::ZERO); Box::pin(async move { sqlx::query( r#" UPDATE conn_stats SET after_release_calls = after_release_calls + 1 "#, ) .execute(&mut *conn) .await?; let stats: ConnStats = sqlx::query_as("SELECT * FROM conn_stats") .fetch_one(conn) .await?; // For odd IDs, cap by the number of before_release calls. // Ignore the check for even IDs. Ok((stats.id & 1) == 0 || stats.after_release_calls < 4) }) }) // Don't establish a connection yet. .connect_lazy_with(conn_options); // Expected pattern of (id, before_acquire_calls, after_release_calls) let pattern = [ // The connection pool starts empty. (0, 0, 0), (0, 1, 1), (0, 2, 2), (1, 0, 0), (1, 1, 1), (1, 2, 2), // We should expect one more `acquire` because the ID is odd (1, 3, 3), (2, 0, 0), (2, 1, 1), (2, 2, 2), (3, 0, 0), ]; for (id, before_acquire_calls, after_release_calls) in pattern { let conn_stats: ConnStats = sqlx::query_as("SELECT * FROM conn_stats") .fetch_one(&pool) .await?; assert_eq!( conn_stats, ConnStats { id, before_acquire_calls, after_release_calls } ); } pool.close().await; Ok(()) } #[ignore] #[sqlx_macros::test] async fn test_connection_maintenance() -> anyhow::Result<()> { sqlx::any::install_default_drivers(); sqlx_test::setup_if_needed(); let conn_options: AnyConnectOptions = std::env::var("DATABASE_URL")?.parse()?; let last_meta = Arc::new(Mutex::new(None)); let last_meta_ = last_meta.clone(); let pool = AnyPoolOptions::new() .max_lifetime(Duration::from_millis(400)) .min_connections(3) .before_acquire(move |_conn, _meta| { *last_meta_.lock().unwrap() = Some(_meta); Box::pin(async { Ok(true) }) }) .connect_lazy_with(conn_options); // Open and release 5 connections let conns = vec![ pool.acquire().await?, pool.acquire().await?, pool.acquire().await?, pool.acquire().await?, pool.acquire().await?, ]; assert_eq!(pool.size(), 5); assert_eq!(pool.num_idle(), 0); for mut conn in conns { conn.return_to_pool().await; } assert_eq!(pool.size(), 5); assert_eq!(pool.num_idle(), 5); // Wait for at least two iterations of maintenance task sqlx_core::rt::sleep(Duration::from_secs(1)).await; // Existing connections should have been closed due to max lifetime // and the pool should have reopened min_connections new ones. // One connection might be in the process of being replaced so we assert 2-3. assert!( pool.size() >= 2 && pool.size() <= 3, "pool.size() = {}", pool.size() ); for _ in 0..2 { // Check that the connections was both acquired from the pool AND it's new let _ = pool.acquire().await.expect("failed to acquire connection"); let meta = last_meta .lock() .unwrap() .take() .expect("expected a connection from the pool"); assert!( meta.age < Duration::from_secs(2), "expected a fresh connection (age {:?})", meta.age ); } Ok(()) } sqlx-0.8.3/tests/certs/ca.crt000064400000000000000000000032741046102023000142100ustar 00000000000000-----BEGIN CERTIFICATE----- MIIEzTCCAzWgAwIBAgIQFN6Kw8UmgcAAAGlENSADBzANBgkqhkiG9w0BAQsFADB/ MR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExKjAoBgNVBAsMIW1laGNv ZGVAR29sZW0ubG9jYWwgKFJ5YW4gTGVja2V5KTExMC8GA1UEAwwobWtjZXJ0IG1l aGNvZGVAR29sZW0ubG9jYWwgKFJ5YW4gTGVja2V5KTAeFw0yMDA3MTgxMjE1NDla Fw0zMDA3MTgxMjE1NDlaMH8xHjAcBgNVBAoTFW1rY2VydCBkZXZlbG9wbWVudCBD QTEqMCgGA1UECwwhbWVoY29kZUBHb2xlbS5sb2NhbCAoUnlhbiBMZWNrZXkpMTEw LwYDVQQDDChta2NlcnQgbWVoY29kZUBHb2xlbS5sb2NhbCAoUnlhbiBMZWNrZXkp MIIBojANBgkqhkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAnElV+r9IY3H3jBM72Og3 MINManqh2VpBvbn6ZlQiYKsyeRiK0hyx+7PugJiw8NW60rHI9z4P4ie5pyCsXQ/F 3dkAmEuliFGOR3NclzUtBu/K3eYrwafO57cLc/k/1skcqV4p8Q4XILsRP25TiSJp O3Q3Oq70t/unZRONKHUsQfag1z79xW36w09mnFMYTBUKQB1QcaZnGZ0xFyp6jk+w Z0molgMSDiJS3gWP0zdSGRHVL+eR/072EqYBZ6ycSlfh1XlaP4SM9DMR5K/X9Iwi AQCSWFPAksUoSLrG/+pyHYWtcelj7u6NNRxIcW6DiyF+HgeSx9YJTnYq0eP9zqbb gBEhaPz6bnTTzJo6+peyHLHaClxx3K9l6oDK2Z2ZMVLXw4oDcwep6JHdBh2w4MCz r7DiopNHrnZ3flSz0msy2bf/aZ5k1TzjtmQDzJ8Ln+UlKdqyyA6UOZGA1yyYPS4j B5EwY/G1bh/1onxHD5h2yBEDtF9mEhXEigioRB/C1W9JAgMBAAGjRTBDMA4GA1Ud DwEB/wQEAwICBDASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBT0YKcYJw+Y op0QUrxexXHJ+ond9DANBgkqhkiG9w0BAQsFAAOCAYEAIoDIJo9eQ4Ifs0oxvwVR CRlZ/VZLm23dMSpMr5BLP/MvUP8W4wQY5Q2U3UO2RSABiJqLiZhS8XSPMzy7o9Ha wqbnXRbrX6TkYLrQT5pvZwImOis6+zYl2uMo3BS1wMTllNhQBAC+SqCrM+t3E5xD oasKmaqVo0flWSrHpU/hTVJLrU1rE00ghxVGjiAXoi+JvN718zI7hiIeHh+ikJlW qEVqtcaPdBKUoaf0c/xDGo/+aEr8BhPcEx5XLjvDiLdc0lHfmt3rRKCrJh8aDoMm 8PpYZwx7zc+CG4BDY+Bz6kZ5VCSd9RQ3XNjJEd25tgd3IDuiXQGp5CQoVOCQQyKv QKKhy87ZsThgcHZUR36KHXXLdRy2uAT36G2+HKdzjVTgS5nlYIdBJFi5Fvc+3Syb vM3rhXbJtg0HPZCHmzIRwLqdhyKEKxEKUy9DfkoO1RtXWLS0+1uDDdmZYLQ2qTC2 /CtEMstXpen5DIy5P1lX8ycUF/UDHOAd6ba6KNul0tCo -----END CERTIFICATE----- sqlx-0.8.3/tests/certs/ca.srl000064400000000000000000000000211046102023000142030ustar 00000000000000B6C71F4B11C0A189 sqlx-0.8.3/tests/certs/client.crt000064400000000000000000000056341046102023000151050ustar 00000000000000Certificate: Data: Version: 1 (0x0) Serial Number: e0:be:1f:7a:49:1e:49:ec Signature Algorithm: NULL Issuer: CN = postgres Validity Not Before: Apr 10 20:59:23 2021 GMT Not After : Apr 8 20:59:23 2031 GMT Subject: CN = postgres Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:bf:4f:18:ca:d8:ff:a3:93:aa:9a:3b:90:35:c7: ff:82:65:d1:d0:e8:65:9d:9c:6c:cb:70:4e:31:7e: 7e:52:ce:2d:85:7a:83:ee:b8:eb:f1:ba:37:0e:34: 66:3d:b6:db:cb:45:6f:64:0f:5c:4d:ba:53:25:c9: ff:e0:a1:39:9b:82:c9:c0:08:e8:17:6b:01:6a:99: 47:05:d8:c5:2f:83:f3:33:f7:ad:bb:f3:dd:5f:6a: 95:4f:d9:8e:1d:bc:ff:84:78:77:eb:98:40:36:2d: 9a:a3:29:a6:ba:58:90:c1:92:88:5f:07:c3:a8:a6: 06:f0:ca:f8:81:40:13:65:1d:08:6c:97:9f:d4:b4: 8d:f7:77:32:f6:2c:d4:9b:07:b3:86:3a:62:7f:da: 3d:3c:e9:96:71:cc:62:2e:ac:6d:00:ca:ac:6c:a1: b4:68:28:67:18:be:4b:31:e7:f1:c3:1d:a4:ad:05: 50:59:44:30:09:b1:91:e1:86:5d:ec:75:06:a9:70: 43:6b:81:5c:ff:98:fd:22:5c:3a:0e:08:2e:e3:b3: c4:e0:65:dd:cd:e7:f2:69:08:0a:1b:90:c4:06:c1: 06:ee:75:ee:d3:3c:ab:a2:9c:51:00:1c:56:fe:24: 92:36:ee:e1:f3:6f:0c:14:79:32:07:f9:12:2b:26: 79:c1 Exponent: 65537 (0x10001) Signature Algorithm: NULL -----BEGIN CERTIFICATE----- MIIDjjCCAfYCCQC2xx9LEcChiTANBgkqhkiG9w0BAQsFADB/MR4wHAYDVQQKExVt a2NlcnQgZGV2ZWxvcG1lbnQgQ0ExKjAoBgNVBAsMIW1laGNvZGVAR29sZW0ubG9j YWwgKFJ5YW4gTGVja2V5KTExMC8GA1UEAwwobWtjZXJ0IG1laGNvZGVAR29sZW0u bG9jYWwgKFJ5YW4gTGVja2V5KTAeFw0yMTA0MTAyMDU5MjNaFw0zMTA0MDgyMDU5 MjNaMBMxETAPBgNVBAMMCHBvc3RncmVzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A MIIBCgKCAQEAv08Yytj/o5OqmjuQNcf/gmXR0OhlnZxsy3BOMX5+Us4thXqD7rjr 8bo3DjRmPbbby0VvZA9cTbpTJcn/4KE5m4LJwAjoF2sBaplHBdjFL4PzM/etu/Pd X2qVT9mOHbz/hHh365hANi2aoymmuliQwZKIXwfDqKYG8Mr4gUATZR0IbJef1LSN 93cy9izUmwezhjpif9o9POmWccxiLqxtAMqsbKG0aChnGL5LMefxwx2krQVQWUQw CbGR4YZd7HUGqXBDa4Fc/5j9Ilw6Dggu47PE4GXdzefyaQgKG5DEBsEG7nXu0zyr opxRABxW/iSSNu7h828MFHkyB/kSKyZ5wQIDAQABMA0GCSqGSIb3DQEBCwUAA4IB gQBxzRXtmp1gXzNTnwQ+acdZ2mRkjoEkr00e5wQTXCcOhfsXG/udQaEU1SUhaCyV HppmxDB4i3aHhiGKztk6JU/SE9o4B//BbdLfmv741lwrE/5Lgx2YSBnATqDWC7rI W2Tj33Sf06y7MKgkG5TszkM2cGdYhowhsyhhpww50gKfoRBNTp935jLo3nytShiM NeQpf7/Wjcd1yIRYbWefTDJDSwGnzBoPCNHIEhAT15RUV2jGe9ctSMU2zQWInDll U8dkWRZp9cZpQCvx2HkMy7oqsigoHxSSnsMzc8gtJHdhovjoLAVu9y5mAtEjHnTd 2ud1woYVo5dDoQEaFMp1Ll4qotLhMRVDl3SBPJoKOrEQfS/4JwITzuS8C7RSlmxE UR2gPw7R39ocTE/rigUnE4WHf4q18kWrkRRZoMsvitv9FSyMkN1yaL0IintkRXzg ZkSZbzxVriE1dZ5u+Ie1zNaa5rB+yb/nzRC9HMbBtZbVgHe1ngr+pEyAMWFd4U8N HRQ= -----END CERTIFICATE----- sqlx-0.8.3/tests/certs/client.csr000064400000000000000000000063651046102023000151060ustar 00000000000000Certificate Request: Data: Version: 1 (0x0) Subject: CN = postgres Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:bf:4f:18:ca:d8:ff:a3:93:aa:9a:3b:90:35:c7: ff:82:65:d1:d0:e8:65:9d:9c:6c:cb:70:4e:31:7e: 7e:52:ce:2d:85:7a:83:ee:b8:eb:f1:ba:37:0e:34: 66:3d:b6:db:cb:45:6f:64:0f:5c:4d:ba:53:25:c9: ff:e0:a1:39:9b:82:c9:c0:08:e8:17:6b:01:6a:99: 47:05:d8:c5:2f:83:f3:33:f7:ad:bb:f3:dd:5f:6a: 95:4f:d9:8e:1d:bc:ff:84:78:77:eb:98:40:36:2d: 9a:a3:29:a6:ba:58:90:c1:92:88:5f:07:c3:a8:a6: 06:f0:ca:f8:81:40:13:65:1d:08:6c:97:9f:d4:b4: 8d:f7:77:32:f6:2c:d4:9b:07:b3:86:3a:62:7f:da: 3d:3c:e9:96:71:cc:62:2e:ac:6d:00:ca:ac:6c:a1: b4:68:28:67:18:be:4b:31:e7:f1:c3:1d:a4:ad:05: 50:59:44:30:09:b1:91:e1:86:5d:ec:75:06:a9:70: 43:6b:81:5c:ff:98:fd:22:5c:3a:0e:08:2e:e3:b3: c4:e0:65:dd:cd:e7:f2:69:08:0a:1b:90:c4:06:c1: 06:ee:75:ee:d3:3c:ab:a2:9c:51:00:1c:56:fe:24: 92:36:ee:e1:f3:6f:0c:14:79:32:07:f9:12:2b:26: 79:c1 Exponent: 65537 (0x10001) Attributes: a0:00 Signature Algorithm: sha256WithRSAEncryption b1:1f:11:89:d3:6a:a3:b3:fb:9e:9d:de:b4:cb:5c:44:0f:86: 69:c7:c5:81:f8:cc:42:24:6d:92:1c:e8:85:bc:22:ba:49:6f: d4:f0:89:21:6c:39:9d:29:31:a5:2a:21:81:76:58:1b:0a:1b: fb:46:9a:59:fd:e3:c8:7b:54:25:ad:ca:86:0f:2b:e7:aa:79: 92:d4:f5:c7:91:5d:f2:f8:ff:fe:d1:5f:0c:30:8a:1a:89:0d: 3a:d1:1b:f2:a4:77:bd:fb:3b:5a:c9:6c:15:e5:54:f9:10:ba: 58:6a:a2:ee:7e:32:dc:fa:ef:51:f8:52:63:67:6e:e8:fa:fc: 21:79:46:fb:f2:6d:16:34:6c:79:96:ae:1c:8b:2c:1b:c5:ab: b7:ac:ad:14:25:55:de:41:76:a1:47:34:0e:b4:c7:48:b1:73: e6:74:ed:17:5f:d9:f2:d0:ec:6a:6a:97:bd:7c:81:b9:22:09: 14:d0:e0:5e:b8:14:70:f3:3d:b1:aa:2e:43:c8:10:7d:00:85: 90:9c:80:9f:3d:03:c3:6c:df:f3:da:50:19:e7:5e:a0:0e:17: f9:5c:ed:83:35:38:c2:9a:5b:ea:ea:ec:8b:27:1d:51:38:8b: 94:eb:d0:69:4a:87:dd:52:49:dc:75:86:ce:5e:ee:ec:33:ff: 8d:0c:30:40 -----BEGIN CERTIFICATE REQUEST----- MIICWDCCAUACAQAwEzERMA8GA1UEAwwIcG9zdGdyZXMwggEiMA0GCSqGSIb3DQEB AQUAA4IBDwAwggEKAoIBAQC/TxjK2P+jk6qaO5A1x/+CZdHQ6GWdnGzLcE4xfn5S zi2FeoPuuOvxujcONGY9ttvLRW9kD1xNulMlyf/goTmbgsnACOgXawFqmUcF2MUv g/Mz9627891fapVP2Y4dvP+EeHfrmEA2LZqjKaa6WJDBkohfB8OopgbwyviBQBNl HQhsl5/UtI33dzL2LNSbB7OGOmJ/2j086ZZxzGIurG0AyqxsobRoKGcYvksx5/HD HaStBVBZRDAJsZHhhl3sdQapcENrgVz/mP0iXDoOCC7js8TgZd3N5/JpCAobkMQG wQbude7TPKuinFEAHFb+JJI27uHzbwwUeTIH+RIrJnnBAgMBAAGgADANBgkqhkiG 9w0BAQsFAAOCAQEAsR8RidNqo7P7np3etMtcRA+GacfFgfjMQiRtkhzohbwiuklv 1PCJIWw5nSkxpSohgXZYGwob+0aaWf3jyHtUJa3Khg8r56p5ktT1x5Fd8vj//tFf DDCKGokNOtEb8qR3vfs7WslsFeVU+RC6WGqi7n4y3PrvUfhSY2du6Pr8IXlG+/Jt FjRseZauHIssG8Wrt6ytFCVV3kF2oUc0DrTHSLFz5nTtF1/Z8tDsamqXvXyBuSIJ FNDgXrgUcPM9saouQ8gQfQCFkJyAnz0Dw2zf89pQGedeoA4X+VztgzU4wppb6urs iycdUTiLlOvQaUqH3VJJ3HWGzl7u7DP/jQwwQA== -----END CERTIFICATE REQUEST----- sqlx-0.8.3/tests/certs/server.crt000064400000000000000000000030111046102023000151200ustar 00000000000000-----BEGIN CERTIFICATE----- MIIESDCCArCgAwIBAgIQODSVfo7ZFlBmT/IUqU7BmzANBgkqhkiG9w0BAQsFADB/ MR4wHAYDVQQKExVta2NlcnQgZGV2ZWxvcG1lbnQgQ0ExKjAoBgNVBAsMIW1laGNv ZGVAR29sZW0ubG9jYWwgKFJ5YW4gTGVja2V5KTExMC8GA1UEAwwobWtjZXJ0IG1l aGNvZGVAR29sZW0ubG9jYWwgKFJ5YW4gTGVja2V5KTAeFw0xOTA2MDEwMDAwMDBa Fw0zMDA3MTgxMjE5MTNaMFUxJzAlBgNVBAoTHm1rY2VydCBkZXZlbG9wbWVudCBj ZXJ0aWZpY2F0ZTEqMCgGA1UECwwhbWVoY29kZUBHb2xlbS5sb2NhbCAoUnlhbiBM ZWNrZXkpMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt2IFkflE4SVs /WOwWjs4MHY+xIQt6LPqVpBLOeem7cm9ZdyHblojesZijV/4vbc4bwlksOBvJEkV OSanMUimT9AMwjOzJBv0Yyj8ElCI+v/2y2QJ7JHVfn5pBTbk84+lugtgP8hW5ULj tPDyE14E+8sNCXSa62C1a+lgssNLc+/EAGYQF4moQxIsZFuiI3EViLx4I6ayD/TY r4U1HBFS8sY/rWDVSh82Bx85OZCK+06xbiMpzbi5b69WwsaOh16e/yJmEa2mQKbl WZo3qT9LfMn5u1AX7KL7WKBeuohZkeI6sBMFcrO3yXGSmoSL07I2Ya2pSmJyMp8x idGp06DwnQIDAQABo2owaDAOBgNVHQ8BAf8EBAMCBaAwEwYDVR0lBAwwCgYIKwYB BQUHAwEwDAYDVR0TAQH/BAIwADAfBgNVHSMEGDAWgBT0YKcYJw+Yop0QUrxexXHJ +ond9DASBgNVHREECzAJggdzcWx4LnJzMA0GCSqGSIb3DQEBCwUAA4IBgQAoGsn1 m5xYYo2I06NFhvoRqXfNhBH3W44hHjp6PjHqONW1U8KSM7lXbElNQ+tmCN8uThU6 RM+xY1bHB7rQxwGb0RxKqQphWJcBz9Or9vmqdam2rnBqza8B560MQSBv8kXCSKKY SSpa/pRaWCbGLDgXs2RL6seBaT2S2qvRPRwxiyDPTPU3fkjyeQDw4nPCpQ/7+dtu Cc7qg/NeUllW1wKYotTSxfo3FUR08Z73j1BFOoPvUgG1m8YWiDe90pQJDIAU059z 3IT4e2Jxm8yxudrlDdXmxBBLy4tA3drXYV654PoIszs1D+U6b84w3wE8/30Y8DyX +InJUQ3kCFeZfS7a7eAtBcqCJVJBpzfieu7ekZpiG3dFo0cn5QFojtwy20VmfKJU A7D0Ibb6J/UoRVBPoY8KTlYnd+7L2dEaVaE7hSE0emrC9NXa5t+it6xeAFDX0G/1 N31vTfkZABuWrPU3XGCkb87TQxk1hRE6Yw/Hu0TM3DSreLd7dkRHnwt6KEs= -----END CERTIFICATE----- sqlx-0.8.3/tests/docker-compose.yml000064400000000000000000000306141046102023000154260ustar 00000000000000version: "3" services: # # MySQL 8.x, 5.7.x # https://www.mysql.com/support/supportedplatforms/database.html # mysql_8: image: mysql:8.0 volumes: - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" ports: - 3306 environment: MYSQL_ROOT_HOST: '%' MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: sqlx mysql_8_client_ssl: build: context: . dockerfile: mysql/Dockerfile args: IMAGE: mysql:8.0 volumes: - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" ports: - 3306 environment: MYSQL_ROOT_HOST: '%' MYSQL_DATABASE: sqlx MYSQL_ALLOW_EMPTY_PASSWORD: 1 mysql_5_7: image: mysql:5.7 volumes: - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" ports: - 3306 environment: MYSQL_ROOT_HOST: '%' MYSQL_ROOT_PASSWORD: password MYSQL_DATABASE: sqlx mysql_5_7_client_ssl: build: context: . dockerfile: mysql/Dockerfile args: IMAGE: mysql:5.7 volumes: - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" ports: - 3306 environment: MYSQL_ROOT_HOST: '%' MYSQL_DATABASE: sqlx MYSQL_ALLOW_EMPTY_PASSWORD: 1 # # MariaDB 10.11, 10.6, 10.5, 10.4 # https://mariadb.org/about/#maintenance-policy # mariadb_11_4: image: mariadb:11.4 volumes: - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" ports: - 3306 environment: MARIADB_ROOT_PASSWORD: password MARIADB_DATABASE: sqlx mariadb_11_4_client_ssl: build: context: . dockerfile: mysql/Dockerfile args: IMAGE: mariadb:11.4 volumes: - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" ports: - 3306 environment: MARIADB_DATABASE: sqlx MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: 1 mariadb_10_11: image: mariadb:10.11 volumes: - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" ports: - 3306 environment: MARIADB_ROOT_PASSWORD: password MARIADB_DATABASE: sqlx mariadb_10_11_client_ssl: build: context: . dockerfile: mysql/Dockerfile args: IMAGE: mariadb:10.11 volumes: - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" ports: - 3306 environment: MARIADB_DATABASE: sqlx MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: 1 mariadb_10_6: image: mariadb:10.6 volumes: - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" ports: - 3306 environment: MARIADB_ROOT_PASSWORD: password MARIADB_DATABASE: sqlx mariadb_10_6_client_ssl: build: context: . dockerfile: mysql/Dockerfile args: IMAGE: mariadb:10.6 volumes: - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" ports: - 3306 environment: MARIADB_DATABASE: sqlx MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: 1 mariadb_10_5: image: mariadb:10.5 volumes: - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" ports: - 3306 environment: MARIADB_ROOT_PASSWORD: password MARIADB_DATABASE: sqlx mariadb_10_5_client_ssl: build: context: . dockerfile: mysql/Dockerfile args: IMAGE: mariadb:10.5 volumes: - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" ports: - 3306 environment: MARIADB_DATABASE: sqlx MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: 1 mariadb_10_4: image: mariadb:10.4 volumes: - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" ports: - 3306 environment: MARIADB_ROOT_PASSWORD: password MARIADB_DATABASE: sqlx mariadb_10_4_client_ssl: build: context: . dockerfile: mysql/Dockerfile args: IMAGE: mariadb:10.4 volumes: - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" ports: - 3306 environment: MARIADB_DATABASE: sqlx MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: 1 # Ensure MariaDB upstream isn't regressing before release # https://mariadb.org/new-service-quay-io-mariadb-foundation-mariadb-devel/ mariadb_verylatest: image: quay.io/mariadb-foundation/mariadb-devel:verylatest volumes: - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" ports: - 3306 environment: MARIADB_ROOT_PASSWORD: password MARIADB_DATABASE: sqlx mariadb_verylatest_client_ssl: build: context: . dockerfile: mysql/Dockerfile args: IMAGE: quay.io/mariadb-foundation/mariadb-devel:verylatest volumes: - "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" ports: - 3306 environment: MARIADB_DATABASE: sqlx MARIADB_ALLOW_EMPTY_ROOT_PASSWORD: 1 # # PostgreSQL 17.x, 16.x, 15.x, 14.x, 13.x # https://www.postgresql.org/support/versioning/ # postgres_17: build: context: . dockerfile: postgres/Dockerfile args: VERSION: 17 ports: - 5432 environment: POSTGRES_DB: sqlx POSTGRES_USER: postgres POSTGRES_PASSWORD: password POSTGRES_HOST_AUTH_METHOD: scram-sha-256 POSTGRES_INITDB_ARGS: --auth-host=scram-sha-256 volumes: - "./postgres/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" # Loading `pg_stat_statements` should serve as a regression test for: # https://github.com/launchbadge/sqlx/issues/2622 command: > -c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key -c shared_preload_libraries=pg_stat_statements postgres_17_client_ssl: build: context: . dockerfile: postgres/Dockerfile args: VERSION: 17 ports: - 5432 environment: POSTGRES_DB: sqlx POSTGRES_HOST_AUTH_METHOD: trust POSTGRES_INITDB_ARGS: --auth-host=trust volumes: - "./postgres/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" command: > -c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key -c ssl_ca_file=/var/lib/postgresql/ca.crt -c hba_file=/var/lib/postgresql/pg_hba.conf postgres_16: build: context: . dockerfile: postgres/Dockerfile args: VERSION: 16 ports: - 5432 environment: POSTGRES_DB: sqlx POSTGRES_USER: postgres POSTGRES_PASSWORD: password POSTGRES_HOST_AUTH_METHOD: scram-sha-256 POSTGRES_INITDB_ARGS: --auth-host=scram-sha-256 volumes: - "./postgres/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" command: > -c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key postgres_16_client_ssl: build: context: . dockerfile: postgres/Dockerfile args: VERSION: 16 ports: - 5432 environment: POSTGRES_DB: sqlx POSTGRES_HOST_AUTH_METHOD: trust POSTGRES_INITDB_ARGS: --auth-host=trust volumes: - "./postgres/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" command: > -c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key -c ssl_ca_file=/var/lib/postgresql/ca.crt -c hba_file=/var/lib/postgresql/pg_hba.conf postgres_15: build: context: . dockerfile: postgres/Dockerfile args: VERSION: 15 ports: - 5432 environment: POSTGRES_DB: sqlx POSTGRES_USER: postgres POSTGRES_PASSWORD: password POSTGRES_HOST_AUTH_METHOD: scram-sha-256 POSTGRES_INITDB_ARGS: --auth-host=scram-sha-256 volumes: - "./postgres/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" command: > -c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key postgres_15_client_ssl: build: context: . dockerfile: postgres/Dockerfile args: VERSION: 15 ports: - 5432 environment: POSTGRES_DB: sqlx POSTGRES_HOST_AUTH_METHOD: trust POSTGRES_INITDB_ARGS: --auth-host=trust volumes: - "./postgres/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" command: > -c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key -c ssl_ca_file=/var/lib/postgresql/ca.crt -c hba_file=/var/lib/postgresql/pg_hba.conf postgres_14: build: context: . dockerfile: postgres/Dockerfile args: VERSION: 14 ports: - 5432 environment: POSTGRES_DB: sqlx POSTGRES_USER: postgres POSTGRES_PASSWORD: password POSTGRES_HOST_AUTH_METHOD: scram-sha-256 POSTGRES_INITDB_ARGS: --auth-host=scram-sha-256 volumes: - "./postgres/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" command: > -c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key postgres_14_client_ssl: build: context: . dockerfile: postgres/Dockerfile args: VERSION: 14 ports: - 5432 environment: POSTGRES_DB: sqlx POSTGRES_HOST_AUTH_METHOD: trust POSTGRES_INITDB_ARGS: --auth-host=trust volumes: - "./postgres/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" command: > -c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key -c ssl_ca_file=/var/lib/postgresql/ca.crt -c hba_file=/var/lib/postgresql/pg_hba.conf postgres_13: build: context: . dockerfile: postgres/Dockerfile args: VERSION: 13 ports: - 5432 environment: POSTGRES_DB: sqlx POSTGRES_USER: postgres POSTGRES_PASSWORD: password POSTGRES_HOST_AUTH_METHOD: scram-sha-256 POSTGRES_INITDB_ARGS: --auth-host=scram-sha-256 volumes: - "./postgres/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" command: > -c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key postgres_13_client_ssl: build: context: . dockerfile: postgres/Dockerfile args: VERSION: 13 ports: - 5432 environment: POSTGRES_DB: sqlx POSTGRES_HOST_AUTH_METHOD: trust POSTGRES_INITDB_ARGS: --auth-host=trust volumes: - "./postgres/setup.sql:/docker-entrypoint-initdb.d/setup.sql:z" command: > -c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key -c ssl_ca_file=/var/lib/postgresql/ca.crt -c hba_file=/var/lib/postgresql/pg_hba.conf sqlx-0.8.3/tests/docker.py000064400000000000000000000046611046102023000136150ustar 00000000000000import subprocess import sys import time from os import path import shutil # base dir of sqlx workspace dir_workspace = path.dirname(path.dirname(path.realpath(__file__))) # dir of tests dir_tests = path.join(dir_workspace, "tests") # start database server and return a URL to use to connect def start_database(driver, database, cwd): if driver == "sqlite": database = path.join(cwd, database) (base_path, ext) = path.splitext(database) new_database = f"{base_path}.test{ext}" shutil.copy(database, new_database) # short-circuit for sqlite return f"sqlite://{path.join(cwd, new_database)}" res = subprocess.run( ["docker-compose", "up", "-d", driver], stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=dir_tests, ) if res.returncode != 0: print(res.stderr, file=sys.stderr) if b"done" in res.stderr: time.sleep(30) # determine appropriate port for driver if driver.startswith("mysql") or driver.startswith("mariadb"): port = 3306 elif driver.startswith("postgres"): port = 5432 else: raise NotImplementedError # find port res = subprocess.run( ["docker", "inspect", f"-f='{{{{(index (index .NetworkSettings.Ports \"{port}/tcp\") 0).HostPort}}}}'", f"sqlx_{driver}_1"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=dir_tests, ) if res.returncode != 0: print(res.stderr, file=sys.stderr) port = int(res.stdout[1:-2].decode()) # need additional permissions to connect to MySQL when using SSL res = subprocess.run( ["docker", "exec", f"sqlx_{driver}_1", "mysql", "-u", "root", "-e", "GRANT ALL PRIVILEGES ON *.* TO 'root' WITH GRANT OPTION;"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=dir_tests, ) if res.returncode != 0: print(res.stderr, file=sys.stderr) # do not set password in URL if authenticating using SSL key file if driver.endswith("client_ssl"): password = "" else: password = ":password" # construct appropriate database URL if driver.startswith("mysql") or driver.startswith("mariadb"): return f"mysql://root{password}@localhost:{port}/{database}" elif driver.startswith("postgres"): return f"postgres://postgres{password}@localhost:{port}/{database}" else: raise NotImplementedError sqlx-0.8.3/tests/fixtures/mysql/posts.sql000064400000000000000000000004031046102023000166710ustar 00000000000000insert into post(post_id, user_id, content, created_at) values (1, 1, 'This new computer is lightning-fast!', timestamp(now(), '-1:00:00')), (2, 2, '@alice is a haxxor :(', timestamp(now(), '-0:30:00')); sqlx-0.8.3/tests/fixtures/mysql/users.sql000064400000000000000000000001051046102023000166610ustar 00000000000000insert into user(user_id, username) values (1, 'alice'), (2, 'bob'); sqlx-0.8.3/tests/fixtures/postgres/posts.sql000064400000000000000000000007121046102023000173750ustar 00000000000000insert into post(post_id, user_id, content, created_at) values ( '252c1d98-a9b0-4f18-8298-e59058bdfe16', '6592b7c0-b531-4613-ace5-94246b7ce0c3', 'This new computer is lightning-fast!', now() + '1 hour ago'::interval ), ( '844265f7-2472-4689-9a2e-b21f40dbf401', '6592b7c0-b531-4613-ace5-94246b7ce0c3', '@alice is a haxxor :(', now() + '30 minutes ago'::interval ); sqlx-0.8.3/tests/fixtures/postgres/users.sql000064400000000000000000000002211046102023000173610ustar 00000000000000insert into "user"(user_id, username) values ('6592b7c0-b531-4613-ace5-94246b7ce0c3', 'alice'), ('297923c5-a83c-4052-bab0-030887154e52', 'bob'); sqlx-0.8.3/tests/keys/ca.key000064400000000000000000000046641046102023000140470ustar 00000000000000-----BEGIN PRIVATE KEY----- MIIG/AIBADANBgkqhkiG9w0BAQEFAASCBuYwggbiAgEAAoIBgQCcSVX6v0hjcfeM EzvY6Dcwg0xqeqHZWkG9ufpmVCJgqzJ5GIrSHLH7s+6AmLDw1brSscj3Pg/iJ7mn IKxdD8Xd2QCYS6WIUY5Hc1yXNS0G78rd5ivBp87ntwtz+T/WyRypXinxDhcguxE/ blOJImk7dDc6rvS3+6dlE40odSxB9qDXPv3FbfrDT2acUxhMFQpAHVBxpmcZnTEX KnqOT7BnSaiWAxIOIlLeBY/TN1IZEdUv55H/TvYSpgFnrJxKV+HVeVo/hIz0MxHk r9f0jCIBAJJYU8CSxShIusb/6nIdha1x6WPu7o01HEhxboOLIX4eB5LH1glOdirR 4/3OptuAESFo/PpudNPMmjr6l7IcsdoKXHHcr2XqgMrZnZkxUtfDigNzB6nokd0G HbDgwLOvsOKik0eudnd+VLPSazLZt/9pnmTVPOO2ZAPMnwuf5SUp2rLIDpQ5kYDX LJg9LiMHkTBj8bVuH/WifEcPmHbIEQO0X2YSFcSKCKhEH8LVb0kCAwEAAQKCAYBJ kXP5vwVSWpmOxJcNefJQ0d2s2eFKMWR07RDkDoLIQo5V1qmyUnOt6ntA6Z+RHur8 t1fEmuBbMxv/gi/g9sXLspTHHATl8I4rMDDLtOpnM4WpguniFR4ekVnA0/mrH6xw RbU0lc9pRuXNmB+WlC45IHHHHAhyevHHcLan7tBQlMoNyMcooqbCPaQtvifX2Kek mqf9d7lco1QydXqPdw+w70l+pB9hq+KaRL/5SzdIOc7C6B0fs4m+KYrLxMMlc8xM n4ZlThYqNPAclqQ53Ygd015K+/iELLT5OHBseg+gYcd3DZ0u7euWp5iXif03SoyW mrkSf9iujGGi8HxgLQfYLlRp1W4/a1J2BrjIPWHv90MgfYX+50MxJfaMvm+ng3ZW suYT4n+8II+sUE1OrRWI5BmJqn4Ztnp+ql/JlI8NjGl9OvkrtBm3+gW80uvDDFYR Rt1/vnr/TyId8hdDf3hZaDYGCxANhoGT+4Ha6fT6910deqNGrwxM34jsAWx5q6EC gcEAxelxZSvHi/puodF2y2YFiZLQOX7X5tK1xUNlUReYwnRpl6og9NuUHjOToi1f 0OuUIAxFWrFwBy9ANXMPORsIjNSGSIqhx1FJSI4fpYPfrcj8xZ/a/ZTwiQkAZUVO /AcJX2Iioyc1xqrJb9f7BQv0uWFlLvD6ufuGa+RTCK0tu1a7d44k5MMuuamLbV4/ DpYKmfir67LxKa4DCnzst3xnUQsrUyLvvXd5DjXHYNxf/wVpVsZaI6pUQXCU4vLq 96ZFAoHBAMooReK5VWU1SFmQ+81W42WPBDzaOVACXtBtyrKSBcOdAh+tegGRCQRt ZqoGGYsLpNeAD1y0cmPDp1n0bmNHz8lpi08ZvL0qdnkEdzgEBU6FIJXwCMTv2sxS okkFHzDOSeJLhVeyfBOxkj5k9vD6uzpooU1LxMvQbZ5n9b3Oenieg6VI1xxUSWzS p1D9xx/OH6TNo9MlqMGip2XaXNNLCSlIgGRDHMiE6UxjIQNUC/1v+kC42w96Ioep b/EUecWnNQKBwCB8z5Zx91GtDFgX0E5XMvWAWhn2Dm7fi+MTyx/ipbrV7TduP/ax zMCgas2mcSIUab5RBVl74w3q3bloZ8lR/LdRc7GYwDG90C/O1LcQzLj0UzoFoaca udlk1uHI3MxWQ12a+GCb4SH5ixOG+re7XLLJoBhyilXZShmP42l7NBSaii937+p1 d8gWYsLTQ7qbOqZHwcDxINFctTcVhq4gB72v/a6p/3y0jfSdM1tdEY0FUGNkMAvG vAn59xbCFXwNmQKBwD63d9b7AI9BlIyU+kmeJzYexIXMffNgiOHI7hWwT7F8SGhj fXJ2+IEzH6Kn+cy1dBKXXCy6nrJsxMKXnpeWc9Rctid4KKHXRqIMzJ/IP404p/9c VJwkfZDeIhLdT+voU+us2lV2vh+t+1z4HdRtNSDNzR0Z4JgKtbdrMUYNhNle9CCX FIVpcM4eHGJ4GQq4/AA4/BZJnK1WNPDYaevzX4g7QfnZCFj9QxO7vUY3EPEziEQE FZIbphLIcj4syAqV8QKBwHmFZNUAhC5ctMRaDsc8AaOTzwVEc5ttHX5XfxWlxAaT lmNi3QGQXpuPeSe5PucbGpDCSMzPlEU36+unwgUfCoAgyiZm3ih0q8u4LUzLMC89 N9JTAKoSEtl4rqrdyNMsVi5eBcRBoyiOHuWRtyqjI+qe87Y+F6YwjLY8oetiSzmi FoFjI+Z0UMD0pqB3EMkGtIwyuibFc7eD9bAKtHT9lQdOzV/Ujo7OgFvSdxdJr1Op DA9cfDb28UfOAMuxPKzOqg== -----END PRIVATE KEY----- sqlx-0.8.3/tests/keys/client.key000064400000000000000000000032501046102023000147300ustar 00000000000000-----BEGIN PRIVATE KEY----- MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC/TxjK2P+jk6qa O5A1x/+CZdHQ6GWdnGzLcE4xfn5Szi2FeoPuuOvxujcONGY9ttvLRW9kD1xNulMl yf/goTmbgsnACOgXawFqmUcF2MUvg/Mz9627891fapVP2Y4dvP+EeHfrmEA2LZqj Kaa6WJDBkohfB8OopgbwyviBQBNlHQhsl5/UtI33dzL2LNSbB7OGOmJ/2j086ZZx zGIurG0AyqxsobRoKGcYvksx5/HDHaStBVBZRDAJsZHhhl3sdQapcENrgVz/mP0i XDoOCC7js8TgZd3N5/JpCAobkMQGwQbude7TPKuinFEAHFb+JJI27uHzbwwUeTIH +RIrJnnBAgMBAAECggEAKNCZO328XIu+lBUtGSxIKOvMLcPHGi8rTuPw6sJP9R6j u5x91UqCnBnccR1gyr3eeqmfsDtOuA6Oertz6dq7zZ/Dp0K/MW/U54c4DdlHiHGg S3AGEtleW2MD4/tIRLPz17FT9GGRIX3tRe428f6/M20txwiDB9IUHP9QsVKYULPX pzX+BMMINj70U1CcwcsIkPH9znDhwdMfjphC/eJUgITDle9EynYRhBHz55ajTTeE hPsttRPYvbXdxd1WdSnt/Xv4+N10RKcEnrPE17WrbUs9RvqOz7hW4e4QifsBf5dR 0Sw1AemmdOK5xTrA0K9D7gRv6qC8QHuTDjIntVd8gQKBgQD+PyQUNJpvUre1zK1A HBTVbX7uIqYrX6FWXFFE55HtcnrhEWIY5QCBPfOFsVdcvBJrqclkInpELjdnVbeP 25ETIKhhiP3FnJjJlNZiFXD85NmHbRJzABvNxspb+9UOuIJfB2ixSGmnEKEQIeJf QmUzz/PJ9+2ct8/rXobZ90Is6QKBgQDAoNe/bUGc/ZmKq132hCcBeHFcjdtW6fkE 6d8giLx90b1kQzYJaM+4jhYF4s1job32ZPlykAlGUCtWBGirhonioJVgiGy4fOc0 SlIcKg2Gh68FDdKGHcBN5duk0nCc+uLT1fNPqo98jy1DI6VCVej0xWBrFkMFXZ3S qJ5PWtT/GQKBgFLBkqjRBoO91PZkDPCVM2LVJT+2H4h2tDk8C2f2SFWVsdGYqumX gLaQx7d4pgsVXJmWxmrFni6bLIWCLSGyQmKLesNkp9Wuxzy2KaH7gK+Qfg3KvvqX ynUMg8m1CwCjpivwaW9rNpienQ53OQvwvKhExAG1pa4hVpgySIqiJPQhAoGAeFUB 8cdisZuKiyG6NQEhDL4csuC7IHRQ50zh4gUJGuAnG7cQzpf3CydXgp3ICHFFpeI2 IebwpEf4imd+q4gEItqF9iPDJwx/sh6rZISwplWkc9fKp5V2SDNLHo+HYckoYYTJ 1f6KXBllAQgHeIUKXb3fGYZyn6t3p91F5/SqEiECgYBzjAYDWIRi7IpMkckts2ZQ p7YXZCbUP4MALTLuWulrI5IFv7gOjW20US/CArNMc4wPv5WtY1uE59pd8Td2CW9X BX1nQXqaVlF6xLOzgqsWPxloRk7y692J9nYMKcB6VxlkFVUQfbRZksFCsn2I4Y5Z ZtG/bPbIR6NgZ6ntNa+KIg== -----END PRIVATE KEY----- sqlx-0.8.3/tests/keys/server.key000064400000000000000000000032501046102023000147600ustar 00000000000000-----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC3YgWR+UThJWz9 Y7BaOzgwdj7EhC3os+pWkEs556btyb1l3IduWiN6xmKNX/i9tzhvCWSw4G8kSRU5 JqcxSKZP0AzCM7MkG/RjKPwSUIj6//bLZAnskdV+fmkFNuTzj6W6C2A/yFblQuO0 8PITXgT7yw0JdJrrYLVr6WCyw0tz78QAZhAXiahDEixkW6IjcRWIvHgjprIP9Niv hTUcEVLyxj+tYNVKHzYHHzk5kIr7TrFuIynNuLlvr1bCxo6HXp7/ImYRraZApuVZ mjepP0t8yfm7UBfsovtYoF66iFmR4jqwEwVys7fJcZKahIvTsjZhralKYnIynzGJ 0anToPCdAgMBAAECggEABVjnXq1NE9+agP0CLG9joQ4hoGtWR13PrHyCpQqbNH3Y dvrqPA6G0FKulv7Aaw/Hpn04oWu58e3rn4IACBDdQKCJbrRBOgFSq/2K9CHDDMaf 9KhTHcHW3txixZMnM+7xXy5rvRBjcEX2C9WmyWfJb2opVChBSDHGuIHSnwPQ1G2R t8PrSTD3uN7VbpdDj0pznZN7UzTxXGNyy98ruZIY6haK6urd9BRGQD8RKe6hQL6k 5m+AWW7vhIEgBmNeYg5RHoxaeECI89L7cq1Z+0kMMqyvaXdBUTpsjXGW56kYRt+A qTwxlBbCQRTZuHz+vnuWEiJ2/YbY5b5xpraHqnc8ZQKBgQDoa2ilYqA+Fb/elgyX kaXuf/d8EGLLQ2KHk+nkJeXLnbMGq7v1ISSVLsWDkMGJyENQOHh5hJVub7ew/AK2 SGQ0FA1F6AcLJ+0aiWY+pvhesKLtW+cXVooiL4M6w8YgDZyH7V988sM0l7FejauK ZyDHDsaWOR2IT0eKg0bVh+N6XwKBgQDJ/P4Ua7wlvphTPc4mfjvTkppDpv0YDW8x t89Y5mLscsDV8okHav3MlKhKOPqEbmcw1sS0txT3NxLFUpFfONsuYsfT24z96XI4 cHKUDP1zEFq+7D+A20F3ydks5+OW5FofcDNbc2+Yg/0upXe38wVfBJj1VEWGz70C GSN+j6vugwKBgDEdKXLxgX09KVuHB8grvg3FOu4bpFThu3t89UsB+ypo+8DoH4Lw awOfa5uexlcwW5EjLco4Cz/YGdAroQMWDx62MgvYuUxRNpiJ+nI45HlWCEfySMY0 wmHw+mE7p610UuSic7A6uKdvesrJUzufCV0nMS3jiesZHbwWe6x518cvAoGBAI9P 7GpKwlS5dVRiXrkbCZGky8VCXwLIzWMmOnymAfwns0BZc/YKaIbV1s3KvZxmxNp3 F1vtJnf84FmWqsQ4D/NKbOOZO+EP2FXJGtKGoPEZ4njiIHBpoHrAgVGGOglefb8e maHCNqSsyV9mUZn3WJFBLtGp+CadkEpD0dZDU8bHAoGADRorYjov1pEEWbHhQaQP X1pQsc8Oc/tQAvh9GOJPpvJ0r2hdMMMJC/lC6+63MyKKrXScagVcajgRk1ES0V2/ 9YizDOY8S4fEtHTSbPJHSS2orXhJ8KSAxl1JbjNDlDPj9EcOvocaehssZ9Tf2bPg 7ywoQK2oCbW2Hq9WcgYIubY= -----END PRIVATE KEY----- sqlx-0.8.3/tests/migrate/macro.rs000064400000000000000000000024231046102023000150650ustar 00000000000000#![cfg(unix)] use sqlx::migrate::Migrator; use std::path::Path; static EMBEDDED_SIMPLE: Migrator = sqlx::migrate!("tests/migrate/migrations_simple"); static EMBEDDED_REVERSIBLE: Migrator = sqlx::migrate!("tests/migrate/migrations_reversible"); static EMBEDDED_SYMLINK: Migrator = sqlx::migrate!("tests/migrate/migrations_symlink"); #[sqlx_macros::test] async fn same_output() -> anyhow::Result<()> { let runtime_simple = Migrator::new(Path::new("tests/migrate/migrations_simple")).await?; let runtime_reversible = Migrator::new(Path::new("tests/migrate/migrations_reversible")).await?; let runtime_symlink = Migrator::new(Path::new("tests/migrate/migrations_symlink")).await?; assert_same(&EMBEDDED_SIMPLE, &runtime_simple); assert_same(&EMBEDDED_REVERSIBLE, &runtime_reversible); assert_same(&EMBEDDED_SYMLINK, &runtime_symlink); Ok(()) } fn assert_same(embedded: &Migrator, runtime: &Migrator) { assert_eq!(runtime.migrations.len(), embedded.migrations.len()); for (e, r) in embedded.iter().zip(runtime.iter()) { assert_eq!(e.version, r.version); assert_eq!(e.description, r.description); assert_eq!(e.migration_type, r.migration_type); assert_eq!(e.sql, r.sql); assert_eq!(e.checksum, r.checksum); } } sqlx-0.8.3/tests/migrate/migrations_reversible/20220721124650_add_table.down.sql000064400000000000000000000000471046102023000250430ustar 00000000000000DROP TABLE migrations_reversible_test; sqlx-0.8.3/tests/migrate/migrations_reversible/20220721124650_add_table.up.sql000064400000000000000000000003101046102023000245110ustar 00000000000000CREATE TABLE migrations_reversible_test ( some_id BIGINT NOT NULL PRIMARY KEY, some_payload BIGINT NOT NUll ); INSERT INTO migrations_reversible_test (some_id, some_payload) VALUES (1, 100); sqlx-0.8.3/tests/migrate/migrations_reversible/20220721125033_modify_column.down.sql000064400000000000000000000001071046102023000260010ustar 00000000000000UPDATE migrations_reversible_test SET some_payload = some_payload - 1; sqlx-0.8.3/tests/migrate/migrations_reversible/20220721125033_modify_column.up.sql000064400000000000000000000001071046102023000254560ustar 00000000000000UPDATE migrations_reversible_test SET some_payload = some_payload + 1; sqlx-0.8.3/tests/migrate/migrations_simple/20220721115250_add_test_table.sql000064400000000000000000000003001046102023000242470ustar 00000000000000CREATE TABLE migrations_simple_test ( some_id BIGINT NOT NULL PRIMARY KEY, some_payload BIGINT NOT NUll ); INSERT INTO migrations_simple_test (some_id, some_payload) VALUES (1, 100); sqlx-0.8.3/tests/migrate/migrations_simple/20220721115524_convert_type.sql000064400000000000000000000020441046102023000240450ustar 00000000000000-- Perform a tricky conversion of the payload. -- -- This script will only succeed once and will fail if executed twice. -- set up temporary target column ALTER TABLE migrations_simple_test ADD some_payload_tmp TEXT; -- perform conversion -- This will fail if `some_payload` is already a string column due to the addition. -- We add a suffix after the addition to ensure that the SQL database does not silently cast the string back to an -- integer. UPDATE migrations_simple_test SET some_payload_tmp = CONCAT(CAST((some_payload + 10) AS TEXT), '_suffix'); -- remove original column including the content ALTER TABLE migrations_simple_test DROP COLUMN some_payload; -- prepare new payload column (nullable, so we can copy over the data) ALTER TABLE migrations_simple_test ADD some_payload TEXT; -- copy new values UPDATE migrations_simple_test SET some_payload = some_payload_tmp; -- "freeze" column ALTER TABLE migrations_simple_test ALTER COLUMN some_payload SET NOT NULL; -- clean up ALTER TABLE migrations_simple_test DROP COLUMN some_payload_tmp; sqlx-0.8.3/tests/migrate/migrations_symlink/20220721115250_add_test_table.sql000064400000000000000000000003001046102023000244440ustar 00000000000000CREATE TABLE migrations_simple_test ( some_id BIGINT NOT NULL PRIMARY KEY, some_payload BIGINT NOT NUll ); INSERT INTO migrations_simple_test (some_id, some_payload) VALUES (1, 100); sqlx-0.8.3/tests/migrate/migrations_symlink/20220721115524_convert_type.sql000064400000000000000000000020441046102023000242420ustar 00000000000000-- Perform a tricky conversion of the payload. -- -- This script will only succeed once and will fail if executed twice. -- set up temporary target column ALTER TABLE migrations_simple_test ADD some_payload_tmp TEXT; -- perform conversion -- This will fail if `some_payload` is already a string column due to the addition. -- We add a suffix after the addition to ensure that the SQL database does not silently cast the string back to an -- integer. UPDATE migrations_simple_test SET some_payload_tmp = CONCAT(CAST((some_payload + 10) AS TEXT), '_suffix'); -- remove original column including the content ALTER TABLE migrations_simple_test DROP COLUMN some_payload; -- prepare new payload column (nullable, so we can copy over the data) ALTER TABLE migrations_simple_test ADD some_payload TEXT; -- copy new values UPDATE migrations_simple_test SET some_payload = some_payload_tmp; -- "freeze" column ALTER TABLE migrations_simple_test ALTER COLUMN some_payload SET NOT NULL; -- clean up ALTER TABLE migrations_simple_test DROP COLUMN some_payload_tmp; sqlx-0.8.3/tests/mssql/Dockerfile000064400000000000000000000011201046102023000151100ustar 00000000000000ARG VERSION FROM mcr.microsoft.com/mssql/server:${VERSION} # Create a config directory RUN mkdir -p /usr/config WORKDIR /usr/config # Bundle config source COPY mssql/entrypoint.sh /usr/config/entrypoint.sh COPY mssql/configure-db.sh /usr/config/configure-db.sh COPY mssql/setup.sql /usr/config/setup.sql # Grant permissions for to our scripts to be executable USER root RUN chmod +x /usr/config/entrypoint.sh RUN chmod +x /usr/config/configure-db.sh RUN chown 10001 /usr/config/entrypoint.sh RUN chown 10001 /usr/config/configure-db.sh USER 10001 ENTRYPOINT ["/usr/config/entrypoint.sh"] sqlx-0.8.3/tests/mssql/configure-db.sh000064400000000000000000000003431046102023000160240ustar 00000000000000#!/usr/bin/env bash # Wait 60 seconds for SQL Server to start up sleep 60 # Run the setup script to create the DB and the schema in the DB /opt/mssql-tools/bin/sqlcmd -S localhost -U sa -P $SA_PASSWORD -d master -i setup.sql sqlx-0.8.3/tests/mssql/describe.rs000064400000000000000000000023011046102023000152460ustar 00000000000000use sqlx::mssql::Mssql; use sqlx::{Column, Executor, TypeInfo}; use sqlx_test::new; #[sqlx_macros::test] async fn it_describes_simple() -> anyhow::Result<()> { let mut conn = new::().await?; let d = conn.describe("SELECT * FROM tweet").await?; assert_eq!(d.columns()[0].name(), "id"); assert_eq!(d.columns()[1].name(), "text"); assert_eq!(d.columns()[2].name(), "is_sent"); assert_eq!(d.columns()[3].name(), "owner_id"); assert_eq!(d.nullable(0), Some(false)); assert_eq!(d.nullable(1), Some(false)); assert_eq!(d.nullable(2), Some(false)); assert_eq!(d.nullable(3), Some(true)); assert_eq!(d.columns()[0].type_info().name(), "BIGINT"); assert_eq!(d.columns()[1].type_info().name(), "NVARCHAR"); assert_eq!(d.columns()[2].type_info().name(), "TINYINT"); assert_eq!(d.columns()[3].type_info().name(), "BIGINT"); Ok(()) } #[sqlx_macros::test] async fn it_describes_with_params() -> anyhow::Result<()> { let mut conn = new::().await?; let d = conn .describe("SELECT text FROM tweet WHERE id = @p1") .await?; assert_eq!(d.columns()[0].name(), "text"); assert_eq!(d.nullable(0), Some(false)); Ok(()) } sqlx-0.8.3/tests/mssql/entrypoint.sh000064400000000000000000000002141046102023000156700ustar 00000000000000#!/usr/bin/env bash # Start the script to create the DB and user /usr/config/configure-db.sh & # Start SQL Server /opt/mssql/bin/sqlservr sqlx-0.8.3/tests/mssql/macros.rs000064400000000000000000000010501046102023000147520ustar 00000000000000use sqlx::Mssql; use sqlx_test::new; #[sqlx_macros::test] async fn test_query_simple() -> anyhow::Result<()> { let mut conn = new::().await?; let account = sqlx::query!("select * from (select (1) as id, 'Herp Derpinson' as name, cast(null as char) as email, CAST(1 as bit) as deleted) accounts") .fetch_one(&mut conn) .await?; assert_eq!(account.id, 1); assert_eq!(account.name, "Herp Derpinson"); assert_eq!(account.email, None); assert_eq!(account.deleted, Some(true)); Ok(()) } sqlx-0.8.3/tests/mssql/mssql-2017.dockerfile000064400000000000000000000010111046102023000166740ustar 00000000000000# vim: set ft=dockerfile: ARG VERSION FROM mcr.microsoft.com/mssql/server:${VERSION} # Create a config directory RUN mkdir -p /usr/config WORKDIR /usr/config # Bundle config source COPY mssql/entrypoint.sh /usr/config/entrypoint.sh COPY mssql/configure-db.sh /usr/config/configure-db.sh COPY mssql/setup.sql /usr/config/setup.sql # Grant permissions for to our scripts to be executable USER root RUN chmod +x /usr/config/entrypoint.sh RUN chmod +x /usr/config/configure-db.sh ENTRYPOINT ["/usr/config/entrypoint.sh"] sqlx-0.8.3/tests/mssql/mssql.rs000064400000000000000000000303511046102023000146330ustar 00000000000000use futures::TryStreamExt; use sqlx::mssql::{Mssql, MssqlPoolOptions}; use sqlx::{Column, Connection, Executor, MssqlConnection, Row, Statement, TypeInfo}; use sqlx_core::mssql::MssqlRow; use sqlx_test::new; use std::sync::atomic::{AtomicI32, Ordering}; use std::time::Duration; #[sqlx_macros::test] async fn it_connects() -> anyhow::Result<()> { let mut conn = new::().await?; conn.ping().await?; conn.close().await?; Ok(()) } #[sqlx_macros::test] async fn it_can_select_expression() -> anyhow::Result<()> { let mut conn = new::().await?; let row: MssqlRow = conn.fetch_one("SELECT 4").await?; let v: i32 = row.try_get(0)?; assert_eq!(v, 4); Ok(()) } #[sqlx_macros::test] async fn it_can_select_expression_by_name() -> anyhow::Result<()> { let mut conn = new::().await?; let row: MssqlRow = conn.fetch_one("SELECT 4 as _3").await?; let v: i32 = row.try_get("_3")?; assert_eq!(v, 4); Ok(()) } #[sqlx_macros::test] async fn it_can_fail_to_connect() -> anyhow::Result<()> { let mut url = dotenvy::var("DATABASE_URL")?; url = url.replace("Password", "NotPassword"); let res = MssqlConnection::connect(&url).await; let err = res.unwrap_err(); let err = err.into_database_error().unwrap(); assert_eq!(err.message(), "Login failed for user \'sa\'."); Ok(()) } #[sqlx_macros::test] async fn it_can_inspect_errors() -> anyhow::Result<()> { let mut conn = new::().await?; let res: Result<_, sqlx::Error> = sqlx::query("select f").execute(&mut conn).await; let err = res.unwrap_err(); // can also do [as_database_error] or use `match ..` let err = err.into_database_error().unwrap(); assert_eq!(err.message(), "Invalid column name 'f'."); Ok(()) } #[sqlx_macros::test] async fn it_maths() -> anyhow::Result<()> { let mut conn = new::().await?; let value = sqlx::query("SELECT 1 + @p1") .bind(5_i32) .try_map(|row: MssqlRow| row.try_get::(0)) .fetch_one(&mut conn) .await?; assert_eq!(6_i32, value); Ok(()) } #[sqlx_macros::test] async fn it_executes() -> anyhow::Result<()> { let mut conn = new::().await?; let _ = conn .execute( r#" CREATE TABLE #users (id INTEGER PRIMARY KEY); "#, ) .await?; for index in 1..=10_i32 { let done = sqlx::query("INSERT INTO #users (id) VALUES (@p1)") .bind(index * 2) .execute(&mut conn) .await?; assert_eq!(done.rows_affected(), 1); } let sum: i32 = sqlx::query("SELECT id FROM #users") .try_map(|row: MssqlRow| row.try_get::(0)) .fetch(&mut conn) .try_fold(0_i32, |acc, x| async move { Ok(acc + x) }) .await?; assert_eq!(sum, 110); Ok(()) } #[sqlx_macros::test] async fn it_can_return_1000_rows() -> anyhow::Result<()> { let mut conn = new::().await?; let _ = conn .execute( r#" CREATE TABLE #users (id INTEGER PRIMARY KEY); "#, ) .await?; for index in 1..=1000_i32 { let done = sqlx::query("INSERT INTO #users (id) VALUES (@p1)") .bind(index * 2) .execute(&mut conn) .await?; assert_eq!(done.rows_affected(), 1); } let sum: i32 = sqlx::query("SELECT id FROM #users") .try_map(|row: MssqlRow| row.try_get::(0)) .fetch(&mut conn) .try_fold(0_i32, |acc, x| async move { Ok(acc + x) }) .await?; assert_eq!(sum, 1001000); Ok(()) } #[sqlx_macros::test] async fn it_selects_null() -> anyhow::Result<()> { let mut conn = new::().await?; let (_, val): (i32, Option) = sqlx::query_as("SELECT 5, NULL") .fetch_one(&mut conn) .await?; assert!(val.is_none()); let val: Option = conn.fetch_one("SELECT 10, NULL").await?.try_get(1)?; assert!(val.is_none()); Ok(()) } #[sqlx_macros::test] async fn it_binds_empty_string_and_null() -> anyhow::Result<()> { let mut conn = new::().await?; let (val, val2): (String, Option) = sqlx::query_as("SELECT @p1, @p2") .bind("") .bind(None::) .fetch_one(&mut conn) .await?; assert!(val.is_empty()); assert!(val2.is_none()); Ok(()) } #[sqlx_macros::test] async fn it_can_work_with_transactions() -> anyhow::Result<()> { let mut conn = new::().await?; conn.execute("IF OBJECT_ID('_sqlx_users_1922', 'U') IS NULL CREATE TABLE _sqlx_users_1922 (id INTEGER PRIMARY KEY)") .await?; conn.execute("DELETE FROM _sqlx_users_1922").await?; // begin .. rollback let mut tx = conn.begin().await?; sqlx::query("INSERT INTO _sqlx_users_1922 (id) VALUES ($1)") .bind(10_i32) .execute(&mut tx) .await?; tx.rollback().await?; let (count,): (i32,) = sqlx::query_as("SELECT COUNT(*) FROM _sqlx_users_1922") .fetch_one(&mut conn) .await?; assert_eq!(count, 0); // begin .. commit let mut tx = conn.begin().await?; sqlx::query("INSERT INTO _sqlx_users_1922 (id) VALUES (@p1)") .bind(10_i32) .execute(&mut tx) .await?; tx.commit().await?; let (count,): (i32,) = sqlx::query_as("SELECT COUNT(*) FROM _sqlx_users_1922") .fetch_one(&mut conn) .await?; assert_eq!(count, 1); // begin .. (drop) { let mut tx = conn.begin().await?; sqlx::query("INSERT INTO _sqlx_users_1922 (id) VALUES (@p1)") .bind(20_i32) .execute(&mut tx) .await?; } conn = new::().await?; let (count,): (i32,) = sqlx::query_as("SELECT COUNT(*) FROM _sqlx_users_1922") .fetch_one(&mut conn) .await?; assert_eq!(count, 1); Ok(()) } #[sqlx_macros::test] async fn it_can_work_with_nested_transactions() -> anyhow::Result<()> { let mut conn = new::().await?; conn.execute("IF OBJECT_ID('_sqlx_users_2523', 'U') IS NULL CREATE TABLE _sqlx_users_2523 (id INTEGER PRIMARY KEY)") .await?; conn.execute("DELETE FROM _sqlx_users_2523").await?; // begin let mut tx = conn.begin().await?; // insert a user sqlx::query("INSERT INTO _sqlx_users_2523 (id) VALUES (@p1)") .bind(50_i32) .execute(&mut tx) .await?; // begin once more let mut tx2 = tx.begin().await?; // insert another user sqlx::query("INSERT INTO _sqlx_users_2523 (id) VALUES (@p1)") .bind(10_i32) .execute(&mut tx2) .await?; // never mind, rollback tx2.rollback().await?; // did we really? let (count,): (i32,) = sqlx::query_as("SELECT COUNT(*) FROM _sqlx_users_2523") .fetch_one(&mut tx) .await?; assert_eq!(count, 1); // actually, commit tx.commit().await?; // did we really? let (count,): (i32,) = sqlx::query_as("SELECT COUNT(*) FROM _sqlx_users_2523") .fetch_one(&mut conn) .await?; assert_eq!(count, 1); Ok(()) } #[sqlx_macros::test] async fn it_can_prepare_then_execute() -> anyhow::Result<()> { let mut conn = new::().await?; let mut tx = conn.begin().await?; let tweet_id: i64 = sqlx::query_scalar( "INSERT INTO tweet ( id, text ) OUTPUT INSERTED.id VALUES ( 50, 'Hello, World' )", ) .fetch_one(&mut tx) .await?; let statement = tx.prepare("SELECT * FROM tweet WHERE id = @p1").await?; assert_eq!(statement.column(0).name(), "id"); assert_eq!(statement.column(1).name(), "text"); assert_eq!(statement.column(2).name(), "is_sent"); assert_eq!(statement.column(3).name(), "owner_id"); assert_eq!(statement.column(0).type_info().name(), "BIGINT"); assert_eq!(statement.column(1).type_info().name(), "NVARCHAR"); assert_eq!(statement.column(2).type_info().name(), "TINYINT"); assert_eq!(statement.column(3).type_info().name(), "BIGINT"); let row = statement.query().bind(tweet_id).fetch_one(&mut tx).await?; let tweet_text: String = row.try_get("text")?; assert_eq!(tweet_text, "Hello, World"); Ok(()) } // MSSQL-specific copy of the test case in `tests/any/pool.rs` // because MSSQL has its own bespoke syntax for temporary tables. #[sqlx_macros::test] async fn test_pool_callbacks() -> anyhow::Result<()> { #[derive(sqlx::FromRow, Debug, PartialEq, Eq)] struct ConnStats { id: i32, before_acquire_calls: i32, after_release_calls: i32, } sqlx_test::setup_if_needed(); let current_id = AtomicI32::new(0); let pool = MssqlPoolOptions::new() .max_connections(1) .acquire_timeout(Duration::from_secs(5)) .after_connect(move |conn, meta| { assert_eq!(meta.age, Duration::ZERO); assert_eq!(meta.idle_for, Duration::ZERO); let id = current_id.fetch_add(1, Ordering::AcqRel); Box::pin(async move { let statement = format!( // language=MSSQL r#" CREATE TABLE #conn_stats( id int primary key, before_acquire_calls int default 0, after_release_calls int default 0 ); INSERT INTO #conn_stats(id) VALUES ({}); "#, // Until we have generalized bind parameters id ); conn.execute(&statement[..]).await?; Ok(()) }) }) .before_acquire(|conn, meta| { // `age` and `idle_for` should both be nonzero assert_ne!(meta.age, Duration::ZERO); assert_ne!(meta.idle_for, Duration::ZERO); Box::pin(async move { // MSSQL doesn't support UPDATE ... RETURNING either sqlx::query( r#" UPDATE #conn_stats SET before_acquire_calls = before_acquire_calls + 1 "#, ) .execute(&mut *conn) .await?; let stats: ConnStats = sqlx::query_as("SELECT * FROM #conn_stats") .fetch_one(conn) .await?; // For even IDs, cap by the number of before_acquire calls. // Ignore the check for odd IDs. Ok((stats.id & 1) == 1 || stats.before_acquire_calls < 3) }) }) .after_release(|conn, meta| { // `age` should be nonzero but `idle_for` should be zero. assert_ne!(meta.age, Duration::ZERO); assert_eq!(meta.idle_for, Duration::ZERO); Box::pin(async move { sqlx::query( r#" UPDATE #conn_stats SET after_release_calls = after_release_calls + 1 "#, ) .execute(&mut *conn) .await?; let stats: ConnStats = sqlx::query_as("SELECT * FROM #conn_stats") .fetch_one(conn) .await?; // For odd IDs, cap by the number of before_release calls. // Ignore the check for even IDs. Ok((stats.id & 1) == 0 || stats.after_release_calls < 4) }) }) // Don't establish a connection yet. .connect_lazy(&std::env::var("DATABASE_URL")?)?; // Expected pattern of (id, before_acquire_calls, after_release_calls) let pattern = [ // The connection pool starts empty. (0, 0, 0), (0, 1, 1), (0, 2, 2), (1, 0, 0), (1, 1, 1), (1, 2, 2), // We should expect one more `acquire` because the ID is odd (1, 3, 3), (2, 0, 0), (2, 1, 1), (2, 2, 2), (3, 0, 0), ]; for (id, before_acquire_calls, after_release_calls) in pattern { let conn_stats: ConnStats = sqlx::query_as("SELECT * FROM #conn_stats") .fetch_one(&pool) .await?; assert_eq!( conn_stats, ConnStats { id, before_acquire_calls, after_release_calls } ); } pool.close().await; Ok(()) } sqlx-0.8.3/tests/mssql/setup.sql000064400000000000000000000005771046102023000150160ustar 00000000000000IF DB_ID('sqlx') IS NULL BEGIN CREATE DATABASE sqlx; END; GO USE sqlx; GO IF OBJECT_ID('tweet') IS NULL BEGIN CREATE TABLE tweet ( id BIGINT NOT NULL PRIMARY KEY, text NVARCHAR(4000) NOT NULL, is_sent TINYINT NOT NULL DEFAULT 1, owner_id BIGINT ); END; GO sqlx-0.8.3/tests/mssql/types.rs000064400000000000000000000020241046102023000146340ustar 00000000000000use sqlx::mssql::Mssql; use sqlx_test::test_type; test_type!(null>(Mssql, "CAST(NULL as INT)" == None:: )); test_type!(u8( Mssql, "CAST(5 AS TINYINT)" == 5_u8, "CAST(0 AS TINYINT)" == 0_u8, "CAST(255 AS TINYINT)" == 255_u8, )); test_type!(i8( Mssql, "CAST(5 AS TINYINT)" == 5_i8, "CAST(0 AS TINYINT)" == 0_i8 )); test_type!(i16(Mssql, "CAST(21415 AS SMALLINT)" == 21415_i16)); test_type!(i32(Mssql, "CAST(2141512 AS INT)" == 2141512_i32)); test_type!(i64(Mssql, "CAST(32324324432 AS BIGINT)" == 32324324432_i64)); test_type!(f32( Mssql, "CAST(3.1410000324249268 AS REAL)" == 3.141f32 as f64 as f32 )); test_type!(f64( Mssql, "CAST(939399419.1225182 AS FLOAT)" == 939399419.1225182_f64 )); test_type!(str_nvarchar(Mssql, "CAST('this is foo' as NVARCHAR)" == "this is foo", )); test_type!(str(Mssql, "'this is foo'" == "this is foo", "''" == "", )); test_type!(bool( Mssql, "CAST(1 as BIT)" == true, "CAST(0 as BIT)" == false )); sqlx-0.8.3/tests/mysql/Dockerfile000064400000000000000000000007121046102023000151240ustar 00000000000000ARG IMAGE FROM ${IMAGE} # Copy SSL certificate (and key) COPY certs/server.crt /etc/mysql/ssl/server.crt COPY certs/ca.crt /etc/mysql/ssl/ca.crt COPY keys/server.key /etc/mysql/ssl/server.key COPY mysql/my.cnf /etc/mysql/my.cnf # Fix permissions RUN chown mysql:mysql /etc/mysql/ssl/server.crt /etc/mysql/ssl/server.key RUN chmod 0600 /etc/mysql/ssl/server.crt /etc/mysql/ssl/server.key # Create dir for secure-file-priv RUN mkdir -p /var/lib/mysql-files sqlx-0.8.3/tests/mysql/derives.rs000064400000000000000000000163651046102023000151540ustar 00000000000000use sqlx_mysql::MySql; use sqlx_test::new; #[sqlx::test] async fn test_derive_strong_enum() -> anyhow::Result<()> { #[derive(sqlx::Type, PartialEq, Eq, Debug)] #[sqlx(rename_all = "PascalCase")] enum PascalCaseEnum { FooFoo, BarBar, BazBaz, } #[derive(sqlx::Type, PartialEq, Eq, Debug)] #[sqlx(rename_all = "camelCase")] enum CamelCaseEnum { FooFoo, BarBar, BazBaz, } #[derive(sqlx::Type, PartialEq, Eq, Debug)] #[sqlx(rename_all = "snake_case")] enum SnakeCaseEnum { FooFoo, BarBar, BazBaz, } #[derive(sqlx::Type, PartialEq, Eq, Debug)] #[sqlx(rename_all = "SCREAMING_SNAKE_CASE")] enum ScreamingSnakeCaseEnum { FooFoo, BarBar, BazBaz, } #[derive(sqlx::Type, PartialEq, Eq, Debug)] #[sqlx(rename_all = "kebab-case")] enum KebabCaseEnum { FooFoo, BarBar, BazBaz, } #[derive(sqlx::Type, PartialEq, Eq, Debug)] #[sqlx(rename_all = "lowercase")] enum LowerCaseEnum { FooFoo, BarBar, BazBaz, } #[derive(sqlx::Type, PartialEq, Eq, Debug)] #[sqlx(rename_all = "UPPERCASE")] enum UpperCaseEnum { FooFoo, BarBar, BazBaz, } #[derive(sqlx::Type, PartialEq, Eq, Debug)] enum DefaultCaseEnum { FooFoo, BarBar, BazBaz, } #[derive(sqlx::FromRow, PartialEq, Eq, Debug)] struct StrongEnumRow { pascal_case: PascalCaseEnum, camel_case: CamelCaseEnum, snake_case: SnakeCaseEnum, screaming_snake_case: ScreamingSnakeCaseEnum, kebab_case: KebabCaseEnum, lowercase: LowerCaseEnum, uppercase: UpperCaseEnum, default_case: DefaultCaseEnum, } let mut conn = new::().await?; sqlx::raw_sql( r#" CREATE TEMPORARY TABLE strong_enum ( pascal_case ENUM('FooFoo', 'BarBar', 'BazBaz'), camel_case ENUM('fooFoo', 'barBar', 'bazBaz'), snake_case ENUM('foo_foo', 'bar_bar', 'baz_baz'), screaming_snake_case ENUM('FOO_FOO', 'BAR_BAR', 'BAZ_BAZ'), kebab_case ENUM('foo-foo', 'bar-bar', 'baz-baz'), lowercase ENUM('foofoo', 'barbar', 'bazbaz'), uppercase ENUM('FOOFOO', 'BARBAR', 'BAZBAZ'), default_case ENUM('FooFoo', 'BarBar', 'BazBaz') ); "#, ) .execute(&mut conn) .await?; let input = StrongEnumRow { pascal_case: PascalCaseEnum::FooFoo, camel_case: CamelCaseEnum::BarBar, snake_case: SnakeCaseEnum::BazBaz, screaming_snake_case: ScreamingSnakeCaseEnum::FooFoo, kebab_case: KebabCaseEnum::BarBar, lowercase: LowerCaseEnum::BazBaz, uppercase: UpperCaseEnum::FooFoo, default_case: DefaultCaseEnum::BarBar, }; sqlx::query( r#" INSERT INTO strong_enum( pascal_case, camel_case, snake_case, screaming_snake_case, kebab_case, lowercase, uppercase, default_case ) VALUES (?, ?, ?, ?, ?, ?, ?, ?) "#, ) .bind(&input.pascal_case) .bind(&input.camel_case) .bind(&input.snake_case) .bind(&input.screaming_snake_case) .bind(&input.kebab_case) .bind(&input.lowercase) .bind(&input.uppercase) .bind(&input.default_case) .execute(&mut conn) .await?; let output: StrongEnumRow = sqlx::query_as("SELECT * FROM strong_enum") .fetch_one(&mut conn) .await?; assert_eq!(input, output); Ok(()) } #[sqlx::test] async fn test_derive_weak_enum() -> anyhow::Result<()> { #[derive(sqlx::Type, Debug, PartialEq, Eq)] #[repr(i8)] enum WeakEnumI8 { Foo = i8::MIN, Bar = 0, Baz = i8::MAX, } #[derive(sqlx::Type, Debug, PartialEq, Eq)] #[repr(i16)] enum WeakEnumI16 { Foo = i16::MIN, Bar = 0, Baz = i16::MAX, } #[derive(sqlx::Type, Debug, PartialEq, Eq)] #[repr(i32)] enum WeakEnumI32 { Foo = i32::MIN, Bar = 0, Baz = i32::MAX, } #[derive(sqlx::Type, Debug, PartialEq, Eq)] #[repr(i64)] enum WeakEnumI64 { Foo = i64::MIN, Bar = 0, Baz = i64::MAX, } #[derive(sqlx::Type, Debug, PartialEq, Eq)] #[repr(u8)] enum WeakEnumU8 { Foo = 0, Bar = 1, Baz = u8::MAX, } #[derive(sqlx::Type, Debug, PartialEq, Eq)] #[repr(u16)] enum WeakEnumU16 { Foo = 0, Bar = 1, Baz = u16::MAX, } #[derive(sqlx::Type, Debug, PartialEq, Eq)] #[repr(u32)] enum WeakEnumU32 { Foo = 0, Bar = 1, Baz = u32::MAX, } #[derive(sqlx::Type, Debug, PartialEq, Eq)] #[repr(u64)] enum WeakEnumU64 { Foo = 0, Bar = 1, Baz = u64::MAX, } #[derive(sqlx::FromRow, Debug, PartialEq, Eq)] struct WeakEnumRow { i8: WeakEnumI8, i16: WeakEnumI16, i32: WeakEnumI32, i64: WeakEnumI64, u8: WeakEnumU8, u16: WeakEnumU16, u32: WeakEnumU32, u64: WeakEnumU64, } let mut conn = new::().await?; sqlx::raw_sql( r#" CREATE TEMPORARY TABLE weak_enum ( i8 TINYINT, i16 SMALLINT, i32 INT, i64 BIGINT, u8 TINYINT UNSIGNED, u16 SMALLINT UNSIGNED, u32 INT UNSIGNED, u64 BIGINT UNSIGNED ) "#, ) .execute(&mut conn) .await?; let rows_in = vec![ WeakEnumRow { i8: WeakEnumI8::Foo, i16: WeakEnumI16::Foo, i32: WeakEnumI32::Foo, i64: WeakEnumI64::Foo, u8: WeakEnumU8::Foo, u16: WeakEnumU16::Foo, u32: WeakEnumU32::Foo, u64: WeakEnumU64::Foo, }, WeakEnumRow { i8: WeakEnumI8::Bar, i16: WeakEnumI16::Bar, i32: WeakEnumI32::Bar, i64: WeakEnumI64::Bar, u8: WeakEnumU8::Bar, u16: WeakEnumU16::Bar, u32: WeakEnumU32::Bar, u64: WeakEnumU64::Bar, }, WeakEnumRow { i8: WeakEnumI8::Baz, i16: WeakEnumI16::Baz, i32: WeakEnumI32::Baz, i64: WeakEnumI64::Baz, u8: WeakEnumU8::Baz, u16: WeakEnumU16::Baz, u32: WeakEnumU32::Baz, u64: WeakEnumU64::Baz, }, ]; for row in &rows_in { sqlx::query( r#" INSERT INTO weak_enum(i8, i16, i32, i64, u8, u16, u32, u64) VALUES (?, ?, ?, ?, ?, ?, ?, ?) "#, ) .bind(&row.i8) .bind(&row.i16) .bind(&row.i32) .bind(&row.i64) .bind(&row.u8) .bind(&row.u16) .bind(&row.u32) .bind(&row.u64) .execute(&mut conn) .await?; } let rows_out: Vec = sqlx::query_as("SELECT * FROM weak_enum") .fetch_all(&mut conn) .await?; assert_eq!(rows_in, rows_out); Ok(()) } sqlx-0.8.3/tests/mysql/describe.rs000064400000000000000000000036701046102023000152660ustar 00000000000000use sqlx::mysql::MySql; use sqlx::{Column, Executor, Type, TypeInfo}; use sqlx_test::new; #[sqlx_macros::test] async fn it_describes_simple() -> anyhow::Result<()> { let mut conn = new::().await?; let d = conn.describe("SELECT * FROM tweet").await?; assert_eq!(d.columns()[0].name(), "id"); assert_eq!(d.columns()[1].name(), "created_at"); assert_eq!(d.columns()[2].name(), "text"); assert_eq!(d.columns()[3].name(), "owner_id"); assert_eq!(d.nullable(0), Some(false)); assert_eq!(d.nullable(1), Some(false)); assert_eq!(d.nullable(2), Some(false)); assert_eq!(d.nullable(3), Some(true)); assert_eq!(d.columns()[0].type_info().name(), "BIGINT"); assert_eq!(d.columns()[1].type_info().name(), "TIMESTAMP"); assert_eq!(d.columns()[2].type_info().name(), "TEXT"); assert_eq!(d.columns()[3].type_info().name(), "BIGINT"); Ok(()) } #[sqlx_macros::test] async fn test_boolean() -> anyhow::Result<()> { let mut conn = new::().await?; conn.execute( r#" CREATE TEMPORARY TABLE with_bit_and_tinyint ( id INT PRIMARY KEY AUTO_INCREMENT, value_bit_1 BIT(1), value_bool BOOLEAN, bit_n BIT(64), value_int TINYINT ); "#, ) .await?; let d = conn.describe("SELECT * FROM with_bit_and_tinyint").await?; assert_eq!(d.column(2).name(), "value_bool"); assert_eq!(d.column(2).type_info().name(), "BOOLEAN"); assert_eq!(d.column(1).name(), "value_bit_1"); assert_eq!(d.column(1).type_info().name(), "BIT"); assert!(>::compatible(&d.column(1).type_info())); assert!(>::compatible(&d.column(2).type_info())); Ok(()) } #[sqlx_macros::test] async fn uses_alias_name() -> anyhow::Result<()> { let mut conn = new::().await?; let d = conn .describe("SELECT text AS tweet_text FROM tweet") .await?; assert_eq!(d.columns()[0].name(), "tweet_text"); Ok(()) } sqlx-0.8.3/tests/mysql/error.rs000064400000000000000000000041041046102023000146300ustar 00000000000000use sqlx::{error::ErrorKind, mysql::MySql, Connection}; use sqlx_test::new; #[sqlx_macros::test] async fn it_fails_with_unique_violation() -> anyhow::Result<()> { let mut conn = new::().await?; let mut tx = conn.begin().await?; sqlx::query("INSERT INTO tweet(id, text, owner_id) VALUES (1, 'Foo', 1)") .execute(&mut *tx) .await?; let res: Result<_, sqlx::Error> = sqlx::query("INSERT INTO tweet VALUES (1, NOW(), 'Foo', 1);") .execute(&mut *tx) .await; let err = res.unwrap_err(); let err = err.into_database_error().unwrap(); assert_eq!(err.kind(), ErrorKind::UniqueViolation); Ok(()) } #[sqlx_macros::test] async fn it_fails_with_foreign_key_violation() -> anyhow::Result<()> { let mut conn = new::().await?; let mut tx = conn.begin().await?; let res: Result<_, sqlx::Error> = sqlx::query("INSERT INTO tweet_reply (tweet_id, text) VALUES (1, 'Reply!');") .execute(&mut *tx) .await; let err = res.unwrap_err(); let err = err.into_database_error().unwrap(); assert_eq!(err.kind(), ErrorKind::ForeignKeyViolation); Ok(()) } #[sqlx_macros::test] async fn it_fails_with_not_null_violation() -> anyhow::Result<()> { let mut conn = new::().await?; let mut tx = conn.begin().await?; let res: Result<_, sqlx::Error> = sqlx::query("INSERT INTO tweet (text) VALUES (null);") .execute(&mut *tx) .await; let err = res.unwrap_err(); let err = err.into_database_error().unwrap(); assert_eq!(err.kind(), ErrorKind::NotNullViolation); Ok(()) } #[sqlx_macros::test] async fn it_fails_with_check_violation() -> anyhow::Result<()> { let mut conn = new::().await?; let mut tx = conn.begin().await?; let res: Result<_, sqlx::Error> = sqlx::query("INSERT INTO products VALUES (1, 'Product 1', 0);") .execute(&mut *tx) .await; let err = res.unwrap_err(); let err = err.into_database_error().unwrap(); assert_eq!(err.kind(), ErrorKind::CheckViolation); Ok(()) } sqlx-0.8.3/tests/mysql/fixtures/comments.sql000064400000000000000000000006121046102023000173500ustar 00000000000000insert into comment(comment_id, post_id, user_id, content, created_at) values (1, 1, 2, 'lol bet ur still bad, 1v1 me', timestamp(now(), '-0:50:00')), (2, 1, 1, 'you''re on!', timestamp(now(), '-0:45:00')), (3, 2, 1, 'lol you''re just mad you lost :P', timestamp(now(), '-0:15:00')); sqlx-0.8.3/tests/mysql/fixtures/posts.sql000064400000000000000000000004031046102023000166710ustar 00000000000000insert into post(post_id, user_id, content, created_at) values (1, 1, 'This new computer is lightning-fast!', timestamp(now(), '-1:00:00')), (2, 2, '@alice is a haxxor :(', timestamp(now(), '-0:30:00')); sqlx-0.8.3/tests/mysql/fixtures/users.sql000064400000000000000000000001051046102023000166610ustar 00000000000000insert into user(user_id, username) values (1, 'alice'), (2, 'bob'); sqlx-0.8.3/tests/mysql/macros.rs000064400000000000000000000312311046102023000147640ustar 00000000000000use sqlx::{Connection, MySql, MySqlConnection, Transaction}; use sqlx_test::new; #[sqlx_macros::test] async fn macro_select_from_cte() -> anyhow::Result<()> { let mut conn = new::().await?; let account = sqlx::query!("select * from (select (1) as id, 'Herp Derpinson' as name, cast(null as char) email) accounts") .fetch_one(&mut conn) .await?; assert_eq!(account.id, 1); assert_eq!(account.name, "Herp Derpinson"); // MySQL can tell us the nullability of expressions, ain't that cool assert_eq!(account.email, None); Ok(()) } #[sqlx_macros::test] async fn macro_select_from_cte_bind() -> anyhow::Result<()> { let mut conn = new::().await?; let account = sqlx::query!( "select * from (select (1) as id, 'Herp Derpinson' as name) accounts where id = ?", 1i32 ) .fetch_one(&mut conn) .await?; println!("{account:?}"); println!("{}: {}", account.id, account.name); Ok(()) } #[derive(Debug)] struct RawAccount { r#type: i32, name: Option, } #[sqlx_macros::test] async fn test_query_as_raw() -> anyhow::Result<()> { let mut conn = new::().await?; let account = sqlx::query_as!( RawAccount, "SELECT * from (select 1 as type, cast(null as char) as name) accounts" ) .fetch_one(&mut conn) .await?; assert_eq!(account.name, None); assert_eq!(account.r#type, 1); println!("{account:?}"); Ok(()) } #[sqlx_macros::test] async fn test_query_scalar() -> anyhow::Result<()> { let mut conn = new::().await?; let id = sqlx::query_scalar!("select 1").fetch_one(&mut conn).await?; // MySQL tells us `LONG LONG` while MariaDB just `LONG` assert_eq!(id, 1); // invalid column names are ignored let id = sqlx::query_scalar!(r#"select 1 as `&foo`"#) .fetch_one(&mut conn) .await?; assert_eq!(id, 1); let id = sqlx::query_scalar!(r#"select 1 as `foo!`"#) .fetch_one(&mut conn) .await?; assert_eq!(id, 1); let id = sqlx::query_scalar!(r#"select 1 as `foo?`"#) .fetch_one(&mut conn) .await?; assert_eq!(id, Some(1)); let id = sqlx::query_scalar!(r#"select 1 as `foo: MyInt`"#) .fetch_one(&mut conn) .await?; assert_eq!(id, MyInt(1)); let id = sqlx::query_scalar!(r#"select 1 as `foo?: MyInt`"#) .fetch_one(&mut conn) .await?; assert_eq!(id, Some(MyInt(1))); let id = sqlx::query_scalar!(r#"select 1 as `foo!: MyInt`"#) .fetch_one(&mut conn) .await?; assert_eq!(id, MyInt(1)); let id: MyInt = sqlx::query_scalar!(r#"select 1 as `foo: _`"#) .fetch_one(&mut conn) .await?; assert_eq!(id, MyInt(1)); let id: MyInt = sqlx::query_scalar!(r#"select 1 as `foo?: _`"#) .fetch_one(&mut conn) .await? // don't hint that it should be `Option` .unwrap(); assert_eq!(id, MyInt(1)); let id: MyInt = sqlx::query_scalar!(r#"select 1 as `foo!: _`"#) .fetch_one(&mut conn) .await?; assert_eq!(id, MyInt(1)); Ok(()) } #[sqlx_macros::test] async fn test_query_as_bool() -> anyhow::Result<()> { let mut conn = new::().await?; struct Article { id: i32, deleted: bool, } let article = sqlx::query_as_unchecked!( Article, "select * from (select 51 as id, true as deleted) articles" ) .fetch_one(&mut conn) .await?; assert_eq!(51, article.id); assert_eq!(true, article.deleted); Ok(()) } #[sqlx_macros::test] async fn test_query_bytes() -> anyhow::Result<()> { let mut conn = new::().await?; let rec = sqlx::query!("SELECT X'01AF' as _1") .fetch_one(&mut conn) .await?; assert_eq!(rec._1, &[0x01_u8, 0xAF_u8]); Ok(()) } #[sqlx_macros::test] async fn test_column_override_not_null() -> anyhow::Result<()> { let mut conn = new::().await?; let record = sqlx::query!("select * from (select 1 as `id!`) records") .fetch_one(&mut conn) .await?; assert_eq!(record.id, 1); Ok(()) } #[sqlx_macros::test] async fn test_column_override_nullable() -> anyhow::Result<()> { let mut conn = new::().await?; // MySQL by default tells us `id` is not-null let record = sqlx::query!("select * from (select 1 as `id?`) records") .fetch_one(&mut conn) .await?; assert_eq!(record.id, Some(1)); Ok(()) } async fn with_test_row<'a>( conn: &'a mut MySqlConnection, ) -> anyhow::Result<(Transaction<'a, MySql>, MyInt)> { let mut transaction = conn.begin().await?; let id = sqlx::query!("INSERT INTO tweet(text, owner_id) VALUES ('#sqlx is pretty cool!', 1)") .execute(&mut *transaction) .await? .last_insert_id(); Ok((transaction, MyInt(id as i64))) } #[derive(PartialEq, Eq, Debug, sqlx::Type)] #[sqlx(transparent)] struct MyInt(i64); struct Record { id: MyInt, } struct OptionalRecord { id: Option, } #[sqlx_macros::test] async fn test_column_override_wildcard() -> anyhow::Result<()> { let mut conn = new::().await?; let (mut conn, id) = with_test_row(&mut conn).await?; let record = sqlx::query_as!(Record, "select id as `id: _` from tweet") .fetch_one(&mut *conn) .await?; assert_eq!(record.id, id); // this syntax is also useful for expressions let record = sqlx::query_as!(Record, "select * from (select 1 as `id: _`) records") .fetch_one(&mut *conn) .await?; assert_eq!(record.id, MyInt(1)); let record = sqlx::query_as!(OptionalRecord, "select owner_id as `id: _` from tweet") .fetch_one(&mut *conn) .await?; assert_eq!(record.id, Some(MyInt(1))); Ok(()) } #[sqlx_macros::test] async fn test_column_override_wildcard_not_null() -> anyhow::Result<()> { let mut conn = new::().await?; let (mut conn, _) = with_test_row(&mut conn).await?; let record = sqlx::query_as!(Record, "select owner_id as `id!: _` from tweet") .fetch_one(&mut *conn) .await?; assert_eq!(record.id, MyInt(1)); Ok(()) } #[sqlx_macros::test] async fn test_column_override_wildcard_nullable() -> anyhow::Result<()> { let mut conn = new::().await?; let (mut conn, id) = with_test_row(&mut conn).await?; let record = sqlx::query_as!(OptionalRecord, "select id as `id?: _` from tweet") .fetch_one(&mut *conn) .await?; assert_eq!(record.id, Some(id)); Ok(()) } #[sqlx_macros::test] async fn test_column_override_exact() -> anyhow::Result<()> { let mut conn = new::().await?; let (mut conn, id) = with_test_row(&mut conn).await?; let record = sqlx::query!("select id as `id: MyInt` from tweet") .fetch_one(&mut *conn) .await?; assert_eq!(record.id, id); // we can also support this syntax for expressions let record = sqlx::query!("select * from (select 1 as `id: MyInt`) records") .fetch_one(&mut *conn) .await?; assert_eq!(record.id, MyInt(1)); let record = sqlx::query!("select owner_id as `id: MyInt` from tweet") .fetch_one(&mut *conn) .await?; assert_eq!(record.id, Some(MyInt(1))); Ok(()) } #[sqlx_macros::test] async fn test_column_override_exact_not_null() -> anyhow::Result<()> { let mut conn = new::().await?; let (mut conn, _) = with_test_row(&mut conn).await?; let record = sqlx::query!("select owner_id as `id!: MyInt` from tweet") .fetch_one(&mut *conn) .await?; assert_eq!(record.id, MyInt(1)); Ok(()) } #[sqlx_macros::test] async fn test_column_override_exact_nullable() -> anyhow::Result<()> { let mut conn = new::().await?; let (mut conn, id) = with_test_row(&mut conn).await?; let record = sqlx::query!("select id as `id?: MyInt` from tweet") .fetch_one(&mut *conn) .await?; assert_eq!(record.id, Some(id)); Ok(()) } #[derive(PartialEq, Eq, Debug, sqlx::Type)] #[sqlx(rename_all = "lowercase")] enum MyEnum { Red, Green, Blue, } #[derive(PartialEq, Eq, Debug, sqlx::Type)] #[repr(i32)] enum MyCEnum { Red = 0, Green, Blue, } #[sqlx_macros::test] async fn test_column_override_exact_enum() -> anyhow::Result<()> { let mut conn = new::().await?; let record = sqlx::query!("select * from (select 'red' as `color: MyEnum`) records") .fetch_one(&mut conn) .await?; assert_eq!(record.color, MyEnum::Red); let record = sqlx::query!("select * from (select 2 as `color: MyCEnum`) records") .fetch_one(&mut conn) .await?; assert_eq!(record.color, MyCEnum::Blue); Ok(()) } #[sqlx_macros::test] async fn test_try_from_attr_for_native_type() -> anyhow::Result<()> { #[derive(sqlx::FromRow)] struct Record { #[sqlx(try_from = "i64")] id: u64, } let mut conn = new::().await?; let (mut conn, id) = with_test_row(&mut conn).await?; let record = sqlx::query_as::<_, Record>("select id from tweet") .fetch_one(&mut *conn) .await?; assert_eq!(record.id, id.0 as u64); Ok(()) } #[sqlx_macros::test] async fn test_try_from_attr_for_custom_type() -> anyhow::Result<()> { #[derive(sqlx::FromRow)] struct Record { #[sqlx(try_from = "i64")] id: Id, } #[derive(Debug, PartialEq)] struct Id(i64); impl std::convert::TryFrom for Id { type Error = std::io::Error; fn try_from(value: i64) -> Result { Ok(Id(value)) } } let mut conn = new::().await?; let (mut conn, id) = with_test_row(&mut conn).await?; let record = sqlx::query_as::<_, Record>("select id from tweet") .fetch_one(&mut *conn) .await?; assert_eq!(record.id, Id(id.0)); Ok(()) } #[sqlx_macros::test] async fn test_try_from_attr_with_flatten() -> anyhow::Result<()> { #[derive(sqlx::FromRow)] struct Record { #[sqlx(try_from = "Id", flatten)] id: u64, } #[derive(Debug, PartialEq, sqlx::FromRow)] struct Id { id: i64, } impl std::convert::TryFrom for u64 { type Error = std::io::Error; fn try_from(value: Id) -> Result { Ok(value.id as u64) } } let mut conn = new::().await?; let (mut conn, id) = with_test_row(&mut conn).await?; let record = sqlx::query_as::<_, Record>("select id from tweet") .fetch_one(&mut *conn) .await?; assert_eq!(record.id, id.0 as u64); Ok(()) } #[sqlx_macros::test] async fn test_try_from_attr_with_complex_type() -> anyhow::Result<()> { mod m { #[derive(sqlx::Type)] #[sqlx(transparent)] pub struct ComplexType(T); impl std::convert::TryFrom> for u64 { type Error = std::num::TryFromIntError; fn try_from(value: ComplexType) -> Result { u64::try_from(value.0) } } } #[derive(sqlx::FromRow)] struct Record { #[sqlx(try_from = "m::ComplexType")] id: u64, } let mut conn = new::().await?; let (mut conn, id) = with_test_row(&mut conn).await?; let record = sqlx::query_as::<_, Record>("select id from tweet") .fetch_one(&mut *conn) .await?; assert_eq!(record.id, id.0 as u64); Ok(()) } #[sqlx_macros::test] async fn test_from_row_json_attr() -> anyhow::Result<()> { #[derive(serde::Deserialize)] struct J { a: u32, b: u32, } #[derive(sqlx::FromRow)] struct Record { #[sqlx(json)] j: J, } let mut conn = new::().await?; let record = sqlx::query_as::<_, Record>("select json_object('a', 1, 'b', 2) as j") .fetch_one(&mut conn) .await?; assert_eq!(record.j.a, 1); assert_eq!(record.j.b, 2); Ok(()) } #[sqlx_macros::test] async fn test_from_row_json_try_from_attr() -> anyhow::Result<()> { #[derive(serde::Deserialize)] struct J { a: u32, b: u32, } // Non-deserializable struct J2 { sum: u32, } impl std::convert::From for J2 { fn from(j: J) -> Self { Self { sum: j.a + j.b } } } #[derive(sqlx::FromRow)] struct Record { #[sqlx(json, try_from = "J")] j: J2, } let mut conn = new::().await?; let record = sqlx::query_as::<_, Record>("select json_object('a', 1, 'b', 2) as j") .fetch_one(&mut conn) .await?; assert_eq!(record.j.sum, 3); Ok(()) } // we don't emit bind parameter type-checks for MySQL so testing the overrides is redundant sqlx-0.8.3/tests/mysql/migrate.rs000064400000000000000000000040571046102023000151360ustar 00000000000000use sqlx::migrate::Migrator; use sqlx::mysql::{MySql, MySqlConnection}; use sqlx::pool::PoolConnection; use sqlx::Executor; use sqlx::Row; use std::path::Path; #[sqlx::test(migrations = false)] async fn simple(mut conn: PoolConnection) -> anyhow::Result<()> { clean_up(&mut conn).await?; let migrator = Migrator::new(Path::new("tests/mysql/migrations_simple")).await?; // run migration migrator.run(&mut conn).await?; // check outcome let res: String = conn .fetch_one("SELECT some_payload FROM migrations_simple_test") .await? .get(0); assert_eq!(res, "110_suffix"); // running it a 2nd time should still work migrator.run(&mut conn).await?; Ok(()) } #[sqlx::test(migrations = false)] async fn reversible(mut conn: PoolConnection) -> anyhow::Result<()> { clean_up(&mut conn).await?; let migrator = Migrator::new(Path::new("tests/mysql/migrations_reversible")).await?; // run migration migrator.run(&mut conn).await?; // check outcome let res: i64 = conn .fetch_one("SELECT some_payload FROM migrations_reversible_test") .await? .get(0); assert_eq!(res, 101); // roll back nothing (last version) migrator.undo(&mut conn, 20220721125033).await?; // check outcome let res: i64 = conn .fetch_one("SELECT some_payload FROM migrations_reversible_test") .await? .get(0); assert_eq!(res, 101); // roll back one version migrator.undo(&mut conn, 20220721124650).await?; // check outcome let res: i64 = conn .fetch_one("SELECT some_payload FROM migrations_reversible_test") .await? .get(0); assert_eq!(res, 100); Ok(()) } /// Ensure that we have a clean initial state. async fn clean_up(conn: &mut MySqlConnection) -> anyhow::Result<()> { conn.execute("DROP TABLE migrations_simple_test").await.ok(); conn.execute("DROP TABLE migrations_reversible_test") .await .ok(); conn.execute("DROP TABLE _sqlx_migrations").await.ok(); Ok(()) } sqlx-0.8.3/tests/mysql/migrations/1_user.sql000064400000000000000000000003431046102023000172250ustar 00000000000000create table user ( -- integer primary keys are the most efficient in SQLite user_id integer primary key auto_increment, -- indexed text values have to have a max length username varchar(16) unique not null ); sqlx-0.8.3/tests/mysql/migrations/2_post.sql000064400000000000000000000005031046102023000172330ustar 00000000000000create table post ( post_id integer primary key auto_increment, user_id integer not null references user (user_id), content text not null, -- Defaults have to be wrapped in parenthesis created_at datetime default current_timestamp ); create index post_created_at on post (created_at desc); sqlx-0.8.3/tests/mysql/migrations/3_comment.sql000064400000000000000000000005061046102023000177140ustar 00000000000000create table comment ( comment_id integer primary key, post_id integer not null references post (post_id), user_id integer not null references user (user_id), content text not null, created_at datetime default current_timestamp ); create index comment_created_at on comment (created_at desc); sqlx-0.8.3/tests/mysql/migrations_reversible/20220721124650_add_table.down.sql000064400000000000000000000000471046102023000245600ustar 00000000000000DROP TABLE migrations_reversible_test; sqlx-0.8.3/tests/mysql/migrations_reversible/20220721124650_add_table.up.sql000064400000000000000000000003101046102023000242260ustar 00000000000000CREATE TABLE migrations_reversible_test ( some_id BIGINT NOT NULL PRIMARY KEY, some_payload BIGINT NOT NUll ); INSERT INTO migrations_reversible_test (some_id, some_payload) VALUES (1, 100); sqlx-0.8.3/tests/mysql/migrations_reversible/20220721125033_modify_column.down.sql000064400000000000000000000001071046102023000255160ustar 00000000000000UPDATE migrations_reversible_test SET some_payload = some_payload - 1; sqlx-0.8.3/tests/mysql/migrations_reversible/20220721125033_modify_column.up.sql000064400000000000000000000001071046102023000251730ustar 00000000000000UPDATE migrations_reversible_test SET some_payload = some_payload + 1; sqlx-0.8.3/tests/mysql/migrations_simple/20220721115250_add_test_table.sql000064400000000000000000000003001046102023000237640ustar 00000000000000CREATE TABLE migrations_simple_test ( some_id BIGINT NOT NULL PRIMARY KEY, some_payload BIGINT NOT NUll ); INSERT INTO migrations_simple_test (some_id, some_payload) VALUES (1, 100); sqlx-0.8.3/tests/mysql/migrations_simple/20220721115524_convert_type.sql000064400000000000000000000020421046102023000235600ustar 00000000000000-- Perform a tricky conversion of the payload. -- -- This script will only succeed once and will fail if executed twice. -- set up temporary target column ALTER TABLE migrations_simple_test ADD some_payload_tmp TEXT; -- perform conversion -- This will fail if `some_payload` is already a string column due to the addition. -- We add a suffix after the addition to ensure that the SQL database does not silently cast the string back to an -- integer. UPDATE migrations_simple_test SET some_payload_tmp = CONCAT(CAST((some_payload + 10) AS CHAR(3)), '_suffix'); -- remove original column including the content ALTER TABLE migrations_simple_test DROP COLUMN some_payload; -- prepare new payload column (nullable, so we can copy over the data) ALTER TABLE migrations_simple_test ADD some_payload TEXT; -- copy new values UPDATE migrations_simple_test SET some_payload = some_payload_tmp; -- "freeze" column ALTER TABLE migrations_simple_test MODIFY some_payload TEXT NOT NULL; -- clean up ALTER TABLE migrations_simple_test DROP COLUMN some_payload_tmp; sqlx-0.8.3/tests/mysql/my.cnf000064400000000000000000000001531046102023000142460ustar 00000000000000[mysqld] ssl-ca=/etc/mysql/ssl/ca.crt ssl-cert=/etc/mysql/ssl/server.crt ssl-key=/etc/mysql/ssl/server.key sqlx-0.8.3/tests/mysql/mysql.rs000064400000000000000000000364341046102023000146570ustar 00000000000000use anyhow::Context; use futures::TryStreamExt; use sqlx::mysql::{MySql, MySqlConnection, MySqlPool, MySqlPoolOptions, MySqlRow}; use sqlx::{Column, Connection, Executor, Row, Statement, TypeInfo}; use sqlx_core::connection::ConnectOptions; use sqlx_mysql::MySqlConnectOptions; use sqlx_test::{new, setup_if_needed}; use std::env; use url::Url; #[sqlx_macros::test] async fn it_connects() -> anyhow::Result<()> { let mut conn = new::().await?; conn.ping().await?; conn.close().await?; Ok(()) } #[sqlx_macros::test] async fn it_connects_without_password() -> anyhow::Result<()> { setup_if_needed(); let mut url = Url::parse(&env::var("DATABASE_URL").context("expected DATABASE_URL")?) .context("error parsing DATABASE_URL")?; url.set_username("no_password").unwrap(); url.set_password(None).unwrap(); let mut conn = MySqlConnectOptions::from_url(&url)?.connect().await?; let vars = sqlx::raw_sql("SHOW VARIABLES").fetch_all(&mut conn).await?; assert!(!vars.is_empty()); conn.close().await?; Ok(()) } #[sqlx_macros::test] async fn it_maths() -> anyhow::Result<()> { let mut conn = new::().await?; let value = sqlx::query("select 1 + CAST(? AS SIGNED)") .bind(5_i32) .try_map(|row: MySqlRow| row.try_get::(0)) .fetch_one(&mut conn) .await?; assert_eq!(6i32, value); Ok(()) } #[sqlx_macros::test] async fn it_can_fail_at_querying() -> anyhow::Result<()> { let mut conn = new::().await?; let _ = conn.execute(sqlx::query("SELECT 1")).await?; // we are testing that this does not cause a panic! let _ = conn .execute(sqlx::query("SELECT non_existence_table")) .await; Ok(()) } #[sqlx_macros::test] async fn it_executes() -> anyhow::Result<()> { let mut conn = new::().await?; let _ = conn .execute( r#" CREATE TEMPORARY TABLE users (id INTEGER PRIMARY KEY); "#, ) .await?; for index in 1..=10_i32 { let done = sqlx::query("INSERT INTO users (id) VALUES (?)") .bind(index) .execute(&mut conn) .await?; assert_eq!(done.rows_affected(), 1); } let sum: i32 = sqlx::query("SELECT id FROM users") .try_map(|row: MySqlRow| row.try_get::(0)) .fetch(&mut conn) .try_fold(0_i32, |acc, x| async move { Ok(acc + x) }) .await?; assert_eq!(sum, 55); Ok(()) } #[sqlx_macros::test] async fn it_executes_with_pool() -> anyhow::Result<()> { let pool: MySqlPool = MySqlPoolOptions::new() .min_connections(2) .max_connections(2) .test_before_acquire(false) .connect(&dotenvy::var("DATABASE_URL")?) .await?; let rows = pool.fetch_all("SELECT 1; SELECT 2").await?; assert_eq!(rows.len(), 2); let count = pool .fetch("SELECT 1; SELECT 2") .try_fold(0, |acc, _| async move { Ok(acc + 1) }) .await?; assert_eq!(count, 2); Ok(()) } #[sqlx_macros::test] async fn it_works_with_cache_disabled() -> anyhow::Result<()> { setup_if_needed(); let mut url = url::Url::parse(&env::var("DATABASE_URL")?)?; url.query_pairs_mut() .append_pair("statement-cache-capacity", "0"); let mut conn = MySqlConnection::connect(url.as_ref()).await?; for index in 1..=10_i32 { let _ = sqlx::query("SELECT ?") .bind(index) .execute(&mut conn) .await?; } Ok(()) } #[sqlx_macros::test] async fn it_drops_results_in_affected_rows() -> anyhow::Result<()> { let mut conn = new::().await?; // ~1800 rows should be iterated and dropped let done = conn .execute("select * from mysql.time_zone limit 1575") .await?; // In MySQL, rows being returned isn't enough to flag it as an _affected_ row assert_eq!(0, done.rows_affected()); Ok(()) } #[sqlx_macros::test] async fn it_selects_null() -> anyhow::Result<()> { let mut conn = new::().await?; let (val,): (Option,) = sqlx::query_as("SELECT NULL").fetch_one(&mut conn).await?; assert!(val.is_none()); let val: Option = conn.fetch_one("SELECT NULL").await?.try_get(0)?; assert!(val.is_none()); Ok(()) } #[sqlx_macros::test] async fn it_can_fetch_one_and_ping() -> anyhow::Result<()> { let mut conn = new::().await?; let (_id,): (i32,) = sqlx::query_as("SELECT 1 as id") .fetch_one(&mut conn) .await?; conn.ping().await?; let (_id,): (i32,) = sqlx::query_as("SELECT 1 as id") .fetch_one(&mut conn) .await?; Ok(()) } /// Test that we can interleave reads and writes to the database in one simple query. #[sqlx_macros::test] async fn it_interleaves_reads_and_writes() -> anyhow::Result<()> { let mut conn = new::().await?; let mut s = conn.fetch( " CREATE TEMPORARY TABLE messages ( id BIGINT PRIMARY KEY AUTO_INCREMENT, text TEXT NOT NULL ); SELECT 'Hello World' as _1; INSERT INTO messages (text) VALUES ('this is a test'); SELECT id, text FROM messages; ", ); let row = s.try_next().await?.unwrap(); assert!("Hello World" == row.try_get::<&str, _>("_1")?); let row = s.try_next().await?.unwrap(); let id: i64 = row.try_get("id")?; let text: &str = row.try_get("text")?; assert_eq!(1_i64, id); assert_eq!("this is a test", text); Ok(()) } #[sqlx_macros::test] async fn it_caches_statements() -> anyhow::Result<()> { let mut conn = new::().await?; for i in 0..2 { let row = sqlx::query("SELECT ? AS val") .bind(i) .persistent(true) .fetch_one(&mut conn) .await?; let val: u32 = row.get("val"); assert_eq!(i, val); } assert_eq!(1, conn.cached_statements_size()); conn.clear_cached_statements().await?; assert_eq!(0, conn.cached_statements_size()); for i in 0..2 { let row = sqlx::query("SELECT ? AS val") .bind(i) .persistent(false) .fetch_one(&mut conn) .await?; let val: u32 = row.get("val"); assert_eq!(i, val); } assert_eq!(0, conn.cached_statements_size()); Ok(()) } #[sqlx_macros::test] async fn it_closes_statements_with_persistent_disabled() -> anyhow::Result<()> { let mut conn = new::().await?; let old_statement_count = select_statement_count(&mut conn).await.unwrap_or_default(); for i in 0..2 { let row = sqlx::query("SELECT ? AS val") .bind(i) .persistent(false) .fetch_one(&mut conn) .await?; let val: i32 = row.get("val"); assert_eq!(i, val); } let new_statement_count = select_statement_count(&mut conn).await.unwrap_or_default(); assert_eq!(old_statement_count, new_statement_count); Ok(()) } #[sqlx_macros::test] async fn it_closes_statements_with_cache_disabled() -> anyhow::Result<()> { setup_if_needed(); let mut url = url::Url::parse(&env::var("DATABASE_URL")?)?; url.query_pairs_mut() .append_pair("statement-cache-capacity", "0"); let mut conn = MySqlConnection::connect(url.as_ref()).await?; let old_statement_count = select_statement_count(&mut conn).await.unwrap_or_default(); for index in 1..=10_i32 { let _ = sqlx::query("SELECT ?") .bind(index) .execute(&mut conn) .await?; } let new_statement_count = select_statement_count(&mut conn).await.unwrap_or_default(); assert_eq!(old_statement_count, new_statement_count); Ok(()) } #[sqlx_macros::test] async fn it_can_bind_null_and_non_null_issue_540() -> anyhow::Result<()> { let mut conn = new::().await?; let row = sqlx::query("SELECT ?, ?") .bind(50_i32) .bind(None::) .fetch_one(&mut conn) .await?; let v0: Option = row.get(0); let v1: Option = row.get(1); assert_eq!(v0, Some(50)); assert_eq!(v1, None); Ok(()) } #[sqlx_macros::test] async fn it_can_bind_only_null_issue_540() -> anyhow::Result<()> { let mut conn = new::().await?; let row = sqlx::query("SELECT ?") .bind(None::) .fetch_one(&mut conn) .await?; let v0: Option = row.get(0); assert_eq!(v0, None); Ok(()) } #[sqlx_macros::test] async fn it_can_bind_and_return_years() -> anyhow::Result<()> { let mut conn = new::().await?; sqlx::raw_sql( r#" CREATE TEMPORARY TABLE too_many_years ( id INT PRIMARY KEY AUTO_INCREMENT, the YEAR NOT NULL ); "#, ) .execute(&mut conn) .await?; sqlx::query( r#" INSERT INTO too_many_years ( the ) VALUES ( ? ); "#, ) .bind(2142) .execute(&mut conn) .await?; let the: u16 = sqlx::query_scalar("SELECT the FROM too_many_years") .fetch_one(&mut conn) .await?; assert_eq!(the, 2142); Ok(()) } #[sqlx_macros::test] async fn it_can_prepare_then_execute() -> anyhow::Result<()> { let mut conn = new::().await?; let mut tx = conn.begin().await?; let tweet_id: u64 = sqlx::query("INSERT INTO tweet ( text ) VALUES ( 'Hello, World' )") .execute(&mut *tx) .await? .last_insert_id(); let statement = tx.prepare("SELECT * FROM tweet WHERE id = ?").await?; assert_eq!(statement.column(0).name(), "id"); assert_eq!(statement.column(1).name(), "created_at"); assert_eq!(statement.column(2).name(), "text"); assert_eq!(statement.column(3).name(), "owner_id"); assert_eq!(statement.column(0).type_info().name(), "BIGINT"); assert_eq!(statement.column(1).type_info().name(), "TIMESTAMP"); assert_eq!(statement.column(2).type_info().name(), "TEXT"); assert_eq!(statement.column(3).type_info().name(), "BIGINT"); let row = statement.query().bind(tweet_id).fetch_one(&mut *tx).await?; let tweet_text: &str = row.try_get("text")?; assert_eq!(tweet_text, "Hello, World"); Ok(()) } // repro is more reliable with the basic scheduler used by `#[tokio::test]` #[cfg(feature = "_rt-tokio")] #[tokio::test] async fn test_issue_622() -> anyhow::Result<()> { use std::time::Instant; setup_if_needed(); let pool = MySqlPoolOptions::new() .max_connections(1) // also fails with higher counts, e.g. 5 .connect(&std::env::var("DATABASE_URL").unwrap()) .await?; println!("pool state: {pool:?}"); let mut handles = vec![]; // given repro spawned 100 tasks but I found it reliably reproduced with 3 for i in 0..3 { let pool = pool.clone(); handles.push(sqlx_core::rt::spawn(async move { { let mut conn = pool.acquire().await.unwrap(); let _ = sqlx::query("SELECT 1").fetch_one(&mut *conn).await.unwrap(); // conn gets dropped here and should be returned to the pool } // (do some other work here without holding on to a connection) // this actually fixes the issue, depending on the timeout used // sqlx_core::rt::sleep(Duration::from_millis(500)).await; { let start = Instant::now(); match pool.acquire().await { Ok(conn) => { println!("{} acquire took {:?}", i, start.elapsed()); drop(conn); } Err(e) => panic!("{i} acquire returned error: {e} pool state: {pool:?}"), } } Result::<(), anyhow::Error>::Ok(()) })); } futures::future::try_join_all(handles).await?; Ok(()) } #[sqlx_macros::test] async fn it_can_work_with_transactions() -> anyhow::Result<()> { let mut conn = new::().await?; sqlx::raw_sql("CREATE TEMPORARY TABLE users (id INTEGER PRIMARY KEY);") .execute(&mut conn) .await?; // begin .. rollback let mut tx = conn.begin().await?; sqlx::query("INSERT INTO users (id) VALUES (?)") .bind(1_i32) .execute(&mut *tx) .await?; let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM users") .fetch_one(&mut *tx) .await?; assert_eq!(count, 1); tx.rollback().await?; let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM users") .fetch_one(&mut conn) .await?; assert_eq!(count, 0); // begin .. commit let mut tx = conn.begin().await?; sqlx::query("INSERT INTO users (id) VALUES (?)") .bind(1_i32) .execute(&mut *tx) .await?; tx.commit().await?; let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM users") .fetch_one(&mut conn) .await?; assert_eq!(count, 1); // begin .. (drop) { let mut tx = conn.begin().await?; sqlx::query("INSERT INTO users (id) VALUES (?)") .bind(2) .execute(&mut *tx) .await?; let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM users") .fetch_one(&mut *tx) .await?; assert_eq!(count, 2); // tx is dropped } let count: i64 = sqlx::query_scalar("SELECT COUNT(*) FROM users") .fetch_one(&mut conn) .await?; assert_eq!(count, 1); Ok(()) } #[sqlx_macros::test] async fn it_can_handle_split_packets() -> anyhow::Result<()> { // This will only take effect on new connections new::() .await? .execute("SET GLOBAL max_allowed_packet = 4294967297") .await?; let mut conn = new::().await?; conn.execute( r#" CREATE TEMPORARY TABLE large_table (data LONGBLOB); "#, ) .await?; let data = vec![0x41; 0xFF_FF_FF * 2]; sqlx::query("INSERT INTO large_table (data) VALUES (?)") .bind(&data) .execute(&mut conn) .await?; let ret: Vec = sqlx::query_scalar("SELECT * FROM large_table") .fetch_one(&mut conn) .await?; assert_eq!(ret, data); Ok(()) } #[sqlx_macros::test] async fn test_shrink_buffers() -> anyhow::Result<()> { // We don't really have a good way to test that `.shrink_buffers()` functions as expected // without exposing a lot of internals, but we can at least be sure it doesn't // materially affect the operation of the connection. let mut conn = new::().await?; // The connection buffer is only 8 KiB by default so this should definitely force it to grow. let data = "This string should be 32 bytes!\n".repeat(1024); assert_eq!(data.len(), 32 * 1024); let ret: String = sqlx::query_scalar("SELECT ?") .bind(&data) .fetch_one(&mut conn) .await?; assert_eq!(ret, data); conn.shrink_buffers(); let ret: i64 = sqlx::query_scalar("SELECT ?") .bind(&12345678i64) .fetch_one(&mut conn) .await?; assert_eq!(ret, 12345678i64); Ok(()) } async fn select_statement_count(conn: &mut MySqlConnection) -> Result { // Fails if performance schema does not exist sqlx::query_scalar( r#" SELECT COUNT(*) FROM performance_schema.threads AS t INNER JOIN performance_schema.prepared_statements_instances AS psi ON psi.OWNER_THREAD_ID = t.THREAD_ID WHERE t.processlist_id = CONNECTION_ID() "#, ) .fetch_one(conn) .await } sqlx-0.8.3/tests/mysql/rustsec.rs000064400000000000000000000044611046102023000151750ustar 00000000000000use sqlx::{Error, MySql}; use std::io; use sqlx_test::new; // https://rustsec.org/advisories/RUSTSEC-2024-0363.html // // During the audit the MySQL driver was found to be *unlikely* to be vulnerable to the exploit, // so this just serves as a sanity check. #[sqlx::test] async fn rustsec_2024_0363() -> anyhow::Result<()> { let overflow_len = 4 * 1024 * 1024 * 1024; // 4 GiB let padding = " ".repeat(overflow_len); let payload = "UPDATE injection_target SET message = 'you''ve been pwned!' WHERE id = 1"; let mut injected_value = String::with_capacity(overflow_len + payload.len()); injected_value.push_str(&padding); injected_value.push_str(payload); // Since this is so large, keeping it around until the end *can* lead to getting OOM-killed. drop(padding); let mut conn = new::().await?; sqlx::raw_sql( "CREATE TEMPORARY TABLE injection_target(id INTEGER PRIMARY KEY AUTO_INCREMENT, message TEXT);\n\ INSERT INTO injection_target(message) VALUES ('existing message');", ) .execute(&mut conn) .await?; // We can't concatenate a query string together like the other tests // because it would just demonstrate a regular old SQL injection. let res = sqlx::query("INSERT INTO injection_target(message) VALUES (?)") .bind(&injected_value) .execute(&mut conn) .await; if let Err(e) = res { // Connection rejected the query; we're happy. // // Current observed behavior is that `mysqld` closes the connection before we're even done // sending the message, giving us a "Broken pipe" error. // // As it turns out, MySQL has a tight limit on packet sizes (even after splitting) // by default: https://dev.mysql.com/doc/refman/8.4/en/packet-too-large.html if matches!(e, Error::Io(ref ioe) if ioe.kind() == io::ErrorKind::BrokenPipe) { return Ok(()); } panic!("unexpected error: {e:?}"); } let messages: Vec = sqlx::query_scalar("SELECT message FROM injection_target ORDER BY id") .fetch_all(&mut conn) .await?; assert_eq!(messages[0], "existing_message"); assert_eq!(messages[1].len(), injected_value.len()); // Injection didn't affect our database; we're happy. Ok(()) } sqlx-0.8.3/tests/mysql/setup.sql000064400000000000000000000020401046102023000150070ustar 00000000000000-- https://github.com/prisma/database-schema-examples/tree/master/postgres/basic-twitter#basic-twitter CREATE TABLE tweet ( id BIGINT PRIMARY KEY AUTO_INCREMENT, created_at TIMESTAMP NOT NULL DEFAULT NOW(), text TEXT NOT NULL, owner_id BIGINT ); CREATE TABLE tweet_reply ( id BIGINT PRIMARY KEY AUTO_INCREMENT, tweet_id BIGINT NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT NOW(), text TEXT NOT NULL, owner_id BIGINT, CONSTRAINT tweet_id_fk FOREIGN KEY (tweet_id) REFERENCES tweet(id) ); CREATE TABLE products ( product_no INTEGER, name TEXT, price NUMERIC CHECK (price > 0) ); -- Create a user without a password to test passwordless auth. CREATE USER 'no_password'@'%'; -- The minimum privilege apparently needed to connect to a specific database. -- Granting no privileges, or just `GRANT USAGE`, gives an "access denied" error. -- https://github.com/launchbadge/sqlx/issues/3484#issuecomment-2350901546 GRANT SELECT ON sqlx.* TO 'no_password'@'%'; sqlx-0.8.3/tests/mysql/test-attr.rs000064400000000000000000000113221046102023000154260ustar 00000000000000// The no-arg variant is covered by other tests already. use sqlx::MySqlPool; const MIGRATOR: sqlx::migrate::Migrator = sqlx::migrate!("tests/mysql/migrations"); #[sqlx::test] async fn it_gets_a_pool(pool: MySqlPool) -> sqlx::Result<()> { let mut conn = pool.acquire().await?; let db_name: String = sqlx::query_scalar("select database()") .fetch_one(&mut *conn) .await?; assert!( db_name.starts_with("_sqlx_test_database_"), "db_name: {:?}", db_name ); Ok(()) } // This should apply migrations and then `fixtures/users.sql` #[sqlx::test(migrations = "tests/mysql/migrations", fixtures("users"))] async fn it_gets_users(pool: MySqlPool) -> sqlx::Result<()> { let usernames: Vec = sqlx::query_scalar(r#"SELECT username FROM user ORDER BY username"#) .fetch_all(&pool) .await?; assert_eq!(usernames, ["alice", "bob"]); let post_exists: bool = sqlx::query_scalar("SELECT exists(SELECT 1 FROM post)") .fetch_one(&pool) .await?; assert!(!post_exists); let comment_exists: bool = sqlx::query_scalar("SELECT exists(SELECT 1 FROM comment)") .fetch_one(&pool) .await?; assert!(!comment_exists); Ok(()) } #[sqlx::test(migrations = "tests/mysql/migrations", fixtures("users", "posts"))] async fn it_gets_posts(pool: MySqlPool) -> sqlx::Result<()> { let post_contents: Vec = sqlx::query_scalar("SELECT content FROM post ORDER BY created_at") .fetch_all(&pool) .await?; assert_eq!( post_contents, [ "This new computer is lightning-fast!", "@alice is a haxxor :(" ] ); let comment_exists: bool = sqlx::query_scalar("SELECT exists(SELECT 1 FROM comment)") .fetch_one(&pool) .await?; assert!(!comment_exists); Ok(()) } #[sqlx::test( migrations = "tests/mysql/migrations", fixtures("../fixtures/mysql/users.sql", "../fixtures/mysql/posts.sql") )] async fn it_gets_posts_explicit_fixtures_path(pool: MySqlPool) -> sqlx::Result<()> { let post_contents: Vec = sqlx::query_scalar("SELECT content FROM post ORDER BY created_at") .fetch_all(&pool) .await?; assert_eq!( post_contents, [ "This new computer is lightning-fast!", "@alice is a haxxor :(" ] ); let comment_exists: bool = sqlx::query_scalar("SELECT exists(SELECT 1 FROM comment)") .fetch_one(&pool) .await?; assert!(!comment_exists); Ok(()) } #[sqlx::test( migrations = "tests/mysql/migrations", fixtures("../fixtures/mysql/users.sql"), fixtures("posts") )] async fn it_gets_posts_mixed_fixtures_path(pool: MySqlPool) -> sqlx::Result<()> { let post_contents: Vec = sqlx::query_scalar("SELECT content FROM post ORDER BY created_at") .fetch_all(&pool) .await?; assert_eq!( post_contents, [ "This new computer is lightning-fast!", "@alice is a haxxor :(" ] ); let comment_exists: bool = sqlx::query_scalar("SELECT exists(SELECT 1 FROM comment)") .fetch_one(&pool) .await?; assert!(!comment_exists); Ok(()) } #[sqlx::test( migrations = "tests/mysql/migrations", fixtures(path = "../fixtures/mysql", scripts("users", "posts")) )] async fn it_gets_posts_custom_relative_fixtures_path(pool: MySqlPool) -> sqlx::Result<()> { let post_contents: Vec = sqlx::query_scalar("SELECT content FROM post ORDER BY created_at") .fetch_all(&pool) .await?; assert_eq!( post_contents, [ "This new computer is lightning-fast!", "@alice is a haxxor :(" ] ); let comment_exists: bool = sqlx::query_scalar("SELECT exists(SELECT 1 FROM comment)") .fetch_one(&pool) .await?; assert!(!comment_exists); Ok(()) } // Try `migrator` #[sqlx::test(migrator = "MIGRATOR", fixtures("users", "posts", "comments"))] async fn it_gets_comments(pool: MySqlPool) -> sqlx::Result<()> { let post_1_comments: Vec = sqlx::query_scalar("SELECT content FROM comment WHERE post_id = ? ORDER BY created_at") .bind(&1) .fetch_all(&pool) .await?; assert_eq!( post_1_comments, ["lol bet ur still bad, 1v1 me", "you're on!"] ); let post_2_comments: Vec = sqlx::query_scalar("SELECT content FROM comment WHERE post_id = ? ORDER BY created_at") .bind(&2) .fetch_all(&pool) .await?; assert_eq!(post_2_comments, ["lol you're just mad you lost :P"]); Ok(()) } sqlx-0.8.3/tests/mysql/types.rs000064400000000000000000000304001046102023000146410ustar 00000000000000extern crate time_ as time; use std::net::SocketAddr; #[cfg(feature = "rust_decimal")] use std::str::FromStr; use sqlx::mysql::MySql; use sqlx::{Executor, Row}; use sqlx::types::Text; use sqlx::mysql::types::MySqlTime; use sqlx_mysql::types::MySqlTimeSign; use sqlx_test::{new, test_type}; test_type!(bool(MySql, "false" == false, "true" == true)); test_type!(u8(MySql, "CAST(253 AS UNSIGNED)" == 253_u8)); test_type!(i8(MySql, "5" == 5_i8, "0" == 0_i8)); test_type!(u16(MySql, "CAST(21415 AS UNSIGNED)" == 21415_u16)); test_type!(i16(MySql, "21415" == 21415_i16)); test_type!(u32(MySql, "CAST(2141512 AS UNSIGNED)" == 2141512_u32)); test_type!(i32(MySql, "2141512" == 2141512_i32)); test_type!(u64(MySql, "CAST(2141512 AS UNSIGNED)" == 2141512_u64)); test_type!(i64(MySql, "2141512" == 2141512_i64)); test_type!(f64(MySql, "3.14159265e0" == 3.14159265_f64)); // NOTE: This behavior can be very surprising. MySQL implicitly widens FLOAT bind parameters // to DOUBLE. This results in the weirdness you see below. MySQL generally recommends to stay // away from FLOATs. test_type!(f32(MySql, "3.1410000324249268e0" == 3.141f32 as f64 as f32)); test_type!(string(MySql, "'helloworld'" == "helloworld", "''" == "" )); test_type!(bytes>(MySql, "X'DEADBEEF'" == vec![0xDE_u8, 0xAD, 0xBE, 0xEF], "X''" == Vec::::new(), "X'0000000052'" == vec![0_u8, 0, 0, 0, 0x52] )); #[cfg(feature = "uuid")] test_type!(uuid(MySql, "x'b731678f636f4135bc6f19440c13bd19'" == sqlx::types::Uuid::parse_str("b731678f-636f-4135-bc6f-19440c13bd19").unwrap(), "x'00000000000000000000000000000000'" == sqlx::types::Uuid::parse_str("00000000-0000-0000-0000-000000000000").unwrap() )); #[cfg(feature = "uuid")] test_type!(uuid_hyphenated(MySql, "'b731678f-636f-4135-bc6f-19440c13bd19'" == sqlx::types::Uuid::parse_str("b731678f-636f-4135-bc6f-19440c13bd19").unwrap().hyphenated(), "'00000000-0000-0000-0000-000000000000'" == sqlx::types::Uuid::parse_str("00000000-0000-0000-0000-000000000000").unwrap().hyphenated() )); #[cfg(feature = "uuid")] test_type!(uuid_simple(MySql, "'b731678f636f4135bc6f19440c13bd19'" == sqlx::types::Uuid::parse_str("b731678f636f4135bc6f19440c13bd19").unwrap().simple(), "'00000000000000000000000000000000'" == sqlx::types::Uuid::parse_str("00000000000000000000000000000000").unwrap().simple() )); test_type!(mysql_time(MySql, "TIME '00:00:00.000000'" == MySqlTime::ZERO, "TIME '-00:00:00.000000'" == MySqlTime::ZERO, "TIME '838:59:59.0'" == MySqlTime::MAX, "TIME '-838:59:59.0'" == MySqlTime::MIN, "TIME '123:45:56.890'" == MySqlTime::new(MySqlTimeSign::Positive, 123, 45, 56, 890_000).unwrap(), "TIME '-123:45:56.890'" == MySqlTime::new(MySqlTimeSign::Negative, 123, 45, 56, 890_000).unwrap(), "TIME '123:45:56.890011'" == MySqlTime::new(MySqlTimeSign::Positive, 123, 45, 56, 890_011).unwrap(), "TIME '-123:45:56.890011'" == MySqlTime::new(MySqlTimeSign::Negative, 123, 45, 56, 890_011).unwrap(), )); #[cfg(feature = "chrono")] mod chrono { use sqlx::types::chrono::{DateTime, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc}; use super::*; test_type!(chrono_date(MySql, "DATE '2001-01-05'" == NaiveDate::from_ymd_opt(2001, 1, 5).unwrap(), "DATE '2050-11-23'" == NaiveDate::from_ymd_opt(2050, 11, 23).unwrap() )); test_type!(chrono_time_zero(MySql, "TIME '00:00:00.000000'" == NaiveTime::from_hms_micro_opt(0, 0, 0, 0).unwrap() )); test_type!(chrono_time(MySql, "TIME '05:10:20.115100'" == NaiveTime::from_hms_micro_opt(5, 10, 20, 115100).unwrap() )); test_type!(chrono_date_time(MySql, "TIMESTAMP '2019-01-02 05:10:20'" == NaiveDate::from_ymd_opt(2019, 1, 2).unwrap().and_hms_opt(5, 10, 20).unwrap() )); test_type!(chrono_timestamp>(MySql, "TIMESTAMP '2019-01-02 05:10:20.115100'" == Utc.from_utc_datetime( &NaiveDate::from_ymd_opt(2019, 1, 2).unwrap().and_hms_micro_opt(5, 10, 20, 115100).unwrap(), ) )); #[sqlx_macros::test] async fn test_type_chrono_zero_date() -> anyhow::Result<()> { let mut conn = sqlx_test::new::().await?; // ensure that zero dates are turned on // newer MySQL has these disabled by default conn.execute("SET @@sql_mode := REPLACE(@@sql_mode, 'NO_ZERO_IN_DATE', '');") .await?; conn.execute("SET @@sql_mode := REPLACE(@@sql_mode, 'NO_ZERO_DATE', '');") .await?; // date let row = sqlx::query("SELECT DATE '0000-00-00'") .fetch_one(&mut conn) .await?; let val: Option = row.get(0); assert_eq!(val, None); assert!(row.try_get::(0).is_err()); // datetime let row = sqlx::query("SELECT TIMESTAMP '0000-00-00 00:00:00'") .fetch_one(&mut conn) .await?; let val: Option = row.get(0); assert_eq!(val, None); assert!(row.try_get::(0).is_err()); Ok(()) } } #[cfg(feature = "time")] mod time_tests { use time::macros::{date, time}; use sqlx::types::time::{Date, OffsetDateTime, PrimitiveDateTime, Time}; use super::*; test_type!(time_date( MySql, "DATE '2001-01-05'" == date!(2001 - 1 - 5), "DATE '2050-11-23'" == date!(2050 - 11 - 23) )); test_type!(time_time_zero