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