diff --git a/docs/reading.md b/docs/reading.md index c6b7d20..83b3b90 100644 --- a/docs/reading.md +++ b/docs/reading.md @@ -11,18 +11,26 @@ Read all rows from a table into a container (e.g., `std::vector`): ```cpp const auto conn = sqlgen::sqlite::connect("database.db"); -const auto people = sqlgen::read>(conn).value(); +const std::vector people = sqlgen::read>(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> const auto people = sqlgen::sqlite::connect("database.db").and_then( - sqlgen::read>).value(); + sqlgen::read>); ``` -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> | 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> | where("age"_c < 18 and "first_name"_c != "Hugo"); -const auto minors = sqlite::connect("database.db").and_then(query).value(); +// sqlgen::Result> +const auto minors = sqlite::connect("database.db").and_then(query); ``` ### With `order_by` and `limit` @@ -65,17 +84,43 @@ const auto query = sqlgen::read> | 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>(conn).value(); + for (const sqlgen::Result& person : people_range) { // process result } ``` +`sqlgen::Range` 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& r) { + return r.value().first_name; +}); + +// Filter range results +const auto adults = people_range | filter([](const sqlgen::Result& r) { + return r && r->age >= 18; +}); +``` + ## Example: Full Query Composition ```cpp @@ -89,6 +134,19 @@ const auto query = sqlgen::read> | 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: diff --git a/docs/writing.md b/docs/writing.md index a92b882..5282d99 100644 --- a/docs/writing.md +++ b/docs/writing.md @@ -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> 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 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>` 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