1#![warn(missing_docs)]
55#![warn(clippy::all)]
56#![deny(unsafe_code)]
57
58pub mod connection;
59pub mod dialect;
60pub mod error;
61pub mod pool;
62pub mod schema;
63pub mod security;
64pub mod sink;
65pub mod source;
66pub mod types;
67
68#[cfg(feature = "postgres")]
70pub mod postgres;
71
72#[cfg(feature = "mysql")]
73pub mod mysql;
74
75#[cfg(feature = "sqlserver")]
76pub mod sqlserver;
77
78pub mod prelude {
80 pub use crate::error::{Error, ErrorCategory, Result};
82
83 pub use crate::types::{ColumnMetadata, Row, TableMetadata, Value};
85
86 pub use crate::connection::{
88 Connection, ConnectionConfig, ConnectionFactory, ConnectionLifecycle, DatabaseType,
89 IsolationLevel, PreparedStatement, RowStream, Transaction,
90 };
91
92 pub use crate::pool::{
94 create_pool, create_pool_with_config, AtomicPoolStats, ConnectionPool, PoolBuilder,
95 PoolConfig, PoolStats, PooledConnection, RecycleReason, SimpleConnectionPool,
96 };
97
98 pub use crate::dialect::{
100 dialect_for, MariaDbDialect, MySqlDialect, PostgresDialect, SqlDialect, SqlServerDialect,
101 };
102
103 pub use crate::schema::{
105 AutoDdlMode, ForeignKeyAction, ForeignKeyMetadata, IdentityNaming, IndexMetadata,
106 PrefixNaming, SchemaEvolutionMode, SchemaEvolutionResult, SchemaManager, SchemaMapping,
107 SchemaProvider, SuffixNaming, TableNamingStrategy,
108 };
109
110 pub use crate::source::{
112 AtomicSourceStats, PollResult, QueryMode, SourceOffset, SourceQueryBuilder, SourceRecord,
113 SourceStats, TableSource, TableSourceConfig,
114 };
115
116 pub use crate::sink::{
118 AtomicSinkStats, BatchConfig, BatchResult, BufferedSink, FailedRecord, SinkRecord,
119 SinkStats, TableSink, TableSinkBuilder, TableSinkConfig, WriteMode,
120 };
121}
122
123pub use error::{Error, Result};
125pub use types::Value;
126
127#[cfg(test)]
128mod tests {
129 use super::prelude::*;
130
131 #[test]
132 fn test_prelude_imports() {
133 let _value = Value::Int32(42);
135 let _config = ConnectionConfig::new("postgres://localhost/test");
136 let _batch = BatchConfig::default();
137 let _mode = WriteMode::Upsert;
138 }
139
140 #[test]
141 fn test_error_types() {
142 let err = Error::connection("test error");
143 assert!(err.is_retriable());
144 assert_eq!(err.category(), ErrorCategory::Connection);
145 }
146
147 #[test]
148 fn test_value_types() {
149 let v = Value::from(42_i32);
150 assert!(!v.is_null());
151 assert_eq!(v.as_i64(), Some(42));
152
153 let v = Value::from("hello");
154 assert_eq!(v.as_str(), Some("hello"));
155 }
156
157 #[test]
158 fn test_table_metadata() {
159 let mut table = TableMetadata::new("users");
160 table.schema = Some("public".into());
161
162 assert_eq!(table.qualified_name(), "public.users");
163 assert!(table.columns.is_empty());
164 }
165
166 #[test]
167 fn test_query_modes() {
168 assert!(!QueryMode::Bulk.is_incremental());
169 assert!(QueryMode::incrementing("id").is_incremental());
170 assert!(QueryMode::timestamp("updated_at").is_incremental());
171 }
172
173 #[test]
174 fn test_write_modes() {
175 assert_eq!(WriteMode::default(), WriteMode::Upsert);
176 }
177
178 #[test]
179 fn test_dialect_selection() {
180 let pg = dialect_for("postgres");
181 assert_eq!(pg.name(), "PostgreSQL");
182
183 let mysql = dialect_for("mysql");
184 assert_eq!(mysql.name(), "MySQL");
185
186 let mssql = dialect_for("sqlserver");
187 assert_eq!(mssql.name(), "SQL Server");
188 }
189
190 #[test]
191 fn test_source_config() {
192 let config = TableSourceConfig::incrementing("events", "id")
193 .with_schema("public")
194 .with_batch_size(500);
195
196 assert_eq!(config.table, "events");
197 assert_eq!(config.schema, Some("public".into()));
198 assert_eq!(config.batch_size, 500);
199 }
200
201 #[test]
202 fn test_sink_config() {
203 let config = TableSinkBuilder::new()
204 .batch_size(2000)
205 .write_mode(WriteMode::Insert)
206 .auto_ddl(AutoDdlMode::CreateAndEvolve)
207 .build();
208
209 assert_eq!(config.batch.max_size, 2000);
210 assert_eq!(config.write_mode, WriteMode::Insert);
211 assert_eq!(config.auto_ddl, AutoDdlMode::CreateAndEvolve);
212 }
213}