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
//! Pool-scoped tenant isolation: set `app.tenant_id` once when a connection
//! is checked out of the pool, reset it when it goes back. Every query made
//! during the request is auto-isolated, with no per-call-site boilerplate.
//!
//! This is the pattern most production codebases want. It pairs with:
//!
//! 1. The [`TENANT_ID`] task-local — set per-request by the
//! [`tenant_scope`] middleware below.
//! 2. The [`with_tenant_hooks`] pool-builder helper — wires the
//! `before_acquire` / `after_release` hooks that read the task-local
//! and run `set_config` / `RESET` on the connection.
//!
//! ## Wiring
//!
//! ```no_run
//! # async fn run() -> sqlx::Result<()> {
//! use sqlx::postgres::PgPoolOptions;
//! use tenaxum::pool;
//!
//! let pool = pool::with_tenant_hooks(PgPoolOptions::new().max_connections(8))
//! .connect("postgres://...").await?;
//! # Ok(()) }
//! ```
//!
//! Then in your Axum app:
//!
//! ```ignore
//! use axum::{Router, middleware};
//! use tenaxum::pool::tenant_scope;
//!
//! let app = Router::new()
//! // ...your routes...
//! .layer(middleware::from_fn(tenant_scope));
//! ```
//!
//! `tenant_scope` reads `Extension<TenantId>` (set by your auth layer) and
//! scopes the [`TENANT_ID`] task-local for the rest of the request. The
//! pool hooks then pick it up automatically on every connection checkout.
//!
//! ## When to use this vs [`crate::PgPoolExt::begin_tenant`]
//!
//! - **Pool hooks** (this module): for the common case where every
//! handler in your app touches tenant-scoped data and you want
//! isolation by default.
//! - **`begin_tenant`**: for explicit, scoped uses — background jobs
//! that operate on a specific tenant outside of an HTTP request,
//! one-off scripts, or admin paths where the pool hooks aren't wired.
use crateTenantId;
use ;
use PgPoolOptions;
task_local!
/// Apply tenaxum pool hooks to a [`PgPoolOptions`] builder.
///
/// `before_acquire`: reads [`TENANT_ID`] task-local; if set, runs
/// `SELECT set_config('app.tenant_id', '<uuid>', false)` (session-level —
/// because the connection may be used outside an explicit transaction).
/// If unset, runs `RESET app.tenant_id` so a connection that previously
/// held a tenant binding can't leak it into the new request.
///
/// `after_release`: unconditionally runs `RESET app.tenant_id` before the
/// connection returns to the pool. Defense in depth — the next acquire
/// will set its own value, but if anything ever bypasses `before_acquire`,
/// the connection comes back clean.
/// Axum middleware that scopes the [`TENANT_ID`] task-local for the
/// duration of the request.
///
/// Reads `Extension<TenantId>` off the request — your auth layer is
/// expected to have inserted it. If absent, the request runs with no
/// tenant binding (the pool hooks will run `RESET app.tenant_id` on
/// every checkout, which is the correct behavior for unauthenticated
/// or admin paths).
pub async