pub struct Rowset<'conn> { /* private fields */ }Expand description
A streaming result set from a SQL query.
Rowset provides memory-efficient streaming access to query results.
Results are fetched on-demand in chunks, keeping memory usage constant
regardless of result set size. This makes it safe for any result size,
from a single row to billions of rows.
§Example
let mut result = conn.execute_query("SELECT * FROM big_table")?;
while let Some(chunk) = result.next_chunk()? {
for row in &chunk {
// Generic typed access (like C++ row.get<T>())
let id: Option<i32> = row.get(0);
let value: Option<f64> = row.get(1);
// Or direct accessors for performance
let id = row.get_i32(0);
let value = row.get_f64(1);
}
}§Memory Behavior
- Only one chunk is held in memory at a time
- Default chunk size is 64K rows (~few MB depending on row width)
- Memory usage is
O(chunk_size), notO(total_rows) - Safe for billion-row results
Implementations§
Source§impl<'conn> Rowset<'conn>
impl<'conn> Rowset<'conn>
Sourcepub fn schema(&self) -> Option<ResultSchema>
pub fn schema(&self) -> Option<ResultSchema>
Returns the schema (column metadata) for the result set.
For TCP connections, the schema is captured from the RowDescription message
after the first chunk is read. For gRPC connections, the schema is available
immediately from the Arrow data.
Returns None if no data has been read yet (TCP only).
§Example
let mut result = conn.execute_query("SELECT id, name FROM users")?;
// Read first chunk to capture schema (TCP) or get it immediately (gRPC)
let _ = result.next_chunk()?;
if let Some(schema) = result.schema() {
for col in schema.columns() {
println!("Column: {} ({})", col.name(), col.sql_type());
}
}Sourcepub fn next_chunk(&mut self) -> Result<Option<Vec<Row>>>
pub fn next_chunk(&mut self) -> Result<Option<Vec<Row>>>
Returns the next chunk of rows from the result set.
Each chunk contains up to chunk_size rows (default 64K).
Returns Ok(None) when all rows have been consumed.
§Example
while let Some(chunk) = result.next_chunk()? {
for row in &chunk {
let id: Option<i32> = row.get(0); // Generic typed access
let value = row.get_f64(1); // Direct accessor
}
}§Errors
- Returns
crate::Error::Clientif the server sends anErrorResponsewhile streaming the result set. - Returns
crate::Error::Ioon transport-level I/O failures. - Returns
crate::Error::Otherif an Arrow IPC chunk cannot be decoded.
Sourcepub fn rows(self) -> RowIterator<'conn> ⓘ
pub fn rows(self) -> RowIterator<'conn> ⓘ
Returns an iterator over all rows in the result set.
This provides a C++-like iteration experience while maintaining Rust’s explicit error handling. Chunks are fetched internally as needed, keeping memory usage constant.
§Example
// Simple iteration (like C++)
let result = conn.execute_query("SELECT * FROM users")?;
for row in result.rows() {
let row = row?; // Handle potential network errors
let id: Option<i32> = row.get(0);
let name: Option<String> = row.get(1);
println!("User: {:?} - {:?}", id, name);
}§Error Handling
Unlike C++ which uses exceptions, Rust requires explicit error handling.
Each item in the iterator is a Result<LightweightRow> to handle
potential network or protocol errors during streaming.
§Comparison with next_chunk()
| Aspect | rows() | next_chunk() |
|---|---|---|
| Syntax | Simpler, C++-like | More verbose |
| Error handling | Per-row with ? | Per-chunk |
| Batch ops | Use .collect() | Natural |
| Best for | Simple iteration | Batch processing |
Sourcepub fn collect_rows(self) -> Result<Vec<Row>>
pub fn collect_rows(self) -> Result<Vec<Row>>
Collects all rows into a Vec.
This is a convenience method that handles error collection more elegantly
than the standard collect::<Result<Vec<_>, _>>() pattern.
§Example
let result = conn.execute_query("SELECT id, name FROM users")?;
let rows = result.collect_rows()?; // Much cleaner than collect::<Result<Vec<_>, _>>()
for row in rows {
let id: Option<i32> = row.get(0);
let name: Option<String> = row.get(1);
println!("User: {:?} - {:?}", id, name);
}§Errors
Returns the first error produced by next_chunk
while draining the stream (transport I/O failure or server-side
error).
Sourcepub fn collect_column<T: RowValue>(self) -> Result<Vec<Option<T>>>
pub fn collect_column<T: RowValue>(self) -> Result<Vec<Option<T>>>
Collects the first column of each row into a Vec.
This is useful for single-column queries or when you only need one column.
§Example
let result = conn.execute_query("SELECT name FROM users")?;
let names: Vec<Option<String>> = result.collect_column()?;
for name in names {
if let Some(name) = name {
println!("User: {}", name);
}
}§Errors
Returns the first streaming error from
next_chunk. SQL NULL cells yield
Option::None entries, not errors.
Sourcepub fn collect_column_non_null<T: RowValue>(self) -> Result<Vec<T>>
pub fn collect_column_non_null<T: RowValue>(self) -> Result<Vec<T>>
Collects the first column, filtering out NULL values.
This is useful when you know the column doesn’t contain NULLs or want to ignore them.
§Example
let result = conn.execute_query("SELECT name FROM users WHERE name IS NOT NULL")?;
let names: Vec<String> = result.collect_column_non_null()?;
for name in names {
println!("User: {}", name); // No need to handle Option
}§Errors
Returns the first streaming error from
collect_column.
Sourcepub fn first_row(self) -> Result<Option<Row>>
pub fn first_row(self) -> Result<Option<Row>>
Gets the first row of the result set.
This is useful for queries that are expected to return exactly one row, such as aggregate queries or lookups by unique key.
§Example
let result = conn.execute_query("SELECT COUNT(*) FROM users")?;
if let Some(row) = result.first_row()? {
let count: Option<i64> = row.get(0);
println!("User count: {:?}", count);
}§Errors
Returns the error from next_chunk. An empty
result set yields Ok(None), not an error.
Sourcepub fn require_first_row(self) -> Result<Row>
pub fn require_first_row(self) -> Result<Row>
Gets the first row or returns an error if no rows were found.
This is useful when you expect exactly one row and want to fail if that’s not the case.
§Example
let result = conn.execute_query("SELECT id, name FROM users WHERE id = 1")?;
let row = result.require_first_row()?; // Fails if no row found
let id: Option<i32> = row.get(0);
let name: Option<String> = row.get(1);
println!("Found user: {:?} - {:?}", id, name);§Errors
- Returns the error from
first_row. - Returns
crate::Error::Otherwith message"Query returned no rows"if the result set is empty.
Sourcepub fn scalar<T: RowValue>(self) -> Result<Option<T>>
pub fn scalar<T: RowValue>(self) -> Result<Option<T>>
Gets a scalar value from the first row, first column.
This is a convenience method for scalar queries like SELECT COUNT(*) or SELECT MAX(id).
§Example
let result = conn.execute_query("SELECT COUNT(*) FROM users")?;
let count: Option<i64> = result.scalar()?; // Much cleaner than manual row handling
println!("User count: {:?}", count);§Errors
Returns the error from require_first_row:
streaming error or empty result. SQL NULL in the single cell
yields Ok(None).
Sourcepub fn require_scalar<T: RowValue>(self) -> Result<T>
pub fn require_scalar<T: RowValue>(self) -> Result<T>
Gets a scalar value from the first row, first column, or returns an error if NULL.
This is useful when you expect a non-NULL scalar result.
§Example
let result = conn.execute_query("SELECT COUNT(*) FROM users")?;
let count: i64 = result.require_scalar()?; // Fails if NULL
println!("User count: {}", count);§Errors
- Returns the error from
scalar. - Returns
crate::Error::Otherwith message"Scalar query returned NULL"if the single cell is SQLNULL.
Trait Implementations§
Auto Trait Implementations§
impl<'conn> Freeze for Rowset<'conn>
impl<'conn> !RefUnwindSafe for Rowset<'conn>
impl<'conn> !Send for Rowset<'conn>
impl<'conn> !Sync for Rowset<'conn>
impl<'conn> Unpin for Rowset<'conn>
impl<'conn> UnsafeUnpin for Rowset<'conn>
impl<'conn> !UnwindSafe for Rowset<'conn>
Blanket Implementations§
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
Source§impl<T> Instrument for T
impl<T> Instrument for T
Source§fn instrument(self, span: Span) -> Instrumented<Self>
fn instrument(self, span: Span) -> Instrumented<Self>
Source§fn in_current_span(self) -> Instrumented<Self>
fn in_current_span(self) -> Instrumented<Self>
Source§impl<T> IntoRequest<T> for T
impl<T> IntoRequest<T> for T
Source§fn into_request(self) -> Request<T>
fn into_request(self) -> Request<T>
T in a tonic::Request