rs-odbc 0.2.0

Minimal safe Rust implementation of ODBC
Documentation
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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
use crate::api::Handle;
use crate::attr::{Attr, AttrGet, AttrLen, AttrZeroAssert, Void};
use crate::convert::AsMutSQLPOINTER;
use crate::env::OdbcVersion;
use crate::handle::SQLHSTMT;
use crate::str::{OdbcChar, OdbcStr};
use crate::{
    sqlreturn::SQLRETURN, Def, Ident, OdbcDefined, Scalar, SQLCHAR, SQLINTEGER, SQLLEN, SQLPOINTER,
    SQLSMALLINT, SQLWCHAR,
};
use core::mem::MaybeUninit;
use rs_odbc_derive::{odbc_type, Ident};

pub trait DiagField<H: Handle, D: Ident>: Attr<D> + AttrLen<Self::DefinedBy, SQLSMALLINT> {
    // TODO: These could be checked by the type system
    // SQL_DIAG_CURSOR_ROW_COUNT -> The contents of this field are defined only after SQLExecute, SQLExecDirect, or SQLMoreResults
    // SQL_DIAG_DYNAMIC_FUNCTION -> The contents of this field are defined only after SQLExecute, SQLExecDirect, or SQLMoreResults
    // SQL_DIAG_DYNAMIC_FUNCTION_CODE -> The contents of this field are defined only after SQLExecute, SQLExecDirect, or SQLMoreResults
    // SQL_DIAG_ROW_COUNT -> SQLExecute, SQLExecDirect, SQLBulkOperations, or SQLSetPos
}

pub const SQLSTATE_SIZE: usize = 5;

#[repr(transparent)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SQLSTATE<C: OdbcChar>([C; SQLSTATE_SIZE + 1]);
impl SQLSTATE<SQLCHAR> {
    pub fn new(init: &str) -> SQLSTATE<SQLCHAR> {
        let bytes = init.as_bytes();

        assert_eq!(
            SQLSTATE_SIZE,
            bytes.len(),
            "SQLSTATE({}) len != {}",
            init,
            SQLSTATE_SIZE
        );

        let mut sqlstate = [SQLCHAR::default(); SQLSTATE_SIZE + 1];
        for (s, i) in sqlstate.iter_mut().zip(bytes.iter()) {
            *s = *i;
        }

        Self(sqlstate)
    }
}
impl SQLSTATE<SQLWCHAR> {
    pub fn new(init: &str) -> SQLSTATE<SQLWCHAR> {
        let bytes = init.as_bytes();

        assert_eq!(
            SQLSTATE_SIZE,
            bytes.len(),
            "SQLSTATE({}) len != {}",
            init,
            SQLSTATE_SIZE
        );

        let mut sqlstate = [SQLWCHAR::default(); SQLSTATE_SIZE + 1];
        for (s, i) in sqlstate.iter_mut().zip(bytes.iter()) {
            *s = *i as u16;
        }

        Self(sqlstate)
    }
}
unsafe impl<C: OdbcChar> AsMutSQLPOINTER for SQLSTATE<C> {
    fn as_mut_SQLPOINTER(&mut self) -> SQLPOINTER {
        (self as *mut Self).cast()
    }
}
unsafe impl<C: OdbcChar> AsMutSQLPOINTER for MaybeUninit<SQLSTATE<C>> {
    fn as_mut_SQLPOINTER(&mut self) -> SQLPOINTER {
        self.as_mut_ptr().cast()
    }
}
impl PartialEq<&str> for SQLSTATE<SQLCHAR> {
    fn eq(&self, other: &&str) -> bool {
        *self == SQLSTATE::<SQLCHAR>::new(other)
    }
}
impl PartialEq<&str> for SQLSTATE<SQLWCHAR> {
    fn eq(&self, other: &&str) -> bool {
        *self == SQLSTATE::<SQLWCHAR>::new(other)
    }
}
impl<'a, C: OdbcChar> PartialEq<SQLSTATE<C>> for &'a str
where
    SQLSTATE<C>: PartialEq<&'a str>,
{
    fn eq(&self, other: &SQLSTATE<C>) -> bool {
        other == self
    }
}
impl<C: OdbcChar> AttrZeroAssert for SQLSTATE<C> {
    // This is character field and doesn't have to be zero checked
}
unsafe impl<C: OdbcChar> AttrLen<OdbcDefined, SQLSMALLINT> for SQLSTATE<C>
where
    MaybeUninit<SQLSTATE<C>>: AttrLen<OdbcDefined, SQLSMALLINT>,
{
    type StrLen = Void;

    fn len(&self) -> SQLSMALLINT {
        // This is ok because MaybeUninit<T> has the same memory layout as T
        <MaybeUninit<SQLSTATE<C>>>::len(unsafe { core::mem::transmute(self) })
    }
}
unsafe impl<AD: Def> AttrLen<AD, SQLSMALLINT> for MaybeUninit<SQLSTATE<SQLCHAR>> {
    type StrLen = Void;

