use std::collections::VecDeque;
use asupersync::Cx;
use oracledb_protocol::thin::{
ColumnMetadata, QueryResult, QueryValue, ORA_TYPE_NUM_BLOB, ORA_TYPE_NUM_CLOB,
ORA_TYPE_NUM_JSON, ORA_TYPE_NUM_VECTOR,
};
use crate::Connection;
use super::collection::SodaCollection;
use super::document::SodaDocument;
use super::error::{Result, SodaError};
use super::operation::SelectColumns;
pub struct SodaCursor {
collection: SodaCollection,
layout: SelectColumns,
buffer: VecDeque<Vec<Option<QueryValue>>>,
columns: Vec<ColumnMetadata>,
cursor_id: u32,
array_size: u32,
more_rows: bool,
previous_row: Option<Vec<Option<QueryValue>>>,
closed: bool,
}
impl SodaCursor {
pub(crate) fn new(
collection: SodaCollection,
result: QueryResult,
layout: SelectColumns,
array_size: u32,
) -> Self {
let QueryResult {
columns,
rows,
cursor_id,
more_rows,
..
} = result;
let previous_row = rows.last().cloned();
SodaCursor {
collection,
layout,
buffer: rows.into_iter().collect(),
columns,
cursor_id,
array_size,
more_rows,
previous_row,
closed: false,
}
}
pub async fn next_doc(
&mut self,
conn: &mut Connection,
cx: &Cx,
) -> Result<Option<SodaDocument>> {
if self.closed {
return Err(SodaError::NotSupported("cursor is closed".to_string()));
}
if self.buffer.is_empty() && self.more_rows {
self.fetch_more(conn, cx).await?;
}
match self.buffer.pop_front() {
Some(row) => {
let doc = self.collection.row_to_document(&row, &self.layout)?;
Ok(Some(doc))
}
None => Ok(None),
}
}
async fn fetch_more(&mut self, conn: &mut Connection, cx: &Cx) -> Result<()> {
let needs_define = self.columns.iter().any(|c| {
matches!(
c.ora_type_num(),
ORA_TYPE_NUM_CLOB | ORA_TYPE_NUM_BLOB | ORA_TYPE_NUM_VECTOR | ORA_TYPE_NUM_JSON
)
});
let result = if needs_define {
conn.define_and_fetch_rows_with_columns(
cx,
self.cursor_id,
self.array_size,
&self.columns,
self.previous_row.as_deref(),
)
.await
.map_err(SodaError::Driver)?
} else {
conn.fetch_rows_with_columns(
cx,
self.cursor_id,
self.array_size,
&self.columns,
self.previous_row.as_deref(),
)
.await
.map_err(SodaError::Driver)?
};
self.more_rows = result.more_rows;
self.previous_row = result.rows.last().cloned();
self.buffer.extend(result.rows);
Ok(())
}
pub async fn close(&mut self, conn: &mut Connection, _cx: &Cx) -> Result<()> {
if self.closed {
return Err(SodaError::NotSupported("cursor already closed".to_string()));
}
self.closed = true;
conn.release_cursor(self.cursor_id);
Ok(())
}
pub fn is_closed(&self) -> bool {
self.closed
}
}