Fix GitHub Actions and integration test (#1346)

* fix test suite

* rustfmt

* need Row

* test: fix integration test scripts and update the upstream supported databases

Signed-off-by: Atkins Chang <atkinschang@gmail.com>

* ci(actions): update supported databases

Signed-off-by: Atkins Chang <atkinschang@gmail.com>

* ci(actions): use `pg_isready` instead of `sleep` to avoid error cause by database not ready

Signed-off-by: Atkins Chang <atkinschang@gmail.com>

* feat(core): add `trait PgConnectionInfo` for connection parameter status from server

Signed-off-by: Atkins Chang <atkinschang@gmail.com>

* test(postgres): fix integration test for postgres

Signed-off-by: Atkins Chang <atkinschang@gmail.com>

* test(mysql): fix integration tests

Signed-off-by: Atkins Chang <atkinschang@gmail.com>

* ci(actions): test database against the oldest and newest supported versions

Signed-off-by: Atkins Chang <atkinschang@gmail.com>

* docs(core): document `trait PgConnectionInfo`

Signed-off-by: Atkins Chang <atkinschang@gmail.com>

Co-authored-by: Montana Low <montanalow@gmail.com>
This commit is contained in:
Atkins 2021-07-29 05:00:34 +08:00 committed by GitHub
parent f0d0dce8e2
commit 9f7205e80f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 435 additions and 211 deletions

View file

@ -32,7 +32,8 @@ jobs:
runs-on: ubuntu-20.04
strategy:
matrix:
runtime: [async-std-native-tls, tokio-native-tls, actix-native-tls, async-std-rustls, tokio-rustls, actix-rustls]
runtime: [async-std, tokio, actix]
tls: [native-tls, rustls]
steps:
- uses: actions/checkout@v2
@ -48,7 +49,7 @@ jobs:
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-check-${{ matrix.runtime }}-${{ hashFiles('**/Cargo.lock') }}
key: ${{ runner.os }}-check-${{ matrix.runtime }}-${{ matrix.tls }}-${{ hashFiles('**/Cargo.lock') }}
- uses: actions-rs/cargo@v1
with:
@ -56,21 +57,22 @@ jobs:
args: >
--manifest-path sqlx-core/Cargo.toml
--no-default-features
--features offline,all-databases,all-types,migrate,runtime-${{ matrix.runtime }}
--features offline,all-databases,all-types,migrate,runtime-${{ matrix.runtime }}-${{ matrix.tls }}
- uses: actions-rs/cargo@v1
with:
command: check
args: >
--no-default-features
--features offline,all-databases,all-types,migrate,runtime-${{ matrix.runtime }},macros
--features offline,all-databases,all-types,migrate,runtime-${{ matrix.runtime }}-${{ matrix.tls }},macros
test:
name: Unit Test
runs-on: ubuntu-20.04
strategy:
matrix:
runtime: [async-std-native-tls, tokio-native-tls, actix-native-tls, async-std-rustls, tokio-rustls, actix-rustls]
runtime: [async-std, tokio, actix]
tls: [native-tls, rustls]
steps:
- uses: actions/checkout@v2
@ -93,7 +95,7 @@ jobs:
command: test
args: >
--manifest-path sqlx-core/Cargo.toml
--features offline,all-databases,all-types,runtime-${{ matrix.runtime }}
--features offline,all-databases,all-types,runtime-${{ matrix.runtime }}-${{ matrix.tls }}
cli:
name: CLI Binaries
@ -148,7 +150,8 @@ jobs:
runs-on: ubuntu-20.04
strategy:
matrix:
runtime: [async-std-native-tls, tokio-native-tls, actix-native-tls, async-std-rustls, tokio-rustls, actix-rustls]
runtime: [async-std, tokio, actix]
tls: [native-tls, rustls]
needs: check
steps:
- uses: actions/checkout@v2
@ -165,14 +168,14 @@ jobs:
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-sqlite-${{ matrix.runtime }}-${{ hashFiles('**/Cargo.lock') }}
key: ${{ runner.os }}-sqlite-${{ matrix.runtime }}-${{ matrix.tls }}-${{ hashFiles('**/Cargo.lock') }}
- uses: actions-rs/cargo@v1
with:
command: test
args: >
--no-default-features
--features any,macros,migrate,sqlite,all-types,runtime-${{ matrix.runtime }}
--features any,macros,migrate,sqlite,all-types,runtime-${{ matrix.runtime }}-${{ matrix.tls }}
--
--test-threads=1
env:
@ -183,8 +186,9 @@ jobs:
runs-on: ubuntu-20.04
strategy:
matrix:
postgres: [12, 10, 9_6, 9_5]
runtime: [async-std-native-tls, tokio-native-tls, actix-native-tls, async-std-rustls, tokio-rustls, actix-rustls]
postgres: [13, 9_6]
runtime: [async-std, tokio, actix]
tls: [native-tls, rustls]
needs: check
steps:
- uses: actions/checkout@v2
@ -201,23 +205,24 @@ jobs:
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-postgres-${{ matrix.runtime }}-${{ hashFiles('**/Cargo.lock') }}
key: ${{ runner.os }}-postgres-${{ matrix.runtime }}-${{ matrix.tls }}-${{ hashFiles('**/Cargo.lock') }}
- uses: actions-rs/cargo@v1
with:
command: build
args: >
--features postgres,all-types,runtime-${{ matrix.runtime }}
--features postgres,all-types,runtime-${{ matrix.runtime }}-${{ matrix.tls }}
- run: docker-compose -f tests/docker-compose.yml run -d -p 5432:5432 postgres_${{ matrix.postgres }}
- run: sleep 10
- 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"
- uses: actions-rs/cargo@v1
with:
command: test
args: >
--no-default-features
--features any,postgres,macros,all-types,runtime-${{ matrix.runtime }}
--features any,postgres,macros,all-types,runtime-${{ matrix.runtime }}-${{ matrix.tls }}
env:
DATABASE_URL: postgres://postgres:password@localhost:5432/sqlx
@ -226,7 +231,7 @@ jobs:
command: test
args: >
--no-default-features
--features any,postgres,macros,migrate,all-types,runtime-${{ matrix.runtime }}
--features any,postgres,macros,migrate,all-types,runtime-${{ matrix.runtime }}-${{ matrix.tls }}
env:
DATABASE_URL: postgres://postgres:password@localhost:5432/sqlx?sslmode=verify-ca&sslrootcert=.%2Ftests%2Fcerts%2Fca.crt
@ -235,8 +240,9 @@ jobs:
runs-on: ubuntu-20.04
strategy:
matrix:
mysql: [8, 5_7, 5_6]
runtime: [async-std-native-tls, tokio-native-tls, actix-native-tls, async-std-rustls, tokio-rustls, actix-rustls]
mysql: [8, 5_6]
runtime: [async-std, tokio, actix]
tls: [native-tls, rustls]
needs: check
steps:
- uses: actions/checkout@v2
@ -253,13 +259,13 @@ jobs:
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-mysql-${{ matrix.runtime }}-${{ hashFiles('**/Cargo.lock') }}
key: ${{ runner.os }}-mysql-${{ matrix.runtime }}-${{ matrix.tls }}-${{ hashFiles('**/Cargo.lock') }}
- uses: actions-rs/cargo@v1
with:
command: build
args: >
--features mysql,all-types,runtime-${{ matrix.runtime }}
--features mysql,all-types,runtime-${{ matrix.runtime }}-${{ matrix.tls }}
- run: docker-compose -f tests/docker-compose.yml run -d -p 3306:3306 mysql_${{ matrix.mysql }}
- run: sleep 60
@ -269,7 +275,7 @@ jobs:
command: test
args: >
--no-default-features
--features any,mysql,macros,migrate,all-types,runtime-${{ matrix.runtime }}
--features any,mysql,macros,migrate,all-types,runtime-${{ matrix.runtime }}-${{ matrix.tls }}
env:
DATABASE_URL: mysql://root:password@localhost:3306/sqlx
@ -278,8 +284,9 @@ jobs:
runs-on: ubuntu-20.04
strategy:
matrix:
mariadb: [10_5, 10_4, 10_3, 10_2, 10_1]
runtime: [async-std-native-tls, tokio-native-tls, actix-native-tls, async-std-rustls, tokio-rustls, actix-rustls]
mariadb: [10_6, 10_2]
runtime: [async-std, tokio, actix]
tls: [native-tls, rustls]
needs: check
steps:
- uses: actions/checkout@v2
@ -297,13 +304,13 @@ jobs:
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-mysql-${{ matrix.runtime }}-${{ hashFiles('**/Cargo.lock') }}
key: ${{ runner.os }}-mysql-${{ matrix.runtime }}-${{ matrix.tls }}-${{ hashFiles('**/Cargo.lock') }}
- uses: actions-rs/cargo@v1
with:
command: build
args: >
--features mysql,all-types,runtime-${{ matrix.runtime }}
--features mysql,all-types,runtime-${{ matrix.runtime }}-${{ matrix.tls }}
- run: docker-compose -f tests/docker-compose.yml run -d -p 3306:3306 mariadb_${{ matrix.mariadb }}
- run: sleep 30
@ -313,7 +320,7 @@ jobs:
command: test
args: >
--no-default-features
--features any,mysql,macros,migrate,all-types,runtime-${{ matrix.runtime }}
--features any,mysql,macros,migrate,all-types,runtime-${{ matrix.runtime }}-${{ matrix.tls }}
env:
DATABASE_URL: mysql://root:password@localhost:3306/sqlx
@ -322,8 +329,9 @@ jobs:
runs-on: ubuntu-20.04
strategy:
matrix:
mssql: [2019]
runtime: [async-std-native-tls, tokio-native-tls, actix-native-tls, async-std-rustls, tokio-rustls, actix-rustls]
mssql: [2019, 2017]
runtime: [async-std, tokio, actix]
tls: [native-tls, rustls]
needs: check
steps:
- uses: actions/checkout@v2
@ -340,13 +348,13 @@ jobs:
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-mssql-${{ matrix.runtime }}-${{ hashFiles('**/Cargo.lock') }}
key: ${{ runner.os }}-mssql-${{ matrix.runtime }}-${{ matrix.tls }}-${{ hashFiles('**/Cargo.lock') }}
- uses: actions-rs/cargo@v1
with:
command: build
args: >
--features mssql,all-types,runtime-${{ matrix.runtime }}
--features mssql,all-types,runtime-${{ matrix.runtime }}-${{ matrix.tls }}
- run: docker-compose -f tests/docker-compose.yml run -d -p 1433:1433 mssql_${{ matrix.mssql }}
- run: sleep 80 # MSSQL takes a "bit" to startup
@ -356,6 +364,6 @@ jobs:
command: test
args: >
--no-default-features
--features any,mssql,macros,migrate,all-types,runtime-${{ matrix.runtime }}
--features any,mssql,macros,migrate,all-types,runtime-${{ matrix.runtime }}-${{ matrix.tls }}
env:
DATABASE_URL: mssql://sa:Password123!@localhost/sqlx

View file

@ -177,3 +177,20 @@ impl Connection for PgConnection {
!self.stream.wbuf.is_empty()
}
}
pub trait PgConnectionInfo {
/// the version number of the server in `libpq` format
fn server_version_num(&self) -> Option<u32>;
}
impl PgConnectionInfo for PgConnection {
fn server_version_num(&self) -> Option<u32> {
self.stream.server_version_num
}
}
impl PgConnectionInfo for crate::pool::PoolConnection<Postgres> {
fn server_version_num(&self) -> Option<u32> {
self.stream.server_version_num
}
}