    fn len(&self) -> SQLSMALLINT {
        (SQLSTATE_SIZE + 1) as SQLSMALLINT
    }
}
unsafe impl<AD: Def> AttrLen<AD, SQLSMALLINT> for MaybeUninit<SQLSTATE<SQLWCHAR>> {
    type StrLen = Void;

    fn len(&self) -> SQLSMALLINT {
        (core::mem::size_of::<SQLWCHAR>() * (SQLSTATE_SIZE + 1)) as SQLSMALLINT
    }
}

// Implement DiagField for uninitialized diagnostic attributes
impl<D: Ident, T: Scalar, H: Handle> DiagField<H, D> for MaybeUninit<T>
where
    T: DiagField<H, D> + AttrGet<D>,
    Self: AttrLen<Self::DefinedBy, SQLSMALLINT>,
{
}

impl<D: Ident, T: Scalar, H: Handle> DiagField<H, D> for [MaybeUninit<T>]
where
    [T]: DiagField<H, D> + AttrGet<D>,
    Self: AttrLen<Self::DefinedBy, SQLSMALLINT>,
{
}

impl<D: Ident, H: Handle> DiagField<H, D> for OdbcStr<MaybeUninit<SQLCHAR>> where
    OdbcStr<SQLCHAR>: DiagField<H, D> + AttrGet<D>
{
}
impl<D: Ident, H: Handle> DiagField<H, D> for OdbcStr<MaybeUninit<SQLWCHAR>> where
    OdbcStr<SQLWCHAR>: DiagField<H, D> + AttrGet<D>
{
}

//=====================================================================================//
//-------------------------------------Attributes--------------------------------------//

/////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////// Header fields ////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////

#[derive(Ident)]
#[identifier(SQLSMALLINT, -1249)]
#[allow(non_camel_case_types)]
pub struct SQL_DIAG_CURSOR_ROW_COUNT;
unsafe impl Attr<SQL_DIAG_CURSOR_ROW_COUNT> for SQLLEN {
    type DefinedBy = OdbcDefined;
}
impl<V: OdbcVersion> DiagField<SQLHSTMT<'_, '_, '_, V>, SQL_DIAG_CURSOR_ROW_COUNT> for SQLLEN {}
unsafe impl AttrGet<SQL_DIAG_CURSOR_ROW_COUNT> for SQLLEN {}

#[derive(Ident)]
#[identifier(SQLSMALLINT, 7)]
#[allow(non_camel_case_types)]
pub struct SQL_DIAG_DYNAMIC_FUNCTION;
unsafe impl Attr<SQL_DIAG_DYNAMIC_FUNCTION> for OdbcStr<SQLCHAR> {
    type DefinedBy = OdbcDefined;
}
impl<V: OdbcVersion> DiagField<SQLHSTMT<'_, '_, '_, V>, SQL_DIAG_DYNAMIC_FUNCTION>
    for OdbcStr<SQLCHAR>
{
}
unsafe impl AttrGet<SQL_DIAG_DYNAMIC_FUNCTION> for OdbcStr<SQLCHAR> {}

#[derive(Ident)]
#[identifier(SQLSMALLINT, 12)]
#[allow(non_camel_case_types)]
pub struct SQL_DIAG_DYNAMIC_FUNCTION_CODE;
unsafe impl Attr<SQL_DIAG_DYNAMIC_FUNCTION_CODE> for DiagDynamicFunctionCode {
    type DefinedBy = OdbcDefined;
}
impl<V: OdbcVersion> DiagField<SQLHSTMT<'_, '_, '_, V>, SQL_DIAG_DYNAMIC_FUNCTION_CODE>
    for DiagDynamicFunctionCode
{
}
unsafe impl AttrGet<SQL_DIAG_DYNAMIC_FUNCTION_CODE> for DiagDynamicFunctionCode {}

