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