odbc_api

Struct BlockCursor

Source
pub struct BlockCursor<C: AsStatementRef, B> { /* private fields */ }
Expand description

In order to save on network overhead, it is recommended to use block cursors instead of fetching values individually. This can greatly reduce the time applications need to fetch data. You can create a block cursor by binding preallocated memory to a cursor using Cursor::bind_buffer. A block cursor saves on a lot of IO overhead by fetching an entire set of rows (called rowset) at once into the buffer bound to it. Reusing the same buffer for each rowset also saves on allocations. A challange with using block cursors might be database schemas with columns there individual fields can be very large. In these cases developers can choose to:

  1. Reserve less memory for each individual field than the schema indicates and deciding on a sensible upper bound themselves. This risks truncation of values though, if they are larger than the upper bound. Using BlockCursor::fetch_with_truncation_check instead of Cursor::next_row your application can detect these truncations. This is usually the best choice, since individual fields in a table rarely actually take up several GiB of memory.
  2. Calculate the number of rows dynamically based on the maximum expected row size. crate::buffers::BufferDesc::bytes_per_row, can be helpful with this task.
  3. Not use block cursors and fetch rows slowly with high IO overhead. Calling [CursorRow::get_data] and [CursorRow::get_text] to fetch large individual values.

See: https://learn.microsoft.com/en-us/sql/odbc/reference/develop-app/block-cursors

Implementations§

Source§

impl<C, B> BlockCursor<C, B>
where C: Cursor,

Source

pub fn fetch(&mut self) -> Result<Option<&B>, Error>
where B: RowSetBuffer,

Fills the bound buffer with the next row set.

§Return

None if the result set is empty and all row sets have been extracted. Some with a reference to the internal buffer otherwise.

use odbc_api::{buffers::TextRowSet, Cursor};

fn print_all_values(mut cursor: impl Cursor) {
    let batch_size = 100;
    let max_string_len = 4000;
    let buffer = TextRowSet::for_cursor(batch_size, &mut cursor, Some(4000)).unwrap();
    let mut cursor = cursor.bind_buffer(buffer).unwrap();
    // Iterate over batches
    while let Some(batch) = cursor.fetch().unwrap() {
        // ... print values in batch ...
    }
}
Source

pub fn fetch_with_truncation_check( &mut self, error_for_truncation: bool, ) -> Result<Option<&B>, Error>
where B: RowSetBuffer,

Fills the bound buffer with the next row set. Should error_for_truncation be trueand any diagnostic indicate truncation of a value an error is returned.

§Return

None if the result set is empty and all row sets have been extracted. Some with a reference to the internal buffer otherwise.

Call this method to find out wether there are any truncated values in the batch, without inspecting all its rows and columns.

use odbc_api::{buffers::TextRowSet, Cursor};

fn print_all_values(mut cursor: impl Cursor) {
    let batch_size = 100;
    let max_string_len = 4000;
    let buffer = TextRowSet::for_cursor(batch_size, &mut cursor, Some(4000)).unwrap();
    let mut cursor = cursor.bind_buffer(buffer).unwrap();
    // Iterate over batches
    while let Some(batch) = cursor.fetch_with_truncation_check(true).unwrap() {
        // ... print values in batch ...
    }
}
Source

pub fn unbind(self) -> Result<(C, B), Error>

Unbinds the buffer from the underlying statement handle. Potential usecases for this function include.

  1. Binding a different buffer to the “same” cursor after letting it point to the next result set obtained with Cursor::more_results`.
  2. Reusing the same buffer with a different statement.
Source§

impl<C, B> BlockCursor<C, B>

Source

pub fn row_array_size(&self) -> usize

Maximum amount of rows fetched from the database in the next call to fetch.

Trait Implementations§

Source§

impl<C, B> Drop for BlockCursor<C, B>
where C: AsStatementRef,

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more

Auto Trait Implementations§

§

impl<C, B> Freeze for BlockCursor<C, B>
where B: Freeze, C: Freeze,

§

impl<C, B> RefUnwindSafe for BlockCursor<C, B>

§

impl<C, B> Send for BlockCursor<C, B>
where B: Send, C: Send,

§

impl<C, B> Sync for BlockCursor<C, B>
where B: Sync, C: Sync,

§

impl<C, B> Unpin for BlockCursor<C, B>
where B: Unpin, C: Unpin,

§

impl<C, B> UnwindSafe for BlockCursor<C, B>
where B: UnwindSafe, C: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.