#[derive(Ident)]
#[identifier(SQLSMALLINT, 2)]
#[allow(non_camel_case_types)]
pub struct SQL_DIAG_NUMBER;
unsafe impl Attr<SQL_DIAG_NUMBER> for SQLINTEGER {
    type DefinedBy = OdbcDefined;
}
impl<H: Handle> DiagField<H, SQL_DIAG_NUMBER> for SQLINTEGER {}
unsafe impl AttrGet<SQL_DIAG_NUMBER> for SQLINTEGER {}

#[derive(Ident)]
#[identifier(SQLSMALLINT, 1)]
#[allow(non_camel_case_types)]
pub struct SQL_DIAG_RETURNCODE;
unsafe impl Attr<SQL_DIAG_RETURNCODE> for SQLRETURN {
    type DefinedBy = OdbcDefined;
}
impl<H: Handle> DiagField<H, SQL_DIAG_RETURNCODE> for SQLRETURN {}
unsafe impl AttrGet<SQL_DIAG_RETURNCODE> for SQLRETURN {}

#[derive(Ident)]
#[identifier(SQLSMALLINT, 3)]
#[allow(non_camel_case_types)]
pub struct SQL_DIAG_ROW_COUNT;
unsafe impl Attr<SQL_DIAG_ROW_COUNT> for SQLLEN {
    type DefinedBy = OdbcDefined;
}
impl<V: OdbcVersion> DiagField<SQLHSTMT<'_, '_, '_, V>, SQL_DIAG_ROW_COUNT> for SQLLEN {}
unsafe impl AttrGet<SQL_DIAG_ROW_COUNT> for SQLLEN {}

/////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////// Record fields ////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////

#[derive(Ident)]
#[identifier(SQLSMALLINT, 8)]
#[allow(non_camel_case_types)]
pub struct SQL_DIAG_CLASS_ORIGIN;
unsafe impl Attr<SQL_DIAG_CLASS_ORIGIN> for OdbcStr<SQLCHAR> {
    type DefinedBy = OdbcDefined;
}
impl<H: Handle> DiagField<H, SQL_DIAG_CLASS_ORIGIN> for OdbcStr<SQLCHAR> {}
unsafe impl AttrGet<SQL_DIAG_CLASS_ORIGIN> for OdbcStr<SQLCHAR> {}

#[derive(Ident)]
#[identifier(SQLSMALLINT, -1247)]
#[allow(non_camel_case_types)]
pub struct SQL_DIAG_COLUMN_NUMBER;
unsafe impl Attr<SQL_DIAG_COLUMN_NUMBER> for DiagColumnNumber {
    type DefinedBy = OdbcDefined;
}
impl<V: OdbcVersion> DiagField<SQLHSTMT<'_, '_, '_, V>, SQL_DIAG_COLUMN_NUMBER>
    for DiagColumnNumber
{
}
unsafe impl AttrGet<SQL_DIAG_COLUMN_NUMBER> for DiagColumnNumber {}

#[derive(Ident)]
#[identifier(SQLSMALLINT, 10)]
#[allow(non_camel_case_types)]
pub struct SQL_DIAG_CONNECTION_NAME;
unsafe impl Attr<SQL_DIAG_CONNECTION_NAME> for OdbcStr<SQLCHAR> {
    type DefinedBy = OdbcDefined;
}
impl<H: Handle> DiagField<H, SQL_DIAG_CONNECTION_NAME> for OdbcStr<SQLCHAR> {}
unsafe impl AttrGet<SQL_DIAG_CONNECTION_NAME> for OdbcStr<SQLCHAR> {}

#[derive(Ident)]
#[identifier(SQLSMALLINT, 6)]
#[allow(non_camel_case_types)]
pub struct SQL_DIAG_MESSAGE_TEXT;
unsafe impl Attr<SQL_DIAG_MESSAGE_TEXT> for OdbcStr<SQLCHAR> {
    type DefinedBy = OdbcDefined;
}
impl<H: Handle> DiagField<H, SQL_DIAG_MESSAGE_TEXT> for OdbcStr<SQLCHAR> {}
unsafe impl AttrGet<SQL_DIAG_MESSAGE_TEXT> for OdbcStr<SQLCHAR> {}

#[derive(Ident)]
#[identifier(SQLSMALLINT, 5)]
#[allow(non_camel_case_types)]
pub struct SQL_DIAG_NATIVE;
unsafe impl Attr<SQL_DIAG_NATIVE> for SQLINTEGER {
    type DefinedBy = OdbcDefined;
}
impl<H: Handle> DiagField<H, SQL_DIAG_NATIVE> for SQLINTEGER {}
unsafe impl AttrGet<SQL_DIAG_NATIVE> for SQLINTEGER {}

