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}