Skip to content

Commit de5149c

Browse files
author
g2px1
committed
updated docs and removed health checker
1 parent 6f0886a commit de5149c

11 files changed

Lines changed: 327 additions & 218 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,7 @@ file(GLOB_RECURSE UPQ_SOURCES CONFIGURE_DEPENDS
4545
${CMAKE_CURRENT_SOURCE_DIR}/src/upq/*.cpp
4646
)
4747

48-
add_library(upq ${UPQ_SOURCES} ${UPQ_HEADERS}
49-
include/upq/PgNotificationMultiplexer.h
50-
include/upq/PgHealthChecker.h
51-
src/upq/PgHealthChecker.cpp
52-
include/upq/PgReflect.h)
48+
add_library(upq ${UPQ_SOURCES} ${UPQ_HEADERS})
5349
add_library(usub::upq ALIAS upq)
5450

5551
target_include_directories(upq

docs/index.md

Lines changed: 42 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,53 @@ It’s designed for **uvent**, built around coroutines, and avoids libpqxx and b
1010
- Coroutine-based queries
1111
- Clean separation between pool, connection, and transaction
1212
- Minimal allocations and zero unnecessary copies
13+
- Opt-in reflection for ergonomic reads/writes (zero boilerplate, positional mapping)
1314

1415
## Components
1516

16-
| Component | Purpose |
17-
|-----------------------|----------------------------------------------------|
18-
| **PgPool** | Global connection pool, manages `PGconn` instances |
19-
| **PgConnectionLibpq** | Async low-level PostgreSQL connection wrapper |
20-
| **PgTransaction** | Transaction wrapper built on pooled connections |
21-
| **QueryResult** | Lightweight query result container |
17+
| Component | Purpose |
18+
|-----------------------|-----------------------------------------------------------|
19+
| **PgPool** | Global connection pool, manages `PGconn` instances |
20+
| **PgConnectionLibpq** | Async low-level PostgreSQL connection wrapper |
21+
| **PgTransaction** | Transaction wrapper built on pooled connections |
22+
| **QueryResult** | Lightweight query result container |
23+
| **PgReflect** | Header-only reflect helpers and mappers (positional only) |
24+
25+
## Features
26+
27+
- **Reflect-aware SELECT**`std::vector<T>` / `std::optional<T>`
28+
Positional mapping: column order in `SELECT` must match the field order of `T` (or tuple elements).
29+
- **Reflect-aware params** for `INSERT/UPDATE`:
30+
- Aggregates/tuples are expanded into multiple `$1..$N` parameters.
31+
- Containers (`std::vector`, `std::array`, `T[N]`, `initializer_list`) are sent as a single typed PostgreSQL array
32+
parameter.
33+
- `std::optional<T>` maps to `NULL` or a value.
34+
- Stays close to libpq semantics; no hidden background threads, no magic.
35+
36+
**Tiny example**
37+
38+
```cpp
39+
struct User { int64_t id; std::string name; std::optional<std::string> password; };
40+
41+
// Read many
42+
auto users = co_await pool.query_reflect<User>(
43+
"SELECT id, name, password FROM users ORDER BY id LIMIT 100"
44+
);
45+
46+
// Insert from aggregate
47+
struct NewUser { std::string name; std::optional<std::string> password; std::vector<int> roles; };
48+
NewUser nu{"alice", std::nullopt, {1,2}};
49+
co_await pool.exec_reflect(
50+
"INSERT INTO users(name, password, roles) VALUES ($1,$2,$3)",
51+
nu
52+
);
53+
```
2254

2355
## What it *doesn’t* do
2456

25-
- No ORM or reflection mapping
26-
- No query builders or migration tools
27-
- No automatic reconnection or retry logic
28-
- No external dependencies besides `libpq` and `uvent`
57+
* No ORM
58+
* No query builders or migration tools
59+
* No automatic reconnection or retry logic
60+
* No external dependencies besides `libpq` and `uvent`
2961

3062
The philosophy: **use coroutines, keep it minimal, and let the compiler inline everything**.

docs/pool.md

Lines changed: 72 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -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

142209
All `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+
```

docs/quickstart.md

Lines changed: 71 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ task::Awaitable<void> create_schema()
4343
"CREATE TABLE IF NOT EXISTS users("
4444
"id SERIAL PRIMARY KEY,"
4545
"name TEXT,"
46-
"password TEXT);"
46+
"password TEXT,"
47+
"roles INT[],"
48+
"tags TEXT[]);"
4749
);
4850

4951
if (!res.ok)
@@ -111,9 +113,75 @@ task::Awaitable<void> get_user(int user_id)
111113

112114
---
113115

114-
## 5. Diagnostics
116+
## 5. Reflect-based queries (struct mapping)
115117

116-
Every query now returns structured error information:
118+
Reflection allows automatic binding of aggregates and tuples to query parameters,
119+
and automatic mapping of query results into structs.
120+
121+
### SELECT → `std::vector<T>` or `std::optional<T>`
122+
123+
```cpp
124+
struct UserRow
125+
{
126+
int64_t id;
127+
std::string name;
128+
std::optional<std::string> password;
129+
std::vector<int> roles;
130+
std::vector<std::string> tags;
131+
};
132+
133+
task::Awaitable<void> get_all()
134+
{
135+
auto& pool = usub::pg::PgPool::instance();
136+
137+
auto rows = co_await pool.query_reflect<UserRow>(
138+
"SELECT id, name, password, roles, tags FROM users ORDER BY id;"
139+
);
140+
141+
for (auto& r : rows)
142+
std::cout << "user=" << r.name << "\n";
143+
co_return;
144+
}
145+
```
146+
147+
### Aggregate → parameters
148+
149+
```cpp
150+
struct NewUser
151+
{
152+
std::string name;
153+
std::optional<std::string> password;
154+
std::vector<int> roles;
155+
std::vector<std::string> tags;
156+
};
157+
158+
task::Awaitable<void> insert_user()
159+
{
160+
NewUser u{ "bob", std::nullopt, {1, 2}, {"vip"} };
161+
162+
auto res = co_await usub::pg::PgPool::instance().exec_reflect(
163+
"INSERT INTO users(name, password, roles, tags) VALUES ($1,$2,$3,$4);",
164+
u
165+
);
166+
167+
if (!res.ok)
168+
std::cout << "Insert failed: " << res.error << "\n";
169+
co_return;
170+
}
171+
```
172+
173+
### Rules
174+
175+
* Field order in `SELECT` must match the order of members in the struct.
176+
* `std::optional<T>` → NULL or value.
177+
* Containers (`vector`, `array`, etc.) → PostgreSQL arrays.
178+
* Aggregate/tuple expands into multiple `$1..$N` parameters.
179+
180+
---
181+
182+
## 6. Diagnostics
183+
184+
Every query returns structured error information:
117185

118186
```cpp
119187
auto res = co_await pool.query_awaitable("SELECT * FROM nonexistent;");

docs/results.md

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,44 @@ Each type includes consistent fields: `ok`, `code`, `error`, and `err_detail`.
1313

1414
---
1515

16+
## Reflect integration
17+
18+
When using the reflect-based API (`query_reflect`, `query_reflect_one`, `exec_reflect`,
19+
`select_reflect`, `select_one_reflect`), you usually **don’t access `QueryResult` directly**.
20+
21+
Instead, data is automatically mapped:
22+
23+
| Operation | Return type | Description |
24+
|---------------------------|--------------------|--------------------------------------------------------------|
25+
| `query_reflect<T>()` | `std::vector<T>` | Maps rows positionally to struct/tuple fields |
26+
| `query_reflect_one<T>()` | `std::optional<T>` | Returns one row or `std::nullopt` |
27+
| `exec_reflect()` | `QueryResult` | Executes `INSERT/UPDATE` using aggregate or tuple parameters |
28+
| `select_reflect<T>()` | `std::vector<T>` | Transactional SELECT → struct list |
29+
| `select_one_reflect<T>()` | `std::optional<T>` | Transactional SELECT single row |
30+
31+
### Example
32+
33+
```cpp
34+
struct User {
35+
int64_t id;
36+
std::string name;
37+
std::optional<std::string> password;
38+
};
39+
40+
auto users = co_await pool.query_reflect<User>(
41+
"SELECT id, name, password FROM users;"
42+
);
43+
44+
for (auto& u : users)
45+
std::cout << "id=" << u.id << " name=" << u.name << "\n";
46+
```
47+
48+
Under the hood, the library still produces an internal `QueryResult`
49+
and uses it to construct your mapped objects. For debugging or mixed workflows,
50+
you can still call `query_awaitable()` to access raw result rows.
51+
52+
---
53+
1654
## PgErrorCode
1755

1856
Every result type references `PgErrorCode` for classification:
@@ -32,7 +70,7 @@ enum class PgErrorCode : uint32_t {
3270
AwaitCanceled,
3371
Unknown
3472
};
35-
````
73+
```
3674
3775
**Meaning:**
3876
@@ -264,4 +302,6 @@ struct PgCursorChunk
264302
* Use `empty()` to detect successful queries with no rows.
265303
* Use `has_rows()` for success with results.
266304
* `rows_valid == false` → truncated/unsafe result.
267-
* No exceptions; all information is explicit and type-safe.
305+
* No exceptions; all information is explicit and type-safe.
306+
307+
```

docs/roadmap.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ CREATE TABLE cron_jobs
8383
last_run timestamptz,
8484
query text
8585
);
86-
```
86+
````
8787

8888
Each job is periodically executed by a background coroutine.
8989

@@ -120,7 +120,7 @@ Migration system using SQL files (`migrations/*.sql`) with transactional apply/r
120120
* Fully coroutine-compatible.
121121

122122
**Goal:** schema management built into runtime.
123-
**Status:** Planned after TypeRegistry.
123+
**Status:** Planned after TypeRegistry & ReflectSystem.
124124

125125
---
126126

@@ -157,7 +157,7 @@ Foundation for future ORM-style or codegen tooling.
157157

158158
---
159159

160-
## 10. PgTestHarness — Transactional Testing Utility
160+
## 11. PgTestHarness — Transactional Testing Utility
161161

162162
Test helper for SQL regression and snapshot testing.
163163

@@ -178,7 +178,7 @@ Test helper for SQL regression and snapshot testing.
178178
|-------|----------------------------------------------|----------------------------------|
179179
| **1** | ReconnectSupervisor, StatsRegistry | Stability & observability |
180180
| **2** | PreparedStatementRegistry, ChannelDispatcher | Throughput & reactive events |
181-
| **3** | CronScheduler, TypeRegistry | Automation & binary performance |
181+
| **3** | CronScheduler, TypeRegistry, ReflectSystem | Automation & binary performance |
182182
| **4** | Migrator, TransactionScope | Schema control & DX improvements |
183183
| **5** | Introspector, TestHarness | Ecosystem & developer tooling |
184184

0 commit comments

Comments
 (0)