#[derive(Ident)]
#[identifier(SQLSMALLINT, -1248)]
#[allow(non_camel_case_types)]
pub struct SQL_DIAG_ROW_NUMBER;
unsafe impl Attr<SQL_DIAG_ROW_NUMBER> for DiagRowNumber {
    type DefinedBy = OdbcDefined;
}
impl<V: OdbcVersion> DiagField<SQLHSTMT<'_, '_, '_, V>, SQL_DIAG_ROW_NUMBER> for DiagRowNumber {}
unsafe impl AttrGet<SQL_DIAG_ROW_NUMBER> for DiagRowNumber {}

#[derive(Ident)]
#[identifier(SQLSMALLINT, 11)]
#[allow(non_camel_case_types)]
pub struct SQL_DIAG_SERVER_NAME;
unsafe impl Attr<SQL_DIAG_SERVER_NAME> for OdbcStr<SQLCHAR> {
    type DefinedBy = OdbcDefined;
}
impl<H: Handle> DiagField<H, SQL_DIAG_SERVER_NAME> for OdbcStr<SQLCHAR> {}
unsafe impl AttrGet<SQL_DIAG_SERVER_NAME> for OdbcStr<SQLCHAR> {}

#[derive(Ident)]
#[identifier(SQLSMALLINT, 4)]
#[allow(non_camel_case_types)]
pub struct SQL_DIAG_SQLSTATE;
unsafe impl<C: OdbcChar> Attr<SQL_DIAG_SQLSTATE> for SQLSTATE<C> {
    type DefinedBy = OdbcDefined;
}
impl<H: Handle> DiagField<H, SQL_DIAG_SQLSTATE> for SQLSTATE<SQLCHAR> {}
impl<H: Handle> DiagField<H, SQL_DIAG_SQLSTATE> for SQLSTATE<SQLWCHAR> {}
unsafe impl<C: OdbcChar> AttrGet<SQL_DIAG_SQLSTATE> for SQLSTATE<C> {}

#[derive(Ident)]
#[identifier(SQLSMALLINT, 9)]
#[allow(non_camel_case_types)]
pub struct SQL_DIAG_SUBCLASS_ORIGIN;
unsafe impl Attr<SQL_DIAG_SUBCLASS_ORIGIN> for OdbcStr<SQLCHAR> {
    type DefinedBy = OdbcDefined;
}
impl<H: Handle> DiagField<H, SQL_DIAG_SUBCLASS_ORIGIN> for OdbcStr<SQLCHAR> {}
unsafe impl AttrGet<SQL_DIAG_SUBCLASS_ORIGIN> for OdbcStr<SQLCHAR> {}

//=====================================================================================//