View file

@ -1,4 +1,6 @@
use std::collections::BTreeMap;
use std::ops::{Deref, DerefMut};
use std::str::FromStr;
use bytes::{Buf, Bytes};
use futures_channel::mpsc::UnboundedSender;
@ -8,7 +10,7 @@ use log::Level;
use crate::error::Error;
use crate::io::{BufStream, Decode, Encode};
use crate::net::{MaybeTlsStream, Socket};
use crate::postgres::message::{Message, MessageFormat, Notice, Notification};
use crate::postgres::message::{Message, MessageFormat, Notice, Notification, ParameterStatus};
use crate::postgres::{PgConnectOptions, PgDatabaseError, PgSeverity};
// the stream is a separate type from the connection to uphold the invariant where an instantiated
@ -27,6 +29,10 @@ pub struct PgStream {
// this is set when creating a PgListener and only written to if that listener is
// re-used for query execution in-between receiving messages
pub(crate) notifications: Option<UnboundedSender<Notification>>,
pub(crate) parameter_statuses: BTreeMap<String, String>,
pub(crate) server_version_num: Option<u32>,
}
impl PgStream {
@ -41,6 +47,8 @@ impl PgStream {
Ok(Self {
inner,
notifications: None,
parameter_statuses: BTreeMap::default(),
server_version_num: None,
})
}
@ -108,7 +116,18 @@ impl PgStream {
// informs the frontend about the current (initial)
// setting of backend parameters
// we currently have no use for that data so we promptly ignore this message
let ParameterStatus { name, value } = message.decode()?;
// TODO: handle `client_encoding`, `DateStyle` change
match name.as_str() {
"server_version" => {
self.server_version_num = parse_server_version(&value);
}
_ => {
self.parameter_statuses.insert(name, value);
}
}
continue;
}
@ -165,3 +184,68 @@ impl DerefMut for PgStream {
&mut self.inner
}
}
// reference:
// https://github.com/postgres/postgres/blob/6feebcb6b44631c3dc435e971bd80c2dd218a5ab/src/interfaces/libpq/fe-exec.c#L1030-L1065
fn parse_server_version(s: &str) -> Option<u32> {
let mut parts = Vec::<u32>::with_capacity(3);
let mut from = 0;
let mut chs = s.char_indices().peekable();
while let Some((i, ch)) = chs.next() {
match ch {
'.' => {
if let Ok(num) = u32::from_str(&s[from..i]) {
parts.push(num);
from = i + 1;
} else {
break;
}
}
_ if ch.is_digit(10) => {
if chs.peek().is_none() {
if let Ok(num) = u32::from_str(&s[from..]) {
parts.push(num);
}
break;
}
}
_ => {
if let Ok(num) = u32::from_str(&s[from..i]) {
parts.push(num);
}
break;
}
};
}
let version_num = match parts.as_slice() {
[major, minor, rev] => (100 * major + minor) * 100 + rev,
[major, minor] if *major >= 10 => 100 * 100 * major + minor,
[major, minor] => (100 * major + minor) * 100,
[major] => 100 * 100 * major,
_ => return None,
};
Some(version_num)
}
#[cfg(test)]
mod tests {
use super::parse_server_version;
#[test]
fn test_parse_server_version_num() {
// old style
assert_eq!(parse_server_version("9.6.1"), Some(90601));
// new style
assert_eq!(parse_server_version("10.1"), Some(100001));
// old style without minor version
assert_eq!(parse_server_version("9.6devel"), Some(90600));
// new style without minor version, e.g. */
assert_eq!(parse_server_version("10devel"), Some(100000));
assert_eq!(parse_server_version("13devel87"), Some(130000));
// unknown
assert_eq!(parse_server_version("unknown"), None);
}
}

