1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
//! `ferrule-sql` — the embeddable, synchronous, bounded-memory SQL core.
//!
//! This crate owns the unified neutral [`Value`]/[`Row`] types, the
//! [`DatabaseUrl`] parser, the [`Connection`] trait and its per-backend
//! drivers, the connect dispatcher (direct, HTTP-proxy, and SSH-tunnel
//! transports), the transaction helpers, and the cross-backend copy /
//! bulk-load write path. It carries no rendering (`tabled`) or
//! credential-resolution (`ferrule-config`) dependency, so it can be
//! embedded by callers that supply already-resolved connection details.
//!
//! Backends are feature-gated (`postgres`, `mysql`, `mssql`, `sqlite`,
//! `oracle`); the SSH tunnel transport is behind `ssh`. The `default`
//! feature set is empty — enable the backends you need.
//!
//! # Why these properties
//!
//! - **Synchronous public API.** No `async fn` / `Future` crosses the
//! crate boundary, so a host with no runtime of its own can call
//! straight through. The async network drivers are still used — each
//! [`Connection`] owns a private current-thread `tokio` runtime and
//! blocks on it — but that runtime is an implementation detail. (SQLite
//! and Oracle are natively synchronous and call through directly.)
//! - **Bounded memory.** [`Connection::query_cursor`] streams from a
//! native database cursor one back-pressured batch at a time, and
//! [`write_rows`] flushes an arbitrarily large row iterator in
//! fixed-size batches — both stay `O(batch)` regardless of total row
//! count. The eager [`Connection::query`] still materializes a full
//! `Vec<Row>`, but it is capped by per-cell / per-row / per-result
//! [`SizeGuards`] so a pathological result fails fast instead of OOMing.
//! - **Caller-resolved credentials.** [`connect`] takes the password as a
//! [`secrecy::SecretString`] on [`ConnectOptions`]; the crate performs
//! no credential resolution and depends on no keyring / prompt library.
//!
//! # Embedding flow
//!
//! The three steps a host follows — **connect → streaming read → batched
//! write** — line up with the runnable
//! [`examples/embed.rs`](https://github.com/rustpunk/ferrule/blob/main/ferrule-sql/examples/embed.rs)
//! (`cargo run -p ferrule-sql --example embed --features sqlite`):
//!
//! ### 1. Connect with a resolved secret
//!
//! Parse a [`DatabaseUrl`], hand the host-resolved credential to
//! [`ConnectOptions::password`], and call [`connect`]. The returned
//! [`Connection`] owns its private runtime and blocks on every call.
//!
//! ### 2. Streaming read (the bounded cursor)
//!
//! [`Connection::query_cursor`] returns a [`RowCursor`]. Pull rows with
//! [`RowCursor::next_batch(n)`](RowCursor::next_batch) (a bounded chunk)
//! or by iterating; either way the driver only fetches more from the
//! server as you consume, so peak memory is `O(batch)`, never the whole
//! result. The cursor borrows the connection for its lifetime, so scope
//! it before issuing the next statement.
//!
//! ### 3. Batched write (back-pressured, structured report)
//!
//! [`write_rows`] consumes any `IntoIterator<Item = Row>` and flushes it
//! in fixed-size batches ([`WriteOptions::batch_size`]), buffering one
//! batch at a time. It reuses the cross-DB copy path's SQL generation and
//! transaction control and returns a [`WriteReport`] naming exactly which
//! batches / rows landed and which were rejected. Pair it with the
//! cursor from step 2 for an end-to-end bounded-memory pipe.
//!
//! ```no_run
//! # #[cfg(feature = "sqlite")]
//! # fn demo() -> Result<(), Box<dyn std::error::Error>> {
//! use ferrule_sql::{
//! connect, write_rows, Backend, ColumnInfo, ConnectOptions, DatabaseUrl,
//! Row, TypeHint, Value, WriteOptions,
//! };
//! use secrecy::SecretString;
//!
//! // 1. Connect. The password is a caller-resolved `SecretString`;
//! // SQLite ignores it, a networked backend would consume it.
//! let url = DatabaseUrl::parse("sqlite:///tmp/embed-demo.db")?;
//! let opts = ConnectOptions {
//! insecure: false,
//! password: Some(SecretString::from("resolved-by-the-host")),
//! };
//! let mut conn = connect(&url, &opts, None)?;
//! conn.execute("CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT)")?;
//!
//! // 2. Streaming read at bounded memory: two rows per pull.
//! {
//! let mut cursor = conn.query_cursor("SELECT id, name FROM t ORDER BY id")?;
//! while !cursor.next_batch(2)?.is_empty() {
//! // process this bounded batch, then pull the next one
//! }
//! }
//!
//! // 3. Batched write: one batch buffered at a time.
//! let columns = [
//! ColumnInfo { name: "id".into(), type_hint: TypeHint::Int64, nullable: false },
//! ColumnInfo { name: "name".into(), type_hint: TypeHint::String, nullable: true },
//! ];
//! let rows: Vec<Row> = (1..=1000)
//! .map(|i| vec![Value::Int64(i), Value::String(format!("n{i}"))])
//! .collect();
//! let report = write_rows(
//! &mut *conn,
//! Backend::Sqlite,
//! "t",
//! &columns,
//! rows,
//! &WriteOptions { batch_size: 200, ..Default::default() },
//! )?;
//! assert!(report.is_complete());
//! # Ok(())
//! # }
//! ```
//!
//! # Reentrancy
//!
//! The private runtime is current-thread, so a [`Connection`] (and its
//! [`RowCursor`]) must not be driven from inside another `block_on` on the
//! same thread. An async host hops to a blocking thread
//! (`tokio::task::spawn_blocking` or a dedicated OS thread) first.
/// Per-backend driver modules, one feature-gated submodule per backend.
///
/// The module is `pub` so the per-backend concrete connection types and
/// their inline integration tests are reachable, but the connection
/// *constructors* are `pub(crate)`: every caller establishes connections
/// through the synchronous URL-scheme dispatcher [`connect`], which is
/// the only blocking entry point and the one that owns the private
/// runtime. Embedders never touch a driver's async constructor directly.
pub use connect_with_tunnel;
pub use ;
pub use ;
pub use ;
pub use Dialect;
pub use SqlError;
pub use SizeGuards;
pub use ;
pub use apply_paging;
pub use ;
pub use ;
pub use SyncConnection;
pub use ;
pub use SshConfig;
pub use ;
pub use DatabaseUrl;
pub use ;
pub use ;