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
//! Arrow Flight adapter layer for `fraiseql-server`.
//!
//! This module is a **thin adapter** (~270 lines) that bridges fraiseql-core's
//! database adapters to the [`fraiseql_arrow`] crate's trait interfaces and manages
//! the Flight gRPC server lifecycle (port 50051, graceful shutdown).
//!
//! # Architecture
//!
//! Arrow Flight support uses a library/consumer split:
//!
//! - [`fraiseql_arrow`] (the `fraiseql-arrow` crate) — full Arrow Flight gRPC implementation,
//! database-agnostic via `ArrowDatabaseAdapter` and `QueryExecutor` traits
//! - This module (`fraiseql-server/src/arrow`) — thin adapter layer that bridges `fraiseql-core`
//! adapters to the `fraiseql-arrow` traits
//!
//! The Flight gRPC server binds on port 50051 alongside the HTTP server (port 3000).
//! Enable with `--features arrow`.
//!
//! # Relationship to `fraiseql-arrow`
//!
//! This module does **not** re-implement the Arrow Flight protocol. All Flight logic
//! (authentication, streaming, caching, JSON↔Arrow conversion) lives in the
//! [`fraiseql_arrow`] library crate. This module provides:
//!
//! - [`FlightDatabaseAdapter`]: Wraps fraiseql-core adapters (Postgres, Wire) to implement
//! `fraiseql_arrow::ArrowDatabaseAdapter`
//! - [`ExecutorQueryAdapter`]: Wraps `Executor` to implement `fraiseql_arrow::QueryExecutor` (type
//! erasure)
//! - [`create_flight_service`]: Factory that assembles a configured `FraiseQLFlightService` from
//! core adapters
//!
//! # Usage
//!
//! This module is only available when the `arrow` feature is enabled.
use Arc;
pub use FlightDatabaseAdapter;
pub use ExecutorQueryAdapter;
use FraiseQLFlightService;
use FraiseWireAdapter;
use PostgresAdapter;
pub use PolicyGatedExecutor;
/// Create an Arrow Flight service with a real database adapter.
///
/// **No `QueryExecutor` is attached here** — the service is built before there is
/// an `AppState` to enforce policy from. The server attaches
/// [`PolicyGatedExecutor`] at serve time (`server::lifecycle`), so the Flight
/// GraphQL paths run through tenant resolution, the suspended-tenant gate,
/// per-tenant quotas and trusted documents like every other transport (#954).
/// A service that never reaches `serve()` keeps refusing GraphQL fail-closed
/// ("no executor configured"). Do **not** attach a bare
/// [`ExecutorQueryAdapter`] instead: that is precisely the shape that makes
/// Flight the one transport skipping all of the above.
///
/// Supports both PostgreSQL and FraiseQL Wire adapters depending on feature flags:
/// - Default: PostgreSQL adapter for traditional database connections
/// - `wire-backend` feature: FraiseQL Wire adapter for streaming JSON queries with low memory
/// overhead
///
/// `upload_tables` is the operator's `flight_upload_tables` allow-list (#953). An
/// empty slice leaves `Upload` **disabled**, which is the default and the only safe
/// one: the target table is named by the client and the write skips the mutation
/// pipeline. This is the config seam — without it `with_upload_tables` would be a
/// library-only setter with no caller, and no operator could reach the feature.
///
/// # Arguments
///
/// * `adapter` - Database adapter from fraiseql-core (PostgreSQL or Wire depending on features)
/// * `upload_tables` - Tables an authenticated client may `Upload` into; empty disables Upload
///
/// # Returns
///
/// `FraiseQLFlightService` configured with the real database adapter
///
/// # Example
///
/// ```text
/// // PostgreSQL (default)
/// let pg_adapter = PostgresAdapter::new(&db_url).await?;
/// let flight_service = create_flight_service(Arc::new(pg_adapter), &config.flight_upload_tables);
///
/// // FraiseQL Wire (with the `wire-backend` feature)
/// let wire_adapter = FraiseWireAdapter::new(&db_url);
/// let flight_service = create_flight_service(Arc::new(wire_adapter), &[]);
/// ```
/// Empty the `OptimizedView` registry the library pre-fills.
///
/// `new_with_db` registers four demo names (`va_orders`, `va_users`, `ta_orders`,
/// `ta_users`) for its tests. The `OptimizedView` ticket reads a registered view with no
/// row scoping (#716), so a database that happened to have such a view served it to every
/// Flight principal. The server serves exactly what its operator declares in
/// `flight_views` ([`register_flight_views`]), and nothing by default.
/// Register the operator's `flight_views` for the `OptimizedView` ticket.
///
/// Every Flight-authenticated principal can read a registered view **whole**: the path
/// applies no row policy, field gate or authorizer (#716). Declare only views whose
/// entire contents every such principal may read. Each is typed by sampling one row; a
/// view that cannot be read or is empty at boot is not served (logged). Returns the views
/// served.
pub async
/// Apply the operator's Upload allow-list, leaving `Upload` disabled when empty.
///
/// An empty list must stay `None` on the service, not `Some(∅)`: both refuse every
/// table, but only `None` reports "Upload is disabled" rather than "not permitted
/// for this table", and that is the message that tells an operator they configured
/// nothing.
/// Create an Arrow Flight service backed by the fraiseql-wire streaming adapter.
///
/// Requires both the `arrow` and `wire-backend` features.