mod types;
mod input;
mod output;
mod prepare;
pub use self::output::Output;
use {ffi, safe, Connection, Return, Result, Raii, Handle};
use ffi::SQLRETURN::*;
use ffi::Nullable;
use std::marker::PhantomData;
pub use self::types::OdbcType;
pub use self::types::{SqlDate, SqlTime, SqlSsTime2, SqlTimestamp};
const CHUNK_LEN: usize = 64;
struct Chunks<T>(Vec<Box<[T; CHUNK_LEN]>>);
impl<T: Copy + Default> Chunks<T> {
fn new() -> Chunks<T> {
Chunks(Vec::new())
}
fn alloc(&mut self, i: usize, value: T) -> *mut T {
let chunk_no = i / CHUNK_LEN;
if self.0.len() <= chunk_no {
self.0.resize(chunk_no + 1, Box::new([T::default(); CHUNK_LEN]))
}
let v = self.0[chunk_no].get_mut(i % CHUNK_LEN).unwrap();
*v = value;
v as *mut T
}
fn clear(&mut self) {
self.0.clear()
}
}
pub enum Allocated {}
pub type Executed = Allocated;
pub enum Prepared {}
pub enum HasResult {}
pub enum NoResult {}
pub enum ResultSetState<'a, 'b, S> {
Data(Statement<'a, 'b, S, HasResult>),
NoData(Statement<'a, 'b, S, NoResult>),
}
pub use ResultSetState::*;
pub struct Statement<'con, 'b, S, R> {
raii: Raii<ffi::Stmt>,
parent: PhantomData<&'con Connection<'con>>,
state: PhantomData<S>,
result: PhantomData<R>,
parameters: PhantomData<&'b [u8]>,
param_ind_buffers: Chunks<ffi::SQLLEN>,
}
pub struct Cursor<'a, 'b: 'a, 'c: 'a, S: 'a> {
stmt: &'a mut Statement<'b, 'c, S, HasResult>,
buffer: Vec<u8>,
}
#[derive(Debug, Clone)]
pub struct ColumnDescriptor {
pub name: String,
pub data_type: ffi::SqlDataType,
pub column_size: Option<ffi::SQLULEN>,
pub decimal_digits: Option<u16>,
pub nullable: Option<bool>,
}
impl<'a, 'b, S, R> Handle for Statement<'a, 'b, S, R> {
type To = ffi::Stmt;
unsafe fn handle(&self) -> ffi::SQLHSTMT {
self.raii.handle()
}
}
impl<'a, 'b, S, R> Statement<'a, 'b, S, R> {
fn with_raii(raii: Raii<ffi::Stmt>) -> Self {
Statement {
raii: raii,
parent: PhantomData,
state: PhantomData,
result: PhantomData,
parameters: PhantomData,
param_ind_buffers: Chunks::new()
}
}
}
impl<'a, 'b, 'env> Statement<'a, 'b, Allocated, NoResult> {
pub fn with_parent(ds: &'a Connection<'env>) -> Result<Self> {
let raii = Raii::with_parent(ds).into_result(ds)?;
Ok(Self::with_raii(raii))
}
pub fn affected_row_count(&self) -> Result<ffi::SQLLEN> {
self.raii.affected_row_count().into_result(self)
}
pub fn tables(mut self, catalog_name: &String, schema_name: &String, table_name: &String, table_type: &String) -> Result<Statement<'a, 'b, Executed, HasResult>> {
self.raii.tables(catalog_name, schema_name, table_name, table_type).into_result(&self)?;
Ok(Statement::with_raii(self.raii))
}
pub fn tables_str(self, catalog_name: &str, schema_name: &str, table_name: &str, table_type: &str) -> Result<Statement<'a, 'b, Executed, HasResult>> {
self.tables(&catalog_name.to_owned(), &schema_name.to_owned(), &table_name.to_owned(), &table_type.to_owned())
}
pub fn exec_direct(mut self, statement_text: &str) -> Result<ResultSetState<'a, 'b, Executed>> {
if self.raii.exec_direct(statement_text).into_result(&self)? {
let num_cols = self.raii.num_result_cols().into_result(&self)?;
if num_cols > 0 {
Ok(ResultSetState::Data(Statement::with_raii(self.raii)))
} else {
Ok(ResultSetState::NoData(Statement::with_raii(self.raii)))
}
} else {
Ok(ResultSetState::NoData(Statement::with_raii(self.raii)))
}
}
}
impl<'a, 'b, S> Statement<'a, 'b, S, HasResult> {
pub fn affected_row_count(&self) -> Result<ffi::SQLLEN> {
self.raii.affected_row_count().into_result(self)
}
pub fn num_result_cols(&self) -> Result<i16> {
self.raii.num_result_cols().into_result(self)
}
pub fn describe_col(&self, idx: u16) -> Result<ColumnDescriptor> {
self.raii.describe_col(idx).into_result(self)
}
pub fn fetch<'c>(&'c mut self) -> Result<Option<Cursor<'c, 'a, 'b, S>>> {
if self.raii.fetch().into_result(self)? {
Ok(Some(Cursor {
stmt: self,
buffer: vec![0; 512],
}))
} else {
Ok(None)
}
}
pub fn close_cursor(mut self) -> Result<Statement<'a, 'b, S, NoResult>> {
self.raii.close_cursor().into_result(&self)?;
Ok(Statement::with_raii(self.raii))
}
}
impl<'a, 'b, 'c, S> Cursor<'a, 'b, 'c, S> {
pub fn get_data<'d, T>(&'d mut self, col_or_param_num: u16) -> Result<Option<T>>
where
T: Output<'d>,
{
T::get_data(&mut self.stmt.raii, col_or_param_num, &mut self.buffer).into_result(self.stmt)
}
}
impl Raii<ffi::Stmt> {
fn affected_row_count(&self) -> Return<ffi::SQLLEN> {
let mut count: ffi::SQLLEN = 0;
unsafe {
match ffi::SQLRowCount(self.handle(), &mut count as *mut ffi::SQLLEN) {
SQL_SUCCESS => Return::Success(count),
SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(count),
SQL_ERROR => Return::Error,
r => panic!("SQLRowCount returned unexpected result: {:?}", r),
}
}
}
fn num_result_cols(&self) -> Return<i16> {
let mut num_cols: ffi::SQLSMALLINT = 0;
unsafe {
match ffi::SQLNumResultCols(self.handle(), &mut num_cols as *mut ffi::SQLSMALLINT) {
SQL_SUCCESS => Return::Success(num_cols),
SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(num_cols),
SQL_ERROR => Return::Error,
r => panic!("SQLNumResultCols returned unexpected result: {:?}", r),
}
}
}
fn describe_col(&self, idx: u16) -> Return<ColumnDescriptor> {
let mut name_buffer: [u8; 512] = [0; 512];
let mut name_length: ffi::SQLSMALLINT = 0;
let mut data_type: ffi::SqlDataType = ffi::SqlDataType::SQL_UNKNOWN_TYPE;
let mut column_size: ffi::SQLULEN = 0;
let mut decimal_digits: ffi::SQLSMALLINT = 0;
let mut nullable: Nullable = Nullable::SQL_NULLABLE_UNKNOWN;
unsafe {
match ffi::SQLDescribeCol(
self.handle(),
idx,
name_buffer.as_mut_ptr(),
name_buffer.len() as ffi::SQLSMALLINT,
&mut name_length as *mut ffi::SQLSMALLINT,
&mut data_type as *mut ffi::SqlDataType,
&mut column_size as *mut ffi::SQLULEN,
&mut decimal_digits as *mut ffi::SQLSMALLINT,
&mut nullable as *mut ffi::Nullable,
) {
SQL_SUCCESS => Return::Success(ColumnDescriptor {
name: ::std::str::from_utf8(&name_buffer[..(name_length as usize)])
.unwrap()
.to_owned(),
data_type: data_type,
column_size: if column_size == 0 {
None
} else {
Some(column_size)
},
decimal_digits: if decimal_digits == 0 {
None
} else {
Some(decimal_digits as u16)
},
nullable: match nullable {
Nullable::SQL_NULLABLE_UNKNOWN => None,
Nullable::SQL_NULLABLE => Some(true),
Nullable::SQL_NO_NULLS => Some(false),
},
}),
SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(ColumnDescriptor {
name: ::std::str::from_utf8(&name_buffer[..(name_length as usize)])
.unwrap()
.to_owned(),
data_type: data_type,
column_size: if column_size == 0 {
None
} else {
Some(column_size)
},
decimal_digits: if decimal_digits == 0 {
None
} else {
Some(decimal_digits as u16)
},
nullable: match nullable {
Nullable::SQL_NULLABLE_UNKNOWN => None,
Nullable::SQL_NULLABLE => Some(true),
Nullable::SQL_NO_NULLS => Some(false),
},
}),
SQL_ERROR => Return::Error,
r => panic!("SQLDescribeCol returned unexpected result: {:?}", r),
}
}
}
fn exec_direct(&mut self, statement_text: &str) -> Return<bool> {
let length = statement_text.len();
if length > ffi::SQLINTEGER::max_value() as usize {
panic!("Statement text too long");
}
match unsafe {
ffi::SQLExecDirect(
self.handle(),
statement_text.as_ptr(),
length as ffi::SQLINTEGER,
)
} {
ffi::SQL_SUCCESS => Return::Success(true),
ffi::SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(true),
ffi::SQL_ERROR => Return::Error,
ffi::SQL_NEED_DATA => panic!("SQLExecDirec returned SQL_NEED_DATA"),
ffi::SQL_NO_DATA => Return::Success(false),
r => panic!("SQLExecDirect returned unexpected result: {:?}", r),
}
}
fn fetch(&mut self) -> Return<bool> {
match unsafe { ffi::SQLFetch(self.handle()) } {
ffi::SQL_SUCCESS => Return::Success(true),
ffi::SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(true),
ffi::SQL_ERROR => Return::Error,
ffi::SQL_NO_DATA => Return::Success(false),
r => panic!("SQLFetch returned unexpected result: {:?}", r),
}
}
fn tables(&mut self, catalog_name: &String, schema_name: &String, table_name: &String, table_type: &String) -> Return<()> {
unsafe {
match ffi::SQLTables(
self.handle(),
catalog_name.as_ptr(),
catalog_name.as_bytes().len() as ffi::SQLSMALLINT,
schema_name.as_ptr(),
schema_name.as_bytes().len() as ffi::SQLSMALLINT,
table_name.as_ptr(),
table_name.as_bytes().len() as ffi::SQLSMALLINT,
table_type.as_ptr(),
table_type.as_bytes().len() as ffi::SQLSMALLINT,
) {
SQL_SUCCESS => Return::Success(()),
SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(()),
SQL_ERROR => Return::Error,
r => panic!("SQLTables returned: {:?}", r),
}
}
}
fn close_cursor(&mut self) -> Return<()> {
unsafe {
match ffi::SQLCloseCursor(self.handle()) {
ffi::SQL_SUCCESS => Return::Success(()),
ffi::SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(()),
ffi::SQL_ERROR => Return::Error,
r => panic!("unexpected return value from SQLCloseCursor: {:?}", r),
}
}
}
}
unsafe impl<'con, 'param, C, P> safe::Handle for Statement<'con, 'param, C, P> {
const HANDLE_TYPE : ffi::HandleType = ffi::SQL_HANDLE_STMT;
fn handle(&self) -> ffi::SQLHANDLE {
<Raii<ffi::Stmt> as safe::Handle>::handle(&self.raii)
}
}