pub struct ReaderQuery<'r> { /* private fields */ }Expand description
Borrows a Reader exclusively while the query is being constructed and
(eventually) the cursor is live.
ReaderQuery is Send, but not safe for concurrent access. It may be
moved to another thread after an explicit happens-before hand-off. Any
installed failover callback must therefore also be Send; it runs on
whichever thread subsequently drives the cursor.
Implementations§
Source§impl<'r> ReaderQuery<'r>
impl<'r> ReaderQuery<'r>
Sourcepub fn initial_credit(self, credit: u64) -> Self
pub fn initial_credit(self, credit: u64) -> Self
Override the initial_credit (bytes; 0 = unbounded).
Sourcepub fn reset_symbol_dict(self, reset: bool) -> Self
pub fn reset_symbol_dict(self, reset: bool) -> Self
Request a query-scoped SYMBOL dict: the server resets the connection
dict before streaming this query so it never inherits symbols from
earlier queries on the same connection. Silently no-op against a server
that does not advertise CAP_QUERY_FLAGS.
Sourcepub fn on_failover_reset<F>(self, callback: F) -> Self
pub fn on_failover_reset<F>(self, callback: F) -> Self
Install a callback fired every time the cursor’s underlying
connection is replaced via mid-query failover. The closure
receives a FailoverResetEvent describing the new endpoint and
runs before any replayed RESULT_BATCH arrives — the
user-side handler must use this signal to discard rows it had
accumulated from the previous (now-dead) connection. The query
restarts from batch_seq=0 against the new endpoint with a
fresh request_id.
Installing this callback is the caller’s opt-in to “I will
handle replay-after-data-delivered correctly.” Without it,
Cursor::next_batch refuses to fail over once any batch has
been yielded — returning
crate::ErrorCode::FailoverWouldDuplicate
instead — to avoid silently doubling up rows in the caller’s
accumulator. Initial-connect failover (before any batch is
yielded) is transparent and does not require this callback.
Calling this method twice on the same ReaderQuery replaces
the previous closure — only the most recent callback is invoked.
The callback must be Send: a query/cursor may be handed to
another thread, and the callback then runs and is dropped on that
destination thread. This bound is required even if the caller never
migrates the handle.
Mirrors the Java client’s onFailoverReset(newNode) contract.
§Panics from the callback
The callback is invoked synchronously from inside
Cursor::next_batch (specifically, from the failover-replay
path). If the callback panics, the unwind propagates through
next_batch to the caller. The cursor’s Drop still runs,
which closes the WebSocket cleanly, so no resources are leaked
— but the Cursor is gone. There is no “swallow and resume”
behavior; treat a panicking callback as a bug and either
catch_unwind inside the callback yourself or ensure the
callback is panic-free. The C FFI binding wraps the callback in
catch_unwind + abort() (panics across the C boundary are
undefined behavior); the pure-Rust API leaves them as normal
unwinds.
use std::sync::{Arc, Mutex};
use questdb::egress::{FailoverResetEvent, Reader};
let mut reader = Reader::from_conf(
"ws::addr=db-a:9000,db-b:9000;target=primary",
)?;
// The handler accumulates rows in a buffer shared with the
// callback. On failover the callback discards what was buffered
// — the replayed query restarts at `batch_seq=0` against the
// new endpoint, so anything already pushed would otherwise
// double up.
let rows: Arc<Mutex<Vec<i64>>> = Arc::new(Mutex::new(Vec::new()));
let rows_for_cb = Arc::clone(&rows);
let mut cursor = reader
.prepare("select x from t order by ts")
.on_failover_reset(move |ev: &FailoverResetEvent| {
eprintln!(
"failover: {} → {} after {} attempt(s) ({:?}, trigger={:?}: {})",
ev.failed_addr, ev.new_addr,
ev.attempts, ev.elapsed,
ev.trigger.code(), ev.trigger.msg(),
);
rows_for_cb.lock().unwrap().clear();
})
.execute()?;
while let Some(_batch) = cursor.next_batch()? {
// ... project `_batch` into `rows.lock().unwrap()` ...
}Sourcepub fn on_failover_progress<F>(self, callback: F) -> Self
pub fn on_failover_progress<F>(self, callback: F) -> Self
Install a callback fired at every phase of a mid-query failover
lifecycle: Disconnected when the cursor’s connection dies,
Retrying before each reconnect dial attempt, Reset after a
successful failover (immediately before
Self::on_failover_reset runs), and GaveUp when the retry
budget is exhausted.
This callback is observational: installing it does not authorize
replay after a batch has already reached the caller. Install
Self::on_failover_reset as well when the caller can discard partial
results safely. Without a reset callback, a post-delivery failure still
returns ErrorCode::FailoverWouldDuplicate.
Calling this method twice on the same ReaderQuery replaces
the previous closure — only the most recent callback is invoked.
The callback must be Send: a query/cursor may be handed to
another thread, and the callback then runs and is dropped on that
destination thread. This bound is required even if the caller never
migrates the handle.
§Reentrancy
The callback is invoked synchronously on the cursor’s drive
thread, while Cursor::next_batch (or add_credit) is
mid-mutation of the underlying Reader. The same contract as
Self::on_failover_reset applies:
- Must not call back into the originating reader, query, or cursor — including read-only stat getters.
- Must not panic /
longjmp/ unwind across the boundary (the FFI trampolinecatch_unwind+aborts on escape). - Must not block indefinitely — every batch read, CREDIT grant, and cancel waits until the callback returns.
pub fn bind_null(self, kind: SimpleNullKind) -> Self
pub fn bind_bool(self, v: bool) -> Self
pub fn bind_i8(self, v: i8) -> Self
pub fn bind_i16(self, v: i16) -> Self
pub fn bind_i32(self, v: i32) -> Self
pub fn bind_i64(self, v: i64) -> Self
pub fn bind_f32(self, v: f32) -> Self
pub fn bind_f64(self, v: f64) -> Self
pub fn bind_timestamp_micros(self, v: i64) -> Self
pub fn bind_timestamp_nanos(self, v: i64) -> Self
pub fn bind_date_millis(self, v: i64) -> Self
pub fn bind_uuid(self, v: [u8; 16]) -> Self
pub fn bind_long256(self, v: [u8; 32]) -> Self
pub fn bind_char(self, v: u16) -> Self
pub fn bind_ipv4(self, v: Ipv4Addr) -> Self
pub fn bind_varchar<S: Into<String>>(self, v: S) -> Self
pub fn bind_decimal64(self, value: i64, scale: i8) -> Self
pub fn bind_decimal128(self, value: i128, scale: i8) -> Self
pub fn bind_decimal256(self, bytes: [u8; 32], scale: i8) -> Self
pub fn bind_geohash(self, value: u64, precision_bits: u8) -> Self
pub fn bind_binary<B: Into<Vec<u8>>>(self, v: B) -> Self
pub fn bind_null_varchar(self) -> Self
pub fn bind_null_binary(self) -> Self
pub fn bind_null_decimal64(self, scale: i8) -> Self
pub fn bind_null_decimal128(self, scale: i8) -> Self
pub fn bind_null_decimal256(self, scale: i8) -> Self
pub fn bind_null_geohash(self, precision_bits: u8) -> Self
Auto Trait Implementations§
impl<'r> !RefUnwindSafe for ReaderQuery<'r>
impl<'r> !Sync for ReaderQuery<'r>
impl<'r> !UnwindSafe for ReaderQuery<'r>
impl<'r> Freeze for ReaderQuery<'r>
impl<'r> Send for ReaderQuery<'r>
impl<'r> Unpin for ReaderQuery<'r>
impl<'r> UnsafeUnpin for ReaderQuery<'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