@@ -16,6 +16,7 @@ Primary high-level entrypoint for queries in **upq**.
1616- Safe recycling with dirty-connection handling
1717- Structured errors (` QueryResult ` )
1818- Optional periodic health checks (` PgPoolHealthConfig ` )
19+ - Reflect-aware SELECT/EXEC helpers (` query_reflect ` , ` exec_reflect ` )
1920- Compatible with high-volume ops exposed on ` PgConnectionLibpq ` :
2021 - ` COPY ... FROM STDIN ` / ` COPY ... TO STDOUT `
2122 - server-side cursors with chunked fetch
@@ -137,7 +138,73 @@ Both APIs return `QueryResult` with `ok`, `code`, `error`, `rows`, `rows_valid`.
137138
138139---
139140
140- ### Pipelined query execution
141+ ## Reflect-aware API
142+
143+ Reflection-based helpers provide direct mapping between SQL and C++ aggregates.
144+
145+ ### SELECT → ` std::vector<T> ` or ` std::optional<T> `
146+
147+ ``` cpp
148+ struct UserRow {
149+ int64_t id;
150+ std::string name;
151+ std::optional< std::string > password;
152+ std::vector<int > roles;
153+ std::vector< std::string > tags;
154+ };
155+
156+ // Multiple rows
157+ auto users = co_await pool.query_reflect<UserRow >(
158+ "SELECT id, name, password, roles, tags FROM users ORDER BY id;"
159+ );
160+
161+ // Single row
162+ auto one = co_await pool.query_reflect_one<UserRow>(
163+ " SELECT id, name, password, roles, tags FROM users WHERE id = 1;"
164+ );
165+ ```
166+
167+ ### INSERT/UPDATE from aggregates
168+
169+ ``` cpp
170+ struct NewUser {
171+ std::string name;
172+ std::optional< std::string > password;
173+ std::vector<int > roles;
174+ std::vector< std::string > tags;
175+ };
176+
177+ NewUser nu{ "bob", std::nullopt, {1, 2}, {"vip"} };
178+
179+ auto res = co_await pool.exec_reflect(
180+ "INSERT INTO users(name, password, roles, tags) VALUES ($1,$2,$3,$4);",
181+ nu
182+ );
183+ ```
184+
185+ ### API summary
186+
187+ | Method | Description |
188+ | --------------------------------------| --------------------------------------------------------------|
189+ | ` query_reflect<T>(sql) ` | SELECT → ` std::vector<T> ` |
190+ | ` query_reflect_one<T>(sql) ` | SELECT one → ` std::optional<T> ` |
191+ | ` exec_reflect(sql, obj) ` | Executes using fields of an aggregate or tuple as parameters |
192+ | ` query_on_reflect<T>(conn, sql) ` | Same as above, bound to existing connection |
193+ | ` query_on_reflect_one<T>(conn, sql) ` | Single-row variant |
194+ | ` exec_reflect_on(conn, sql, obj) ` | Aggregate parameter execution on given connection |
195+
196+ ### Mapping rules
197+
198+ * Field order in ` SELECT ` must match member order in the struct.
199+ * ` std::optional<T> ` → NULL or value.
200+ * Containers (` vector ` , ` array ` , etc.) → PostgreSQL arrays.
201+ * Aggregate or tuple expands into multiple ` $1..$N ` parameters.
202+ * Non-aggregate containers are sent as a single typed array parameter.
203+ * Pointers (except ` char* ` ) are unsupported.
204+
205+ ---
206+
207+ ## Pipelined query execution
141208
142209All ` query*() ` methods support ** PostgreSQL pipelining** via a compile-time template flag.
143210
@@ -253,15 +320,6 @@ task::Awaitable<void> cursor_stream_example()
253320
254321` PgCursorChunk ` is described in ` results.md ` .
255322
256- This approach:
257-
258- * Opens a cursor (` DECLARE ... CURSOR FOR <query> ` )
259- * Fetches N rows at a time using ` FETCH FORWARD <N> FROM <cursor> `
260- * Closes cursor and commits
261-
262- The pool’s recycling logic ensures that after this multi-step usage, the connection is either safely recycled (if
263- drained) or retired and replaced.
264-
265323---
266324
267325## Error model
@@ -288,5 +346,8 @@ Pool self-heals by retiring broken connections and opening new ones on demand.
288346| Dirty handling | Drain on async release, otherwise retire |
289347| Health checks | Optional periodic probes with ` checked/alive/reconnected ` stats |
290348| COPY & cursors | Via ` PgConnectionLibpq ` , safe to return to pool |
349+ | Reflect API | ` query_reflect ` / ` exec_reflect ` for aggregates & structs |
291350| Structured errors | Clear non-exceptional failure reporting |
292- | Pipeline execution | Compile-time toggle ` <true> ` for batched async queries |
351+ | Pipeline execution | Compile-time toggle ` <true> ` for batched async queries |
352+
353+ ```
0 commit comments