odbc_api/
cursor.rs

1mod block_cursor;
2mod concurrent_block_cursor;
3
4use odbc_sys::HStmt;
5
6use crate::{
7    buffers::Indicator,
8    error::ExtendResult,
9    handles::{AsStatementRef, CDataMut, SqlResult, State, Statement, StatementRef},
10    parameter::{Binary, CElement, Text, VarCell, VarKind, WideText},
11    sleep::{wait_for, Sleep},
12    Error, ResultSetMetadata,
13};
14
15use std::{
16    mem::{size_of, MaybeUninit},
17    ptr,
18    thread::panicking,
19};
20
21pub use self::{block_cursor::BlockCursor, concurrent_block_cursor::ConcurrentBlockCursor};
22
23/// Cursors are used to process and iterate the result sets returned by executing queries.
24///
25/// # Example: Fetching result in batches
26///
27/// ```rust
28/// use odbc_api::{Cursor, buffers::{BufferDesc, ColumnarAnyBuffer}, Error};
29///
30/// /// Fetches all values from the first column of the cursor as i32 in batches of 100 and stores
31/// /// them in a vector.
32/// fn fetch_all_ints(cursor: impl Cursor) -> Result<Vec<i32>, Error> {
33///     let mut all_ints = Vec::new();
34///     // Batch size determines how many values we fetch at once.
35///     let batch_size = 100;
36///     // We expect the first column to hold INTEGERs (or a type convertible to INTEGER). Use
37///     // the metadata on the result set, if you want to investige the types of the columns at
38///     // runtime.
39///     let description = BufferDesc::I32 { nullable: false };
40///     // This is the buffer we bind to the driver, and repeatedly use to fetch each batch
41///     let buffer = ColumnarAnyBuffer::from_descs(batch_size, [description]);
42///     // Bind buffer to cursor
43///     let mut row_set_buffer = cursor.bind_buffer(buffer)?;
44///     // Fetch data batch by batch
45///     while let Some(batch) = row_set_buffer.fetch()? {
46///         all_ints.extend_from_slice(batch.column(0).as_slice().unwrap())
47///     }
48///     Ok(all_ints)
49/// }
50/// ```
51pub trait Cursor: ResultSetMetadata {
52    /// Advances the cursor to the next row in the result set. This is **Slow**. Bind
53    /// [`crate::buffers`] instead, for good performance.
54    ///
55    /// ⚠ While this method is very convenient due to the fact that the application does not have
56    /// to declare and bind specific buffers, it is also in many situations extremely slow. Concrete
57    /// performance depends on the ODBC driver in question, but it is likely it performs a roundtrip
58    /// to the datasource for each individual row. It is also likely an extra conversion is
59    /// performed then requesting individual fields, since the C buffer type is not known to the
60    /// driver in advance. Consider binding a buffer to the cursor first using
61    /// [`Self::bind_buffer`].
62    ///
63    /// That being said, it is a convenient programming model, as the developer does not need to
64    /// prepare and allocate the buffers beforehand. It is also a good way to retrieve really large
65    /// single values out of a data source (like one large text file). See [`CursorRow::get_text`].
66    fn next_row(&mut self) -> Result<Option<CursorRow<'_>>, Error> {
67        let row_available = unsafe {
68            self.as_stmt_ref()
69                .fetch()
70                .into_result_bool(&self.as_stmt_ref())?
71        };
72        let ret = if row_available {
73            Some(unsafe { CursorRow::new(self.as_stmt_ref()) })
74        } else {
75            None
76        };
77        Ok(ret)
78    }
79
80    /// Binds this cursor to a buffer holding a row set.
81    fn bind_buffer<B>(self, row_set_buffer: B) -> Result<BlockCursor<Self, B>, Error>
82    where
83        Self: Sized,
84        B: RowSetBuffer;
85
86    /// For some datasources it is possible to create more than one result set at once via a call to
87    /// execute. E.g. by calling a stored procedure or executing multiple SQL statements at once.
88    /// This method consumes the current cursor and creates a new one representing the next result
89    /// set should it exist.
90    fn more_results(self) -> Result<Option<Self>, Error>
91    where
92        Self: Sized;
93}
94
95/// An individual row of an result set. See [`crate::Cursor::next_row`].
96pub struct CursorRow<'s> {
97    statement: StatementRef<'s>,
98}
99
100impl<'s> CursorRow<'s> {
101    /// # Safety
102    ///
103    /// `statement` must be in a cursor state.
104    unsafe fn new(statement: StatementRef<'s>) -> Self {
105        CursorRow { statement }
106    }
107}
108
109impl CursorRow<'_> {
110    /// Fills a suitable target buffer with a field from the current row of the result set. This
111    /// method drains the data from the field. It can be called repeatedly to if not all the data
112    /// fit in the output buffer at once. It should not called repeatedly to fetch the same value
113    /// twice. Column index starts at `1`.
114    pub fn get_data(
115        &mut self,
116        col_or_param_num: u16,
117        target: &mut (impl CElement + CDataMut),
118    ) -> Result<(), Error> {
119        self.statement
120            .get_data(col_or_param_num, target)
121            .into_result(&self.statement)
122            .provide_context_for_diagnostic(|record, function| {
123                if record.state == State::INDICATOR_VARIABLE_REQUIRED_BUT_NOT_SUPPLIED {
124                    Error::UnableToRepresentNull(record)
125                } else {
126                    Error::Diagnostics { record, function }
127                }
128            })
129    }
130
131    /// Retrieves arbitrary large character data from the row and stores it in the buffer. Column
132    /// index starts at `1`. The used encoding is accordig to the ODBC standard determined by your
133    /// system local. Ultimatly the choice is up to the implementation of your ODBC driver, which
134    /// often defaults to always UTF-8.
135    ///
136    /// # Example
137    ///
138    /// Retrieve an arbitrary large text file from a database field.
139    ///
140    /// ```
141    /// use odbc_api::{Connection, Error, IntoParameter, Cursor};
142    ///
143    /// fn get_large_text(name: &str, conn: &mut Connection<'_>) -> Result<Option<String>, Error> {
144    ///     let query = "SELECT content FROM LargeFiles WHERE name=?";
145    ///     let parameters = &name.into_parameter();
146    ///     let timeout_sec = None;
147    ///     let mut cursor = conn
148    ///         .execute(query, parameters, timeout_sec)?
149    ///         .expect("Assume select statement creates cursor");
150    ///     if let Some(mut row) = cursor.next_row()? {
151    ///         let mut buf = Vec::new();
152    ///         row.get_text(1, &mut buf)?;
153    ///         let ret = String::from_utf8(buf).unwrap();
154    ///         Ok(Some(ret))
155    ///     } else {
156    ///         Ok(None)
157    ///     }
158    /// }
159    /// ```
160    ///
161    /// # Return
162    ///
163    /// `true` indicates that the value has not been `NULL` and the value has been placed in `buf`.
164    /// `false` indicates that the value is `NULL`. The buffer is cleared in that case.
165    pub fn get_text(&mut self, col_or_param_num: u16, buf: &mut Vec<u8>) -> Result<bool, Error> {
166        self.get_variadic::<Text>(col_or_param_num, buf)
167    }
168
169    /// Retrieves arbitrary large character data from the row and stores it in the buffer. Column
170    /// index starts at `1`. The used encoding is UTF-16.
171    ///
172    /// # Return
173    ///
174    /// `true` indicates that the value has not been `NULL` and the value has been placed in `buf`.
175    /// `false` indicates that the value is `NULL`. The buffer is cleared in that case.
176    pub fn get_wide_text(
177        &mut self,
178        col_or_param_num: u16,
179        buf: &mut Vec<u16>,
180    ) -> Result<bool, Error> {
181        self.get_variadic::<WideText>(col_or_param_num, buf)
182    }
183
184    /// Retrieves arbitrary large binary data from the row and stores it in the buffer. Column index
185    /// starts at `1`.
186    ///
187    /// # Return
188    ///
189    /// `true` indicates that the value has not been `NULL` and the value has been placed in `buf`.
190    /// `false` indicates that the value is `NULL`. The buffer is cleared in that case.
191    pub fn get_binary(&mut self, col_or_param_num: u16, buf: &mut Vec<u8>) -> Result<bool, Error> {
192        self.get_variadic::<Binary>(col_or_param_num, buf)
193    }
194
195    fn get_variadic<K: VarKind>(
196        &mut self,
197        col_or_param_num: u16,
198        buf: &mut Vec<K::Element>,
199    ) -> Result<bool, Error> {
200        if buf.capacity() == 0 {
201            // User did just provide an empty buffer. So it is fair to assume not much domain
202            // knowledge has been used to decide its size. We just default to 256 to increase the
203            // chance that we get it done with one alloctaion. The buffer size being 0 we need at
204            // least 1 anyway. If the capacity is not `0` we'll leave the buffer size untouched as
205            // we do not want to prevent users from providing better guessen based on domain
206            // knowledge.
207            // This also implicitly makes sure that we can at least hold one terminating zero.
208            buf.reserve(256);
209        }
210        // Utilize all of the allocated buffer.
211        buf.resize(buf.capacity(), K::ZERO);
212
213        // Did we learn how much capacity we need in the last iteration? We use this only to panic
214        // on erroneous implementations of get_data and avoid endless looping until we run out of
215        // memory.
216        let mut remaining_length_known = false;
217        // We repeatedly fetch data and add it to the buffer. The buffer length is therefore the
218        // accumulated value size. The target always points to the last window in buf which is going
219        // to contain the **next** part of the data, whereas buf contains the entire accumulated
220        // value so far.
221        let mut target =
222            VarCell::<&mut [K::Element], K>::from_buffer(buf.as_mut_slice(), Indicator::NoTotal);
223        self.get_data(col_or_param_num, &mut target)?;
224        while !target.is_complete() {
225            // Amount of payload bytes (excluding terminating zeros) fetched with the last call to
226            // get_data.
227            let fetched = target
228                .len_in_bytes()
229                .expect("ODBC driver must always report how many bytes were fetched.");
230            match target.indicator() {
231                // If Null the value would be complete
232                Indicator::Null => unreachable!(),
233                // We do not know how large the value is. Let's fetch the data with repeated calls
234                // to get_data.
235                Indicator::NoTotal => {
236                    let old_len = buf.len();
237                    // Use an exponential strategy for increasing buffer size.
238                    buf.resize(old_len * 2, K::ZERO);
239                    let buf_extend = &mut buf[(old_len - K::TERMINATING_ZEROES)..];
240                    target = VarCell::<&mut [K::Element], K>::from_buffer(
241                        buf_extend,
242                        Indicator::NoTotal,
243                    );
244                }
245                // We did not get all of the value in one go, but the data source has been friendly
246                // enough to tell us how much is missing.
247                Indicator::Length(len) => {
248                    if remaining_length_known {
249                        panic!(
250                            "SQLGetData has been unable to fetch all data, even though the \
251                            capacity of the target buffer has been adapted to hold the entire \
252                            payload based on the indicator of the last part. You may consider \
253                            filing a bug with the ODBC driver you are using."
254                        )
255                    }
256                    remaining_length_known = true;
257                    // Amount of bytes missing from the value using get_data, excluding terminating
258                    // zero.
259                    let still_missing_in_bytes = len - fetched;
260                    let still_missing = still_missing_in_bytes / size_of::<K::Element>();
261                    let old_len = buf.len();
262                    buf.resize(old_len + still_missing, K::ZERO);
263                    let buf_extend = &mut buf[(old_len - K::TERMINATING_ZEROES)..];
264                    target = VarCell::<&mut [K::Element], K>::from_buffer(
265                        buf_extend,
266                        Indicator::NoTotal,
267                    );
268                }
269            }
270            // Fetch binary data into buffer.
271            self.get_data(col_or_param_num, &mut target)?;
272        }
273        // We did get the complete value, including the terminating zero. Let's resize the buffer to
274        // match the retrieved value exactly (excluding terminating zero).
275        if let Some(len_in_bytes) = target.indicator().length() {
276            // Since the indicator refers to value length without terminating zero, and capacity is
277            // including the terminating zero this also implicitly drops the terminating zero at the
278            // end of the buffer.
279            let shrink_by_bytes = target.capacity_in_bytes() - len_in_bytes;
280            let shrink_by_chars = shrink_by_bytes / size_of::<K::Element>();
281            buf.resize(buf.len() - shrink_by_chars, K::ZERO);
282            Ok(true)
283        } else {
284            // value is NULL
285            buf.clear();
286            Ok(false)
287        }
288    }
289}
290
291/// Cursors are used to process and iterate the result sets returned by executing queries. Created
292/// by either a prepared query or direct execution. Usually utilized through the [`crate::Cursor`]
293/// trait.
294#[derive(Debug)]
295pub struct CursorImpl<Stmt: AsStatementRef> {
296    /// A statement handle in cursor mode.
297    statement: Stmt,
298}
299
300impl<S> Drop for CursorImpl<S>
301where
302    S: AsStatementRef,
303{
304    fn drop(&mut self) {
305        let mut stmt = self.statement.as_stmt_ref();
306        if let Err(e) = stmt.close_cursor().into_result(&stmt) {
307            // Avoid panicking, if we already have a panic. We don't want to mask the original
308            // error.
309            if !panicking() {
310                panic!("Unexpected error closing cursor: {e:?}")
311            }
312        }
313    }
314}
315
316impl<S> AsStatementRef for CursorImpl<S>
317where
318    S: AsStatementRef,
319{
320    fn as_stmt_ref(&mut self) -> StatementRef<'_> {
321        self.statement.as_stmt_ref()
322    }
323}
324
325impl<S> ResultSetMetadata for CursorImpl<S> where S: AsStatementRef {}
326
327impl<S> Cursor for CursorImpl<S>
328where
329    S: AsStatementRef,
330{
331    fn bind_buffer<B>(mut self, mut row_set_buffer: B) -> Result<BlockCursor<Self, B>, Error>
332    where
333        B: RowSetBuffer,
334    {
335        let stmt = self.statement.as_stmt_ref();
336        unsafe {
337            bind_row_set_buffer_to_statement(stmt, &mut row_set_buffer)?;
338        }
339        Ok(BlockCursor::new(row_set_buffer, self))
340    }
341
342    fn more_results(self) -> Result<Option<Self>, Error>
343    where
344        Self: Sized,
345    {
346        // Consume self without calling drop to avoid calling close_cursor.
347        let mut statement = self.into_stmt();
348        let mut stmt = statement.as_stmt_ref();
349
350        let has_another_result = unsafe { stmt.more_results() }.into_result_bool(&stmt)?;
351        let next = if has_another_result {
352            Some(CursorImpl { statement })
353        } else {
354            None
355        };
356        Ok(next)
357    }
358}
359
360impl<S> CursorImpl<S>
361where
362    S: AsStatementRef,
363{
364    /// Users of this library are encouraged not to call this constructor directly but rather invoke
365    /// [`crate::Connection::execute`] or [`crate::Prepared::execute`] to get a cursor and utilize
366    /// it using the [`crate::Cursor`] trait. This method is public so users with an understanding
367    /// of the raw ODBC C-API have a way to create a cursor, after they left the safety rails of the
368    /// Rust type System, in order to implement a use case not covered yet, by the safe abstractions
369    /// within this crate.
370    ///
371    /// # Safety
372    ///
373    /// `statement` must be in Cursor state, for the invariants of this type to hold.
374    pub unsafe fn new(statement: S) -> Self {
375        Self { statement }
376    }
377
378    /// Deconstructs the `CursorImpl` without calling drop. This is a way to get to the underlying
379    /// statement, while preventing a call to close cursor.
380    pub fn into_stmt(self) -> S {
381        // We want to move `statement` out of self, which would make self partially uninitialized.
382        let dont_drop_me = MaybeUninit::new(self);
383        let self_ptr = dont_drop_me.as_ptr();
384
385        // Safety: We know `dont_drop_me` is valid at this point so reading the ptr is okay
386        unsafe { ptr::read(&(*self_ptr).statement) }
387    }
388
389    pub(crate) fn as_sys(&mut self) -> HStmt {
390        self.as_stmt_ref().as_sys()
391    }
392}
393
394/// A Row set buffer binds row, or column wise buffers to a cursor in order to fill them with row
395/// sets with each call to fetch.
396///
397/// # Safety
398///
399/// Implementers of this trait must ensure that every pointer bound in `bind_to_cursor` stays valid
400/// even if an instance is moved in memory. Bound members should therefore be likely references
401/// themselves. To bind stack allocated buffers it is recommended to implement this trait on the
402/// reference type instead.
403pub unsafe trait RowSetBuffer {
404    /// Declares the bind type of the Row set buffer. `0` Means a columnar binding is used. Any non
405    /// zero number is interpreted as the size of a single row in a row wise binding style.
406    fn bind_type(&self) -> usize;
407
408    /// The batch size for bulk cursors, if retrieving many rows at once.
409    fn row_array_size(&self) -> usize;
410
411    /// Mutable reference to the number of fetched rows.
412    ///
413    /// # Safety
414    ///
415    /// Implementations of this method must take care that the returned referenced stays valid, even
416    /// if `self` should be moved.
417    fn mut_num_fetch_rows(&mut self) -> &mut usize;
418
419    /// Binds the buffer either column or row wise to the cursor.
420    ///
421    /// # Safety
422    ///
423    /// It's the implementation's responsibility to ensure that all bound buffers are valid until
424    /// unbound or the statement handle is deleted.
425    unsafe fn bind_colmuns_to_cursor(&mut self, cursor: StatementRef<'_>) -> Result<(), Error>;
426
427    /// Find an indicator larger than the maximum element size of the buffer.
428    fn find_truncation(&self) -> Option<TruncationInfo>;
429}
430
431/// Returned by [`RowSetBuffer::find_truncation`]. Contains information about the truncation found.
432#[derive(Clone, Copy, PartialEq, Eq, Debug)]
433pub struct TruncationInfo {
434    /// Length of the untruncated value if known
435    pub indicator: Option<usize>,
436    /// Zero based buffer index of the column in which the truncation occurred.
437    pub buffer_index: usize,
438}
439
440unsafe impl<T: RowSetBuffer> RowSetBuffer for &mut T {
441    fn bind_type(&self) -> usize {
442        (**self).bind_type()
443    }
444
445    fn row_array_size(&self) -> usize {
446        (**self).row_array_size()
447    }
448
449    fn mut_num_fetch_rows(&mut self) -> &mut usize {
450        (*self).mut_num_fetch_rows()
451    }
452
453    unsafe fn bind_colmuns_to_cursor(&mut self, cursor: StatementRef<'_>) -> Result<(), Error> {
454        (*self).bind_colmuns_to_cursor(cursor)
455    }
456
457    fn find_truncation(&self) -> Option<TruncationInfo> {
458        (**self).find_truncation()
459    }
460}
461
462/// The asynchronous sibiling of [`CursorImpl`]. Use this to fetch results in asynchronous code.
463///
464/// Like [`CursorImpl`] this is an ODBC statement handle in cursor state. However unlike its
465/// synchronous sibling this statement handle is in asynchronous polling mode.
466pub struct CursorPolling<Stmt: AsStatementRef> {
467    /// A statement handle in cursor state with asynchronous mode enabled.
468    statement: Stmt,
469}
470
471impl<S> CursorPolling<S>
472where
473    S: AsStatementRef,
474{
475    /// Users of this library are encouraged not to call this constructor directly. This method is
476    /// pubilc so users with an understanding of the raw ODBC C-API have a way to create an
477    /// asynchronous cursor, after they left the safety rails of the Rust type System, in order to
478    /// implement a use case not covered yet, by the safe abstractions within this crate.
479    ///
480    /// # Safety
481    ///
482    /// `statement` must be in Cursor state, for the invariants of this type to hold. Preferable
483    /// `statement` should also have asynchrous mode enabled, otherwise constructing a synchronous
484    /// [`CursorImpl`] is more suitable.
485    pub unsafe fn new(statement: S) -> Self {
486        Self { statement }
487    }
488
489    /// Binds this cursor to a buffer holding a row set.
490    pub fn bind_buffer<B>(
491        mut self,
492        mut row_set_buffer: B,
493    ) -> Result<BlockCursorPolling<Self, B>, Error>
494    where
495        B: RowSetBuffer,
496    {
497        let stmt = self.statement.as_stmt_ref();
498        unsafe {
499            bind_row_set_buffer_to_statement(stmt, &mut row_set_buffer)?;
500        }
501        Ok(BlockCursorPolling::new(row_set_buffer, self))
502    }
503}
504
505impl<S> AsStatementRef for CursorPolling<S>
506where
507    S: AsStatementRef,
508{
509    fn as_stmt_ref(&mut self) -> StatementRef<'_> {
510        self.statement.as_stmt_ref()
511    }
512}
513
514impl<S> Drop for CursorPolling<S>
515where
516    S: AsStatementRef,
517{
518    fn drop(&mut self) {
519        let mut stmt = self.statement.as_stmt_ref();
520        if let Err(e) = stmt.close_cursor().into_result(&stmt) {
521            // Avoid panicking, if we already have a panic. We don't want to mask the original
522            // error.
523            if !panicking() {
524                panic!("Unexpected error closing cursor: {e:?}")
525            }
526        }
527    }
528}
529
530/// Asynchronously iterates in blocks (called row sets) over a result set, filling a buffers with
531/// a lot of rows at once, instead of iterating the result set row by row. This is usually much
532/// faster. Asynchronous sibiling of [`self::BlockCursor`].
533pub struct BlockCursorPolling<C, B>
534where
535    C: AsStatementRef,
536{
537    buffer: B,
538    cursor: C,
539}
540
541impl<C, B> BlockCursorPolling<C, B>
542where
543    C: AsStatementRef,
544{
545    fn new(buffer: B, cursor: C) -> Self {
546        Self { buffer, cursor }
547    }
548
549    /// Fills the bound buffer with the next row set.
550    ///
551    /// # Return
552    ///
553    /// `None` if the result set is empty and all row sets have been extracted. `Some` with a
554    /// reference to the internal buffer otherwise.
555    pub async fn fetch(&mut self, sleep: impl Sleep) -> Result<Option<&B>, Error>
556    where
557        B: RowSetBuffer,
558    {
559        self.fetch_with_truncation_check(false, sleep).await
560    }
561
562    /// Fills the bound buffer with the next row set. Should `error_for_truncation` be `true`and any
563    /// diagnostic indicate truncation of a value an error is returned.
564    ///
565    /// # Return
566    ///
567    /// `None` if the result set is empty and all row sets have been extracted. `Some` with a
568    /// reference to the internal buffer otherwise.
569    ///
570    /// Call this method to find out whether there are any truncated values in the batch, without
571    /// inspecting all its rows and columns.
572    pub async fn fetch_with_truncation_check(
573        &mut self,
574        error_for_truncation: bool,
575        mut sleep: impl Sleep,
576    ) -> Result<Option<&B>, Error>
577    where
578        B: RowSetBuffer,
579    {
580        let mut stmt = self.cursor.as_stmt_ref();
581        let result = unsafe { wait_for(|| stmt.fetch(), &mut sleep).await };
582        let has_row = error_handling_for_fetch(result, stmt, &self.buffer, error_for_truncation)?;
583        Ok(has_row.then_some(&self.buffer))
584    }
585}
586
587/// Binds a row set buffer to a statment. Implementation is shared between synchronous and
588/// asynchronous cursors.
589unsafe fn bind_row_set_buffer_to_statement(
590    mut stmt: StatementRef<'_>,
591    row_set_buffer: &mut impl RowSetBuffer,
592) -> Result<(), Error> {
593    stmt.set_row_bind_type(row_set_buffer.bind_type())
594        .into_result(&stmt)?;
595    let size = row_set_buffer.row_array_size();
596    stmt.set_row_array_size(size)
597        .into_result(&stmt)
598        // SAP anywhere has been seen to return with an "invalid attribute" error instead of
599        // a success with "option value changed" info. Let us map invalid attributes during
600        // setting row set array size to something more precise.
601        .provide_context_for_diagnostic(|record, function| {
602            if record.state == State::INVALID_ATTRIBUTE_VALUE {
603                Error::InvalidRowArraySize { record, size }
604            } else {
605                Error::Diagnostics { record, function }
606            }
607        })?;
608    stmt.set_num_rows_fetched(row_set_buffer.mut_num_fetch_rows())
609        .into_result(&stmt)?;
610    row_set_buffer.bind_colmuns_to_cursor(stmt)?;
611    Ok(())
612}
613
614/// Error handling for bulk fetching is shared between synchronous and asynchronous usecase.
615fn error_handling_for_fetch(
616    result: SqlResult<()>,
617    mut stmt: StatementRef,
618    buffer: &impl RowSetBuffer,
619    error_for_truncation: bool,
620) -> Result<bool, Error> {
621    // Only check for truncation if a) the user indicated that he wants to error instead of just
622    // ignoring it and if there is at least one diagnostic record. ODBC standard requires a
623    // diagnostic record to be there in case of truncation. Sadly we can not rely on this particular
624    // record to be there, as the driver could generate a large amount of diagnostic records,
625    // while we are limited in the amount we can check. The second check serves as an optimization
626    // for the happy path.
627    if error_for_truncation && result == SqlResult::SuccessWithInfo(()) {
628        if let Some(TruncationInfo {
629            indicator,
630            buffer_index,
631        }) = buffer.find_truncation()
632        {
633            return Err(Error::TooLargeValueForBuffer {
634                indicator,
635                buffer_index,
636            });
637        }
638    }
639
640    let has_row = result
641        .on_success(|| true)
642        .into_result_with(&stmt.as_stmt_ref(), Some(false), None)
643        // Oracle's ODBC driver does not support 64Bit integers. Furthermore, it does not
644        // tell it to the user when binding parameters, but rather now then we fetch
645        // results. The error code returned is `HY004` rather than `HY003` which should
646        // be used to indicate invalid buffer types.
647        .provide_context_for_diagnostic(|record, function| {
648            if record.state == State::INVALID_SQL_DATA_TYPE {
649                Error::OracleOdbcDriverDoesNotSupport64Bit(record)
650            } else {
651                Error::Diagnostics { record, function }
652            }
653        })?;
654    Ok(has_row)
655}
656
657impl<C, B> Drop for BlockCursorPolling<C, B>
658where
659    C: AsStatementRef,
660{
661    fn drop(&mut self) {
662        if let Err(e) = unbind_buffer_from_cursor(&mut self.cursor) {
663            // Avoid panicking, if we already have a panic. We don't want to mask the original
664            // error.
665            if !panicking() {
666                panic!("Unexpected error unbinding columns: {e:?}")
667            }
668        }
669    }
670}
671
672/// Unbinds buffer and num_rows_fetched from the cursor. This implementation is shared between
673/// unbind and the drop handler, and the synchronous and asynchronous variant.
674fn unbind_buffer_from_cursor(cursor: &mut impl AsStatementRef) -> Result<(), Error> {
675    // Now that we have cursor out of block cursor, we need to unbind the buffer.
676    let mut stmt = cursor.as_stmt_ref();
677    stmt.unbind_cols().into_result(&stmt)?;
678    stmt.unset_num_rows_fetched().into_result(&stmt)?;
679    Ok(())
680}