Skip to main content

Cursor

Struct Cursor 

Source
pub struct Cursor<'r> { /* private fields */ }
Expand description

Streaming cursor over RESULT_BATCH frames.

next_batch advances the stream by one batch, returning None once a terminal frame arrives (which is then accessible via Cursor::terminal). cancel sends a CANCEL frame and drains until the server’s terminal.

Cursor is Send, but not safe for concurrent access. It may be moved to another thread after an explicit happens-before hand-off. Failover callbacks run on whichever thread drives the cursor.

Implementations§

Source§

impl Cursor<'_>

Source

pub fn next_polars(&mut self) -> Result<Option<DataFrame>>

Decode one batch as a Polars DataFrame. Ok(None) on stream end.

This is the low-level per-batch entry point and does not detect mid-stream Arrow schema drift; if a later batch’s schema differs from earlier ones the resulting DataFrames will simply disagree on columns. Use Cursor::iter_polars for a drift-checked iterator, or Cursor::fetch_all_polars / Cursor::as_arrow_reader for higher-level adapters that pin the schema on first batch.

Source

pub fn fetch_all_polars(&mut self) -> Result<DataFrame>

Eagerly drain into one chunked Polars DataFrame. A stream that yields a schema but no batches becomes an empty DataFrame; only a stream without a schema (e.g. cancelled pre-prelude) errors as NoSchema. Drift detection is inherited from Cursor::iter_polars.

Source§

impl<'r> Cursor<'r>

Source

pub fn request_id(&self) -> i64

Source

pub fn terminal(&self) -> Option<&Terminal>

Some after a RESULT_END or EXEC_DONE has been observed.

Source

pub fn connection_reusable(&self) -> bool

Whether dropping this cursor leaves its reader connection reusable.

Source

pub fn credit_granted_total(&self) -> u64

Pass-through to Reader::credit_granted_total. Exists so callers holding the cursor’s mutable borrow on the reader can still observe the connection-level CREDIT-bytes counter.

Source

pub fn next_batch(&mut self) -> Result<Option<BatchView<'_>>>

Advance the cursor by one batch. Returns Ok(None) when the stream has terminated (success). QUERY_ERROR becomes Err.

On a transport-level failure (socket close, TLS error, WS framing error), the cursor will reconnect to the next address in the configured list (with exponential backoff and a bounded retry budget — see failover_* config keys), replay the QUERY_REQUEST with a fresh request_id, and resume from batch_seq=0 on the new connection. The user-side handler is notified before any replayed batches arrive via the ReaderQuery::on_failover_reset callback. If failover is disabled (failover=off) or the retry budget is exhausted, the failure is surfaced as the underlying error.

Silent-duplicate guard. If a batch has already been yielded to the caller and no on_failover_reset callback was installed, the cursor refuses to fail over and returns crate::ErrorCode::FailoverWouldDuplicate instead. Replay would otherwise re-deliver rows the caller already consumed — with no signal — because the server restarts streaming from batch_seq=0 on the new connection. Install the callback (and discard partial state on each invocation) to opt in to seeing replays; otherwise re-execute the query from scratch when this error fires. Failover that happens before the first batch is yielded — including initial connect failover — is unaffected and remains transparent.

Failover-eligible decode errors (malformed payload, bad varint, zstd corruption) use the same reconnect-and-replay path as transport failures. Replaying after rows were already yielded is still blocked unless the caller installed a replay-aware callback, since the server restarts streaming from batch_seq=0.

Blocking time during failover. When failover is engaged, this method blocks the calling thread for the duration of the reconnect cycle: each attempt sleeps the configured backoff (capped by failover_backoff_max_ms), then dials, handshakes, and reads SERVER_INFO against the next endpoint. The worst-case wall-clock blocking time is approximately 2 × (failover_max_attempts - 1) × failover_backoff_max_ms plus per-attempt connect+handshake overhead — with the parse-time caps that’s up to ~2 hours. There is no per-call timeout or AtomicBool cancel hook; use on_failover_progress for observability. If you need bounded latency, set failover_max_attempts and failover_backoff_max_ms to values appropriate for your SLA, or set failover=off and handle reconnect at the application layer.

Source

pub fn as_arrow_reader<'c>( &'c mut self, ) -> Result<CursorRecordBatchReader<'r, 'c>>

Wrap this cursor as an Arrow RecordBatchReader. Blocks until the first RESULT_BATCH is decoded, then snapshots its schema. Mid-stream schema drift poisons the adapter; re-wrap to resume. Returns ErrorCode::NoSchema if the stream terminates before any batch is produced.

Source

pub fn fetch_all_arrow(&mut self) -> Result<(SchemaRef, Vec<RecordBatch>)>

Eagerly drain every batch and return them together with the pinned Arrow schema. Symmetric with Cursor::fetch_all_polars. Errors as ErrorCode::NoSchema if the stream ends without producing a batch; surfaces drift as ErrorCode::SchemaDrift.

Source

pub fn iter_polars<'c>(&'c mut self) -> Result<CursorPolarsIter<'r, 'c>>

Drift-checked iterator over Polars DataFrames, one per QWP batch. Snapshots the first batch’s Arrow schema and yields Err(SchemaDrift) then terminates if a later batch diverges. Returns Err(NoSchema) if the stream ends before any batch is produced.

