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<'_>
impl Cursor<'_>
Sourcepub fn next_polars(&mut self) -> Result<Option<DataFrame>>
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.
Sourcepub fn fetch_all_polars(&mut self) -> Result<DataFrame>
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>
impl<'r> Cursor<'r>
pub fn request_id(&self) -> i64
Sourcepub fn terminal(&self) -> Option<&Terminal>
pub fn terminal(&self) -> Option<&Terminal>
Some after a RESULT_END or EXEC_DONE has been observed.
Sourcepub fn connection_reusable(&self) -> bool
pub fn connection_reusable(&self) -> bool
Whether dropping this cursor leaves its reader connection reusable.
Sourcepub fn credit_granted_total(&self) -> u64
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.
Sourcepub fn next_batch(&mut self) -> Result<Option<BatchView<'_>>>
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.
Sourcepub fn as_arrow_reader<'c>(
&'c mut self,
) -> Result<CursorRecordBatchReader<'r, 'c>>
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.
Sourcepub fn fetch_all_arrow(&mut self) -> Result<(SchemaRef, Vec<RecordBatch>)>
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.
Sourcepub fn iter_polars<'c>(&'c mut self) -> Result<CursorPolarsIter<'r, 'c>>
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.
Sourcepub fn next_arrow_batch(&mut self) -> Result<Option<RecordBatch>>
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.
Sourcepub fn failover_resets(&self) -> u32
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.
Sourcepub fn stale_plan_retries(&self) -> u32
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.
Sourcepub fn current_addr(&self) -> &Endpoint
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).
Sourcepub fn server_version(&self) -> Result<u8>
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.
Sourcepub fn server_info(&self) -> Option<&ServerInfo>
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.
Sourcepub fn cancel(&mut self) -> Result<()>
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.
Sourcepub fn add_credit(&mut self, additional_bytes: u64) -> Result<()>
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§
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<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedExplicit<'a, E> for Twhere
T: 'a,
Source§impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
impl<'a, T, E> AsTaggedImplicit<'a, E> for Twhere
T: 'a,
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
Source§impl<T> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
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