View file

@ -14,6 +14,7 @@ mod execute;
mod flush;
mod notification;
mod parameter_description;
mod parameter_status;
mod parse;
mod password;
mod query;
@ -37,6 +38,7 @@ pub use execute::Execute;
pub use flush::Flush;
pub use notification::Notification;
pub use parameter_description::ParameterDescription;
pub use parameter_status::ParameterStatus;
pub use parse::Parse;
pub use password::Password;
pub use query::Query;

View file

@ -0,0 +1,49 @@
use bytes::Bytes;
use crate::error::Error;
use crate::io::{BufExt, Decode};
#[derive(Debug)]
pub struct ParameterStatus {
pub name: String,
pub value: String,
}
impl Decode<'_> for ParameterStatus {
fn decode_with(mut buf: Bytes, _: ()) -> Result<Self, Error> {
let name = buf.get_str_nul()?;
let value = buf.get_str_nul()?;
Ok(Self { name, value })
}
}
#[test]
fn test_decode_parameter_status() {
const DATA: &[u8] = b"client_encoding\x00UTF8\x00";
let m = ParameterStatus::decode(DATA.into()).unwrap();
assert_eq!(&m.name, "client_encoding");
assert_eq!(&m.value, "UTF8")
}
#[test]
fn test_decode_empty_parameter_status() {
const DATA: &[u8] = b"\x00\x00";
let m = ParameterStatus::decode(DATA.into()).unwrap();
assert!(m.name.is_empty());
assert!(m.value.is_empty());
}
#[cfg(all(test, not(debug_assertions)))]
#[bench]
fn bench_decode_parameter_status(b: &mut test::Bencher) {
const DATA: &[u8] = b"client_encoding\x00UTF8\x00";
b.iter(|| {
ParameterStatus::decode(test::black_box(Bytes::from_static(DATA))).unwrap();
});
}

