#![allow(non_snake_case, non_camel_case_types, non_upper_case_globals, unused)]
use std::{
borrow::Cow,
convert::{Infallible, TryFrom, TryInto},
ffi::{CStr, CString},
fmt, mem,
os::raw::{c_int, c_long, c_short, c_uint, c_ulong, c_ushort, c_void},
};
mod value_cursor;
use thiserror::Error;
pub use value_cursor::ExtFnValueCursor;
const DT_TYPES: u16 = 1022;
const DT_NOTYPE: u16 = 0;
const DT_SMALLINT: u16 = 500;
const DT_INT: u16 = 496;
const DT_FLOAT: u16 = 482;
const DT_DOUBLE: u16 = 480;
const DT_STRING: u16 = 460;
const DT_FIXCHAR: u16 = 452;
const DT_VARCHAR: u16 = 448;
const DT_LONGVARCHAR: u16 = 456;
const DT_BINARY: u16 = 524;
const DT_LONGBINARY: u16 = 528;
const DT_TINYINT: u16 = 604;
const DT_BIGINT: u16 = 608;
const DT_UNSINT: u16 = 612;
const DT_UNSSMALLINT: u16 = 616;
const DT_UNSBIGINT: u16 = 620;
const DT_BIT: u16 = 624;
const DT_NSTRING: u16 = 628;
const DT_NFIXCHAR: u16 = 632;
const DT_NVARCHAR: u16 = 636;
const DT_LONGNVARCHAR: u16 = 640;
const EXTFN_V0_API: u32 = 0;
const EXTFN_V2_API: u32 = 2;
const EXTFN_V3_API: u32 = 3;
const EXTFN_V4_API: u32 = 4;
pub const EXTFN_API_VERSION: u32 = 2;
const EXTFN_CONNECTION_HANDLE_ARG_NUM: a_sql_uint32 = !0;
const EXTFN_RESULT_SET_ARG_NUM: a_sql_uint32 = !0;
pub type a_sql_int32 = c_int;
pub type a_sql_uint32 = c_uint;
pub type a_sql_int64 = c_long;
pub type a_sql_uint64 = c_ulong;
type a_sql_data_type = c_ushort;
#[repr(u16)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum SqlDataTypeKind {
Unknown = DT_NOTYPE,
FixedChar = DT_FIXCHAR,
VarChar = DT_VARCHAR,
LongVarChar = DT_LONGVARCHAR,
NFixedChar = DT_NFIXCHAR,
NVarChar = DT_NVARCHAR,
LongNVarChar = DT_LONGNVARCHAR,
Binary = DT_BINARY,
LongBinary = DT_LONGBINARY,
U8 = DT_TINYINT,
I16 = DT_SMALLINT,
U16 = DT_UNSSMALLINT,
I32 = DT_INT,
U32 = DT_UNSINT,
I64 = DT_BIGINT,
U64 = DT_UNSBIGINT,
F32 = DT_FLOAT,
F64 = DT_DOUBLE,
}
#[repr(C)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
pub struct SqlDataType(::std::os::raw::c_ushort);
bitflags::bitflags! {
pub struct SqlDataTypeFlags: u16 {
const NULLS_ALLOWED = 1;
const PROCEDURE_OUT = 32768;
const PROCEDURE_IN = 16384;
const UPDATABLE = 8192;
const DESCRIBE_INPUT = 4096;
const AUTO_INCREMENT = 2048;
const KEY_COLUMN = 1024;
const HIDDEN_COLUMN = 512;
const HAS_USERTYPE_INFO = 256;
}
}
impl SqlDataType {
pub fn new(kind: SqlDataTypeKind) -> Self {
SqlDataType(kind as u16)
}
pub fn flags(self) -> SqlDataTypeFlags {
unsafe { SqlDataTypeFlags::from_bits_unchecked(self.0) }
}
pub fn kind(self) -> SqlDataTypeKind {
match self.0 & DT_TYPES {
DT_FIXCHAR => SqlDataTypeKind::FixedChar,
DT_VARCHAR => SqlDataTypeKind::VarChar,
DT_LONGVARCHAR => SqlDataTypeKind::LongVarChar,
DT_NFIXCHAR => SqlDataTypeKind::NFixedChar,
DT_NVARCHAR => SqlDataTypeKind::NVarChar,
DT_LONGNVARCHAR => SqlDataTypeKind::LongNVarChar,
DT_BINARY => SqlDataTypeKind::Binary,
DT_LONGBINARY => SqlDataTypeKind::LongBinary,
DT_TINYINT => SqlDataTypeKind::U8,
DT_SMALLINT => SqlDataTypeKind::I16,
DT_UNSSMALLINT => SqlDataTypeKind::I16,
DT_INT => SqlDataTypeKind::I32,
DT_UNSINT => SqlDataTypeKind::U32,
DT_BIGINT => SqlDataTypeKind::I64,
DT_UNSBIGINT => SqlDataTypeKind::U64,
DT_FLOAT => SqlDataTypeKind::F32,
DT_DOUBLE => SqlDataTypeKind::F64,
DT_NOTYPE | _ => SqlDataTypeKind::Unknown,
}
}
}
pub type RawSqlFnArguments = c_void;
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct RawExtFnValue {
data: *mut c_void,
piece_len: a_sql_uint32,
len: a_sql_uint32,
data_type: SqlDataType,
}
impl<'a> TryFrom<RawExtFnValue> for &'a str {
type Error = Error;
fn try_from(value: RawExtFnValue) -> Result<&'a str, Self::Error> {
use SqlDataTypeKind::*;
match value.data_type.kind() {
FixedChar | VarChar | LongVarChar => {
let c_str = unsafe { CStr::from_ptr(value.data as *mut std::os::raw::c_char) };
Ok(c_str.to_str()?)
}
NFixedChar | NVarChar | LongNVarChar => {
let data = value.data as *const u8;
unsafe {
let slice = std::slice::from_raw_parts(data, value.piece_len as usize);
Ok(std::str::from_utf8_unchecked(slice))
}
}
k @ _ => Err(Error::InvalidSqlDataType(k)),
}
}
}
impl<'a> TryFrom<RawExtFnValue> for &'a [u8] {
type Error = Error;
fn try_from(value: RawExtFnValue) -> Result<&'a [u8], Self::Error> {
if value.data.is_null() {
return Err(Error::NullValue);
} else {
unsafe {
let slice =
std::slice::from_raw_parts(value.data as *const u8, value.piece_len as usize);
Ok(slice)
}
}
}
}
macro_rules! impl_scatted_arg_getter {
($ident: ident, $type: ty, $to_cursor: ident, $to_opt_cursor: ident) => {
pub struct $ident<'a> {
value: ExtFnValue,
current: Option<&'a $type>,
}
impl<'a> ExtFnValueCursor for $ident<'a> {
type Item = $type;
fn get(&self) -> Option<&Self::Item> {
self.current
}
fn advance(&mut self) -> Result<(), Error> {
self.current = match self.value {
ExtFnValue::Null(_) => None,
ExtFnValue::Inline(value) => {
let next = value.try_into()?;
self.value = ExtFnValue::Null(value.data_type);
Some(next)
}
ExtFnValue::Multipart {
idx,
raw_value,
total_len,
offset,
api,
} => {
let next = raw_value.try_into()?;
if offset < total_len {
let offset = offset + raw_value.len;
let raw_value = api.next_part(idx, offset)?;
self.value = ExtFnValue::Multipart {
idx: idx,
raw_value,
total_len: total_len,
offset,
api: api,
};
} else {
self.value = ExtFnValue::Null(raw_value.data_type);
}
Some(next)
}
};
Ok(())
}
fn is_null(&self) -> bool {
self.value.is_null()
}
fn kind(&self) -> SqlDataTypeKind {
self.value.kind()
}
fn data_type(&self) -> SqlDataType {
self.value.data_type()
}
fn len(&self) -> usize {
self.value.len()
}
}
impl ExtFnValue {
pub fn $to_cursor<'a>(self) -> impl ExtFnValueCursor<Item = $type> {
$ident {
value: self,
current: None,
}
}
pub fn $to_opt_cursor<'a>(self) -> Option<impl ExtFnValueCursor<Item = $type>> {
if self.is_null() {
None
} else {
Some($ident {
value: self,
current: None,
})
}
}
}
};
}
impl_scatted_arg_getter!(ExtFnByteValues, [u8], to_byte_cursor, to_opt_byte_cursor);
impl_scatted_arg_getter!(ExtFnStrValues, str, to_str_cursor, to_opt_str_cursor);
#[derive(Debug)]
pub enum ExtFnValue {
Null(SqlDataType),
Inline(RawExtFnValue),
Multipart {
idx: u32,
raw_value: RawExtFnValue,
total_len: u32,
offset: u32,
api: ExtFnApi,
},
}
#[derive(Debug, Clone, Error)]
pub enum Error {
#[error("The value was `NULL` where it was not expected to be")]
NullValue,
#[error("Converting `{0:?}` is impossible to this rust type")]
InvalidSqlDataType(SqlDataTypeKind),
#[error("The string is not valid in rust `{0}`")]
UndecodableString(#[from] std::str::Utf8Error),
#[error("The requested argument was not presented by SQLAnywhere")]
InvalidArgumentIndex,
#[error("The given value cannot be set")]
SetValue,
#[error("An unknown logic error occured")]
Unknown,
#[error("The value size is too small for the type")]
InvalidPieceLen,
#[error("The size of the value being returned is greater than 4gb")]
ReturnSizeTooLarge,
#[error("Attempt to use a cursor that has not had `advance` called on it")]
InvalidArgCursor,
}
macro_rules! ensure {
($expr: expr, $err: expr) => {
if !$expr {
return Err($err);
}
};
}
impl From<Infallible> for Error {
fn from(_value: Infallible) -> Self {
unreachable!()
}
}
impl ExtFnValue {
fn new(raw_value: RawExtFnValue, idx: u32, api: ExtFnApi) -> Self {
if raw_value.len != raw_value.piece_len {
let total_len = raw_value.len;
Self::Multipart {
idx,
raw_value,
total_len,
offset: 0,
api,
}
} else if raw_value.data.is_null() {
Self::Null(raw_value.data_type)
} else {
Self::Inline(raw_value)
}
}
pub fn is_null(&self) -> bool {
match self {
Self::Null(_) => true,
_ => false,
}
}
pub fn kind(&self) -> SqlDataTypeKind {
self.data_type().kind()
}
pub fn data_type(&self) -> SqlDataType {
match self {
Self::Null(dt) => *dt,
Self::Inline(raw_value) => raw_value.data_type,
Self::Multipart { raw_value, .. } => raw_value.data_type,
}
}
pub fn len(&self) -> usize {
match self {
Self::Null(dt) => 0,
Self::Inline(raw_value) => raw_value.len as usize,
Self::Multipart { total_len, .. } => *total_len as usize,
}
}
}
macro_rules! basic_type_impl {
($type: ty, $type_name: ident, $type_name_opt: ident, $sql_type_kind: expr, $sql_type_pat: pat) => {
impl From<$type> for RawExtFnValue {
fn from(value: $type) -> Self {
RawExtFnValue {
data: value as *const $type as *mut c_void,
piece_len: std::mem::size_of::<$type>() as a_sql_uint32,
len: std::mem::size_of::<$type>() as a_sql_uint32,
data_type: SqlDataType::new($sql_type_kind),
}
}
}
impl TryFrom<ExtFnValue> for $type {
type Error = Error;
fn try_from(value: ExtFnValue) -> Result<$type, Self::Error> {
match value {
ExtFnValue::Null(_) => Err(Error::NullValue),
ExtFnValue::Inline(raw_value) => match raw_value.data_type.kind() {
$sql_type_pat => unsafe { Ok(*(raw_value.data as *const $type)) },
k @ _ => Err(Error::InvalidSqlDataType(k)),
},
ExtFnValue::Multipart { .. } => Err(Error::InvalidPieceLen),
}
}
}
impl TryFrom<ExtFnValue> for Option<$type> {
type Error = Error;
fn try_from(value: ExtFnValue) -> Result<Option<$type>, Self::Error> {
if value.is_null() {
Ok(None)
} else {
Self::try_from(value)
}
}
}
impl ExtFnValue {
pub fn $type_name(self) -> Result<$type, Error> {
self.try_into()
}
pub fn $type_name_opt(self) -> Result<Option<$type>, Error> {
self.try_into()
}
}
};
}
basic_type_impl!(
u8,
to_u8,
to_opt_u8,
SqlDataTypeKind::U8,
SqlDataTypeKind::U8
);
basic_type_impl!(
u16,
to_u16,
to_opt_u16,
SqlDataTypeKind::U16,
SqlDataTypeKind::U16
);
basic_type_impl!(
i16,
to_i16,
to_opt_i16,
SqlDataTypeKind::I16,
SqlDataTypeKind::I16
);
basic_type_impl!(
u32,
to_u32,
to_opt_u32,
SqlDataTypeKind::U32,
SqlDataTypeKind::U32
);
basic_type_impl!(
i32,
to_i32,
to_opt_i32,
SqlDataTypeKind::I32,
SqlDataTypeKind::I32
);
basic_type_impl!(
u64,
to_u64,
to_opt_u64,
SqlDataTypeKind::U64,
SqlDataTypeKind::U64
);
basic_type_impl!(
i64,
to_i64,
to_opt_i64,
SqlDataTypeKind::I64,
SqlDataTypeKind::I64
);
impl Into<RawExtFnValue> for &str {
fn into(self) -> RawExtFnValue {
unsafe {
let c = CString::new(self.to_owned()).unwrap_or_default();
let p = c.as_ptr();
mem::forget(c);
RawExtFnValue {
data: p as *mut c_void,
piece_len: self.len() as a_sql_uint32,
len: self.len() as a_sql_uint32,
data_type: SqlDataType::new(SqlDataTypeKind::LongNVarChar),
}
}
}
}
impl Into<RawExtFnValue> for String {
fn into(self) -> RawExtFnValue {
self.as_str().into()
}
}
impl Default for RawExtFnValue {
fn default() -> Self {
Self {
data: std::ptr::null_mut(),
piece_len: 0,
len: 0,
data_type: SqlDataType(0),
}
}
}
#[derive(Copy, Clone, Debug)]
pub struct ExtFnApi {
extapi: *mut RawExtApi,
arg_handle: *mut RawSqlFnArguments,
}
impl ExtFnApi {
pub fn from_raw(extapi: *mut RawExtApi, arg_handle: *mut RawSqlFnArguments) -> Self {
Self { extapi, arg_handle }
}
pub fn get_connection(&self) -> Result<*mut c_void, Error> {
let mut value = RawExtFnValue::default();
let ret = unsafe {
((*self.extapi).get_value)(self.arg_handle, EXTFN_CONNECTION_HANDLE_ARG_NUM, &mut value)
};
match ret {
0 => Err(Error::InvalidArgumentIndex),
_ => Ok(value.data),
}
}
pub fn arg(&self, idx: u32) -> Result<ExtFnValue, Error> {
self.raw_arg(idx)
.map(|raw_value| ExtFnValue::new(raw_value, idx, self.clone()))
}
pub fn return_value<E, R>(&self, idx: u32, to_return: R) -> Result<(), Error>
where
E: Into<Error>,
R: TryInto<RawExtFnValue, Error=E>,
{
let mut retval = to_return.try_into().map_err(|e| e.into())?;
unsafe {
match ((*self.extapi).set_value)(self.arg_handle, idx, &mut retval, 0) {
0 => Err(Error::SetValue),
_ => Ok(()),
}
}
}
fn raw_arg(&self, idx: u32) -> Result<RawExtFnValue, Error> {
let mut value = RawExtFnValue::default();
let ret = unsafe { ((*self.extapi).get_value)(self.arg_handle, idx + 1, &mut value) };
match ret {
0 => Err(Error::InvalidArgumentIndex),
_ => Ok(value),
}
}
fn next_part(&self, idx: u32, offset: u32) -> Result<RawExtFnValue, Error> {
let mut value = RawExtFnValue::default();
let ret =
unsafe { ((*self.extapi).get_piece)(self.arg_handle, idx + 1, &mut value, offset) };
match ret {
0 => Err(Error::InvalidArgumentIndex),
_ => Ok(value),
}
}
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct RawExtApi {
get_value: unsafe extern "system" fn(
arg_handle: *mut RawSqlFnArguments,
arg_num: a_sql_uint32,
value: *mut RawExtFnValue,
) -> c_short,
get_piece: unsafe extern "system" fn(
arg_handle: *mut RawSqlFnArguments,
arg_num: a_sql_uint32,
value: *mut RawExtFnValue,
offset: a_sql_uint32,
) -> c_short,
set_value: unsafe extern "system" fn(
arg_handle: *mut RawSqlFnArguments,
arg_num: a_sql_uint32,
value: *mut RawExtFnValue,
append: c_short,
) -> c_short,
set_cancel:
unsafe extern "system" fn(arg_handle: *mut RawSqlFnArguments, cancel_handle: *mut c_void),
}