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