Added SQL snippets to the documentation

This commit is contained in:
Dr. Patrick Urbanke
2025-05-09 18:17:18 +02:00
parent 2bdb446aa2
commit 650dd8a026
2 changed files with 95 additions and 4 deletions

View File

@@ -11,18 +11,26 @@ Read all rows from a table into a container (e.g., `std::vector<Person>`):
```cpp
const auto conn = sqlgen::sqlite::connect("database.db");
const auto people = sqlgen::read<std::vector<Person>>(conn).value();
const std::vector<Person> people = sqlgen::read<std::vector<Person>>(conn).value();
```
This generates the following SQL:
```sql
SELECT "id", "first_name", "last_name", "age"
FROM "Person";
```
Note that `conn` is actually a connection wrapped into an `sqlgen::Result<...>`.
This means you can use monadic error handling and fit this into a single line:
```cpp
// sqlgen::Result<std::vector<Person>>
const auto people = sqlgen::sqlite::connect("database.db").and_then(
sqlgen::read<std::vector<Person>>).value();
sqlgen::read<std::vector<Person>>);
```
Please refer to the documentation on `sqlgen::Result<...>` for more information.
Please refer to the documentation on `sqlgen::Result<...>` for more information on error handling.
### With `where` clause
@@ -37,6 +45,16 @@ const auto query = sqlgen::read<std::vector<Person>> |
const auto minors = query(conn).value();
```
This generates the following SQL:
```sql
SELECT "id", "first_name", "last_name", "age"
FROM "Person"
WHERE
("age" < 18) AND
("first_name" != 'Hugo');
```
Note that `"..."_c` refers to the name of the column. If such a field does not
exists on the struct `Person`, the code will fail to compile.
@@ -48,7 +66,8 @@ using namespace sqlgen;
const auto query = sqlgen::read<std::vector<Person>> |
where("age"_c < 18 and "first_name"_c != "Hugo");
const auto minors = sqlite::connect("database.db").and_then(query).value();
// sqlgen::Result<std::vector<Person>>
const auto minors = sqlite::connect("database.db").and_then(query);
```
### With `order_by` and `limit`
@@ -65,17 +84,43 @@ const auto query = sqlgen::read<std::vector<Person>> |
const auto youngest_two = query(conn).value();
```
This generates the following SQL:
```sql
SELECT "id", "first_name", "last_name", "age"
FROM "Person"
ORDER BY "age"
LIMIT 2;
```
### With ranges
Read results as a lazy range:
```cpp
const auto people_range = sqlgen::read<sqlgen::Range<Person>>(conn).value();
for (const sqlgen::Result<Person>& person : people_range) {
// process result
}
```
`sqlgen::Range<T>` satisfies the `std::ranges::input_range` concept, making it compatible with C++20 ranges and views. This allows for memory-efficient iteration through database results and enables composition with other range operations:
```cpp
using namespace std::ranges::views;
// Transform range results
const auto first_names = people_range | transform([](const sqlgen::Result<Person>& r) {
return r.value().first_name;
});
// Filter range results
const auto adults = people_range | filter([](const sqlgen::Result<Person>& r) {
return r && r->age >= 18;
});
```
## Example: Full Query Composition
```cpp
@@ -89,6 +134,19 @@ const auto query = sqlgen::read<std::vector<Person>> |
const auto adults = query(conn).value();
```
This generates the following SQL:
```sql
SELECT "id", "first_name", "last_name", "age"
FROM "Person"
WHERE
("age" >= 18)
ORDER BY
"last_name",
"first_name" DESC
LIMIT 10;
```
It is strongly recommended that you use `using namespace sqlgen`. However,
if you do not want to do that, you can rewrite the example above as follows:

View File

@@ -19,6 +19,32 @@ const auto conn = sqlgen::sqlite::connect();
sqlgen::write(conn, people);
```
This generates the following SQL for PostgreSQL:
```sql
CREATE TABLE IF NOT EXISTS "Person" (
"id" INTEGER,
"first_name" TEXT NOT NULL,
"last_name" TEXT NOT NULL,
"age" INTEGER NOT NULL
);
COPY "Person"("id", "first_name", "last_name", "age") FROM STDIN WITH DELIMITER '\t' NULL '\e' QUOTE '\a';
```
For other dialects like SQLite, it uses prepared INSERT statements instead:
```sql
CREATE TABLE IF NOT EXISTS "Person" (
"id" INTEGER,
"first_name" TEXT NOT NULL,
"last_name" TEXT NOT NULL,
"age" INTEGER NOT NULL
);
INSERT INTO "Person" ("id", "first_name", "last_name", "age") VALUES (?, ?, ?, ?);
```
### With Result<Ref<Connection>>
Handle connection creation and writing in a single chain:
@@ -29,6 +55,8 @@ sqlgen::sqlite::connect("database.db")
.value();
```
This generates the same SQL as above, adapted to the specific database dialect being used.
### With Iterators
Write a range of objects using iterators:
@@ -38,6 +66,8 @@ std::vector<Person> people = /* ... */;
sqlgen::write(conn, people.begin(), people.end());
```
This also generates the same SQL, adapted to the specific database dialect.
## How It Works
The `write` function performs the following operations in sequence:
@@ -56,4 +86,7 @@ The `write` function performs the following operations in sequence:
1. Takes a connection reference and iterators
2. Takes a `Result<Ref<Connection>>` and iterators
3. Takes a connection and a container directly
- The SQL generation adapts to the database dialect:
- PostgreSQL uses the efficient COPY command for bulk inserts
- Other dialects use prepared INSERT statements