1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
use crate::protocol::parts::type_id::TypeId;
use std::sync::Arc;
use vec_map::VecMap;

// The structure is a bit weird; reason is that we want to retain the transfer format
// which seeks to avoid String duplication

/// Metadata of a field in a `ResultSet`.
#[derive(Clone, Debug)]
pub struct FieldMetadata {
    inner: InnerFieldMetadata,
    names: Arc<VecMap<String>>,
}

/// Describes a single field (column) in a result set.
#[derive(Clone, Copy, Debug)]
pub(crate) struct InnerFieldMetadata {
    schemaname_idx: u32,
    tablename_idx: u32,
    columnname_idx: u32,
    displayname_idx: u32,
    // Column_options.
    // Bit pattern:
    // 0 = Mandatory
    // 1 = Optional
    // 2 = Default
    // 3 = Escape_char
    // 4 = Readonly
    // 5 = Autoincrement
    // 6 = ArrayType
    column_options: u8,
    type_id: TypeId,
    // scale
    scale: i16,
    // Precision
    precision: i16,
}
impl InnerFieldMetadata {
    #[allow(clippy::too_many_arguments)]
    pub fn new(
        schemaname_idx: u32,
        tablename_idx: u32,
        columnname_idx: u32,
        displayname_idx: u32,
        column_options: u8,
        type_id: TypeId,
        scale: i16,
        precision: i16,
    ) -> Self {
        Self {
            schemaname_idx,
            tablename_idx,
            columnname_idx,
            displayname_idx,
            column_options,
            type_id,
            scale,
            precision,
        }
    }
}

impl FieldMetadata {
    pub(crate) fn new(inner: InnerFieldMetadata, names: Arc<VecMap<String>>) -> Self {
        Self { inner, names }
    }

    /// Database schema of the field.
    pub fn schemaname(&self) -> &str {
        self.names
            .get(self.inner.schemaname_idx as usize)
            .map_or("", String::as_str)
    }

    /// Database table.
    pub fn tablename(&self) -> &str {
        self.names
            .get(self.inner.tablename_idx as usize)
            .map_or("", String::as_str)
    }

    /// Column name.
    pub fn columnname(&self) -> &str {
        self.names
            .get(self.inner.columnname_idx as usize)
            .map_or("", String::as_str)
    }

    /// Display name of the column.
    pub fn displayname(&self) -> &str {
        self.names
            .get(self.inner.displayname_idx as usize)
            .map_or("", String::as_str)
    }

    /// Returns the id of the value type.
    pub fn type_id(&self) -> TypeId {
        self.inner.type_id
    }

    // Returns true for BLOB, CLOB, and NCLOB, and false otherwise.
    pub(crate) fn is_lob(&self) -> bool {
        matches!(
            self.inner.type_id,
            TypeId::BLOB | TypeId::CLOB | TypeId::NCLOB
        )
    }

    /// True if column can contain NULL values.
    pub fn is_nullable(&self) -> bool {
        (self.inner.column_options & 0b_0000_0010_u8) != 0
    }

    /// The length or the precision of the value.
    ///
    /// Is `-1` for LOB types.
    pub fn precision(&self) -> i16 {
        self.inner.precision
    }

    /// The scale of the value.
    ///
    /// Is `0` for all types where a scale does not make sense.
    pub fn scale(&self) -> i16 {
        self.inner.scale
    }

    /// Returns true if the column has a default value.
    pub fn has_default(&self) -> bool {
        (self.inner.column_options & 0b_0000_0100_u8) != 0
    }

    ///  Returns true if the column is read-only.
    pub fn is_read_only(&self) -> bool {
        (self.inner.column_options & 0b_0100_0000_u8) != 0
    }

    /// Returns true if the column is auto-incremented.
    pub fn is_auto_incremented(&self) -> bool {
        (self.inner.column_options & 0b_0010_0000_u8) != 0
    }

    /// Returns true if the column is of array type.
    pub fn is_array_type(&self) -> bool {
        (self.inner.column_options & 0b_0100_0000_u8) != 0
    }
}