From 006bce238ba592ff4443da72557c5fc9a7cd4bee Mon Sep 17 00:00:00 2001 From: "Dr. Patrick Urbanke" Date: Sun, 27 Apr 2025 05:37:37 +0200 Subject: [PATCH] Started writing documentation --- README.md | 14 ++-- docs/README.md | 3 + docs/defining_tables.md | 134 ++++++++++++++++++++++++++++++++++ include/sqlgen/PrimaryKey.hpp | 3 + 4 files changed, 149 insertions(+), 5 deletions(-) create mode 100644 docs/README.md create mode 100644 docs/defining_tables.md diff --git a/README.md b/README.md index 3735050..d082263 100644 --- a/README.md +++ b/README.md @@ -7,11 +7,11 @@ sqlgen is closely integrated with our sister project [reflect-cpp](https://githu ## Simple example -Here is how you create a simple sqlite database +Here is how you connect to a PostgreSQL database and insert some data: ```cpp -#include +#include struct People { std::string first_name; @@ -24,7 +24,11 @@ const auto people = std::vector({ .last_name = "Simpson", .age = 45}}); -const auto conn = sqlgen::sqlite::connect("example.db"); +const auto credentials = sqlgen::postgres::Credentials{ + .user = "...", .password = "...", ... +}; + +const auto conn = sqlgen::postgres::connect(credentials); // Will automatically create a table called 'People' // with the columns 'first_name', 'last_name' and 'age', @@ -41,9 +45,9 @@ and print the results as a JSON: ```cpp #include // reflect-cpp -#include +#include -const auto conn = sqlgen::sqlite::connect("example.db"); +const auto conn = sqlgen::postgres::connect(credentials); const sqlgen::Result> result = sqlgen::read>(conn); diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..c1992c9 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,3 @@ +# Documentation + +[Defining tables](definining_tables.md) - Explains how you can define tables using C++ structs. diff --git a/docs/defining_tables.md b/docs/defining_tables.md new file mode 100644 index 0000000..ed9635e --- /dev/null +++ b/docs/defining_tables.md @@ -0,0 +1,134 @@ +# Defining tables + +In sqlgen, tables are defined using C++ structs. In its simplest +form, this can look like this: + +```cpp +struct People { + std::string first_name; + std::string last_name; + uint age; +}; +``` + +When you then try to write a vector of `People`, +the table will be automatically created, if necessary: + +```cpp +const auto people = std::vector(...); + +const auto result = sqlgen::write(conn, people); +``` + +In other words, `sqlgen::write(...)` might call the following SQL +statement under-the-hood: + +```sql +CREATE TABLE IF NOT EXISTS "People"("first_name" TEXT NOT NULL, + "last_name" TEXT NOT NULL, + "age" INTEGER NOT NULL); + +``` + +(As with all SQL examples, the exact code will vary between dialects.) + +## Custom table names + +Obviously, it is very convenient to just infer the table name +from the name of the struct. But sometimes we don't want that. +So we can do something like this: + +```cpp +struct People { + constexpr static const char* tablename = "PEOPLE"; + + std::string first_name; + std::string last_name; + uint age; +}; +``` + +Now, the generated SQL code will look like this: + +```cpp +CREATE TABLE IF NOT EXISTS "PEOPLE"("first_name" TEXT NOT NULL, + "last_name" TEXT NOT NULL, + "age" INTEGER NOT NULL); +``` + +## Custom schemata + +In a similar manner, you can embed your table in a schema: + +```cpp +struct People { + constexpr static const char* schema = "my_schema"; + + std::string first_name; + std::string last_name; + uint age; +}; +``` + +Now, the generated SQL code will look like this: + +```cpp +CREATE TABLE IF NOT EXISTS "my_schema"."People"("first_name" TEXT NOT NULL, + "last_name" TEXT NOT NULL, + "age" INTEGER NOT NULL); +``` + +## Primary keys + +Of course, you would also like to set primary keys on your +tables. This is possible as well: + +```cpp +struct People { + rfl::PrimaryKey first_name; + std::string last_name; + uint age; +}; +``` + +Now, the generated SQL code will look something like this: + +```sql +CREATE TABLE IF NOT EXISTS "People"("first_name" TEXT NOT NULL, + "last_name" TEXT NOT NULL, + "age" INTEGER NOT NULL, + PRIMARY_KEY("first_name")); +``` + +`rfl::PrimaryKey<...>` is a simple wrapper. You can simply assign to it as follows: + +```cpp +const auto person = People{.first_name = "Homer", ...}; +``` + +And you can retrieve the underlying value using any of the following: + +```cpp +person.first_name(); +person.first_name.get(); +person.first_name.value(); +``` + +## Nullable types + +As we have seen, all columns are non-nullable by default. But what if we do want +nullability? + +As with reflect-cpp, `std::optional`, `std::shared_ptr` and `std::unique_ptr` are interpreted +as nullable types. So you can create nullable fields as follows: + +```cpp +struct People { + rfl::PrimaryKey first_name; + std::string last_name; + std::optional age; +}; +``` + +You can induce the NULL value by passing `std::nullopt` to age and NULL values +will be translated to `std::nullopt`. diff --git a/include/sqlgen/PrimaryKey.hpp b/include/sqlgen/PrimaryKey.hpp index 8dd1929..92b4681 100644 --- a/include/sqlgen/PrimaryKey.hpp +++ b/include/sqlgen/PrimaryKey.hpp @@ -43,6 +43,9 @@ struct PrimaryKey { ~PrimaryKey() = default; + /// Returns the underlying object. + ReflectionType& get() { return value_; } + /// Returns the underlying object. const ReflectionType& get() const { return value_; }