1
2mod types;
3mod input;
4mod output;
5mod prepare;
6pub use self::output::Output;
7use {ffi, safe, Connection, Return, Result, Raii, Handle};
8use ffi::SQLRETURN::*;
9use ffi::Nullable;
10use std::marker::PhantomData;
11pub use self::types::OdbcType;
12pub use self::types::{SqlDate, SqlTime, SqlSsTime2, SqlTimestamp, EncodedValue};
13
14const CHUNK_LEN: usize = 64;
16struct Chunks<T>(Vec<Box<[T; CHUNK_LEN]>>);
17
18impl<T: Copy + Default> Chunks<T> {
20 fn new() -> Chunks<T> {
21 Chunks(Vec::new())
22 }
23
24 fn alloc(&mut self, i: usize, value: T) -> *mut T {
25 let chunk_no = i / CHUNK_LEN;
26 if self.0.len() <= chunk_no {
27 self.0.resize(chunk_no + 1, Box::new([T::default(); CHUNK_LEN]))
29 }
30 let v = self.0[chunk_no].get_mut(i % CHUNK_LEN).unwrap();
31 *v = value;
32 v as *mut T
33 }
34
35 fn clear(&mut self) {
36 self.0.clear()
37 }
38}
39
40pub enum Allocated {}
42pub type Executed = Allocated;
45pub enum Prepared {}
48pub enum HasResult {}
51pub enum NoResult {}
54
55pub enum ResultSetState<'a, 'b, S, AC: AutocommitMode> {
61 Data(Statement<'a, 'b, S, HasResult, AC>),
62 NoData(Statement<'a, 'b, S, NoResult, AC>),
63}
64pub use ResultSetState::*;
65use std::ptr::null_mut;
66use odbc_safe::AutocommitMode;
67
68pub struct Statement<'a, 'b, S, R, AC: AutocommitMode> {
70 raii: Raii<'a, ffi::Stmt>,
71 state: PhantomData<S>,
72 autocommit_mode: PhantomData<AC>,
73 result: PhantomData<R>,
75 parameters: PhantomData<&'b [u8]>,
76 param_ind_buffers: Chunks<ffi::SQLLEN>,
77 encoded_values: Vec<EncodedValue>,
79}
80
81pub struct Cursor<'s, 'a: 's, 'b: 's, S: 's, AC: AutocommitMode> {
83 stmt: &'s mut Statement<'a, 'b, S, HasResult, AC>,
84 buffer: Vec<u8>,
85}
86
87#[derive(Clone, Debug, Eq, PartialEq)]
88pub struct ColumnDescriptor {
89 pub name: String,
90 pub data_type: ffi::SqlDataType,
91 pub column_size: Option<ffi::SQLULEN>,
92 pub decimal_digits: Option<u16>,
93 pub nullable: Option<bool>,
94}
95
96impl<'a, 'b, S, R, AC: AutocommitMode> Handle for Statement<'a, 'b, S, R, AC> {
97 type To = ffi::Stmt;
98 unsafe fn handle(&self) -> ffi::SQLHSTMT {
99 self.raii.handle()
100 }
101}
102
103impl<'a, 'b, S, R, AC: AutocommitMode> Statement<'a, 'b, S, R, AC> {
104 fn with_raii(raii: Raii<'a, ffi::Stmt>) -> Self {
105 Statement {
106 raii: raii,
107 autocommit_mode: PhantomData,
108 state: PhantomData,
109 result: PhantomData,
110 parameters: PhantomData,
111 param_ind_buffers: Chunks::new(),
112 encoded_values: Vec::new(),
113 }
114 }
115}
116
117impl<'a, 'b, 'env, AC: AutocommitMode> Statement<'a, 'b, Allocated, NoResult, AC> {
118 pub fn with_parent(ds: &'a Connection<'env, AC>) -> Result<Self> {
119 let raii = Raii::with_parent(ds).into_result(ds)?;
120 Ok(Self::with_raii(raii))
121 }
122
123 pub fn affected_row_count(&self) -> Result<ffi::SQLLEN> {
124 self.raii.affected_row_count().into_result(self)
125 }
126
127 pub fn tables(self, catalog_name: &String, schema_name: &String, table_name: &String, table_type: &String) -> Result<Statement<'a, 'b, Executed, HasResult, AC>> {
128 self.tables_str(catalog_name.as_str(), schema_name.as_str(), table_name.as_str(), table_type.as_str())
129 }
130
131 pub fn tables_str(self, catalog_name: &str, schema_name: &str, table_name: &str, table_type: &str) -> Result<Statement<'a, 'b, Executed, HasResult, AC>> {
132 self.tables_opt_str(Option::Some(catalog_name), Option::Some(schema_name), Option::Some(table_name), table_type)
133 }
134
135 pub fn tables_opt_str(mut self, catalog_name: Option<&str>, schema_name: Option<&str>, table_name:Option<&str>, table_type: &str) -> Result<Statement<'a, 'b, Executed, HasResult, AC>> {
136 self.raii.tables(catalog_name, schema_name, table_name, table_type).into_result(&self)?;
137 Ok(Statement::with_raii(self.raii))
138 }
139
140 pub fn exec_direct(mut self, statement_text: &str) -> Result<ResultSetState<'a, 'b, Executed, AC>> {
145 if self.raii.exec_direct(statement_text).into_result(&self)? {
146 let num_cols = self.raii.num_result_cols().into_result(&self)?;
147 if num_cols > 0 {
148 Ok(ResultSetState::Data(Statement::with_raii(self.raii)))
149 } else {
150 Ok(ResultSetState::NoData(Statement::with_raii(self.raii)))
151 }
152 } else {
153 Ok(ResultSetState::NoData(Statement::with_raii(self.raii)))
154 }
155 }
156
157 pub fn exec_direct_bytes(mut self, bytes: &[u8]) -> Result<ResultSetState<'a, 'b, Executed, AC>> {
162 if self.raii.exec_direct_bytes(bytes).into_result(&self)? {
163 let num_cols = self.raii.num_result_cols().into_result(&self)?;
164 if num_cols > 0 {
165 Ok(ResultSetState::Data(Statement::with_raii(self.raii)))
166 } else {
167 Ok(ResultSetState::NoData(Statement::with_raii(self.raii)))
168 }
169 } else {
170 Ok(ResultSetState::NoData(Statement::with_raii(self.raii)))
171 }
172 }
173}
174
175impl<'a, 'b, S, AC: AutocommitMode> Statement<'a, 'b, S, HasResult, AC> {
176
177 pub fn affected_row_count(&self) -> Result<ffi::SQLLEN> {
178 self.raii.affected_row_count().into_result(self)
179 }
180
181 pub fn num_result_cols(&self) -> Result<i16> {
186 self.raii.num_result_cols().into_result(self)
187 }
188
189 pub fn describe_col(&self, idx: u16) -> Result<ColumnDescriptor> {
191 self.raii.describe_col(idx).into_result(self)
192 }
193
194 pub fn fetch<'s>(&'s mut self) -> Result<Option<Cursor<'s, 'a, 'b, S, AC>>> {
196 if self.raii.fetch().into_result(self)? {
197 Ok(Some(Cursor {
198 stmt: self,
199 buffer: vec![0; 512],
200 }))
201 } else {
202 Ok(None)
203 }
204 }
205
206 pub fn close_cursor(mut self) -> Result<Statement<'a, 'b, S, NoResult, AC>> {
234 self.raii.close_cursor().into_result(&self)?;
235 Ok(Statement::with_raii(self.raii))
236 }
237}
238
239impl<'a, 'b, 'c, S, AC: AutocommitMode> Cursor<'a, 'b, 'c, S, AC> {
240 pub fn get_data<'d, T>(&'d mut self, col_or_param_num: u16) -> Result<Option<T>>
247 where
248 T: Output<'d>,
249 {
250 T::get_data(&mut self.stmt.raii, col_or_param_num, &mut self.buffer).into_result(self.stmt)
251 }
252}
253
254impl<'p> Raii<'p, ffi::Stmt> {
255 fn affected_row_count(&self) -> Return<ffi::SQLLEN> {
256 let mut count: ffi::SQLLEN = 0;
257 unsafe {
258 match ffi::SQLRowCount(self.handle(), &mut count as *mut ffi::SQLLEN) {
259 SQL_SUCCESS => Return::Success(count),
260 SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(count),
261 SQL_ERROR => Return::Error,
262 r => panic!("SQLRowCount returned unexpected result: {:?}", r),
263 }
264 }
265 }
266
267 fn num_result_cols(&self) -> Return<i16> {
268 let mut num_cols: ffi::SQLSMALLINT = 0;
269 unsafe {
270 match ffi::SQLNumResultCols(self.handle(), &mut num_cols as *mut ffi::SQLSMALLINT) {
271 SQL_SUCCESS => Return::Success(num_cols),
272 SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(num_cols),
273 SQL_ERROR => Return::Error,
274 r => panic!("SQLNumResultCols returned unexpected result: {:?}", r),
275 }
276 }
277 }
278
279 fn describe_col(&self, idx: u16) -> Return<ColumnDescriptor> {
280 let mut name_buffer: [u8; 512] = [0; 512];
281 let mut name_length: ffi::SQLSMALLINT = 0;
282 let mut data_type: ffi::SqlDataType = ffi::SqlDataType::SQL_UNKNOWN_TYPE;
283 let mut column_size: ffi::SQLULEN = 0;
284 let mut decimal_digits: ffi::SQLSMALLINT = 0;
285 let mut nullable: Nullable = Nullable::SQL_NULLABLE_UNKNOWN;
286 unsafe {
287 match ffi::SQLDescribeCol(
288 self.handle(),
289 idx,
290 name_buffer.as_mut_ptr(),
291 name_buffer.len() as ffi::SQLSMALLINT,
292 &mut name_length as *mut ffi::SQLSMALLINT,
293 &mut data_type as *mut ffi::SqlDataType,
294 &mut column_size as *mut ffi::SQLULEN,
295 &mut decimal_digits as *mut ffi::SQLSMALLINT,
296 &mut nullable as *mut ffi::Nullable,
297 ) {
298 SQL_SUCCESS => Return::Success(ColumnDescriptor {
299 name: ::environment::DB_ENCODING.decode(&name_buffer[..(name_length as usize)]).0
300 .to_string(),
301 data_type: data_type,
302 column_size: if column_size == 0 {
303 None
304 } else {
305 Some(column_size)
306 },
307 decimal_digits: if decimal_digits == 0 {
308 None
309 } else {
310 Some(decimal_digits as u16)
311 },
312 nullable: match nullable {
313 Nullable::SQL_NULLABLE_UNKNOWN => None,
314 Nullable::SQL_NULLABLE => Some(true),
315 Nullable::SQL_NO_NULLS => Some(false),
316 },
317 }),
318 SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(ColumnDescriptor {
319 name: ::environment::DB_ENCODING.decode(&name_buffer[..(name_length as usize)]).0
320 .to_string(),
321 data_type: data_type,
322 column_size: if column_size == 0 {
323 None
324 } else {
325 Some(column_size)
326 },
327 decimal_digits: if decimal_digits == 0 {
328 None
329 } else {
330 Some(decimal_digits as u16)
331 },
332 nullable: match nullable {
333 Nullable::SQL_NULLABLE_UNKNOWN => None,
334 Nullable::SQL_NULLABLE => Some(true),
335 Nullable::SQL_NO_NULLS => Some(false),
336 },
337 }),
338 SQL_ERROR => Return::Error,
339 r => panic!("SQLDescribeCol returned unexpected result: {:?}", r),
340 }
341 }
342
343 }
344
345 fn exec_direct(&mut self, statement_text: &str) -> Return<bool> {
346 let bytes = unsafe { crate::environment::DB_ENCODING }.encode(statement_text).0;
347
348 let length = bytes.len();
349 if length > ffi::SQLINTEGER::max_value() as usize {
350 panic!("Statement text too long");
351 }
352 match unsafe {
353 ffi::SQLExecDirect(
354 self.handle(),
355 bytes.as_ptr(),
356 length as ffi::SQLINTEGER,
357 )
358 } {
359 ffi::SQL_SUCCESS => Return::Success(true),
360 ffi::SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(true),
361 ffi::SQL_ERROR => Return::Error,
362 ffi::SQL_NEED_DATA => panic!("SQLExecDirec returned SQL_NEED_DATA"),
363 ffi::SQL_NO_DATA => Return::Success(false),
364 r => panic!("SQLExecDirect returned unexpected result: {:?}", r),
365 }
366 }
367
368 fn exec_direct_bytes(&mut self, bytes: &[u8]) -> Return<bool> {
369 let length = bytes.len();
370 if length > ffi::SQLINTEGER::max_value() as usize {
371 panic!("Statement text too long");
372 }
373 match unsafe {
374 ffi::SQLExecDirect(
375 self.handle(),
376 bytes.as_ptr(),
377 length as ffi::SQLINTEGER,
378 )
379 } {
380 ffi::SQL_SUCCESS => Return::Success(true),
381 ffi::SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(true),
382 ffi::SQL_ERROR => Return::Error,
383 ffi::SQL_NEED_DATA => panic!("SQLExecDirec returned SQL_NEED_DATA"),
384 ffi::SQL_NO_DATA => Return::Success(false),
385 r => panic!("SQLExecDirect returned unexpected result: {:?}", r),
386 }
387 }
388
389 fn fetch(&mut self) -> Return<bool> {
391 match unsafe { ffi::SQLFetch(self.handle()) } {
392 ffi::SQL_SUCCESS => Return::Success(true),
393 ffi::SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(true),
394 ffi::SQL_ERROR => Return::Error,
395 ffi::SQL_NO_DATA => Return::Success(false),
396 r => panic!("SQLFetch returned unexpected result: {:?}", r),
397 }
398 }
399
400 fn tables(&mut self, catalog_name: Option<&str>, schema_name: Option<&str>, table_name: Option<&str>, table_type: &str) -> Return<()> {
401 unsafe {
402 let mut catalog: *const odbc_sys::SQLCHAR = null_mut();
403 let mut schema: *const odbc_sys::SQLCHAR = null_mut();
404 let mut table: *const odbc_sys::SQLCHAR = null_mut();
405
406 let mut catalog_size: odbc_sys::SQLSMALLINT = 0;
407 let mut schema_size: odbc_sys::SQLSMALLINT = 0;
408 let mut table_size: odbc_sys::SQLSMALLINT = 0;
409
410 if catalog_name.is_some() {
411 catalog = catalog_name.unwrap().as_ptr();
412 catalog_size = catalog_name.unwrap().len() as odbc_sys::SQLSMALLINT;
413 }
414
415 if schema_name.is_some() {
416 schema = schema_name.unwrap().as_ptr();
417 schema_size = schema_name.unwrap().len() as odbc_sys::SQLSMALLINT;
418 }
419
420 if table_name.is_some() {
421 table = table_name.unwrap().as_ptr();
422 table_size = table_name.unwrap().len() as odbc_sys::SQLSMALLINT;
423 }
424
425 match odbc_sys::SQLTables(
426 self.handle(),
427 catalog,
428 catalog_size,
429 schema,
430 schema_size,
431 table,
432 table_size,
433 table_type.as_ptr(),
434 table_type.as_bytes().len() as odbc_sys::SQLSMALLINT,
435 ) {
436 SQL_SUCCESS => Return::Success(()),
437 SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(()),
438 SQL_ERROR => Return::Error,
439 r => panic!("SQLTables returned: {:?}", r),
440 }
441 }
442 }
443
444 fn close_cursor(&mut self) -> Return<()> {
445 unsafe {
446 match ffi::SQLCloseCursor(self.handle()) {
447 ffi::SQL_SUCCESS => Return::Success(()),
448 ffi::SQL_SUCCESS_WITH_INFO => Return::SuccessWithInfo(()),
449 ffi::SQL_ERROR => Return::Error,
450 r => panic!("unexpected return value from SQLCloseCursor: {:?}", r),
451 }
452 }
453 }
454}
455
456unsafe impl<'con, 'param, C, P, AC: AutocommitMode> safe::Handle for Statement<'con, 'param, C, P, AC> {
457
458 const HANDLE_TYPE : ffi::HandleType = ffi::SQL_HANDLE_STMT;
459
460 fn handle(&self) -> ffi::SQLHANDLE {
461 <Raii<ffi::Stmt> as safe::Handle>::handle(&self.raii)
462 }
463}