Use this in preference to a while let Some(df) = cursor.next_polars()? loop when you care about schema consistency mid-stream.

Source

pub fn next_arrow_batch(&mut self) -> Result<Option<RecordBatch>>

Next batch as an Arrow RecordBatch. Ok(None) on stream end; replays terminal errors like Cursor::next_batch. No drift check — use Cursor::as_arrow_reader for that.

Source

pub fn failover_resets(&self) -> u32

Number of successful failover reconnects this cursor has observed since execute(). Useful for tests asserting the query did or did not silently restart.

Source

pub fn stale_plan_retries(&self) -> u32

Number of times this cursor transparently re-issued its query on the current connection after the server reported the transient stale-cached-plan INTERNAL_ERROR (see Cursor::next_batch). Stays 0 on the happy path; exposed for tests and diagnostics that want to confirm the self-heal fired (and how often) without the caller ever seeing the underlying error.

Source

pub fn current_addr(&self) -> &Endpoint

The endpoint the cursor’s underlying connection is currently bound to. While the cursor is live the Reader is mutably borrowed, so Reader::current_addr is unreachable from user code — this is the in-cursor accessor for “which endpoint did the last batch come from?”. After mid-query failover, this reflects the new endpoint (matching the new_addr from the most recent crate::egress::FailoverResetEvent).

Source

pub fn server_version(&self) -> Result<u8>

Negotiated QWP version of the cursor’s underlying connection. The in-cursor accessor for Reader::server_version, unreachable from user code while the cursor holds the Reader’s mutable borrow. Reflects the renegotiated version after mid-query failover.

Source

pub fn server_info(&self) -> Option<&ServerInfo>

SERVER_INFO of the cursor’s currently connected endpoint; None only while a reconnect is in flight (the single QWP version always supplies it). The in-cursor accessor for Reader::server_info, unreachable from user code while the cursor holds the Reader’s mutable borrow. Reflects the new endpoint after mid-query failover.

Source

pub fn cancel(&mut self) -> Result<()>

Send a CANCEL frame and drain until the server emits a terminal frame for this request.

Blocking, but bounded. The CANCEL write inherits the transport’s WRITE_TIMEOUT; immediately after the CANCEL is accepted by the kernel send buffer, the read timeout is tightened to CANCEL_DRAIN_READ_TIMEOUT and the write timeout to CLOSE_TIMEOUT for the duration of the credit-nudge + drain. That bounds the worst-case latency at one WRITE_TIMEOUT (CANCEL) + CLOSE_TIMEOUT (nudge) + CANCEL_DRAIN_READ_TIMEOUT (drain) — installing the drain bounds before the nudge avoids a second WRITE_TIMEOUT window on a stuck TLS peer. If the CANCEL write itself fails, the transport is torn down before the error is returned so the cursor’s flags and the underlying connection state are left coherent.

Source

pub fn add_credit(&mut self, additional_bytes: u64) -> Result<()>

Manually grant the server additional_bytes of read budget on this cursor’s request. Useful when the user wants a larger outstanding window than the per-batch auto-replenishment would give them, or when initial_credit was 0 but the user changes their mind mid-stream.

Mirrors Self::next_batch’s failover policy: a transport- class write failure on the current connection triggers a reconnect-and-replay (when the connect string declares failover endpoints), after which the credit frame is re-sent on the new connection so the user’s grant is preserved. If the reconnect fails or the failure is not failover-eligible (auth/config/protocol), the cursor is torn down so a follow-up next_batch sees a dead cursor instead of silently failing over.

Trait Implementations§

Source§

impl Drop for Cursor<'_>

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more

Auto Trait Implementations§

§

impl<'r> !Freeze for Cursor<'r>

§

impl<'r> !RefUnwindSafe for Cursor<'r>

§

impl<'r> !Sync for Cursor<'r>

§

impl<'r> !UnwindSafe for Cursor<'r>

§

impl<'r> Send for Cursor<'r>

§

impl<'r> Unpin for Cursor<'r>

§

impl<'r> UnsafeUnpin for Cursor<'r>

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<'a, T, E> AsTaggedExplicit<'a, E> for T
where T: 'a,

Source§

fn explicit(self, class: Class, tag: u32) -> TaggedParser<'a, Explicit, Self, E>

Source§

impl<'a, T, E> AsTaggedImplicit<'a, E> for T
where T: 'a,

Source§

fn implicit( self, class: Class, constructed: bool, tag: u32, ) -> TaggedParser<'a, Implicit, Self, E>

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<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

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> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T> Pointable for T

Source§

const ALIGN: usize

The alignment of pointer.
Source§

type Init = T

The type for initializers.
Source§

unsafe fn init(init: <T as Pointable>::Init) -> usize

Initializes a with the given initializer. Read more
Source§

unsafe fn deref<'a>(ptr: usize) -> &'a T

Dereferences the given pointer. Read more
Source§

unsafe fn deref_mut<'a>(ptr: usize) -> &'a mut T

Mutably dereferences the given pointer. Read more
Source§

unsafe fn drop(ptr: usize)

Drops the object pointed to by the given pointer. Read more
Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
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.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V