Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions .github/workflows/docs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
name: Deploy documentation to Github Pages

on:
push:
branches: ["main"]

# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
permissions:
contents: read
pages: write
id-token: write

# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
concurrency:
group: "pages"
cancel-in-progress: false

# Default to bash
defaults:
run:
shell: bash

jobs:
# Build job
build:
runs-on: ubuntu-latest
env:
HUGO_VERSION: 0.156.0
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # fetch all history for .GitInfo and .Lastmod
submodules: recursive
- name: Setup Go
uses: actions/setup-go@v5
with:
go-version: '1.26'
- name: Setup Pages
id: pages
uses: actions/configure-pages@v4
- name: Setup Hugo
run: |
wget -O ${{ runner.temp }}/hugo.deb https://github.com/gohugoio/hugo/releases/download/v${HUGO_VERSION}/hugo_extended_${HUGO_VERSION}_linux-amd64.deb \
&& sudo dpkg -i ${{ runner.temp }}/hugo.deb
- name: Build with Hugo
env:
# For maximum backward compatibility with Hugo modules
HUGO_ENVIRONMENT: production
HUGO_ENV: production
run: |
hugo --gc --minify --baseURL "${{ steps.pages.outputs.base_url }}/"
working-directory: docs
- name: Upload artifact
uses: actions/upload-pages-artifact@v3
with:
path: ./docs/public

# Deployment job
deploy:
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
needs: build
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
# Fortran module files
*.mod
*.smod
!docs/**/*.mod

# Compiled Static libraries
*.lai
Expand All @@ -36,3 +37,6 @@ build/
vcpkg_installed/
CMakeUserPresets.json

docs/public/
*.lock

