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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
//! Cloudflare Hyperdrive connector (async, WASM-only).
//!
//! Hyperdrive is Cloudflare's connection pooler and edge cache for existing
//! `PostgreSQL` databases. Inside a Worker, the binding hands out a
//! `worker::Socket` already connected to the pooler, and the pooler speaks the
//! plain `PostgreSQL` wire protocol.
//!
//! This module is **not a separate driver**. `worker::Socket` implements
//! tokio's `AsyncRead`/`AsyncWrite`, so
//! [`tokio_postgres::Config::connect_raw`] hands back the very same
//! [`tokio_postgres::Client`] the native driver wraps. Everything downstream —
//! query surface, transactions, savepoints, prepared statements and the
//! statement cache, relational queries, `migrate`, `push`, `introspect` — is
//! the [`tokio`](crate::postgres::tokio) driver compiled verbatim for
//! `wasm32-unknown-unknown`. The only thing this module adds is the dial.
//!
//! # Requirements
//!
//! - `target_arch = "wasm32"` — the binding only links inside a Worker runtime.
//! - The `worker` crate.
//!
//! ```toml
//! [dependencies]
//! drizzle = { version = "*", features = ["hyperdrive", "uuid"] }
//! worker = { version = "*" }
//! ```
//!
//! ```toml
//! # wrangler.toml
//! [[hyperdrive]]
//! binding = "HYPERDRIVE"
//! id = "<your-hyperdrive-id>"
//! ```
//!
//! # TLS
//!
//! Hyperdrive terminates TLS at the edge and the Worker reaches the pooler over
//! a local, already-authenticated channel, so the documented pattern is
//! [`NoTls`](tokio_postgres::NoTls) — which is what [`connect`] uses.
//!
//! Dialing a database *directly* (no Hyperdrive) with
//! `worker::Socket::builder()` does need TLS. That path is out of scope for
//! [`connect_raw`], which is also `NoTls`: use `worker`'s own
//! `postgres_tls::PassthroughTls` with `Config::connect_raw` (enable the
//! `worker` crate's `tokio-postgres` feature), then hand the resulting client
//! to [`Drizzle::new`](crate::postgres::tokio::Drizzle::new).
//!
//! # Quick start
//!
//! ```rust
//! # let _ = r####"
//! use drizzle::postgres::prelude::*;
//! use drizzle::postgres::hyperdrive;
//! use worker::{event, Context, Env, Request, Response};
//!
//! #[PostgresTable]
//! struct User {
//! #[column(serial, primary)]
//! id: i32,
//! name: String,
//! }
//!
//! #[derive(PostgresSchema)]
//! struct AppSchema {
//! user: User,
//! }
//!
//! #[event(fetch)]
//! async fn fetch(_req: Request, env: Env, _ctx: Context) -> worker::Result<Response> {
//! let (db, AppSchema { user }) =
//! hyperdrive::connect(&env.hyperdrive("HYPERDRIVE")?, AppSchema::new())
//! .await
//! .map_err(|e| worker::Error::RustError(e.to_string()))?;
//!
//! db.insert(user)
//! .values([InsertUser::new("Alice")])
//! .execute()
//! .await
//! .map_err(|e| worker::Error::RustError(e.to_string()))?;
//!
//! let users: Vec<SelectUser> = db
//! .select(())
//! .from(user)
//! .all()
//! .await
//! .map_err(|e| worker::Error::RustError(e.to_string()))?;
//!
//! Response::ok(format!("{} users", users.len()))
//! }
//! # "####;
//! ```
//!
//! # Migrations
//!
//! Prefer applying migrations out of band (CI, or `drizzle migrate` against the
//! database's direct connection string) — a Worker invocation is short-lived
//! and many run concurrently. When the Worker must migrate itself,
//! [`Drizzle::migrate`](crate::postgres::tokio::Drizzle::migrate) works
//! unchanged: it takes the same `pg_advisory_lock`, so concurrent invocations
//! serialize rather than race, and
//! [`migrate_with_repair`](crate::postgres::tokio::Drizzle::migrate_with_repair)
//! reconciles a migration interrupted by a Worker eviction.
//!
//! ```rust
//! # let _ = r####"
//! use drizzle_migrations::Tracking;
//!
//! // Embeds the migration files at compile time (expands to a Vec).
//! let migrations = drizzle::include_migrations!("./migrations");
//!
//! let (mut db, schema) = hyperdrive::connect(&env.hyperdrive("HYPERDRIVE")?, AppSchema::new()).await?;
//! db.migrate(&migrations, Tracking::POSTGRES).await?;
//! # "####;
//! ```
//!
//! `migrate` needs `&mut Drizzle` with no outstanding clones, so run it before
//! handing clones to other tasks.
//!
//! # Lifetime of the connection
//!
//! [`tokio_postgres`] splits a connection into a [`Client`] and a driver future
//! that owns the socket. The future is spawned with
//! [`wasm_bindgen_futures::spawn_local`], so it lives as long as the Worker
//! invocation that created it and is torn down with the isolate. A `Client`
//! therefore must not outlive the request that dialed it — connect per
//! invocation and let Hyperdrive's pooler absorb the cost.
//!
//! # Integer precision
//!
//! Unlike the D1 and Durable Objects drivers, values never cross a JS number
//! boundary here: only raw bytes traverse the socket and `postgres-types`
//! decodes the binary wire format in wasm. `i64`, `numeric`, and `bytea`
//! round-trip exactly.
use DrizzleError;
use ;
use ;
use crateDrizzle;
/// `tokio_postgres::Error`'s `Display` is just "db error"; the server's actual
/// message lives in the `DbError` source.
/// Connects to `PostgreSQL` through a Cloudflare Hyperdrive binding.
///
/// Returns the same `(Drizzle, Schema)` tuple as
/// [`Drizzle::new`](crate::postgres::tokio::Drizzle::new), for destructuring.
///
/// The connection string carried by the binding points at the local pooler
/// endpoint; TLS is terminated by Hyperdrive at the edge, so the wire to the
/// pooler is dialed with [`NoTls`].
///
/// # Errors
///
/// Returns [`DrizzleError::Other`] if the binding cannot open a socket, if its
/// connection string does not parse as a [`Config`], or if the `PostgreSQL`
/// startup handshake fails.
pub async
/// Connects over an already-opened [`Socket`] using an explicit [`Config`].
///
/// Use this when the socket does not come from a Hyperdrive binding — e.g. a
/// direct `worker::Socket::builder().connect(host, port)` — or when the
/// connection parameters need adjusting (`application_name`, `options`, a
/// different `dbname`) before the handshake.
///
/// The connection driver future is spawned with
/// [`wasm_bindgen_futures::spawn_local`]; if it ever resolves with an error the
/// error is written to the Worker console, since there is no join handle to
/// surface it through.
///
/// # Errors
///
/// Returns [`DrizzleError::Other`] if the `PostgreSQL` startup handshake fails.
pub async