Skip to content

Commit

Permalink
Merge e67c2c6 into 73b8fbf
Browse files Browse the repository at this point in the history
  • Loading branch information
pacman82 committed Apr 12, 2023
2 parents 73b8fbf + e67c2c6 commit 12f7e31
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 30 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Changelog.md
@@ -1,5 +1,9 @@
# Changelog

## 0.56.2

* Support `U16Str` and `Option<U16Str>` as input parameter via `IntoParameter` trait.

## 0.56.1

* Fix: `Statement::complete_async` is now annotated to be only available in ODBC version 3.8. This missing annotation prevented compilation then specifying ODBC version 3.5.
Expand Down
2 changes: 1 addition & 1 deletion odbc-api/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "odbc-api"
version = "0.56.1"
version = "0.56.2"
authors = ["Markus Klein"]
edition = "2021"
license = "MIT"
Expand Down
27 changes: 25 additions & 2 deletions odbc-api/src/into_parameter.rs
@@ -1,7 +1,9 @@
use widestring::U16Str;

use crate::{
fixed_sized::Pod,
parameter::{InputParameter, VarBinaryBox, VarBinarySlice, VarCharBox, VarCharSlice},
Nullable,
parameter::{InputParameter, VarBinaryBox, VarBinarySlice, VarCharBox, VarCharSlice, VarWCharSlice},
Nullable, buffers::Indicator,
};

/// An instance can be consumed and to create a parameter which can be bound to a statement during
Expand Down Expand Up @@ -105,6 +107,27 @@ impl IntoParameter for Option<Vec<u8>> {
}
}

impl<'a> IntoParameter for &'a U16Str {
type Parameter = VarWCharSlice<'a>;

fn into_parameter(self) -> Self::Parameter {
let slice = self.as_slice();
let (_, bytes, _) = unsafe { slice.align_to::<u8>() };
VarWCharSlice::from_buffer(bytes, Indicator::Length(bytes.len()))
}
}

impl<'a> IntoParameter for Option<&'a U16Str> {
type Parameter = VarWCharSlice<'a>;

fn into_parameter(self) -> Self::Parameter {
match self {
Some(str) => str.into_parameter(),
None => VarWCharSlice::NULL,
}
}
}

impl<T> IntoParameter for Option<T>
where
T: Pod + InputParameter,
Expand Down
3 changes: 3 additions & 0 deletions odbc-api/src/parameter.rs
Expand Up @@ -346,6 +346,9 @@ pub use self::{
},
};

/// Currenty not made public due to unsure if it might not be better to implement this via u16
pub(crate) use self::varcell::VarWCharSlice;

use std::ffi::c_void;

use odbc_sys::CDataType;
Expand Down
21 changes: 21 additions & 0 deletions odbc-api/src/parameter/varcell.rs
Expand Up @@ -43,6 +43,24 @@ unsafe impl VarKind for Text {
}
}

/// Intended to be used as a generic argument for [`VariadicCell`] to declare that this buffer is
/// used to hold wide UTF-16 (as opposed to narrow ASCII or UTF-8) text. Use this to annotate binary
/// buffers (`[u8]`), rather than `[u16]` buffers.
pub struct WideTextBytes;

unsafe impl VarKind for WideTextBytes {
const TERMINATING_ZEROES: usize = 2;
const C_DATA_TYPE: CDataType = CDataType::WChar;

fn relational_type(length: usize) -> DataType {
// Since we might use as an input buffer, we report the full buffer length in the type and
// do not deduct 2 for the terminating zero.
//
// Also the length is in bytes and needs to be converted to characters.
DataType::WVarchar { length: length / 2 }
}
}