#[odbc_type(SQLINTEGER)]
pub struct DiagDynamicFunctionCode;
pub const SQL_DIAG_ALTER_DOMAIN: DiagDynamicFunctionCode = DiagDynamicFunctionCode(3);
pub const SQL_DIAG_ALTER_TABLE: DiagDynamicFunctionCode = DiagDynamicFunctionCode(4);
pub const SQL_DIAG_CREATE_ASSERTION: DiagDynamicFunctionCode = DiagDynamicFunctionCode(6);
pub const SQL_DIAG_CREATE_CHARACTER_SET: DiagDynamicFunctionCode = DiagDynamicFunctionCode(8);
pub const SQL_DIAG_CREATE_COLLATION: DiagDynamicFunctionCode = DiagDynamicFunctionCode(10);
pub const SQL_DIAG_CREATE_DOMAIN: DiagDynamicFunctionCode = DiagDynamicFunctionCode(23);
pub const SQL_DIAG_CREATE_INDEX: DiagDynamicFunctionCode = DiagDynamicFunctionCode(-1);
pub const SQL_DIAG_CREATE_TABLE: DiagDynamicFunctionCode = DiagDynamicFunctionCode(77);
pub const SQL_DIAG_CREATE_VIEW: DiagDynamicFunctionCode = DiagDynamicFunctionCode(84);
pub const SQL_DIAG_SELECT_CURSOR: DiagDynamicFunctionCode = DiagDynamicFunctionCode(85);
pub const SQL_DIAG_DYNAMIC_DELETE_CURSOR: DiagDynamicFunctionCode = DiagDynamicFunctionCode(38);
pub const SQL_DIAG_DELETE_WHERE: DiagDynamicFunctionCode = DiagDynamicFunctionCode(19);
pub const SQL_DIAG_DROP_ASSERTION: DiagDynamicFunctionCode = DiagDynamicFunctionCode(24);
pub const SQL_DIAG_DROP_CHARACTER_SET: DiagDynamicFunctionCode = DiagDynamicFunctionCode(25);
pub const SQL_DIAG_DROP_COLLATION: DiagDynamicFunctionCode = DiagDynamicFunctionCode(26);
pub const SQL_DIAG_DROP_DOMAIN: DiagDynamicFunctionCode = DiagDynamicFunctionCode(27);
pub const SQL_DIAG_DROP_INDEX: DiagDynamicFunctionCode = DiagDynamicFunctionCode(-2);
pub const SQL_DIAG_DROP_SCHEMA: DiagDynamicFunctionCode = DiagDynamicFunctionCode(31);
pub const SQL_DIAG_DROP_TABLE: DiagDynamicFunctionCode = DiagDynamicFunctionCode(32);
pub const SQL_DIAG_DROP_TRANSLATION: DiagDynamicFunctionCode = DiagDynamicFunctionCode(33);
pub const SQL_DIAG_DROP_VIEW: DiagDynamicFunctionCode = DiagDynamicFunctionCode(36);
pub const SQL_DIAG_GRANT: DiagDynamicFunctionCode = DiagDynamicFunctionCode(48);
pub const SQL_DIAG_INSERT: DiagDynamicFunctionCode = DiagDynamicFunctionCode(50);
pub const SQL_DIAG_CALL: DiagDynamicFunctionCode = DiagDynamicFunctionCode(7);
pub const SQL_DIAG_REVOKE: DiagDynamicFunctionCode = DiagDynamicFunctionCode(59);
pub const SQL_DIAG_CREATE_SCHEMA: DiagDynamicFunctionCode = DiagDynamicFunctionCode(64);
pub const SQL_DIAG_CREATE_TRANSLATION: DiagDynamicFunctionCode = DiagDynamicFunctionCode(79);
pub const SQL_DIAG_DYNAMIC_UPDATE_CURSOR: DiagDynamicFunctionCode = DiagDynamicFunctionCode(81);
pub const SQL_DIAG_UPDATE_WHERE: DiagDynamicFunctionCode = DiagDynamicFunctionCode(82);
pub const SQL_DIAG_UNKNOWN_STATEMENT: DiagDynamicFunctionCode = DiagDynamicFunctionCode(0);

#[odbc_type(SQLINTEGER)]
pub struct DiagColumnNumber;
pub const SQL_NO_COLUMN_NUMBER: DiagColumnNumber = DiagColumnNumber(-1);
pub const SQL_COLUMN_NUMBER_UNKNOWN: DiagColumnNumber = DiagColumnNumber(-2);

#[odbc_type(SQLLEN)]
pub struct DiagRowNumber;
pub const SQL_NO_ROW_NUMBER: DiagRowNumber = DiagRowNumber(-1);
pub const SQL_ROW_NUMBER_UNKNOWN: DiagRowNumber = DiagRowNumber(-2);

//=====================================================================================//
//----------------------------------------Tests----------------------------------------//

#[cfg(test)]
mod test {
    #![allow(non_snake_case)]

    use super::*;

    #[test]
    fn new_sqlstate_SQLCHAR() {
        let sqlstate = SQLSTATE::<SQLCHAR>::new("12345");

        assert_eq!(6, sqlstate.len());
        assert_eq!([49, 50, 51, 52, 53, 0].as_ref(), sqlstate.0);
    }

    #[test]
    fn new_sqlstate_SQLWCHAR() {
        let sqlstate = SQLSTATE::<SQLWCHAR>::new("12345");

        assert_eq!(12, sqlstate.len());
        assert_eq!([49, 50, 51, 52, 53, 0].as_ref(), sqlstate.0);
    }

    #[test]
    #[should_panic]
    fn new_sqlstate_SQLCHAR_size_4() {
        SQLSTATE::<SQLCHAR>::new("0000");
    }

    #[test]
    #[should_panic]
    fn new_sqlstate_SQLWCHAR_size_4() {
        SQLSTATE::<SQLWCHAR>::new("0000");
    }

    #[test]
    fn sqlstate_SQLCHAR_cmp() {
        let sqlstate = SQLSTATE::<SQLCHAR>::new("12345");

        assert_eq!("12345", sqlstate);
        assert_eq!(sqlstate, "12345");
    }

    #[test]
    fn sqlstate_SQLWCHAR_cmp() {
        let sqlstate = SQLSTATE::<SQLWCHAR>::new("12345");

        assert_eq!("12345", sqlstate);
        assert_eq!(sqlstate, "12345");
    }
}