odbc_api/preallocated.rs
1use crate::{
2 BlockCursorIterator, Cursor, CursorImpl, Error, ParameterCollectionRef, PreallocatedPolling,
3 buffers::RowVec,
4 catalog::{
5 ColumnsRow, ForeignKeysRow, PrimaryKeysRow, TablesRow, execute_columns,
6 execute_foreign_keys, execute_primary_keys, execute_tables,
7 },
8 execute::execute_with_parameters,
9 handles::{AsStatementRef, SqlText, Statement, StatementRef},
10};
11
12/// A preallocated SQL statement handle intended for sequential execution of different queries. See
13/// [`crate::Connection::preallocate`].
14///
15/// # Example
16///
17/// ```
18/// use odbc_api::{Connection, Error};
19/// use std::io::{self, stdin, Read};
20///
21/// fn interactive(conn: &Connection<'_>) -> io::Result<()>{
22/// let mut statement = conn.preallocate().unwrap();
23/// let mut query = String::new();
24/// stdin().read_line(&mut query)?;
25/// while !query.is_empty() {
26/// match statement.execute(&query, ()) {
27/// Err(e) => println!("{}", e),
28/// Ok(None) => println!("No results set generated."),
29/// Ok(Some(cursor)) => {
30/// // ...print cursor contents...
31/// },
32/// }
33/// stdin().read_line(&mut query)?;
34/// }
35/// Ok(())
36/// }
37/// ```
38pub struct Preallocated<S> {
39 /// A valid statement handle.
40 statement: S,
41}
42
43impl<S> Preallocated<S>
44where
45 S: Statement,
46{
47 /// Users which intend to write their application in safe Rust should prefer using
48 /// [`crate::Connection::preallocate`] as opposed to this constructor.
49 ///
50 /// # Safety
51 ///
52 /// `statement` must be an allocated handled with no pointers bound for either results or
53 /// arguments. The statement must not be prepared, but in the state of a "freshly" allocated
54 /// handle.
55 pub unsafe fn new(statement: S) -> Self {
56 Self { statement }
57 }
58
59 /// Executes a statement. This is the fastest way to sequentially execute different SQL
60 /// Statements.
61 ///
62 /// This method produces a cursor which borrowes the statement handle. If you want to take
63 /// ownership you can use the sibling [`Self::into_cursor`].
64 ///
65 /// # Parameters
66 ///
67 /// * `query`: The text representation of the SQL statement. E.g. "SELECT * FROM my_table;".
68 /// * `params`: `?` may be used as a placeholder in the statement text. You can use `()` to
69 /// represent no parameters. Check the [`crate::parameter`] module level documentation for
70 /// more information on how to pass parameters.
71 ///
72 /// # Return
73 ///
74 /// Returns `Some` if a cursor is created. If `None` is returned no cursor has been created (
75 /// e.g. the query came back empty). Note that an empty query may also create a cursor with zero
76 /// rows. Since we want to reuse the statement handle a returned cursor will not take ownership
77 /// of it and instead borrow it.
78 ///
79 /// # Example
80 ///
81 /// ```
82 /// use odbc_api::{Connection, Error};
83 /// use std::io::{self, stdin, Read};
84 ///
85 /// fn interactive(conn: &Connection) -> io::Result<()>{
86 /// let mut statement = conn.preallocate().unwrap();
87 /// let mut query = String::new();
88 /// stdin().read_line(&mut query)?;
89 /// while !query.is_empty() {
90 /// match statement.execute(&query, ()) {
91 /// Err(e) => println!("{}", e),
92 /// Ok(None) => println!("No results set generated."),
93 /// Ok(Some(cursor)) => {
94 /// // ...print cursor contents...
95 /// },
96 /// }
97 /// stdin().read_line(&mut query)?;
98 /// }
99 /// Ok(())
100 /// }
101 /// ```
102 pub fn execute(
103 &mut self,
104 query: &str,
105 params: impl ParameterCollectionRef,
106 ) -> Result<Option<CursorImpl<StatementRef<'_>>>, Error> {
107 let query = SqlText::new(query);
108 let stmt = self.statement.as_stmt_ref();
109 execute_with_parameters(stmt, Some(&query), params)
110 }
111
112 /// Similar to [`Self::execute`], but transfers ownership of the statement handle to the
113 /// resulting cursor if any is created. This makes this method not suitable to repeatedly
114 /// execute statements. In most situations you may want to call [`crate::Connection::execute`]
115 /// instead of this method, yet this method is useful if you have some time in your application
116 /// until the query is known, and once you have it want to execute it as fast as possible.
117 pub fn into_cursor(
118 self,
119 query: &str,
120 params: impl ParameterCollectionRef,
121 ) -> Result<Option<CursorImpl<S>>, Error> {
122 let query = SqlText::new(query);
123 execute_with_parameters(self.statement, Some(&query), params)
124 }
125
126 /// Transfer ownership to the underlying statement handle.
127 ///
128 /// The resulting type is one level of indirection away from the raw pointer of the ODBC API. It
129 /// no longer has any guarantees about bound buffers, but is still guaranteed to be a valid
130 /// allocated statement handle. This serves together with
131 /// [`crate::handles::StatementImpl::into_sys`] or [`crate::handles::Statement::as_sys`] this
132 /// serves as an escape hatch to access the functionality provided by `crate::sys` not yet
133 /// accessible through safe abstractions.
134 pub fn into_handle(self) -> S {
135 self.statement
136 }
137
138 /// List tables, schemas, views and catalogs of a datasource.
139 ///
140 /// # Parameters
141 ///
142 /// * `catalog_name`: Filter result by catalog name. Accept search patterns. Use `%` to match
143 /// any number of characters. Use `_` to match exactly on character. Use `\` to escape
144 /// characeters.
145 /// * `schema_name`: Filter result by schema. Accepts patterns in the same way as
146 /// `catalog_name`.
147 /// * `table_name`: Filter result by table. Accepts patterns in the same way as `catalog_name`.
148 /// * `table_type`: Filters results by table type. E.g: 'TABLE', 'VIEW'. This argument accepts a
149 /// comma separeted list of table types. Omit it to not filter the result by table type at
150 /// all.
151 pub fn tables_cursor(
152 &mut self,
153 catalog_name: &str,
154 schema_name: &str,
155 table_name: &str,
156 table_type: &str,
157 ) -> Result<CursorImpl<StatementRef<'_>>, Error> {
158 let stmt = self.statement.as_stmt_ref();
159 execute_tables(stmt, catalog_name, schema_name, table_name, table_type)
160 }
161
162 /// An iterator over the tables of a data source. Same as [`Self::tables_cursor`] but returns
163 /// strongly typed [`TablesRow`] items instead of a raw cursor.
164 pub fn tables(
165 &mut self,
166 catalog_name: &str,
167 schema_name: &str,
168 table_name: &str,
169 table_type: &str,
170 ) -> Result<BlockCursorIterator<CursorImpl<StatementRef<'_>>, TablesRow>, Error> {
171 let cursor = self.tables_cursor(catalog_name, schema_name, table_name, table_type)?;
172 let buffer = RowVec::<TablesRow>::new(100);
173 Ok(cursor.bind_buffer(buffer)?.into_iter())
174 }
175
176 /// Same as [`Self::tables_cursor`] but the cursor takes ownership of the statement handle.
177 pub fn into_tables_cursor(
178 self,
179 catalog_name: &str,
180 schema_name: &str,
181 table_name: &str,
182 table_type: &str,
183 ) -> Result<CursorImpl<S>, Error> {
184 execute_tables(
185 self.statement,
186 catalog_name,
187 schema_name,
188 table_name,
189 table_type,
190 )
191 }
192
193 /// Same as [`Self::tables`] but the cursor takes ownership of the statement handle.
194 pub fn into_tables(
195 self,
196 catalog_name: &str,
197 schema_name: &str,
198 table_name: &str,
199 table_type: &str,
200 ) -> Result<BlockCursorIterator<CursorImpl<S>, TablesRow>, Error> {
201 let cursor = self.into_tables_cursor(catalog_name, schema_name, table_name, table_type)?;
202 let buffer = RowVec::<TablesRow>::new(100);
203 Ok(cursor.bind_buffer(buffer)?.into_iter())
204 }
205
206 /// A cursor describing columns of all tables matching the patterns. Patterns support as
207 /// placeholder `%` for multiple characters or `_` for a single character. Use `\` to escape.The
208 /// returned cursor has the columns:
209 /// `TABLE_CAT`, `TABLE_SCHEM`, `TABLE_NAME`, `COLUMN_NAME`, `DATA_TYPE`, `TYPE_NAME`,
210 /// `COLUMN_SIZE`, `BUFFER_LENGTH`, `DECIMAL_DIGITS`, `NUM_PREC_RADIX`, `NULLABLE`,
211 /// `REMARKS`, `COLUMN_DEF`, `SQL_DATA_TYPE`, `SQL_DATETIME_SUB`, `CHAR_OCTET_LENGTH`,
212 /// `ORDINAL_POSITION`, `IS_NULLABLE`.
213 ///
214 /// In addition to that there may be a number of columns specific to the data source.
215 pub fn columns_cursor(
216 &mut self,
217 catalog_name: &str,
218 schema_name: &str,
219 table_name: &str,
220 column_name: &str,
221 ) -> Result<CursorImpl<StatementRef<'_>>, Error> {
222 let stmt = self.statement.as_stmt_ref();
223 execute_columns(
224 stmt,
225 &SqlText::new(catalog_name),
226 &SqlText::new(schema_name),
227 &SqlText::new(table_name),
228 &SqlText::new(column_name),
229 )
230 }
231
232 /// An iterator over the columns of a table. Same as [`Self::columns_cursor`] but returns
233 /// strongly typed [`ColumnsRow`] items instead of a raw cursor.
234 pub fn columns(
235 &mut self,
236 catalog_name: &str,
237 schema_name: &str,
238 table_name: &str,
239 column_name: &str,
240 ) -> Result<BlockCursorIterator<CursorImpl<StatementRef<'_>>, ColumnsRow>, Error> {
241 let cursor = self.columns_cursor(catalog_name, schema_name, table_name, column_name)?;
242 let buffer = RowVec::<ColumnsRow>::new(250);
243 Ok(cursor.bind_buffer(buffer)?.into_iter())
244 }
245
246 /// Same as [`Self::columns_cursor`], but the cursor takes ownership of the statement handle.
247 pub fn into_columns_cursor(
248 self,
249 catalog_name: &str,
250 schema_name: &str,
251 table_name: &str,
252 column_name: &str,
253 ) -> Result<CursorImpl<S>, Error> {
254 execute_columns(
255 self.statement,
256 &SqlText::new(catalog_name),
257 &SqlText::new(schema_name),
258 &SqlText::new(table_name),
259 &SqlText::new(column_name),
260 )
261 }
262
263 /// Same as [`Self::columns`] but the cursor takes ownership of the statement handle.
264 pub fn into_columns(
265 self,
266 catalog_name: &str,
267 schema_name: &str,
268 table_name: &str,
269 column_name: &str,
270 ) -> Result<BlockCursorIterator<CursorImpl<S>, ColumnsRow>, Error> {
271 let cursor =
272 self.into_columns_cursor(catalog_name, schema_name, table_name, column_name)?;
273 let buffer = RowVec::<ColumnsRow>::new(250);
274 Ok(cursor.bind_buffer(buffer)?.into_iter())
275 }
276
277 /// Create a result set which contains the column names that make up the primary key for the
278 /// table. Same as [`Self::into_primary_keys_cursor`] but the cursor borrowes the statement
279 /// handle instead of taking ownership of it. This allows you to reuse the statement handle for
280 /// multiple calls to [`Self::primary_keys_cursor`] or other queries, without the need to ask
281 /// the driver for repeated allocations of new handles.
282 ///
283 /// # Parameters
284 ///
285 /// * `catalog_name`: Catalog name. If a driver supports catalogs for some tables but not for
286 /// others, such as when the driver retrieves data from different DBMSs, an empty string ("")
287 /// denotes those tables that do not have catalogs. `catalog_name` must not contain a string
288 /// search pattern.
289 /// * `schema_name`: Schema name. If a driver supports schemas for some tables but not for
290 /// others, such as when the driver retrieves data from different DBMSs, an empty string ("")
291 /// denotes those tables that do not have schemas. `schema_name` must not contain a string
292 /// search pattern.
293 /// * `table_name`: Table name. `table_name` must not contain a string search pattern.
294 ///
295 /// The resulting result set contains the following columns:
296 ///
297 /// * `TABLE_CAT`: Primary key table catalog name. NULL if not applicable to the data source. If
298 /// a driver supports catalogs for some tables but not for others, such as when the driver
299 /// retrieves data from different DBMSs, it returns an empty string ("") for those tables that
300 /// do not have catalogs. `VARCHAR`
301 /// * `TABLE_SCHEM`: Primary key table schema name; NULL if not applicable to the data source.
302 /// If a driver supports schemas for some tables but not for others, such as when the driver
303 /// retrieves data from different DBMSs, it returns an empty string ("") for those tables that
304 /// do not have schemas. `VARCHAR`
305 /// * `TABLE_NAME`: Primary key table name. `VARCHAR NOT NULL`
306 /// * `COLUMN_NAME`: Primary key column name. The driver returns an empty string for a column
307 /// that does not have a name. `VARCHAR NOT NULL`
308 /// * `KEY_SEQ`: Column sequence number in key (starting with 1). `SMALLINT NOT NULL`
309 /// * `PK_NAME`: Primary key name. NULL if not applicable to the data source. `VARCHAR`
310 ///
311 /// The maximum length of the VARCHAR columns is driver specific.
312 ///
313 /// If [`crate::sys::StatementAttribute::MetadataId`] statement attribute is set to true,
314 /// catalog, schema and table name parameters are treated as an identifiers and their case is
315 /// not significant. If it is false, they are ordinary arguments. As such they treated literally
316 /// and their case is significant.
317 ///
318 /// See: <https://learn.microsoft.com/sql/odbc/reference/syntax/sqlprimarykeys-function>
319 pub fn primary_keys_cursor(
320 &mut self,
321 catalog_name: Option<&str>,
322 schema_name: Option<&str>,
323 table_name: &str,
324 ) -> Result<CursorImpl<StatementRef<'_>>, Error> {
325 execute_primary_keys(
326 self.statement.as_stmt_ref(),
327 catalog_name,
328 schema_name,
329 table_name,
330 )
331 }
332
333 /// An iterator whose items contains the column names that make up the primary key for the
334 /// table. Same as [`Self::into_primary_keys`] but the cursor borrowes the statement handle
335 /// instead of taking ownership of it. This allows you to reuse the statement handle for
336 /// multiple calls to [`Self::primary_keys`] or other queries, without the need to ask the
337 /// driver for repeated allocations of new handles.
338 ///
339 /// # Parameters
340 ///
341 /// * `catalog_name`: Catalog name. If a driver supports catalogs for some tables but not for
342 /// others, such as when the driver retrieves data from different DBMSs, an empty string ("")
343 /// denotes those tables that do not have catalogs. `catalog_name` must not contain a string
344 /// search pattern.
345 /// * `schema_name`: Schema name. If a driver supports schemas for some tables but not for
346 /// others, such as when the driver retrieves data from different DBMSs, an empty string ("")
347 /// denotes those tables that do not have schemas. `schema_name` must not contain a string
348 /// search pattern.
349 /// * `table_name`: Table name. `table_name` must not contain a string search pattern.
350 ///
351 /// If [`crate::sys::StatementAttribute::MetadataId`] statement attribute is set to true,
352 /// catalog, schema and table name parameters are treated as an identifiers and their case is
353 /// not significant. If it is false, they are ordinary arguments. As such they treated literally
354 /// and their case is significant.
355 ///
356 /// See: <https://learn.microsoft.com/sql/odbc/reference/syntax/sqlprimarykeys-function>
357 pub fn primary_keys(
358 &mut self,
359 catalog_name: Option<&str>,
360 schema_name: Option<&str>,
361 table_name: &str,
362 ) -> Result<BlockCursorIterator<CursorImpl<StatementRef<'_>>, PrimaryKeysRow>, Error> {
363 let cursor = self.primary_keys_cursor(catalog_name, schema_name, table_name)?;
364 // 5 seems like a senisble soft upper bound for the number of columns in a primary key. If
365 // it is more than that, we need an extra roundtrip
366 let buffer = RowVec::<PrimaryKeysRow>::new(5);
367 Ok(cursor.bind_buffer(buffer)?.into_iter())
368 }
369
370 /// Same as [`Self::primary_keys_cursor`] but the cursor takes ownership of the statement handle.
371 pub fn into_primary_keys_cursor(
372 self,
373 catalog_name: Option<&str>,
374 schema_name: Option<&str>,
375 table_name: &str,
376 ) -> Result<CursorImpl<S>, Error> {
377 execute_primary_keys(self.statement, catalog_name, schema_name, table_name)
378 }
379
380 /// Same as [`Self::primary_keys`] but the cursor takes ownership of the statement handle.
381 pub fn into_primary_keys(
382 self,
383 catalog_name: Option<&str>,
384 schema_name: Option<&str>,
385 table_name: &str,
386 ) -> Result<BlockCursorIterator<CursorImpl<S>, PrimaryKeysRow>, Error> {
387 let cursor = self.into_primary_keys_cursor(catalog_name, schema_name, table_name)?;
388 // 5 seems like a senisble soft upper bound for the number of columns in a primary key. If
389 // it is more than that, we need an extra roundtrip
390 let buffer = RowVec::<PrimaryKeysRow>::new(5);
391 Ok(cursor.bind_buffer(buffer)?.into_iter())
392 }
393
394 /// This can be used to retrieve either a list of foreign keys in the specified table or a list
395 /// of foreign keys in other table that refer to the primary key of the specified table.
396 ///
397 /// See: <https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlforeignkeys-function>
398 pub fn foreign_keys_cursor(
399 &mut self,
400 pk_catalog_name: &str,
401 pk_schema_name: &str,
402 pk_table_name: &str,
403 fk_catalog_name: &str,
404 fk_schema_name: &str,
405 fk_table_name: &str,
406 ) -> Result<CursorImpl<StatementRef<'_>>, Error> {
407 let stmt = self.statement.as_stmt_ref();
408 execute_foreign_keys(
409 stmt,
410 pk_catalog_name,
411 pk_schema_name,
412 pk_table_name,
413 fk_catalog_name,
414 fk_schema_name,
415 fk_table_name,
416 )
417 }
418
419 /// This can be used to retrieve either a list of foreign keys in the specified table or a list
420 /// of foreign keys in other table that refer to the primary key of the specified table.
421 ///
422 /// See: <https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlforeignkeys-function>
423 pub fn into_foreign_keys_cursor(
424 self,
425 pk_catalog_name: &str,
426 pk_schema_name: &str,
427 pk_table_name: &str,
428 fk_catalog_name: &str,
429 fk_schema_name: &str,
430 fk_table_name: &str,
431 ) -> Result<CursorImpl<S>, Error> {
432 execute_foreign_keys(
433 self.statement,
434 pk_catalog_name,
435 pk_schema_name,
436 pk_table_name,
437 fk_catalog_name,
438 fk_schema_name,
439 fk_table_name,
440 )
441 }
442
443 /// An iterator over the foreign keys of a table.
444 ///
445 /// Same as [`Self::foreign_keys_cursor`] but returns [`ForeignKeysRow`] items instead of a raw
446 /// cursor.
447 ///
448 /// See: <https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlforeignkeys-function>
449 pub fn foreign_keys(
450 &mut self,
451 pk_catalog_name: &str,
452 pk_schema_name: &str,
453 pk_table_name: &str,
454 fk_catalog_name: &str,
455 fk_schema_name: &str,
456 fk_table_name: &str,
457 ) -> Result<BlockCursorIterator<CursorImpl<StatementRef<'_>>, ForeignKeysRow>, Error> {
458 let cursor = self.foreign_keys_cursor(
459 pk_catalog_name,
460 pk_schema_name,
461 pk_table_name,
462 fk_catalog_name,
463 fk_schema_name,
464 fk_table_name,
465 )?;
466 let buffer = RowVec::<ForeignKeysRow>::new(100);
467 Ok(cursor.bind_buffer(buffer)?.into_iter())
468 }
469
470 /// Same as [`Self::foreign_keys`] but the cursor takes ownership of the statement handle.
471 pub fn into_foreign_keys(
472 self,
473 pk_catalog_name: &str,
474 pk_schema_name: &str,
475 pk_table_name: &str,
476 fk_catalog_name: &str,
477 fk_schema_name: &str,
478 fk_table_name: &str,
479 ) -> Result<BlockCursorIterator<CursorImpl<S>, ForeignKeysRow>, Error> {
480 let cursor = self.into_foreign_keys_cursor(
481 pk_catalog_name,
482 pk_schema_name,
483 pk_table_name,
484 fk_catalog_name,
485 fk_schema_name,
486 fk_table_name,
487 )?;
488 let buffer = RowVec::<ForeignKeysRow>::new(100);
489 Ok(cursor.bind_buffer(buffer)?.into_iter())
490 }
491
492 /// Number of rows affected by the last `INSERT`, `UPDATE` or `DELETE` statment. May return
493 /// `None` if row count is not available. Some drivers may also allow to use this to determine
494 /// how many rows have been fetched using `SELECT`. Most drivers however only know how many rows
495 /// have been fetched after they have been fetched.
496 ///
497 /// ```
498 /// use odbc_api::{Connection, Error};
499 ///
500 /// /// Make everyone rich and return how many colleagues are happy now.
501 /// fn raise_minimum_salary(
502 /// conn: &Connection<'_>,
503 /// new_min_salary: i32
504 /// ) -> Result<usize, Error> {
505 /// // We won't use conn.execute directly, because we need a handle to ask about the number
506 /// // of changed rows. So let's allocate the statement explicitly.
507 /// let mut stmt = conn.preallocate()?;
508 /// stmt.execute(
509 /// "UPDATE Employees SET salary = ? WHERE salary < ?",
510 /// (&new_min_salary, &new_min_salary),
511 /// )?;
512 /// let number_of_updated_rows = stmt
513 /// .row_count()?
514 /// .expect("For UPDATE statements row count must always be available.");
515 /// Ok(number_of_updated_rows)
516 /// }
517 /// ```
518 pub fn row_count(&mut self) -> Result<Option<usize>, Error> {
519 self.statement
520 .row_count()
521 .into_result(&self.statement)
522 .map(|count| {
523 // ODBC returns -1 in case a row count is not available
524 if count == -1 {
525 None
526 } else {
527 Some(count.try_into().unwrap())
528 }
529 })
530 }
531
532 /// Use this to limit the time the query is allowed to take, before responding with data to the
533 /// application. The driver may replace the number of seconds you provide with a minimum or
534 /// maximum value. You can specify ``0``, to deactivate the timeout, this is the default. For
535 /// this to work the driver must support this feature. E.g. PostgreSQL, and Microsoft SQL Server
536 /// do, but SQLite or MariaDB do not.
537 ///
538 /// This corresponds to `SQL_ATTR_QUERY_TIMEOUT` in the ODBC C API.
539 ///
540 /// See:
541 /// <https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlsetstmtattr-function>
542 pub fn set_query_timeout_sec(&mut self, timeout_sec: usize) -> Result<(), Error> {
543 self.statement
544 .set_query_timeout_sec(timeout_sec)
545 .into_result(&self.statement)
546 }
547
548 /// The number of seconds to wait for a SQL statement to execute before returning to the
549 /// application. If `timeout_sec` is equal to 0 (default), there is no timeout.
550 ///
551 /// This corresponds to `SQL_ATTR_QUERY_TIMEOUT` in the ODBC C API.
552 ///
553 /// See:
554 /// <https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlsetstmtattr-function>
555 pub fn query_timeout_sec(&mut self) -> Result<usize, Error> {
556 self.statement
557 .query_timeout_sec()
558 .into_result(&self.statement)
559 }
560
561 /// Call this method to enable asynchronous polling mode on the statement.
562 ///
563 /// ⚠️**Attention**⚠️: Please read
564 /// [Asynchronous execution using polling
565 /// mode](crate::guide#asynchronous-execution-using-polling-mode)
566 pub fn into_polling(mut self) -> Result<PreallocatedPolling<S>, Error> {
567 self.statement
568 .set_async_enable(true)
569 .into_result(&self.statement)?;
570 Ok(PreallocatedPolling::new(self.statement))
571 }
572}
573
574impl<S> AsStatementRef for Preallocated<S>
575where
576 S: AsStatementRef,
577{
578 fn as_stmt_ref(&mut self) -> StatementRef<'_> {
579 self.statement.as_stmt_ref()
580 }
581}