/// Intended to be used as a generic argument for [`VariadicCell`] to declare that this buffer is
/// used to hold raw binary input.
pub struct Binary;
Expand Down Expand Up @@ -88,6 +106,7 @@ pub struct VarCell<B, K> {

pub type VarBinary<B> = VarCell<B, Binary>;
pub type VarChar<B> = VarCell<B, Text>;
pub type VarWChar<B> = VarCell<B, WideTextBytes>;

/// Parameter type for owned, variable sized character data.
///
Expand Down Expand Up @@ -335,6 +354,8 @@ where
/// ```
pub type VarCharSlice<'a> = VarChar<&'a [u8]>;

pub type VarWCharSlice<'a> = VarWChar<&'a [u8]>;

/// Binds a byte array as a variadic binary input parameter.
///
/// While a byte array can provide us with a pointer to the start of the array and the length of the
Expand Down
59 changes: 34 additions & 25 deletions odbc-api/tests/integration.rs
Expand Up @@ -324,35 +324,11 @@ fn column_name(profile: &Profile) {
assert_eq!("b", desc.name_to_string().unwrap());
}

/// Bind a CHAR column to a character buffer.
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
#[test_case(POSTGRES; "PostgreSQL")]
fn bind_char(profile: &Profile) {
let table_name = table_name!();
let (conn, table) = profile.given(&table_name, &["CHAR(5)"]).unwrap();
let insert_sql = table.sql_insert();
conn.execute(&insert_sql, &"Hello".into_parameter())
.unwrap();

let cursor = conn
.execute(&table.sql_all_ordered_by_id(), ())
.unwrap()
.unwrap();
let mut buf = ColumnarBuffer::new(vec![(1, TextColumn::new(1, 5))]);
let mut row_set_cursor = cursor.bind_buffer(&mut buf).unwrap();
let batch = row_set_cursor.fetch().unwrap().unwrap();

assert_eq!(Some(&b"Hello"[..]), batch.column(0).get(0));
}

/// Bind a CHAR column to a wchar buffer
#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
#[test_case(POSTGRES; "PostgreSQL")]
fn bind_char_to_wchar(profile: &Profile) {
fn bind_wide_column_to_char(profile: &Profile) {
let table_name = table_name!();
let (conn, table) = profile.given(&table_name, &["CHAR(5)"]).unwrap();
let insert_sql = table.sql_insert();
Expand Down Expand Up @@ -1451,6 +1427,39 @@ fn wchar_as_char(profile: &Profile) {
assert_eq!("A\nÜ", table.content_as_string(&conn));
}

#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
#[test_case(POSTGRES; "PostgreSQL")]
fn bind_str_parameter_to_char(profile: &Profile) {
let table_name = table_name!();
let (conn, table) = profile.given(&table_name, &["CHAR(5)"]).unwrap();
let insert_sql = table.sql_insert();

conn.execute(&insert_sql, &"Hello".into_parameter())
.unwrap();

let actual = table.content_as_string(&conn);
assert_eq!("Hello", actual);
}

#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
#[test_case(POSTGRES; "PostgreSQL")]
fn bind_u16str_parameter_to_char(profile: &Profile) {
let table_name = table_name!();
let (conn, table) = profile.given(&table_name, &["CHAR(5)"]).unwrap();
let insert_sql = table.sql_insert();

let hello = U16String::from_str("Hello");
conn.execute(&insert_sql, &hello.into_parameter())
.unwrap();

let actual = table.content_as_string(&conn);
assert_eq!("Hello", &actual);
}

#[test_case(MSSQL; "Microsoft SQL Server")]
#[test_case(MARIADB; "Maria DB")]
#[test_case(SQLITE_3; "SQLite 3")]
Expand Down
2 changes: 1 addition & 1 deletion odbcsv/Cargo.toml
Expand Up @@ -29,7 +29,7 @@ readme = "Readme.md"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
odbc-api = { version = "0.56.1", path = "../odbc-api" }
odbc-api = { version = "0.56.2", path = "../odbc-api" }
csv = "1.2.1"
anyhow = "1.0.70"
stderrlog = "0.5.4"
Expand Down

0 comments on commit 12f7e31

Please sign in to comment.