odbc_api/cursor/polling_cursor.rs
1use crate::{
2 Error,
3 handles::{AsStatementRef, Statement, StatementRef},
4 sleep::{Sleep, wait_for},
5};
6
7use super::{
8 RowSetBuffer, bind_row_set_buffer_to_statement, error_handling_for_fetch,
9 unbind_buffer_from_cursor,
10};
11
12use std::thread::panicking;
13
14/// The asynchronous sibiling of [`super::CursorImpl`]. Use this to fetch results in asynchronous
15/// code.
16///
17/// Like [`super::CursorImpl`] this is an ODBC statement handle in cursor state. However unlike its
18/// synchronous sibling this statement handle is in asynchronous polling mode.
19pub struct CursorPolling<Stmt: Statement> {
20 /// A statement handle in cursor state with asynchronous mode enabled.
21 statement: Stmt,
22}
23
24impl<S> CursorPolling<S>
25where
26 S: Statement,
27{
28 /// Users of this library are encouraged not to call this constructor directly. This method is
29 /// pubilc so users with an understanding of the raw ODBC C-API have a way to create an
30 /// asynchronous cursor, after they left the safety rails of the Rust type System, in order to
31 /// implement a use case not covered yet, by the safe abstractions within this crate.
32 ///
33 /// # Safety
34 ///
35 /// `statement` must be in Cursor state, for the invariants of this type to hold. Preferable
36 /// `statement` should also have asynchrous mode enabled, otherwise constructing a synchronous
37 /// [`super::CursorImpl`] is more suitable.
38 pub unsafe fn new(statement: S) -> Self {
39 Self { statement }
40 }
41
42 /// Binds this cursor to a buffer holding a row set.
43 pub fn bind_buffer<B>(
44 mut self,
45 mut row_set_buffer: B,
46 ) -> Result<BlockCursorPolling<Self, B>, Error>
47 where
48 B: RowSetBuffer,
49 {
50 let stmt = self.statement.as_stmt_ref();
51 unsafe {
52 bind_row_set_buffer_to_statement(stmt, &mut row_set_buffer)?;
53 }
54 Ok(BlockCursorPolling::new(row_set_buffer, self))
55 }
56}
57
58impl<S> AsStatementRef for CursorPolling<S>
59where
60 S: Statement,
61{
62 fn as_stmt_ref(&mut self) -> StatementRef<'_> {
63 self.statement.as_stmt_ref()
64 }
65}
66
67impl<S> Drop for CursorPolling<S>
68where
69 S: Statement,
70{
71 fn drop(&mut self) {
72 if let Err(e) = self
73 .statement
74 .end_cursor_scope()
75 .into_result(&self.statement)
76 {
77 // Avoid panicking, if we already have a panic. We don't want to mask the original
78 // error.
79 if !panicking() {
80 panic!("Unexpected error closing cursor: {e:?}")
81 }
82 }
83 }
84}
85
86/// Asynchronously iterates in blocks (called row sets) over a result set, filling a buffers with
87/// a lot of rows at once, instead of iterating the result set row by row. This is usually much
88/// faster. Asynchronous sibiling of [`super::BlockCursor`].
89pub struct BlockCursorPolling<C, B>
90where
91 C: AsStatementRef,
92{
93 buffer: B,
94 cursor: C,
95}
96
97impl<C, B> BlockCursorPolling<C, B>
98where
99 C: AsStatementRef,
100{
101 fn new(buffer: B, cursor: C) -> Self {
102 Self { buffer, cursor }
103 }
104
105 /// Fills the bound buffer with the next row set.
106 ///
107 /// # Return
108 ///
109 /// `None` if the result set is empty and all row sets have been extracted. `Some` with a
110 /// reference to the internal buffer otherwise.
111 pub async fn fetch(&mut self, sleep: impl Sleep) -> Result<Option<&B>, Error>
112 where
113 B: RowSetBuffer,
114 {
115 self.fetch_with_truncation_check(false, sleep).await
116 }
117
118 /// Fills the bound buffer with the next row set. Should `error_for_truncation` be `true`and any
119 /// diagnostic indicate truncation of a value an error is returned.
120 ///
121 /// # Return
122 ///
123 /// `None` if the result set is empty and all row sets have been extracted. `Some` with a
124 /// reference to the internal buffer otherwise.
125 ///
126 /// Call this method to find out whether there are any truncated values in the batch, without
127 /// inspecting all its rows and columns.
128 pub async fn fetch_with_truncation_check(
129 &mut self,
130 error_for_truncation: bool,
131 mut sleep: impl Sleep,
132 ) -> Result<Option<&B>, Error>
133 where
134 B: RowSetBuffer,
135 {
136 let mut stmt = self.cursor.as_stmt_ref();
137 let result = unsafe { wait_for(|| stmt.fetch(), &mut sleep).await };
138 let has_row = error_handling_for_fetch(result, stmt, &self.buffer, error_for_truncation)?;
139 Ok(has_row.then_some(&self.buffer))
140 }
141}
142
143impl<C, B> Drop for BlockCursorPolling<C, B>
144where
145 C: AsStatementRef,
146{
147 fn drop(&mut self) {
148 if let Err(e) = unbind_buffer_from_cursor(&mut self.cursor) {
149 // Avoid panicking, if we already have a panic. We don't want to mask the original
150 // error.
151 if !panicking() {
152 panic!("Unexpected error unbinding columns: {e:?}")
153 }
154 }
155 }
156}