sqlany_extfnapi/
lib.rs

1#![allow(non_snake_case, non_camel_case_types, non_upper_case_globals, unused)]
2
3use std::{
4    borrow::Cow,
5    convert::{Infallible, TryFrom, TryInto},
6    ffi::{CStr, CString},
7    fmt, mem,
8    os::raw::{c_int, c_long, c_short, c_uint, c_ulong, c_ushort, c_void},
9};
10
11mod value_cursor;
12
13use thiserror::Error;
14pub use value_cursor::ExtFnValueCursor;
15
16const DT_TYPES: u16 = 1022;
17
18const DT_NOTYPE: u16 = 0;
19
20const DT_SMALLINT: u16 = 500;
21const DT_INT: u16 = 496;
22const DT_FLOAT: u16 = 482;
23const DT_DOUBLE: u16 = 480;
24const DT_STRING: u16 = 460;
25const DT_FIXCHAR: u16 = 452;
26const DT_VARCHAR: u16 = 448;
27const DT_LONGVARCHAR: u16 = 456;
28const DT_BINARY: u16 = 524;
29const DT_LONGBINARY: u16 = 528;
30const DT_TINYINT: u16 = 604;
31const DT_BIGINT: u16 = 608;
32const DT_UNSINT: u16 = 612;
33const DT_UNSSMALLINT: u16 = 616;
34const DT_UNSBIGINT: u16 = 620;
35const DT_BIT: u16 = 624;
36const DT_NSTRING: u16 = 628;
37const DT_NFIXCHAR: u16 = 632;
38const DT_NVARCHAR: u16 = 636;
39const DT_LONGNVARCHAR: u16 = 640;
40
41const EXTFN_V0_API: u32 = 0;
42const EXTFN_V2_API: u32 = 2;
43const EXTFN_V3_API: u32 = 3;
44const EXTFN_V4_API: u32 = 4;
45pub const EXTFN_API_VERSION: u32 = 2;
46
47const EXTFN_CONNECTION_HANDLE_ARG_NUM: a_sql_uint32 = !0;
48const EXTFN_RESULT_SET_ARG_NUM: a_sql_uint32 = !0;
49
50pub type a_sql_int32 = c_int;
51pub type a_sql_uint32 = c_uint;
52pub type a_sql_int64 = c_long;
53pub type a_sql_uint64 = c_ulong;
54type a_sql_data_type = c_ushort;
55
56/// Representation of the SQLDef data type presented to the function
57/// These types are derived from sqldef.h
58#[repr(u16)]
59#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
60pub enum SqlDataTypeKind {
61    Unknown = DT_NOTYPE,
62    FixedChar = DT_FIXCHAR,
63    VarChar = DT_VARCHAR,
64    LongVarChar = DT_LONGVARCHAR,
65    NFixedChar = DT_NFIXCHAR,
66    NVarChar = DT_NVARCHAR,
67    LongNVarChar = DT_LONGNVARCHAR,
68    Binary = DT_BINARY,
69    LongBinary = DT_LONGBINARY,
70    U8 = DT_TINYINT,
71    I16 = DT_SMALLINT,
72    U16 = DT_UNSSMALLINT,
73    I32 = DT_INT,
74    U32 = DT_UNSINT,
75    I64 = DT_BIGINT,
76    U64 = DT_UNSBIGINT,
77    F32 = DT_FLOAT,
78    F64 = DT_DOUBLE,
79}
80
81#[repr(C)]
82#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
83pub struct SqlDataType(::std::os::raw::c_ushort);
84
85bitflags::bitflags! {
86    pub struct SqlDataTypeFlags: u16 {
87        const NULLS_ALLOWED = 1;
88        const PROCEDURE_OUT = 32768;
89        const PROCEDURE_IN = 16384;
90        const UPDATABLE = 8192;
91        const DESCRIBE_INPUT = 4096;
92        const AUTO_INCREMENT = 2048;
93        const KEY_COLUMN = 1024;
94        const HIDDEN_COLUMN = 512;
95        const HAS_USERTYPE_INFO = 256;
96    }
97}
98
99impl SqlDataType {
100    pub fn new(kind: SqlDataTypeKind) -> Self {
101        SqlDataType(kind as u16)
102    }
103
104    pub fn flags(self) -> SqlDataTypeFlags {
105        unsafe { SqlDataTypeFlags::from_bits_unchecked(self.0) }
106    }
107
108    pub fn kind(self) -> SqlDataTypeKind {
109        match self.0 & DT_TYPES {
110            DT_FIXCHAR => SqlDataTypeKind::FixedChar,
111            DT_VARCHAR => SqlDataTypeKind::VarChar,
112            DT_LONGVARCHAR => SqlDataTypeKind::LongVarChar,
113            DT_NFIXCHAR => SqlDataTypeKind::NFixedChar,
114            DT_NVARCHAR => SqlDataTypeKind::NVarChar,
115            DT_LONGNVARCHAR => SqlDataTypeKind::LongNVarChar,
116            DT_BINARY => SqlDataTypeKind::Binary,
117            DT_LONGBINARY => SqlDataTypeKind::LongBinary,
118            DT_TINYINT => SqlDataTypeKind::U8,
119            DT_SMALLINT => SqlDataTypeKind::I16,
120            DT_UNSSMALLINT => SqlDataTypeKind::I16,
121            DT_INT => SqlDataTypeKind::I32,
122            DT_UNSINT => SqlDataTypeKind::U32,
123            DT_BIGINT => SqlDataTypeKind::I64,
124            DT_UNSBIGINT => SqlDataTypeKind::U64,
125            DT_FLOAT => SqlDataTypeKind::F32,
126            DT_DOUBLE => SqlDataTypeKind::F64,
127            DT_NOTYPE | _ => SqlDataTypeKind::Unknown,
128        }
129    }
130}
131
132pub type RawSqlFnArguments = c_void;
133
134#[repr(C)]
135#[derive(Copy, Clone, Debug)]
136pub struct RawExtFnValue {
137    data: *mut c_void,
138    piece_len: a_sql_uint32,
139    len: a_sql_uint32,
140    data_type: SqlDataType,
141}
142
143impl<'a> TryFrom<RawExtFnValue> for &'a str {
144    type Error = Error;
145
146    fn try_from(value: RawExtFnValue) -> Result<&'a str, Self::Error> {
147        use SqlDataTypeKind::*;
148        match value.data_type.kind() {
149            FixedChar | VarChar | LongVarChar => {
150                let c_str = unsafe { CStr::from_ptr(value.data as *mut std::os::raw::c_char) };
151                Ok(c_str.to_str()?)
152            }
153            NFixedChar | NVarChar | LongNVarChar => {
154                let data = value.data as *const u8;
155                unsafe {
156                    let slice = std::slice::from_raw_parts(data, value.piece_len as usize);
157                    Ok(std::str::from_utf8_unchecked(slice))
158                }
159            }
160            k @ _ => Err(Error::InvalidSqlDataType(k)),
161        }
162    }
163}
164
165impl<'a> TryFrom<RawExtFnValue> for &'a [u8] {
166    type Error = Error;
167
168    fn try_from(value: RawExtFnValue) -> Result<&'a [u8], Self::Error> {
169        if value.data.is_null() {
170            return Err(Error::NullValue);
171        } else {
172            unsafe {
173                let slice =
174                    std::slice::from_raw_parts(value.data as *const u8, value.piece_len as usize);
175                Ok(slice)
176            }
177        }
178    }
179}
180
181macro_rules! impl_scatted_arg_getter {
182    ($ident: ident, $type: ty, $to_cursor: ident, $to_opt_cursor: ident) => {
183        pub struct $ident<'a> {
184            value: ExtFnValue,
185            current: Option<&'a $type>,
186        }
187
188        impl<'a> ExtFnValueCursor for $ident<'a> {
189            type Item = $type;
190
191            fn get(&self) -> Option<&Self::Item> {
192                self.current
193            }
194
195            fn advance(&mut self) -> Result<(), Error> {
196                self.current = match self.value {
197                    ExtFnValue::Null(_) => None,
198                    ExtFnValue::Inline(value) => {
199                        let next = value.try_into()?;
200                        self.value = ExtFnValue::Null(value.data_type);
201                        Some(next)
202                    }
203                    ExtFnValue::Multipart {
204                        idx,
205                        raw_value,
206                        total_len,
207                        offset,
208                        api,
209                    } => {
210                        let next = raw_value.try_into()?;
211
212                        if offset < total_len {
213                            let offset = offset + raw_value.len;
214                            let raw_value = api.next_part(idx, offset)?;
215                            self.value = ExtFnValue::Multipart {
216                                idx: idx,
217                                raw_value,
218                                total_len: total_len,
219                                offset,
220                                api: api,
221                            };
222                        } else {
223                            self.value = ExtFnValue::Null(raw_value.data_type);
224                        }
225
226                        Some(next)
227                    }
228                };
229
230                Ok(())
231            }
232
233            fn is_null(&self) -> bool {
234                self.value.is_null()
235            }
236
237            fn kind(&self) -> SqlDataTypeKind {
238                self.value.kind()
239            }
240
241            fn data_type(&self) -> SqlDataType {
242                self.value.data_type()
243            }
244
245            fn len(&self) -> usize {
246                self.value.len()
247            }
248        }
249
250        impl ExtFnValue {
251            /// Return a value cursor for $type
252            pub fn $to_cursor<'a>(self) -> impl ExtFnValueCursor<Item = $type> {
253                $ident {
254                    value: self,
255                    current: None,
256                }
257            }
258
259            /// Return a potentially nullable value cursor for $type
260            pub fn $to_opt_cursor<'a>(self) -> Option<impl ExtFnValueCursor<Item = $type>> {
261                if self.is_null() {
262                    None
263                } else {
264                    Some($ident {
265                        value: self,
266                        current: None,
267                    })
268                }
269            }
270        }
271    };
272}
273
274impl_scatted_arg_getter!(ExtFnByteValues, [u8], to_byte_cursor, to_opt_byte_cursor);
275impl_scatted_arg_getter!(ExtFnStrValues, str, to_str_cursor, to_opt_str_cursor);
276
277#[derive(Debug)]
278pub enum ExtFnValue {
279    Null(SqlDataType),
280    Inline(RawExtFnValue),
281    Multipart {
282        idx: u32,
283        raw_value: RawExtFnValue,
284        total_len: u32,
285        offset: u32,
286        api: ExtFnApi,
287    },
288}
289
290#[derive(Debug, Clone, Error)]
291pub enum Error {
292    #[error("The value was `NULL` where it was not expected to be")]
293    NullValue,
294    #[error("Converting `{0:?}` is impossible to this rust type")]
295    InvalidSqlDataType(SqlDataTypeKind),
296    #[error("The string is not valid in rust `{0}`")]
297    UndecodableString(#[from] std::str::Utf8Error),
298    #[error("The requested argument was not presented by SQLAnywhere")]
299    InvalidArgumentIndex,
300    #[error("The given value cannot be set")]
301    SetValue,
302    #[error("An unknown logic error occured")]
303    Unknown,
304    #[error("The value size is too small for the type")]
305    InvalidPieceLen,
306    #[error("The size of the value being returned is greater than 4gb")]
307    ReturnSizeTooLarge,
308    #[error("Attempt to use a cursor that has not had `advance` called on it")]
309    InvalidArgCursor,
310}
311
312macro_rules! ensure {
313    ($expr: expr, $err: expr) => {
314        if !$expr {
315            return Err($err);
316        }
317    };
318}
319
320// This exists to satisfy type logic, it cant happen as is thus `ud2`
321impl From<Infallible> for Error {
322    fn from(_value: Infallible) -> Self {
323        unreachable!()
324    }
325}
326
327impl ExtFnValue {
328    fn new(raw_value: RawExtFnValue, idx: u32, api: ExtFnApi) -> Self {
329        if raw_value.len != raw_value.piece_len {
330            let total_len = raw_value.len;
331            Self::Multipart {
332                idx,
333                raw_value,
334                total_len,
335                offset: 0,
336                api,
337            }
338        } else if raw_value.data.is_null() {
339            Self::Null(raw_value.data_type)
340        } else {
341            Self::Inline(raw_value)
342        }
343    }
344
345    pub fn is_null(&self) -> bool {
346        match self {
347            Self::Null(_) => true,
348            _ => false,
349        }
350    }
351
352    pub fn kind(&self) -> SqlDataTypeKind {
353        self.data_type().kind()
354    }
355
356    pub fn data_type(&self) -> SqlDataType {
357        match self {
358            Self::Null(dt) => *dt,
359            Self::Inline(raw_value) => raw_value.data_type,
360            Self::Multipart { raw_value, .. } => raw_value.data_type,
361        }
362    }
363
364    pub fn len(&self) -> usize {
365        match self {
366            Self::Null(dt) => 0,
367            Self::Inline(raw_value) => raw_value.len as usize,
368            Self::Multipart { total_len, .. } => *total_len as usize,
369        }
370    }
371}
372
373macro_rules! basic_type_impl {
374    ($type: ty, $type_name: ident, $type_name_opt: ident, $sql_type_kind: expr, $sql_type_pat: pat) => {
375        impl From<$type> for RawExtFnValue {
376            fn from(value: $type) -> Self {
377                RawExtFnValue {
378                    data: value as *const $type as *mut c_void,
379                    piece_len: std::mem::size_of::<$type>() as a_sql_uint32,
380                    len: std::mem::size_of::<$type>() as a_sql_uint32,
381                    data_type: SqlDataType::new($sql_type_kind),
382                }
383            }
384        }
385
386        impl TryFrom<ExtFnValue> for $type {
387            type Error = Error;
388
389            fn try_from(value: ExtFnValue) -> Result<$type, Self::Error> {
390                match value {
391                    ExtFnValue::Null(_) => Err(Error::NullValue),
392                    ExtFnValue::Inline(raw_value) => match raw_value.data_type.kind() {
393                        $sql_type_pat => unsafe { Ok(*(raw_value.data as *const $type)) },
394                        k @ _ => Err(Error::InvalidSqlDataType(k)),
395                    },
396                    ExtFnValue::Multipart { .. } => Err(Error::InvalidPieceLen),
397                }
398            }
399        }
400
401        impl TryFrom<ExtFnValue> for Option<$type> {
402            type Error = Error;
403
404            fn try_from(value: ExtFnValue) -> Result<Option<$type>, Self::Error> {
405                if value.is_null() {
406                    Ok(None)
407                } else {
408                    Self::try_from(value)
409                }
410            }
411        }
412
413        impl ExtFnValue {
414            pub fn $type_name(self) -> Result<$type, Error> {
415                self.try_into()
416            }
417
418            pub fn $type_name_opt(self) -> Result<Option<$type>, Error> {
419                self.try_into()
420            }
421        }
422    };
423}
424
425basic_type_impl!(
426    u8,
427    to_u8,
428    to_opt_u8,
429    SqlDataTypeKind::U8,
430    SqlDataTypeKind::U8
431);
432basic_type_impl!(
433    u16,
434    to_u16,
435    to_opt_u16,
436    SqlDataTypeKind::U16,
437    SqlDataTypeKind::U16
438);
439basic_type_impl!(
440    i16,
441    to_i16,
442    to_opt_i16,
443    SqlDataTypeKind::I16,
444    SqlDataTypeKind::I16
445);
446basic_type_impl!(
447    u32,
448    to_u32,
449    to_opt_u32,
450    SqlDataTypeKind::U32,
451    SqlDataTypeKind::U32
452);
453basic_type_impl!(
454    i32,
455    to_i32,
456    to_opt_i32,
457    SqlDataTypeKind::I32,
458    SqlDataTypeKind::I32
459);
460basic_type_impl!(
461    u64,
462    to_u64,
463    to_opt_u64,
464    SqlDataTypeKind::U64,
465    SqlDataTypeKind::U64
466);
467basic_type_impl!(
468    i64,
469    to_i64,
470    to_opt_i64,
471    SqlDataTypeKind::I64,
472    SqlDataTypeKind::I64
473);
474//basic_type_impl!(f32, SqlDataTypeKind::F32);
475//basic_type_impl!(f64, SqlDataTypeKind::F64);
476
477impl Into<RawExtFnValue> for &str {
478    fn into(self) -> RawExtFnValue {
479        unsafe {
480            let c = CString::new(self.to_owned()).unwrap_or_default();
481            let p = c.as_ptr();
482            mem::forget(c);
483            RawExtFnValue {
484                data: p as *mut c_void,
485                piece_len: self.len() as a_sql_uint32,
486                len: self.len() as a_sql_uint32,
487                data_type: SqlDataType::new(SqlDataTypeKind::LongNVarChar),
488            }
489        }
490    }
491}
492
493impl Into<RawExtFnValue> for String {
494    fn into(self) -> RawExtFnValue {
495        self.as_str().into()
496    }
497}
498
499impl Default for RawExtFnValue {
500    fn default() -> Self {
501        Self {
502            data: std::ptr::null_mut(),
503            piece_len: 0,
504            len: 0,
505            data_type: SqlDataType(0),
506        }
507    }
508}
509
510#[derive(Copy, Clone, Debug)]
511pub struct ExtFnApi {
512    extapi: *mut RawExtApi,
513    arg_handle: *mut RawSqlFnArguments,
514}
515
516impl ExtFnApi {
517    pub fn from_raw(extapi: *mut RawExtApi, arg_handle: *mut RawSqlFnArguments) -> Self {
518        Self { extapi, arg_handle }
519    }
520
521    pub fn get_connection(&self) -> Result<*mut c_void, Error> {
522        let mut value = RawExtFnValue::default();
523        let ret = unsafe {
524            ((*self.extapi).get_value)(self.arg_handle, EXTFN_CONNECTION_HANDLE_ARG_NUM, &mut value)
525        };
526
527        match ret {
528            0 => Err(Error::InvalidArgumentIndex),
529            _ => Ok(value.data),
530        }
531    }
532
533    pub fn arg(&self, idx: u32) -> Result<ExtFnValue, Error> {
534        self.raw_arg(idx)
535            .map(|raw_value| ExtFnValue::new(raw_value, idx, self.clone()))
536    }
537
538    pub fn return_value<E, R>(&self, idx: u32, to_return: R) -> Result<(), Error>
539        where
540            E: Into<Error>,
541            R: TryInto<RawExtFnValue, Error=E>,
542    {
543        let mut retval = to_return.try_into().map_err(|e| e.into())?;
544        unsafe {
545            match ((*self.extapi).set_value)(self.arg_handle, idx, &mut retval, 0) {
546                0 => Err(Error::SetValue),
547                _ => Ok(()),
548            }
549        }
550    }
551
552    fn raw_arg(&self, idx: u32) -> Result<RawExtFnValue, Error> {
553        let mut value = RawExtFnValue::default();
554        let ret = unsafe { ((*self.extapi).get_value)(self.arg_handle, idx + 1, &mut value) };
555
556        match ret {
557            0 => Err(Error::InvalidArgumentIndex),
558            _ => Ok(value),
559        }
560    }
561
562    fn next_part(&self, idx: u32, offset: u32) -> Result<RawExtFnValue, Error> {
563        let mut value = RawExtFnValue::default();
564        let ret =
565            unsafe { ((*self.extapi).get_piece)(self.arg_handle, idx + 1, &mut value, offset) };
566
567        match ret {
568            0 => Err(Error::InvalidArgumentIndex),
569            _ => Ok(value),
570        }
571    }
572}
573
574#[repr(C)]
575#[derive(Copy, Clone)]
576pub struct RawExtApi {
577    get_value: unsafe extern "system" fn(
578        arg_handle: *mut RawSqlFnArguments,
579        arg_num: a_sql_uint32,
580        value: *mut RawExtFnValue,
581    ) -> c_short,
582    get_piece: unsafe extern "system" fn(
583        arg_handle: *mut RawSqlFnArguments,
584        arg_num: a_sql_uint32,
585        value: *mut RawExtFnValue,
586        offset: a_sql_uint32,
587    ) -> c_short,
588    set_value: unsafe extern "system" fn(
589        arg_handle: *mut RawSqlFnArguments,
590        arg_num: a_sql_uint32,
591        value: *mut RawExtFnValue,
592        append: c_short,
593    ) -> c_short,
594    set_cancel:
595    unsafe extern "system" fn(arg_handle: *mut RawSqlFnArguments, cancel_handle: *mut c_void),
596}