use odbc_api::{
BindParamDesc, Connection, DataType, InOrder, InputParameterMapping, IntoParameter, U16String,
buffers::{AnySliceMut, BufferDesc, Item, TextColumn},
parameter::WithDataType,
sys::{NULL_DATA, Numeric, Timestamp},
};
use stdext::function_name;
use test_case::test_case;
use widestring::Utf16String;
use crate::common::{Given, MARIADB, MSSQL, POSTGRES, Profile, SQLITE_3, cursor_to_string};
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
#[test_case(POSTGRES; "PostgreSQL")]
fn bulk_insert_long_strings(profile: &Profile) {
let table_name = table_name!();
let (conn, table) = Given::new(&table_name)
.column_types(&["VARCHAR(5000)"])
.build(profile)
.unwrap();
let text = "a".repeat(5000);
let result = conn.execute(&table.sql_insert(), &text.into_parameter(), None);
assert!(result.is_ok());
assert_eq!("a".repeat(5000), table.content_as_string(&conn));
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
#[test_case(POSTGRES; "PostgreSQL")]
fn bulk_insert_long_strings_as_wchar(profile: &Profile) {
let table_name = table_name!();
let (conn, table) = Given::new(&table_name)
.column_types(&["VARCHAR(5000)"])
.build(profile)
.unwrap();
let text = Utf16String::from_str(&"a".repeat(5000));
let mut inserter = conn
.prepare(&table.sql_insert())
.unwrap()
.into_column_inserter(1, [BindParamDesc::wide_text(5000)])
.unwrap();
inserter
.column_mut(0)
.as_w_text_view()
.unwrap()
.set_cell(0, Some(text.as_slice()));
inserter.set_num_rows(1);
inserter.execute().unwrap();
assert_eq!("a".repeat(5000), table.content_as_string(&conn));
}
#[test_case(MSSQL, "VARCHAR(MAX)"; "Microsoft SQL Server")]
#[test_case(MARIADB, "VARCHAR(8001)"; "Maria DB")]
#[test_case(SQLITE_3, "VARCHAR(8001)"; "SQLite 3")]
#[test_case(POSTGRES, "VARCHAR(8001)"; "PostgreSQL")]
fn long_strings_with_more_than_8000_bytes(profile: &Profile, column_type: &str) {
let table_name = table_name!();
let column_types = [column_type];
let (conn, table) = Given::new(&table_name)
.column_types(&column_types)
.build(profile)
.unwrap();
let text = "a".repeat(8001);
let mut inserter = conn
.prepare(&table.sql_insert())
.unwrap()
.into_column_inserter(1, [BindParamDesc::text(8001)])
.unwrap();
inserter
.column_mut(0)
.as_text_view()
.unwrap()
.set_cell(0, Some(text.as_bytes()));
inserter.set_num_rows(1);
inserter.execute().unwrap();
assert_eq!(text, table.content_as_string(&conn));
}
#[test_case(MSSQL; "Microsoft SQL Server")]
fn columnar_insert_timestamp(profile: &Profile) {
let table_name = table_name!();
let (conn, table) = Given::new(&table_name)
.column_types(&["DATETIME2"])
.build(profile)
.unwrap();
let desc = BindParamDesc::timestamp(true, 7);
let prepared = conn.prepare(&table.sql_insert()).unwrap();
let mut prebound = prepared.into_column_inserter(10, [desc]).unwrap();
let input = [
Some(Timestamp {
year: 2020,
month: 3,
day: 20,
hour: 16,
minute: 13,
second: 54,
fraction: 0,
}),
Some(Timestamp {
year: 2021,
month: 3,
day: 20,
hour: 16,
minute: 13,
second: 54,
fraction: 123456700,
}),
None,
];
prebound.set_num_rows(input.len());
let column = prebound.column_mut(0);
let mut writer = Timestamp::as_nullable_slice_mut(column).unwrap();
writer.write(input.iter().copied());
prebound.execute().unwrap();
let actual = table.content_as_string(&conn);
let expected = "2020-03-20 16:13:54.0000000\n2021-03-20 16:13:54.1234567\nNULL";
assert_eq!(expected, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
#[test_case(POSTGRES; "PostgreSQL")]
fn columnar_insert_int_raw(profile: &Profile) {
let table_name = table_name!();
let (conn, table) = Given::new(&table_name)
.column_types(&["INTEGER"])
.build(profile)
.unwrap();
let nullable = true;
let desc = BindParamDesc::i32(nullable);
let prepared = conn.prepare(&table.sql_insert()).unwrap();
let mut prebound = prepared.into_column_inserter(10, [desc]).unwrap();
let input_values = [1, 0, 3];
let mask = [true, false, true];
prebound.set_num_rows(input_values.len());
let mut writer = prebound.column_mut(0).as_nullable_slice::<i32>().unwrap();
let (values, indicators) = writer.raw_values();
values[..input_values.len()].copy_from_slice(&input_values);
indicators
.iter_mut()
.zip(mask.iter())
.for_each(|(indicator, &mask)| *indicator = if mask { 0 } else { NULL_DATA });
prebound.execute().unwrap();
let actual = table.content_as_string(&conn);
let expected = "1\nNULL\n3";
assert_eq!(expected, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(SQLITE_3; "SQLite 3")]
fn columnar_insert_timestamp_ms(profile: &Profile) {
let table_name = table_name!();
let (conn, _table) = Given::new(&table_name)
.column_types(&["DATETIME2(3)"])
.build(profile)
.unwrap();
let prepared = conn
.prepare(&format!("INSERT INTO {table_name} (a) VALUES (?)"))
.unwrap();
let desc = BindParamDesc::timestamp(true, 3);
let mut prebound = prepared.into_column_inserter(10, [desc]).unwrap();
let input = [
Some(Timestamp {
year: 2020,
month: 3,
day: 20,
hour: 16,
minute: 13,
second: 54,
fraction: 0,
}),
Some(Timestamp {
year: 2021,
month: 3,
day: 20,
hour: 16,
minute: 13,
second: 54,
fraction: 123000000,
}),
None,
];
prebound.set_num_rows(input.len());
let mut writer = prebound.column_mut(0).as_nullable_slice().unwrap();
writer.write(input.iter().copied());
prebound.execute().unwrap();
let cursor = conn
.execute(&format!("SELECT a FROM {table_name} ORDER BY Id"), (), None)
.unwrap()
.unwrap();
let actual = cursor_to_string(cursor);
let expected = "2020-03-20 16:13:54.000\n2021-03-20 16:13:54.123\nNULL";
assert_eq!(expected, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
fn columnar_insert_varbinary(profile: &Profile) {
let table_name = table_name!();
let (conn, table) = Given::new(&table_name)
.column_types(&["VARBINARY(13)"])
.build(profile)
.unwrap();
let prepared = conn.prepare(&table.sql_insert()).unwrap();
let desc = BindParamDesc::binary(5);
let mut prebound = prepared.into_column_inserter(4, [desc]).unwrap();
let input = [
Some(&b"Hello"[..]),
Some(&b"World"[..]),
None,
Some(&b"Hello, World!"[..]),
];
prebound.set_num_rows(input.len());
let mut writer = prebound.column_mut(0).as_bin_view().unwrap();
writer.ensure_max_element_length(13, 0).unwrap();
writer.set_cell(0, Some("Hello".as_bytes()));
writer.set_cell(1, Some("World".as_bytes()));
writer.set_cell(2, None);
writer.set_cell(3, Some("Hello, World!".as_bytes()));
prebound.execute().unwrap();
let cursor = conn
.execute(&table.sql_all_ordered_by_id(), (), None)
.unwrap()
.unwrap();
let actual = cursor_to_string(cursor);
let expected = "48656C6C6F\n576F726C64\nNULL\n48656C6C6F2C20576F726C6421";
assert_eq!(expected, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
#[test_case(POSTGRES; "PostgreSQL")]
fn columnar_insert_varchar(profile: &Profile) {
let table_name = table_name!();
let (conn, _table) = Given::new(&table_name)
.column_types(&["VARCHAR(13)"])
.build(profile)
.unwrap();
let prepared = conn
.prepare(&format!("INSERT INTO {table_name} (a) VALUES (?)"))
.unwrap();
let max_str_len = 5;
let desc = BindParamDesc::text(max_str_len);
let mut prebound = prepared.into_column_inserter(4, [desc]).unwrap();
let input = [
Some(&b"Hello"[..]),
Some(&b"World"[..]),
None,
Some(&b"Hello, World!"[..]),
];
prebound.set_num_rows(input.len());
let mut col_view = prebound.column_mut(0).as_text_view().unwrap();
col_view.ensure_max_element_length(13, 0).unwrap();
col_view.set_cell(0, Some("Hello".as_bytes()));
col_view.set_cell(1, Some("World".as_bytes()));
col_view.set_cell(2, None);
col_view.set_cell(3, Some("Hello, World!".as_bytes()));
prebound.execute().unwrap();
let cursor = conn
.execute(&format!("SELECT a FROM {table_name} ORDER BY Id"), (), None)
.unwrap()
.unwrap();
let actual = cursor_to_string(cursor);
let expected = "Hello\nWorld\nNULL\nHello, World!";
assert_eq!(expected, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
#[test_case(POSTGRES; "PostgreSQL")]
fn columnar_insert_text_as_sql_integer(profile: &Profile) {
let table_name = table_name!();
let (conn, table) = Given::new(&table_name)
.column_types(&["INTEGER"])
.build(profile)
.unwrap();
let prepared = conn.prepare(&table.sql_insert()).unwrap();
let parameter_buffers = vec![WithDataType::new(
TextColumn::try_new(4, 5).unwrap(),
DataType::Integer,
)];
let index_mapping = InOrder::new(parameter_buffers.len());
let mut prebound = unsafe {
prepared.unchecked_bind_columnar_array_parameters(parameter_buffers, index_mapping)
}
.unwrap();
prebound.set_num_rows(4);
let mut writer = prebound.column_mut(0);
writer.set_cell(0, Some("1".as_bytes()));
writer.set_cell(1, Some("2".as_bytes()));
writer.set_cell(2, None);
writer.set_cell(3, Some("4".as_bytes()));
prebound.execute().unwrap();
let actual = table.content_as_string(&conn);
let expected = "1\n2\nNULL\n4";
assert_eq!(expected, actual);
}
#[test_case(MARIADB; "Maria DB")]
#[test_case(POSTGRES; "PostgreSQL")]
fn columnar_insert_numeric_using_numeric_buffer(profile: &Profile) {
let table_name = table_name!();
let (conn, table) = Given::new(&table_name)
.column_types(&["DECIMAL(5,3)"])
.build(profile)
.unwrap();
let stmt = conn.prepare(&table.sql_insert()).unwrap();
let desc = BindParamDesc {
buffer_desc: BufferDesc::Numeric,
data_type: DataType::Numeric {
precision: 5,
scale: 3,
},
};
let mut inserter = stmt.into_column_inserter(3, [desc]).unwrap();
let AnySliceMut::Numeric(slice) = inserter.column_mut(0) else {
panic!("Expected numeric column");
};
slice[0] = Numeric {
precision: 5,
scale: 3,
sign: 1,
val: 12345u128.to_le_bytes(),
};
slice[1] = Numeric {
precision: 5,
scale: 3,
sign: 1,
val: 23456u128.to_le_bytes(),
};
slice[2] = Numeric {
precision: 5,
scale: 3,
sign: 1,
val: 34567u128.to_le_bytes(),
};
inserter.set_num_rows(3);
inserter.execute().unwrap();
let content = table.content_as_string(&conn);
assert_eq!("12.345\n23.456\n34.567", content);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
#[test_case(POSTGRES; "PostgreSQL")]
fn columnar_insert_text_as_decimal(profile: &Profile) {
let table_name = table_name!();
let (conn, table) = Given::new(&table_name)
.column_types(&["DECIMAL(5,3)"])
.build(profile)
.unwrap();
let prepared = conn.prepare(&table.sql_insert()).unwrap();
let descriptions = [BindParamDesc::decimal_as_text(5, 3)];
let index_mapping = InOrder::new(descriptions.len());
let mut inserter = prepared
.into_column_inserter_with_mapping(4, descriptions, index_mapping)
.unwrap();
inserter.set_num_rows(4);
let mut col_view = inserter.column_mut(0).as_text_view().unwrap();
col_view.set_cell(0, Some(b"12.345"));
col_view.set_cell(1, Some(b"23.456"));
col_view.set_cell(2, None);
col_view.set_cell(3, Some(b"34.567"));
inserter.execute().unwrap();
let content = table.content_as_string(&conn);
assert_eq!("12.345\n23.456\nNULL\n34.567", content);
}
#[test_case(MSSQL, "TIME(3)"; "Microsoft SQL Server")]
#[test_case(SQLITE_3, "TIME(3)"; "SQLite 3")]
#[test_case(POSTGRES, "TIME(3)"; "PostgreSQL")]
fn columnar_insert_text_as_time_ms(profile: &Profile, time_ms: &str) {
let table_name = table_name!();
let types = [time_ms];
let (conn, table) = Given::new(&table_name)
.column_types(&types)
.build(profile)
.unwrap();
let prepared = conn.prepare(&table.sql_insert()).unwrap();
let descriptions = [BindParamDesc::time_as_text(3)];
let index_mapping = InOrder::new(descriptions.len());
let mut inserter = prepared
.into_column_inserter_with_mapping(2, descriptions, index_mapping)
.unwrap();
inserter.set_num_rows(2);
let mut col_view = inserter.column_mut(0).as_text_view().unwrap();
col_view.set_cell(0, Some(b"09:18:53.123"));
col_view.set_cell(1, None);
inserter.execute().unwrap();
let content = table.content_as_string(&conn);
assert_eq!("09:18:53.123\nNULL", content);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
#[test_case(POSTGRES; "PostgreSQL")]
fn adaptive_columnar_insert_varchar(profile: &Profile) {
let table_name = table_name!();
let (conn, _table) = Given::new(&table_name)
.column_types(&["VARCHAR(13)"])
.build(profile)
.unwrap();
let max_str_len = 1;
let desc = BindParamDesc::text(max_str_len);
let prepared = conn
.prepare(&format!("INSERT INTO {table_name} (a) VALUES (?)"))
.unwrap();
let input = [
Some(&b"Hi"[..]),
Some(&b"Hello"[..]),
Some(&b"World"[..]),
None,
Some(&b"Hello, World!"[..]),
];
let mut prebound = prepared.into_column_inserter(input.len(), [desc]).unwrap();
prebound.set_num_rows(input.len());
let mut col_view = prebound.column_mut(0).as_text_view().unwrap();
for (index, &text) in input.iter().enumerate() {
col_view
.ensure_max_element_length(input[index].map(|s| s.len()).unwrap_or(0), index)
.unwrap();
col_view.set_cell(index, text)
}
prebound.execute().unwrap();
let cursor = conn
.execute(&format!("SELECT a FROM {table_name} ORDER BY Id"), (), None)
.unwrap()
.unwrap();
let actual = cursor_to_string(cursor);
let expected = "Hi\nHello\nWorld\nNULL\nHello, World!";
assert_eq!(expected, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
fn adaptive_columnar_insert_varbin(profile: &Profile) {
let table_name = table_name!();
let (conn, _table) = Given::new(&table_name)
.column_types(&["VARBINARY(13)"])
.build(profile)
.unwrap();
let max_bytes = 1;
let desc = BindParamDesc::binary(max_bytes);
let input = [
Some(&b"Hi"[..]),
Some(&b"Hello"[..]),
Some(&b"World"[..]),
None,
Some(&b"Hello, World!"[..]),
];
let prepared = conn
.prepare(&format!("INSERT INTO {table_name} (a) VALUES (?)"))
.unwrap();
let mut prebound = prepared.into_column_inserter(input.len(), [desc]).unwrap();
prebound.set_num_rows(input.len());
let mut writer = prebound.column_mut(0).as_bin_view().unwrap();
for (row_index, &bytes) in input.iter().enumerate() {
writer
.ensure_max_element_length(bytes.map(|b| b.len()).unwrap_or(0), row_index)
.unwrap();
writer.set_cell(row_index, bytes)
}
prebound.execute().unwrap();
let cursor = conn
.execute(&format!("SELECT a FROM {table_name} ORDER BY Id"), (), None)
.unwrap()
.unwrap();
let actual = cursor_to_string(cursor);
let expected = "4869\n48656C6C6F\n576F726C64\nNULL\n48656C6C6F2C20576F726C6421";
assert_eq!(expected, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
#[test_case(POSTGRES; "PostgreSQL")]
fn with_varying_buffer_sizes(profile: &Profile) {
let table_name = table_name!();
let (conn, table) = Given::new(&table_name)
.column_types(&["INTEGER"])
.build(profile)
.unwrap();
let prepared = conn.prepare(&table.sql_insert()).unwrap();
let mut inserter = prepared
.into_column_inserter(1, [BindParamDesc::i32(false)])
.unwrap();
inserter.set_num_rows(1);
inserter.column_mut(0).as_slice::<i32>().unwrap()[0] = 1;
inserter.execute().unwrap();
let mapping = InOrder::new(1);
let mut inserter = inserter.resize(2, mapping).unwrap();
inserter.set_num_rows(2);
inserter.column_mut(0).as_slice::<i32>().unwrap()[0] = 2;
inserter.column_mut(0).as_slice::<i32>().unwrap()[1] = 3;
inserter.execute().unwrap();
let actual = table.content_as_string(&conn);
let expected = "1\n2\n3";
assert_eq!(expected, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn columnar_insert_wide_varchar(profile: &Profile) {
let table_name = table_name!();
let (conn, _table) = Given::new(&table_name)
.column_types(&["NVARCHAR(13)"])
.build(profile)
.unwrap();
let prepared = conn
.prepare(&format!("INSERT INTO {table_name} (a) VALUES (?)"))
.unwrap();
let input = [
Some(U16String::from_str("Hello")),
Some(U16String::from_str("World")),
None,
Some(U16String::from_str("Hello, World!")),
];
let max_str_len = 20;
let desc = BindParamDesc::wide_text(max_str_len);
let mut prebound = prepared.into_column_inserter(input.len(), [desc]).unwrap();
prebound.set_num_rows(input.len());
let mut writer = prebound.column_mut(0).as_w_text_view().unwrap();
for (row_index, value) in input
.iter()
.map(|opt| opt.as_ref().map(|ustring| ustring.as_slice()))
.enumerate()
{
writer.set_cell(row_index, value)
}
prebound.execute().unwrap();
let cursor = conn
.execute(&format!("SELECT a FROM {table_name} ORDER BY Id"), (), None)
.unwrap()
.unwrap();
let actual = cursor_to_string(cursor);
let expected = "Hello\nWorld\nNULL\nHello, World!";
assert_eq!(expected, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
#[test_case(POSTGRES; "PostgreSQL")]
fn bulk_insert_with_text_buffer(profile: &Profile) {
let table_name = table_name!();
let (conn, table) = Given::new(&table_name)
.column_types(&["VARCHAR(50)"])
.build(profile)
.unwrap();
let insert_sql = table.sql_insert();
let prepared = conn.prepare(&insert_sql).unwrap();
let mut prebound = prepared
.into_text_inserter(5, [50].iter().copied())
.unwrap();
prebound
.append(["England"].iter().map(|s| Some(s.as_bytes())))
.unwrap();
prebound
.append(["France"].iter().map(|s| Some(s.as_bytes())))
.unwrap();
prebound
.append(["Germany"].iter().map(|s| Some(s.as_bytes())))
.unwrap();
prebound.execute().unwrap();
let expected = "England\nFrance\nGermany";
let actual = table.content_as_string(&conn);
assert_eq!(expected, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
#[test_case(POSTGRES; "PostgreSQL")]
fn bulk_insert_with_columnar_buffer(profile: &Profile) {
let table_name = table_name!();
let (conn, table) = Given::new(&table_name)
.column_types(&["VARCHAR(50)", "INTEGER"])
.build(profile)
.unwrap();
let prepared = conn.prepare(&table.sql_insert()).unwrap();
let description = [BindParamDesc::text(50), BindParamDesc::i32(true)];
let mut prebound = prepared.into_column_inserter(5, description).unwrap();
prebound.set_num_rows(3);
let mut col_view = prebound.column_mut(0).as_text_view().unwrap();
col_view.set_cell(0, Some("England".as_bytes()));
col_view.set_cell(1, Some("France".as_bytes()));
col_view.set_cell(2, Some("Germany".as_bytes()));
let input = [1, 2, 3];
let mut col = prebound.column_mut(1).as_nullable_slice::<i32>().unwrap();
col.write(input.iter().map(|&i| Some(i)));
prebound.execute().unwrap();
let expected = "England,1\nFrance,2\nGermany,3";
let actual = table.content_as_string(&conn);
assert_eq!(expected, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
#[test_case(POSTGRES; "PostgreSQL")]
fn bulk_insert_two_columns_from_one_buffer(profile: &Profile) {
let table_name = table_name!();
let (conn, table) = Given::new(&table_name)
.column_types(&["INTEGER", "INTEGER"])
.build(profile)
.unwrap();
let prepared = conn.prepare(&table.sql_insert()).unwrap();
let description = [BindParamDesc {
buffer_desc: BufferDesc::I32 { nullable: false },
data_type: DataType::Integer,
}];
struct MyMapping;
impl InputParameterMapping for MyMapping {
fn parameter_index_to_column_index(&self, _paramteter_index: u16) -> usize {
0
}
fn num_parameters(&self) -> usize {
2
}
}
let mut prebound = prepared
.into_column_inserter_with_mapping(5, description, MyMapping)
.unwrap();
prebound.set_num_rows(3);
let col_view = prebound.column_mut(0).as_slice().unwrap();
col_view[0] = 42;
col_view[1] = 5;
col_view[2] = 7;
prebound.execute().unwrap();
let expected = "42,42\n5,5\n7,7";
let actual = table.content_as_string(&conn);
assert_eq!(expected, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
#[test_case(POSTGRES; "PostgreSQL")]
fn bulk_insert_with_multiple_batches(profile: &Profile) {
let table_name = table_name!();
let (conn, table) = Given::new(&table_name)
.column_types(&["VARCHAR(50)", "INTEGER"])
.build(profile)
.unwrap();
let prepared = conn.prepare(&table.sql_insert()).unwrap();
let description = [BindParamDesc::text(50), BindParamDesc::i32(true)];
let mut prebound = prepared.into_column_inserter(5, description).unwrap();
prebound.set_num_rows(3);
let mut col_view = prebound.column_mut(0).as_text_view().unwrap();
col_view.set_cell(0, Some("England".as_bytes()));
col_view.set_cell(1, Some("France".as_bytes()));
col_view.set_cell(2, Some("Germany".as_bytes()));
let input = [1, 2, 3];
let mut col = prebound.column_mut(1).as_nullable_slice::<i32>().unwrap();
col.write(input.iter().map(|&i| Some(i)));
prebound.execute().unwrap();
prebound.set_num_rows(1);
let mut col_view = prebound.column_mut(0).as_text_view().unwrap();
col_view.set_cell(0, Some("Spain".as_bytes()));
let input = [4];
let mut col = prebound.column_mut(1).as_nullable_slice::<i32>().unwrap();
col.write(input.iter().map(|&i| Some(i)));
prebound.execute().unwrap();
let expected = "England,1\nFrance,2\nGermany,3\nSpain,4";
let actual = table.content_as_string(&conn);
assert_eq!(expected, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
#[test_case(POSTGRES; "PostgreSQL")]
fn insert_i64_in_bulk(profile: &Profile) -> Result<(), odbc_api::Error> {
let table_name = table_name!();
let (conn, table) = profile.create_table(&table_name, &["BIGINT"], &["a"])?;
let prepared = conn.prepare(&table.sql_insert())?;
let mut inserter = prepared.into_column_inserter(2, [BindParamDesc::i64(true)])?;
inserter.set_num_rows(2);
let mut view = inserter.column_mut(0).as_nullable_slice().unwrap();
view.set_cell(0, Some(1i64));
view.set_cell(1, Some(2));
inserter.execute()?;
let actual = table.content_as_string(&conn);
assert_eq!("1\n2", actual);
Ok(())
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
#[test_case(POSTGRES; "PostgreSQL")]
fn grow_batch_size_during_bulk_insert(profile: &Profile) {
let table_name = table_name!();
let conn = profile
.setup_empty_table(&table_name, &["INTEGER"])
.unwrap();
let mut prepared = conn
.prepare(&format!("INSERT INTO {table_name} (a) VALUES (?)"))
.unwrap();
let desc = BindParamDesc::i32(false);
let mut prebound = prepared.column_inserter(1, [desc]).unwrap();
prebound.set_num_rows(1);
let col = prebound.column_mut(0).as_slice::<i32>().unwrap();
col[0] = 1;
prebound.execute().unwrap();
let mut prebound = prepared.column_inserter(2, [desc]).unwrap();
prebound.set_num_rows(2);
let col = prebound.column_mut(0).as_slice::<i32>().unwrap();
col[0] = 2;
col[1] = 3;
prebound.execute().unwrap();
let cursor = conn
.execute(&format!("SELECT a FROM {table_name} ORDER BY id"), (), None)
.unwrap()
.unwrap();
let actual = cursor_to_string(cursor);
assert_eq!("1\n2\n3", actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
#[test_case(POSTGRES; "PostgreSQL")]
fn bulk_inserter_owning_connection(profile: &Profile) {
let table_name = table_name!();
let conn = profile
.setup_empty_table(&table_name, &["INTEGER"])
.unwrap();
let mut prepared = conn
.into_prepared(&format!("INSERT INTO {table_name} (a) VALUES (?)"))
.unwrap();
let desc = BindParamDesc::i32(false);
let mut prebound = prepared.column_inserter(1, [desc]).unwrap();
prebound.set_num_rows(1);
let col = prebound.column_mut(0).as_slice::<i32>().unwrap();
col[0] = 1;
prebound.execute().unwrap();
let conn = profile.connection().unwrap();
let cursor = conn
.execute(&format!("SELECT a FROM {table_name} ORDER BY id"), (), None)
.unwrap()
.unwrap();
let actual = cursor_to_string(cursor);
assert_eq!("1", actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
#[test_case(POSTGRES; "PostgreSQL")]
fn insert_vec_column_using_generic_code(profile: &Profile) {
let table_name = table_name!();
let (conn, table) = Given::new(&table_name)
.column_types(&["INTEGER", "BIGINT", "FLOAT(53)"])
.build(profile)
.unwrap();
let insert_sql = table.sql_insert();
fn insert_tuple_vec<A: Item, B: Item, C: Item>(
conn: &Connection<'_>,
insert_sql: &str,
source: &[(A, B, C)],
) {
let mut prepared = conn.prepare(insert_sql).unwrap();
let capacity = source.len();
let descriptions = [
A::bind_param_desc(false),
B::bind_param_desc(false),
C::bind_param_desc(false),
];
let mut inserter = prepared.column_inserter(capacity, descriptions).unwrap();
inserter.set_num_rows(source.len());
for (index, (a, b, c)) in source.iter().enumerate() {
inserter.column_mut(0).as_slice::<A>().unwrap()[index] = *a;
inserter.column_mut(1).as_slice::<B>().unwrap()[index] = *b;
inserter.column_mut(2).as_slice::<C>().unwrap()[index] = *c;
}
inserter.execute().unwrap();
}
insert_tuple_vec(
&conn,
&insert_sql,
&[(1i32, 1i64, 0.5f64), (2, 2, 0.25), (3, 3, 0.125)],
);
let actual = table.content_as_string(&conn);
assert_eq!("1,1,0.5\n2,2,0.25\n3,3,0.125", actual);
}