Struct odbc_api::BlockCursor

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

In order to safe 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 safes 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 safes 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 themselfes. 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 appliacation can detect these truncations. This is usually the best choice, since individual fields in a table rarerly actuallly 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 [self::more_results`].
  2. Reusing the same buffer with a different statement.

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> RefUnwindSafe for BlockCursor<C, B>where B: RefUnwindSafe, C: RefUnwindSafe,

§

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 Twhere T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

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

const: unstable · source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

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

const: unstable · source§

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

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

const: unstable · source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

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

const: unstable · 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 Twhere U: Into<T>,

§

type Error = Infallible

The type returned in the event of a conversion error.
const: unstable · source§

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

Performs the conversion.
source§

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

§

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

The type returned in the event of a conversion error.
const: unstable · source§

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

Performs the conversion.