Merge remote-tracking branch 'origin/ab/macro-fixes'

This commit is contained in:
Ryan Leckey 2020-06-07 02:35:12 -07:00
commit 53c074130f
13 changed files with 205 additions and 6 deletions

View File

@ -108,6 +108,11 @@ name = "sqlite-describe"
path = "tests/sqlite/describe.rs"
required-features = [ "sqlite" ]
[[test]]
name = "sqlite-macros"
path = "tests/sqlite/macros.rs"
required-features = [ "sqlite", "macros" ]
#
# MySQL
#
@ -127,6 +132,11 @@ name = "mysql-describe"
path = "tests/mysql/describe.rs"
required-features = [ "mysql" ]
[[test]]
name = "mysql-macros"
path = "tests/mysql/macros.rs"
required-features = [ "mysql", "macros" ]
#
# PostgreSQL
#
@ -146,6 +156,11 @@ name = "postgres-describe"
path = "tests/postgres/describe.rs"
required-features = [ "postgres" ]
[[test]]
name = "postgres-macros"
path = "tests/postgres/macros.rs"
required-features = [ "postgres", "macros" ]
#
# Microsoft SQL Server (MSSQL)
#

View File

@ -275,9 +275,10 @@ impl<'c> Executor<'c> for &'c mut MySqlConnection {
for _ in 0..(ok.columns as usize) {
let def: ColumnDefinition = self.stream.recv().await?;
let ty = MySqlTypeInfo::from_column(&def);
let name = def.name()?;
columns.push(Column {
name: def.name()?.to_owned(),
name: if name.is_empty() { def.alias()? } else { name }.to_owned(),
type_info: ty,
not_null: Some(def.flags.contains(ColumnFlags::NOT_NULL)),
})

View File

@ -78,6 +78,18 @@ impl PartialEq<MySqlTypeInfo> for MySqlTypeInfo {
== other.flags.contains(ColumnFlags::UNSIGNED);
}
// for string types, check that our charset matches
ColumnType::VarChar
| ColumnType::Blob
| ColumnType::TinyBlob
| ColumnType::MediumBlob
| ColumnType::LongBlob
| ColumnType::String
| ColumnType::VarString
| ColumnType::Enum => {
return self.char_set == other.char_set;
}
_ => {}
}

View File

@ -36,7 +36,7 @@ impl<'r> Decode<'r, MySql> for &'r str {
| ColumnType::String
| ColumnType::VarString
| ColumnType::Enum
)
) && ty.char_set == 224
}
fn decode(value: MySqlValueRef<'r>) -> Result<Self, BoxDynError> {

View File

@ -13,6 +13,7 @@ impl_database_ext! {
f32,
f64,
// ordering is important here as otherwise we might infer strings to be binary
// CHAR, VAR_CHAR, TEXT
String,

View File

@ -10,6 +10,7 @@ use sqlx_core::executor::Executor;
deserialize = "Describe<DB>: serde::de::DeserializeOwned"
))
)]
#[derive(Debug)]
pub struct QueryData<DB: Database> {
#[allow(dead_code)]
pub(super) query: String,

View File

@ -183,7 +183,7 @@ where
let sql = &input.src;
quote! {
sqlx::query::<#db_path>(#sql).bind_all(#query_args)
sqlx::query_with::<#db_path, _>(#sql, #query_args)
}
} else {
let columns = output::columns_to_rust::<DB>(&data.describe)?;

View File

@ -119,7 +119,7 @@ pub fn quote_query_as<DB: DatabaseExt>(
let sql = &input.src;
quote! {
sqlx::query_with::<#db_path>(#sql, #bind_args).try_map(|row: #row_path| {
sqlx::query_with::<#db_path, _>(#sql, #bind_args).try_map(|row: #row_path| {
use sqlx::Row as _;
use sqlx::result_ext::ResultExt as _;

94
tests/mysql/macros.rs Normal file
View File

@ -0,0 +1,94 @@
use sqlx::MySql;
use sqlx_test::new;
#[sqlx_macros::test]
async fn macro_select_from_cte() -> anyhow::Result<()> {
let mut conn = new::<MySql>().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::<MySql>().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<String>,
}
#[sqlx_macros::test]
async fn test_query_as_raw() -> anyhow::Result<()> {
let mut conn = new::<MySql>().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_as_bool() -> anyhow::Result<()> {
let mut conn = new::<MySql>().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::<MySql>().await?;
let rec = sqlx::query!("SELECT X'01AF' as _1")
.fetch_one(&mut conn)
.await?;
assert_eq!(rec._1, &[0x01_u8, 0xAF_u8]);
Ok(())
}

View File

@ -65,7 +65,8 @@ async fn test_text_var_char_char_n() -> anyhow::Result<()> {
async fn _file() -> anyhow::Result<()> {
let mut conn = new::<Postgres>().await?;
let account = sqlx::query_file!("tests/test-query.sql",)
// keep trailing comma as a test
let account = sqlx::query_file!("tests/postgres/test-query.sql",)
.fetch_one(&mut conn)
.await?;
@ -129,7 +130,7 @@ async fn test_query_as_raw() -> anyhow::Result<()> {
async fn test_query_file_as() -> anyhow::Result<()> {
let mut conn = new::<Postgres>().await?;
let account = sqlx::query_file_as!(Account, "tests/test-query.sql",)
let account = sqlx::query_file_as!(Account, "tests/postgres/test-query.sql",)
.fetch_one(&mut conn)
.await?;

View File

@ -0,0 +1 @@
SELECT * from (VALUES (1, null)) accounts(id, name)

73
tests/sqlite/macros.rs Normal file
View File

@ -0,0 +1,73 @@
use sqlx::Sqlite;
use sqlx_test::new;
#[sqlx_macros::test]
async fn macro_select() -> anyhow::Result<()> {
let mut conn = new::<Sqlite>().await?;
let account = sqlx::query!("select id, name, is_active from accounts where id = 1")
.fetch_one(&mut conn)
.await?;
assert_eq!(1, account.id);
assert_eq!("Herp Derpinson", account.name);
assert_eq!(account.is_active, None);
Ok(())
}
#[sqlx_macros::test]
async fn macro_select_bind() -> anyhow::Result<()> {
let mut conn = new::<Sqlite>().await?;
let account = sqlx::query!(
"select id, name, is_active from accounts where id = ?",
1i32
)
.fetch_one(&mut conn)
.await?;
assert_eq!(1, account.id);
assert_eq!("Herp Derpinson", account.name);
assert_eq!(account.is_active, None);
Ok(())
}
#[derive(Debug)]
struct RawAccount {
id: i32,
name: String,
is_active: Option<bool>,
}
#[sqlx_macros::test]
async fn test_query_as_raw() -> anyhow::Result<()> {
let mut conn = new::<Sqlite>().await?;
let account = sqlx::query_as!(RawAccount, "SELECT id, name, is_active from accounts")
.fetch_one(&mut conn)
.await?;
assert_eq!(account.id, 1);
assert_eq!(account.name, "Herp Derpinson");
assert_eq!(account.is_active, None);
Ok(())
}
#[sqlx_macros::test]
async fn macro_select_from_view() -> anyhow::Result<()> {
let mut conn = new::<Sqlite>().await?;
let account = sqlx::query!("SELECT id, name, is_active from accounts_view")
.fetch_one(&mut conn)
.await?;
// SQLite tells us the true origin of these columns even through the view
assert_eq!(account.id, 1);
assert_eq!(account.name, "Herp Derpinson");
assert_eq!(account.is_active, None);
Ok(())
}

Binary file not shown.