View file

@ -22,7 +22,7 @@ mod migrate;
pub use arguments::{PgArgumentBuffer, PgArguments};
pub use column::PgColumn;
pub use connection::PgConnection;
pub use connection::{PgConnection, PgConnectionInfo};
pub use database::Postgres;
pub use error::{PgDatabaseError, PgErrorPosition};
pub use listener::{PgListener, PgNotification};

18
tests/README.md Normal file
View file

@ -0,0 +1,18 @@
### 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

View file

@ -2,7 +2,7 @@ version: "3"
services:
#
# MySQL 5.6.x, 5.7.x, 8.x
# MySQL 8.x, 5.7.x, 5.6.x
# https://www.mysql.com/support/supportedplatforms/database.html
#
@ -40,10 +40,20 @@ services:
MYSQL_DATABASE: sqlx
#
# MariaDB 10.5, 10.4, 10.3, 10.2, 10.1
# MariaDB 10.6, 10.5, 10.4, 10.3, 10.2
# https://mariadb.org/about/#maintenance-policy
#
mariadb_10_6:
image: mariadb:10.6
volumes:
- "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql"
ports:
- 3306
environment:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: sqlx
mariadb_10_5:
image: mariadb:10.5
volumes:
@ -84,27 +94,36 @@ services:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: sqlx
mariadb_10_1:
image: mariadb:10.1
volumes:
- "./mysql/setup.sql:/docker-entrypoint-initdb.d/setup.sql"
ports:
- 3306
environment:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: sqlx
#
# PostgreSQL 12.x, 10.x, 9.6.x, 9.5.x
# PostgreSQL 13.x, 12.x, 11.x 10.x, 9.6.x
# https://www.postgresql.org/support/versioning/
#
postgres_14:
build:
context: .
dockerfile: postgres/Dockerfile
args:
VERSION: 14beta2
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"
command: >
-c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key
postgres_13:
build:
context: .
dockerfile: postgres/Dockerfile
args:
VERSION: 13-beta1
VERSION: 13
ports:
- 5432
environment:
@ -123,7 +142,7 @@ services:
context: .
dockerfile: postgres/Dockerfile
args:
VERSION: 12.3
VERSION: 12
ports:
- 5432
environment:
@ -137,19 +156,39 @@ services:
command: >
-c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key
postgres_11:
build:
context: .
dockerfile: postgres/Dockerfile
args:
VERSION: 11
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"
command: >
-c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key
postgres_10:
build:
context: .
dockerfile: postgres/Dockerfile
args:
VERSION: 10.13
VERSION: 10
ports:
- 5432
environment:
POSTGRES_DB: sqlx
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_HOST_AUTH_METHOD: trust
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"
command: >
@ -173,24 +212,6 @@ services:
command: >
-c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key
postgres_9_5:
build:
context: .
dockerfile: postgres/Dockerfile
args:
VERSION: 9.5
ports:
- 5432
environment:
POSTGRES_DB: sqlx
POSTGRES_USER: postgres
POSTGRES_PASSWORD: password
POSTGRES_HOST_AUTH_METHOD: password
volumes:
- "./postgres/setup.sql:/docker-entrypoint-initdb.d/setup.sql"
command: >
-c ssl=on -c ssl_cert_file=/var/lib/postgresql/server.crt -c ssl_key_file=/var/lib/postgresql/server.key
#
# Microsoft SQL Server (MSSQL)
# https://hub.docker.com/_/microsoft-mssql-server
@ -205,17 +226,17 @@ services:
ports:
- 1433
environment:
ACCEPT_EULA: Y
ACCEPT_EULA: "Y"
SA_PASSWORD: Password123!
mssql_2017:
build:
context: .
dockerfile: mssql/Dockerfile
dockerfile: mssql/mssql-2017.dockerfile
args:
VERSION: 2017-latest
environment:
ACCEPT_EULA: Y
ACCEPT_EULA: "Y"
SA_PASSWORD: Password123!
#

