odbc_api/handles/
column_description.rs

1use super::{
2    data_type::DataType,
3    sql_char::{slice_to_utf8, DecodingError, SqlChar},
4};
5
6/// Indication of whether a column is nullable or not.
7#[derive(Clone, Copy, Hash, Debug, Eq, PartialEq, Default)]
8pub enum Nullability {
9    /// Indicates that we do not know whether the column is Nullable or not.
10    #[default]
11    Unknown,
12    /// The column may hold NULL values.
13    Nullable,
14    /// The column can not hold NULL values.
15    NoNulls,
16}
17
18impl Nullability {
19    /// Construct a new instance from a `Nullability` new type constant.
20    ///
21    /// ```
22    /// use odbc_api::Nullability;
23    ///
24    /// assert_eq!(Nullability::Unknown, Nullability::new(odbc_sys::Nullability::UNKNOWN));
25    /// assert_eq!(Nullability::NoNulls, Nullability::new(odbc_sys::Nullability::NO_NULLS));
26    /// assert_eq!(Nullability::Nullable, Nullability::new(odbc_sys::Nullability::NULLABLE));
27    /// ```
28    pub fn new(nullability: odbc_sys::Nullability) -> Self {
29        match nullability {
30            odbc_sys::Nullability::UNKNOWN => Nullability::Unknown,
31            odbc_sys::Nullability::NO_NULLS => Nullability::NoNulls,
32            odbc_sys::Nullability::NULLABLE => Nullability::Nullable,
33            other => panic!("ODBC returned invalid value for Nullable: {}", other.0),
34        }
35    }
36}
37
38/// Describes the type and attributes of a column.
39#[derive(Clone, Debug, Eq, PartialEq, Default)]
40pub struct ColumnDescription {
41    /// Column name. May be empty if unavailable.
42    pub name: Vec<SqlChar>,
43    /// Type of the column
44    pub data_type: DataType,
45    /// Indicates whether the column is nullable or not.
46    pub nullability: Nullability,
47}
48
49impl ColumnDescription {
50    /// In production, an 'empty' [`ColumnDescription`] is expected to be constructed via the
51    /// [`Default`] trait. It is then filled using [`crate::ResultSetMetadata::describe_col`]. When
52    /// writing test cases however it might be desirable to directly instantiate a
53    /// [`ColumnDescription`]. This constructor enables you to do that, without caring which type
54    /// `SqlChar` resolves to.
55    pub fn new(name: &str, data_type: DataType, nullability: Nullability) -> Self {
56        #[cfg(not(any(feature = "wide", all(not(feature = "narrow"), target_os = "windows"))))]
57        pub fn utf8_to_vec_char(text: &str) -> Vec<u8> {
58            text.to_owned().into_bytes()
59        }
60        #[cfg(any(feature = "wide", all(not(feature = "narrow"), target_os = "windows")))]
61        pub fn utf8_to_vec_char(text: &str) -> Vec<u16> {
62            use widestring::U16String;
63            U16String::from_str(text).into_vec()
64        }
65        Self {
66            name: utf8_to_vec_char(name),
67            data_type,
68            nullability,
69        }
70    }
71
72    /// Converts the internal UTF16 representation of the column name into UTF8 and returns the
73    /// result as a `String`.
74    pub fn name_to_string(&self) -> Result<String, DecodingError> {
75        slice_to_utf8(&self.name)
76    }
77
78    /// `true` if the column is `Nullable` or it is not know whether the column is nullable. `false`
79    /// if and only if the column is `NoNulls`.
80    pub fn could_be_nullable(&self) -> bool {
81        match self.nullability {
82            Nullability::Nullable | Nullability::Unknown => true,
83            Nullability::NoNulls => false,
84        }
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use crate::Nullability;
91
92    /// Application should panic if ODBC driver returns unsupported value for nullable
93    #[test]
94    #[should_panic(expected = "ODBC returned invalid value for Nullable: 5")]
95    fn invalid_nullable_representation() {
96        Nullability::new(odbc_sys::Nullability(5));
97    }
98}