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
// Copyright (c) 2026, Salesforce, Inc. All rights reserved.
// SPDX-License-Identifier: Apache-2.0 OR MIT
//! Shared Hyper instance for compile-time validation.
//!
//! One `CompileTimeDb` is shared across all macro invocations in a single
//! crate compilation (rustc spawns one proc-macro host process per crate).
//! The instance is lazily initialized on first use via `get_or_init()` and
//! dropped when the host process exits.
use Mutex;
/// A live connection to an in-process Hyper instance used for SQL dry-runs.
// `HyperProcess` manages the hyperd subprocess; it can produce many independent
// `Connection`s (see `hyperdb_api::pool` for the production N-connection pool).
// Here we hold exactly ONE `Connection` — a single TCP session used for all
// `LIMIT 0` dry-runs. A `Connection` has internal mutable TCP + protocol state
// and is NOT safe to use from multiple threads simultaneously.
//
// The `parking_lot::Mutex` is what makes this safe: it ensures only one
// proc-macro expansion thread touches the connection at a time. Each `query_as!`
// site locks, runs one dry-run (~7ms), unlocks. They serialize on the one
// connection rather than each getting their own (a connection-pool approach
// would work too but adds startup cost for negligible gain at v1 scale).
//
// Neither `HyperProcess` nor `Connection` is `Send`/`Sync` in the public API.
// We implement both here because `OnceLock<T>` requires `T: Send + Sync`.
// The `Mutex` upholds the invariant that only one thread ever accesses the
// fields — making the `Send`/`Sync` impls sound.
//
// REVISIT: if `HyperProcess`/`Connection` are made `Send` upstream, remove
// these impls and let the compiler derive them.
//
// # Why `parking_lot::Mutex` instead of `std::sync::Mutex`
//
// Proc-macros routinely call `panic!` to emit a `compile_error!`. A
// `std::sync::Mutex` poisons on the first panic, causing every subsequent
// macro invocation in the same crate to receive `PoisonError` regardless of
// whether they have anything to do with the failing site. `parking_lot::Mutex`
// never poisons — lock acquisition always succeeds after the panicking thread
// releases the lock, so a bad `query_as!` site doesn't cascade.
// SAFETY: `OnceLock` requires `Send`; safe because the `Mutex` guarantees
// exclusive access — `CompileTimeDb` is never touched without holding the lock.
unsafe
// SAFETY: `OnceLock` requires `Sync`; safe for the same reason as `Send` above.
unsafe
/// Global storage: initialized at most once per proc-macro host process.
///
/// We use `std::sync::OnceLock` (stable since 1.70) rather than a raw
/// `static mut` + `Once` pair to avoid the `static_mut_refs` UB concern in
/// Rust 2024 edition. `OnceLock` provides the same "write-once, read-many"
/// guarantee without unsafe code in the accessor.
static DB_STORAGE: OnceLock = new;
/// Returns a reference to the global `Mutex<CompileTimeDb>`, initializing it
/// on the first call.
///
/// # Panics
///
/// Panics if Hyper fails to start (e.g. `HYPERD_PATH` is invalid or the
/// binary is absent). The error is surfaced as a `compile_error!` by the
/// calling macro.