mod common;
use odbc_sys::{SqlDataType, Timestamp};
use sys::NULL_DATA;
use tempfile::NamedTempFile;
use test_case::test_case;
use common::{
cursor_to_string, setup_empty_table, table_to_string, Profile, SingleColumnRowSetBuffer, ENV,
};
use odbc_api::{
buffers::{
AnyColumnViewMut, BufferDescription, BufferKind, ColumnarAnyBuffer, ColumnarBuffer,
Indicator, Item, TextColumn, TextRowSet,
},
handles::{OutputStringBuffer, Statement},
parameter::InputParameter,
parameter::{
Blob, BlobRead, BlobSlice, VarBinaryArray, VarCharArray, VarCharSlice, WithDataType,
},
sys, Bit, ColumnDescription, Cursor, DataType, Error, InOut, IntoParameter, Nullability,
Nullable, Out, ResultSetMetadata, U16Str, U16String,
};
use std::{
ffi::CString,
io::{self, Write},
iter, str, thread,
};
const MSSQL_CONNECTION: &str =
"Driver={ODBC Driver 17 for SQL Server};Server=localhost;UID=SA;PWD=My@Test@Password1;";
const MSSQL: &Profile = &Profile {
connection_string: MSSQL_CONNECTION,
index_type: "int IDENTITY(1,1)",
blob_type: "Image",
};
#[cfg(target_os = "windows")]
const SQLITE_3_CONNECTION: &str = "Driver={SQLite3 ODBC Driver};Database=sqlite-test.db";
#[cfg(not(target_os = "windows"))]
const SQLITE_3_CONNECTION: &str = "Driver={SQLite3};Database=sqlite-test.db";
const SQLITE_3: &Profile = &Profile {
connection_string: SQLITE_3_CONNECTION,
index_type: "int IDENTITY(1,1)",
blob_type: "BLOB",
};
#[cfg(target_os = "windows")]
const MARIADB_CONNECTION: &str = "Driver={MariaDB ODBC 3.1 Driver};\
Server=localhost;DB=test_db;\
UID=root;PWD=my-secret-pw;\
Port=3306";
#[cfg(not(target_os = "windows"))]
const MARIADB_CONNECTION: &str = "Driver={MariaDB 3.1 Driver};\
Server=127.0.0.1;DB=test_db;\
UID=root;PWD=my-secret-pw;\
Port=3306";
const MARIADB: &Profile = &Profile {
connection_string: MARIADB_CONNECTION,
index_type: "INTEGER AUTO_INCREMENT PRIMARY KEY",
blob_type: "BLOB",
};
#[test]
#[should_panic]
fn insert_too_large_element_in_bin_column() {
let desc = BufferDescription {
kind: BufferKind::Binary { length: 1 },
nullable: true,
};
let mut buffer = ColumnarAnyBuffer::try_from_description(10, iter::once(desc)).unwrap();
buffer.set_num_rows(1);
if let AnyColumnViewMut::Binary(mut col) = buffer.column_mut(0) {
col.write(iter::once(Some(&b"too large input."[..])))
}
}
#[test]
#[should_panic]
fn insert_too_large_element_in_text_column() {
let desc = BufferDescription {
kind: BufferKind::Text { max_str_len: 1 },
nullable: true,
};
let mut buffer = ColumnarAnyBuffer::try_from_description(10, iter::once(desc)).unwrap();
buffer.set_num_rows(1);
let mut col_view = buffer.column_mut(0).as_text_view().unwrap();
col_view.write(iter::once(Some(&b"too large input."[..])))
}
#[test]
fn bogus_connection_string() {
let result = ENV.connect_with_connection_string("foobar");
assert!(result.is_err());
let error = result.err().unwrap();
if let Error::Diagnostics { record, function } = error {
assert_eq!("SQLDriverConnect", function);
assert!(!record.message.contains(&0));
} else {
panic!("Expected Error::Diagnostics")
};
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn connect_to_db(profile: &Profile) {
let conn = profile.connection().unwrap();
assert!(!conn.is_dead().unwrap())
}
#[test]
fn describe_columns() {
let conn = MSSQL.connection().unwrap();
setup_empty_table(
&conn,
MSSQL.index_type,
"DescribeColumns",
&[
"VARCHAR(255) NOT NULL",
"INTEGER",
"BINARY(12)",
"VARBINARY(100)",
"NCHAR(10)",
"NUMERIC(3,2)",
"DATETIME2",
"TIME",
"text",
"Image",
"DOUBLE PRECISION",
],
)
.unwrap();
let sql = "SELECT a,b,c,d,e,f,g,h,i,j,k FROM DescribeColumns ORDER BY Id;";
let cursor = conn.execute(sql, ()).unwrap().unwrap();
assert_eq!(cursor.num_result_cols().unwrap(), 11);
let mut actual = ColumnDescription::default();
let kind = DataType::Varchar { length: 255 };
let expected = ColumnDescription::new("a", kind, Nullability::NoNulls);
cursor.describe_col(1, &mut actual).unwrap();
assert_eq!(expected, actual);
assert_eq!(kind, cursor.col_data_type(1).unwrap());
let kind = DataType::Integer;
let expected = ColumnDescription::new("b", kind, Nullability::Nullable);
cursor.describe_col(2, &mut actual).unwrap();
assert_eq!(expected, actual);
assert_eq!(kind, cursor.col_data_type(2).unwrap());
let kind = DataType::Binary { length: 12 };
let expected = ColumnDescription::new("c", kind, Nullability::Nullable);
cursor.describe_col(3, &mut actual).unwrap();
assert_eq!(expected, actual);
assert_eq!(kind, cursor.col_data_type(3).unwrap());
let kind = DataType::Varbinary { length: 100 };
let expected = ColumnDescription::new("d", kind, Nullability::Nullable);
cursor.describe_col(4, &mut actual).unwrap();
assert_eq!(expected, actual);
assert_eq!(kind, cursor.col_data_type(4).unwrap());
let kind = DataType::WChar { length: 10 };
let expected = ColumnDescription::new("e", kind, Nullability::Nullable);
cursor.describe_col(5, &mut actual).unwrap();
assert_eq!(expected, actual);
assert_eq!(kind, cursor.col_data_type(5).unwrap());
let kind = DataType::Numeric {
precision: 3,
scale: 2,
};
let expected = ColumnDescription::new("f", kind, Nullability::Nullable);
cursor.describe_col(6, &mut actual).unwrap();
assert_eq!(expected, actual);
assert_eq!(kind, cursor.col_data_type(6).unwrap());
let kind = DataType::Timestamp { precision: 7 };
let expected = ColumnDescription::new("g", kind, Nullability::Nullable);
cursor.describe_col(7, &mut actual).unwrap();
assert_eq!(expected, actual);
assert_eq!(kind, cursor.col_data_type(7).unwrap());
let kind = DataType::Other {
data_type: SqlDataType(-154),
column_size: 16,
decimal_digits: 7,
};
let expected = ColumnDescription::new("h", kind, Nullability::Nullable);
cursor.describe_col(8, &mut actual).unwrap();
assert_eq!(expected, actual);
assert_eq!(kind, cursor.col_data_type(8).unwrap());
let kind = DataType::LongVarchar { length: 2147483647 };
let expected = ColumnDescription::new("i", kind, Nullability::Nullable);
cursor.describe_col(9, &mut actual).unwrap();
assert_eq!(expected, actual);
assert_eq!(kind, cursor.col_data_type(9).unwrap());
let kind = DataType::LongVarbinary { length: 2147483647 };
let expected = ColumnDescription::new("j", kind, Nullability::Nullable);
cursor.describe_col(10, &mut actual).unwrap();
assert_eq!(expected, actual);
assert_eq!(kind, cursor.col_data_type(10).unwrap());
let kind = DataType::Float { precision: 53 };
let expected = ColumnDescription::new("k", kind, Nullability::Nullable);
cursor.describe_col(11, &mut actual).unwrap();
assert_eq!(expected, actual);
assert_eq!(kind, cursor.col_data_type(11).unwrap());
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn bulk_fetch_text(profile: &Profile) {
let table_name = "TextBuffer";
let conn = profile
.setup_empty_table(table_name, &["VARCHAR(255)", "INT"])
.unwrap();
let insert = format!("INSERT INTO {} (a,b) VALUES (?,?), (?,?),(?,?)", table_name);
conn.execute(
&insert,
(
&"Interstellar".into_parameter(),
&None::<i32>.into_parameter(),
&"2001: A Space Odyssey".into_parameter(),
&1968,
&"Jurassic Park".into_parameter(),
&1993,
),
)
.unwrap();
let query = format!("SELECT a,b FROM {} ORDER BY id;", table_name);
let cursor = conn.execute(&query, ()).unwrap().unwrap();
let actual = cursor_to_string(cursor);
let expected = "Interstellar,NULL\n2001: A Space Odyssey,1968\nJurassic Park,1993";
assert_eq!(expected, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn into_cursor(profile: &Profile) {
let table_name = "IntoCursor";
let conn = profile
.setup_empty_table(table_name, &["VARCHAR(255)", "INT"])
.unwrap();
let insert = format!("INSERT INTO {} (a,b) VALUES (?,?), (?,?),(?,?)", table_name);
conn.execute(
&insert,
(
&"Interstellar".into_parameter(),
&None::<i32>.into_parameter(),
&"2001: A Space Odyssey".into_parameter(),
&1968,
&"Jurassic Park".into_parameter(),
&1993,
),
)
.unwrap();
let make_cursor = || {
let conn = profile.connection().unwrap();
let query = format!("SELECT a,b FROM {} ORDER BY id;", table_name);
conn.into_cursor(&query, ()).unwrap().unwrap()
};
let cursor = make_cursor();
let actual = cursor_to_string(cursor);
let expected = "Interstellar,NULL\n2001: A Space Odyssey,1968\nJurassic Park,1993";
assert_eq!(expected, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn column_name(profile: &Profile) {
let table_name = "ColumnName";
let conn = profile
.setup_empty_table(table_name, &["VARCHAR(255)", "INT"])
.unwrap();
let sql = format!("SELECT a, b FROM {};", table_name);
let cursor = conn.execute(&sql, ()).unwrap().unwrap();
let name = cursor.col_name(1).unwrap();
assert_eq!("a", name);
let name = cursor.col_name(2).unwrap();
assert_eq!("b", name);
let mut desc = ColumnDescription::default();
cursor.describe_col(1, &mut desc).unwrap();
assert_eq!("a", desc.name_to_string().unwrap());
cursor.describe_col(2, &mut desc).unwrap();
assert_eq!("b", desc.name_to_string().unwrap());
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn bind_char(profile: &Profile) {
let table_name = "BindChar";
let conn = profile.setup_empty_table(table_name, &["CHAR(5)"]).unwrap();
let insert_sql = format!("INSERT INTO {} (a) VALUES ('Hello');", table_name);
conn.execute(&insert_sql, ()).unwrap();
let sql = format!("SELECT a FROM {};", table_name);
let cursor = conn.execute(&sql, ()).unwrap().unwrap();
let mut buf = SingleColumnRowSetBuffer::with_text_column(1, 5);
let mut row_set_cursor = cursor.bind_buffer(&mut buf).unwrap();
row_set_cursor.fetch().unwrap();
drop(row_set_cursor);
assert_eq!(Some(&b"Hello"[..]), buf.value_at(0));
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn bind_char_to_wchar(profile: &Profile) {
let table_name = "BindCharToWChar";
let conn = profile.setup_empty_table(table_name, &["CHAR(5)"]).unwrap();
let insert_sql = format!("INSERT INTO {} (a) VALUES ('Hello');", table_name);
conn.execute(&insert_sql, ()).unwrap();
let sql = format!("SELECT a FROM {};", table_name);
let cursor = conn.execute(&sql, ()).unwrap().unwrap();
let mut buf = SingleColumnRowSetBuffer::with_wide_text_column(1, 5);
let mut row_set_cursor = cursor.bind_buffer(&mut buf).unwrap();
row_set_cursor.fetch().unwrap();
drop(row_set_cursor);
assert_eq!(Some(U16String::from_str("Hello").as_ustr()), buf.ustr_at(0));
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn bind_bit(profile: &Profile) {
let table_name = "BindBit";
let conn = profile.setup_empty_table(table_name, &["BIT"]).unwrap();
let insert_sql = format!("INSERT INTO {} (a) VALUES (0),(1);", table_name);
conn.execute(&insert_sql, ()).unwrap();
let sql = format!("SELECT a FROM {};", table_name);
let cursor = conn.execute(&sql, ()).unwrap().unwrap();
let mut buf = SingleColumnRowSetBuffer::<Vec<Bit>>::new(3);
let mut row_set_cursor = cursor.bind_buffer(&mut buf).unwrap();
row_set_cursor.fetch().unwrap();
drop(row_set_cursor);
assert!(!buf.get()[0].as_bool());
assert!(buf.get()[1].as_bool());
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn truncate_fixed_sized(profile: &Profile) {
let table_name = "TruncateFixedSized";
let conn = profile.setup_empty_table(table_name, &["CHAR(5)"]).unwrap();
let insert_sql = format!("INSERT INTO {} (a) VALUES ('Hello');", table_name);
conn.execute(&insert_sql, ()).unwrap();
let sql = format!("SELECT a FROM {};", table_name);
let cursor = conn.execute(&sql, ()).unwrap().unwrap();
let mut buf = SingleColumnRowSetBuffer::with_text_column(1, 3);
let mut row_set_cursor = cursor.bind_buffer(&mut buf).unwrap();
row_set_cursor.fetch().unwrap();
drop(row_set_cursor);
assert_eq!(Some(&b"Hel"[..]), buf.value_at(0));
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn bind_varchar(profile: &Profile) {
let table_name = "BindVarchar";
let conn = profile
.setup_empty_table(table_name, &["VARCHAR(100)"])
.unwrap();
let insert_sql = format!("INSERT INTO {} (a) VALUES ('Hello, World!');", table_name);
conn.execute(&insert_sql, ()).unwrap();
let sql = format!("SELECT a FROM {};", table_name);
let cursor = conn.execute(&sql, ()).unwrap().unwrap();
let mut buf = SingleColumnRowSetBuffer::with_text_column(1, 100);
let mut row_set_cursor = cursor.bind_buffer(&mut buf).unwrap();
row_set_cursor.fetch().unwrap();
drop(row_set_cursor);
assert_eq!(Some(&b"Hello, World!"[..]), buf.value_at(0));
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn bind_varchar_to_wchar(profile: &Profile) {
let table_name = "BindVarcharToWChar";
let conn = profile
.setup_empty_table(table_name, &["VARCHAR(100)"])
.unwrap();
let insert_sql = format!("INSERT INTO {} (a) VALUES ('Hello, World!');", table_name);
conn.execute(&insert_sql, ()).unwrap();
let sql = format!("SELECT a FROM {};", table_name);
let cursor = conn.execute(&sql, ()).unwrap().unwrap();
let mut buf = SingleColumnRowSetBuffer::with_wide_text_column(1, 100);
let mut row_set_cursor = cursor.bind_buffer(&mut buf).unwrap();
row_set_cursor.fetch().unwrap();
drop(row_set_cursor);
assert_eq!(
Some(U16String::from_str("Hello, World!").as_ustr()),
buf.ustr_at(0)
);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[cfg(not(target_os = "windows"))] fn nvarchar_to_text(profile: &Profile) {
let table_name = "NvarcharToText";
let conn = profile
.setup_empty_table(table_name, &["NVARCHAR(1)"])
.unwrap();
let insert_sql = format!("INSERT INTO {} (a) VALUES (?);", table_name);
conn.execute(&insert_sql, &"â„¢".into_parameter()).unwrap();
let sql = format!("SELECT a FROM {};", table_name);
let cursor = conn.execute(&sql, ()).unwrap().unwrap();
let text = cursor_to_string(cursor);
assert_eq!("â„¢", text);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn bind_numeric_to_float(profile: &Profile) {
let table_name = "BindNumericToFloat";
let conn = profile
.setup_empty_table(table_name, &["NUMERIC(3,2)"])
.unwrap();
let insert_sql = format!("INSERT INTO {} (a) VALUES (?);", table_name);
conn.execute(&insert_sql, &1.23).unwrap();
let sql = format!("SELECT a FROM {}", table_name);
let cursor = conn.execute(&sql, ()).unwrap().unwrap();
let buf: SingleColumnRowSetBuffer<Vec<f64>> = SingleColumnRowSetBuffer::new(1);
let mut row_set_cursor = cursor.bind_buffer(buf).unwrap();
let actual = row_set_cursor.fetch().unwrap().unwrap().get();
assert_eq!(1, actual.len());
assert!((1.23f64 - actual[0]).abs() < f64::EPSILON);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn bind_numeric_to_i64(profile: &Profile) {
let table_name = "BindNumericToI64";
let conn = profile
.setup_empty_table(table_name, &["NUMERIC(10,0)"])
.unwrap();
let insert_sql = format!("INSERT INTO {} (a) VALUES (?);", table_name);
conn.execute(&insert_sql, &1234567890i64).unwrap();
let sql = format!("SELECT a FROM {}", table_name);
let cursor = conn.execute(&sql, ()).unwrap().unwrap();
let buf: SingleColumnRowSetBuffer<Vec<i64>> = SingleColumnRowSetBuffer::new(1);
let mut row_set_cursor = cursor.bind_buffer(buf).unwrap();
let actual = row_set_cursor.fetch().unwrap().unwrap().get();
assert_eq!(1, actual.len());
assert_eq!(1234567890, actual[0]);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
fn columnar_fetch_varbinary(profile: &Profile) {
let table_name = "ColumnarFetchVarbinary";
let conn = profile
.setup_empty_table(table_name, &["VARBINARY(10)"])
.unwrap();
let insert_sql = format!(
"INSERT INTO {} (a) Values \
(CONVERT(Varbinary(10), 'Hello')),\
(CONVERT(Varbinary(10), 'World')),\
(NULL)",
table_name
);
conn.execute(&insert_sql, ()).unwrap();
let cursor = conn
.execute("SELECT a FROM ColumnarFetchVarbinary ORDER BY Id", ())
.unwrap()
.unwrap();
let data_type = cursor.col_data_type(1).unwrap();
assert_eq!(DataType::Varbinary { length: 10 }, data_type);
let buffer_kind = BufferKind::from_data_type(data_type).unwrap();
assert_eq!(BufferKind::Binary { length: 10 }, buffer_kind);
let buffer_desc = BufferDescription {
kind: buffer_kind,
nullable: true,
};
let row_set_buffer =
ColumnarAnyBuffer::try_from_description(10, iter::once(buffer_desc)).unwrap();
let mut cursor = cursor.bind_buffer(row_set_buffer).unwrap();
let batch = cursor.fetch().unwrap().unwrap();
let mut col_it = batch.column(0).as_bin_view().unwrap().iter();
assert_eq!(Some(&b"Hello"[..]), col_it.next().unwrap());
assert_eq!(Some(&b"World"[..]), col_it.next().unwrap());
assert_eq!(Some(None), col_it.next()); assert_eq!(None, col_it.next()); }
#[test_case(MSSQL; "Microsoft SQL Server")]
fn columnar_fetch_binary(profile: &Profile) {
let conn = profile
.setup_empty_table("ColumnarFetchBinary", &["BINARY(5)"])
.unwrap();
conn.execute(
"INSERT INTO ColumnarFetchBinary (a) Values \
(CONVERT(Binary(5), 'Hello')),\
(CONVERT(Binary(5), 'World')),\
(NULL)",
(),
)
.unwrap();
let cursor = conn
.execute("SELECT a FROM ColumnarFetchBinary ORDER BY Id", ())
.unwrap()
.unwrap();
let data_type = cursor.col_data_type(1).unwrap();
assert_eq!(DataType::Binary { length: 5 }, data_type);
let buffer_kind = BufferKind::from_data_type(data_type).unwrap();
assert_eq!(BufferKind::Binary { length: 5 }, buffer_kind);
let buffer_desc = BufferDescription {
kind: buffer_kind,
nullable: true,
};
let row_set_buffer =
ColumnarAnyBuffer::try_from_description(10, iter::once(buffer_desc)).unwrap();
let mut cursor = cursor.bind_buffer(row_set_buffer).unwrap();
let batch = cursor.fetch().unwrap().unwrap();
let mut col_it = batch.column(0).as_bin_view().unwrap().iter();
assert_eq!(Some(&b"Hello"[..]), col_it.next().unwrap());
assert_eq!(Some(&b"World"[..]), col_it.next().unwrap());
assert_eq!(Some(None), col_it.next()); assert_eq!(None, col_it.next()); }
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(SQLITE_3; "SQLite 3")]
fn columnar_fetch_timestamp(profile: &Profile) {
let table_name = "ColumnarFetchTimestamp";
let conn = profile
.setup_empty_table(table_name, &["DATETIME2(3)"])
.unwrap();
conn.execute(
&format!(
"INSERT INTO {} (a) Values \
({{ ts '2021-03-20 15:24:12.12' }}),\
({{ ts '2020-03-20 15:24:12' }}),\
({{ ts '1970-01-01 00:00:00' }}),\
(NULL)",
table_name
),
(),
)
.unwrap();
let cursor = conn
.execute(&format!("SELECT a FROM {} ORDER BY Id", table_name), ())
.unwrap()
.unwrap();
let data_type = cursor.col_data_type(1).unwrap();
assert_eq!(DataType::Timestamp { precision: 3 }, data_type);
let buffer_kind = BufferKind::from_data_type(data_type).unwrap();
assert_eq!(BufferKind::Timestamp, buffer_kind);
let buffer_desc = BufferDescription {
kind: buffer_kind,
nullable: true,
};
let row_set_buffer =
ColumnarAnyBuffer::try_from_description(10, iter::once(buffer_desc)).unwrap();
let mut cursor = cursor.bind_buffer(row_set_buffer).unwrap();
let batch = cursor.fetch().unwrap().unwrap();
let mut col_it = batch.column(0).as_nullable_slice().unwrap();
assert_eq!(
Some(&Timestamp {
year: 2021,
month: 3,
day: 20,
hour: 15,
minute: 24,
second: 12,
fraction: 120_000_000,
}),
col_it.next().unwrap()
);
assert_eq!(
Some(&Timestamp {
year: 2020,
month: 3,
day: 20,
hour: 15,
minute: 24,
second: 12,
fraction: 0,
}),
col_it.next().unwrap()
);
assert_eq!(
Some(&Timestamp {
year: 1970,
month: 1,
day: 1,
hour: 0,
minute: 0,
second: 0,
fraction: 0,
}),
col_it.next().unwrap()
);
assert_eq!(Some(None), col_it.next()); assert_eq!(None, col_it.next()); }
#[test_case(MSSQL; "Microsoft SQL Server")]
fn columnar_insert_timestamp(profile: &Profile) {
let table_name = "ColumnarInsertTimestamp";
let conn = profile
.setup_empty_table(table_name, &["DATETIME2"])
.unwrap();
let desc = BufferDescription {
kind: BufferKind::Timestamp,
nullable: true,
};
let mut buffer = ColumnarAnyBuffer::try_from_description(10, iter::once(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,
];
buffer.set_num_rows(input.len());
if let AnyColumnViewMut::NullableTimestamp(mut writer) = buffer.column_mut(0) {
writer.write(input.iter().copied());
} else {
panic!("Expected timestamp column writer");
};
conn.execute(
&format!("INSERT INTO {} (a) VALUES (?)", table_name),
&buffer,
)
.unwrap();
let actual = table_to_string(&conn, table_name, &["a"]);
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")]
fn columnar_insert_int_raw(profile: &Profile) {
let table_name = "ColumnarInsertIntRaw";
let conn = profile.setup_empty_table(table_name, &["INTEGER"]).unwrap();
let desc = BufferDescription {
kind: BufferKind::I32,
nullable: true,
};
let mut buffer = ColumnarAnyBuffer::try_from_description(10, iter::once(desc)).unwrap();
let input_values = [1, 0, 3];
let mask = [true, false, true];
buffer.set_num_rows(input_values.len());
if let AnyColumnViewMut::NullableI32(mut writer) = buffer.column_mut(0) {
let (values, indicators) = writer.raw_values();
values.copy_from_slice(&input_values);
indicators
.iter_mut()
.zip(mask.iter())
.for_each(|(indicator, &mask)| *indicator = if mask { 0 } else { NULL_DATA })
} else {
panic!("Expected i32 column writer");
};
conn.execute(
&format!("INSERT INTO {} (a) VALUES (?)", table_name),
&buffer,
)
.unwrap();
let actual = table_to_string(&conn, table_name, &["a"]);
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 = "ColmunarInsertTimestampMs";
let conn = profile
.setup_empty_table(table_name, &["DATETIME2(3)"])
.unwrap();
let desc = BufferDescription {
kind: BufferKind::Timestamp,
nullable: true,
};
let mut buffer = ColumnarAnyBuffer::try_from_description(10, iter::once(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,
];
buffer.set_num_rows(input.len());
if let AnyColumnViewMut::NullableTimestamp(mut writer) = buffer.column_mut(0) {
writer.write(input.iter().copied());
} else {
panic!("Expected timestamp column writer");
};
conn.execute(
&format!("INSERT INTO {} (a) VALUES (?)", table_name),
&buffer,
)
.unwrap();
let cursor = conn
.execute(&format!("SELECT a FROM {} ORDER BY Id", table_name), ())
.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 conn = profile
.setup_empty_table("ColumnarInsertVarbinary", &["VARBINARY(13)"])
.unwrap();
let desc = BufferDescription {
kind: BufferKind::Binary { length: 5 },
nullable: true,
};
let mut buffer = ColumnarAnyBuffer::try_from_description(4, iter::once(desc)).unwrap();
let input = [
Some(&b"Hello"[..]),
Some(&b"World"[..]),
None,
Some(&b"Hello, World!"[..]),
];
buffer.set_num_rows(input.len());
if let AnyColumnViewMut::Binary(mut writer) = buffer.column_mut(0) {
writer.set_max_len(13);
assert_eq!(13, writer.max_len());
writer.write(input.iter().copied());
} else {
panic!("Expected binary column writer");
};
conn.execute(
"INSERT INTO ColumnarInsertVarbinary (a) VALUES (?)",
&buffer,
)
.unwrap();
let cursor = conn
.execute("SELECT a FROM ColumnarInsertVarbinary ORDER BY Id", ())
.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")]
fn columnar_insert_varchar(profile: &Profile) {
let table_name = "ColumnarInsertVarchar";
let conn = profile
.setup_empty_table(table_name, &["VARCHAR(13)"])
.unwrap();
let desc = BufferDescription {
kind: BufferKind::Text { max_str_len: 5 },
nullable: true,
};
let mut buffer = ColumnarAnyBuffer::try_from_description(4, iter::once(desc)).unwrap();
let input = [
Some(&b"Hello"[..]),
Some(&b"World"[..]),
None,
Some(&b"Hello, World!"[..]),
];
buffer.set_num_rows(input.len());
let mut col_view = buffer.column_mut(0).as_text_view().unwrap();
col_view.set_max_len(13);
assert_eq!(col_view.max_len(), 13);
col_view.write(input.iter().copied());
conn.execute(
&format!("INSERT INTO {} (a) VALUES (?)", table_name),
&buffer,
)
.unwrap();
let cursor = conn
.execute(&format!("SELECT a FROM {} ORDER BY Id", table_name), ())
.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")]
fn columnar_insert_text_as_sql_integer(profile: &Profile) {
let table_name = "ColumnarInsertTextAsSqlInteger";
let conn = profile.setup_empty_table(table_name, &["INTEGER"]).unwrap();
let column_buffer = WithDataType {
value: TextColumn::try_new(4, 5).unwrap(),
data_type: DataType::Integer,
};
let mut buffer = ColumnarBuffer::new(vec![(1, column_buffer)]);
let input = [Some(&b"1"[..]), Some(&b"2"[..]), None, Some(&b"4"[..])];
buffer.set_num_rows(input.len());
let mut writer = buffer.column_mut(0);
writer.write(input.iter().copied());
conn.execute(
&format!("INSERT INTO {} (a) VALUES (?)", table_name),
&buffer,
)
.unwrap();
let cursor = conn
.execute(&format!("SELECT a FROM {} ORDER BY Id", table_name), ())
.unwrap()
.unwrap();
let actual = cursor_to_string(cursor);
let expected = "1\n2\nNULL\n4";
assert_eq!(expected, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn adaptive_columnar_insert_varchar(profile: &Profile) {
let table_name = "AdaptiveColumnarInsertVarchar";
let conn = profile
.setup_empty_table(table_name, &["VARCHAR(13)"])
.unwrap();
let desc = BufferDescription {
kind: BufferKind::Text { max_str_len: 1 },
nullable: true,
};
let input = [
Some(&b"Hi"[..]),
Some(&b"Hello"[..]),
Some(&b"World"[..]),
None,
Some(&b"Hello, World!"[..]),
];
let mut buffer =
ColumnarAnyBuffer::try_from_description(input.len(), iter::once(desc)).unwrap();
buffer.set_num_rows(input.len());
let mut col_view = buffer.column_mut(0).as_text_view().unwrap();
for (index, &text) in input.iter().enumerate() {
col_view.append(index, text)
}
conn.execute(
&format!("INSERT INTO {} (a) VALUES (?)", table_name),
&buffer,
)
.unwrap();
let cursor = conn
.execute(&format!("SELECT a FROM {} ORDER BY Id", table_name), ())
.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 = "AdaptiveColumnarInsertVarbin";
let conn = profile
.setup_empty_table(table_name, &["VARBINARY(13)"])
.unwrap();
let desc = BufferDescription {
kind: BufferKind::Binary { length: 1 },
nullable: true,
};
let input = [
Some(&b"Hi"[..]),
Some(&b"Hello"[..]),
Some(&b"World"[..]),
None,
Some(&b"Hello, World!"[..]),
];
let mut buffer =
ColumnarAnyBuffer::try_from_description(input.len(), iter::once(desc)).unwrap();
buffer.set_num_rows(input.len());
if let AnyColumnViewMut::Binary(mut writer) = buffer.column_mut(0) {
for (index, &bytes) in input.iter().enumerate() {
writer.append(index, bytes)
}
} else {
panic!("Expected binary column writer");
};
conn.execute(
&format!("INSERT INTO {} (a) VALUES (?)", table_name),
&buffer,
)
.unwrap();
let cursor = conn
.execute(&format!("SELECT a FROM {} ORDER BY Id", table_name), ())
.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")]
fn columnar_insert_wide_varchar(profile: &Profile) {
let table_name = "ColumnarInsertWideVarchar";
let conn = profile
.setup_empty_table(table_name, &["NVARCHAR(13)"])
.unwrap();
let desc = BufferDescription {
kind: BufferKind::WText { max_str_len: 5 },
nullable: true,
};
let mut buffer = ColumnarAnyBuffer::try_from_description(10, iter::once(desc)).unwrap();
let input = [
Some(U16String::from_str("Hello")),
Some(U16String::from_str("World")),
None,
Some(U16String::from_str("Hello, World!")),
];
buffer.set_num_rows(input.len());
let mut writer = buffer.column_mut(0).as_w_text_view().unwrap();
writer.set_max_len(13);
writer.write(
input
.iter()
.map(|opt| opt.as_ref().map(|ustring| ustring.as_slice())),
);
conn.execute(
&format!("INSERT INTO {} (a) VALUES (?)", table_name),
&buffer,
)
.unwrap();
let cursor = conn
.execute(&format!("SELECT a FROM {} ORDER BY Id", table_name), ())
.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")]
fn bind_integer_parameter(profile: &Profile) {
let table_name = "BindIntegerParam";
let conn = profile
.setup_empty_table(table_name, &["INTEGER", "INTEGER"])
.unwrap();
let insert = format!("INSERT INTO {} (a,b) VALUES (1,1), (2,2);", table_name);
conn.execute(&insert, ()).unwrap();
let sql = format!("SELECT a FROM {} where b=?;", table_name);
let cursor = conn.execute(&sql, &1).unwrap().unwrap();
let actual = cursor_to_string(cursor);
assert_eq!("1", actual);
let cursor = conn.execute(&sql, &2).unwrap().unwrap();
let actual = cursor_to_string(cursor);
assert_eq!("2", actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
fn insert_string_ending_with_nul(profile: &Profile) {
let table_name = "InsertStringEndingWithNul";
let conn = profile
.setup_empty_table(table_name, &["VARCHAR(10)"])
.unwrap();
let sql = format!("INSERT INTO {} (a) VALUES(?)", table_name);
let param = "Hell\0";
conn.execute(&sql, ¶m.into_parameter()).unwrap();
let actual = table_to_string(&conn, table_name, &["a"]);
assert_eq!("Hell\0", actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn prepared_statement(profile: &Profile) {
let table_name = "PreparedStatement";
let conn = profile
.setup_empty_table(table_name, &["VARCHAR(13)", "INTEGER"])
.unwrap();
let insert = format!(
"INSERT INTO {} (a,b) VALUES ('First', 1), ('Second', 2);",
table_name
);
conn.execute(&insert, ()).unwrap();
let sql = format!("SELECT a FROM {} where b=?;", table_name);
let mut prepared = conn.prepare(&sql).unwrap();
{
let cursor = prepared.execute(&1).unwrap().unwrap();
let title = cursor_to_string(cursor);
assert_eq!("First", title);
}
{
let cursor = prepared.execute(&2).unwrap().unwrap();
let title = cursor_to_string(cursor);
assert_eq!("Second", title);
}
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn preallocated(profile: &Profile) {
let conn = profile
.setup_empty_table("Preallocated", &["VARCHAR(10)"])
.unwrap();
let mut prealloc = conn.preallocate().unwrap();
{
let res = prealloc
.execute("INSERT INTO Preallocated (a) VALUES ('Hello')", ())
.unwrap();
assert!(res.is_none());
}
{
let cursor = prealloc
.execute("SELECT a FROM Preallocated ORDER BY id", ())
.unwrap()
.unwrap();
let actual = cursor_to_string(cursor);
let expected = "Hello";
assert_eq!(expected, actual);
}
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn preallocation_soundness(profile: &Profile) {
let conn = profile
.setup_empty_table("PreallocationSoundness", &["VARCHAR(10)"])
.unwrap();
let mut prealloc = conn.preallocate().unwrap();
{
let res = prealloc
.execute(
"INSERT INTO PreallocationSoundness (a) VALUES ('Hello')",
(),
)
.unwrap();
assert!(res.is_none());
}
{
let cursor = prealloc
.execute("SELECT a FROM PreallocationSoundness ORDER BY id", ())
.unwrap()
.unwrap();
let actual = cursor_to_string(cursor);
let expected = "Hello";
assert_eq!(expected, actual);
}
{
let mut cursor = prealloc
.execute("SELECT a FROM PreallocationSoundness ORDER BY id", ())
.unwrap()
.unwrap();
let _row = cursor.next_row().unwrap().unwrap();
assert!(cursor.next_row().unwrap().is_none());
}
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn integer_parameter_as_string(profile: &Profile) {
let table_name = "IntegerParameterAsString";
let conn = profile
.setup_empty_table(table_name, &["INTEGER", "INTEGER"])
.unwrap();
let insert = format!("INSERT INTO {} (a,b) VALUES (1,1), (2,2);", table_name);
conn.execute(&insert, ()).unwrap();
let sql = format!("SELECT a FROM {} where b=?;", table_name);
let cursor = conn.execute(&sql, &"2".into_parameter()).unwrap().unwrap();
let actual = cursor_to_string(cursor);
assert_eq!("2", actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn bind_optional_integer_parameter(profile: &Profile) {
let table_name = "ParameterOptionIntegerSome";
let conn = profile
.setup_empty_table(table_name, &["INTEGER", "INTEGER"])
.unwrap();
let insert = format!("INSERT INTO {} (a,b) VALUES (1,1), (2,2);", table_name);
conn.execute(&insert, ()).unwrap();
let sql = format!("SELECT a FROM {} where b=?;", table_name);
let cursor = conn
.execute(&sql, &Some(2).into_parameter())
.unwrap()
.unwrap();
let actual = cursor_to_string(cursor);
assert_eq!("2", actual);
let cursor = conn
.execute(&sql, &None::<i32>.into_parameter())
.unwrap()
.unwrap();
let actual = cursor_to_string(cursor);
assert_eq!("", actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[cfg(not(target_os = "windows"))] fn non_ascii_char(profile: &Profile) {
let table_name = "NonAsciiChar";
let conn = profile
.setup_empty_table(table_name, &["VARCHAR(1)"])
.unwrap();
conn.execute(
&format!("INSERT INTO {} (a) VALUES (?), (?);", table_name),
(&"A".into_parameter(), &"Ü".into_parameter()),
)
.unwrap();
let sql = format!("SELECT a FROM {} ORDER BY id;", table_name);
let cursor = conn.execute(&sql, ()).unwrap().unwrap();
let output = cursor_to_string(cursor);
assert_eq!("A\nÜ", output);
}
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn wchar(profile: &Profile) {
let table_name = "WChar";
let conn = profile
.setup_empty_table(table_name, &["NVARCHAR(1)"])
.unwrap();
conn.execute(
&format!("INSERT INTO {} (a) VALUES (?), (?);", table_name),
(&"A".into_parameter(), &"Ü".into_parameter()),
)
.unwrap();
let sql = format!("SELECT a FROM {} ORDER BY id;", table_name);
let cursor = conn.execute(&sql, ()).unwrap().unwrap();
let desc = BufferDescription {
nullable: false,
kind: BufferKind::WText { max_str_len: 1 },
};
let row_set_buffer = ColumnarAnyBuffer::try_from_description(2, iter::once(desc)).unwrap();
let mut row_set_cursor = cursor.bind_buffer(row_set_buffer).unwrap();
let batch = row_set_cursor.fetch().unwrap().unwrap();
let col = batch.column(0);
let wtext_col = col.as_w_text_view().unwrap();
assert_eq!(2, wtext_col.len());
assert_eq!(
&U16String::from_str("A"),
&U16Str::from_slice(wtext_col.get(0).unwrap())
);
assert_eq!(
&U16String::from_str("Ü"),
&U16Str::from_slice(wtext_col.get(1).unwrap())
);
assert!(row_set_cursor.fetch().unwrap().is_none());
}
#[test]
#[cfg(not(target_os = "windows"))] fn wchar_as_char() {
let conn = ENV
.connect_with_connection_string(MSSQL.connection_string)
.unwrap();
setup_empty_table(&conn, MSSQL.index_type, "WCharAsChar", &["NVARCHAR(1)"]).unwrap();
conn.execute(
"INSERT INTO WCharAsChar (a) VALUES (?), (?);",
(&"A".into_parameter(), &"Ü".into_parameter()),
)
.unwrap();
let sql = "SELECT a FROM WCharAsChar ORDER BY id;";
let cursor = conn.execute(sql, ()).unwrap().unwrap();
let output = cursor_to_string(cursor);
assert_eq!("A\nÜ", output);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn two_parameters_in_tuple(profile: &Profile) {
let table_name = "TwoParmetersInTuple";
let conn = profile.setup_empty_table(table_name, &["INTEGER"]).unwrap();
let insert = format!("INSERT INTO {} (a) VALUES (1), (2), (3), (4);", table_name);
conn.execute(&insert, ()).unwrap();
let sql = format!(
"SELECT a FROM {} where ? < a AND a < ? ORDER BY id;",
table_name
);
let cursor = conn.execute(&sql, (&1, &4)).unwrap().unwrap();
let actual = cursor_to_string(cursor);
assert_eq!("2\n3", actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn heterogenous_parameters_in_array(profile: &Profile) {
let table_name = "heterogenous_parameters_in_array";
let conn = profile
.setup_empty_table(table_name, &["INTEGER", "VARCHAR(13)"])
.unwrap();
let insert_sql = format!(
"INSERT INTO {} (a, b) VALUES (1, 'Hello'), (2, 'Hello'), (3, 'Hello'), (3, 'Hallo')",
table_name
);
conn.execute(&insert_sql, ()).unwrap();
let query = format!("SELECT a,b FROM {} where a > ? AND b = ?;", table_name);
let params: [Box<dyn InputParameter>; 2] = [Box::new(2), Box::new("Hello".into_parameter())];
let cursor = conn.execute(&query, ¶ms[..]).unwrap().unwrap();
let actual = cursor_to_string(cursor);
assert_eq!("3,Hello", actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn column_names_iterator(profile: &Profile) {
let table_name = "column_names_iterator";
let conn = profile
.setup_empty_table(table_name, &["INTEGER", "VARCHAR(13)"])
.unwrap();
let sql = format!("SELECT a, b FROM {};", table_name);
let cursor = conn.execute(&sql, ()).unwrap().unwrap();
let names: Vec<_> = cursor
.column_names()
.unwrap()
.collect::<Result<_, _>>()
.unwrap();
assert_eq!(&["a", "b"], names.as_slice());
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn column_names_from_prepared_query(profile: &Profile) {
let table_name = "column_names_from_prepared_query";
let conn = profile
.setup_empty_table(table_name, &["INTEGER", "VARCHAR(13)"])
.unwrap();
let sql = format!("SELECT a, b FROM {};", table_name);
let prepared = conn.prepare(&sql).unwrap();
let names: Vec<_> = prepared
.column_names()
.unwrap()
.collect::<Result<_, _>>()
.unwrap();
assert_eq!(&["a", "b"], names.as_slice());
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn metadata_from_prepared_insert_query(profile: &Profile) {
let table_name = "metadata_from_prepared_insert_query";
let conn = profile
.setup_empty_table(table_name, &["INTEGER", "VARCHAR(13)"])
.unwrap();
let sql = format!("INSERT INTO {} (a, b) VALUES (42, 'Hello');", table_name);
let prepared = conn.prepare(&sql).unwrap();
assert_eq!(0, prepared.num_result_cols().unwrap());
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn bulk_insert_with_text_buffer(profile: &Profile) {
let conn = profile
.setup_empty_table("BulkInsertWithTextBuffer", &["VARCHAR(50)"])
.unwrap();
let mut prepared = conn
.prepare("INSERT INTO BulkInsertWithTextBuffer (a) Values (?)")
.unwrap();
let mut params = TextRowSet::from_max_str_lens(5, [50].iter().copied()).unwrap();
params.append(["England"].iter().map(|s| Some(s.as_bytes())));
params.append(["France"].iter().map(|s| Some(s.as_bytes())));
params.append(["Germany"].iter().map(|s| Some(s.as_bytes())));
prepared.execute(¶ms).unwrap();
let expected = "England\nFrance\nGermany";
let cursor = conn
.execute("SELECT a FROM BulkInsertWithTextBuffer ORDER BY id;", ())
.unwrap()
.unwrap();
let actual = cursor_to_string(cursor);
assert_eq!(expected, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn bulk_insert_with_columnar_buffer(profile: &Profile) {
let conn = profile
.setup_empty_table("BulkInsertWithColumnarBuffer", &["VARCHAR(50)", "INTEGER"])
.unwrap();
let mut prepared = conn
.prepare("INSERT INTO BulkInsertWithColumnarBuffer (a,b) Values (?,?)")
.unwrap();
let description = [
BufferDescription {
nullable: true,
kind: BufferKind::Text { max_str_len: 50 },
},
BufferDescription {
nullable: true,
kind: BufferKind::I32,
},
]
.iter()
.copied();
let mut params = ColumnarAnyBuffer::try_from_description(5, description).unwrap();
params.set_num_rows(3);
let mut col_view = params.column_mut(0).as_text_view().unwrap();
let input = ["England", "France", "Germany"];
col_view.write(input.iter().map(|&s| Some(s.as_bytes())));
let input = [1, 2, 3];
let view_mut = params.column_mut(1);
let mut col = i32::as_nullable_slice_mut(view_mut).unwrap();
col.write(input.iter().map(|&i| Some(i)));
prepared.execute(¶ms).unwrap();
let expected = "England,1\nFrance,2\nGermany,3";
let cursor = conn
.execute(
"SELECT a,b FROM BulkInsertWithColumnarBuffer ORDER BY id;",
(),
)
.unwrap()
.unwrap();
let actual = cursor_to_string(cursor);
assert_eq!(expected, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn bulk_insert_with_multiple_batches(profile: &Profile) {
let table_name = "BulkInsertWithMultipleBatches";
let conn = profile
.setup_empty_table(table_name, &["VARCHAR(50)", "INTEGER"])
.unwrap();
let mut prepared = conn
.prepare(&format!("INSERT INTO {table_name} (a,b) Values (?,?)"))
.unwrap();
let description = [
BufferDescription {
nullable: true,
kind: BufferKind::Text { max_str_len: 50 },
},
BufferDescription {
nullable: true,
kind: BufferKind::I32,
},
]
.iter()
.copied();
let mut params = ColumnarAnyBuffer::try_from_description(5, description).unwrap();
params.set_num_rows(3);
let mut col_view = params.column_mut(0).as_text_view().unwrap();
let input = ["England", "France", "Germany"];
col_view.write(input.iter().map(|&s| Some(s.as_bytes())));
let input = [1, 2, 3];
let view_mut = params.column_mut(1);
let mut col = i32::as_nullable_slice_mut(view_mut).unwrap();
col.write(input.iter().map(|&i| Some(i)));
prepared.execute(¶ms).unwrap();
params.set_num_rows(1);
let mut col_view = params.column_mut(0).as_text_view().unwrap();
let input = ["Spain"];
col_view.write(input.iter().map(|&s| Some(s.as_bytes())));
let input = [4];
let view_mut = params.column_mut(1);
let mut col = i32::as_nullable_slice_mut(view_mut).unwrap();
col.write(input.iter().map(|&i| Some(i)));
prepared.execute(¶ms).unwrap();
let expected = "England,1\nFrance,2\nGermany,3\nSpain,4";
let cursor = conn
.execute(&format!("SELECT a,b FROM {table_name} ORDER BY id;"), ())
.unwrap()
.unwrap();
let actual = cursor_to_string(cursor);
assert_eq!(expected, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn send_connection(profile: &Profile) {
let table_name = "SendConnection";
let conn = profile.setup_empty_table(table_name, &["INTEGER"]).unwrap();
let insert_sql = format!("INSERT INTO {} (a) VALUES (1),(2),(3)", table_name);
conn.execute(&insert_sql, ()).unwrap();
let conn = unsafe { conn.promote_to_send() };
let handle = thread::spawn(move || table_to_string(&conn, table_name, &["a"]));
let actual = handle.join().unwrap();
assert_eq!("1\n2\n3", actual)
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn parameter_option_strings(profile: &Profile) {
let conn = profile
.setup_empty_table("ParameterOptionStr", &["VARCHAR(50)"])
.unwrap();
let sql = "INSERT INTO ParameterOptionStr (a) VALUES (?);";
let mut prepared = conn.prepare(sql).unwrap();
prepared.execute(&None::<&str>.into_parameter()).unwrap();
prepared.execute(&Some("Bernd").into_parameter()).unwrap();
prepared.execute(&None::<String>.into_parameter()).unwrap();
prepared
.execute(&Some("Hello".to_string()).into_parameter())
.unwrap();
let cursor = conn
.execute("SELECT a FROM ParameterOptionStr ORDER BY id", ())
.unwrap()
.unwrap();
let actual = cursor_to_string(cursor);
let expected = "NULL\nBernd\nNULL\nHello";
assert_eq!(expected, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
fn parameter_option_bytes(profile: &Profile) {
let table_name = "ParameterOptionByteSlice";
let conn = profile
.setup_empty_table(table_name, &["VARBINARY(50)"])
.unwrap();
let sql = format!("INSERT INTO {} (a) VALUES (?);", table_name);
let mut prepared = conn.prepare(&sql).unwrap();
prepared.execute(&None::<&[u8]>.into_parameter()).unwrap();
prepared
.execute(&Some(&[1, 2, 3][..]).into_parameter())
.unwrap();
prepared.execute(&None::<Vec<u8>>.into_parameter()).unwrap();
prepared
.execute(&Some(vec![1, 2, 3]).into_parameter())
.unwrap();
let cursor = conn
.execute(&format!("SELECT a FROM {} ORDER BY id", table_name), ())
.unwrap()
.unwrap();
let actual = cursor_to_string(cursor);
let expected = "NULL\n010203\nNULL\n010203";
assert_eq!(expected, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn parameter_varchar_512(profile: &Profile) {
let table_name = "ParameterVarchar512";
let conn = profile
.setup_empty_table(table_name, &["VARCHAR(50)"])
.unwrap();
let sql = format!("INSERT INTO {} (a) VALUES (?);", table_name);
let mut prepared = conn.prepare(&sql).unwrap();
prepared.execute(&VarCharArray::<512>::NULL).unwrap();
prepared
.execute(&VarCharArray::<512>::new(b"Bernd"))
.unwrap();
let actual = table_to_string(&conn, table_name, &["a"]);
let expected = "NULL\nBernd";
assert_eq!(expected, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
fn parameter_varbinary_512(profile: &Profile) {
let table_name = "ParameterVarbinary512";
let conn = profile
.setup_empty_table(table_name, &["VARBINARY(50)"])
.unwrap();
let sql = format!("INSERT INTO {} (a) VALUES (?);", table_name);
let mut prepared = conn.prepare(&sql).unwrap();
prepared.execute(&VarBinaryArray::<512>::NULL).unwrap();
prepared
.execute(&VarBinaryArray::<512>::new(&[1, 2, 3]))
.unwrap();
let actual = table_to_string(&conn, table_name, &["a"]);
let expected = "NULL\n010203";
assert_eq!(expected, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn parameter_cstr(profile: &Profile) {
let table_name = "ParameterCStr";
let conn = profile
.setup_empty_table(table_name, &["VARCHAR(50)"])
.unwrap();
let sql = format!("INSERT INTO {} (a) VALUES (?);", table_name);
let mut prepared = conn.prepare(&sql).unwrap();
let param = CString::new("Hello, World!").unwrap();
prepared.execute(¶m).unwrap();
prepared.execute(param.as_c_str()).unwrap();
let actual = table_to_string(&conn, table_name, &["a"]);
let expected = "Hello, World!\nHello, World!";
assert_eq!(expected, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn read_into_columnar_buffer(profile: &Profile) {
let conn = profile
.setup_empty_table("ReadIntoColumnarBuffer", &["INTEGER", "VARCHAR(20)"])
.unwrap();
conn.execute(
"INSERT INTO ReadIntoColumnarBuffer (a, b) VALUES (42, 'Hello, World!')",
(),
)
.unwrap();
let cursor = conn
.execute("SELECT a,b FROM ReadIntoColumnarBuffer ORDER BY id", ())
.unwrap()
.unwrap();
let buffer_description = [
BufferDescription {
kind: BufferKind::I32,
nullable: true,
},
BufferDescription {
nullable: true,
kind: BufferKind::Text { max_str_len: 20 },
},
];
let buffer =
ColumnarAnyBuffer::try_from_description(20, buffer_description.iter().copied()).unwrap();
let mut cursor = cursor.bind_buffer(buffer).unwrap();
let batch = cursor.fetch().unwrap().unwrap();
let mut col = i32::as_nullable_slice(batch.column(0)).unwrap();
assert_eq!(Some(&42), col.next().unwrap());
assert_eq!(
Some(&b"Hello, World!"[..]),
batch.column(1).as_text_view().unwrap().get(0)
);
assert!(cursor.fetch().unwrap().is_none());
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn ignore_output_column(profile: &Profile) {
let conn = profile
.setup_empty_table("IgnoreOutputColumn", &["INTEGER", "INTEGER", "INTEGER"])
.unwrap();
let cursor = conn
.execute("SELECT a, b, c FROM IgnoreOutputColumn", ())
.unwrap()
.unwrap();
let bd = BufferDescription {
kind: BufferKind::I32,
nullable: true,
};
let buffer =
ColumnarAnyBuffer::from_description_and_indices(20, [(1, bd), (3, bd)].iter().copied());
let mut cursor = cursor.bind_buffer(buffer).unwrap();
assert!(cursor.fetch().unwrap().is_none());
}
#[test_case(MSSQL; "Microsoft SQL Server")]
fn output_parameter(profile: &Profile) {
let conn = profile.connection().unwrap();
conn.execute(
r#"
IF EXISTS (SELECT name FROM sysobjects WHERE name = 'TestOutputParam')
DROP PROCEDURE TestOutputParam
"#,
(),
)
.unwrap();
conn.execute(
r#"CREATE PROCEDURE TestOutputParam
@OutParm int OUTPUT
AS
SELECT @OutParm = @OutParm + 5
RETURN 99
"#,
(),
)
.unwrap();
let mut ret = Nullable::<i32>::null();
let mut param = Nullable::<i32>::new(7);
conn.execute(
"{? = call TestOutputParam(?)}",
(Out(&mut ret), InOut(&mut param)),
)
.unwrap();
assert_eq!(Some(99), ret.into_opt());
assert_eq!(Some(7 + 5), param.into_opt());
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn manual_commit_mode(profile: &Profile) {
let conn = profile
.setup_empty_table("ManualCommitMode", &["INTEGER"])
.unwrap();
conn.set_autocommit(false).unwrap();
conn.execute("INSERT INTO ManualCommitMode (a) VALUES (5);", ())
.unwrap();
conn.rollback().unwrap();
let cursor = conn
.execute("SELECT a FROM ManualCommitMode", ())
.unwrap()
.unwrap();
let actual = cursor_to_string(cursor);
assert_eq!(actual, "");
conn.execute("INSERT INTO ManualCommitMode (a) VALUES (42);", ())
.unwrap();
conn.commit().unwrap();
let cursor = conn
.execute("SELECT a FROM ManualCommitMode", ())
.unwrap()
.unwrap();
let actual = cursor_to_string(cursor);
assert_eq!(actual, "42");
conn.commit().unwrap();
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn unfinished_transaction(profile: &Profile) {
let conn = profile
.setup_empty_table("UnfinishedTransaction", &["INTEGER"])
.unwrap();
conn.set_autocommit(false).unwrap();
conn.execute("INSERT INTO UnfinishedTransaction (a) VALUES (5);", ())
.unwrap();
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
fn interior_nul(profile: &Profile) {
let conn = profile
.setup_empty_table("InteriorNul", &["VARCHAR(10)"])
.unwrap();
conn.execute(
"INSERT INTO InteriorNul (a) VALUES (?);",
&"a\0b".into_parameter(),
)
.unwrap();
let cursor = conn
.execute("SELECT A FROM InteriorNul;", ())
.unwrap()
.unwrap();
let actual = cursor_to_string(cursor);
let expected = "a\0b";
assert_eq!(expected, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn get_data_int(profile: &Profile) {
let conn = profile
.setup_empty_table("GetDataInt", &["INTEGER"])
.unwrap();
conn.execute("INSERT INTO GetDataInt (a) VALUES (42),(NULL)", ())
.unwrap();
let mut cursor = conn
.execute("SELECT a FROM GetDataInt", ())
.unwrap()
.unwrap();
let mut actual = Nullable::<i32>::null();
let mut row = cursor.next_row().unwrap().unwrap();
row.get_data(1, &mut actual).unwrap();
assert_eq!(Some(42), actual.into_opt());
row = cursor.next_row().unwrap().unwrap();
row.get_data(1, &mut actual).unwrap();
assert_eq!(None, actual.into_opt());
assert!(cursor.next_row().unwrap().is_none())
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn get_data_string(profile: &Profile) {
let table_name = "GetDataString";
let conn = profile
.setup_empty_table(table_name, &["Varchar(50)"])
.unwrap();
conn.execute(
&format!(
"INSERT INTO {} (a) VALUES ('Hello, World!'), (NULL)",
table_name
),
(),
)
.unwrap();
let mut cursor = conn
.execute(&format!("SELECT a FROM {} ORDER BY id", table_name), ())
.unwrap()
.unwrap();
let mut row = cursor.next_row().unwrap().unwrap();
let mut actual = VarCharArray::<32>::NULL;
row.get_data(1, &mut actual).unwrap();
assert_eq!(Some(&b"Hello, World!"[..]), actual.as_bytes());
row = cursor.next_row().unwrap().unwrap();
row.get_data(1, &mut actual).unwrap();
assert!(actual.as_bytes().is_none());
assert!(cursor.next_row().unwrap().is_none())
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn get_data_binary(profile: &Profile) {
let table_name = "GetDataBinary";
let conn = profile
.setup_empty_table(table_name, &["Varbinary(50)"])
.unwrap();
conn.execute(
&format!("INSERT INTO {} (a) VALUES (?), (NULL)", table_name),
&[1u8, 2, 3].into_parameter(),
)
.unwrap();
let mut cursor = conn
.execute(&format!("SELECT a FROM {} ORDER BY id", table_name), ())
.unwrap()
.unwrap();
let mut row = cursor.next_row().unwrap().unwrap();
let mut actual = VarBinaryArray::<32>::NULL;
row.get_data(1, &mut actual).unwrap();
assert_eq!(Some(&[1u8, 2, 3][..]), actual.as_bytes());
row = cursor.next_row().unwrap().unwrap();
row.get_data(1, &mut actual).unwrap();
assert!(actual.as_bytes().is_none());
assert!(cursor.next_row().unwrap().is_none())
}
#[test_case(MSSQL; "Microsoft SQL Server")]
fn large_strings(profile: &Profile) {
let conn = profile
.setup_empty_table("LargeStrings", &["Varchar(max)"])
.unwrap();
let input = String::from_utf8(vec![b'a'; 2000]).unwrap();
conn.execute(
"INSERT INTO LargeStrings (a) VALUES (?)",
&input.as_str().into_parameter(),
)
.unwrap();
let mut cursor = conn
.execute("SELECT a FROM LargeStrings ORDER BY id", ())
.unwrap()
.unwrap();
let mut row = cursor.next_row().unwrap().unwrap();
let mut buf = VarCharArray::<32>::NULL;
let mut actual = String::new();
loop {
row.get_data(1, &mut buf).unwrap();
actual += std::str::from_utf8(buf.as_bytes().unwrap()).unwrap();
if buf.is_complete() {
break;
}
}
assert_eq!(input, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
fn large_strings_get_text(profile: &Profile) {
let conn = profile
.setup_empty_table("LargeStringsGetText", &["Varchar(max)"])
.unwrap();
let input = String::from_utf8(vec![b'a'; 2000]).unwrap();
conn.execute(
"INSERT INTO LargeStringsGetText (a) VALUES (?)",
&input.as_str().into_parameter(),
)
.unwrap();
let mut cursor = conn
.execute("SELECT a FROM LargeStringsGetText ORDER BY id", ())
.unwrap()
.unwrap();
let mut row = cursor.next_row().unwrap().unwrap();
let mut actual = Vec::new();
row.get_text(1, &mut actual).unwrap();
assert_eq!(input, String::from_utf8(actual).unwrap());
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn short_strings_get_text(profile: &Profile) {
let conn = profile
.setup_empty_table("ShortStringsGetText", &["Varchar(15)"])
.unwrap();
conn.execute(
"INSERT INTO ShortStringsGetText (a) VALUES ('Hello, World!')",
(),
)
.unwrap();
let mut cursor = conn
.execute("SELECT a FROM ShortStringsGetText ORDER BY id", ())
.unwrap()
.unwrap();
let mut row = cursor.next_row().unwrap().unwrap();
let mut actual = Vec::with_capacity(100);
row.get_text(1, &mut actual).unwrap();
assert_eq!("Hello, World!", std::str::from_utf8(&actual).unwrap());
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn short_get_binary(profile: &Profile) {
let table_name = "ShortGetBinary";
let conn = profile
.setup_empty_table(table_name, &["Varbinary(15)"])
.unwrap();
conn.execute(
&format!("INSERT INTO {} (a) VALUES (?)", table_name),
&[1u8, 2, 3].into_parameter(),
)
.unwrap();
let mut cursor = conn
.execute(&format!("SELECT a FROM {} ORDER BY id", table_name), ())
.unwrap()
.unwrap();
let mut row = cursor.next_row().unwrap().unwrap();
let mut actual = Vec::with_capacity(100);
row.get_binary(1, &mut actual).unwrap();
assert_eq!(&[1u8, 2, 3][..], &actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
fn large_get_binary(profile: &Profile) {
let table_name = "LargeGetBinary";
let conn = profile
.setup_empty_table(table_name, &["Varbinary(max)"])
.unwrap();
let input = vec![42; 2000];
conn.execute(
&format!("INSERT INTO {} (a) VALUES (?)", table_name),
&input.as_slice().into_parameter(),
)
.unwrap();
let mut cursor = conn
.execute(&format!("SELECT a FROM {} ORDER BY id", table_name), ())
.unwrap()
.unwrap();
let mut row = cursor.next_row().unwrap().unwrap();
let mut actual = Vec::new();
row.get_binary(1, &mut actual).unwrap();
assert_eq!(input, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn capped_text_buffer(profile: &Profile) {
let table_name = "CappedTextBuffer";
let conn = profile
.setup_empty_table(table_name, &["VARCHAR(13)"])
.unwrap();
conn.execute(
&format!("INSERT INTO {} (a) VALUES ('Hello, World!');", table_name),
(),
)
.unwrap();
let cursor = conn
.execute(&format!("SELECT a FROM {} ORDER BY id", table_name), ())
.unwrap()
.unwrap();
let row_set_buffer = TextRowSet::for_cursor(1, &cursor, Some(5)).unwrap();
let mut row_set_cursor = cursor.bind_buffer(row_set_buffer).unwrap();
let batch = row_set_cursor.fetch().unwrap().unwrap();
let field = batch.at_as_str(0, 0).unwrap().unwrap();
assert_eq!("Hello", field);
assert_eq!(Indicator::Length(13), batch.indicator_at(0, 0));
assert_eq!(5, batch.max_len(0));
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn use_truncated_output_as_input(profile: &Profile) {
let table_name = "UseTruncatedOutputAsInput";
let conn = profile
.setup_empty_table(table_name, &["VARCHAR(13)"])
.unwrap();
conn.execute(
&format!("INSERT INTO {} (a) VALUES ('Hello, World!');", table_name),
(),
)
.unwrap();
let mut buf = VarCharArray::<5>::NULL;
let query = format!("SELECT a FROM {}", table_name);
let mut cursor = conn.execute(&query, ()).unwrap().unwrap();
let mut row = cursor.next_row().unwrap().unwrap();
row.get_data(1, &mut buf).unwrap();
assert_eq!(b"Hell", buf.as_bytes().unwrap());
assert!(!buf.is_complete());
drop(row);
drop(cursor);
let insert = format!("INSERT INTO {} (a) VALUES (?)", table_name);
buf.hide_truncation();
conn.execute(&insert, &buf).unwrap();
let actual = table_to_string(&conn, table_name, &["a"]);
assert_eq!("Hello, World!\nHell", actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(SQLITE_3; "SQLite 3")]
fn insert_truncated_value(profile: &Profile) {
let table_name = "InsertedTruncatedValue";
let conn = profile
.setup_empty_table(table_name, &["VARCHAR(50)"])
.unwrap();
let memory = "Hello\0INVALID MEMORY\0";
let valid = &memory.as_bytes()[..6];
let parameter = VarCharSlice::from_buffer(valid, Indicator::Length(memory.len()));
let result = conn.execute(
&format!("INSERT INTO {} (a) VALUES (?);", table_name),
¶meter,
);
match result {
Err(e) => {
eprintln!("{}", e)
}
Ok(None) => {
let actual = table_to_string(&conn, table_name, &["a"]);
assert_eq!("Hello", actual)
}
_ => panic!("Unexpected cursor"),
}
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(SQLITE_3; "SQLite 3")]
fn insert_truncated_var_char_array(profile: &Profile) {
let table_name = "InsertedTruncatedVarCharArray";
let conn = profile
.setup_empty_table(table_name, &["VARCHAR(50)"])
.unwrap();
let memory = "Hello, World!";
let parameter = VarCharArray::<5>::new(memory.as_bytes());
let result = conn.execute(
&format!("INSERT INTO {} (a) VALUES (?);", table_name),
¶meter,
);
match result {
Err(e) => {
eprintln!("{}", e)
}
Ok(None) => {
let actual = table_to_string(&conn, table_name, &["a"]);
eprintln!("{}", actual);
assert!(matches!(actual.as_str(), "Hello" | "Hell"))
}
_ => panic!("Unexpected cursor"),
}
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn arbitrary_input_parameters(profile: &Profile) {
let table_name = "ArbitraryInputParameters";
let conn = profile
.setup_empty_table(table_name, &["VARCHAR(20)", "INTEGER"])
.unwrap();
let insert_statement = format!("INSERT INTO {} (a, b) VALUES (?, ?);", table_name);
let param_a: Box<dyn InputParameter> = Box::new("Hello, World!".to_string().into_parameter());
let param_b: Box<dyn InputParameter> = Box::new(42.into_parameter());
let parameters = vec![param_a, param_b];
conn.execute(&insert_statement, parameters.as_slice())
.unwrap();
let actual = table_to_string(&conn, table_name, &["a", "b"]);
assert_eq!("Hello, World!,42", actual)
}
#[test]
fn synchronized_access_to_driver_and_data_source_info() {
let expected_drivers = ENV.drivers().unwrap();
let expected_data_sources = ENV.data_sources().unwrap();
const NUM_THREADS: usize = 5;
let threads = iter::repeat(())
.take(NUM_THREADS)
.map(|_| {
let expected_drivers = expected_drivers.clone();
let expected_data_sources = expected_data_sources.clone();
thread::spawn(move || {
let drivers = ENV.drivers().unwrap();
assert_eq!(expected_drivers, drivers);
let data_sources_for_thread = ENV.data_sources().unwrap();
assert_eq!(expected_data_sources, data_sources_for_thread);
})
})
.collect::<Vec<_>>();
for handle in threads {
handle.join().unwrap();
}
}
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn insert_large_texts(profile: &Profile) {
let table_name = "InsertLargeTexts";
let conn = profile.setup_empty_table(table_name, &["Text"]).unwrap();
let insert = format!("INSERT INTO {} (a) VALUES (?)", table_name);
let data = String::from_utf8(vec![b'a'; 8000]).unwrap();
conn.execute(&insert, &data.as_str().into_parameter())
.unwrap();
let actual = table_to_string(&conn, table_name, &["a"]);
assert_eq!(data.len(), actual.len());
assert!(data == actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn send_long_data_binary_vec(profile: &Profile) {
let table_name = "SendLongDataBinaryVec";
let conn = profile
.setup_empty_table(table_name, &[profile.blob_type])
.unwrap();
let input: Vec<_> = (0..12000).map(|i| (i % 256) as u8).collect();
let mut blob = BlobSlice::from_byte_slice(&input);
let insert = format!("INSERT INTO {} (a) VALUES (?)", table_name);
conn.execute(&insert, &mut blob.as_blob_param()).unwrap();
let select = format!("SELECT a FROM {}", table_name);
let mut result = conn.execute(&select, ()).unwrap().unwrap();
let mut row = result.next_row().unwrap().unwrap();
let mut output = Vec::new();
row.get_binary(1, &mut output).unwrap();
assert_eq!(input, output);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn send_long_data_string(profile: &Profile) {
let table_name = "SendLongDataString";
let conn = profile.setup_empty_table(table_name, &["Text"]).unwrap();
let input: String = (0..1200).map(|_| "abcdefghijklmnopqrstuvwxyz").collect();
let mut blob = BlobSlice::from_text(&input);
let insert = format!("INSERT INTO {} (a) VALUES (?)", table_name);
conn.execute(&insert, &mut blob.as_blob_param()).unwrap();
let select = format!("SELECT a FROM {}", table_name);
let mut result = conn.execute(&select, ()).unwrap().unwrap();
let mut row = result.next_row().unwrap().unwrap();
let mut output = Vec::new();
row.get_text(1, &mut output).unwrap();
let output = String::from_utf8(output).unwrap();
assert_eq!(input, output);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
fn send_long_data_binary_read(profile: &Profile) {
let table_name = "SendLongDataBinaryRead";
let conn = profile
.setup_empty_table(table_name, &[profile.blob_type])
.unwrap();
let input: Vec<_> = (0..12000).map(|i| (i % 256) as u8).collect();
let read = io::Cursor::new(&input);
let mut blob = BlobRead::with_upper_bound(read, 14000);
let insert = format!("INSERT INTO {} (a) VALUES (?)", table_name);
conn.execute(&insert, &mut blob.as_blob_param()).unwrap();
let select = format!("SELECT a FROM {}", table_name);
let mut result = conn.execute(&select, ()).unwrap().unwrap();
let mut row = result.next_row().unwrap().unwrap();
let mut output = Vec::new();
row.get_binary(1, &mut output).unwrap();
assert_eq!(input, output);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn send_long_data_binary_file(profile: &Profile) {
let table_name = "SendLongDataBinaryFile";
let conn = profile
.setup_empty_table(table_name, &[profile.blob_type])
.unwrap();
let input: Vec<_> = (0..12000).map(|i| (i % 256) as u8).collect();
let mut file = NamedTempFile::new().unwrap();
file.write_all(&input).unwrap();
let path = file.into_temp_path();
let mut blob = BlobRead::from_path(&path).unwrap();
let insert = format!("INSERT INTO {} (a) VALUES (?)", table_name);
conn.execute(&insert, &mut blob.as_blob_param()).unwrap();
let select = format!("SELECT a FROM {}", table_name);
let mut result = conn.execute(&select, ()).unwrap().unwrap();
let mut row = result.next_row().unwrap().unwrap();
let mut output = Vec::new();
row.get_binary(1, &mut output).unwrap();
assert_eq!(input, output);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn escape_hatch(profile: &Profile) {
let table_name = "EscapeHatch";
let conn = profile.setup_empty_table(table_name, &["INTEGER"]).unwrap();
let preallocated = conn.preallocate().unwrap();
let mut statement = preallocated.into_statement();
statement.reset_parameters().unwrap();
unsafe {
let select = U16String::from_str("SELECT * FROM EscapeHatch");
let ret = sys::SQLPrepareW(
statement.as_sys(),
select.as_ptr(),
select.len().try_into().unwrap(),
);
assert_eq!(ret, sys::SqlReturn::SUCCESS);
}
let hstmt = statement.into_sys();
unsafe {
let ret = sys::SQLFreeHandle(sys::HandleType::Stmt, hstmt as sys::Handle);
assert_eq!(ret, sys::SqlReturn::SUCCESS);
}
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn varchar_null(profile: &Profile) {
let table_name = "VarcharNull";
let conn = profile
.setup_empty_table(table_name, &["VARCHAR(10)"])
.unwrap();
let insert = format!("INSERT INTO {} (a) VALUES (?)", table_name);
conn.execute(&insert, &VarCharSlice::NULL).unwrap();
let actual = table_to_string(&conn, table_name, &["a"]);
assert_eq!("NULL", actual)
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn get_full_connection_string(profile: &Profile) {
let mut completed_connection_string = OutputStringBuffer::with_buffer_size(1024);
ENV.driver_connect(
profile.connection_string,
&mut completed_connection_string,
odbc_api::DriverCompleteOption::NoPrompt,
)
.unwrap();
assert!(!completed_connection_string.is_truncated());
let completed_connection_string = completed_connection_string.to_utf8();
eprintln!("Completed Connection String: {completed_connection_string}");
assert!(profile.connection_string.len() <= completed_connection_string.len());
}
#[test_case(MSSQL; "Microsoft SQL Server")]
fn get_full_connection_string_truncated(profile: &Profile) {
let mut completed_connection_string = OutputStringBuffer::with_buffer_size(2);
ENV.driver_connect(
profile.connection_string,
&mut completed_connection_string,
odbc_api::DriverCompleteOption::NoPrompt,
)
.unwrap();
eprintln!(
"Output connection string: {}",
completed_connection_string.to_utf8()
);
assert!(completed_connection_string.is_truncated());
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn driver_connect_with_empty_out_connection_sring(profile: &Profile) {
let mut completed_connection_string = OutputStringBuffer::empty();
ENV.driver_connect(
profile.connection_string,
&mut completed_connection_string,
odbc_api::DriverCompleteOption::NoPrompt,
)
.unwrap();
assert!(completed_connection_string.is_truncated());
assert!(completed_connection_string.to_utf8().is_empty());
}
#[test_case(MSSQL, "Microsoft SQL Server"; "Microsoft SQL Server")]
#[test_case(MARIADB, "MariaDB"; "Maria DB")]
#[test_case(SQLITE_3, "SQLite"; "SQLite 3")]
fn database_management_system_name(profile: &Profile, expected_name: &'static str) {
let conn = profile.connection().unwrap();
let actual_name = conn.database_management_system_name().unwrap();
assert_eq!(expected_name, actual_name);
}
#[test_case(MSSQL, 128, 128, 128, 128; "Microsoft SQL Server")]
#[test_case(MARIADB, 256, 0, 256, 255; "Maria DB")]
#[test_case(SQLITE_3, 255, 255, 255, 255; "SQLite 3")]
fn name_limits(
profile: &Profile,
expected_max_catalog_name_len: u16,
expected_max_schema_name_len: u16,
expected_max_table_name_len: u16,
expected_max_column_name_len: u16,
) {
let conn = profile.connection().unwrap();
assert_eq!(
conn.max_catalog_name_len().unwrap(),
expected_max_catalog_name_len
);
assert_eq!(
conn.max_schema_name_len().unwrap(),
expected_max_schema_name_len
);
assert_eq!(
conn.max_table_name_len().unwrap(),
expected_max_table_name_len
);
assert_eq!(
conn.max_column_name_len().unwrap(),
expected_max_column_name_len
);
}
#[test_case(MSSQL, "master"; "Microsoft SQL Server")]
#[test_case(MARIADB, "test_db"; "Maria DB")]
#[test_case(SQLITE_3, ""; "SQLite 3")]
fn current_catalog(profile: &Profile, expected_catalog: &str) {
let conn = profile.connection().unwrap();
assert_eq!(conn.current_catalog().unwrap(), expected_catalog);
}
#[test_case(MSSQL, "dbo"; "Microsoft SQL Server")]
#[test_case(MARIADB, ""; "Maria DB")]
#[test_case(SQLITE_3, "dbo"; "SQLite 3")]
fn columns_query(profile: &Profile, schema: &str) {
let table_name = "ColumnsQuery";
let conn = profile
.setup_empty_table(table_name, &["VARCHAR(10)"])
.unwrap();
let row_set_buffer = ColumnarAnyBuffer::try_from_description(
2,
conn.columns_buffer_description(255, 255, 255)
.unwrap()
.into_iter(),
)
.unwrap();
let columns = conn
.columns(&conn.current_catalog().unwrap(), schema, table_name, "a")
.unwrap();
let mut cursor = columns.bind_buffer(row_set_buffer).unwrap();
let batch = cursor.fetch().unwrap().unwrap();
const COLUMN_NAME_INDEX: usize = 3;
let column_names = batch.column(COLUMN_NAME_INDEX).as_text_view().unwrap();
const COLUMN_SIZE_INDEX: usize = 6;
let column_sizes = i32::as_nullable_slice(batch.column(COLUMN_SIZE_INDEX)).unwrap();
let column_has_name_a_and_size_10 = column_names
.iter()
.zip(column_sizes)
.any(|(name, size)| str::from_utf8(name.unwrap()).unwrap() == "a" && *size.unwrap() == 10);
assert!(column_has_name_a_and_size_10);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn fill_vec_of_rows(profile: &Profile) {
let table_name = "FillVecOfRows";
let conn = profile
.setup_empty_table(table_name, &["VARCHAR(50)", "INTEGER"])
.unwrap();
let insert_sql = format!("INSERT INTO {} (a,b) VALUES ('A', 1), ('B',2)", table_name);
conn.execute(&insert_sql, ()).unwrap();
let query_sql = format!("SELECT a,b FROM {}", table_name);
let cursor = conn.execute(&query_sql, ()).unwrap().unwrap();
let buf_desc = [
BufferDescription {
nullable: true,
kind: BufferKind::Text { max_str_len: 50 },
},
BufferDescription {
nullable: false,
kind: BufferKind::I32,
},
];
let buffer = ColumnarAnyBuffer::try_from_description(1, buf_desc.iter().copied()).unwrap();
let mut cursor = cursor.bind_buffer(buffer).unwrap();
let mut actual = Vec::new();
while let Some(batch) = cursor.fetch().unwrap() {
let col_a = batch.column(0).as_text_view().unwrap();
let col_b = i32::as_slice(batch.column(1)).unwrap();
for &b in col_b {
let a = col_a
.iter()
.next()
.unwrap()
.map(|bytes| str::from_utf8(bytes).unwrap().to_owned());
actual.push((a, b))
}
}
assert_eq!(
actual,
[(Some("A".to_string()), 1), (Some("B".to_string()), 2)]
)
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn no_data(profile: &Profile) {
let table_name = "NoData";
let conn = profile.setup_empty_table(table_name, &["INTEGER"]).unwrap();
let sql = format!("DELETE FROM {} WHERE id=5", table_name);
conn.execute(&sql, ()).unwrap();
conn.prepare(&sql).unwrap().execute(()).unwrap();
}
#[test_case(MSSQL, "master,dbo,ListTables,TABLE,NULL"; "Microsoft SQL Server")]
#[test_case(MARIADB, "test_db,NULL,ListTables,TABLE,"; "Maria DB")]
#[test_case(SQLITE_3, "NULL,NULL,ListTables,TABLE,NULL"; "SQLite 3")]
fn list_tables(profile: &Profile, expected: &str) {
let table_name = "ListTables";
let conn = profile.setup_empty_table(table_name, &["INTEGER"]).unwrap();
let cursor = conn.tables("", "", table_name, "").unwrap();
let actual = cursor_to_string(cursor).to_lowercase();
assert_eq!(expected.to_lowercase(), actual);
}
#[test_case(MSSQL, "master,dbo,ListTablesPreallocated,TABLE,NULL"; "Microsoft SQL Server")]
#[test_case(MARIADB, "test_db,NULL,ListTablesPreallocated,TABLE,"; "Maria DB")]
#[test_case(SQLITE_3, "NULL,NULL,ListTablesPreallocated,TABLE,NULL"; "SQLite 3")]
fn list_tables_preallocated(profile: &Profile, expected: &str) {
let table_name = "ListTablesPreallocated";
let conn = profile.setup_empty_table(table_name, &["INTEGER"]).unwrap();
let mut preallocated = conn.preallocate().unwrap();
let cursor = preallocated.tables("", "", table_name, "").unwrap();
let actual = cursor_to_string(cursor).to_lowercase();
assert_eq!(expected.to_lowercase(), actual);
}
#[test_case(MSSQL, "master,dbo,ListColumns,a,4,int,10,4,0,10,1,NULL,NULL,4,NULL,NULL,2,YES,0,0,0,0,NULL,NULL,NULL,NULL,NULL,NULL,38"; "Microsoft SQL Server")]
#[test_case(MARIADB, "test_db,NULL,ListColumns,a,4,INT,10,4,0,10,1,,NULL,4,NULL,2,2,YES"; "Maria DB")]
#[test_case(SQLITE_3, ",,ListColumns,a,4,INTEGER,9,10,10,0,1,NULL,NULL,4,NULL,16384,2,YES"; "SQLite 3")]
fn list_columns(profile: &Profile, expected: &str) {
let table_name = "ListColumns";
let conn = profile.setup_empty_table(table_name, &["INTEGER"]).unwrap();
let cursor = conn.columns("", "", table_name, "a").unwrap();
let actual = cursor_to_string(cursor).to_lowercase();
assert_eq!(expected.to_lowercase(), actual);
}
#[test_case(MSSQL, "master,dbo,ListColumnsPreallocated,a,4,int,10,4,0,10,1,NULL,NULL,4,NULL,NULL,2,YES,0,0,0,0,NULL,NULL,NULL,NULL,NULL,NULL,38"; "Microsoft SQL Server")]
#[test_case(MARIADB, "test_db,NULL,ListColumnsPreallocated,a,4,INT,10,4,0,10,1,,NULL,4,NULL,2,2,YES"; "Maria DB")]
#[test_case(SQLITE_3, ",,ListColumnsPreallocated,a,4,INTEGER,9,10,10,0,1,NULL,NULL,4,NULL,16384,2,YES"; "SQLite 3")]
fn list_columns_preallocated(profile: &Profile, expected: &str) {
let table_name = "ListColumnsPreallocated";
let conn = profile.setup_empty_table(table_name, &["INTEGER"]).unwrap();
let mut preallocated = conn.preallocate().unwrap();
let cursor = preallocated.columns("", "", table_name, "a").unwrap();
let actual = cursor_to_string(cursor).to_lowercase();
assert_eq!(expected.to_lowercase(), actual);
}
#[cfg(all(target_os = "windows", feature = "narrow"))]
const MARIADB_EXPECTED_ROW_SIZE_IN_BYTES: usize = 716025845;
#[cfg(not(all(target_os = "windows", feature = "narrow")))]
const MARIADB_EXPECTED_ROW_SIZE_IN_BYTES: usize = 537068874;
#[test_case(MSSQL, 10039; "Microsoft SQL Server")]
#[test_case(MARIADB, MARIADB_EXPECTED_ROW_SIZE_IN_BYTES; "Maria DB")]
#[test_case(SQLITE_3, 986; "SQLite 3")]
fn list_columns_oom(profile: &Profile, expected_row_size_in_bytes: usize) {
let conn = profile.connection().unwrap();
let table_name = "table_does_not_exist";
let cursor = conn.columns("", "", table_name, "").unwrap();
let mut column_description = ColumnDescription::default();
let mut size_of_row = 0;
for index in 0..cursor.num_result_cols().unwrap() {
cursor
.describe_col(index as u16 + 1, &mut column_description)
.unwrap();
let buffer_description = BufferDescription {
kind: BufferKind::from_data_type(column_description.data_type).unwrap(),
nullable: column_description.could_be_nullable(),
};
size_of_row += buffer_description.bytes_per_row();
}
assert_eq!(expected_row_size_in_bytes, size_of_row)
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn row_array_size_66536(profile: &Profile) {
let table_name = "RowArraySize66536";
let conn = profile.setup_empty_table(table_name, &["BIT"]).unwrap();
let sql = format!("SELECT a FROM {}", table_name);
let cursor = conn.execute(&sql, ()).unwrap().unwrap();
let row_set_buffer = ColumnarAnyBuffer::try_from_description(
u16::MAX as usize + 1,
iter::once(BufferDescription {
kind: BufferKind::Bit,
nullable: false,
}),
)
.unwrap();
assert!(cursor.bind_buffer(row_set_buffer).is_ok())
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn execute_query_twice_with_different_args_by_modifying_bound_param_buffer_on_stack(
profile: &Profile,
) {
let table_name = "ModifyingBoundParamBufferOnStack";
let conn = profile
.setup_empty_table(table_name, &["INTEGER", "INTEGER"])
.unwrap();
let insert = format!("INSERT INTO {} (a,b) VALUES (1,1), (2,2);", table_name);
conn.execute(&insert, ()).unwrap();
let query = format!("SELECT a FROM {} WHERE b=?;", table_name);
let prepared = conn.prepare(&query).unwrap();
let mut b = 1;
let mut prebound = prepared.bind_parameters(&mut b).unwrap();
let cursor = prebound.execute().unwrap().unwrap();
assert_eq!("1", cursor_to_string(cursor));
*prebound.params_mut() = 2;
let cursor = prebound.execute().unwrap().unwrap();
assert_eq!("2", cursor_to_string(cursor));
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn modifying_bound_param_buffer_on_heap(profile: &Profile) {
let table_name = "ModifyingBoundParamBufferOnHeap";
let conn = profile
.setup_empty_table(table_name, &["INTEGER", "INTEGER"])
.unwrap();
let insert = format!("INSERT INTO {} (a,b) VALUES (1,1), (2,2);", table_name);
conn.execute(&insert, ()).unwrap();
let query = format!("SELECT a FROM {} WHERE b=?;", table_name);
let prepared = conn.prepare(&query).unwrap();
let b = Box::new(1);
let mut prebound = prepared.bind_parameters(b).unwrap();
let cursor = prebound.execute().unwrap().unwrap();
assert_eq!("1", cursor_to_string(cursor));
*prebound.params_mut() = 2;
let cursor = prebound.execute().unwrap().unwrap();
assert_eq!("2", cursor_to_string(cursor));
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
#[should_panic(expected = "SQLFreeHandle failed with error code: -1")]
fn should_panic_if_connection_cannot_be_freed(profile: &Profile) {
let conn = profile.connection().unwrap();
let conn = conn.into_handle();
drop(conn);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
#[should_panic(expected = "original error")]
fn panic_in_drop_handlers_should_not_mask_original_error(profile: &Profile) {
let conn = profile.connection().unwrap();
let _conn = conn.into_handle();
panic!("original error")
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn memcopy_values_from_nullable_slice(profile: &Profile) {
let table_name = "MemcopyValuesFromNullableSlice";
let conn = profile.setup_empty_table(table_name, &["INTEGER"]).unwrap();
conn.execute(
&format!("INSERT INTO {table_name} (a) VALUES (42), (NULL), (5);"),
(),
)
.unwrap();
let cursor = conn
.execute(&format!("SELECT a FROM {table_name}"), ())
.unwrap() .unwrap(); let buffer = ColumnarAnyBuffer::try_from_description(
3,
iter::once(BufferDescription {
kind: BufferKind::I32,
nullable: true,
}),
)
.unwrap();
let mut cursor = cursor.bind_buffer(buffer).unwrap();
let batch = cursor.fetch().unwrap().unwrap();
let nullable_slice = batch.column(0).as_nullable_slice::<i32>().unwrap();
let (values, indicators) = nullable_slice.raw_values();
let values = values.to_vec();
let nulls: Vec<bool> = indicators
.iter()
.map(|&indicator| indicator == sys::NULL_DATA)
.collect();
assert!(!nulls[0]);
assert_eq!(values[0], 42);
assert!(nulls[1]);
assert!(!nulls[2]);
assert_eq!(values[2], 5);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn text_column_view_should_allow_for_filling_arrow_arrays(profile: &Profile) {
let table_name = "TextColumnViewShouldAllowForFillingArrowArrays";
let conn = profile
.setup_empty_table(table_name, &["VARCHAR(50)"])
.unwrap();
conn.execute(
&format!(
"INSERT INTO {table_name} (a) VALUES \
('abcd'), \
(NULL), \
('efghij'), \
('klm'), \
('npqrstu')"
),
(),
)
.unwrap();
let cursor = conn
.execute(&format!("SELECT a FROM {table_name}"), ())
.unwrap()
.unwrap();
let columnar_buffer = ColumnarAnyBuffer::try_from_description(
10,
iter::once(BufferDescription {
kind: BufferKind::Text { max_str_len: 50 },
nullable: true,
}),
)
.unwrap();
let mut cursor = cursor.bind_buffer(columnar_buffer).unwrap();
let batch = cursor.fetch().unwrap().unwrap();
let view = batch.column(0).as_text_view().unwrap();
let mut valid = Vec::with_capacity(view.len());
let mut offsets = Vec::with_capacity(view.len() + 1);
let mut offset: usize = 0;
for index in 0..view.len() {
offset += view.content_length_at(index).unwrap_or(0)
}
let mut consequtives_values = Vec::with_capacity(offset);
let raw_values_odbc = view.raw_value_buffer();
offset = 0;
for index in 0..view.len() {
offsets.push(offset);
if let Some(len) = view.content_length_at(index) {
valid.push(true);
offset += len;
let start_index = index * (view.max_len() + 1);
consequtives_values
.extend_from_slice(&raw_values_odbc[start_index..(start_index + len)])
} else {
valid.push(false);
}
}
assert_eq!(valid, [true, false, true, true, true]);
assert_eq!(offsets, [0, 4, 4, 10, 13]);
assert_eq!(consequtives_values, b"abcdefghijklmnpqrstu");
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
fn detect_truncated_output_in_bulk_fetch(profile: &Profile) {
let table_name = "DetectTruncatedOutputInBulkFetch";
let conn = profile
.setup_empty_table(table_name, &["VARCHAR(10)"])
.unwrap();
conn.execute(
&format!("INSERT INTO {table_name} (a) VALUES ('0123456789')"),
(),
)
.unwrap();
let buffer_description = BufferDescription {
nullable: true,
kind: BufferKind::Text { max_str_len: 5 },
};
let buffer =
ColumnarAnyBuffer::try_from_description(1, iter::once(buffer_description)).unwrap();
let query = format!("SELECT a FROM {table_name}");
let cursor = conn.execute(&query, ()).unwrap().unwrap();
let mut cursor = cursor.bind_buffer(buffer).unwrap();
matches!(cursor.fetch(), Err(Error::TooLargeValueForBuffer));
}
#[test]
#[ignore = "Runs for a very long time"]
fn many_diagnostic_messages() {
let table_name = "ManyDiagnosticMessages";
let conn = ENV
.connect_with_connection_string(MSSQL.connection_string)
.unwrap();
setup_empty_table(&conn, MSSQL.index_type, table_name, &["VARCHAR(2)"]).unwrap();
let batch_size = 2 << 15;
let mut buffer = TextRowSet::from_max_str_lens(batch_size, iter::once(2)).unwrap();
for _ in 0..batch_size {
buffer.append([Some(&b"ab"[..])].iter().cloned());
}
let insert_sql = format!("INSERT INTO {} (a) VALUES (?)", table_name);
conn.execute(&insert_sql, &buffer).unwrap();
let query_sql = format!("SELECT a FROM {}", table_name);
buffer = TextRowSet::from_max_str_lens(batch_size, iter::once(1)).unwrap();
let cursor = conn.execute(&query_sql, ()).unwrap().unwrap();
let mut row_set_cursor = cursor.bind_buffer(buffer).unwrap();
let _ = row_set_cursor.fetch();
}