From bddb2e560f74800875c4ccf14d8f5000da4d79e4 Mon Sep 17 00:00:00 2001 From: Austin Bonander Date: Fri, 5 Jun 2020 20:28:21 -0700 Subject: [PATCH 1/3] fix(macros): fix macros for Postgres --- Cargo.toml | 5 +++++ sqlx-macros/src/query/mod.rs | 2 +- sqlx-macros/src/query/output.rs | 2 +- tests/postgres/macros.rs | 5 +++-- tests/postgres/test-query.sql | 1 + 5 files changed, 11 insertions(+), 4 deletions(-) create mode 100644 tests/postgres/test-query.sql diff --git a/Cargo.toml b/Cargo.toml index aed501a6..e23fbdc1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -144,3 +144,8 @@ required-features = [ "postgres" ] name = "postgres-describe" path = "tests/postgres/describe.rs" required-features = [ "postgres" ] + +[[test]] +name = "postgres-macros" +path = "tests/postgres/macros.rs" +required-features = [ "postgres", "macros" ] diff --git a/sqlx-macros/src/query/mod.rs b/sqlx-macros/src/query/mod.rs index 1300b024..2daed064 100644 --- a/sqlx-macros/src/query/mod.rs +++ b/sqlx-macros/src/query/mod.rs @@ -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::(&data.describe)?; diff --git a/sqlx-macros/src/query/output.rs b/sqlx-macros/src/query/output.rs index 09c78cd9..cdc6c738 100644 --- a/sqlx-macros/src/query/output.rs +++ b/sqlx-macros/src/query/output.rs @@ -119,7 +119,7 @@ pub fn quote_query_as( 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 _; diff --git a/tests/postgres/macros.rs b/tests/postgres/macros.rs index f0005d0a..37d5019a 100644 --- a/tests/postgres/macros.rs +++ b/tests/postgres/macros.rs @@ -65,7 +65,8 @@ async fn test_text_var_char_char_n() -> anyhow::Result<()> { async fn _file() -> anyhow::Result<()> { let mut conn = new::().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::().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?; diff --git a/tests/postgres/test-query.sql b/tests/postgres/test-query.sql new file mode 100644 index 00000000..cb8ab010 --- /dev/null +++ b/tests/postgres/test-query.sql @@ -0,0 +1 @@ +SELECT * from (VALUES (1, null)) accounts(id, name) From 80b4e2fca687c8fd781a7afc8fb780232dfa25f3 Mon Sep 17 00:00:00 2001 From: Austin Bonander Date: Fri, 5 Jun 2020 21:02:24 -0700 Subject: [PATCH 2/3] fix(macros): reintroduce and fix macro tests for MySQL --- Cargo.toml | 10 +++ sqlx-core/src/mysql/connection/executor.rs | 3 +- sqlx-core/src/mysql/type_info.rs | 12 +++ sqlx-core/src/mysql/types/str.rs | 2 +- sqlx-macros/src/database/mysql.rs | 1 + sqlx-macros/src/query/data.rs | 1 + tests/mysql/macros.rs | 94 ++++++++++++++++++++++ 7 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 tests/mysql/macros.rs diff --git a/Cargo.toml b/Cargo.toml index e23fbdc1..634af794 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -107,6 +107,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 # @@ -126,6 +131,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 # diff --git a/sqlx-core/src/mysql/connection/executor.rs b/sqlx-core/src/mysql/connection/executor.rs index b0042062..6530ab9a 100644 --- a/sqlx-core/src/mysql/connection/executor.rs +++ b/sqlx-core/src/mysql/connection/executor.rs @@ -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)), }) diff --git a/sqlx-core/src/mysql/type_info.rs b/sqlx-core/src/mysql/type_info.rs index a37e5bd9..75153e84 100644 --- a/sqlx-core/src/mysql/type_info.rs +++ b/sqlx-core/src/mysql/type_info.rs @@ -78,6 +78,18 @@ impl PartialEq 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; + } + _ => {} } diff --git a/sqlx-core/src/mysql/types/str.rs b/sqlx-core/src/mysql/types/str.rs index 86210901..17a6464e 100644 --- a/sqlx-core/src/mysql/types/str.rs +++ b/sqlx-core/src/mysql/types/str.rs @@ -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 { diff --git a/sqlx-macros/src/database/mysql.rs b/sqlx-macros/src/database/mysql.rs index e6b04d48..43c5291e 100644 --- a/sqlx-macros/src/database/mysql.rs +++ b/sqlx-macros/src/database/mysql.rs @@ -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, diff --git a/sqlx-macros/src/query/data.rs b/sqlx-macros/src/query/data.rs index 3f7e4183..9af35a48 100644 --- a/sqlx-macros/src/query/data.rs +++ b/sqlx-macros/src/query/data.rs @@ -10,6 +10,7 @@ use sqlx_core::executor::Executor; deserialize = "Describe: serde::de::DeserializeOwned" )) )] +#[derive(Debug)] pub struct QueryData { #[allow(dead_code)] pub(super) query: String, diff --git a/tests/mysql/macros.rs b/tests/mysql/macros.rs new file mode 100644 index 00000000..f949fb48 --- /dev/null +++ b/tests/mysql/macros.rs @@ -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::().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_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(()) +} From 0652864315f0a7f43df6585717fbb929126c71a9 Mon Sep 17 00:00:00 2001 From: Austin Bonander Date: Fri, 5 Jun 2020 21:16:14 -0700 Subject: [PATCH 3/3] fix(macros): reintroduce and fix test for macros in SQLite --- tests/sqlite/macros.rs | 73 +++++++++++++++++++++++++++++++++++++++++ tests/sqlite/sqlite.db | Bin 20480 -> 36864 bytes 2 files changed, 73 insertions(+) create mode 100644 tests/sqlite/macros.rs diff --git a/tests/sqlite/macros.rs b/tests/sqlite/macros.rs new file mode 100644 index 00000000..2e0825c9 --- /dev/null +++ b/tests/sqlite/macros.rs @@ -0,0 +1,73 @@ +use sqlx::Sqlite; +use sqlx_test::new; + +#[sqlx_macros::test] +async fn macro_select() -> anyhow::Result<()> { + let mut conn = new::().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::().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, +} + +#[sqlx_macros::test] +async fn test_query_as_raw() -> anyhow::Result<()> { + let mut conn = new::().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::().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(()) +} diff --git a/tests/sqlite/sqlite.db b/tests/sqlite/sqlite.db index 8262b8a3999c96461cb3f564c127796aeaa0f270..9faa9c8e7ed7dfa6e4d7a95e8ee4beb03fac3660 100644 GIT binary patch delta 763 zcmZozz}T>WX@az%5(5JRClIp(F%uArOw=)!S7Ol9$mHdj%)rj$$iOemf0nP1?;ejM z?^E6$o=-fE8ygRE*9UO2iEC>!MwTQd<)juD=46(n#uuj+mZs(Tq#_{H9QnS^<=XDgg>+7RM(hmt>ZuDkSCS=cFd)acPEgvx%y!a}<|lrk2CK zg293MILy;ET%jICnL=W*LUC$NYI2E!mO@%ley##cx{;HMU0hZc-P15>Hmsqjpuwd8 z1c(rXJ6{3jKNGMAL8`F%5v&{2o0|*y`W?A=fys=K{|y8Go6Uj(kN8!D7+DxJ89Y*p z3KU#`L}p%bejZQ>2rvRM2M7aIzUQC(U!Ro?Nv9k(ogmX01Q;NuKjjw`;eh!Snvej6 CVeu9K delta 61 zcmZozz|^pSae}lUHv