mod common;
use test_case::test_case;
use common::{cursor_to_string, setup_empty_table, SingleColumnRowSetBuffer, ENV};
use odbc_api::{
buffers::{
AnyColumnView, AnyColumnViewMut, BufferDescription, BufferKind, ColumnarRowSet, TextRowSet,
},
parameter::VarChar32,
ColumnDescription, Cursor, DataType, IntoParameter, Nullability, Nullable, U16String,
};
use std::{iter, thread};
const MSSQL: &str =
"Driver={ODBC Driver 17 for SQL Server};Server=localhost;UID=SA;PWD=<YourStrong@Passw0rd>;";
#[cfg(target_os = "windows")]
const SQLITE_3: &str = "Driver={SQLite3 ODBC Driver};Database=sqlite-test.db";
#[cfg(not(target_os = "windows"))]
const SQLITE_3: &str = "Driver={SQLite3};Database=sqlite-test.db";
#[test]
#[should_panic]
fn insert_too_large_element_in_bin_column() {
let desc = BufferDescription {
kind: BufferKind::Binary { length: 1 },
nullable: true,
};
let mut buffer = ColumnarRowSet::new(10, iter::once(desc));
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 = ColumnarRowSet::new(10, iter::once(desc));
buffer.set_num_rows(1);
if let AnyColumnViewMut::Text(mut col) = buffer.column_mut(0) {
col.write(iter::once(Some(&b"too large input."[..])))
}
}
#[test]
fn bogus_connection_string() {
let conn = ENV.connect_with_connection_string("foobar");
assert!(matches!(conn, Err(_)));
}
#[test]
fn connect_to_movies_db() {
let _conn = ENV.connect_with_connection_string(MSSQL).unwrap();
}
#[test]
fn describe_columns() {
let conn = ENV.connect_with_connection_string(MSSQL).unwrap();
setup_empty_table(
&conn,
"DescribeColumns",
&[
"VARCHAR(255) NOT NULL",
"INTEGER",
"BINARY(12)",
"VARBINARY(100)",
],
)
.unwrap();
let sql = "SELECT a,b,c,d FROM DescribeColumns ORDER BY Id;";
let cursor = conn.execute(sql, ()).unwrap().unwrap();
assert_eq!(cursor.num_result_cols().unwrap(), 4);
let mut actual = ColumnDescription::default();
let desc = |name, data_type, nullability| ColumnDescription {
name: U16String::from_str(name).into_vec(),
data_type,
nullability,
};
let expected = desc("a", DataType::Varchar { length: 255 }, Nullability::NoNulls);
cursor.describe_col(1, &mut actual).unwrap();
assert_eq!(expected, actual);
let expected = desc("b", DataType::Integer, Nullability::Nullable);
cursor.describe_col(2, &mut actual).unwrap();
assert_eq!(expected, actual);
let expected = desc("c", DataType::Binary { length: 12 }, Nullability::Nullable);
cursor.describe_col(3, &mut actual).unwrap();
assert_eq!(expected, actual);
let expected = desc(
"d",
DataType::Varbinary { length: 100 },
Nullability::Nullable,
);
cursor.describe_col(4, &mut actual).unwrap();
assert_eq!(expected, actual);
}
#[test]
fn text_buffer() {
let query = "SELECT title, year FROM Movies ORDER BY year;";
let conn = ENV.connect_with_connection_string(MSSQL).unwrap();
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]
fn column_attributes() {
let conn = ENV.connect_with_connection_string(MSSQL).unwrap();
let sql = "SELECT title, year FROM Movies;";
let cursor = conn.execute(sql, ()).unwrap().unwrap();
let mut buf = Vec::new();
cursor.col_name(1, &mut buf).unwrap();
let buf = U16String::from_vec(buf);
assert_eq!("title", buf.to_string().unwrap());
let mut buf = buf.into_vec();
cursor.col_name(2, &mut buf).unwrap();
let name = U16String::from_vec(buf);
assert_eq!("year", name.to_string().unwrap());
}
#[test]
fn prices() {
let conn = ENV.connect_with_connection_string(MSSQL).unwrap();
let sql = "SELECT id,day,time,product,price FROM Sales ORDER BY id;";
let cursor = conn.execute(sql, ()).unwrap().unwrap();
let mut buf = Vec::new();
let mut name = |column_number| {
cursor.col_name(column_number, &mut buf).unwrap();
std::char::decode_utf16(buf.iter().copied())
.collect::<Result<String, _>>()
.unwrap()
};
assert_eq!("id", name(1));
assert_eq!("day", name(2));
assert_eq!("time", name(3));
assert_eq!("product", name(4));
assert_eq!("price", name(5));
assert_eq!(
DataType::Decimal {
precision: 10,
scale: 2
},
cursor.col_data_type(5).unwrap()
);
let batch_size = 10;
assert_eq!(DataType::Integer, cursor.col_data_type(1).unwrap());
let id_buffer = SingleColumnRowSetBuffer::new(batch_size);
let mut row_set_cursor = cursor.bind_buffer(id_buffer).unwrap();
assert_eq!(&[1, 2, 3], row_set_cursor.fetch().unwrap().unwrap().get());
}
#[test]
fn bind_char() {
let conn = ENV.connect_with_connection_string(MSSQL).unwrap();
let sql = "SELECT my_char FROM AllTheTypes;";
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"abcde"[..]), buf.value_at(0));
}
#[test]
fn bind_varchar() {
let conn = ENV.connect_with_connection_string(MSSQL).unwrap();
let sql = "SELECT my_varchar FROM AllTheTypes;";
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]
fn bind_numeric_to_float() {
let conn = ENV.connect_with_connection_string(MSSQL).unwrap();
let sql = "SELECT my_numeric FROM AllTheTypes;";
let cursor = conn.execute(sql, ()).unwrap().unwrap();
let buf = SingleColumnRowSetBuffer::new(1);
let mut row_set_cursor = cursor.bind_buffer(buf).unwrap();
assert_eq!(&[1.23], row_set_cursor.fetch().unwrap().unwrap().get());
}
#[test]
fn columnar_fetch_varbinary() {
let conn = ENV.connect_with_connection_string(MSSQL).unwrap();
setup_empty_table(&conn, "ColumnarFetchVarbinary", &["VARBINARY(10)"]).unwrap();
conn.execute(
"INSERT INTO ColumnarFetchVarbinary (a) Values \
(CONVERT(Varbinary(10), 'Hello')),\
(CONVERT(Varbinary(10), 'World')),\
(NULL)",
(),
)
.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 = ColumnarRowSet::new(10, iter::once(buffer_desc));
let mut cursor = cursor.bind_buffer(row_set_buffer).unwrap();
let batch = cursor.fetch().unwrap().unwrap();
let col_view = batch.column(0);
let mut col_it = if let AnyColumnView::Binary(col_it) = col_view {
col_it
} else {
panic!("Column View expected to be binary")
};
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]
fn columnar_fetch_binary() {
let conn = ENV.connect_with_connection_string(MSSQL).unwrap();
setup_empty_table(&conn, "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 = ColumnarRowSet::new(10, iter::once(buffer_desc));
let mut cursor = cursor.bind_buffer(row_set_buffer).unwrap();
let batch = cursor.fetch().unwrap().unwrap();
let col_view = batch.column(0);
let mut col_it = if let AnyColumnView::Binary(col_it) = col_view {
col_it
} else {
panic!("Column View expected to be binary")
};
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]
fn columnar_insert_varbinary() {
let conn = ENV.connect_with_connection_string(MSSQL).unwrap();
setup_empty_table(&conn, "ColumnarInsertVarbinary", &["VARBINARY(13)"]).unwrap();
let desc = BufferDescription {
kind: BufferKind::Binary { length: 5 },
nullable: true,
};
let mut buffer = ColumnarRowSet::new(10, iter::once(desc));
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);
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(SQLITE_3; "SQLite 3")]
fn columnar_insert_varchar(connection_string: &str) {
let conn = ENV
.connect_with_connection_string(connection_string)
.unwrap();
setup_empty_table(&conn, "ColumnarInsertVarchar", &["VARCHAR(13)"]).unwrap();
let desc = BufferDescription {
kind: BufferKind::Text { max_str_len: 5 },
nullable: true,
};
let mut buffer = ColumnarRowSet::new(10, iter::once(desc));
let input = [
Some(&b"Hello"[..]),
Some(&b"World"[..]),
None,
Some(&b"Hello, World!"[..]),
];
buffer.set_num_rows(input.len());
if let AnyColumnViewMut::Text(mut writer) = buffer.column_mut(0) {
writer.set_max_len(13);
writer.write(input.iter().copied());
} else {
panic!("Expected text column writer");
};
conn.execute("INSERT INTO ColumnarInsertVarchar (a) VALUES (?)", &buffer)
.unwrap();
let cursor = conn
.execute("SELECT a FROM ColumnarInsertVarchar ORDER BY Id", ())
.unwrap()
.unwrap();
let actual = cursor_to_string(cursor);
let expected = "Hello\nWorld\nNULL\nHello, World!";
assert_eq!(expected, actual);
}
#[test]
fn all_types() {
let conn = ENV.connect_with_connection_string(MSSQL).unwrap();
let sql = "SELECT my_char, my_numeric, my_varchar, my_float FROM AllTheTypes;";
let cursor = conn.execute(sql, ()).unwrap().unwrap();
let mut cd = ColumnDescription::default();
cursor.describe_col(1, &mut cd).unwrap();
assert_eq!(DataType::Char { length: 5 }, cd.data_type);
cursor.describe_col(2, &mut cd).unwrap();
assert_eq!(
DataType::Numeric {
precision: 3,
scale: 2
},
cd.data_type
);
cursor.describe_col(3, &mut cd).unwrap();
assert_eq!(DataType::Varchar { length: 100 }, cd.data_type);
cursor.describe_col(4, &mut cd).unwrap();
assert_eq!(DataType::Real, cd.data_type);
}
#[test]
fn bind_integer_parameter() {
let conn = ENV.connect_with_connection_string(MSSQL).unwrap();
let sql = "SELECT title FROM Movies where year=?;";
let cursor = conn.execute(sql, &1968).unwrap().unwrap();
let mut buffer = TextRowSet::for_cursor(1, &cursor).unwrap();
let mut cursor = cursor.bind_buffer(&mut buffer).unwrap();
let batch = cursor.fetch().unwrap().unwrap();
let title = batch.at_as_str(0, 0).unwrap().unwrap();
assert_eq!("2001: A Space Odyssey", title);
}
#[test]
fn prepared_statement() {
let conn = ENV.connect_with_connection_string(MSSQL).unwrap();
let sql = "SELECT title FROM Movies where year=?;";
let mut prepared = conn.prepare(sql).unwrap();
{
let cursor = prepared.execute(&1968).unwrap().unwrap();
let title = cursor_to_string(cursor);
assert_eq!("2001: A Space Odyssey", title);
}
{
let cursor = prepared.execute(&1993).unwrap().unwrap();
let title = cursor_to_string(cursor);
assert_eq!("Jurassic Park", title);
}
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(SQLITE_3; "SQLite 3")]
fn preallocated(connection_string: &str) {
let conn = ENV
.connect_with_connection_string(connection_string)
.unwrap();
setup_empty_table(&conn, "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(SQLITE_3; "SQLite 3")]
fn preallocation_soundness(connection_string: &str) {
let conn = ENV
.connect_with_connection_string(connection_string)
.unwrap();
setup_empty_table(&conn, "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]
fn integer_parameter_as_string() {
let conn = ENV.connect_with_connection_string(MSSQL).unwrap();
let sql = "SELECT title FROM Movies where year=?;";
let cursor = conn
.execute(sql, &"1968".into_parameter())
.unwrap()
.unwrap();
let mut buffer = TextRowSet::for_cursor(1, &cursor).unwrap();
let mut cursor = cursor.bind_buffer(&mut buffer).unwrap();
let batch = cursor.fetch().unwrap().unwrap();
let title = batch.at_as_str(0, 0).unwrap().unwrap();
assert_eq!("2001: A Space Odyssey", title);
}
#[test]
fn parameter_option_integer_some() {
let conn = ENV.connect_with_connection_string(MSSQL).unwrap();
let sql = "SELECT title FROM Movies where year=?;";
let cursor = conn
.execute(sql, &Some(1968).into_parameter())
.unwrap()
.unwrap();
let mut buffer = TextRowSet::for_cursor(1, &cursor).unwrap();
let mut cursor = cursor.bind_buffer(&mut buffer).unwrap();
let batch = cursor.fetch().unwrap().unwrap();
let title = batch.at_as_str(0, 0).unwrap().unwrap();
assert_eq!("2001: A Space Odyssey", title);
}
#[test]
fn parameter_option_integer_none() {
let conn = ENV.connect_with_connection_string(MSSQL).unwrap();
let sql = "SELECT title FROM Movies where year=?;";
let cursor = conn
.execute(sql, &None::<i32>.into_parameter())
.unwrap()
.unwrap();
let mut buffer = TextRowSet::for_cursor(1, &cursor).unwrap();
let mut cursor = cursor.bind_buffer(&mut buffer).unwrap();
assert!(cursor.fetch().unwrap().is_none());
}
#[test]
#[cfg(not(target_os = "windows"))] fn char() {
let conn = ENV.connect_with_connection_string(MSSQL).unwrap();
setup_empty_table(&conn, "Char", &["VARCHAR(1)"]).unwrap();
conn.execute("INSERT INTO CHAR (a) VALUES ('A'), ('Ü');", ())
.unwrap();
let sql = "SELECT a FROM Char ORDER BY id;";
let cursor = conn.execute(sql, ()).unwrap().unwrap();
let output = cursor_to_string(cursor);
assert_eq!("A\nÜ", output);
}
#[test]
#[cfg(not(target_os = "windows"))] fn wchar_as_char() {
let conn = ENV.connect_with_connection_string(MSSQL).unwrap();
setup_empty_table(&conn, "WCharAsChar", &["NVARCHAR(1)"]).unwrap();
conn.execute("INSERT INTO WCharAsChar (a) VALUES ('A'), ('Ü');", ())
.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]
fn two_parameters_in_tuple() {
let conn = ENV.connect_with_connection_string(MSSQL).unwrap();
let sql = "SELECT title FROM Movies where ? < year AND year < ?;";
let cursor = conn.execute(sql, (&1960, &1970)).unwrap().unwrap();
let mut buffer = TextRowSet::for_cursor(1, &cursor).unwrap();
let mut cursor = cursor.bind_buffer(&mut buffer).unwrap();
let batch = cursor.fetch().unwrap().unwrap();
let title = batch.at_as_str(0, 0).unwrap().unwrap();
assert_eq!("2001: A Space Odyssey", title);
}
#[test]
fn column_names_iterator() {
let conn = ENV.connect_with_connection_string(MSSQL).unwrap();
let sql = "SELECT title, year FROM Movies;";
let cursor = conn.execute(sql, ()).unwrap().unwrap();
let names: Vec<_> = cursor
.column_names()
.unwrap()
.collect::<Result<_, _>>()
.unwrap();
assert_eq!(&["title", "year"], names.as_slice());
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(SQLITE_3; "SQLite 3")]
fn bulk_insert_with_text_buffer(connection_string: &str) {
let conn = ENV
.connect_with_connection_string(connection_string)
.unwrap();
setup_empty_table(&conn, "BulkInsertWithTextBuffer", &["VARCHAR(50)"]).unwrap();
let mut prepared = conn
.prepare("INSERT INTO BulkInsertWithTextBuffer (a) Values (?)")
.unwrap();
let mut params = TextRowSet::new(5, [50].iter().copied());
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(SQLITE_3; "SQLite 3")]
fn bulk_insert_with_columnar_buffer(connection_string: &str) {
let conn = ENV
.connect_with_connection_string(connection_string)
.unwrap();
setup_empty_table(
&conn,
"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 = ColumnarRowSet::new(5, description);
params.set_num_rows(3);
let mut view_mut = params.column_mut(0);
match &mut view_mut {
AnyColumnViewMut::Text(col) => {
let input = ["England", "France", "Germany"];
col.write(input.iter().map(|&s| Some(s.as_bytes())))
}
_ => panic!("Unexpected column type"),
}
let mut view_mut = params.column_mut(1);
match &mut view_mut {
AnyColumnViewMut::NullableI32(col) => {
let input = [1, 2, 3];
col.write(input.iter().map(|&i| Some(i)))
}
_ => panic!("Unexpected column type"),
}
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]
fn send_connection() {
let conn = ENV.connect_with_connection_string(MSSQL).unwrap();
let conn = unsafe { conn.promote_to_send() };
let handle = thread::spawn(move || {
conn.execute("SELECT title FROM Movies ORDER BY year", ())
.unwrap()
.unwrap();
});
handle.join().unwrap();
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(SQLITE_3; "SQLite 3")]
fn parameter_option_str(connection_string: &str) {
let conn = ENV
.connect_with_connection_string(connection_string)
.unwrap();
setup_empty_table(&conn, "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();
let cursor = conn
.execute("SELECT a FROM ParameterOptionStr ORDER BY id", ())
.unwrap()
.unwrap();
let actual = cursor_to_string(cursor);
let expected = "NULL\nBernd";
assert_eq!(expected, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(SQLITE_3; "SQLite 3")]
fn parameter_varchar_512(connection_string: &str) {
let conn = ENV
.connect_with_connection_string(connection_string)
.unwrap();
setup_empty_table(&conn, "ParameterVarchar512", &["VARCHAR(50)"]).unwrap();
let sql = "INSERT INTO ParameterVarchar512 (a) VALUES (?);";
let mut prepared = conn.prepare(sql).unwrap();
prepared.execute(&VarChar32::copy_from_bytes(None)).unwrap();
prepared
.execute(&VarChar32::copy_from_bytes(Some(b"Bernd")))
.unwrap();
let cursor = conn
.execute("SELECT a FROM ParameterVarchar512 ORDER BY id", ())
.unwrap()
.unwrap();
let actual = cursor_to_string(cursor);
let expected = "NULL\nBernd";
assert_eq!(expected, actual);
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(SQLITE_3; "SQLite 3")]
fn read_into_columnar_buffer(connection_string: &str) {
let conn = ENV
.connect_with_connection_string(connection_string)
.unwrap();
setup_empty_table(&conn, "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 = ColumnarRowSet::new(20, buffer_description.iter().copied());
let mut cursor = cursor.bind_buffer(buffer).unwrap();
let batch = cursor.fetch().unwrap().unwrap();
match dbg!(batch.column(0)) {
AnyColumnView::NullableI32(mut col) => assert_eq!(Some(&42), col.next().unwrap()),
_ => panic!("Unexpected buffer type"),
}
match dbg!(batch.column(1)) {
AnyColumnView::Text(mut col) => {
assert_eq!(Some(&b"Hello, World!"[..]), col.next().unwrap())
}
_ => panic!("Unexpected buffer type"),
}
assert!(cursor.fetch().unwrap().is_none());
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(SQLITE_3; "SQLite 3")]
fn ignore_output_column(connection_string: &str) {
let conn = ENV
.connect_with_connection_string(connection_string)
.unwrap();
setup_empty_table(
&conn,
"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 = ColumnarRowSet::with_column_indices(20, [(1, bd), (3, bd)].iter().copied());
let mut cursor = cursor.bind_buffer(buffer).unwrap();
assert!(cursor.fetch().unwrap().is_none());
}
#[test]
fn output_parameter() {
use odbc_api::Out;
let mut ret = Nullable::<i32>::null();
let mut param = Nullable::<i32>::new(7);
let conn = ENV.connect_with_connection_string(MSSQL).unwrap();
conn.execute("{? = call TestParam(?)}", (Out(&mut ret), &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(SQLITE_3; "SQLite 3")]
fn manual_commit_mode(connection_string: &str) {
let conn = ENV
.connect_with_connection_string(connection_string)
.unwrap();
setup_empty_table(&conn, "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(SQLITE_3; "SQLite 3")]
fn unfinished_transaction(connection_string: &str) {
let conn = ENV
.connect_with_connection_string(connection_string)
.unwrap();
setup_empty_table(&conn, "UnfinishedTransaction", &["INTEGER"]).unwrap();
conn.set_autocommit(false).unwrap();
conn.execute("INSERT INTO UnfinishedTransaction (a) VALUES (5);", ())
.unwrap();
}
#[test]
fn interior_nul() {
let conn = ENV.connect_with_connection_string(MSSQL).unwrap();
setup_empty_table(&conn, "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(SQLITE_3; "SQLite 3")]
fn get_data_int(connection_string: &str) {
let conn = ENV
.connect_with_connection_string(connection_string)
.unwrap();
setup_empty_table(&conn, "GetDataInt", &["INTEGER"]).unwrap();
conn.execute("INSERT INTO GetDataInt (a) VALUES (42)", ())
.unwrap();
let mut cursor = conn
.execute("SELECT a FROM GetDataInt", ())
.unwrap()
.unwrap();
let mut row = cursor.next_row().unwrap().unwrap();
let mut actual = Nullable::<i32>::null();
row.get_data(1, &mut actual).unwrap();
assert_eq!(Some(42), actual.into_opt());
assert!(cursor.next_row().unwrap().is_none())
}
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(SQLITE_3; "SQLite 3")]
fn get_data_string(connection_string: &str) {
let conn = ENV
.connect_with_connection_string(connection_string)
.unwrap();
setup_empty_table(&conn, "GetDataString", &["Varchar(50)"]).unwrap();
conn.execute(
"INSERT INTO GetDataString (a) VALUES ('Hello, World!'), (NULL)",
(),
)
.unwrap();
let mut cursor = conn
.execute("SELECT a FROM GetDataString ORDER BY id", ())
.unwrap()
.unwrap();
let mut row = cursor.next_row().unwrap().unwrap();
let mut actual = VarChar32::copy_from_bytes(None);
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")]
fn large_strings(connection_string: &str) {
let conn = ENV
.connect_with_connection_string(connection_string)
.unwrap();
setup_empty_table(&conn, "LargeStrings", &["Varchar(max)"]).unwrap();
let input = String::from_utf8(vec![b'a'; 2000]).unwrap();
conn.execute(
"INSERT INTO LargeStrings (a) VALUES (?)",
&input.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 = VarChar32::copy_from_bytes(None);
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(connection_string: &str) {
let conn = ENV
.connect_with_connection_string(connection_string)
.unwrap();
setup_empty_table(&conn, "LargeStringsGetText", &["Varchar(max)"]).unwrap();
let input = String::from_utf8(vec![b'a'; 2000]).unwrap();
conn.execute(
"INSERT INTO LargeStringsGetText (a) VALUES (?)",
&input.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(SQLITE_3; "SQLite 3")]
fn short_strings_get_text(connection_string: &str) {
let conn = ENV
.connect_with_connection_string(connection_string)
.unwrap();
setup_empty_table(&conn, "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]
#[ignore = "Runs for a very long time"]
fn many_diagnostic_messages() {
let conn = ENV.connect_with_connection_string(MSSQL).unwrap();
setup_empty_table(&conn, "ManyDiagnosticMessages", &["VARCHAR(2)"]).unwrap();
let batch_size = 2 << 15;
let mut buffer = TextRowSet::new(batch_size, iter::once(2));
for _ in 0..batch_size {
buffer.append([Some(&b"ab"[..])].iter().cloned());
}
conn.execute("INSERT INTO ManyDiagnosticMessages (a) VALUES (?)", &buffer)
.unwrap();
buffer = TextRowSet::new(batch_size, iter::once(1));
let cursor = conn
.execute("SELECT a FROM ManyDiagnosticMessages", ())
.unwrap()
.unwrap();
let mut row_set_cursor = cursor.bind_buffer(buffer).unwrap();
let _ = row_set_cursor.fetch();
}