Skip to main content

odbc_api/handles/
statement.rs

1use super::{
2    CData, Descriptor, SqlChar, SqlResult, SqlText,
3    any_handle::AnyHandle,
4    bind::{CDataMut, DelayedInput, HasDataType},
5    buffer::{clamp_small_int, mut_buf_ptr},
6    column_description::{ColumnDescription, Nullability},
7    data_type::DataType,
8    drop_handle,
9    sql_char::{binary_length, is_truncated_bin, resize_to_fit_without_tz},
10    sql_result::ExtSqlReturn,
11};
12use log::trace;
13use odbc_sys::{
14    Desc, FreeStmtOption, HDbc, HDesc, HStmt, Handle, HandleType, IS_POINTER, Len, ParamType,
15    Pointer, SQLBindCol, SQLBindParameter, SQLCloseCursor, SQLDescribeParam, SQLExecute, SQLFetch,
16    SQLFreeStmt, SQLGetData, SQLMoreResults, SQLNumParams, SQLNumResultCols, SQLParamData,
17    SQLPutData, SQLRowCount, SqlDataType, SqlReturn, StatementAttribute,
18};
19use std::{
20    ffi::c_void,
21    marker::PhantomData,
22    mem::ManuallyDrop,
23    num::NonZeroUsize,
24    ptr::{null, null_mut},
25};
26
27#[cfg(feature = "odbc_version_3_80")]
28use odbc_sys::SQLCompleteAsync;
29
30#[cfg(not(any(feature = "wide", all(not(feature = "narrow"), target_os = "windows"))))]
31use odbc_sys::{
32    SQLColAttribute as sql_col_attribute, SQLColumns as sql_columns,
33    SQLDescribeCol as sql_describe_col, SQLExecDirect as sql_exec_direc,
34    SQLForeignKeys as sql_foreign_keys, SQLGetStmtAttr as sql_get_stmt_attr,
35    SQLPrepare as sql_prepare, SQLPrimaryKeys as sql_primary_keys,
36    SQLSetStmtAttr as sql_set_stmt_attr, SQLTables as sql_tables,
37};
38
39#[cfg(any(feature = "wide", all(not(feature = "narrow"), target_os = "windows")))]
40use odbc_sys::{
41    SQLColAttributeW as sql_col_attribute, SQLColumnsW as sql_columns,
42    SQLDescribeColW as sql_describe_col, SQLExecDirectW as sql_exec_direc,
43    SQLForeignKeysW as sql_foreign_keys, SQLGetStmtAttrW as sql_get_stmt_attr,
44    SQLPrepareW as sql_prepare, SQLPrimaryKeysW as sql_primary_keys,
45    SQLSetStmtAttrW as sql_set_stmt_attr, SQLTablesW as sql_tables,
46};
47
48/// An owned valid (i.e. successfully allocated) ODBC statement handle. [`StatementImpl`] borrows
49/// the parent connection used to create the handle in order to ensure the Parent is alive and valid
50/// during the lifetime of the statement.
51///
52/// If you want a handle to the statement that instead of borrowing the parent connection does own
53/// it, you should use [`super::StatementConnection`] instead.
54#[derive(Debug)]
55pub struct StatementImpl<'s> {
56    parent: PhantomData<&'s HDbc>,
57    handle: HStmt,
58}
59
60unsafe impl AnyHandle for StatementImpl<'_> {
61    fn as_handle(&self) -> Handle {
62        self.handle.as_handle()
63    }
64
65    fn handle_type(&self) -> HandleType {
66        HandleType::Stmt
67    }
68}
69
70impl Drop for StatementImpl<'_> {
71    fn drop(&mut self) {
72        unsafe {
73            drop_handle(self.handle.as_handle(), HandleType::Stmt);
74        }
75    }
76}
77
78impl StatementImpl<'_> {
79    /// # Safety
80    ///
81    /// `handle` must be a valid (successfully allocated) statement handle.
82    pub unsafe fn new(handle: HStmt) -> Self {
83        Self {
84            handle,
85            parent: PhantomData,
86        }
87    }
88
89    /// Transfer ownership of this statement to a raw system handle. It is the users responsibility
90    /// to call [`crate::sys::SQLFreeHandle`].
91    pub fn into_sys(self) -> HStmt {
92        // We do not want to run the drop handler, but transfer ownership instead.
93        ManuallyDrop::new(self).handle
94    }
95
96    /// Special wrapper to a borrowed statement. Acts like a mutable reference to an owned
97    /// statement, but allows the lifetime of the tracked connection to stay covariant.
98    pub fn as_stmt_ref(&mut self) -> StatementRef<'_> {
99        StatementRef {
100            parent: self.parent,
101            handle: self.handle,
102        }
103    }
104}
105
106/// According to the ODBC documentation this is safe. See:
107/// <https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/multithreading>
108///
109/// We maybe could consider a statement to be `Sync` as well, since all operations on it currently
110/// require `&mut self`. Yet maybe we get forced to allow some of these operations to take `&self`
111/// in the future, like we do for [`crate::Connection`] to allow for shared ownership of
112/// connections by multiple statements.
113///
114/// A non obvious implication of implementing `Send` for `StatementImpl` is that we must demand all
115/// parameters bound to the statement are also `Send`. There might be room for a statement handle
116/// which is not `Send`, but could therefore bind also parameters which are not `Send`. So far it is
117/// not clear in what use-case we would need this. Yet there are  multithreaded programs out there
118/// which want to make use of ODBC.
119unsafe impl Send for StatementImpl<'_> {}
120
121/// A borrowed valid (i.e. successfully allocated) ODBC statement handle. This can be used instead
122/// of a mutable reference to a [`StatementImpl`]. The main advantage here is that the lifetime
123/// paramater remains covariant, whereas if we would just take a mutable reference to an owned
124/// statement it would become invariant.
125#[derive(Debug)]
126pub struct StatementRef<'s> {
127    parent: PhantomData<&'s HDbc>,
128    handle: HStmt,
129}
130
131impl StatementRef<'_> {
132    pub(crate) unsafe fn new(handle: HStmt) -> Self {
133        Self {
134            handle,
135            parent: PhantomData,
136        }
137    }
138}
139
140impl Statement for StatementRef<'_> {
141    fn as_sys(&self) -> HStmt {
142        self.handle
143    }
144
145    fn end_cursor_scope(&mut self) -> SqlResult<()> {
146        self.close_cursor()
147    }
148}
149
150unsafe impl AnyHandle for StatementRef<'_> {
151    fn as_handle(&self) -> Handle {
152        self.handle.as_handle()
153    }
154
155    fn handle_type(&self) -> HandleType {
156        HandleType::Stmt
157    }
158}
159
160/// According to the ODBC documentation this is safe. See:
161/// <https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/multithreading>
162///
163/// We maybe could consider a statement to be `Sync` as well, since all operations on it currently
164/// require `&mut self`. Yet maybe we get forced to allow some of these operations to take `&self`
165/// in the future, like we do for [`crate::Connection`] to allow for shared ownership of
166/// connections by multiple statements.
167unsafe impl Send for StatementRef<'_> {}
168
169/// Allows us to be generic over the ownership type (mutably borrowed or owned) of a statement
170pub trait AsStatementRef {
171    /// Get an exclusive reference to the underlying statement handle. This method is used to
172    /// implement other more higher level methods on top of it. It is not intended to be called by
173    /// users of this crate directly, yet it may serve as an escape hatch for low level use cases.
174    fn as_stmt_ref(&mut self) -> StatementRef<'_>;
175}
176
177impl<T> AsStatementRef for T
178where
179    T: Statement,
180{
181    fn as_stmt_ref(&mut self) -> StatementRef<'_> {
182        unsafe { StatementRef::new(self.as_sys()) }
183    }
184}
185
186/// An ODBC statement handle. In this crate it is implemented by [`self::StatementImpl`]. In ODBC
187/// Statements are used to execute statements and retrieve results. Both parameter and result
188/// buffers are bound to the statement and dereferenced during statement execution and fetching
189/// results.
190///
191/// The trait allows us to reason about statements without taking the lifetime of their connection
192/// into account. It also allows for the trait to be implemented by a handle taking ownership of
193/// both, the statement and the connection.
194pub trait Statement: AnyHandle {
195    /// Gain access to the underlying statement handle without transferring ownership to it.
196    fn as_sys(&self) -> HStmt;
197
198    /// Invoke [`Self::close_cursor`] to close the cursor for implementations which borrow the
199    /// statement handle. For implementations which own the statement handle exclusively, this is a
200    /// no-op. The idea is that if the statement handle is borrowed, we must assume it is going to
201    /// be reused for other queries, so we must spend the effort to close the cursor. If the
202    /// statement handle is exclusively owned it is dropped and freed right away if dropping a
203    /// cursor wrapper. So we do not need to bother with closing the cursor explicitly. The driver
204    /// can take care of it during cleanup however it seems fit.
205    fn end_cursor_scope(&mut self) -> SqlResult<()>;
206
207    /// Binds application data buffers to columns in the result set.
208    ///
209    /// * `column_number`: `0` is the bookmark column. It is not included in some result sets. All
210    ///   other columns are numbered starting with `1`. It is an error to bind a higher-numbered
211    ///   column than there are columns in the result set. This error cannot be detected until the
212    ///   result set has been created, so it is returned by `fetch`, not `bind_col`.
213    /// * `target_type`: The identifier of the C data type of the `value` buffer. When it is
214    ///   retrieving data from the data source with `fetch`, the driver converts the data to this
215    ///   type. When it sends data to the source, the driver converts the data from this type.
216    /// * `target_value`: Pointer to the data buffer to bind to the column.
217    /// * `target_length`: Length of target value in bytes. (Or for a single element in case of bulk
218    ///   aka. block fetching data).
219    /// * `indicator`: Buffer is going to hold length or indicator values.
220    ///
221    /// # Safety
222    ///
223    /// It is the callers responsibility to make sure the bound columns live until they are no
224    /// longer bound. I.e. `target` must live long enough to cover every read of the bound value
225    /// pointers until they are unbound.
226    unsafe fn bind_col(&mut self, column_number: u16, target: &mut impl CDataMut) -> SqlResult<()> {
227        unsafe {
228            SQLBindCol(
229                self.as_sys(),
230                column_number,
231                target.cdata_type(),
232                target.mut_value_ptr(),
233                target.buffer_length(),
234                target.mut_indicator_ptr(),
235            )
236        }
237        .into_sql_result("SQLBindCol")
238    }
239
240    /// Returns the next row set in the result set.
241    ///
242    /// It can be called only while a result set exists: I.e., after a call that creates a result
243    /// set and before the cursor over that result set is closed. If any columns are bound, it
244    /// returns the data in those columns. If the application has specified a pointer to a row
245    /// status array or a buffer in which to return the number of rows fetched, `fetch` also returns
246    /// this information. Calls to `fetch` can be mixed with calls to `fetch_scroll`.
247    ///
248    /// # Safety
249    ///
250    /// Fetch dereferences bound column pointers.
251    unsafe fn fetch(&mut self) -> SqlResult<()> {
252        unsafe { SQLFetch(self.as_sys()) }.into_sql_result("SQLFetch")
253    }
254
255    /// Retrieves data for a single column in the result set or for a single parameter.
256    fn get_data(&mut self, col_or_param_num: u16, target: &mut impl CDataMut) -> SqlResult<()> {
257        unsafe {
258            SQLGetData(
259                self.as_sys(),
260                col_or_param_num,
261                target.cdata_type(),
262                target.mut_value_ptr(),
263                target.buffer_length(),
264                target.mut_indicator_ptr(),
265            )
266        }
267        .into_sql_result("SQLGetData")
268    }
269
270    /// Release all column buffers bound by `bind_col`. Except bookmark column.
271    fn unbind_cols(&mut self) -> SqlResult<()> {
272        unsafe { SQLFreeStmt(self.as_sys(), FreeStmtOption::Unbind) }.into_sql_result("SQLFreeStmt")
273    }
274
275    /// Bind an integer to hold the number of rows retrieved with fetch in the current row set.
276    /// Calling [`Self::unset_num_rows_fetched`] is going to unbind the value from the statement.
277    ///
278    /// # Safety
279    ///
280    /// `num_rows` must not be moved and remain valid, as long as it remains bound to the cursor.
281    unsafe fn set_num_rows_fetched(&mut self, num_rows: &mut usize) -> SqlResult<()> {
282        let value = num_rows as *mut usize as Pointer;
283        unsafe {
284            sql_set_stmt_attr(
285                self.as_sys(),
286                StatementAttribute::RowsFetchedPtr,
287                value,
288                IS_POINTER,
289            )
290        }
291        .into_sql_result("SQLSetStmtAttr")
292    }
293
294    /// The number of seconds to wait for an SQL statement to execute before returning to the
295    /// application. If `timeout_sec` is `0` (default), there is no timeout.
296    ///
297    /// Note that the application need not call SQLCloseCursor to reuse the statement if a SELECT
298    /// statement timed out.
299    ///
300    /// This corresponds to `SQL_ATTR_QUERY_TIMEOUT` in the ODBC C API.
301    ///
302    /// See:
303    /// <https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlsetstmtattr-function>
304    fn set_query_timeout_sec(&mut self, timeout_sec: usize) -> SqlResult<()> {
305        let value = timeout_sec as *mut usize as Pointer;
306        // This is safe, because `.as_sys`  returns a valid statement handle.
307        unsafe { sql_set_stmt_attr(self.as_sys(), StatementAttribute::QueryTimeout, value, 0) }
308            .into_sql_result("SQLSetStmtAttr")
309    }
310
311    /// The number of seconds to wait for an SQL statement to execute before returning to the
312    /// application. If `timeout_sec` is `0` (default), there is no timeout.
313    ///
314    /// This corresponds to `SQL_ATTR_QUERY_TIMEOUT` in the ODBC C API.
315    fn query_timeout_sec(&mut self) -> SqlResult<usize> {
316        let mut out: usize = 0;
317        let value = &mut out as *mut usize as Pointer;
318        unsafe {
319            sql_get_stmt_attr(
320                self.as_sys(),
321                StatementAttribute::QueryTimeout,
322                value,
323                0,
324                null_mut(),
325            )
326        }
327        .into_sql_result("SQLGetStmtAttr")
328        .on_success(|| out)
329    }
330
331    /// Unsets the integer set by [`Self::set_num_rows_fetched`].
332    ///
333    /// This being a seperate method from [`Self::set_num_rows_fetched` allows us to write us
334    /// cleanup code with less `unsafe` statements since this operation is always safe.
335    fn unset_num_rows_fetched(&mut self) -> SqlResult<()> {
336        unsafe {
337            sql_set_stmt_attr(
338                self.as_sys(),
339                StatementAttribute::RowsFetchedPtr,
340                null_mut(),
341                IS_POINTER,
342            )
343            .into_sql_result("SQLSetStmtAttr")
344        }
345    }
346
347    /// Fetch a column description using the column index.
348    ///
349    /// # Parameters
350    ///
351    /// * `column_number`: Column index. `0` is the bookmark column. The other column indices start
352    ///   with `1`.
353    /// * `column_description`: Holds the description of the column after the call. This method does
354    ///   not provide strong exception safety as the value of this argument is undefined in case of
355    ///   an error.
356    fn describe_col(
357        &mut self,
358        column_number: u16,
359        column_description: &mut ColumnDescription,
360    ) -> SqlResult<()> {
361        let name = &mut column_description.name;
362        // Use maximum available capacity.
363        name.resize(name.capacity(), 0);
364        let mut name_length: i16 = 0;
365        let mut data_type = SqlDataType::UNKNOWN_TYPE;
366        let mut column_size = 0;
367        let mut decimal_digits = 0;
368        let mut nullable = odbc_sys::Nullability::UNKNOWN;
369
370        let res = unsafe {
371            sql_describe_col(
372                self.as_sys(),
373                column_number,
374                mut_buf_ptr(name),
375                clamp_small_int(name.len()),
376                &mut name_length,
377                &mut data_type,
378                &mut column_size,
379                &mut decimal_digits,
380                &mut nullable,
381            )
382            .into_sql_result("SQLDescribeCol")
383        };
384
385        if res.is_err() {
386            return res;
387        }
388
389        column_description.nullability = Nullability::new(nullable);
390
391        if name_length + 1 > clamp_small_int(name.len()) {
392            // Buffer is to small to hold name, retry with larger buffer
393            name.resize(name_length as usize + 1, 0);
394            self.describe_col(column_number, column_description)
395        } else {
396            name.resize(name_length as usize, 0);
397            column_description.data_type = DataType::new(data_type, column_size, decimal_digits);
398            res
399        }
400    }
401
402    /// Executes a statement, using the current values of the parameter marker variables if any
403    /// parameters exist in the statement. SQLExecDirect is the fastest way to submit an SQL
404    /// statement for one-time execution.
405    ///
406    /// # Safety
407    ///
408    /// While `self` as always guaranteed to be a valid allocated handle, this function may
409    /// dereference bound parameters. It is the callers responsibility to ensure these are still
410    /// valid. One strategy is to reset potentially invalid parameters right before the call using
411    /// `reset_parameters`.
412    ///
413    /// # Return
414    ///
415    /// * [`SqlResult::NeedData`] if execution requires additional data from delayed parameters.
416    /// * [`SqlResult::NoData`] if a searched update or delete statement did not affect any rows at
417    ///   the data source.
418    unsafe fn exec_direct(&mut self, statement: &SqlText) -> SqlResult<()> {
419        unsafe {
420            sql_exec_direc(
421                self.as_sys(),
422                statement.ptr(),
423                statement.len_char().try_into().unwrap(),
424            )
425        }
426        .into_sql_result("SQLExecDirect")
427    }
428
429    /// Close an open cursor.
430    fn close_cursor(&mut self) -> SqlResult<()> {
431        unsafe { SQLCloseCursor(self.as_sys()) }.into_sql_result("SQLCloseCursor")
432    }
433
434    /// Send an SQL statement to the data source for preparation. The application can include one or
435    /// more parameter markers in the SQL statement. To include a parameter marker, the application
436    /// embeds a question mark (?) into the SQL string at the appropriate position.
437    fn prepare(&mut self, statement: &SqlText) -> SqlResult<()> {
438        unsafe {
439            sql_prepare(
440                self.as_sys(),
441                statement.ptr(),
442                statement.len_char().try_into().unwrap(),
443            )
444        }
445        .into_sql_result("SQLPrepare")
446    }
447
448    /// Executes a statement prepared by `prepare`. After the application processes or discards the
449    /// results from a call to `execute`, the application can call SQLExecute again with new
450    /// parameter values.
451    ///
452    /// # Safety
453    ///
454    /// While `self` as always guaranteed to be a valid allocated handle, this function may
455    /// dereference bound parameters. It is the callers responsibility to ensure these are still
456    /// valid. One strategy is to reset potentially invalid parameters right before the call using
457    /// `reset_parameters`.
458    ///
459    /// # Return
460    ///
461    /// * [`SqlResult::NeedData`] if execution requires additional data from delayed parameters.
462    /// * [`SqlResult::NoData`] if a searched update or delete statement did not affect any rows at
463    ///   the data source.
464    unsafe fn execute(&mut self) -> SqlResult<()> {
465        unsafe { SQLExecute(self.as_sys()) }.into_sql_result("SQLExecute")
466    }
467
468    /// Number of columns in result set.
469    ///
470    /// Can also be used to check, whether or not a result set has been created at all.
471    fn num_result_cols(&mut self) -> SqlResult<i16> {
472        let mut out: i16 = 0;
473        unsafe { SQLNumResultCols(self.as_sys(), &mut out) }
474            .into_sql_result("SQLNumResultCols")
475            .on_success(|| out)
476    }
477
478    /// Number of placeholders of a prepared query.
479    fn num_params(&mut self) -> SqlResult<u16> {
480        let mut out: i16 = 0;
481        unsafe { SQLNumParams(self.as_sys(), &mut out) }
482            .into_sql_result("SQLNumParams")
483            .on_success(|| out.try_into().unwrap())
484    }
485
486    /// Sets the batch size for bulk cursors, if retrieving many rows at once.
487    ///
488    /// # Safety
489    ///
490    /// It is the callers responsibility to ensure that buffers bound using `bind_col` can hold the
491    /// specified amount of rows.
492    unsafe fn set_row_array_size(&mut self, size: usize) -> SqlResult<()> {
493        assert!(size > 0);
494        unsafe {
495            sql_set_stmt_attr(
496                self.as_sys(),
497                StatementAttribute::RowArraySize,
498                size as Pointer,
499                0,
500            )
501        }
502        .into_sql_result("SQLSetStmtAttr")
503    }
504
505    /// Gets the batch size for bulk cursors, if retrieving many rows at once.
506    fn row_array_size(&mut self) -> SqlResult<usize> {
507        let mut out: usize = 0;
508        let value = &mut out as *mut usize as Pointer;
509        unsafe {
510            sql_get_stmt_attr(
511                self.as_sys(),
512                StatementAttribute::RowArraySize,
513                value,
514                0,
515                null_mut(),
516            )
517        }
518        .into_sql_result("SQLGetStmtAttr")
519        .on_success(|| out)
520    }
521
522    /// Specifies the number of values for each parameter. If it is greater than 1, the data and
523    /// indicator buffers of the statement point to arrays. The cardinality of each array is equal
524    /// to the value of this field.
525    ///
526    /// # Safety
527    ///
528    /// The bound buffers must at least hold the number of elements specified in this call then the
529    /// statement is executed.
530    unsafe fn set_paramset_size(&mut self, size: usize) -> SqlResult<()> {
531        assert!(size > 0);
532        unsafe {
533            sql_set_stmt_attr(
534                self.as_sys(),
535                StatementAttribute::ParamsetSize,
536                size as Pointer,
537                0,
538            )
539        }
540        .into_sql_result("SQLSetStmtAttr")
541    }
542
543    /// Sets the binding type to columnar binding for batch cursors.
544    ///
545    /// Any Positive number indicates a row wise binding with that row length. `0` indicates a
546    /// columnar binding.
547    ///
548    /// # Safety
549    ///
550    /// It is the callers responsibility to ensure that the bound buffers match the memory layout
551    /// specified by this function.
552    unsafe fn set_row_bind_type(&mut self, row_size: usize) -> SqlResult<()> {
553        unsafe {
554            sql_set_stmt_attr(
555                self.as_sys(),
556                StatementAttribute::RowBindType,
557                row_size as Pointer,
558                0,
559            )
560        }
561        .into_sql_result("SQLSetStmtAttr")
562    }
563
564    fn set_metadata_id(&mut self, metadata_id: bool) -> SqlResult<()> {
565        unsafe {
566            sql_set_stmt_attr(
567                self.as_sys(),
568                StatementAttribute::MetadataId,
569                metadata_id as usize as Pointer,
570                0,
571            )
572            .into_sql_result("SQLSetStmtAttr")
573        }
574    }
575
576    /// Enables or disables asynchronous execution for this statement handle. If asynchronous
577    /// execution is not enabled on connection level it is disabled by default and everything is
578    /// executed synchronously.
579    ///
580    /// This is equivalent to stetting `SQL_ATTR_ASYNC_ENABLE` in the bare C API.
581    ///
582    /// See
583    /// <https://docs.microsoft.com/en-us/sql/odbc/reference/develop-app/executing-statements-odbc>
584    fn set_async_enable(&mut self, on: bool) -> SqlResult<()> {
585        unsafe {
586            sql_set_stmt_attr(
587                self.as_sys(),
588                StatementAttribute::AsyncEnable,
589                on as usize as Pointer,
590                0,
591            )
592            .into_sql_result("SQLSetStmtAttr")
593        }
594    }
595
596    /// Binds a buffer holding an input parameter to a parameter marker in an SQL statement. This
597    /// specialized version takes a constant reference to parameter, but is therefore limited to
598    /// binding input parameters. See [`Statement::bind_parameter`] for the version which can bind
599    /// input and output parameters.
600    ///
601    /// See <https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlbindparameter-function>.
602    ///
603    /// # Safety
604    ///
605    /// * It is up to the caller to ensure the lifetimes of the bound parameters.
606    /// * Calling this function may influence other statements that share the APD.
607    /// * `parameter` must be complete, i.e not be truncated.
608    unsafe fn bind_input_parameter(
609        &mut self,
610        parameter_number: u16,
611        parameter: &(impl HasDataType + CData + ?Sized + Send),
612    ) -> SqlResult<()> {
613        let parameter_type = parameter.data_type();
614        unsafe {
615            SQLBindParameter(
616                self.as_sys(),
617                parameter_number,
618                ParamType::Input,
619                parameter.cdata_type(),
620                parameter_type.data_type(),
621                parameter_type
622                    .column_size()
623                    .map(NonZeroUsize::get)
624                    .unwrap_or_default(),
625                parameter_type.decimal_digits(),
626                // We cast const to mut here, but we specify the input_output_type as input.
627                parameter.value_ptr() as *mut c_void,
628                parameter.buffer_length(),
629                // We cast const to mut here, but we specify the input_output_type as input.
630                parameter.indicator_ptr() as *mut isize,
631            )
632        }
633        .into_sql_result("SQLBindParameter")
634    }
635
636    /// Binds a buffer holding a single parameter to a parameter marker in an SQL statement. To bind
637    /// input parameters using constant references see [`Statement::bind_input_parameter`].
638    ///
639    /// See <https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlbindparameter-function>.
640    ///
641    /// # Safety
642    ///
643    /// * It is up to the caller to ensure the lifetimes of the bound parameters.
644    /// * Calling this function may influence other statements that share the APD.
645    /// * `parameter` must be complete, i.e not be truncated. If `input_output_type` indicates
646    ///   [`ParamType::Input`] or [`ParamType::InputOutput`].
647    unsafe fn bind_parameter(
648        &mut self,
649        parameter_number: u16,
650        input_output_type: ParamType,
651        parameter: &mut (impl CDataMut + HasDataType + Send),
652    ) -> SqlResult<()> {
653        let parameter_type = parameter.data_type();
654        unsafe {
655            SQLBindParameter(
656                self.as_sys(),
657                parameter_number,
658                input_output_type,
659                parameter.cdata_type(),
660                parameter_type.data_type(),
661                parameter_type
662                    .column_size()
663                    .map(NonZeroUsize::get)
664                    .unwrap_or_default(),
665                parameter_type.decimal_digits(),
666                parameter.value_ptr() as *mut c_void,
667                parameter.buffer_length(),
668                parameter.mut_indicator_ptr(),
669            )
670        }
671        .into_sql_result("SQLBindParameter")
672    }
673
674    /// Binds an input stream to a parameter marker in an SQL statement. Use this to stream large
675    /// values at statement execution time. To bind preallocated constant buffers see
676    /// [`Statement::bind_input_parameter`].
677    ///
678    /// See <https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlbindparameter-function>.
679    ///
680    /// # Safety
681    ///
682    /// * It is up to the caller to ensure the lifetimes of the bound parameters.
683    /// * Calling this function may influence other statements that share the APD.
684    unsafe fn bind_delayed_input_parameter(
685        &mut self,
686        parameter_number: u16,
687        parameter: &mut (impl DelayedInput + HasDataType),
688    ) -> SqlResult<()> {
689        let paramater_type = parameter.data_type();
690        unsafe {
691            SQLBindParameter(
692                self.as_sys(),
693                parameter_number,
694                ParamType::Input,
695                parameter.cdata_type(),
696                paramater_type.data_type(),
697                paramater_type
698                    .column_size()
699                    .map(NonZeroUsize::get)
700                    .unwrap_or_default(),
701                paramater_type.decimal_digits(),
702                parameter.stream_ptr(),
703                0,
704                // We cast const to mut here, but we specify the input_output_type as input.
705                parameter.indicator_ptr() as *mut isize,
706            )
707        }
708        .into_sql_result("SQLBindParameter")
709    }
710
711    /// `true` if a given column in a result set is unsigned or not a numeric type, `false`
712    /// otherwise.
713    ///
714    /// `column_number`: Index of the column, starting at 1.
715    fn is_unsigned_column(&mut self, column_number: u16) -> SqlResult<bool> {
716        unsafe { self.numeric_col_attribute(Desc::Unsigned, column_number) }.map(|out| match out {
717            0 => false,
718            1 => true,
719            _ => panic!("Unsigned column attribute must be either 0 or 1."),
720        })
721    }
722
723    /// Returns a number identifying the SQL type of the column in the result set.
724    ///
725    /// `column_number`: Index of the column, starting at 1.
726    fn col_type(&mut self, column_number: u16) -> SqlResult<SqlDataType> {
727        unsafe { self.numeric_col_attribute(Desc::Type, column_number) }.map(|ret| {
728            SqlDataType(ret.try_into().expect(
729                "Failed to retrieve data type from ODBC driver. The SQLLEN could not be converted to
730                a 16 Bit integer. If you are on a 64Bit Platform, this may be because your \
731                database driver being compiled against a SQLLEN with 32Bit size instead of 64Bit. \
732                E.g. IBM offers libdb2o.* and libdb2.*. With libdb2o.* being the one with the \
733                correct size.",
734            ))
735        })
736    }
737
738    /// The concise data type. For the datetime and interval data types, this field returns the
739    /// concise data type; for example, `TIME` or `INTERVAL_YEAR`.
740    ///
741    /// `column_number`: Index of the column, starting at 1.
742    fn col_concise_type(&mut self, column_number: u16) -> SqlResult<SqlDataType> {
743        unsafe { self.numeric_col_attribute(Desc::ConciseType, column_number) }.map(|ret| {
744            SqlDataType(ret.try_into().expect(
745                "Failed to retrieve data type from ODBC driver. The SQLLEN could not be \
746                    converted to a 16 Bit integer. If you are on a 64Bit Platform, this may be \
747                    because your database driver being compiled against a SQLLEN with 32Bit size \
748                    instead of 64Bit. E.g. IBM offers libdb2o.* and libdb2.*. With libdb2o.* being \
749                    the one with the correct size.",
750            ))
751        })
752    }
753
754    /// Returns the size in bytes of the columns. For variable sized types the maximum size is
755    /// returned, excluding a terminating zero.
756    ///
757    /// `column_number`: Index of the column, starting at 1.
758    fn col_octet_length(&mut self, column_number: u16) -> SqlResult<isize> {
759        unsafe { self.numeric_col_attribute(Desc::OctetLength, column_number) }
760    }
761
762    /// Maximum number of characters required to display data from the column.
763    ///
764    /// `column_number`: Index of the column, starting at 1.
765    fn col_display_size(&mut self, column_number: u16) -> SqlResult<isize> {
766        unsafe { self.numeric_col_attribute(Desc::DisplaySize, column_number) }
767    }
768
769    /// Precision of the column.
770    ///
771    /// Denotes the applicable precision. For data types SQL_TYPE_TIME, SQL_TYPE_TIMESTAMP, and all
772    /// the interval data types that represent a time interval, its value is the applicable
773    /// precision of the fractional seconds component.
774    fn col_precision(&mut self, column_number: u16) -> SqlResult<isize> {
775        unsafe { self.numeric_col_attribute(Desc::Precision, column_number) }
776    }
777
778    /// The applicable scale for a numeric data type. For DECIMAL and NUMERIC data types, this is
779    /// the defined scale. It is undefined for all other data types.
780    fn col_scale(&mut self, column_number: u16) -> SqlResult<isize> {
781        unsafe { self.numeric_col_attribute(Desc::Scale, column_number) }
782    }
783
784    /// Nullability of the column.
785    ///
786    /// `column_number`: Index of the column, starting at 1.
787    ///
788    /// See `SQL_DESC_NULLABLE ` in the ODBC reference:
789    /// <https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlcolattribute-function>
790    fn col_nullability(&mut self, column_number: u16) -> SqlResult<Nullability> {
791        unsafe { self.numeric_col_attribute(Desc::Nullable, column_number) }
792            .map(|nullability| Nullability::new(odbc_sys::Nullability(nullability as i16)))
793    }
794
795    /// The column alias, if it applies. If the column alias does not apply, the column name is
796    /// returned. If there is no column name or a column alias, an empty string is returned.
797    fn col_name(&mut self, column_number: u16, buffer: &mut Vec<SqlChar>) -> SqlResult<()> {
798        // String length in bytes, not characters. Terminating zero is excluded.
799        let mut string_length_in_bytes: i16 = 0;
800        // Let's utilize all of `buf`s capacity.
801        buffer.resize(buffer.capacity(), 0);
802        unsafe {
803            let mut res = sql_col_attribute(
804                self.as_sys(),
805                column_number,
806                Desc::Name,
807                mut_buf_ptr(buffer) as Pointer,
808                binary_length(buffer).try_into().unwrap(),
809                &mut string_length_in_bytes as *mut i16,
810                null_mut(),
811            )
812            .into_sql_result("SQLColAttribute");
813
814            if res.is_err() {
815                return res;
816            }
817
818            if is_truncated_bin(buffer, string_length_in_bytes.try_into().unwrap()) {
819                // If we could rely on every ODBC driver sticking to the specifcation it would
820                // probably best to resize by `string_length_in_bytes / 2 + 1`. Yet e.g. SQLite
821                // seems to report the length in characters, so to work with a wide range of DB
822                // systems, and since buffers for names are not expected to become super large we
823                // omit the division by two here.
824                buffer.resize((string_length_in_bytes + 1).try_into().unwrap(), 0);
825
826                res = sql_col_attribute(
827                    self.as_sys(),
828                    column_number,
829                    Desc::Name,
830                    mut_buf_ptr(buffer) as Pointer,
831                    binary_length(buffer).try_into().unwrap(),
832                    &mut string_length_in_bytes as *mut i16,
833                    null_mut(),
834                )
835                .into_sql_result("SQLColAttribute");
836            }
837            // Resize buffer to exact string length without terminal zero
838            resize_to_fit_without_tz(buffer, string_length_in_bytes.try_into().unwrap());
839
840            res
841        }
842    }
843
844    /// # Safety
845    ///
846    /// It is the callers responsibility to ensure that `attribute` refers to a numeric attribute.
847    unsafe fn numeric_col_attribute(
848        &mut self,
849        attribute: Desc,
850        column_number: u16,
851    ) -> SqlResult<Len> {
852        let mut out: Len = 0;
853        unsafe {
854            sql_col_attribute(
855                self.as_sys(),
856                column_number,
857                attribute,
858                null_mut(),
859                0,
860                null_mut(),
861                &mut out as *mut Len,
862            )
863        }
864        .into_sql_result("SQLColAttribute")
865        .on_success(|| {
866            #[cfg(not(feature = "structured_logging"))]
867            trace!(
868                "SQLColAttribute called with attribute '{attribute:?}' for column \
869                '{column_number}' reported {out}."
870            );
871            #[cfg(feature = "structured_logging")]
872            trace!(
873                target: "odbc_api",
874                attribute:? = attribute,
875                column_number = column_number,
876                value = out;
877                "Column attribute queried"
878            );
879            out
880        })
881    }
882
883    /// Sets the SQL_DESC_COUNT field of the APD to 0, releasing all parameter buffers set for the
884    /// given StatementHandle.
885    fn reset_parameters(&mut self) -> SqlResult<()> {
886        unsafe {
887            SQLFreeStmt(self.as_sys(), FreeStmtOption::ResetParams).into_sql_result("SQLFreeStmt")
888        }
889    }
890
891    /// Describes parameter marker associated with a prepared SQL statement.
892    ///
893    /// # Parameters
894    ///
895    /// * `parameter_number`: Parameter marker number ordered sequentially in increasing parameter
896    ///   order, starting at 1.
897    fn describe_param(&mut self, parameter_number: u16) -> SqlResult<ColumnType> {
898        let mut data_type = SqlDataType::UNKNOWN_TYPE;
899        let mut parameter_size = 0;
900        let mut decimal_digits = 0;
901        let mut nullable = odbc_sys::Nullability::UNKNOWN;
902        unsafe {
903            SQLDescribeParam(
904                self.as_sys(),
905                parameter_number,
906                &mut data_type,
907                &mut parameter_size,
908                &mut decimal_digits,
909                &mut nullable,
910            )
911        }
912        .into_sql_result("SQLDescribeParam")
913        .on_success(|| ColumnType {
914            data_type: DataType::new(data_type, parameter_size, decimal_digits),
915            nullability: Nullability::new(nullable),
916        })
917    }
918
919    /// Use to check if which additional parameters need data. Should be called after binding
920    /// parameters with an indicator set to [`crate::sys::DATA_AT_EXEC`] or a value created with
921    /// [`crate::sys::len_data_at_exec`].
922    ///
923    /// Return value contains a parameter identifier passed to bind parameter as a value pointer.
924    fn param_data(&mut self) -> SqlResult<Option<Pointer>> {
925        unsafe {
926            let mut param_id: Pointer = null_mut();
927            // Use cases for `PARAM_DATA_AVAILABLE` and `NO_DATA` not implemented yet.
928            match SQLParamData(self.as_sys(), &mut param_id as *mut Pointer) {
929                SqlReturn::NEED_DATA => SqlResult::Success(Some(param_id)),
930                other => other.into_sql_result("SQLParamData").on_success(|| None),
931            }
932        }
933    }
934
935    /// Executes a columns query using this statement handle.
936    fn columns(
937        &mut self,
938        catalog_name: &SqlText,
939        schema_name: &SqlText,
940        table_name: &SqlText,
941        column_name: &SqlText,
942    ) -> SqlResult<()> {
943        unsafe {
944            sql_columns(
945                self.as_sys(),
946                catalog_name.ptr(),
947                catalog_name.len_char().try_into().unwrap(),
948                schema_name.ptr(),
949                schema_name.len_char().try_into().unwrap(),
950                table_name.ptr(),
951                table_name.len_char().try_into().unwrap(),
952                column_name.ptr(),
953                column_name.len_char().try_into().unwrap(),
954            )
955            .into_sql_result("SQLColumns")
956        }
957    }
958
959    /// Returns the list of table, catalog, or schema names, and table types, stored in a specific
960    /// data source. The driver returns the information as a result set.
961    ///
962    /// The catalog, schema and table parameters are search patterns by default unless
963    /// [`Self::set_metadata_id`] is called with `true`. In that case they must also not be `None`
964    /// since otherwise a NulPointer error is emitted.
965    fn tables(
966        &mut self,
967        catalog_name: &SqlText,
968        schema_name: &SqlText,
969        table_name: &SqlText,
970        table_type: &SqlText,
971    ) -> SqlResult<()> {
972        unsafe {
973            sql_tables(
974                self.as_sys(),
975                catalog_name.ptr(),
976                catalog_name.len_char().try_into().unwrap(),
977                schema_name.ptr(),
978                schema_name.len_char().try_into().unwrap(),
979                table_name.ptr(),
980                table_name.len_char().try_into().unwrap(),
981                table_type.ptr(),
982                table_type.len_char().try_into().unwrap(),
983            )
984            .into_sql_result("SQLTables")
985        }
986    }
987
988    /// Create a result set which contains the column names that make up the primary key for the
989    /// table.
990    ///
991    /// # Parameters
992    ///
993    /// * `catalog_name`: Catalog name. If a driver supports catalogs for some tables but not for
994    ///   others, such as when the driver retrieves data from different DBMSs, an empty string ("")
995    ///   denotes those tables that do not have catalogs. `catalog_name` must not contain a string
996    ///   search pattern.
997    /// * `schema_name`: Schema name. If a driver supports schemas for some tables but not for
998    ///   others, such as when the driver retrieves data from different DBMSs, an empty string ("")
999    ///   denotes those tables that do not have schemas. `schema_name` must not contain a string
1000    ///   search pattern.
1001    /// * `table_name`: Table name. `table_name` must not contain a string search pattern.
1002    ///
1003    /// The resulting result set contains the following columns:
1004    ///
1005    /// * `TABLE_CAT`: Primary key table catalog name. NULL if not applicable to the data source. If
1006    ///   a driver supports catalogs for some tables but not for others, such as when the driver
1007    ///   retrieves data from different DBMSs, it returns an empty string ("") for those tables that
1008    ///   do not have catalogs. `VARCHAR`
1009    /// * `TABLE_SCHEM`: Primary key table schema name; NULL if not applicable to the data source.
1010    ///   If a driver supports schemas for some tables but not for others, such as when the driver
1011    ///   retrieves data from different DBMSs, it returns an empty string ("") for those tables that
1012    ///   do not have schemas. `VARCHAR`
1013    /// * `TABLE_NAME`: Primary key table name. `VARCHAR NOT NULL`
1014    /// * `COLUMN_NAME`: Primary key column name. The driver returns an empty string for a column
1015    ///   that does not have a name. `VARCHAR NOT NULL`
1016    /// * `KEY_SEQ`: Column sequence number in key (starting with 1). `SMALLINT NOT NULL`
1017    /// * `PK_NAME`: Primary key name. NULL if not applicable to the data source. `VARCHAR`
1018    ///
1019    /// The maximum length of the VARCHAR columns is driver specific.
1020    ///
1021    /// If [`StatementAttribute::MetadataId`] statement attribute is set to true, catalog, schema
1022    /// and table name parameters are treated as an identifiers and their case is not significant.
1023    /// If it is false, they are ordinary arguments. As such they treated literally and their case
1024    /// is significant.
1025    ///
1026    /// See: <https://learn.microsoft.com/sql/odbc/reference/syntax/sqlprimarykeys-function>
1027    fn primary_keys(
1028        &mut self,
1029        catalog_name: Option<&SqlText>,
1030        schema_name: Option<&SqlText>,
1031        table_name: &SqlText,
1032    ) -> SqlResult<()> {
1033        unsafe {
1034            sql_primary_keys(
1035                self.as_sys(),
1036                catalog_name.map_or(null(), |c| c.ptr()),
1037                catalog_name.map_or(0, |c| c.len_char().try_into().unwrap()),
1038                schema_name.map_or(null(), |s| s.ptr()),
1039                schema_name.map_or(0, |s| s.len_char().try_into().unwrap()),
1040                table_name.ptr(),
1041                table_name.len_char().try_into().unwrap(),
1042            )
1043            .into_sql_result("SQLPrimaryKeys")
1044        }
1045    }
1046
1047    /// This can be used to retrieve either a list of foreign keys in the specified table or a list
1048    /// of foreign keys in other table that refer to the primary key of the specified table.
1049    ///
1050    /// Like [`Self::tables`] this changes the statement to a cursor over the result set.
1051    fn foreign_keys(
1052        &mut self,
1053        pk_catalog_name: &SqlText,
1054        pk_schema_name: &SqlText,
1055        pk_table_name: &SqlText,
1056        fk_catalog_name: &SqlText,
1057        fk_schema_name: &SqlText,
1058        fk_table_name: &SqlText,
1059    ) -> SqlResult<()> {
1060        unsafe {
1061            sql_foreign_keys(
1062                self.as_sys(),
1063                pk_catalog_name.ptr(),
1064                pk_catalog_name.len_char().try_into().unwrap(),
1065                pk_schema_name.ptr(),
1066                pk_schema_name.len_char().try_into().unwrap(),
1067                pk_table_name.ptr(),
1068                pk_table_name.len_char().try_into().unwrap(),
1069                fk_catalog_name.ptr(),
1070                fk_catalog_name.len_char().try_into().unwrap(),
1071                fk_schema_name.ptr(),
1072                fk_schema_name.len_char().try_into().unwrap(),
1073                fk_table_name.ptr(),
1074                fk_table_name.len_char().try_into().unwrap(),
1075            )
1076            .into_sql_result("SQLForeignKeys")
1077        }
1078    }
1079
1080    /// To put a batch of binary data into the data source at statement execution time. May return
1081    /// [`SqlResult::NeedData`]
1082    ///
1083    /// Panics if batch is empty.
1084    fn put_binary_batch(&mut self, batch: &[u8]) -> SqlResult<()> {
1085        // Probably not strictly necessary. MSSQL returns an error than inserting empty batches.
1086        // Still strikes me as a programming error. Maybe we could also do nothing instead.
1087        if batch.is_empty() {
1088            panic!("Attempt to put empty batch into data source.")
1089        }
1090
1091        unsafe {
1092            SQLPutData(
1093                self.as_sys(),
1094                batch.as_ptr() as Pointer,
1095                batch.len().try_into().unwrap(),
1096            )
1097            .into_sql_result("SQLPutData")
1098        }
1099    }
1100
1101    /// Number of rows affected by an `UPDATE`, `INSERT`, or `DELETE` statement.
1102    ///
1103    /// See:
1104    ///
1105    /// <https://docs.microsoft.com/en-us/sql/relational-databases/native-client-odbc-api/sqlrowcount>
1106    /// <https://docs.microsoft.com/en-us/sql/odbc/reference/syntax/sqlrowcount-function>
1107    fn row_count(&mut self) -> SqlResult<isize> {
1108        let mut ret = 0isize;
1109        unsafe {
1110            SQLRowCount(self.as_sys(), &mut ret as *mut isize)
1111                .into_sql_result("SQLRowCount")
1112                .on_success(|| ret)
1113        }
1114    }
1115
1116    /// In polling mode can be used instead of repeating the function call. In notification mode
1117    /// this completes the asynchronous operation. This method panics, in case asynchronous mode is
1118    /// not enabled. [`SqlResult::NoData`] if no asynchronous operation is in progress, or (specific
1119    /// to notification mode) the driver manager has not notified the application.
1120    ///
1121    /// See: <https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlcompleteasync-function>
1122    #[cfg(feature = "odbc_version_3_80")]
1123    fn complete_async(&mut self, function_name: &'static str) -> SqlResult<SqlResult<()>> {
1124        let mut ret = SqlReturn::ERROR;
1125        unsafe {
1126            // Possible return codes are (according to MS ODBC docs):
1127            // * INVALID_HANDLE: The handle indicated by HandleType and Handle is not a valid
1128            //   handle. => Must not happen due self always being a valid statement handle.
1129            // * ERROR: ret is NULL or asynchronous processing is not enabled on the handle. => ret
1130            //   is never NULL. User may choose not to enable asynchronous processing though.
1131            // * NO_DATA: In notification mode, an asynchronous operation is not in progress or the
1132            //   Driver Manager has not notified the application. In polling mode, an asynchronous
1133            //   operation is not in progress.
1134            SQLCompleteAsync(self.handle_type(), self.as_handle(), &mut ret.0 as *mut _)
1135                .into_sql_result("SQLCompleteAsync")
1136        }
1137        .on_success(|| ret.into_sql_result(function_name))
1138    }
1139
1140    /// Determines whether more results are available on a statement containing SELECT, UPDATE,
1141    /// INSERT, or DELETE statements and, if so, initializes processing for those results.
1142    /// [`SqlResult::NoData`] is returned to indicate that there are no more result sets.
1143    ///
1144    /// See: <https://learn.microsoft.com/en-us/sql/odbc/reference/syntax/sqlmoreresults-function>
1145    ///
1146    /// # Safety
1147    ///
1148    /// Since a different result set might have a different schema, care needs to be taken that
1149    /// bound buffers are used correctly.
1150    unsafe fn more_results(&mut self) -> SqlResult<()> {
1151        unsafe { SQLMoreResults(self.as_sys()).into_sql_result("SQLMoreResults") }
1152    }
1153
1154    /// Application Row Descriptor (ARD) associated with the statement handle. It describes the row
1155    /// afte the conversions for the application have been applied. It can be used to query
1156    /// information as well as to set specific desired conversions. E.g. precision and scale for
1157    /// numeric structs. Usually applications have no need to interact directly with the ARD
1158    /// though.
1159    fn application_row_descriptor(&mut self) -> SqlResult<Descriptor<'_>> {
1160        unsafe {
1161            let mut hdesc = HDesc::null();
1162            let hdesc_out = &mut hdesc as *mut HDesc as Pointer;
1163            odbc_sys::SQLGetStmtAttr(
1164                self.as_sys(),
1165                odbc_sys::StatementAttribute::AppRowDesc,
1166                hdesc_out,
1167                0,
1168                null_mut(),
1169            )
1170            .into_sql_result("SQLGetStmtAttr")
1171            .on_success(|| Descriptor::new(hdesc))
1172        }
1173    }
1174
1175    /// Application Parameter Descriptor (APD) associated with the statement handle. Describes the
1176    /// parameter buffers bound to an SQL statement. Usually there is no need for an application to
1177    /// directly interact with the APD. It may be required though to set precision and scale for
1178    /// numeric parameters.
1179    fn application_parameter_descriptor(&mut self) -> SqlResult<Descriptor<'_>> {
1180        unsafe {
1181            let mut hdesc = HDesc::null();
1182            let hdesc_out = &mut hdesc as *mut HDesc as Pointer;
1183            odbc_sys::SQLGetStmtAttr(
1184                self.as_sys(),
1185                odbc_sys::StatementAttribute::AppParamDesc,
1186                hdesc_out,
1187                0,
1188                null_mut(),
1189            )
1190            .into_sql_result("SQLGetStmtAttr")
1191            .on_success(|| Descriptor::new(hdesc))
1192        }
1193    }
1194}
1195
1196impl Statement for StatementImpl<'_> {
1197    /// Gain access to the underlying statement handle without transferring ownership to it.
1198    fn as_sys(&self) -> HStmt {
1199        self.handle
1200    }
1201
1202    fn end_cursor_scope(&mut self) -> SqlResult<()> {
1203        // No-op. We own the statement handle exclusively. We can assume it is freed right after.
1204        SqlResult::Success(())
1205    }
1206}
1207
1208/// The relational type of a column or parameter, including nullability. Returned e.g. by
1209/// [`crate::Prepared::describe_param`].
1210#[derive(Clone, Copy, PartialEq, Eq, Debug)]
1211pub struct ColumnType {
1212    /// Indicates whether the column or parameter may be NULL.
1213    pub nullability: Nullability,
1214    /// The SQL data type of the column or parameter.
1215    pub data_type: DataType,
1216}
1217
1218/// Use [`ColumnType`] instead.
1219#[deprecated(note = "Use `ColumnType` instead.")]
1220pub type ParameterDescription = ColumnType;