110 changes: 56 additions & 54 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# ![C++](https://img.shields.io/badge/c++-%2300599C.svg?style=for-the-badge&logo=c%2B%2B&logoColor=white) sqlgen
# ![C++](https://img.shields.io/badge/c++-%2300599C.svg?style=for-the-badge&logo=c%2B%2B&logoColor=white) sqlgen

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Maintenance](https://img.shields.io/badge/Maintained%3F-yes-green.svg)](https://github.com/getml/reflect-cpp/graphs/commit-activity)
Expand All @@ -10,6 +10,8 @@

**📖 Documentation**: [Click here](docs/README.md)

![image](docs/content/sqlgen.png)

**sqlgen** is a modern, type-safe ORM and SQL query generator for C++20, inspired by Python's [SQLAlchemy](https://github.com/sqlalchemy/sqlalchemy)/[SQLModel](https://github.com/fastapi/sqlmodel) and Rust's [Diesel](https://github.com/diesel-rs/diesel). It provides a fluent, composable interface for database operations with compile-time type checking and SQL injection protection.

sqlgen is based on and tightly integrated with [reflect-cpp](https://github.com/getml/reflect-cpp), a C++-20 library for fast serialization, deserialization and validation using reflection, similar to pydantic in Python, serde in Rust, encoding in Go or aeson in Haskell.
Expand All @@ -24,9 +26,9 @@ Together, reflect-cpp and sqlgen enable reliable and efficient ETL pipelines.
- 🚀 **High Performance**: Efficient batch operations and prepared statements
- 📦 **Modern C++**: Leverages C++20 features for a clean, expressive API
- 🔌 **Multiple Backends**: Support for PostgreSQL and SQLite
- 🔍 **Reflection Integration**: Seamless integration with [reflect-cpp](https://github.com/getml/reflect-cpp)
- 🔍 **Reflection Integration**: Seamless integration with [reflect-cpp](https://github.com/getml/reflect-cpp)

## Supported databases
## Supported databases

The following table lists the databases currently supported by sqlgen and the underlying libraries used:

Expand All @@ -41,7 +43,7 @@ The following table lists the databases currently supported by sqlgen and the un

### Install using vcpkg or Conan

You can install the latest release of sqlgen
You can install the latest release of sqlgen
using either [vcpkg](https://vcpkg.io/en/package/sqlgen) or [Conan](https://conan.io/center/recipes/sqlgen).

### Build using vcpkg
Expand All @@ -68,11 +70,11 @@ cmake --build build -j 4 # gcc, clang
cmake --build build --config Release -j 4 # MSVC
```

This will build the static library. To build the shared library
This will build the static library. To build the shared library
add `-DBUILD_SHARED_LIBS=ON -DVCPKG_TARGET_TRIPLET=...` to the first line.
Run `./vcpkg/vcpkg help triplets` to view all supported triplets.
Common triplets for shared libraries are `x64-linux-dynamic`,
`arm64-osx-dynamic` or `x64-osx-dynamic`.
Run `./vcpkg/vcpkg help triplets` to view all supported triplets.
Common triplets for shared libraries are `x64-linux-dynamic`,
`arm64-osx-dynamic` or `x64-osx-dynamic`.

Add `-DSQLGEN_MYSQL=ON` to support MySQL/MariaDB. Add `-DSQLGEN_DUCKDB=ON` to support DuckDB.

Expand Down Expand Up @@ -102,7 +104,7 @@ conan build . --build=missing -s compiler.cppstd=gnu20
```

This will build the static library. To build the shared library,
add `-o */*:shared=True`.
add `-o */*:shared=True`.

Add `-o sqlgen/*:with_mysql=True` to support MySQL/MariaDB.

Expand Down Expand Up @@ -130,14 +132,14 @@ struct User {
int main() {
// Connect to SQLite database
const auto conn = sqlgen::sqlite::connect("test.db");

// Create and insert a user
const auto user = User{.name = "John", .age = 30};
sqlgen::write(conn, user);

// Read all users
const auto users = sqlgen::read<std::vector<User>>(conn).value();

for (const auto& u : users) {
std::cout << u.name << " is " << u.age << " years old\n";
}
Expand Down Expand Up @@ -218,7 +220,7 @@ CREATE TABLE IF NOT EXISTS "Person" (
"email" TEXT
);

INSERT INTO "Person" ("first_name", "last_name", "age", "email")
INSERT INTO "Person" ("first_name", "last_name", "age", "email")
VALUES (?, ?, ?, ?);
COMMIT;
```
Expand Down Expand Up @@ -285,7 +287,7 @@ const std::vector<Children> children = get_children(conn).value();

Generated SQL:
```sql
SELECT
SELECT
"last_name",
COUNT(*) as "num_children",
MAX("age") as "max_age",
Expand Down Expand Up @@ -322,15 +324,15 @@ const auto get_people =

Generated SQL:
```sql
SELECT t1."last_name" AS "last_name",
t1."first_name" AS "first_name_parent",
t3."first_name" AS "first_name_child",
t1."age" - t3."age" AS "parent_age_at_birth"
FROM "Person" t1
INNER JOIN "Relationship" t2
ON t1."id" = t2."parent_id"
SELECT t1."last_name" AS "last_name",
t1."first_name" AS "first_name_parent",
t3."first_name" AS "first_name_child",
t1."age" - t3."age" AS "parent_age_at_birth"
FROM "Person" t1
INNER JOIN "Relationship" t2
ON t1."id" = t2."parent_id"
INNER JOIN "Person" t3
ON t3."id" = t2."child_id"
ON t3."id" = t2."child_id"
ORDER BY t1."id", t3."id"
```

Expand Down Expand Up @@ -363,26 +365,26 @@ const auto get_people =
("age"_t1 - "age"_t2) | as<"parent_age_at_birth">) |
inner_join<"t2">(
get_children, // Use the subquery as the source
"id"_t1 == "id"_t2) |
"id"_t1 == "id"_t2) |
order_by("id"_t1, "id"_t2) | to<std::vector<ParentAndChild>>;
```

Generated SQL:
```sql
SELECT t1."last_name" AS "last_name",
t1."first_name" AS "first_name_parent",
t2."first_name" AS "first_name_child",
t1."age" - t2."age" AS "parent_age_at_birth"
FROM "Person" t1
SELECT t1."last_name" AS "last_name",
t1."first_name" AS "first_name_parent",
t2."first_name" AS "first_name_child",
t1."age" - t2."age" AS "parent_age_at_birth"
FROM "Person" t1
INNER JOIN (
SELECT t1."parent_id" AS "id",
t2."first_name" AS "first_name",
t2."age" AS "age"
FROM "Relationship" t1
SELECT t1."parent_id" AS "id",
t2."first_name" AS "first_name",
t2."age" AS "age"
FROM "Relationship" t1
INNER JOIN "Person" t2
ON t2."id" = t1."child_id"
) t2
ON t1."id" = t2."id"
) t2
ON t1."id" = t2."id"
ORDER BY t1."id", t2."id"
```

Expand Down Expand Up @@ -412,27 +414,27 @@ const auto get_people = select_from<"t1">(
"last_name"_t1 | as<"last_name">,
"first_name"_t1 | as<"first_name_parent">,
"first_name"_t2 | as<"first_name_child">,
("age"_t1 - "age"_t2) | as<"parent_age_at_birth">) |
inner_join<Person, "t2">("id"_t1 == "id"_t2) |
("age"_t1 - "age"_t2) | as<"parent_age_at_birth">) |
inner_join<Person, "t2">("id"_t1 == "id"_t2) |
order_by("id"_t1, "id"_t2) | to<std::vector<ParentAndChild>>;
```

Generated SQL:
```sql
SELECT t1."last_name" AS "last_name",
t1."first_name" AS "first_name_parent",
t2."first_name" AS "first_name_child",
(t1."age") - (t2."age") AS "parent_age_at_birth"
SELECT t1."last_name" AS "last_name",
t1."first_name" AS "first_name_parent",
t2."first_name" AS "first_name_child",
(t1."age") - (t2."age") AS "parent_age_at_birth"
FROM (
SELECT t2."child_id" AS "id",
t1."first_name" AS "first_name",
t1."last_name" AS "last_name",
t1."age" AS "age"
FROM "Person" t1
SELECT t2."child_id" AS "id",
t1."first_name" AS "first_name",
t1."last_name" AS "last_name",
t1."age" AS "age"
FROM "Person" t1
INNER JOIN "Relationship" t2
ON t1."id" = t2."parent_id"
) t1
INNER JOIN "Person" t2
) t1
INNER JOIN "Person" t2
ON t1."id" = t2."id"
ORDER BY t1."id", t2."id"
```
Expand All @@ -446,26 +448,26 @@ sqlgen provides comprehensive compile-time checks and runtime protection:
const auto query = read<std::vector<Person>> |
where("color"_c == "blue");

// Compile-time error: Cannot compare column "age" to a string
// Compile-time error: Cannot compare column "age" to a string
const auto query = read<std::vector<Person>> |
where("age"_c == "Homer");

// Compile-time error: "age" must be aggregated or included in GROUP BY
// Compile-time error: "age" must be aggregated or included in GROUP BY
const auto query = select_from<Person>(
"last_name"_c,
"age"_c
"age"_c
) | group_by("last_name"_c);

// Compile-time error: Cannot add string and int
// Compile-time error: Cannot add string and int
const auto query = select_from<Person>(
"last_name"_c + "age"_c
"last_name"_c + "age"_c
);

// Runtime protection against SQL injection
std::vector<Person> get_people(const auto& conn,
std::vector<Person> get_people(const auto& conn,
const sqlgen::AlphaNumeric& first_name) {
using namespace sqlgen;
return (read<std::vector<Person>> |
return (read<std::vector<Person>> |
where("first_name"_c == first_name))(conn).value();
}

Expand Down
68 changes: 0 additions & 68 deletions docs/README.md

This file was deleted.

Loading
Loading