use futures::{AsyncRead, AsyncWrite}; use futures_util::TryStreamExt; use names::{Generator, Name}; use once_cell::sync::Lazy; use std::cell::RefCell; use std::env; use std::sync::Once; use tiberius::FromSql; use tiberius::{numeric::Numeric, xml::XmlData, ColumnType, Query, QueryItem, Result}; use uuid::Uuid; use runtimes_macro::test_on_runtimes; // This is used in the testing macro :) #[allow(dead_code)] static LOGGER_SETUP: Once = Once::new(); static CONN_STR: Lazy = Lazy::new(|| { env::var("TIBERIUS_TEST_CONNECTION_STRING").unwrap_or_else(|_| { "server=tcp:localhost,1433;user=SA;password=;IntegratedSecurity=true;TrustServerCertificate=true".to_owned() }) }); thread_local! { static NAMES: RefCell>> = RefCell::new(None); } async fn random_table() -> String { NAMES.with(|maybe_generator| { maybe_generator .borrow_mut() .get_or_insert_with(|| Generator::with_naming(Name::Plain)) .next() .unwrap() .replace('-', "") }) } static DOT_CONN_STR: Lazy = Lazy::new(|| CONN_STR.replace("localhost", ".")); static ENCRYPTED_CONN_STR: Lazy = Lazy::new(|| format!("{};encrypt=true", *CONN_STR)); static PLAIN_TEXT_CONN_STR: Lazy = Lazy::new(|| format!("{};encrypt=DANGER_PLAINTEXT", *CONN_STR)); #[test_on_runtimes(connection_string = "ENCRYPTED_CONN_STR")] async fn connect_with_full_encryption(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let row = conn .query("SELECT @P1", &[&-4i32]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(-4i32), row.get(0)); Ok(()) } #[test_on_runtimes(connection_string = "PLAIN_TEXT_CONN_STR")] async fn connect_as_plain_text(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let row = conn .query("SELECT @P1", &[&-4i32]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(-4i32), row.get(0)); Ok(()) } #[test_on_runtimes(connection_string = "DOT_CONN_STR")] async fn connect_dot_server_as_localhost(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let row = conn .query("SELECT @P1", &[&-4i32]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(-4i32), row.get(0)); Ok(()) } #[test_on_runtimes] async fn transactions(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { conn.simple_query("BEGIN TRAN").await?; let row = conn .query("SELECT @P1", &[&-4i32]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(-4i32), row.get(0)); conn.simple_query("COMMIT").await?; Ok(()) } #[test_on_runtimes] async fn transactions_300(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { for _ in 1..300 { conn.simple_query("BEGIN TRAN").await?; let row = conn .query("SELECT @P1", &[&-4i32]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(-4i32), row.get(0)); conn.simple_query("COMMIT").await?; } Ok(()) } #[test_on_runtimes] async fn multistatement_query_with_exec_proc(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; let proc = random_table().await; conn.simple_query(format!( r#" create table ##{} ( id int identity(1,1), other varchar(50), ) "#, table )) .await?; conn.simple_query(format!( r#" create or alter procedure {} @Param1 varchar(50) as insert into ##{} (other) values (@Param1) return scope_identity() "#, proc, table, )) .await?; let stream = conn .query( format!( "set nocount off; declare @rc int; exec @rc = {} @P1; select @rc as Id;", proc ), &[&"test insert"], ) .await?; let row = stream.into_row().await?.unwrap(); assert_eq!(Some(1), row.get(0)); let stream = conn .query( format!( "set nocount on; declare @rc int; exec @rc = {} @P1; select @rc as Id;", proc ), &[&"test insert"], ) .await?; let row = stream.into_row().await?.unwrap(); assert_eq!(Some(2), row.get(0)); Ok(()) } #[test_on_runtimes] async fn read_and_write_tinyint(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.execute(format!("CREATE TABLE ##{} (content TINYINT)", table), &[]) .await?; let res = conn .execute( format!("INSERT INTO ##{} (content) VALUES (@P1), (@P2)", table), &[&u8::MIN, &u8::MAX], ) .await?; assert_eq!(2, res.total()); let rows = conn .query(format!("SELECT content FROM ##{}", table), &[]) .await? .into_first_result() .await?; assert_eq!(Some(u8::MIN), rows[0].get(0)); assert_eq!(Some(u8::MAX), rows[1].get(0)); Ok(()) } #[test_on_runtimes] async fn read_and_write_kanji_varchars(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.execute( format!("CREATE TABLE ##{} (content NVARCHAR(max))", table), &[], ) .await?; let kanji = "余ったものを後で皆に分けようと思っていただけなのに".to_string(); let long_kanji = "余".repeat(80001); let res = conn .execute( format!("INSERT INTO ##{} (content) VALUES (@P1), (@P2)", table), &[&kanji, &long_kanji], ) .await?; assert_eq!(2, res.total()); let rows = conn .query(format!("SELECT content FROM ##{}", table), &[]) .await? .into_first_result() .await?; assert_eq!(Some(kanji.as_str()), rows[0].get(0)); assert_eq!(Some(long_kanji.as_str()), rows[1].get(0)); Ok(()) } #[test_on_runtimes] async fn read_and_write_weird_garbage(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.execute( format!("CREATE TABLE ##{} (content NVARCHAR(1000))", table), &[], ) .await?; let s = "¥฿😀😁😂😃😄😅😆😇😈😉😊😋😌😍😎😏😐😑😒😓😔😕😖😗😘😙😚😛😜😝😞😟😠😡😢😣😤😥😦😧😨😩😪😫😬😭😮😯😰😱😲😳😴😵😶😷😸😹😺😻😼😽😾😿🙀🙁🙂🙃🙄🙅🙆🙇🙈🙉🙊🙋🙌🙍🙎🙏ऀँंःऄअआइईउऊऋऌऍऎएऐऑऒओऔकखगघङचछजझञटठडढणतथदधनऩपफबभमयर€₭₮₯₰₱₲₳₴₵₶₷₸₹₺₻₼₽₾₿⃀".to_string(); let res = conn .execute( format!("INSERT INTO ##{} (content) VALUES (@P1)", table), &[&s], ) .await?; assert_eq!(1, res.total()); let rows = conn .query(format!("SELECT content FROM ##{}", table), &[]) .await? .into_first_result() .await?; assert_eq!(Some(s.as_str()), rows[0].get(0)); Ok(()) } #[test_on_runtimes] async fn read_and_write_finnish_varchars(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.execute( format!("CREATE TABLE ##{} (content NVARCHAR(max))", table), &[], ) .await?; let kalevala = "Vaka vanha Väinämöinen / elelevi aikojansa / noilla Väinölän ahoilla, Kalevalan kankahilla."; let res = conn .execute( format!("INSERT INTO ##{} (content) VALUES (@P1)", table), &[&kalevala], ) .await?; assert_eq!(1, res.total()); let row = conn .query(format!("SELECT content FROM ##{}", table), &[]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(kalevala), row.get(0)); Ok(()) } #[test_on_runtimes] async fn read_and_write_char(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.simple_query(format!("CREATE TABLE ##{} (a char)", table)) .await? .into_results() .await?; conn.execute(format!("INSERT INTO ##{} (a) values (@P1)", table), &[&"a"]) .await?; let row = conn .query(format!("SELECT a FROM ##{}", table), &[]) .await? .into_row() .await? .unwrap(); assert_eq!(Some("a"), row.get(0)); Ok(()) } #[test_on_runtimes] async fn read_and_write_nchar(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.simple_query(format!("CREATE TABLE ##{} (a nchar)", table)) .await? .into_results() .await?; conn.execute(format!("INSERT INTO ##{} (a) values (@P1)", table), &[&"ä"]) .await?; let row = conn .query(format!("SELECT a FROM ##{}", table), &[]) .await? .into_row() .await? .unwrap(); assert_eq!(Some("ä"), row.get(0)); Ok(()) } #[test_on_runtimes] async fn execute_insert_update_delete(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.execute(format!("CREATE TABLE ##{} (id int)", table), &[]) .await?; let insert_count = conn .execute( format!("INSERT INTO ##{} (id) VALUES (@P1), (@P2), (@P3)", table), &[&1i32, &2i32, &3i32], ) .await? .total(); assert_eq!(3, insert_count); let update_count = conn .execute( format!("UPDATE ##{} SET id = @P1 WHERE id = @P2", table), &[&2i32, &1i32], ) .await? .total(); assert_eq!(1, update_count); let delete_count = conn .execute(format!("DELETE ##{} WHERE id <> @P1", table), &[&3i32]) .await? .total(); assert_eq!(2, delete_count); Ok(()) } #[test_on_runtimes] async fn execute_with_multiple_separate_results(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.execute(format!("CREATE TABLE ##{} (id int)", table), &[]) .await?; let insert_count = conn .execute( format!("INSERT INTO ##{table} (id) VALUES (@P1); INSERT INTO ##{table} (id) VALUES (@P2), (@P3);", table = table), &[&1i32, &2i32, &3i32], ) .await?; let result: Vec<_> = insert_count.into_iter().collect(); assert_eq!(vec![1, 2], result); Ok(()) } #[test_on_runtimes] async fn execute_multiple_count_total(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.execute(format!("CREATE TABLE ##{} (id int)", table), &[]) .await?; let insert_count = conn .execute( format!( "INSERT INTO ##{table} (id) VALUES (@P1); INSERT INTO ##{table} (id) VALUES (@P2), (@P3);", table = table ), &[&1i32, &2i32, &3i32], ) .await?; let result = insert_count.total(); assert_eq!(3, result); Ok(()) } #[test_on_runtimes] async fn correct_row_handling_when_not_enough_data(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let mut stream = conn .query( "SELECT @P1; SELECT @P2;", &[&"a".repeat(4001), &"b".repeat(2095)], ) .await?; while let Some(item) = stream.try_next().await? { match item { QueryItem::Metadata(_) => { continue; } QueryItem::Row(row) if row.result_index() == 0 => { assert_eq!(Some("a".repeat(4001).as_str()), row.get(0)); } QueryItem::Row(row) => { assert_eq!(Some("b".repeat(2095).as_str()), row.get(0)); } } } Ok(()) } #[test_on_runtimes] async fn multiple_stored_procedure_functions(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let stream = conn.query("EXECUTE sp_executesql N'SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1'; EXECUTE sp_executesql N'SELECT 2 UNION ALL SELECT 2'", &[]).await?; let rows: Result> = stream .try_filter_map(|item| async move { Ok(item.into_row()) }) .map_ok(|row| row.get::(0).unwrap()) .try_collect() .await; assert_eq!(rows?, vec![1, 1, 1, 2, 2]); Ok(()) } #[test_on_runtimes] async fn multiple_queries(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let mut stream = conn .query("SELECT 'a' AS first; SELECT 'b' AS second;", &[]) .await?; while let Some(item) = stream.try_next().await? { match item { QueryItem::Metadata(meta) if meta.result_index() == 0 => { assert_eq!("first", meta.columns()[0].name()); } QueryItem::Row(row) if row.result_index() == 0 => { assert_eq!(Some("a"), row.get(0)) } QueryItem::Metadata(meta) => { assert_eq!("second", meta.columns()[0].name()); } QueryItem::Row(row) => { assert_eq!(Some("b"), row.get(0)) } } } Ok(()) } #[test_on_runtimes] async fn bool_type(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let row = conn .query("SELECT @P1, @P2 ORDER BY 1", &[&false, &true]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(false), row.get(0)); assert_eq!(Some(true), row.get(1)); Ok(()) } #[test_on_runtimes] async fn u8_token(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let row = conn .query("SELECT @P1", &[&4u8]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(4u8), row.get(0)); Ok(()) } #[test_on_runtimes] async fn i16_token(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let row = conn .query("SELECT @P1", &[&-4i16]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(-4i16), row.get(0)); Ok(()) } #[test_on_runtimes] async fn i32_token(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let row = conn .query("SELECT @P1", &[&-4i32]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(-4i32), row.get(0)); Ok(()) } #[test_on_runtimes] async fn i64_token(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let row = conn .query("SELECT @P1", &[&-4i64]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(-4i64), row.get(0)); Ok(()) } #[test_on_runtimes] async fn f32_token(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let row = conn .query("SELECT @P1", &[&4.20f32]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(4.20f32), row.get(0)); Ok(()) } #[test_on_runtimes] async fn f64_token(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let row = conn .query("SELECT @P1", &[&4.20f64]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(4.20f64), row.get(0)); Ok(()) } #[test_on_runtimes] async fn read_nullable_u8(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.simple_query(format!("CREATE TABLE ##{} (a tinyint null)", table)) .await? .into_results() .await?; conn.execute(format!("INSERT INTO ##{} (a) values (null)", table), &[]) .await?; let row = conn .query(format!("SELECT a FROM ##{}", table), &[]) .await? .into_row() .await? .unwrap(); for val in row { assert_eq!(&tiberius::ColumnData::U8(None), &val); assert_eq!(Option::::None, u8::from_sql(&val)?) } Ok(()) } #[test_on_runtimes] async fn read_nullable_i16(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.simple_query(format!("CREATE TABLE ##{} (a smallint null)", table)) .await? .into_results() .await?; conn.execute(format!("INSERT INTO ##{} (a) values (null)", table), &[]) .await?; let row = conn .query(format!("SELECT a FROM ##{}", table), &[]) .await? .into_row() .await? .unwrap(); for val in row { assert_eq!(&tiberius::ColumnData::I16(None), &val); assert_eq!(Option::::None, i16::from_sql(&val)?) } Ok(()) } #[test_on_runtimes] async fn read_nullable_i32(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.simple_query(format!("CREATE TABLE ##{} (a int null)", table)) .await? .into_results() .await?; conn.execute(format!("INSERT INTO ##{} (a) values (null)", table), &[]) .await?; let row = conn .query(format!("SELECT a FROM ##{}", table), &[]) .await? .into_row() .await? .unwrap(); for val in row { assert_eq!(&tiberius::ColumnData::I32(None), &val); assert_eq!(Option::::None, i32::from_sql(&val)?) } Ok(()) } #[test_on_runtimes] async fn read_nullable_i64(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.simple_query(format!("CREATE TABLE ##{} (a bigint null)", table)) .await? .into_results() .await?; conn.execute(format!("INSERT INTO ##{} (a) values (null)", table), &[]) .await?; let row = conn .query(format!("SELECT a FROM ##{}", table), &[]) .await? .into_row() .await? .unwrap(); for val in row { assert_eq!(&tiberius::ColumnData::I64(None), &val); assert_eq!(Option::::None, i64::from_sql(&val)?) } Ok(()) } #[test_on_runtimes] async fn read_nullable_f32(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.simple_query(format!("CREATE TABLE ##{} (a real null)", table)) .await? .into_results() .await?; conn.execute(format!("INSERT INTO ##{} (a) values (null)", table), &[]) .await?; let row = conn .query(format!("SELECT a FROM ##{}", table), &[]) .await? .into_row() .await? .unwrap(); for val in row { assert_eq!(Option::::None, f32::from_sql(&val)?); dbg!(&val); assert_eq!(tiberius::ColumnData::F32(None), val); } Ok(()) } #[test_on_runtimes] async fn read_nullable_f64(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.simple_query(format!("CREATE TABLE ##{} (a float(25) null)", table)) .await? .into_results() .await?; conn.execute(format!("INSERT INTO ##{} (a) values (null)", table), &[]) .await?; let row = conn .query(format!("SELECT a FROM ##{}", table), &[]) .await? .into_row() .await? .unwrap(); for val in row { assert_eq!(&tiberius::ColumnData::F64(None), &val); assert_eq!(Option::::None, f64::from_sql(&val)?) } Ok(()) } #[test_on_runtimes] async fn short_strings(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let s = "Hallo"; let row = conn .query("SELECT @P1", &[&s]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(s), row.get::<&str, _>(0)); Ok(()) } #[test_on_runtimes] async fn long_strings(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let string = "a".repeat(4001); let row = conn .query("SELECT @P1", &[&string.as_str()]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(string.as_str()), row.get(0)); Ok(()) } #[test_on_runtimes] async fn stored_procedures(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let rows = conn .query( "EXECUTE sp_executesql N'SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3'", &[], ) .await? .into_first_result() .await?; assert_eq!(Some(1), rows[0].get(0)); assert_eq!(Some(2), rows[1].get(0)); assert_eq!(Some(3), rows[2].get(0)); Ok(()) } #[test_on_runtimes] async fn drop_stream_before_handling_all_results_should_not_cause_weird_things( mut conn: tiberius::Client, ) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { { let mut stream = conn .query( "SELECT @P1; SELECT @P2;", &[&"a".repeat(8000), &"b".repeat(8001)], ) .await?; while let Some(item) = stream.try_next().await? { if let QueryItem::Row(row) = item { assert_eq!(Some("a".repeat(8000).as_str()), row.get(0)); break; } } } { let row = conn .query("SELECT @P1", &[&1i32]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(1i32), row.get(0)); } Ok(()) } #[test_on_runtimes] async fn floats_in_nbc_rows(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.simple_query(format!( r#" create table ##{} ( id int identity(1,1), col1 float(53) null, col2 float(53) null, col3 float(53) null, col4 float(53) null, col5 float(53) null, col6 float(53) null, col7 float(53) null, col8 float(53) null ) "#, table )) .await?; conn.execute( format!( "INSERT INTO ##{} (col1, col2, col3, col4, col5, col6, col7, col8) VALUES (@P1, @P2, @P3, @P4, @P5, @P6, @P7, @P8)", table ), &[ &Option::::None, &Option::::None, &Option::::Some(1.23f64), &Option::::None, &Option::::None, &Option::::None, &Option::::None, &Option::::None ], ).await?; let mut stream = conn .query( &format!( "SELECT col1, col2, col3, col4, col5, col6, col7, col8 FROM ##{}", table ), &[], ) .await?; let expected_results = vec![None, None, Some(1.23f64), None, None, None, None]; let mut res: Vec> = Vec::new(); while let Some(item) = stream.try_next().await? { if let QueryItem::Row(row) = item { for i in 0..expected_results.len() { res.push(row.get(i)) } } } assert_eq!(expected_results, res); Ok(()) } #[test_on_runtimes] async fn tinyint_in_nbc_rows(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.simple_query(format!( r#" create table ##{} ( id int identity(1,1), col1 tinyint null, col2 tinyint null, col3 tinyint null, col4 tinyint null, col5 tinyint null, col6 tinyint null, col7 tinyint null, col8 tinyint null ) "#, table )) .await?; conn.execute( format!( "INSERT INTO ##{} (col1, col2, col3, col4, col5, col6, col7, col8) VALUES (@P1, @P2, @P3, @P4, @P5, @P6, @P7, @P8)", table ), &[ &Option::::None, &Option::::None, &Option::::Some(1u8), &Option::::None, &Option::::None, &Option::::None, &Option::::None, &Option::::None ], ).await?; let mut stream = conn .query( &format!( "SELECT col1, col2, col3, col4, col5, col6, col7, col8 FROM ##{}", table ), &[], ) .await?; let expected_results = vec![None, None, Some(1u8), None, None, None, None]; let mut res: Vec> = Vec::new(); while let Some(item) = stream.try_next().await? { if let QueryItem::Row(row) = item { for i in 0..expected_results.len() { res.push(row.get(i)) } } } assert_eq!(expected_results, res); Ok(()) } #[test_on_runtimes] async fn smallint_in_nbc_rows(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.simple_query(format!( r#" create table ##{} ( id int identity(1,1), col1 smallint null, col2 smallint null, col3 smallint null, col4 smallint null, col5 smallint null, col6 smallint null, col7 smallint null, col8 smallint null ) "#, table )) .await?; conn.execute( format!( "INSERT INTO ##{} (col1, col2, col3, col4, col5, col6, col7, col8) VALUES (@P1, @P2, @P3, @P4, @P5, @P6, @P7, @P8)", table ), &[ &Option::::None, &Option::::None, &Option::::Some(1i16), &Option::::None, &Option::::None, &Option::::None, &Option::::None, &Option::::None ], ).await?; let mut stream = conn .query( &format!( "SELECT col1, col2, col3, col4, col5, col6, col7, col8 FROM ##{}", table ), &[], ) .await?; let expected_results = vec![None, None, Some(1i16), None, None, None, None]; let mut res: Vec> = Vec::new(); while let Some(item) = stream.try_next().await? { if let QueryItem::Row(row) = item { for i in 0..expected_results.len() { res.push(row.get(i)) } } } assert_eq!(expected_results, res); Ok(()) } #[test_on_runtimes] async fn int_in_nbc_rows(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.simple_query(format!( r#" create table ##{} ( id int identity(1,1), col1 int null, col2 int null, col3 int null, col4 int null, col5 int null, col6 int null, col7 int null, col8 int null ) "#, table )) .await?; conn.execute( format!( "INSERT INTO ##{} (col1, col2, col3, col4, col5, col6, col7, col8) VALUES (@P1, @P2, @P3, @P4, @P5, @P6, @P7, @P8)", table ), &[ &Option::::None, &Option::::None, &Option::::Some(1i32), &Option::::None, &Option::::None, &Option::::None, &Option::::None, &Option::::None ], ).await?; let mut stream = conn .query( &format!( "SELECT col1, col2, col3, col4, col5, col6, col7, col8 FROM ##{}", table ), &[], ) .await?; let expected_results = vec![None, None, Some(1i32), None, None, None, None]; let mut res: Vec> = Vec::new(); while let Some(item) = stream.try_next().await? { if let QueryItem::Row(row) = item { for i in 0..expected_results.len() { res.push(row.get(i)) } } } assert_eq!(expected_results, res); Ok(()) } #[test_on_runtimes] async fn bigint_in_nbc_rows(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.simple_query(format!( r#" create table ##{} ( id int identity(1,1), col1 bigint null, col2 bigint null, col3 bigint null, col4 bigint null, col5 bigint null, col6 bigint null, col7 bigint null, col8 bigint null ) "#, table )) .await?; conn.execute( format!( "INSERT INTO ##{} (col1, col2, col3, col4, col5, col6, col7, col8) VALUES (@P1, @P2, @P3, @P4, @P5, @P6, @P7, @P8)", table ), &[ &Option::::None, &Option::::None, &Option::::Some(1i64), &Option::::None, &Option::::None, &Option::::None, &Option::::None, &Option::::None ], ).await?; let mut stream = conn .query( &format!( "SELECT col1, col2, col3, col4, col5, col6, col7, col8 FROM ##{}", table ), &[], ) .await?; let expected_results = vec![None, None, Some(1i64), None, None, None, None]; let mut res: Vec> = Vec::new(); while let Some(item) = stream.try_next().await? { if let QueryItem::Row(row) = item { for i in 0..expected_results.len() { res.push(row.get(i)) } } } assert_eq!(expected_results, res); Ok(()) } #[test_on_runtimes] async fn nbc_rows(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let mut stream = conn .query( "SELECT NULL, NULL, NULL, NULL, 1, NULL, NULL, NULL, 2, NULL, NULL, 3, NULL, 4", &[], ) .await?; let expected_results = vec![ None, None, None, None, Some(1), None, None, None, Some(2), None, None, Some(3), None, Some(4), ]; let mut res: Vec> = Vec::new(); while let Some(item) = stream.try_next().await? { if let QueryItem::Row(row) = item { for i in 0..expected_results.len() { res.push(row.get(i)) } } } assert_eq!(expected_results, res); Ok(()) } #[test_on_runtimes] async fn ntext_type(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; let string = r#"Hääpuhetta voi värittää kertomalla vitsejä, aforismeja, sananlaskuja, laulunsäkeitä ja muita lainauksia. Huumori sopii hääpuheeseen, mutta vitsit eivät saa loukata. Häissä ensimmäisen juhlapuheen pitää perinteisesti morsiamen isä."#; conn.execute(format!("CREATE TABLE ##{} (content NTEXT)", table), &[]) .await?; conn.execute( format!("INSERT INTO ##{} (content) VALUES (@P1)", table), &[&string], ) .await?; let row = conn .query(format!("SELECT content FROM ##{}", table), &[]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(string), row.get::<&str, _>(0)); Ok(()) } #[test_on_runtimes] async fn ntext_empty(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.execute(format!("CREATE TABLE ##{} (content NTEXT)", table), &[]) .await?; conn.execute( format!("INSERT INTO ##{} (content) VALUES (@P1)", table), &[&Option::::None], ) .await?; let row = conn .query(format!("SELECT content FROM ##{}", table), &[]) .await? .into_row() .await? .unwrap(); assert_eq!(None, row.get::<&str, _>(0)); Ok(()) } #[test_on_runtimes] async fn text_type(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; let string = "a".repeat(10000); conn.execute(format!("CREATE TABLE ##{} (content TEXT)", table), &[]) .await?; conn.execute( format!("INSERT INTO ##{} (content) VALUES (@P1)", table), &[&string.as_str()], ) .await?; let row = conn .query(format!("SELECT content FROM ##{}", table), &[]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(string.as_str()), row.get(0)); Ok(()) } #[test_on_runtimes] async fn varchar_empty(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.execute( format!("CREATE TABLE ##{} (content NVARCHAR(max))", table), &[], ) .await?; conn.execute( format!("INSERT INTO ##{} (content) VALUES (@P1)", table), &[&Option::::None], ) .await?; let row = conn .query(format!("SELECT content FROM ##{}", table), &[]) .await? .into_row() .await? .unwrap(); assert_eq!(None, row.get::<&str, _>(0)); Ok(()) } #[test_on_runtimes] async fn text_empty(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.execute(format!("CREATE TABLE ##{} (content TEXT)", table), &[]) .await?; conn.execute( format!("INSERT INTO ##{} (content) VALUES (@P1)", table), &[&Option::::None], ) .await?; let row = conn .query(format!("SELECT content FROM ##{}", table), &[]) .await? .into_row() .await? .unwrap(); assert_eq!(None, row.get::<&str, _>(0)); Ok(()) } #[test_on_runtimes] async fn varbinary_empty(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.execute( format!("CREATE TABLE ##{} (content VARBINARY(max))", table), &[], ) .await?; let total = conn .execute( format!("INSERT INTO ##{} (content) VALUES (@P1)", table), &[&Option::>::None], ) .await? .total(); assert_eq!(1, total); let row = conn .query(format!("SELECT content FROM ##{}", table), &[]) .await? .into_row() .await? .unwrap(); assert_eq!(None, row.get::<&[u8], _>(0)); Ok(()) } #[test_on_runtimes] async fn binary_type(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.execute( format!("CREATE TABLE ##{} (content BINARY(8000))", table), &[], ) .await?; let mut binary = vec![0; 7999]; binary.push(5); let inserted = conn .execute( format!("INSERT INTO ##{} (content) VALUES (@P1)", table), &[&binary.as_slice()], ) .await? .total(); assert_eq!(1, inserted); let row = conn .query(format!("SELECT content FROM ##{}", table), &[]) .await? .into_row() .await? .unwrap(); let result: &[u8] = row.get(0).unwrap(); assert_eq!(8000, result.len()); assert_eq!(binary.as_slice(), result); Ok(()) } #[test_on_runtimes] async fn varbinary_type(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.execute( format!("CREATE TABLE ##{} (content VARBINARY(8000))", table), &[], ) .await?; let mut binary = vec![0; 79]; binary.push(5); let inserted = conn .execute( format!("INSERT INTO ##{} (content) VALUES (@P1)", table), &[&binary.as_slice()], ) .await? .total(); assert_eq!(1, inserted); let row = conn .query(format!("SELECT content FROM ##{}", table), &[]) .await? .into_row() .await? .unwrap(); let result: &[u8] = row.get(0).unwrap(); assert_eq!(80, result.len()); assert_eq!(binary, result); Ok(()) } #[test_on_runtimes] async fn image_type(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.execute(format!("CREATE TABLE ##{} (content IMAGE)", table), &[]) .await?; let mut binary = vec![0; 79]; binary.push(5); let inserted = conn .execute( format!("INSERT INTO ##{} (content) VALUES (@P1)", table), &[&binary.as_slice()], ) .await? .total(); assert_eq!(1, inserted); let row = conn .query(format!("SELECT content FROM ##{}", table), &[]) .await? .into_row() .await? .unwrap(); let result: &[u8] = row.get(0).unwrap(); assert_eq!("content", row.columns()[0].name()); assert_eq!(80, result.len()); assert_eq!(binary, result); Ok(()) } #[test_on_runtimes] async fn guid_type_roundtrip(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let id = Uuid::new_v4(); let row = conn .query("SELECT @P1", &[&id]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(id), row.get(0)); Ok(()) } #[test_on_runtimes] async fn guid_type_byte_ordering(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let id = Uuid::parse_str("c97dbc01-fb45-4384-a194-e39a4560cf4a").unwrap(); let row = conn .simple_query("SELECT CAST('c97dbc01-fb45-4384-a194-e39a4560cf4a' AS UNIQUEIDENTIFIER)") .await? .into_row() .await? .unwrap(); assert_eq!(Some(id), row.get(0)); Ok(()) } #[test_on_runtimes] async fn varbinary_max(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.execute( format!("CREATE TABLE ##{} (content VARBINARY(max))", table), &[], ) .await?; let mut binary = vec![0; 8000]; binary.push(5); let inserted = conn .execute( format!("INSERT INTO ##{} (content) VALUES (@P1)", table), &[&binary.as_slice()], ) .await? .total(); assert_eq!(1, inserted); let row = conn .query(format!("SELECT content FROM ##{}", table), &[]) .await? .into_row() .await? .unwrap(); let result: &[u8] = row.get(0).unwrap(); assert_eq!(8001, result.len()); assert_eq!(binary, result); Ok(()) } #[test_on_runtimes] async fn numeric_type_u32_presentation(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let num = Numeric::new_with_scale(2, 1); let row = conn .query("SELECT @P1", &[&num]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(num), row.get(0)); Ok(()) } #[test_on_runtimes] async fn numeric_type_u64_presentation(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let num = Numeric::new_with_scale(std::i32::MAX as i128 + 10, 1); let row = conn .query("SELECT @P1", &[&num]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(num), row.get(0)); Ok(()) } #[test_on_runtimes] async fn numeric_type_u96_presentation(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let num = Numeric::new_with_scale(std::i64::MAX as i128, 19); let row = conn .query("SELECT @P1", &[&num]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(num), row.get(0)); Ok(()) } #[test_on_runtimes] async fn numeric_type_u128_presentation(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let num = Numeric::new_with_scale(std::i64::MAX as i128, 37); let row = conn .query("SELECT @P1", &[&num]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(num), row.get(0)); Ok(()) } #[cfg(feature = "rust_decimal")] #[cfg(test)] mod rust_decimal { use super::*; #[test_on_runtimes] async fn decimal_type_u32_presentation(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { use tiberius::numeric::Decimal; let num = Decimal::from_i128_with_scale(2, 1); let row = conn .query("SELECT @P1", &[&num]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(num), row.get(0)); Ok(()) } #[test_on_runtimes] async fn decimal_type_u64_presentation(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { use tiberius::numeric::Decimal; let num = Decimal::from_i128_with_scale(i32::MAX as i128 + 10, 1); let row = conn .query("SELECT @P1 AS foo", &[&num]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(num), row.get("foo")); Ok(()) } #[test_on_runtimes] async fn decimal_type_u96_presentation(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { use tiberius::numeric::Decimal; let num = Decimal::from_i128_with_scale(i64::MAX as i128, 19); let row = conn .query("SELECT @P1", &[&num]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(num), row.get(0)); Ok(()) } #[test_on_runtimes] async fn decimal_type_u128_presentation(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { use tiberius::numeric::Decimal; let num = Decimal::from_i128_with_scale(i64::MAX as i128, 28); let row = conn .query("SELECT @P1", &[&num]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(num), row.get(0)); Ok(()) } } #[cfg(feature = "bigdecimal")] #[cfg(test)] mod bigdecimal { use super::*; #[test_on_runtimes] async fn bigdecimal_type_u32_presentation(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { use std::str::FromStr; use tiberius::numeric::BigDecimal; let num = BigDecimal::from_str("2").unwrap(); let row = conn .query("SELECT @P1", &[&num]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(num), row.get(0)); Ok(()) } #[test_on_runtimes] async fn handles_scale_underflow_with_bigdecimal(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { use bigdecimal_::num_bigint::BigInt; use num_traits::FromPrimitive; use tiberius::numeric::BigDecimal; let int = BigInt::from_i128(90).unwrap(); let num = BigDecimal::new(int, -1); let row = conn .query("SELECT @P1", &[&num]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(num), row.get(0)); Ok(()) } #[test_on_runtimes] async fn bigdecimal_type_u64_presentation(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { use bigdecimal_::num_bigint::BigInt; use num_traits::FromPrimitive; use tiberius::numeric::BigDecimal; let int = BigInt::from_i128(i32::MAX as i128 + 10).unwrap(); let num = BigDecimal::new(int, 1); let row = conn .query("SELECT @P1 AS foo", &[&num]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(num), row.get("foo")); Ok(()) } #[test_on_runtimes] async fn bigdecimal_type_u96_presentation(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { use bigdecimal_::num_bigint::BigInt; use num_traits::FromPrimitive; use tiberius::numeric::BigDecimal; let int = BigInt::from_i128(i64::MAX as i128 + 10).unwrap(); let num = BigDecimal::new(int, 19); let row = conn .query("SELECT @P1", &[&num]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(num), row.get(0)); Ok(()) } #[test_on_runtimes] async fn bigdecimal_type_u128_presentation(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { use bigdecimal_::num_bigint::BigInt; use num_traits::FromPrimitive; use tiberius::numeric::BigDecimal; let int = BigInt::from_i128(i64::MAX as i128).unwrap(); let num = BigDecimal::new(int, 28); let row = conn .query("SELECT @P1", &[&num]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(num), row.get(0)); Ok(()) } #[test_on_runtimes] async fn dynamic_query_binding_bigdecimal(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { use bigdecimal_::num_bigint::BigInt; use num_traits::FromPrimitive; use tiberius::numeric::BigDecimal; let int = BigInt::from_i128(i64::MAX as i128).unwrap(); let num = BigDecimal::new(int, 28); let mut query = Query::new("SELECT @P1, @P2"); query.bind(num.clone()); query.bind(Option::::None); let row = query.query(&mut conn).await?.into_row().await?.unwrap(); assert_eq!(Some(num), row.get(0)); assert_eq!(Option::::None, row.get(1)); Ok(()) } } #[cfg(all(not(feature = "tds73"), feature = "chrono"))] #[test_on_runtimes] async fn naive_date_time_tds72(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { use chrono::NaiveDate; let dt = NaiveDate::from_ymd_opt(2020, 4, 20) .unwrap() .and_hms_opt(16, 20, 0) .unwrap(); let stream = conn.query("SELECT @P1", &[&dt]).await?; let row = stream.into_row().await?.unwrap(); assert_eq!(Some(dt), row.get(0)); Ok(()) } #[cfg(all(feature = "tds73", feature = "chrono"))] #[test_on_runtimes] async fn naive_small_date_time_tds73(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { use chrono::NaiveDate; let dt = NaiveDate::from_ymd_opt(2020, 4, 20) .unwrap() .and_hms_opt(16, 20, 0) .unwrap(); let table = random_table().await; conn.execute( format!("CREATE TABLE ##{} (date smalldatetime)", table), &[], ) .await?; conn.execute( format!("INSERT INTO ##{} (date) VALUES (@P1)", table), &[&dt], ) .await? .total(); let row = conn .query(format!("SELECT date FROM ##{}", table), &[]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(dt), row.get(0)); Ok(()) } #[cfg(all(feature = "tds73", feature = "chrono"))] #[test_on_runtimes] async fn naive_date_time2_tds73(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { use chrono::NaiveDate; let dt = NaiveDate::from_ymd_opt(2020, 4, 20) .unwrap() .and_hms_opt(16, 20, 0) .unwrap(); let table = random_table().await; conn.execute(format!("CREATE TABLE ##{} (date datetime2)", table), &[]) .await?; conn.execute( format!("INSERT INTO ##{} (date) VALUES (@P1)", table), &[&dt], ) .await? .total(); let row = conn .query(format!("SELECT date FROM ##{}", table), &[]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(dt), row.get(0)); Ok(()) } #[cfg(all(feature = "tds73", feature = "chrono"))] #[test_on_runtimes] async fn datetime_as_datetime2_tds73(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let dt = chrono::DateTime::parse_from_rfc3339("2020-02-27T19:10:00Z").unwrap(); let table = random_table().await; conn.execute(format!("CREATE TABLE ##{} (date datetime2)", table), &[]) .await?; conn.execute( format!("INSERT INTO ##{} (date) VALUES (@P1)", table), &[&dt], ) .await? .total(); let row = conn .query(format!("SELECT date FROM ##{}", table), &[]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(dt.naive_utc()), row.get(0)); Ok(()) } #[cfg(all(feature = "tds73", feature = "chrono"))] #[test_on_runtimes] async fn naive_time(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { use chrono::NaiveTime; let time = NaiveTime::from_hms_opt(16, 20, 0).unwrap(); let row = conn .query("SELECT @P1", &[&time]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(time), row.get(0)); let mut query = Query::new("SELECT @P1, @P2"); query.bind(time); query.bind(Option::::None); let stream = query.query(&mut conn).await?; let row = stream.into_row().await?.unwrap(); assert_eq!(Some(time), row.get(0)); assert_eq!(Option::::None, row.get(1)); Ok(()) } #[cfg(all(feature = "tds73", feature = "chrono"))] #[test_on_runtimes] async fn naive_date(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { use chrono::NaiveDate; let date = NaiveDate::from_ymd_opt(2020, 4, 20).unwrap(); let row = conn .query("SELECT @P1", &[&date]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(date), row.get(0)); let mut query = Query::new("SELECT @P1, @P2"); query.bind(date); query.bind(Option::::None); let stream = query.query(&mut conn).await?; let row = stream.into_row().await?.unwrap(); assert_eq!(Some(date), row.get(0)); assert_eq!(Option::::None, row.get(1)); Ok(()) } #[cfg(all(feature = "tds73", feature = "chrono"))] #[test_on_runtimes] async fn date_time_utc(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { use chrono::{offset::Utc, DateTime, NaiveDate}; let naive = NaiveDate::from_ymd_opt(2020, 4, 20) .unwrap() .and_hms_opt(16, 20, 0) .unwrap(); let dt: DateTime = DateTime::from_utc(naive, Utc); let row = conn .query("SELECT @P1", &[&dt]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(dt), row.get(0)); let mut query = Query::new("SELECT @P1, @P2"); query.bind(dt); query.bind(Option::>::None); let stream = query.query(&mut conn).await?; let row = stream.into_row().await?.unwrap(); assert_eq!(Some(dt), row.get(0)); assert_eq!(Option::>::None, row.get(1)); Ok(()) } #[cfg(all(feature = "tds73", feature = "chrono"))] #[test_on_runtimes] async fn date_time_fixed(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { use chrono::{offset::FixedOffset, DateTime, NaiveDate}; let naive = NaiveDate::from_ymd_opt(2020, 4, 20) .unwrap() .and_hms_opt(16, 20, 0) .unwrap(); let fixed = FixedOffset::east_opt(3600 * 3).unwrap(); let dt: DateTime = DateTime::from_utc(naive, fixed); let row = conn .query("SELECT @P1", &[&dt]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(dt), row.get(0)); let mut query = Query::new("SELECT @P1, @P2"); query.bind(dt); query.bind(Option::>::None); let stream = query.query(&mut conn).await?; let row = stream.into_row().await?.unwrap(); assert_eq!(Some(dt), row.get(0)); assert_eq!(Option::>::None, row.get(1)); Ok(()) } // time crate #[cfg(all(not(feature = "tds73"), feature = "time"))] #[test_on_runtimes] async fn primitive_date_time_tds72_time_crate(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let dt = time::Date::from_calendar_date(2020, time::Month::April, 20) .unwrap() .with_hms(16, 20, 0) .unwrap(); let stream = conn.query("SELECT @P1", &[&dt]).await?; let row = stream.into_row().await?.unwrap(); assert_eq!(Some(dt), row.get(0)); Ok(()) } #[cfg(all(feature = "tds73", feature = "time"))] #[test_on_runtimes] async fn primitive_small_date_time_tds73_time_crate(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let dt = time::Date::from_calendar_date(2020, time::Month::April, 20) .unwrap() .with_hms(16, 20, 0) .unwrap(); let table = random_table().await; conn.execute( format!("CREATE TABLE ##{} (date smalldatetime)", table), &[], ) .await?; conn.execute( format!("INSERT INTO ##{} (date) VALUES (@P1)", table), &[&dt], ) .await? .total(); let row = conn .query(format!("SELECT date FROM ##{}", table), &[]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(dt), row.get(0)); Ok(()) } #[cfg(all(feature = "tds73", feature = "time"))] #[test_on_runtimes] async fn primitive_date_time2_tds73_time_crate(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let dt = time::Date::from_calendar_date(2020, time::Month::April, 20) .unwrap() .with_hms(16, 20, 0) .unwrap(); let table = random_table().await; conn.execute(format!("CREATE TABLE ##{} (date datetime2)", table), &[]) .await?; conn.execute( format!("INSERT INTO ##{} (date) VALUES (@P1)", table), &[&dt], ) .await? .total(); let row = conn .query(format!("SELECT date FROM ##{}", table), &[]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(dt), row.get(0)); Ok(()) } #[cfg(all(feature = "tds73", feature = "time"))] #[test_on_runtimes] async fn time_with_time_crate(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let time = time::Time::from_hms(16, 20, 0).unwrap(); let row = conn .query("SELECT @P1", &[&time]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(time), row.get(0)); Ok(()) } #[cfg(all(feature = "tds73", feature = "time"))] #[test_on_runtimes] async fn date_with_time_crate(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let date = time::Date::from_calendar_date(2020, time::Month::April, 20).unwrap(); let row = conn .query("SELECT @P1", &[&date]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(date), row.get(0)); Ok(()) } #[cfg(all(feature = "tds73", feature = "time"))] #[test_on_runtimes] async fn offset_datetime_utc_with_time_crate(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let dt = time::Date::from_calendar_date(2020, time::Month::April, 20) .unwrap() .with_hms(16, 20, 00) .unwrap() .assume_offset(time::UtcOffset::UTC); let row = conn .query("SELECT @P1", &[&dt]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(dt), row.get(0)); Ok(()) } #[cfg(all(feature = "tds73", feature = "time"))] #[test_on_runtimes] async fn offset_date_time_fixed_with_time_crate(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let dt = time::Date::from_calendar_date(2020, time::Month::April, 20) .unwrap() .with_hms(16, 20, 00) .unwrap() .assume_offset(time::UtcOffset::from_hms(3, 0, 0).unwrap()); let row = conn .query("SELECT @P1", &[&dt]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(dt), row.get(0)); Ok(()) } #[test_on_runtimes] async fn xml_read_write(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let xml = XmlData::new(""); let row = conn .query("SELECT @P1", &[&xml]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(&xml), row.get(0)); Ok(()) } #[test_on_runtimes] async fn xml_read_null_xml(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let row = conn .query("SELECT CAST(NULL AS XML)", &[]) .await? .into_row() .await? .unwrap(); assert_eq!(None, row.get::<&XmlData, _>(0)); Ok(()) } #[test_on_runtimes] async fn money_smallmoney(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.execute( format!( "CREATE TABLE ##{} (m1 Money NOT NULL, m2 SmallMoney NOT NULL, m3 Money, m4 SmallMoney)", table ), &[], ) .await?; let res = conn .execute( format!( "INSERT INTO ##{} (m1, m2, m3, m4) VALUES (@P1, @P2, @P3, @P4)", table ), &[&1.23, &2.33, &4.56, &5.67], ) .await?; assert_eq!(1, res.total()); let row = conn .query(format!("SELECT m1, m2, m3, m4 FROM ##{}", table), &[]) .await? .into_row() .await? .unwrap(); assert_eq!(Some(1.23), row.get(0)); assert_eq!(Some(2.33), row.get(1)); assert_eq!(Some(4.56), row.get(2)); assert_eq!(Some(5.67), row.get(3)); Ok(()) } #[test_on_runtimes] async fn mars_sp_routines_must_fetch_all_results(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let q = r#" DECLARE @SQL NVARCHAR(MAX) = N'' SELECT @SQL += N'ALTER TABLE ' + QUOTENAME(OBJECT_SCHEMA_NAME(PARENT_OBJECT_ID)) + '.' + QUOTENAME(OBJECT_NAME(PARENT_OBJECT_ID)) + ' DROP CONSTRAINT ' + OBJECT_NAME(OBJECT_ID) + ';' FROM SYS.OBJECTS WHERE TYPE_DESC LIKE '%CONSTRAINT' AND TYPE_DESC <> 'FOREIGN_KEY_CONSTRAINT' AND OBJECT_NAME(PARENT_OBJECT_ID) = 'Post' AND SCHEMA_NAME(SCHEMA_ID) = 'making_an_existing_id_field_autoincrement_works_with_foreign_keys' EXEC sp_execute @SQL "#; let res = conn.simple_query(q).await; assert!(res.is_err()); Ok(()) } #[test_on_runtimes] async fn warnings_should_not_affect_column_fetch(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let proc = random_table().await; let q = format!( r#" CREATE PROCEDURE {} AS BEGIN SET NOCOUNT ON; DECLARE @tmp TABLE (col int); INSERT INTO @tmp SELECT SUM(col) AS col FROM (SELECT NULL AS col UNION SELECT 1) t; SELECT SUM(col) AS col FROM @tmp; END "#, proc ); conn.simple_query(&q).await?; let mut rs = conn.simple_query(format!("EXEC {}", proc)).await?; while let Some(item) = rs.try_next().await? { if let QueryItem::Metadata(meta) = item { let col = meta.columns().first(); assert_eq!(Some("col"), col.map(|c| c.name())); assert_eq!(Some(ColumnType::Int4), col.map(|c| c.column_type())); } } Ok(()) } #[test] #[cfg(feature = "sql-browser-async-std")] fn cyrillic_collations_should_work() -> Result<()> { LOGGER_SETUP.call_once(|| { env_logger::init(); }); async_std::task::block_on(async { let mut admin = { let config = tiberius::Config::from_ado_string(&CONN_STR)?; let tcp = async_std::net::TcpStream::connect(config.get_addr()).await?; tcp.set_nodelay(true)?; tiberius::Client::connect(config, tcp).await? }; admin .simple_query("CREATE DATABASE ru_test COLLATE Cyrillic_General_CI_AS") .await?; { let mut client = { let mut config = tiberius::Config::from_ado_string(&CONN_STR)?; config.database("ru_test"); let tcp = async_std::net::TcpStream::connect(config.get_addr()).await?; tcp.set_nodelay(true)?; tiberius::Client::connect(config, tcp).await? }; client .simple_query( "CREATE TABLE test (id INT IDENTITY PRIMARY KEY, single CHAR(1), multi VARCHAR(255), huge TEXT)", ) .await?; client.execute( "INSERT INTO test (single, multi, huge) VALUES (@P1, @P2, @P3)", &[&"Ж", &"В Советском Союзе попытки борьбы с пьянством предпринимались не единожды. Первая антиалкогольная", &"Первая антиалкогольная"] ).await?; let row = client .query("SELECT single, multi, huge FROM test", &[]) .await? .into_row() .await? .unwrap(); assert_eq!(Some("Ж"), row.get(0)); assert_eq!(Some("В Советском Союзе попытки борьбы с пьянством предпринимались не единожды. Первая антиалкогольная"), row.get(1)); assert_eq!(Some("Первая антиалкогольная"), row.get(2)); } admin.simple_query("DROP DATABASE ru_test").await?; Ok(()) }) } #[test] #[cfg(feature = "sql-browser-async-std")] fn application_name_should_be_set_correctly() -> Result<()> { LOGGER_SETUP.call_once(|| { env_logger::init(); }); async_std::task::block_on(async { let mut config = tiberius::Config::from_ado_string(&CONN_STR)?; config.application_name("meow"); let tcp = async_std::net::TcpStream::connect(config.get_addr()).await?; tcp.set_nodelay(true)?; let mut client = tiberius::Client::connect(config, tcp).await?; let row = client .query("SELECT APP_NAME()", &[]) .await? .into_row() .await? .unwrap(); assert_eq!(Some("meow"), row.get(0)); Ok(()) }) } #[test_on_runtimes] async fn columns_fetch_should_work(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let mut stream = conn .query("SELECT @P1 AS first; SELECT @P2 AS second", &[&1i32, &2i32]) .await?; // Nothing is fetched, the first result set starts. let cols = stream.columns().await?.unwrap(); assert_eq!("first", cols[0].name()); // Move over the metadata. stream.try_next().await?; // We're in the first row, seeing the metadata for that set. let cols = stream.columns().await?.unwrap(); assert_eq!("first", cols[0].name()); // Move over the only row in the first set. stream.try_next().await?; // End of the first set, getting the metadata by peaking the next item. let cols = stream.columns().await?.unwrap(); assert_eq!("second", cols[0].name()); Ok(()) } #[test_on_runtimes] async fn into_row_stream_should_work(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let stream = conn .query("SELECT @P1 AS first; SELECT @P2 AS second", &[&1i32, &2i32]) .await?; let mut stream = stream.into_row_stream(); let row = stream.try_next().await?.unwrap(); assert_eq!(Some(1), row.get(0)); let row = stream.try_next().await?.unwrap(); assert_eq!(Some(2), row.get(0)); Ok(()) } #[test_on_runtimes] async fn dynamic_query_binding_strings(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let data = String::from("foo"); let mut query = Query::new("SELECT @P1, @P2, @P3, @P4, @P5, @P6, @P7, @P8, @P9, @P10, @P11, @P12"); query.bind(&data); query.bind(Some(&data)); query.bind(Option::<&String>::None); query.bind(data.as_str()); query.bind(Some(data.as_str())); query.bind(Option::<&str>::None); query.bind("foo"); query.bind(Some("foo")); query.bind(Option::<&'static str>::None); query.bind(String::from("foo")); query.bind(Some(String::from("foo"))); query.bind(Option::::None); let mut stream = query.query(&mut conn).await?.into_row_stream(); let row = stream.try_next().await?.unwrap(); assert_eq!(Some("foo"), row.get(0)); assert_eq!(Some("foo"), row.get(1)); assert_eq!(Option::<&str>::None, row.get(2)); assert_eq!(Some("foo"), row.get(3)); assert_eq!(Some("foo"), row.get(4)); assert_eq!(Option::<&str>::None, row.get(5)); assert_eq!(Some("foo"), row.get(6)); assert_eq!(Some("foo"), row.get(7)); assert_eq!(Option::<&str>::None, row.get(8)); assert_eq!(Some("foo"), row.get(9)); assert_eq!(Some("foo"), row.get(10)); assert_eq!(Option::<&str>::None, row.get(11)); Ok(()) } #[test_on_runtimes] async fn dynamic_query_binding_bytes(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let data = vec![1u8, 6u8, 2u8, 0u8]; let mut query = Query::new("SELECT @P1, @P2, @P3, @P4, @P5, @P6"); query.bind(&data); query.bind(Option::<&Vec>::None); query.bind(data.as_slice()); query.bind(Option::<&[u8]>::None); query.bind(data.clone()); query.bind(Option::>::None); let mut stream = query.query(&mut conn).await?.into_row_stream(); let row = stream.try_next().await?.unwrap(); assert_eq!(Some(data.as_slice()), row.get(0)); assert_eq!(Option::<&[u8]>::None, row.get(1)); assert_eq!(Some(data.as_slice()), row.get(2)); assert_eq!(Option::<&[u8]>::None, row.get(3)); assert_eq!(Some(data.as_slice()), row.get(4)); assert_eq!(Option::<&[u8]>::None, row.get(5)); Ok(()) } #[test_on_runtimes] async fn dynamic_query_binding_xml(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let data = XmlData::new("lol"); let mut query = Query::new("SELECT @P1, @P2, @P3, @P4"); query.bind(&data); query.bind(data.clone()); query.bind(Option::<&XmlData>::None); query.bind(Option::::None); let mut stream = query.query(&mut conn).await?.into_row_stream(); let row = stream.try_next().await?.unwrap(); assert_eq!(Some(&data), row.get(0)); assert_eq!(Some(&data), row.get(1)); assert_eq!(Option::<&XmlData>::None, row.get(2)); assert_eq!(Option::<&XmlData>::None, row.get(3)); Ok(()) } #[test_on_runtimes] async fn dynamic_query_binding_guid(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let data = Uuid::new_v4(); let mut query = Query::new("SELECT @P1, @P2, @P3, @P4"); query.bind(data); query.bind(data); query.bind(Option::<&Uuid>::None); query.bind(Option::::None); let mut stream = query.query(&mut conn).await?.into_row_stream(); let row = stream.try_next().await?.unwrap(); assert_eq!(Some(data), row.get(0)); assert_eq!(Some(data), row.get(1)); assert_eq!(Option::::None, row.get(2)); assert_eq!(Option::::None, row.get(3)); Ok(()) } #[test_on_runtimes] async fn dynamic_query_binding_numbers(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let mut query = Query::new("SELECT @P1, @P2, @P3, @P4, @P5, @P6, @P7, @P8, @P9, @P10, @P11, @P12"); query.bind(1u8); query.bind(1i16); query.bind(1i32); query.bind(1i64); query.bind(std::f32::consts::PI); query.bind(std::f64::consts::PI); query.bind(Option::::None); query.bind(Option::::None); query.bind(Option::::None); query.bind(Option::::None); query.bind(Option::::None); query.bind(Option::::None); let mut stream = query.query(&mut conn).await?.into_row_stream(); let row = stream.try_next().await?.unwrap(); assert_eq!(Some(1u8), row.get(0)); assert_eq!(Some(1i16), row.get(1)); assert_eq!(Some(1i32), row.get(2)); assert_eq!(Some(1i64), row.get(3)); assert_eq!(Some(std::f32::consts::PI), row.get(4)); assert_eq!(Some(std::f64::consts::PI), row.get(5)); assert_eq!(Option::::None, row.get(6)); assert_eq!(Option::::None, row.get(7)); assert_eq!(Option::::None, row.get(8)); assert_eq!(Option::::None, row.get(9)); assert_eq!(Option::::None, row.get(10)); assert_eq!(Option::::None, row.get(11)); Ok(()) } #[test_on_runtimes] async fn dynamic_query_binding_boolean(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let mut query = Query::new("SELECT @P1, @P2"); query.bind(true); query.bind(Option::::None); let mut stream = query.query(&mut conn).await?.into_row_stream(); let row = stream.try_next().await?.unwrap(); assert_eq!(Some(true), row.get(0)); assert_eq!(Option::::None, row.get(1)); Ok(()) } #[test_on_runtimes] async fn new_execute_interface(mut conn: tiberius::Client) -> Result<()> where S: AsyncRead + AsyncWrite + Unpin + Send, { let table = random_table().await; conn.execute( format!("CREATE TABLE ##{} (m1 NVARCHAR(255) NOT NULL)", table), &[], ) .await?; let data = String::from("foo"); let mut query = Query::new(format!("INSERT INTO ##{} (m1) VALUES (@P1)", table)); query.bind(&data); let res = query.execute(&mut conn).await?; assert_eq!(1, res.total()); Ok(()) }