use crate::{schema::db, stmt};
#[derive(Debug)]
pub struct Capability {
pub sql: bool,
pub storage_types: StorageTypes,
pub schema_mutations: SchemaMutations,
pub cte_with_update: bool,
pub select_for_update: bool,
pub returning_from_mutation: bool,
pub primary_key_ne_predicate: bool,
pub auto_increment: bool,
pub native_varchar: bool,
pub native_timestamp: bool,
pub native_date: bool,
pub native_time: bool,
pub native_datetime: bool,
pub native_decimal: bool,
pub bigdecimal_implemented: bool,
pub decimal_arbitrary_precision: bool,
pub index_or_predicate: bool,
pub test_connection_pool: bool,
}
#[derive(Debug)]
pub struct StorageTypes {
pub default_string_type: db::Type,
pub varchar: Option<u64>,
pub default_uuid_type: db::Type,
pub default_bytes_type: db::Type,
pub default_decimal_type: db::Type,
pub default_bigdecimal_type: db::Type,
pub default_timestamp_type: db::Type,
pub default_zoned_type: db::Type,
pub default_date_type: db::Type,
pub default_time_type: db::Type,
pub default_datetime_type: db::Type,
pub max_unsigned_integer: Option<u64>,
}
#[derive(Debug)]
pub struct SchemaMutations {
pub alter_column_type: bool,
pub alter_column_properties_atomic: bool,
}
impl Capability {
pub fn validate(&self) -> crate::Result<()> {
if self.native_varchar && self.storage_types.varchar.is_none() {
return Err(crate::Error::invalid_driver_configuration(
"native_varchar is true but storage_types.varchar is None",
));
}
if !self.native_varchar && self.storage_types.varchar.is_some() {
return Err(crate::Error::invalid_driver_configuration(
"native_varchar is false but storage_types.varchar is Some",
));
}
Ok(())
}
pub fn default_string_max_length(&self) -> Option<u64> {
match &self.storage_types.default_string_type {
db::Type::VarChar(len) => Some(*len),
_ => None, }
}
pub fn native_type_for(&self, ty: &stmt::Type) -> stmt::Type {
match ty {
stmt::Type::Uuid => self.storage_types.default_uuid_type.bridge_type(ty),
#[cfg(feature = "jiff")]
stmt::Type::Timestamp => self.storage_types.default_timestamp_type.bridge_type(ty),
#[cfg(feature = "jiff")]
stmt::Type::Zoned => self.storage_types.default_zoned_type.bridge_type(ty),
#[cfg(feature = "jiff")]
stmt::Type::Date => self.storage_types.default_date_type.bridge_type(ty),
#[cfg(feature = "jiff")]
stmt::Type::Time => self.storage_types.default_time_type.bridge_type(ty),
#[cfg(feature = "jiff")]
stmt::Type::DateTime => self.storage_types.default_datetime_type.bridge_type(ty),
_ => ty.clone(),
}
}
pub const SQLITE: Self = Self {
sql: true,
storage_types: StorageTypes::SQLITE,
schema_mutations: SchemaMutations::SQLITE,
cte_with_update: false,
select_for_update: false,
returning_from_mutation: true,
primary_key_ne_predicate: true,
auto_increment: true,
bigdecimal_implemented: false,
native_varchar: true,
native_timestamp: false,
native_date: false,
native_time: false,
native_datetime: false,
native_decimal: false,
decimal_arbitrary_precision: false,
index_or_predicate: true,
test_connection_pool: false,
};
pub const POSTGRESQL: Self = Self {
cte_with_update: true,
storage_types: StorageTypes::POSTGRESQL,
schema_mutations: SchemaMutations::POSTGRESQL,
select_for_update: true,
auto_increment: true,
bigdecimal_implemented: false,
native_timestamp: true,
native_date: true,
native_time: true,
native_datetime: true,
native_decimal: true,
decimal_arbitrary_precision: true,
test_connection_pool: true,
..Self::SQLITE
};
pub const MYSQL: Self = Self {
cte_with_update: false,
storage_types: StorageTypes::MYSQL,
schema_mutations: SchemaMutations::MYSQL,
select_for_update: true,
returning_from_mutation: false,
auto_increment: true,
bigdecimal_implemented: true,
native_timestamp: true,
native_date: true,
native_time: true,
native_datetime: true,
native_decimal: true,
decimal_arbitrary_precision: false,
test_connection_pool: true,
..Self::SQLITE
};
pub const DYNAMODB: Self = Self {
sql: false,
storage_types: StorageTypes::DYNAMODB,
schema_mutations: SchemaMutations::DYNAMODB,
cte_with_update: false,
select_for_update: false,
returning_from_mutation: false,
primary_key_ne_predicate: false,
auto_increment: false,
bigdecimal_implemented: false,
native_varchar: false,
native_timestamp: false,
native_date: false,
native_time: false,
native_datetime: false,
native_decimal: false,
decimal_arbitrary_precision: false,
index_or_predicate: false,
test_connection_pool: false,
};
}
impl StorageTypes {
pub const SQLITE: StorageTypes = StorageTypes {
default_string_type: db::Type::Text,
varchar: Some(1_000_000_000),
default_uuid_type: db::Type::Blob,
default_bytes_type: db::Type::Blob,
default_decimal_type: db::Type::Text,
default_bigdecimal_type: db::Type::Text,
default_timestamp_type: db::Type::Text,
default_zoned_type: db::Type::Text,
default_date_type: db::Type::Text,
default_time_type: db::Type::Text,
default_datetime_type: db::Type::Text,
max_unsigned_integer: Some(i64::MAX as u64),
};
pub const POSTGRESQL: StorageTypes = StorageTypes {
default_string_type: db::Type::Text,
varchar: Some(10_485_760),
default_uuid_type: db::Type::Uuid,
default_bytes_type: db::Type::Blob,
default_decimal_type: db::Type::Numeric(None),
default_bigdecimal_type: db::Type::Text,
default_timestamp_type: db::Type::Timestamp(6),
default_zoned_type: db::Type::Text,
default_date_type: db::Type::Date,
default_time_type: db::Type::Time(6),
default_datetime_type: db::Type::DateTime(6),
max_unsigned_integer: Some(i64::MAX as u64),
};
pub const MYSQL: StorageTypes = StorageTypes {
default_string_type: db::Type::VarChar(191),
varchar: Some(65_535),
default_uuid_type: db::Type::VarChar(36),
default_bytes_type: db::Type::Blob,
default_decimal_type: db::Type::Text,
default_bigdecimal_type: db::Type::Text,
default_timestamp_type: db::Type::DateTime(6),
default_zoned_type: db::Type::Text,
default_date_type: db::Type::Date,
default_time_type: db::Type::Time(6),
default_datetime_type: db::Type::DateTime(6),
max_unsigned_integer: None,
};
pub const DYNAMODB: StorageTypes = StorageTypes {
default_string_type: db::Type::Text,
varchar: None,
default_uuid_type: db::Type::Text,
default_bytes_type: db::Type::Blob,
default_decimal_type: db::Type::Text,
default_bigdecimal_type: db::Type::Text,
default_timestamp_type: db::Type::Text,
default_zoned_type: db::Type::Text,
default_date_type: db::Type::Text,
default_time_type: db::Type::Text,
default_datetime_type: db::Type::Text,
max_unsigned_integer: None,
};
}
impl SchemaMutations {
pub const SQLITE: Self = Self {
alter_column_type: false,
alter_column_properties_atomic: false,
};
pub const POSTGRESQL: Self = Self {
alter_column_type: true,
alter_column_properties_atomic: false,
};
pub const MYSQL: Self = Self {
alter_column_type: true,
alter_column_properties_atomic: true,
};
pub const DYNAMODB: Self = Self {
alter_column_type: false,
alter_column_properties_atomic: false,
};
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_validate_sqlite_capability() {
assert!(Capability::SQLITE.validate().is_ok());
}
#[test]
fn test_validate_postgresql_capability() {
assert!(Capability::POSTGRESQL.validate().is_ok());
}
#[test]
fn test_validate_mysql_capability() {
assert!(Capability::MYSQL.validate().is_ok());
}
#[test]
fn test_validate_dynamodb_capability() {
assert!(Capability::DYNAMODB.validate().is_ok());
}
#[test]
fn test_validate_fails_when_native_varchar_true_but_no_varchar() {
let invalid = Capability {
native_varchar: true,
storage_types: StorageTypes {
varchar: None, ..StorageTypes::SQLITE
},
..Capability::SQLITE
};
let result = invalid.validate();
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("native_varchar is true but storage_types.varchar is None")
);
}
#[test]
fn test_validate_fails_when_native_varchar_false_but_has_varchar() {
let invalid = Capability {
native_varchar: false,
storage_types: StorageTypes {
varchar: Some(1000), ..StorageTypes::DYNAMODB
},
..Capability::DYNAMODB
};
let result = invalid.validate();
assert!(result.is_err());
assert!(
result
.unwrap_err()
.to_string()
.contains("native_varchar is false but storage_types.varchar is Some")
);
}
}