oracle/statement.rs
1// Rust-oracle - Rust binding for Oracle database
2//
3// URL: https://github.com/kubo/rust-oracle
4//
5//-----------------------------------------------------------------------------
6// Copyright (c) 2017-2022 Kubo Takehiro <kubo@jiubao.org>. All rights reserved.
7// This program is free software: you can modify it and/or redistribute it
8// under the terms of:
9//
10// (i) the Universal Permissive License v 1.0 or at your option, any
11// later version (http://oss.oracle.com/licenses/upl); and/or
12//
13// (ii) the Apache License v 2.0. (http://www.apache.org/licenses/LICENSE-2.0)
14//-----------------------------------------------------------------------------
15
16use crate::chkerr;
17use crate::connection::Conn;
18use crate::oci_attr::data_type::{AttrValue, DataType};
19use crate::oci_attr::mode::{ReadMode, WriteMode};
20use crate::oci_attr::{self, OciAttr, SqlFnCode};
21use crate::private;
22use crate::sql_type::FromSql;
23use crate::sql_type::OracleType;
24use crate::sql_type::RefCursor;
25use crate::sql_type::ToSql;
26#[cfg(doc)]
27use crate::sql_type::{Blob, Clob, Nclob};
28use crate::sql_value::BufferRowIndex;
29use crate::to_rust_str;
30use crate::AssertSend;
31use crate::Connection;
32use crate::Context;
33use crate::DpiStmt;
34use crate::Error;
35use crate::OdpiStr;
36use crate::Result;
37use crate::ResultSet;
38use crate::Row;
39use crate::RowValue;
40use crate::SqlValue;
41use odpic_sys::*;
42use std::borrow::ToOwned;
43use std::fmt;
44#[cfg(doc)]
45use std::io::Read;
46use std::mem::MaybeUninit;
47use std::os::raw::c_char;
48use std::ptr;
49use std::sync::atomic::{AtomicU32, Ordering};
50use std::sync::Arc;
51
52// https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-A251CF91-EB9F-4DBC-8BB8-FB5EA92C20DE
53const SQLFNCODE_CREATE_TYPE: u16 = 77;
54const SQLFNCODE_ALTER_TYPE: u16 = 80;
55const SQLFNCODE_DROP_TYPE: u16 = 78;
56
57#[derive(Clone, Copy, Debug, PartialEq, Eq)]
58pub enum LobBindType {
59 Locator,
60 Bytes,
61}
62
63#[derive(Clone, Debug)]
64pub struct QueryParams {
65 pub fetch_array_size: u32,
66 pub prefetch_rows: Option<u32>,
67 pub lob_bind_type: LobBindType,
68}
69
70impl QueryParams {
71 pub fn new() -> QueryParams {
72 QueryParams {
73 fetch_array_size: DPI_DEFAULT_FETCH_ARRAY_SIZE,
74 prefetch_rows: None,
75 lob_bind_type: LobBindType::Bytes,
76 }
77 }
78}
79
80/// A builder to create a [`Statement`][] with various configuration
81pub struct StatementBuilder<'conn, 'sql> {
82 conn: &'conn Connection,
83 sql: &'sql str,
84 query_params: QueryParams,
85 scrollable: bool,
86 tag: String,
87 exclude_from_cache: bool,
88}
89
90impl<'conn, 'sql> StatementBuilder<'conn, 'sql> {
91 pub(crate) fn new(conn: &'conn Connection, sql: &'sql str) -> StatementBuilder<'conn, 'sql> {
92 StatementBuilder {
93 conn,
94 sql,
95 query_params: QueryParams::new(),
96 scrollable: false,
97 tag: "".into(),
98 exclude_from_cache: false,
99 }
100 }
101
102 /// Changes the array size used for performing fetches.
103 ///
104 /// This specifies the number of rows allocated before performing
105 /// fetches. The default value is 100. Higher value reduces
106 /// the number of network round trips to fetch rows but requires
107 /// more memory. The preferable value depends on the query and
108 /// the environment.
109 ///
110 /// If the query returns only onw row, it is better to change
111 /// size to one.
112 ///
113 /// ```
114 /// # use oracle::Error;
115 /// # use oracle::test_util;
116 /// # let conn = test_util::connect()?;
117 /// let mut stmt = conn
118 /// .statement("select StringCol from TestStrings where IntCol = :1")
119 /// .fetch_array_size(1)
120 /// .build()?;
121 /// assert_eq!(stmt.query_row_as::<String>(&[&1])?, "String 1");
122 /// assert_eq!(stmt.query_row_as::<String>(&[&2])?, "String 2");
123 /// # Ok::<(), Error>(())
124 /// ```
125 pub fn fetch_array_size(&mut self, size: u32) -> &mut StatementBuilder<'conn, 'sql> {
126 self.query_params.fetch_array_size = size;
127 self
128 }
129
130 /// The number of rows that will be prefetched by the Oracle Client
131 /// library when a query is executed. The default value is
132 /// DPI_DEFAULT_PREFETCH_ROWS (2). Increasing this value may reduce
133 /// the number of round-trips to the database that are required in
134 /// order to fetch rows, but at the cost of increasing memory
135 /// requirements.
136 /// Setting this value to 0 will disable prefetch completely,
137 /// which may be useful when the timing for fetching rows must be
138 /// controlled by the caller.
139 pub fn prefetch_rows(&mut self, size: u32) -> &mut StatementBuilder<'conn, 'sql> {
140 self.query_params.prefetch_rows = Some(size);
141 self
142 }
143
144 /// Enables lob data types to be fetched or bound as [`Clob`], [`Nclob`] or [`Blob`].
145 ///
146 /// Lob data types are internally bound as string or bytes by default.
147 /// It is proper for small data but not for big data. That's because
148 /// when a lob contains 1 gigabyte data, the whole data are copied to the client
149 /// and consume 1 gigabyte or more memory. When `lob_locator` is set and
150 /// a column is fetched as [`Clob`], data are copied using [`Read::read`].
151 ///
152 /// # Examples
153 ///
154 /// ```
155 /// # use oracle::Connection;
156 /// # use oracle::Error;
157 /// # use oracle::sql_type::Clob;
158 /// # use oracle::test_util;
159 /// # use std::io::{Read, Write};
160 /// # let conn = test_util::connect()?;
161 /// # conn.execute("delete from TestClobs", &[])?;
162 /// # conn.execute("insert into TestClobs values (:1, :2)", &[&1i32, &"clob data"])?;
163 /// # let mut out = vec![0u8; 0];
164 /// let mut stmt = conn
165 /// .statement("select ClobCol from TestClobs where IntCol = :1")
166 /// .lob_locator()
167 /// .build()?;
168 /// let mut clob = stmt.query_row_as::<Clob>(&[&1i32])?;
169 ///
170 /// // Copy contents of clob using 1MB buffer.
171 /// let mut buf = vec![0u8; 1 * 1024 * 1024];
172 /// loop {
173 /// let size = clob.read(&mut buf)?;
174 /// if size == 0 {
175 /// break;
176 /// }
177 /// out.write(&buf[0..size]);
178 /// }
179 /// # Ok::<(), Box::<dyn std::error::Error>>(())
180 /// ```
181 pub fn lob_locator(&mut self) -> &mut StatementBuilder<'conn, 'sql> {
182 self.query_params.lob_bind_type = LobBindType::Locator;
183 self
184 }
185
186 /// Specifies the key to be used for searching for the statement in the statement cache.
187 /// If the key is not found, the SQL text specified by [`Connection::statement`] is used
188 /// to create a statement.
189 ///
190 /// # Examples
191 ///
192 /// ```
193 /// # use oracle::Error;
194 /// # use oracle::test_util;
195 /// # let conn = test_util::connect()?;
196 ///
197 /// // When both SQL text and a tag are specifed and the tag is not found
198 /// // in the statement cache, the SQL text is used to make a statement.
199 /// // The statement is backed to the cache with the tag when
200 /// // it is closed.
201 /// let mut stmt = conn.statement("select 1 from dual").tag("query one").build()?;
202 /// assert_eq!(stmt.query_row_as::<i32>(&[])?, 1);
203 /// stmt.close()?;
204 ///
205 /// // When only a tag is specified and the tag is found in the cache,
206 /// // the statement with the tag is returned.
207 /// let mut stmt = conn.statement("").tag("query one").build()?;
208 /// assert_eq!(stmt.query_row_as::<i32>(&[])?, 1);
209 /// stmt.close()?;
210 ///
211 /// // When only a tag is specified and the tag isn't found in the cache,
212 /// // ORA-24431 is returned.
213 /// let err = conn.statement("").tag("not existing tag").build().unwrap_err();
214 /// match err {
215 /// Error::OciError(err) if err.code() == 24431 => {
216 /// // ORA-24431: Statement does not exist in the cache
217 /// },
218 /// _ => panic!("unexpected err {:?}", err),
219 /// }
220 ///
221 /// // WARNING: The SQL statement is not checked when the tag is found.
222 /// let mut stmt = conn.statement("select 2 from dual").tag("query one").build()?;
223 /// // The result must be 2 if the SQL text is used. However it is 1
224 /// // because the statement tagged with "query one" is "select 1 from dual".
225 /// assert_eq!(stmt.query_row_as::<i32>(&[])?, 1);
226 /// stmt.close()?;
227 ///
228 /// # // test whether the statement is tagged when it is closed by drop.
229 /// # {
230 /// # let mut stmt = conn.statement("select 2 from dual").tag("query two").build()?;
231 /// # assert_eq!(stmt.query_row_as::<i32>(&[])?, 2);
232 /// # // stmt is dropped here.
233 /// # }
234 /// # let mut stmt = conn.statement("").tag("query two").build()?;
235 /// # assert_eq!(stmt.query_row_as::<i32>(&[])?, 2);
236 /// # Ok::<(), Error>(())
237 /// ```
238 pub fn tag<T>(&mut self, tag_name: T) -> &mut StatementBuilder<'conn, 'sql>
239 where
240 T: Into<String>,
241 {
242 self.tag = tag_name.into();
243 self
244 }
245
246 /// Excludes the statement from the cache even when stmt_cache_size is not zero.
247 pub fn exclude_from_cache(&mut self) -> &mut StatementBuilder<'conn, 'sql> {
248 self.exclude_from_cache = true;
249 self
250 }
251
252 pub fn build(&self) -> Result<Statement> {
253 Statement::new(self)
254 }
255}
256
257/// Statement type returned by [`Statement::statement_type`].
258#[derive(Debug, Copy, Clone, PartialEq, Eq)]
259pub enum StatementType {
260 /// SELECT statement
261 Select,
262
263 /// INSERT statement
264 Insert,
265
266 /// UPDATE statement
267 Update,
268
269 /// DELETE statement
270 Delete,
271
272 /// [MERGE][] statement
273 ///
274 /// [MERGE]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-5692CCB7-24D9-4C0E-81A7-A22436DC968F
275 Merge,
276
277 /// CREATE statement
278 Create,
279
280 /// ALTER statement
281 Alter,
282
283 /// DROP statement
284 Drop,
285
286 /// PL/SQL statement without declare clause
287 Begin,
288
289 /// PL/SQL statement with declare clause
290 Declare,
291
292 /// COMMIT statement
293 Commit,
294
295 /// ROLLBACK statement
296 Rollback,
297
298 /// [EXPLAIN PLAN][] statement
299 ///
300 /// [EXPLAIN PLAN]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-FD540872-4ED3-4936-96A2-362539931BA0
301 ExplainPlan,
302
303 /// [CALL][] statement
304 ///
305 /// [CALL]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-6CD7B9C4-E5DC-4F3C-9B6A-876AD2C63545
306 Call,
307
308 /// Unknown statement
309 Unknown,
310}
311
312impl StatementType {
313 pub(crate) fn from_enum(num: dpiStatementType) -> StatementType {
314 match num {
315 DPI_STMT_TYPE_SELECT => StatementType::Select,
316 DPI_STMT_TYPE_INSERT => StatementType::Insert,
317 DPI_STMT_TYPE_UPDATE => StatementType::Update,
318 DPI_STMT_TYPE_DELETE => StatementType::Delete,
319 DPI_STMT_TYPE_MERGE => StatementType::Merge,
320 DPI_STMT_TYPE_CREATE => StatementType::Create,
321 DPI_STMT_TYPE_ALTER => StatementType::Alter,
322 DPI_STMT_TYPE_DROP => StatementType::Drop,
323 DPI_STMT_TYPE_BEGIN => StatementType::Begin,
324 DPI_STMT_TYPE_DECLARE => StatementType::Declare,
325 DPI_STMT_TYPE_COMMIT => StatementType::Commit,
326 DPI_STMT_TYPE_ROLLBACK => StatementType::Rollback,
327 DPI_STMT_TYPE_EXPLAIN_PLAN => StatementType::ExplainPlan,
328 DPI_STMT_TYPE_CALL => StatementType::Call,
329 _ => StatementType::Unknown,
330 }
331 }
332}
333
334impl fmt::Display for StatementType {
335 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
336 match self {
337 StatementType::Select => write!(f, "select"),
338 StatementType::Insert => write!(f, "insert"),
339 StatementType::Update => write!(f, "update"),
340 StatementType::Delete => write!(f, "delete"),
341 StatementType::Merge => write!(f, "merge"),
342 StatementType::Create => write!(f, "create"),
343 StatementType::Alter => write!(f, "alter"),
344 StatementType::Drop => write!(f, "drop"),
345 StatementType::Begin => write!(f, "PL/SQL(begin)"),
346 StatementType::Declare => write!(f, "PL/SQL(declare)"),
347 StatementType::Commit => write!(f, "commit"),
348 StatementType::Rollback => write!(f, "rollback"),
349 StatementType::ExplainPlan => write!(f, "explain plan"),
350 StatementType::Call => write!(f, "call"),
351 StatementType::Unknown => write!(f, "unknown"),
352 }
353 }
354}
355
356#[derive(Debug)]
357pub(crate) struct Stmt {
358 pub(crate) conn: Conn,
359 pub(crate) handle: DpiStmt,
360 pub(crate) row: Option<Row>,
361 shared_buffer_row_index: Arc<AtomicU32>,
362 last_buffer_row_index: u32,
363 more_rows: bool,
364 pub(crate) query_params: QueryParams,
365 tag: String,
366}
367
368impl Stmt {
369 pub(crate) fn new(conn: Conn, handle: DpiStmt, query_params: QueryParams, tag: String) -> Stmt {
370 Stmt {
371 conn,
372 handle,
373 row: None,
374 shared_buffer_row_index: Arc::new(AtomicU32::new(0)),
375 last_buffer_row_index: 0,
376 more_rows: false,
377 query_params,
378 tag,
379 }
380 }
381
382 pub(crate) fn ctxt(&self) -> &Context {
383 self.conn.ctxt()
384 }
385
386 pub(crate) fn conn(&self) -> &Conn {
387 &self.conn
388 }
389
390 pub(crate) fn handle(&self) -> *mut dpiStmt {
391 self.handle.raw
392 }
393
394 fn close(&mut self) -> Result<()> {
395 let tag = OdpiStr::new(&self.tag);
396 chkerr!(self.ctxt(), dpiStmt_close(self.handle(), tag.ptr, tag.len));
397 Ok(())
398 }
399
400 pub(crate) fn init_row(&mut self, num_cols: usize) -> Result<()> {
401 self.shared_buffer_row_index.store(0, Ordering::Relaxed);
402 self.last_buffer_row_index = 0;
403 self.more_rows = true;
404 if self.row.is_some() {
405 return Ok(());
406 }
407 let mut column_info = Vec::with_capacity(num_cols);
408 let mut column_values = Vec::with_capacity(num_cols);
409
410 for i in 0..num_cols {
411 let info = ColumnInfo::new(self, i)?;
412 let val = SqlValue::for_column(
413 self.conn.clone(),
414 self.query_params.clone(),
415 self.shared_buffer_row_index.clone(),
416 info.oracle_type(),
417 self.handle(),
418 (i + 1) as u32,
419 )?;
420 column_info.push(info);
421 column_values.push(val);
422 }
423 self.row = Some(Row::new(column_info, column_values)?);
424 Ok(())
425 }
426
427 fn try_next(&mut self) -> Result<Option<&Row>> {
428 let index = self.shared_buffer_row_index.load(Ordering::Relaxed);
429 let last_index = self.last_buffer_row_index;
430 if index + 1 < last_index {
431 self.shared_buffer_row_index
432 .store(index + 1, Ordering::Relaxed);
433 Ok(Some(self.row.as_ref().unwrap()))
434 } else if self.more_rows && self.fetch_rows()? {
435 Ok(Some(self.row.as_ref().unwrap()))
436 } else {
437 Ok(None)
438 }
439 }
440
441 pub fn next(&mut self) -> Option<Result<&Row>> {
442 self.try_next().transpose()
443 }
444
445 pub fn fetch_rows(&mut self) -> Result<bool> {
446 let handle = self.handle();
447 let row = self.row.as_mut().unwrap();
448 for i in 0..(row.column_info.len()) {
449 // If fetch array buffer is referenced only by self, it is reusable.
450 // Otherwise, a new SqlValue must be created to allocate a new buffer
451 // because dpiStmt_fetchRows() overwrites the buffer.
452 if row.column_values[i].fetch_array_buffer_shared_count()? > 1 {
453 let oratype = row.column_info[i].oracle_type();
454 row.column_values[i] = SqlValue::for_column(
455 self.conn.clone(),
456 self.query_params.clone(),
457 self.shared_buffer_row_index.clone(),
458 oratype,
459 handle,
460 (i + 1) as u32,
461 )?;
462 }
463 }
464 let mut new_index = 0;
465 let mut num_rows = 0;
466 let mut more_rows = 0;
467 chkerr!(
468 self.ctxt(),
469 dpiStmt_fetchRows(
470 handle,
471 self.query_params.fetch_array_size,
472 &mut new_index,
473 &mut num_rows,
474 &mut more_rows
475 )
476 );
477 self.shared_buffer_row_index
478 .store(new_index, Ordering::Relaxed);
479 self.last_buffer_row_index = new_index + num_rows;
480 self.more_rows = more_rows != 0;
481 Ok(num_rows != 0)
482 }
483
484 pub fn row_count(&self) -> Result<u64> {
485 let mut count = 0;
486 chkerr!(self.ctxt(), dpiStmt_getRowCount(self.handle(), &mut count));
487 Ok(count)
488 }
489}
490
491impl AssertSend for Stmt {}
492
493impl Drop for Stmt {
494 fn drop(&mut self) {
495 let _ = self.close();
496 }
497}
498
499/// Statement
500#[derive(Debug)]
501pub struct Statement {
502 pub(crate) stmt: Stmt,
503 statement_type: StatementType,
504 is_returning: bool,
505 bind_count: usize,
506 bind_names: Vec<String>,
507 bind_values: Vec<SqlValue<'static>>,
508}
509
510impl Statement {
511 fn new(builder: &StatementBuilder<'_, '_>) -> Result<Statement> {
512 let conn = builder.conn;
513 let sql = OdpiStr::new(builder.sql);
514 let tag = OdpiStr::new(&builder.tag);
515 let mut handle = DpiStmt::null();
516 chkerr!(
517 conn.ctxt(),
518 dpiConn_prepareStmt(
519 conn.handle(),
520 i32::from(builder.scrollable),
521 sql.ptr,
522 sql.len,
523 tag.ptr,
524 tag.len,
525 &mut handle.raw
526 )
527 );
528 let mut info = MaybeUninit::uninit();
529 chkerr!(conn.ctxt(), dpiStmt_getInfo(handle.raw, info.as_mut_ptr()));
530 let info = unsafe { info.assume_init() };
531 let mut num = 0;
532 chkerr!(conn.ctxt(), dpiStmt_getBindCount(handle.raw, &mut num));
533 let bind_count = num as usize;
534 let mut bind_names = Vec::with_capacity(bind_count);
535 let mut bind_values = Vec::with_capacity(bind_count);
536 if bind_count > 0 {
537 let mut names: Vec<*const c_char> = vec![ptr::null_mut(); bind_count];
538 let mut lengths = vec![0; bind_count];
539 chkerr!(
540 conn.ctxt(),
541 dpiStmt_getBindNames(
542 handle.raw,
543 &mut num,
544 names.as_mut_ptr(),
545 lengths.as_mut_ptr()
546 )
547 );
548 bind_names = Vec::with_capacity(num as usize);
549 for i in 0..(num as usize) {
550 bind_names.push(to_rust_str(names[i], lengths[i]));
551 bind_values.push(SqlValue::for_bind(
552 conn.conn.clone(),
553 builder.query_params.clone(),
554 1,
555 ));
556 }
557 };
558 let tag = if builder.exclude_from_cache {
559 chkerr!(conn.ctxt(), dpiStmt_deleteFromCache(handle.raw));
560 String::new()
561 } else {
562 builder.tag.clone()
563 };
564 Ok(Statement {
565 stmt: Stmt::new(conn.conn.clone(), handle, builder.query_params.clone(), tag),
566 statement_type: StatementType::from_enum(info.statementType),
567 is_returning: info.isReturning != 0,
568 bind_count,
569 bind_names,
570 bind_values,
571 })
572 }
573
574 /// Closes the statement before the end of lifetime.
575 pub fn close(&mut self) -> Result<()> {
576 self.stmt.close()
577 }
578
579 pub(crate) fn ctxt(&self) -> &Context {
580 self.conn().ctxt()
581 }
582
583 pub(crate) fn conn(&self) -> &Conn {
584 &self.stmt.conn
585 }
586
587 pub(crate) fn handle(&self) -> *mut dpiStmt {
588 self.stmt.handle.raw
589 }
590
591 /// Executes the prepared statement and returns a result set containing [`Row`]s.
592 ///
593 /// See [Query Methods][].
594 ///
595 /// [Query Methods]: https://github.com/kubo/rust-oracle/blob/master/docs/query-methods.md
596 pub fn query(&mut self, params: &[&dyn ToSql]) -> Result<ResultSet<Row>> {
597 self.exec(params, true, "query")?;
598 Ok(ResultSet::<Row>::new(&mut self.stmt))
599 }
600
601 /// Executes the prepared statement using named parameters and returns a result set containing [`Row`]s.
602 ///
603 /// See [Query Methods][].
604 ///
605 /// [Query Methods]: https://github.com/kubo/rust-oracle/blob/master/docs/query-methods.md
606 pub fn query_named(&mut self, params: &[(&str, &dyn ToSql)]) -> Result<ResultSet<Row>> {
607 self.exec_named(params, true, "query_named")?;
608 Ok(ResultSet::<Row>::new(&mut self.stmt))
609 }
610
611 /// Executes the prepared statement and returns a result set containing [`RowValue`]s.
612 ///
613 /// See [Query Methods][].
614 ///
615 /// [Query Methods]: https://github.com/kubo/rust-oracle/blob/master/docs/query-methods.md
616 pub fn query_as<T>(&mut self, params: &[&dyn ToSql]) -> Result<ResultSet<T>>
617 where
618 T: RowValue,
619 {
620 self.exec(params, true, "query_as")?;
621 Ok(ResultSet::new(&mut self.stmt))
622 }
623
624 /// Executes the prepared statement and returns a result set containing [`RowValue`]s.
625 ///
626 /// This is the same as [`Statement::query_as()`], but takes ownership of the [`Statement`].
627 ///
628 /// See [Query Methods][].
629 ///
630 /// [Query Methods]: https://github.com/kubo/rust-oracle/blob/master/docs/query-methods.md
631 pub fn into_result_set<T>(mut self, params: &[&dyn ToSql]) -> Result<ResultSet<'static, T>>
632 where
633 T: RowValue,
634 {
635 self.exec(params, true, "into_result_set")?;
636 Ok(ResultSet::from_stmt(self.stmt))
637 }
638
639 /// Executes the prepared statement using named parameters and returns a result set containing [`RowValue`]s.
640 ///
641 /// See [Query Methods][].
642 ///
643 /// [Query Methods]: https://github.com/kubo/rust-oracle/blob/master/docs/query-methods.md
644 pub fn query_as_named<T>(&mut self, params: &[(&str, &dyn ToSql)]) -> Result<ResultSet<T>>
645 where
646 T: RowValue,
647 {
648 self.exec_named(params, true, "query_as_named")?;
649 Ok(ResultSet::new(&mut self.stmt))
650 }
651
652 /// Executes the prepared statement using named parameters and returns a result set containing [`RowValue`]s.
653 ///
654 /// This is the same as [`Statement::query_as_named()`], but takes ownership of the [`Statement`].
655 ///
656 /// See [Query Methods][].
657 ///
658 /// [Query Methods]: https://github.com/kubo/rust-oracle/blob/master/docs/query-methods.md
659 pub fn into_result_set_named<T>(
660 mut self,
661 params: &[(&str, &dyn ToSql)],
662 ) -> Result<ResultSet<'static, T>>
663 where
664 T: RowValue,
665 {
666 self.exec_named(params, true, "into_result_set_named")?;
667 Ok(ResultSet::from_stmt(self.stmt))
668 }
669
670 /// Gets one row from the prepared statement using positoinal bind parameters.
671 ///
672 /// See [Query Methods][].
673 ///
674 /// [Query Methods]: https://github.com/kubo/rust-oracle/blob/master/docs/query-methods.md
675 pub fn query_row(&mut self, params: &[&dyn ToSql]) -> Result<Row> {
676 let mut rows = self.query(params)?;
677 rows.next().unwrap_or(Err(Error::no_data_found()))
678 }
679
680 /// Gets one row from the prepared statement using named bind parameters.
681 ///
682 /// See [Query Methods][].
683 ///
684 /// [Query Methods]: https://github.com/kubo/rust-oracle/blob/master/docs/query-methods.md
685 pub fn query_row_named(&mut self, params: &[(&str, &dyn ToSql)]) -> Result<Row> {
686 let mut rows = self.query_named(params)?;
687 rows.next().unwrap_or(Err(Error::no_data_found()))
688 }
689
690 /// Gets one row from the prepared statement as specified type using positoinal bind parameters.
691 ///
692 /// See [Query Methods][].
693 ///
694 /// [Query Methods]: https://github.com/kubo/rust-oracle/blob/master/docs/query-methods.md
695 pub fn query_row_as<T>(&mut self, params: &[&dyn ToSql]) -> Result<T>
696 where
697 T: RowValue,
698 {
699 let mut rows = self.query_as::<T>(params)?;
700 rows.next().unwrap_or(Err(Error::no_data_found()))
701 }
702
703 /// Gets one row from the prepared statement as specified type using named bind parameters.
704 ///
705 /// See [Query Methods][].
706 ///
707 /// [Query Methods]: https://github.com/kubo/rust-oracle/blob/master/docs/query-methods.md
708 pub fn query_row_as_named<T>(&mut self, params: &[(&str, &dyn ToSql)]) -> Result<T>
709 where
710 T: RowValue,
711 {
712 let mut rows = self.query_as_named::<T>(params)?;
713 rows.next().unwrap_or(Err(Error::no_data_found()))
714 }
715
716 /// Binds values by position and executes the statement.
717 /// It will retunrs `Err` when the statemnet is a select statement.
718 ///
719 /// See also [`Connection::execute`].
720 ///
721 /// # Examples
722 ///
723 /// ```no_run
724 /// # use oracle::*;
725 /// let conn = Connection::connect("scott", "tiger", "")?;
726 ///
727 /// // execute a statement without bind parameters
728 /// let mut stmt = conn
729 /// .statement("insert into emp(empno, ename) values (113, 'John')")
730 /// .build()?;
731 /// stmt.execute(&[])?;
732 ///
733 /// // execute a statement with binding parameters by position
734 /// let mut stmt = conn
735 /// .statement("insert into emp(empno, ename) values (:1, :2)")
736 /// .build()?;
737 /// stmt.execute(&[&114, &"Smith"])?;
738 /// stmt.execute(&[&115, &"Paul"])?; // execute with other values.
739 ///
740 /// # Ok::<(), Error>(())
741 /// ```
742 pub fn execute(&mut self, params: &[&dyn ToSql]) -> Result<()> {
743 self.exec(params, false, "execute")
744 }
745
746 /// Binds values by name and executes the statement.
747 /// It will retunrs `Err` when the statemnet is a select statement.
748 ///
749 /// See also [`Connection::execute_named`].
750 ///
751 /// # Examples
752 ///
753 /// ```no_run
754 /// # use oracle::*;
755 /// let conn = Connection::connect("scott", "tiger", "")?;
756 ///
757 /// // execute a statement with binding parameters by name
758 /// let mut stmt = conn
759 /// .statement("insert into emp(empno, ename) values (:id, :name)")
760 /// .build()?;
761 /// stmt.execute_named(&[("id", &114),
762 /// ("name", &"Smith")])?;
763 /// stmt.execute_named(&[("id", &115),
764 /// ("name", &"Paul")])?; // execute with other values.
765 /// # Ok::<(), Error>(())
766 /// ```
767 pub fn execute_named(&mut self, params: &[(&str, &dyn ToSql)]) -> Result<()> {
768 self.exec_named(params, false, "execute_named")
769 }
770
771 fn check_stmt_type(&self, must_be_query: bool, method_name: &str) -> Result<()> {
772 if must_be_query {
773 if self.statement_type == StatementType::Select {
774 Ok(())
775 } else {
776 Err(Error::invalid_operation(format!(
777 "could not use the `{}` method for non-select statements",
778 method_name
779 )))
780 }
781 } else if self.statement_type != StatementType::Select {
782 Ok(())
783 } else {
784 Err(Error::invalid_operation(format!(
785 "could not use the `{}` method for select statements",
786 method_name
787 )))
788 }
789 }
790
791 pub(crate) fn exec(
792 &mut self,
793 params: &[&dyn ToSql],
794 must_be_query: bool,
795 method_name: &str,
796 ) -> Result<()> {
797 self.check_stmt_type(must_be_query, method_name)?;
798 for (i, param) in params.iter().enumerate() {
799 self.bind(i + 1, *param)?;
800 }
801 self.exec_common()
802 }
803
804 pub(crate) fn exec_named(
805 &mut self,
806 params: &[(&str, &dyn ToSql)],
807 must_be_query: bool,
808 method_name: &str,
809 ) -> Result<()> {
810 self.check_stmt_type(must_be_query, method_name)?;
811 for param in params {
812 self.bind(param.0, param.1)?;
813 }
814 self.exec_common()
815 }
816
817 fn exec_common(&mut self) -> Result<()> {
818 let mut num_query_columns = 0;
819 let mut exec_mode = DPI_MODE_EXEC_DEFAULT;
820 if self.conn().autocommit() {
821 exec_mode |= DPI_MODE_EXEC_COMMIT_ON_SUCCESS;
822 }
823 chkerr!(
824 self.ctxt(),
825 dpiStmt_setFetchArraySize(self.handle(), self.stmt.query_params.fetch_array_size)
826 );
827 if let Some(prefetch_rows) = self.stmt.query_params.prefetch_rows {
828 chkerr!(
829 self.ctxt(),
830 dpiStmt_setPrefetchRows(self.handle(), prefetch_rows)
831 );
832 }
833 chkerr!(
834 self.ctxt(),
835 dpiStmt_execute(self.handle(), exec_mode, &mut num_query_columns)
836 );
837 self.ctxt().set_warning();
838 if self.is_ddl() {
839 let fncode = self.oci_attr::<SqlFnCode>()?;
840 match fncode {
841 SQLFNCODE_CREATE_TYPE | SQLFNCODE_ALTER_TYPE | SQLFNCODE_DROP_TYPE => {
842 self.conn().clear_object_type_cache()?
843 }
844 _ => (),
845 }
846 }
847 if self.statement_type == StatementType::Select {
848 self.stmt.init_row(num_query_columns as usize)?;
849 }
850 if self.is_returning {
851 for val in self.bind_values.iter_mut() {
852 val.fix_internal_data()?;
853 }
854 }
855 Ok(())
856 }
857
858 /// Returns the number of bind variables in the statement.
859 ///
860 /// In SQL statements this is the total number of bind variables whereas in
861 /// PL/SQL statements this is the count of the **unique** bind variables.
862 ///
863 /// ```no_run
864 /// # use oracle::*;
865 /// let conn = Connection::connect("scott", "tiger", "")?;
866 ///
867 /// // SQL statements
868 /// let stmt = conn.statement("select :val1, :val2, :val1 from dual").build()?;
869 /// assert_eq!(stmt.bind_count(), 3); // val1, val2 and val1
870 ///
871 /// // PL/SQL statements
872 /// let stmt = conn.statement("begin :val1 := :val1 || :val2; end;").build()?;
873 /// assert_eq!(stmt.bind_count(), 2); // val1(twice) and val2
874 /// # Ok::<(), Error>(())
875 /// ```
876 pub fn bind_count(&self) -> usize {
877 self.bind_count
878 }
879
880 /// Returns the names of the unique bind variables in the statement.
881 ///
882 /// The bind variable names in statements are converted to upper-case.
883 ///
884 /// # Examples
885 ///
886 /// ```no_run
887 /// # use oracle::*;
888 /// let conn = Connection::connect("scott", "tiger", "")?;
889 ///
890 /// let stmt = conn.statement("BEGIN :val1 := :val2 || :val1 || :aàáâãäå; END;").build()?;
891 /// assert_eq!(stmt.bind_count(), 3);
892 /// let bind_names = stmt.bind_names();
893 /// assert_eq!(bind_names.len(), 3);
894 /// assert_eq!(bind_names[0], "VAL1");
895 /// assert_eq!(bind_names[1], "VAL2");
896 /// assert_eq!(bind_names[2], "AÀÁÂÃÄÅ");
897 /// # Ok::<(), Error>(())
898 /// ```
899 pub fn bind_names(&self) -> Vec<&str> {
900 self.bind_names.iter().map(|name| name.as_str()).collect()
901 }
902
903 /// Set a bind value in the statement.
904 ///
905 /// The position starts from one when the bind index type is `usize`.
906 /// The variable name is compared case-insensitively when the bind index
907 /// type is `&str`.
908 ///
909 /// # Examples
910 ///
911 /// ```no_run
912 /// # use oracle::*; use oracle::sql_type::*;
913 /// let conn = Connection::connect("scott", "tiger", "")?;
914 /// let mut stmt = conn.statement("begin :outval := upper(:inval); end;").build()?;
915 ///
916 /// // Sets NULL whose data type is VARCHAR2(60) to the first bind value.
917 /// stmt.bind(1, &OracleType::Varchar2(60))?;
918 ///
919 /// // Sets "to be upper-case" to the second by its name.
920 /// stmt.bind("inval", &"to be upper-case")?;
921 ///
922 /// stmt.execute(&[])?;
923 /// let outval: String = stmt.bind_value(1)?;
924 /// assert_eq!(outval, "TO BE UPPER-CASE");
925 /// # Ok::<(), Error>(())
926 /// ```
927 pub fn bind<I>(&mut self, bindidx: I, value: &dyn ToSql) -> Result<()>
928 where
929 I: BindIndex,
930 {
931 let pos = bindidx.idx(self)?;
932 let conn = Connection::from_conn(self.conn().clone());
933 if self.bind_values[pos].init_handle(&value.oratype(&conn)?)? {
934 chkerr!(
935 self.ctxt(),
936 bindidx.bind(self.handle(), self.bind_values[pos].handle()?)
937 );
938 }
939 self.bind_values[pos].set(value)
940 }
941
942 /// Gets a bind value in the statement.
943 ///
944 /// The position starts from one when the bind index type is `usize`.
945 /// The variable name is compared case-insensitively when the bind index
946 /// type is `&str`.
947 ///
948 /// # Examples
949 ///
950 /// ```no_run
951 /// # use oracle::*; use oracle::sql_type::*;
952 /// let conn = Connection::connect("scott", "tiger", "")?;
953 ///
954 /// // Prepares "begin :outval := upper(:inval); end;",
955 /// // sets NULL whose data type is VARCHAR2(60) to the first bind variable,
956 /// // sets "to be upper-case" to the second and then executes it.
957 /// let mut stmt = conn.statement("begin :outval := upper(:inval); end;").build()?;
958 /// stmt.execute(&[&OracleType::Varchar2(60),
959 /// &"to be upper-case"])?;
960 ///
961 /// // Get the first bind value by position.
962 /// let outval: String = stmt.bind_value(1)?;
963 /// assert_eq!(outval, "TO BE UPPER-CASE");
964 ///
965 /// // Get the first bind value by name.
966 /// let outval: String = stmt.bind_value("outval")?;
967 /// assert_eq!(outval, "TO BE UPPER-CASE");
968 /// # Ok::<(), Error>(())
969 /// ```
970 pub fn bind_value<I, T>(&self, bindidx: I) -> Result<T>
971 where
972 I: BindIndex,
973 T: FromSql,
974 {
975 let pos = bindidx.idx(self)?;
976 self.bind_values[pos].get()
977 }
978
979 /// Gets values returned by RETURNING INTO clause.
980 ///
981 /// When the `bindidx` ponints to a bind variable out of RETURNING INTO clause,
982 /// the behavior is undefined.
983 ///
984 /// # Examples
985 ///
986 /// ```no_run
987 /// # use oracle::*; use oracle::sql_type::*;
988 /// let conn = Connection::connect("scott", "tiger", "")?;
989 ///
990 /// // create a table using identity column (Oracle 12c feature).
991 /// conn.execute("create table people (id number generated as identity, name varchar2(30))", &[])?;
992 ///
993 /// // insert one person and return the generated id into :id.
994 /// let stmt = conn.execute("insert into people(name) values ('Asimov') returning id into :id", &[&None::<i32>])?;
995 /// let inserted_id: i32 = stmt.returned_values("id")?[0];
996 /// println!("Asimov's ID is {}", inserted_id);
997 ///
998 /// // insert another person and return the generated id into :id.
999 /// let stmt = conn.execute("insert into people(name) values ('Clark') returning id into :id", &[&None::<i32>])?;
1000 /// let inserted_id: i32 = stmt.returned_values("id")?[0];
1001 /// println!("Clark's ID is {}", inserted_id);
1002 ///
1003 /// // delete all people and return deleted names into :name.
1004 /// let stmt = conn.execute("delete from people returning name into :name", &[&OracleType::Varchar2(30)])?;
1005 /// let deleted_names: Vec<String> = stmt.returned_values("name")?;
1006 /// for name in deleted_names {
1007 /// println!("{} is deleted.", name);
1008 /// }
1009 ///
1010 /// // cleanup
1011 /// conn.execute("drop table people purge", &[])?;
1012 /// # Ok::<(), Error>(())
1013 /// ```
1014 pub fn returned_values<I, T>(&self, bindidx: I) -> Result<Vec<T>>
1015 where
1016 I: BindIndex,
1017 T: FromSql,
1018 {
1019 let mut rows = 0;
1020 chkerr!(self.ctxt(), dpiStmt_getRowCount(self.handle(), &mut rows));
1021 if rows == 0 {
1022 return Ok(vec![]);
1023 }
1024 let mut sqlval = self.bind_values[bindidx.idx(self)?].clone_with_narrow_lifetime()?;
1025 if rows > sqlval.array_size as u64 {
1026 rows = sqlval.array_size as u64;
1027 }
1028 let mut vec = Vec::with_capacity(rows as usize);
1029 for i in 0..rows {
1030 sqlval.buffer_row_index = BufferRowIndex::Owned(i as u32);
1031 vec.push(sqlval.get()?);
1032 }
1033 Ok(vec)
1034 }
1035
1036 /// Returns the number of rows fetched when the SQL statement is a query.
1037 /// Otherwise, the number of rows affected.
1038 ///
1039 /// # Examples
1040 ///
1041 /// ```
1042 /// # use oracle::Error;
1043 /// # use oracle::test_util::{self, check_version, VER12_1};
1044 /// # let conn = test_util::connect()?;
1045 /// // number of affected rows
1046 /// let stmt = conn.execute("update TestStrings set StringCol = StringCol where IntCol >= :1", &[&6])?;
1047 /// assert_eq!(stmt.row_count()?, 5);
1048 ///
1049 /// // number of fetched rows
1050 /// let mut stmt = conn
1051 /// .statement("select * from TestStrings where IntCol >= :1")
1052 /// .build()?;
1053 /// assert_eq!(stmt.row_count()?, 0); // before fetch
1054 /// let mut nrows = 0;
1055 /// for _ in stmt.query(&[&6])? {
1056 /// nrows += 1;
1057 /// }
1058 /// assert_eq!(stmt.row_count()?, nrows); // after fetch
1059 ///
1060 /// // fetch again using same stmt with a different bind value.
1061 /// let mut nrows = 0;
1062 /// for _ in stmt.query(&[&4])? {
1063 /// nrows += 1;
1064 /// }
1065 /// assert_eq!(stmt.row_count()?, nrows); // after fetch
1066 /// # Ok::<(), Error>(())
1067 /// ```
1068 pub fn row_count(&self) -> Result<u64> {
1069 self.stmt.row_count()
1070 }
1071
1072 /// Returns the next implicit result returned by [`dbms_sql.return_result()`]
1073 /// in a PL/SQL block or a stored procedure.
1074 ///
1075 /// This feature is available when both the client and server are 12.1 or higher.
1076 ///
1077 /// [`dbms_sql.return_result()`]: https://www.oracle.com/pls/topic/lookup?ctx=dblatest&id=GUID-87562BF3-682C-48A7-B0C1-61075F19382A
1078 ///
1079 /// # Examples
1080 ///
1081 /// ```
1082 /// # use oracle::Error;
1083 /// # use oracle::test_util::{self, check_version, VER12_1};
1084 /// # let conn = test_util::connect()?;
1085 /// # if !check_version(&conn, &VER12_1, &VER12_1)? {
1086 /// # return Ok(()); // skip this test
1087 /// # }
1088 ///
1089 /// let sql = r#"
1090 /// declare
1091 /// cursor1 SYS_REFCURSOR;
1092 /// cursor2 SYS_REFCURSOR;
1093 /// begin
1094 /// open cursor1 for select StringCol from TestStrings where IntCol = :1;
1095 /// -- return the first result set
1096 /// dbms_sql.return_result(cursor1);
1097 ///
1098 /// open cursor2 for select StringCol from TestStrings where IntCol = :2;
1099 /// -- return the second result set
1100 /// dbms_sql.return_result(cursor2);
1101 /// end;
1102 /// "#;
1103 ///
1104 /// let mut stmt = conn.statement(sql).build()?;
1105 /// stmt.execute(&[&1, &2])?;
1106 ///
1107 /// // Get the first result set.
1108 /// let mut opt_cursor = stmt.implicit_result()?;
1109 /// assert!(opt_cursor.is_some());
1110 /// let mut cursor = opt_cursor.unwrap();
1111 /// assert_eq!(cursor.query_row_as::<String>()?, "String 1");
1112 ///
1113 /// // Get the second result set.
1114 /// let mut opt_cursor = stmt.implicit_result()?;
1115 /// assert!(opt_cursor.is_some());
1116 /// let mut cursor = opt_cursor.unwrap();
1117 /// assert_eq!(cursor.query_row_as::<String>()?, "String 2");
1118 ///
1119 /// // No more result sets
1120 /// let mut opt_cursor = stmt.implicit_result()?;
1121 /// assert!(opt_cursor.is_none());
1122 /// # Ok::<(), Error>(())
1123 /// ```
1124 pub fn implicit_result(&self) -> Result<Option<RefCursor>> {
1125 let mut handle = DpiStmt::null();
1126 chkerr!(
1127 self.ctxt(),
1128 dpiStmt_getImplicitResult(self.handle(), &mut handle.raw)
1129 );
1130 if handle.is_null() {
1131 Ok(None)
1132 } else {
1133 let cursor = RefCursor::from_handle(
1134 self.stmt.conn.clone(),
1135 handle,
1136 self.stmt.query_params.clone(),
1137 )?;
1138 Ok(Some(cursor))
1139 }
1140 }
1141
1142 /// Returns statement type
1143 pub fn statement_type(&self) -> StatementType {
1144 self.statement_type
1145 }
1146
1147 /// Returns true when the SQL statement is a query.
1148 pub fn is_query(&self) -> bool {
1149 self.statement_type == StatementType::Select
1150 }
1151
1152 /// Returns true when the SQL statement is a PL/SQL block.
1153 pub fn is_plsql(&self) -> bool {
1154 matches!(
1155 self.statement_type,
1156 StatementType::Begin | StatementType::Declare | StatementType::Call
1157 )
1158 }
1159
1160 /// Returns true when the SQL statement is DDL (data definition language).
1161 pub fn is_ddl(&self) -> bool {
1162 matches!(
1163 self.statement_type,
1164 StatementType::Create | StatementType::Drop | StatementType::Alter
1165 )
1166 }
1167
1168 /// Returns true when the SQL statement is DML (data manipulation language).
1169 pub fn is_dml(&self) -> bool {
1170 matches!(
1171 self.statement_type,
1172 StatementType::Insert
1173 | StatementType::Update
1174 | StatementType::Delete
1175 | StatementType::Merge
1176 )
1177 }
1178
1179 /// Returns true when the SQL statement has a `RETURNING INTO` clause.
1180 pub fn is_returning(&self) -> bool {
1181 self.is_returning
1182 }
1183
1184 /// Returns the rowid of the last row that was affected by the statement.
1185 ///
1186 /// ```
1187 /// # use oracle::Error;
1188 /// # use oracle::test_util;
1189 /// # let conn = test_util::connect()?;
1190 /// let mut stmt = conn
1191 /// .statement("insert into TestDates values(100, sysdate, null)")
1192 /// .build()?;
1193 /// stmt.execute(&[])?;
1194 /// // get the rowid inserted by stmt
1195 /// let rowid = stmt.last_row_id()?.unwrap();
1196 /// // query the inserted row using rowid
1197 /// let intcol_value = conn.query_row_as::<i64>("select IntCol from TestDates where rowid = :1", &[&rowid])?;
1198 /// assert_eq!(intcol_value, 100);
1199 /// # conn.rollback()?;
1200 /// # Ok::<(), Error>(())
1201 /// ```
1202 pub fn last_row_id(&self) -> Result<Option<String>> {
1203 let mut rowid = ptr::null_mut();
1204 chkerr!(self.ctxt(), dpiStmt_getLastRowid(self.handle(), &mut rowid));
1205 if rowid.is_null() {
1206 Ok(None)
1207 } else {
1208 let mut ptr = ptr::null();
1209 let mut len = 0;
1210 chkerr!(
1211 self.ctxt(),
1212 dpiRowid_getStringValue(rowid, &mut ptr, &mut len)
1213 );
1214 Ok(Some(to_rust_str(ptr, len)))
1215 }
1216 }
1217
1218 /// Gets an OCI handle attribute corresponding to the specified type parameter
1219 /// See the [`oci_attr` module][crate::oci_attr] for details.
1220 pub fn oci_attr<T>(&self) -> Result<<<T::DataType as DataType>::Type as ToOwned>::Owned>
1221 where
1222 T: OciAttr<HandleType = oci_attr::handle::Stmt>,
1223 T::Mode: ReadMode,
1224 {
1225 let attr_value = AttrValue::from_stmt(self, <T>::ATTR_NUM);
1226 unsafe { <T::DataType>::get(attr_value) }
1227 }
1228
1229 /// Sets an OCI handle attribute corresponding to the specified type parameter
1230 /// See the [`oci_attr` module][crate::oci_attr] for details.
1231 pub fn set_oci_attr<T>(&mut self, value: &<T::DataType as DataType>::Type) -> Result<()>
1232 where
1233 T: OciAttr<HandleType = oci_attr::handle::Stmt>,
1234 T::Mode: WriteMode,
1235 {
1236 let mut attr_value = AttrValue::from_stmt(self, <T>::ATTR_NUM);
1237 unsafe { <T::DataType>::set(&mut attr_value, value) }
1238 }
1239}
1240
1241impl AssertSend for Statement {}
1242
1243/// Column information in a select statement
1244///
1245/// # Examples
1246///
1247/// Print column information of `emp` table.
1248///
1249/// ```no_run
1250/// # use oracle::*;
1251/// let conn = Connection::connect("scott", "tiger", "")?;
1252/// let mut stmt = conn.statement("select * from emp").build()?;
1253/// let rows = stmt.query(&[])?;
1254/// println!(" {:-30} {:-8} {}", "Name", "Null?", "Type");
1255/// println!(" {:-30} {:-8} {}", "------------------------------", "--------", "----------------------------");
1256/// for info in rows.column_info() {
1257/// println!("{:-30} {:-8} {}",
1258/// info.name(),
1259/// if info.nullable() {""} else {"NOT NULL"},
1260/// info.oracle_type());
1261/// }
1262/// # Ok::<(), Error>(())
1263/// ```
1264///
1265/// The output is:
1266///
1267/// ```text
1268/// Name Null? Type
1269/// ------------------------------ -------- ----------------------------
1270/// EMPNO NOT NULL NUMBER(4)
1271/// ENAME VARCHAR2(10)
1272/// JOB VARCHAR2(9)
1273/// MGR NUMBER(4)
1274/// HIREDATE DATE
1275/// SAL NUMBER(7,2)
1276/// COMM NUMBER(7,2)
1277/// DEPTNO NUMBER(2)
1278/// ```
1279#[derive(Debug, Clone)]
1280pub struct ColumnInfo {
1281 name: String,
1282 oracle_type: OracleType,
1283 nullable: bool,
1284}
1285
1286impl ColumnInfo {
1287 fn new(stmt: &Stmt, idx: usize) -> Result<ColumnInfo> {
1288 let mut info = MaybeUninit::uninit();
1289 chkerr!(
1290 stmt.ctxt(),
1291 dpiStmt_getQueryInfo(stmt.handle(), (idx + 1) as u32, info.as_mut_ptr())
1292 );
1293 let info = unsafe { info.assume_init() };
1294 Ok(ColumnInfo {
1295 name: to_rust_str(info.name, info.nameLength),
1296 oracle_type: OracleType::from_type_info(stmt.conn(), &info.typeInfo)?,
1297 nullable: info.nullOk != 0,
1298 })
1299 }
1300
1301 /// Gets column name
1302 pub fn name(&self) -> &str {
1303 &self.name
1304 }
1305
1306 /// Gets Oracle type
1307 pub fn oracle_type(&self) -> &OracleType {
1308 &self.oracle_type
1309 }
1310
1311 /// Gets whether the column may be NULL.
1312 /// False when the column is defined as `NOT NULL`.
1313 pub fn nullable(&self) -> bool {
1314 self.nullable
1315 }
1316}
1317
1318impl fmt::Display for ColumnInfo {
1319 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1320 if self.nullable {
1321 write!(f, "{} {}", self.name, self.oracle_type)
1322 } else {
1323 write!(f, "{} {} NOT NULL", self.name, self.oracle_type)
1324 }
1325 }
1326}
1327
1328/// A trait implemented by types that can index into bind values of a statement.
1329///
1330/// This trait is sealed and cannot be implemented for types outside of the `oracle` crate.
1331pub trait BindIndex: private::Sealed {
1332 /// Returns the index of the bind value specified by `self`.
1333 #[doc(hidden)]
1334 fn idx(&self, stmt: &Statement) -> Result<usize>;
1335 /// Binds the specified value by using a private method.
1336 #[doc(hidden)]
1337 unsafe fn bind(&self, stmt_handle: *mut dpiStmt, var_handle: *mut dpiVar) -> i32;
1338}
1339
1340impl BindIndex for usize {
1341 fn idx(&self, stmt: &Statement) -> Result<usize> {
1342 let num = stmt.bind_count();
1343 if 0 < num && 1 <= *self && *self <= num {
1344 Ok(*self - 1)
1345 } else {
1346 Err(Error::invalid_bind_index(*self))
1347 }
1348 }
1349
1350 unsafe fn bind(&self, stmt_handle: *mut dpiStmt, var_handle: *mut dpiVar) -> i32 {
1351 dpiStmt_bindByPos(stmt_handle, *self as u32, var_handle)
1352 }
1353}
1354
1355impl BindIndex for &str {
1356 fn idx(&self, stmt: &Statement) -> Result<usize> {
1357 let bindname = self.to_uppercase();
1358 stmt.bind_names()
1359 .iter()
1360 .position(|&name| name == bindname)
1361 .ok_or_else(|| Error::invalid_bind_name(*self))
1362 }
1363
1364 unsafe fn bind(&self, stmt_handle: *mut dpiStmt, var_handle: *mut dpiVar) -> i32 {
1365 let s = OdpiStr::new(self);
1366 dpiStmt_bindByName(stmt_handle, s.ptr, s.len, var_handle)
1367 }
1368}
1369
1370/// A trait implemented by types that can index into columns of a row.
1371///
1372/// This trait is sealed and cannot be implemented for types outside of the `oracle` crate.
1373pub trait ColumnIndex: private::Sealed {
1374 /// Returns the index of the column specified by `self`.
1375 #[doc(hidden)]
1376 fn idx(&self, column_info: &[ColumnInfo]) -> Result<usize>;
1377}
1378
1379impl ColumnIndex for usize {
1380 fn idx(&self, column_info: &[ColumnInfo]) -> Result<usize> {
1381 let ncols = column_info.len();
1382 if *self < ncols {
1383 Ok(*self)
1384 } else {
1385 Err(Error::invalid_column_index(*self))
1386 }
1387 }
1388}
1389
1390impl ColumnIndex for &str {
1391 fn idx(&self, column_info: &[ColumnInfo]) -> Result<usize> {
1392 for (idx, info) in column_info.iter().enumerate() {
1393 if info.name.as_str().eq_ignore_ascii_case(self) {
1394 return Ok(idx);
1395 }
1396 }
1397 Err(Error::invalid_column_name(*self))
1398 }
1399}
1400
1401#[cfg(test)]
1402mod tests {
1403 use super::*;
1404 use crate::test_util;
1405
1406 #[test]
1407 fn column_info() -> Result<()> {
1408 let conn = test_util::connect()?;
1409 let mut stmt = conn.statement("select * from TestDataTypes").build()?;
1410 let rows = stmt.query(&[])?;
1411 let colinfo = rows.column_info();
1412 assert_eq!(colinfo[0].name(), "STRINGCOL");
1413 assert_eq!(colinfo[0].oracle_type(), &OracleType::Varchar2(100));
1414 assert_eq!(colinfo[1].name(), "UNICODECOL");
1415 assert_eq!(colinfo[1].oracle_type(), &OracleType::NVarchar2(100));
1416 assert_eq!(colinfo[2].name(), "FIXEDCHARCOL");
1417 assert_eq!(colinfo[2].oracle_type(), &OracleType::Char(100));
1418 assert_eq!(colinfo[3].name(), "FIXEDUNICODECOL");
1419 assert_eq!(colinfo[3].oracle_type(), &OracleType::NChar(100));
1420 assert_eq!(colinfo[4].name(), "RAWCOL");
1421 assert_eq!(colinfo[4].oracle_type(), &OracleType::Raw(30));
1422 assert_eq!(colinfo[5].name(), "FLOATCOL");
1423 assert_eq!(colinfo[5].oracle_type(), &OracleType::Float(126));
1424 assert_eq!(colinfo[6].name(), "DOUBLEPRECCOL");
1425 assert_eq!(colinfo[6].oracle_type(), &OracleType::Float(126));
1426 assert_eq!(colinfo[7].name(), "INTCOL");
1427 assert_eq!(colinfo[7].oracle_type(), &OracleType::Number(9, 0));
1428 assert_eq!(colinfo[8].name(), "NUMBERCOL");
1429 assert_eq!(colinfo[8].oracle_type(), &OracleType::Number(9, 2));
1430 assert_eq!(colinfo[9].name(), "DATECOL");
1431 assert_eq!(colinfo[9].oracle_type(), &OracleType::Date);
1432 assert_eq!(colinfo[10].name(), "TIMESTAMPCOL");
1433 assert_eq!(colinfo[10].oracle_type(), &OracleType::Timestamp(6));
1434 assert_eq!(colinfo[11].name(), "TIMESTAMPTZCOL");
1435 assert_eq!(colinfo[11].oracle_type(), &OracleType::TimestampTZ(6));
1436 assert_eq!(colinfo[12].name(), "TIMESTAMPLTZCOL");
1437 assert_eq!(colinfo[12].oracle_type(), &OracleType::TimestampLTZ(6));
1438 assert_eq!(colinfo[13].name(), "INTERVALDSCOL");
1439 assert_eq!(colinfo[13].oracle_type(), &OracleType::IntervalDS(2, 6));
1440 assert_eq!(colinfo[14].name(), "INTERVALYMCOL");
1441 assert_eq!(colinfo[14].oracle_type(), &OracleType::IntervalYM(2));
1442 assert_eq!(colinfo[15].name(), "BINARYFLTCOL");
1443 assert_eq!(colinfo[15].oracle_type(), &OracleType::BinaryFloat);
1444 assert_eq!(colinfo[16].name(), "BINARYDOUBLECOL");
1445 assert_eq!(colinfo[16].oracle_type(), &OracleType::BinaryDouble);
1446 assert_eq!(colinfo[17].name(), "CLOBCOL");
1447 assert_eq!(colinfo[17].oracle_type(), &OracleType::CLOB);
1448 assert_eq!(colinfo[18].name(), "NCLOBCOL");
1449 assert_eq!(colinfo[18].oracle_type(), &OracleType::NCLOB);
1450 assert_eq!(colinfo[19].name(), "BLOBCOL");
1451 assert_eq!(colinfo[19].oracle_type(), &OracleType::BLOB);
1452 assert_eq!(colinfo[20].name(), "BFILECOL");
1453 assert_eq!(colinfo[20].oracle_type(), &OracleType::BFILE);
1454 assert_eq!(colinfo[21].name(), "LONGCOL");
1455 assert_eq!(colinfo[21].oracle_type(), &OracleType::Long);
1456 assert_eq!(colinfo[22].name(), "UNCONSTRAINEDCOL");
1457 assert_eq!(colinfo[22].oracle_type(), &OracleType::Number(0, -127));
1458 assert_eq!(colinfo[23].name(), "SIGNEDINTCOL");
1459 assert_eq!(colinfo[23].oracle_type(), &OracleType::Number(38, 0));
1460 assert_eq!(colinfo[24].name(), "SUBOBJECTCOL");
1461 assert_eq!(
1462 colinfo[24].oracle_type().to_string(),
1463 OracleType::Object(conn.object_type("UDT_SUBOBJECT")?).to_string()
1464 );
1465 assert_eq!(colinfo.len(), 25);
1466
1467 let mut stmt = conn.statement("select * from TestLongRaws").build()?;
1468 let rows = stmt.query(&[])?;
1469 let colinfo = rows.column_info();
1470 assert_eq!(colinfo[0].name(), "INTCOL");
1471 assert_eq!(colinfo[0].oracle_type(), &OracleType::Number(9, 0));
1472 assert_eq!(colinfo[1].name(), "LONGRAWCOL");
1473 assert_eq!(colinfo[1].oracle_type(), &OracleType::LongRaw);
1474 assert_eq!(colinfo.len(), 2);
1475
1476 let mut stmt = conn.statement("select * from TestXml").build()?;
1477 let rows = stmt.query(&[])?;
1478 let colinfo = rows.column_info();
1479 assert_eq!(colinfo[0].name(), "INTCOL");
1480 assert_eq!(colinfo[0].oracle_type(), &OracleType::Number(9, 0));
1481 assert_eq!(colinfo[1].name(), "XMLCOL");
1482 assert_eq!(colinfo[1].oracle_type(), &OracleType::Xml);
1483 assert_eq!(colinfo.len(), 2);
1484
1485 Ok(())
1486 }
1487
1488 #[test]
1489 fn fetch_rows_to_vec() -> Result<()> {
1490 let conn = test_util::connect()?;
1491 // The fetch array size must be less than the number of rows in TestStrings
1492 // in order to make situation that a new fetch array buffer must allocated
1493 // in Stmt::fetch_rows().
1494 let mut stmt = conn
1495 .statement("select IntCol from TestStrings order by IntCol")
1496 .fetch_array_size(3)
1497 .build()?;
1498 let mut rows = Vec::new();
1499 for row_result in stmt.query(&[])? {
1500 rows.push(row_result?);
1501 }
1502 for (index, row) in rows.iter().enumerate() {
1503 let int_col: usize = row.get(0)?;
1504 assert_eq!(int_col, index + 1);
1505 }
1506 Ok(())
1507 }
1508}