odbc_api/
cursor.rs

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