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#[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 pub fn $to_cursor<'a>(self) -> impl ExtFnValueCursor<Item = $type> {
253 $ident {
254 value: self,
255 current: None,
256 }
257 }
258
259 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
320impl 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);
474impl 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}