View file

@ -1,4 +1,5 @@
import subprocess
import sys
import time
from os import path

View file

@ -0,0 +1,19 @@
# 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"]

View file

@ -188,12 +188,13 @@ async fn test_column_override_nullable() -> anyhow::Result<()> {
async fn with_test_row<'a>(
conn: &'a mut MySqlConnection,
) -> anyhow::Result<Transaction<'a, MySql>> {
) -> anyhow::Result<(Transaction<'a, MySql>, MyInt)> {
let mut transaction = conn.begin().await?;
sqlx::query!("INSERT INTO tweet(id, text, owner_id) VALUES (1, '#sqlx is pretty cool!', 1)")
let id = sqlx::query!("INSERT INTO tweet(text, owner_id) VALUES ('#sqlx is pretty cool!', 1)")
.execute(&mut transaction)
.await?;
Ok(transaction)
.await?
.last_insert_id();
Ok((transaction, MyInt(id as i64)))
}
#[derive(PartialEq, Eq, Debug, sqlx::Type)]
@ -211,13 +212,13 @@ struct OptionalRecord {
#[sqlx_macros::test]
async fn test_column_override_wildcard() -> anyhow::Result<()> {
let mut conn = new::<MySql>().await?;
let mut conn = with_test_row(&mut conn).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, MyInt(1));
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")
@ -238,7 +239,7 @@ async fn test_column_override_wildcard() -> anyhow::Result<()> {
#[sqlx_macros::test]
async fn test_column_override_wildcard_not_null() -> anyhow::Result<()> {
let mut conn = new::<MySql>().await?;
let mut conn = with_test_row(&mut conn).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)
@ -252,13 +253,13 @@ async fn test_column_override_wildcard_not_null() -> anyhow::Result<()> {
#[sqlx_macros::test]
async fn test_column_override_wildcard_nullable() -> anyhow::Result<()> {
let mut conn = new::<MySql>().await?;
let mut conn = with_test_row(&mut conn).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(MyInt(1)));
assert_eq!(record.id, Some(id));
Ok(())
}
@ -266,13 +267,13 @@ async fn test_column_override_wildcard_nullable() -> anyhow::Result<()> {
#[sqlx_macros::test]
async fn test_column_override_exact() -> anyhow::Result<()> {
let mut conn = new::<MySql>().await?;
let mut conn = with_test_row(&mut conn).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, MyInt(1));
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")
@ -293,7 +294,7 @@ async fn test_column_override_exact() -> anyhow::Result<()> {
#[sqlx_macros::test]
async fn test_column_override_exact_not_null() -> anyhow::Result<()> {
let mut conn = new::<MySql>().await?;
let mut conn = with_test_row(&mut conn).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)
@ -307,13 +308,13 @@ async fn test_column_override_exact_not_null() -> anyhow::Result<()> {
#[sqlx_macros::test]
async fn test_column_override_exact_nullable() -> anyhow::Result<()> {
let mut conn = new::<MySql>().await?;
let mut conn = with_test_row(&mut conn).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(MyInt(1)));
assert_eq!(record.id, Some(id));
Ok(())
}

View file

@ -2,7 +2,7 @@ use futures::TryStreamExt;
use sqlx::postgres::{
PgConnectOptions, PgConnection, PgDatabaseError, PgErrorPosition, PgSeverity,
};
use sqlx::postgres::{PgPoolOptions, PgRow, Postgres};
use sqlx::postgres::{PgConnectionInfo, PgPoolOptions, PgRow, Postgres};
use sqlx::{Column, Connection, Executor, Row, Statement, TypeInfo};
use sqlx_test::{new, setup_if_needed};
use std::env;
@ -968,6 +968,30 @@ async fn test_listener_cleanup() -> anyhow::Result<()> {
#[sqlx_macros::test]
async fn it_supports_domain_types_in_composite_domain_types() -> anyhow::Result<()> {
// Only supported in Postgres 11+
let mut conn = new::<Postgres>().await?;
if matches!(conn.server_version_num(), Some(version) if version < 110000) {
return Ok(());
}
conn.execute(
r#"
DROP TABLE IF EXISTS heating_bills;
DROP DOMAIN IF EXISTS winter_year_month;
DROP TYPE IF EXISTS year_month;
DROP DOMAIN IF EXISTS month_id;
CREATE DOMAIN month_id AS INT2 CHECK (1 <= value AND value <= 12);
CREATE TYPE year_month AS (year INT4, month month_id);
CREATE DOMAIN winter_year_month AS year_month CHECK ((value).month <= 3);
CREATE TABLE heating_bills (
month winter_year_month NOT NULL PRIMARY KEY,
cost INT4 NOT NULL
);
"#,
)
.await?;
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
struct MonthId(i16);
@ -1039,41 +1063,44 @@ async fn it_supports_domain_types_in_composite_domain_types() -> anyhow::Result<
sqlx::encode::IsNull::No
}
}
let mut conn = new::<Postgres>().await?;
{
let result = sqlx::query("DELETE FROM heating_bills;")
.execute(&mut conn)
.await;
let result = result.unwrap();
assert_eq!(result.rows_affected(), 1);
}
{
let result = sqlx::query(
"INSERT INTO heating_bills(month, cost) VALUES($1::winter_year_month, 100);",
)
.bind(WinterYearMonth {
year: 2021,
month: MonthId(1),
})
let result = sqlx::query("DELETE FROM heating_bills;")
.execute(&mut conn)
.await;
let result = result.unwrap();
assert_eq!(result.rows_affected(), 1);
}
let result = result.unwrap();
assert_eq!(result.rows_affected(), 0);
{
let result = sqlx::query("DELETE FROM heating_bills;")
let result =
sqlx::query("INSERT INTO heating_bills(month, cost) VALUES($1::winter_year_month, 100);")
.bind(WinterYearMonth {
year: 2021,
month: MonthId(1),
})
.execute(&mut conn)
.await;
let result = result.unwrap();
assert_eq!(result.rows_affected(), 1);
}
let result = result.unwrap();
assert_eq!(result.rows_affected(), 1);
let result = sqlx::query("DELETE FROM heating_bills;")
.execute(&mut conn)
.await;
let result = result.unwrap();
assert_eq!(result.rows_affected(), 1);
Ok(())
}
#[sqlx_macros::test]
async fn test_pg_server_num() -> anyhow::Result<()> {
use sqlx::postgres::PgConnectionInfo;
let conn = new::<Postgres>().await?;
assert!(conn.server_version_num().is_some());
Ok(())
}

View file

@ -29,11 +29,3 @@ CREATE TABLE products (
name TEXT,
price NUMERIC CHECK (price > 0)
);
CREATE DOMAIN month_id AS INT2 CHECK (1 <= value AND value <= 12);
CREATE TYPE year_month AS (year INT4, month month_id);
CREATE DOMAIN winter_year_month AS year_month CHECK ((value).month <= 3);
CREATE TABLE heating_bills (
month winter_year_month NOT NULL PRIMARY KEY,
cost INT4 NOT NULL
);

View file

@ -211,7 +211,7 @@ test_type!(ipnetwork_vec<Vec<sqlx::types::ipnetwork::IpNetwork>>(Postgres,
#[cfg(feature = "mac_address")]
test_type!(mac_address_vec<Vec<sqlx::types::mac_address::MacAddress>>(Postgres,
"'{01:02:03:04:05:06,FF:FF:FF:FF:FF:FF}'::inet[]"
"'{01:02:03:04:05:06,FF:FF:FF:FF:FF:FF}'::macaddr[]"
== vec![
"01:02:03:04:05:06".parse::<sqlx::types::mac_address::MacAddress>().unwrap(),
"FF:FF:FF:FF:FF:FF".parse::<sqlx::types::mac_address::MacAddress>().unwrap()

Binary file not shown.

View file

@ -88,116 +88,101 @@ for path in glob(os.path.join(os.path.dirname(__file__), "target/**/*.gc*"), rec
# check
#
run("cargo c", comment="check with a default set of features", tag="check")
run(
"cargo c --no-default-features --features runtime-async-std-native-tls,all-databases,all-types,offline,macros",
comment="check with async-std",
tag="check_async_std"
)
run(
"cargo c --no-default-features --features runtime-tokio-native-tls,all-databases,all-types,offline,macros",
comment="check with tokio",
tag="check_tokio"
)
run(
"cargo c --no-default-features --features runtime-actix-native-tls,all-databases,all-types,offline,macros",
comment="check with actix",
tag="check_actix"
)
for runtime in ["async-std", "tokio", "actix"]:
for tls in ["native-tls", "rustls"]:
run(
f"cargo c --no-default-features --features all-databases,all-types,offline,macros,runtime-{runtime}-{tls}",
comment="check with async-std",
tag=f"check_{runtime}_{tls}"
)
#
# unit test
#
run(
"cargo test --manifest-path sqlx-core/Cargo.toml --features all-databases,all-types",
comment="unit test core",
tag="unit"
)
run(
"cargo test --no-default-features --manifest-path sqlx-core/Cargo.toml --features all-databases,all-types,runtime-tokio-native-tls",
comment="unit test core",
tag="unit_tokio"
)
for runtime in ["async-std", "tokio", "actix"]:
for tls in ["native-tls", "rustls"]:
run(
f"cargo test --no-default-features --manifest-path sqlx-core/Cargo.toml --features all-databases,all-types,runtime-{runtime}-{tls}",
comment="unit test core",
tag=f"unit_{runtime}_{tls}"
)
#
# integration tests
#
for runtime in ["async-std", "tokio", "actix"]:
for tls in ["native-tls", "rustls"]:
#
# sqlite
#
#
# sqlite
#
run(
f"cargo test --no-default-features --features macros,offline,any,all-types,sqlite,runtime-{runtime}-native-tls",
comment=f"test sqlite",
service="sqlite",
tag=f"sqlite" if runtime == "async-std" else f"sqlite_{runtime}",
)
#
# postgres
#
for version in ["12", "10", "9_6", "9_5"]:
run(
f"cargo test --no-default-features --features macros,offline,any,all-types,postgres,runtime-{runtime}-native-tls",
comment=f"test postgres {version}",
service=f"postgres_{version}",
tag=f"postgres_{version}" if runtime == "async-std" else f"postgres_{version}_{runtime}",
f"cargo test --no-default-features --features macros,offline,any,all-types,sqlite,runtime-{runtime}-native-tls",
comment=f"test sqlite",
service="sqlite",
tag=f"sqlite" if runtime == "async-std" else f"sqlite_{runtime}",
)
# +ssl
for version in ["12", "10", "9_6", "9_5"]:
run(
f"cargo test --no-default-features --features macros,offline,any,all-types,postgres,runtime-{runtime}-native-tls",
comment=f"test postgres {version} ssl",
database_url_args="sslmode=verify-ca&sslrootcert=.%2Ftests%2Fcerts%2Fca.crt",
service=f"postgres_{version}",
tag=f"postgres_{version}_ssl" if runtime == "async-std" else f"postgres_{version}_ssl_{runtime}",
)
#
# postgres
#
#
# mysql
#
for version in ["13", "12", "11", "10", "9_6"]:
run(
f"cargo test --no-default-features --features macros,offline,any,all-types,postgres,runtime-{runtime}-native-tls",
comment=f"test postgres {version}",
service=f"postgres_{version}",
tag=f"postgres_{version}" if runtime == "async-std" else f"postgres_{version}_{runtime}",
)
for version in ["8", "5_7", "5_6"]:
run(
f"cargo test --no-default-features --features macros,offline,any,all-types,mysql,runtime-{runtime}-native-tls",
comment=f"test mysql {version}",
service=f"mysql_{version}",
tag=f"mysql_{version}" if runtime == "async-std" else f"mysql_{version}_{runtime}",
)
## +ssl
for version in ["13", "12", "11", "10", "9_6"]:
run(
f"cargo test --no-default-features --features macros,offline,any,all-types,postgres,runtime-{runtime}-native-tls",
comment=f"test postgres {version} ssl",
database_url_args="sslmode=verify-ca&sslrootcert=.%2Ftests%2Fcerts%2Fca.crt",
service=f"postgres_{version}",
tag=f"postgres_{version}_ssl" if runtime == "async-std" else f"postgres_{version}_ssl_{runtime}",
)
#
# mariadb
#
#
# mysql
#
for version in ["10_5", "10_4", "10_3", "10_2", "10_1"]:
run(
f"cargo test --no-default-features --features macros,offline,any,all-types,mysql,runtime-{runtime}-native-tls",
comment=f"test mariadb {version}",
service=f"mariadb_{version}",
tag=f"mariadb_{version}" if runtime == "async-std" else f"mariadb_{version}_{runtime}",
)
for version in ["8", "5_7", "5_6"]:
run(
f"cargo test --no-default-features --features macros,offline,any,all-types,mysql,runtime-{runtime}-native-tls",
comment=f"test mysql {version}",
service=f"mysql_{version}",
tag=f"mysql_{version}" if runtime == "async-std" else f"mysql_{version}_{runtime}",
)
#
# mssql
#
#
# mariadb
#
for version in ["2019"]:
run(
f"cargo test --no-default-features --features macros,offline,any,all-types,mssql,runtime-{runtime}-native-tls",
comment=f"test mssql {version}",
service=f"mssql_{version}",
tag=f"mssql_{version}" if runtime == "async-std" else f"mssql_{version}_{runtime}",
)
for version in ["10_6", "10_5", "10_4", "10_3", "10_2"]:
run(
f"cargo test --no-default-features --features macros,offline,any,all-types,mysql,runtime-{runtime}-native-tls",
comment=f"test mariadb {version}",
service=f"mariadb_{version}",
tag=f"mariadb_{version}" if runtime == "async-std" else f"mariadb_{version}_{runtime}",
)
#
# mssql
#
for version in ["2019", "2017"]:
run(
f"cargo test --no-default-features --features macros,offline,any,all-types,mssql,runtime-{runtime}-native-tls",
comment=f"test mssql {version}",
service=f"mssql_{version}",
tag=f"mssql_{version}" if runtime == "async-std" else f"mssql_{version}_{runtime}",
)
# TODO: Use [grcov] if available
# ~/.cargo/bin/grcov tests/.cache/target/debug -s sqlx-core/ -t html --llvm --branch -o ./target/debug/coverage