#![warn(missing_docs)]
#[cfg(test)]
extern crate self as sz_orm_core;
use std::sync::Arc;
pub use async_trait::async_trait;
pub use bytes::Bytes;
pub use chrono::{DateTime, Utc};
pub use serde::{Deserialize, Serialize};
pub mod access_control;
pub mod accessors;
pub mod active_model;
#[allow(missing_docs)]
pub mod api_coverage;
pub mod behaviors;
#[cfg(feature = "benchmark-suite")]
pub mod benchmark;
pub mod bloom;
mod cache;
pub mod change_tracker;
pub mod circuit_breaker;
#[cfg(feature = "circuit-breaker")]
#[allow(missing_docs)]
pub mod degradation;
#[allow(missing_docs)]
pub mod binding_coverage;
#[cfg(feature = "type-safe-columns")]
pub mod column;
#[cfg(feature = "zero-copy")]
pub mod columnar;
#[cfg(any(
feature = "prepared-stmt-cache",
feature = "async-row-stream",
feature = "parallel-batch"
))]
pub mod connection_ext;
pub mod cursor_stream;
pub mod cycle_detection;
pub mod data_permission;
mod db_type;
pub mod dialect;
#[cfg(feature = "prod-dialect-security")]
pub mod dialect_security;
pub mod dirty_attributes;
#[cfg(feature = "dist-cache")]
pub mod dist_cache;
#[cfg(feature = "dist-cache-cluster")]
#[allow(missing_docs)]
pub mod dist_cache_cluster;
pub mod dynamic_filter;
pub mod dynamic_sql;
pub mod eager_loader;
pub mod entity_graph;
mod error;
pub mod find_with_related;
#[cfg(feature = "compile-governance")]
pub mod governance;
pub mod guard;
pub mod hooks;
#[cfg(feature = "composable-plugin")]
pub use hooks::{ExtensionHandler, ExtensionPoint, ExtensionPointRegistry};
pub mod hydration_plugin;
pub mod i18n;
pub mod join_dsl;
pub mod json_query;
#[cfg(feature = "l1-cache")]
pub mod l1_cache;
pub mod l2_cache;
pub mod lambda;
pub mod lazy_loader;
pub mod linq;
pub mod migration;
#[cfg(feature = "migration-dry-run")]
pub mod migration_dry_run;
pub mod mock;
mod model;
#[cfg(feature = "multi-tenant-pool")]
#[allow(missing_docs)]
pub mod multi_tenant_pool;
pub mod n1_eliminator;
pub mod nested_active_model;
pub mod observer;
pub mod optimistic_lock;
pub mod paginator;
pub mod partial_model;
pub mod phinx_migration;
#[cfg(feature = "plan-cache")]
pub mod plan_cache;
pub mod plugin;
#[cfg(feature = "composable-plugin")]
pub use plugin::{MiddlewareChain, PanicSafeRegistry, PluginSigner, PluginState, SignatureStatus};
mod pool;
#[cfg(feature = "prepared-stmt-cache")]
pub mod prepared_cache;
#[cfg(feature = "auto-prewarm")]
pub mod prewarm;
#[cfg(feature = "prod-ready")]
pub mod prod_ready_check;
mod query;
pub mod query_cache;
#[cfg(feature = "query-result-cache")]
#[allow(missing_docs)]
pub mod query_result_cache;
#[cfg(feature = "async-row-stream")]
pub mod row_stream;
#[cfg(feature = "rw-split-enhanced")]
#[allow(missing_docs)]
pub mod rw_split_enhanced;
#[cfg(feature = "saga-tx")]
#[allow(missing_docs)]
pub mod saga;
#[cfg(feature = "pool-elastic")]
#[allow(missing_docs)]
pub mod pool_elastic;
#[cfg(feature = "serverless-adapt")]
pub use pool_elastic::{
CdcCheckpoint, GracefulShutdown, GracefulShutdownConfig, ShutdownError, ShutdownResult,
};
#[cfg(feature = "serverless-adapt")]
pub use prewarm::{ColdStartOptimizer, ColdStartStats};
#[cfg(feature = "io-uring")]
#[allow(missing_docs)]
pub mod io_uring_probe;
#[cfg(feature = "io-uring")]
#[allow(missing_docs)]
pub mod io_uring_io;
#[cfg(feature = "field-encryption")]
#[allow(missing_docs)]
pub mod field_cipher;
#[cfg(feature = "tde-interceptor")]
pub use field_cipher::{TdeError, TdeInterceptor};
#[cfg(feature = "tde-interceptor")]
pub use sz_orm_crypto::{
ColumnCryptoConfig, ColumnEncryptionPolicy, DekBuffer, EncryptionAlgo, KmsClient,
LocalKmsClient,
};
#[cfg(feature = "data-validation")]
pub mod validation;
pub use query::QueryBuilder;
#[cfg(feature = "adaptive-query")]
pub mod adaptive_adapter;
#[cfg(feature = "cache-coherence")]
pub mod cache_coherence;
#[cfg(feature = "l1-cache")]
#[allow(missing_docs)]
pub mod cache_warmup_protection;
#[cfg(feature = "config-center")]
pub mod config_adapter;
#[cfg(feature = "connection-level-tenant")]
pub mod connection_tenant;
#[cfg(feature = "forward-compat-sandbox")]
#[allow(missing_docs)]
pub mod forward_compat_sandbox;
#[cfg(feature = "graph")]
pub mod graph_adapter;
#[cfg(feature = "graphql")]
pub mod graphql_adapter;
#[cfg(feature = "structured-logging")]
pub mod logger_adapter;
#[cfg(feature = "postgis")]
pub mod postgis_adapter;
#[cfg(feature = "read-write-splitting")]
pub mod rw_adapter;
#[cfg(feature = "search")]
pub mod search_adapter;
#[cfg(feature = "timeseries")]
pub mod timeseries_adapter;
#[cfg(feature = "distributed-tracing")]
pub mod tracing_adapter;
#[cfg(feature = "migration-branch")]
pub mod migration_branch;
#[cfg(feature = "l1-cache")]
pub mod process_l1_cache;
#[cfg(feature = "qb-migration-tool")]
pub mod qb_migration_fix;
#[cfg(feature = "qb-migration-tool")]
pub mod qb_migration_lint;
pub mod queryable;
pub mod quick_query;
pub mod rate_limiter;
pub mod relation_trait;
pub mod repository;
pub mod result_map;
pub mod retry;
#[cfg(feature = "zero-downtime-rollback")]
pub mod rollback_zero_downtime;
#[cfg(feature = "schema-diff-viz")]
pub mod schema_diff_viz;
pub mod schema_gen;
pub mod schema_sync;
#[cfg(feature = "data-seeding")]
pub mod seeding;
pub mod select_types;
pub mod shadow;
#[cfg(feature = "simd")]
pub mod simd;
pub mod smart_eager_loader;
pub mod sql_buffer;
pub mod sql_safety;
#[cfg(feature = "sql-verify-proc")]
pub mod sql_verify;
pub mod stream_api;
#[cfg(feature = "streaming-export")]
pub mod streaming_export;
pub mod telemetry;
#[cfg(feature = "multi-tenant-enhanced")]
pub mod tenant_context;
#[cfg(feature = "tenant-quota-rls-enhanced")]
#[allow(missing_docs)]
pub mod tenant_quota_rls;
#[cfg(feature = "multi-tenant-enhanced")]
pub mod tenant_security;
mod transaction;
pub mod type_handler;
pub mod typed;
pub mod typed_ast;
#[cfg(feature = "typed-relation")]
pub mod typed_relation;
mod value;
#[cfg(feature = "zero-copy")]
pub mod value_borrowed;
#[cfg(feature = "zero-copy-deep")]
#[allow(missing_docs)]
pub mod zero_copy_pipeline;
pub mod perf_metrics;
#[cfg(any(
feature = "cdc-mysql",
feature = "cdc-postgres",
feature = "cdc-sqlite",
feature = "cdc-realtime-sync"
))]
#[allow(missing_docs)]
pub mod cdc;
#[cfg(feature = "rbac-abac-enhanced")]
#[allow(missing_docs)]
pub mod column_mask_interceptor;
#[cfg(feature = "rbac-abac-enhanced")]
#[allow(missing_docs)]
pub mod row_level_policy;
#[cfg(feature = "olap-vectorized")]
#[allow(missing_docs)]
pub mod olap;
#[cfg(feature = "executor-opt")]
#[allow(missing_docs)]
pub mod executor_passes;
pub use queryable::Query;
pub use queryable::QueryAs;
pub use sz_orm_macros::api_beta;
pub use sz_orm_macros::api_stable;
pub use sz_orm_macros::migrate;
pub use sz_orm_macros::query;
pub use sz_orm_macros::query_as;
pub use sz_orm_macros::schema;
pub use sz_orm_macros::sql_string;
pub use sz_orm_macros::typed_query;
#[cfg(feature = "n1-lint")]
pub use sz_orm_macros::detect_n_plus_one;
pub use sz_orm_macros::FromQueryResult;
pub use sz_orm_macros::RelationTrait;
#[cfg(feature = "data-validation")]
pub use sz_orm_macros::Validate;
pub use change_tracker::{ChangeTracker, EntityEntry, EntityState};
pub use lazy_loader::{LazyCollection, LazyLoader, LazyRef};
pub use linq::LinqQuery;
pub use query_cache::{QueryCache, QueryCacheKey, TimestampCache};
pub use cache::*;
pub use cycle_detection::{CycleDetector, CyclePolicy};
pub use db_type::*;
#[allow(ambiguous_glob_reexports)]
pub use dialect::*;
pub use eager_loader::NestedEagerResult;
pub use error::*;
#[allow(ambiguous_glob_reexports)]
pub use migration::*;
pub use model::*;
pub use nested_active_model::CascadeStrategy;
pub use pool::*;
#[allow(unused_imports)]
pub use query::*;
pub use schema_sync::{Confirm, DataMigrationHook, DestructiveSyncResult};
pub use transaction::*;
pub use value::*;
pub type Shared<T> = Arc<T>;
pub type Boxed<T> = Box<T>;
pub type DbResult<T> = Result<T, DbError>;
pub type PoolResult<T> = Result<T, PoolError>;
pub type CacheResult<T> = Result<T, CacheError>;
pub type TxResult<T> = Result<T, TxError>;
pub const DEFAULT_BATCH_SIZE: usize = 1000;
pub const DEFAULT_ACQUIRE_TIMEOUT: u64 = 30;
pub const DEFAULT_IDLE_TIMEOUT: u64 = 600;
pub const DEFAULT_MAX_LIFETIME: u64 = 1800;
pub const DEFAULT_MIN_IDLE: u32 = 5;
pub const DEFAULT_MAX_SIZE: u32 = 100;
#[cfg(feature = "perf-accel")]
#[derive(Debug, Clone)]
pub struct PerfConfig {
pub simd_enabled: bool,
pub simd_row_threshold: usize,
pub zero_copy_enabled: bool,
pub prewarm_enabled: bool,
pub prewarm_count: usize,
pub plan_cache_enabled: bool,
pub plan_cache_capacity: usize,
pub plan_cache_ttl_ms: u64,
}
#[cfg(feature = "perf-accel")]
impl Default for PerfConfig {
fn default() -> Self {
Self {
simd_enabled: false,
simd_row_threshold: 1024,
zero_copy_enabled: false,
prewarm_enabled: false,
prewarm_count: 1,
plan_cache_enabled: true,
plan_cache_capacity: 256,
plan_cache_ttl_ms: 300_000,
}
}
}
#[cfg(feature = "perf-accel")]
impl PerfConfig {
pub fn validate(&self) -> Result<(), DbError> {
if self.simd_row_threshold < 1 {
return Err(DbError::ConfigError(
"simd_row_threshold must be >= 1".to_string(),
));
}
if self.prewarm_count < 1 {
return Err(DbError::ConfigError(
"prewarm_count must be >= 1".to_string(),
));
}
if self.plan_cache_capacity < 1 {
return Err(DbError::ConfigError(
"plan_cache_capacity must be >= 1".to_string(),
));
}
if self.plan_cache_ttl_ms == 0 {
return Err(DbError::ConfigError(
"plan_cache_ttl_ms must be > 0".to_string(),
));
}
Ok(())
}
pub fn builder() -> PerfConfigBuilder {
PerfConfigBuilder::default()
}
}
#[cfg(feature = "perf-accel")]
#[derive(Debug, Clone, Default)]
pub struct PerfConfigBuilder {
config: PerfConfig,
}
#[cfg(feature = "perf-accel")]
impl PerfConfigBuilder {
pub fn simd(mut self, enabled: bool) -> Self {
self.config.simd_enabled = enabled;
self
}
pub fn simd_row_threshold(mut self, threshold: usize) -> Self {
self.config.simd_row_threshold = threshold;
self
}
pub fn zero_copy(mut self, enabled: bool) -> Self {
self.config.zero_copy_enabled = enabled;
self
}
pub fn prewarm(mut self, enabled: bool) -> Self {
self.config.prewarm_enabled = enabled;
self
}
pub fn prewarm_count(mut self, count: usize) -> Self {
self.config.prewarm_count = count;
self
}
pub fn plan_cache(mut self, enabled: bool) -> Self {
self.config.plan_cache_enabled = enabled;
self
}
pub fn plan_cache_capacity(mut self, capacity: usize) -> Self {
self.config.plan_cache_capacity = capacity;
self
}
pub fn plan_cache_ttl_ms(mut self, ttl_ms: u64) -> Self {
self.config.plan_cache_ttl_ms = ttl_ms;
self
}
pub fn build(self) -> Result<PerfConfig, DbError> {
self.config.validate()?;
Ok(self.config)
}
}
#[cfg(feature = "perf-accel")]
#[derive(Debug, Clone, Default)]
pub struct PerfMetricsSnapshot {
pub simd_hit_count: u64,
pub simd_miss_count: u64,
pub simd_latency_reduction_pct: f64,
pub zero_copy_hit_count: u64,
pub zero_copy_rss_reduction_pct: f64,
pub prewarm_success_count: u64,
pub plan_cache_hit_rate: f64,
pub plan_cache_eviction_count: u64,
}
#[cfg(feature = "perf-accel")]
#[derive(Debug, Default)]
pub struct PerfMetrics {
simd_hit_count: std::sync::atomic::AtomicU64,
simd_miss_count: std::sync::atomic::AtomicU64,
simd_latency_reduction_pct: std::sync::atomic::AtomicU64,
zero_copy_hit_count: std::sync::atomic::AtomicU64,
zero_copy_rss_reduction_pct: std::sync::atomic::AtomicU64,
prewarm_success_count: std::sync::atomic::AtomicU64,
plan_cache_hit_rate: std::sync::atomic::AtomicU64,
plan_cache_eviction_count: std::sync::atomic::AtomicU64,
}
#[cfg(feature = "perf-accel")]
impl PerfMetrics {
pub fn new() -> Self {
Self::default()
}
pub fn record_simd_hit(&self) {
self.simd_hit_count
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
}
pub fn record_simd_miss(&self) {
self.simd_miss_count
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
}
pub fn record_zero_copy_hit(&self) {
self.zero_copy_hit_count
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
}
pub fn record_prewarm_success(&self) {
self.prewarm_success_count
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
}
pub fn record_plan_cache_eviction(&self) {
self.plan_cache_eviction_count
.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
}
pub fn set_simd_latency_reduction_pct(&self, pct: f64) {
self.simd_latency_reduction_pct
.store(pct.to_bits(), std::sync::atomic::Ordering::Relaxed);
}
pub fn set_zero_copy_rss_reduction_pct(&self, pct: f64) {
self.zero_copy_rss_reduction_pct
.store(pct.to_bits(), std::sync::atomic::Ordering::Relaxed);
}
pub fn set_plan_cache_hit_rate(&self, rate: f64) {
self.plan_cache_hit_rate
.store(rate.to_bits(), std::sync::atomic::Ordering::Relaxed);
}
pub fn snapshot(&self) -> PerfMetricsSnapshot {
let o = std::sync::atomic::Ordering::Relaxed;
PerfMetricsSnapshot {
simd_hit_count: self.simd_hit_count.load(o),
simd_miss_count: self.simd_miss_count.load(o),
simd_latency_reduction_pct: f64::from_bits(self.simd_latency_reduction_pct.load(o)),
zero_copy_hit_count: self.zero_copy_hit_count.load(o),
zero_copy_rss_reduction_pct: f64::from_bits(self.zero_copy_rss_reduction_pct.load(o)),
prewarm_success_count: self.prewarm_success_count.load(o),
plan_cache_hit_rate: f64::from_bits(self.plan_cache_hit_rate.load(o)),
plan_cache_eviction_count: self.plan_cache_eviction_count.load(o),
}
}
}
#[cfg(feature = "auto-failover")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum FailbackStrategy {
Manual,
Auto,
}
#[cfg(feature = "auto-failover")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct FailoverConfig {
pub primary_url: String,
pub replica_url: String,
pub probe_interval: std::time::Duration,
pub probe_failure_threshold: u32,
pub failback_strategy: FailbackStrategy,
}
#[cfg(feature = "auto-failover")]
impl Default for FailoverConfig {
fn default() -> Self {
Self {
primary_url: String::new(),
replica_url: String::new(),
probe_interval: std::time::Duration::from_secs(1),
probe_failure_threshold: 3,
failback_strategy: FailbackStrategy::Manual,
}
}
}
#[cfg(feature = "auto-failover")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct HaConfig {
pub failover_enabled: bool,
pub failover: Option<FailoverConfig>,
pub rate_limit_threshold: f64,
pub rate_limit_queue_timeout_ms: u64,
pub circuit_breaker_error_threshold: f64,
pub circuit_breaker_half_open_probes: u32,
pub trace_sample_rate: f64,
pub trace_otlp_endpoint: Option<String>,
}
#[cfg(feature = "auto-failover")]
impl Default for HaConfig {
fn default() -> Self {
Self {
failover_enabled: false,
failover: None,
rate_limit_threshold: 0.0,
rate_limit_queue_timeout_ms: 100,
circuit_breaker_error_threshold: 0.5,
circuit_breaker_half_open_probes: 1,
trace_sample_rate: 1.0,
trace_otlp_endpoint: None,
}
}
}
#[cfg(feature = "auto-failover")]
impl HaConfig {
pub fn validate(&self) -> Result<(), DbError> {
if let Some(failover) = &self.failover {
if failover.probe_interval > std::time::Duration::from_secs(5) {
return Err(DbError::ConfigError(
"probe_interval must be <= 5s for RTO <= 5s".to_string(),
));
}
if failover.probe_failure_threshold == 0 {
return Err(DbError::ConfigError(
"probe_failure_threshold must be >= 1".to_string(),
));
}
}
if self.circuit_breaker_error_threshold <= 0.0
|| self.circuit_breaker_error_threshold >= 1.0
{
return Err(DbError::ConfigError(
"circuit_breaker_error_threshold must be in (0, 1)".to_string(),
));
}
if self.circuit_breaker_half_open_probes == 0 {
return Err(DbError::ConfigError(
"circuit_breaker_half_open_probes must be >= 1".to_string(),
));
}
if self.trace_sample_rate < 0.0 || self.trace_sample_rate > 1.0 {
return Err(DbError::ConfigError(
"trace_sample_rate must be in [0, 1]".to_string(),
));
}
Ok(())
}
pub fn builder() -> HaConfigBuilder {
HaConfigBuilder::default()
}
}
#[cfg(feature = "auto-failover")]
#[derive(Debug, Clone, Default)]
pub struct HaConfigBuilder {
config: HaConfig,
}
#[cfg(feature = "auto-failover")]
impl HaConfigBuilder {
pub fn failover_enabled(mut self, enabled: bool) -> Self {
self.config.failover_enabled = enabled;
self
}
pub fn failover(mut self, config: FailoverConfig) -> Self {
self.config.failover = Some(config);
self
}
pub fn rate_limit_threshold(mut self, threshold: f64) -> Self {
self.config.rate_limit_threshold = threshold;
self
}
pub fn rate_limit_queue_timeout_ms(mut self, ms: u64) -> Self {
self.config.rate_limit_queue_timeout_ms = ms;
self
}
pub fn circuit_breaker_error_threshold(mut self, threshold: f64) -> Self {
self.config.circuit_breaker_error_threshold = threshold;
self
}
pub fn circuit_breaker_half_open_probes(mut self, probes: u32) -> Self {
self.config.circuit_breaker_half_open_probes = probes;
self
}
pub fn trace_sample_rate(mut self, rate: f64) -> Self {
self.config.trace_sample_rate = rate;
self
}
pub fn trace_otlp_endpoint(mut self, endpoint: impl Into<String>) -> Self {
self.config.trace_otlp_endpoint = Some(endpoint.into());
self
}
pub fn build(self) -> Result<HaConfig, DbError> {
self.config.validate()?;
Ok(self.config)
}
}
#[cfg(feature = "eco-config")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum WebFramework {
Axum,
Actix,
Warp,
}
#[cfg(feature = "eco-config")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum MiddlewareFeature {
PoolInject,
Transaction,
RateLimit,
Tracing,
HealthEndpoint,
}
#[cfg(feature = "eco-config")]
#[derive(Debug, Clone, Copy, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum SourceOrm {
Diesel,
SeaOrm,
Sqlx,
}
#[cfg(feature = "eco-config")]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct EcoConfig {
pub web_framework: WebFramework,
pub middleware_features: Vec<MiddlewareFeature>,
pub migration_source_orm: Option<SourceOrm>,
pub migration_dry_run: bool,
pub schema_diff_left_url: Option<String>,
pub schema_diff_right_url: Option<String>,
}
#[cfg(feature = "eco-config")]
impl Default for EcoConfig {
fn default() -> Self {
Self {
web_framework: WebFramework::Axum,
middleware_features: Vec::new(),
migration_source_orm: None,
migration_dry_run: true,
schema_diff_left_url: None,
schema_diff_right_url: None,
}
}
}
#[cfg(feature = "eco-config")]
impl EcoConfig {
pub fn validate(&self) -> Result<(), DbError> {
if self.schema_diff_left_url.is_some() != self.schema_diff_right_url.is_some() {
return Err(DbError::ConfigError(
"schema_diff_left_url 和 schema_diff_right_url 必须同时提供或同时缺失".to_string(),
));
}
Ok(())
}
pub fn builder() -> EcoConfigBuilder {
EcoConfigBuilder::default()
}
}
#[cfg(feature = "eco-config")]
#[derive(Debug, Clone, Default)]
pub struct EcoConfigBuilder {
config: EcoConfig,
}
#[cfg(feature = "eco-config")]
impl EcoConfigBuilder {
pub fn web_framework(mut self, fw: WebFramework) -> Self {
self.config.web_framework = fw;
self
}
pub fn middleware_features(mut self, features: Vec<MiddlewareFeature>) -> Self {
self.config.middleware_features = features;
self
}
pub fn migration_source_orm(mut self, orm: SourceOrm) -> Self {
self.config.migration_source_orm = Some(orm);
self
}
pub fn migration_dry_run(mut self, dry_run: bool) -> Self {
self.config.migration_dry_run = dry_run;
self
}
pub fn schema_diff_left_url(mut self, url: impl Into<String>) -> Self {
self.config.schema_diff_left_url = Some(url.into());
self
}
pub fn schema_diff_right_url(mut self, url: impl Into<String>) -> Self {
self.config.schema_diff_right_url = Some(url.into());
self
}
pub fn build(self) -> Result<EcoConfig, DbError> {
self.config.validate()?;
Ok(self.config)
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::error::Error;
#[test]
fn test_db_type() {
assert_eq!(DbType::MySQL.as_str(), "mysql");
assert_eq!(DbType::PostgreSQL.as_str(), "postgres");
assert_eq!(DbType::Sqlite.as_str(), "sqlite");
}
#[test]
fn test_value() {
let v = Value::Null;
assert!(v.is_null());
let v = Value::I64(42);
assert!(v.is_i64());
let v = Value::String("hello".to_string());
assert!(v.is_string());
}
#[test]
fn test_db_error_display() {
let err = DbError::QueryError("test query failed".to_string());
assert_eq!(format!("{}", err), "Query error: test query failed");
let err = DbError::ConnectionRefused("localhost".to_string());
assert_eq!(format!("{}", err), "Connection refused: localhost");
}
#[test]
fn test_db_error_source() {
let err = DbError::PoolError(PoolError::Timeout);
assert!(err.source().is_some());
}
#[tokio::test]
async fn test_async_trait_export() {
fn _check_send_sync<T: Send + Sync>() {}
struct TestImpl;
#[async_trait]
trait AsyncFoo: Send + Sync {
async fn foo(&self);
}
#[async_trait]
impl AsyncFoo for TestImpl {
async fn foo(&self) {}
}
let impl_ = TestImpl;
impl_.foo().await;
_check_send_sync::<TestImpl>();
}
#[test]
fn test_sql_string_valid_select() {
let sql = sql_string!("SELECT * FROM users WHERE id = 1");
assert!(sql.contains("SELECT"));
assert!(sql.contains("FROM"));
}
#[test]
fn test_sql_string_valid_insert() {
let sql = sql_string!("INSERT INTO users (name) VALUES ('alice')");
assert!(sql.contains("INSERT"));
}
#[test]
fn test_sql_string_valid_update() {
let sql = sql_string!("UPDATE users SET name = 'bob' WHERE id = 1");
assert!(sql.contains("UPDATE"));
}
#[test]
fn test_sql_string_valid_delete() {
let sql = sql_string!("DELETE FROM users WHERE id = 1");
assert!(sql.contains("DELETE"));
}
#[test]
fn test_sql_string_valid_create() {
let sql = sql_string!("CREATE TABLE test (id INT PRIMARY KEY)");
assert!(sql.contains("CREATE"));
}
#[test]
fn test_sql_string_with_params() {
let sql = sql_string!("SELECT * FROM users WHERE id = ?"; params: 1);
assert!(sql.contains("?"));
}
#[test]
fn test_sql_string_complex_query() {
let sql = sql_string!(
"SELECT u.*, o.total FROM users u LEFT JOIN orders o ON u.id = o.user_id WHERE u.status = 'active'"
);
assert!(sql.contains("LEFT JOIN"));
}
#[test]
fn test_sql_string_nested_parens() {
let sql = sql_string!("SELECT * FROM (SELECT * FROM users) t");
assert!(sql.contains("SELECT"));
}
}