Skip to content

Commit 9d47a8a

Browse files
Merge branch 'main' into f/raii
2 parents b59e1b3 + 8568a83 commit 9d47a8a

114 files changed

Lines changed: 6608 additions & 12 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/workflows/windows-cxx20-vcpkg.yaml

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ jobs:
1414
- db: postgres
1515
- db: sqlite
1616
- db: mysql
17-
- db: headers
1817
name: "(windows-${{ matrix.db }})"
1918
concurrency:
2019
group: ci-${{ github.ref }}-windows-${{ matrix.db }}
@@ -34,11 +33,6 @@ jobs:
3433
core.exportVariable('ACTIONS_RUNTIME_TOKEN', process.env.ACTIONS_RUNTIME_TOKEN || '');
3534
- uses: ilammy/msvc-dev-cmd@v1
3635
- uses: lukka/run-vcpkg@v11
37-
- name: Compile
38-
if: matrix.db == 'headers'
39-
run: |
40-
cmake -S . -B build -DCMAKE_CXX_STANDARD=20 -DSQLGEN_CHECK_HEADERS=ON
41-
cmake --build build --config Release -j4
4236
- name: Compile
4337
if: matrix.db == 'postgres'
4438
run: |
@@ -55,6 +49,5 @@ jobs:
5549
cmake -S . -B build -DCMAKE_CXX_STANDARD=20 -DCMAKE_BUILD_TYPE=Release -DSQLGEN_BUILD_TESTS=ON -DSQLGEN_MYSQL=ON -DSQLGEN_POSTGRES=OFF -DSQLGEN_SQLITE3=OFF -DSQLGEN_BUILD_DRY_TESTS_ONLY=ON -DBUILD_SHARED_LIBS=ON -DVCPKG_TARGET_TRIPLET=x64-windows-release
5650
cmake --build build --config Release -j4
5751
- name: Run tests
58-
if: matrix.db != 'headers'
5952
run: |
6053
ctest --test-dir build --output-on-failure

CMakeLists.txt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
44

55
option(SQLGEN_BUILD_SHARED "Build shared library" ${BUILD_SHARED_LIBS})
66

7+
option(SQLGEN_DUCKDB "Enable DuckDB support" OFF)
8+
79
option(SQLGEN_MYSQL "Enable MySQL support" OFF)
810

911
option(SQLGEN_POSTGRES "Enable PostgreSQL support" ON) # enabled by default
@@ -30,6 +32,10 @@ if (SQLGEN_USE_VCPKG)
3032
list(APPEND VCPKG_MANIFEST_FEATURES "tests")
3133
endif()
3234

35+
if (SQLGEN_DUCKDB OR SQLGEN_CHECK_HEADERS)
36+
list(APPEND VCPKG_MANIFEST_FEATURES "duckdb")
37+
endif()
38+
3339
if (SQLGEN_MYSQL OR SQLGEN_CHECK_HEADERS)
3440
list(APPEND VCPKG_MANIFEST_FEATURES "mysql")
3541
endif()
@@ -89,6 +95,16 @@ target_include_directories(
8995
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
9096
$<INSTALL_INTERFACE:include>)
9197

98+
99+
if (SQLGEN_DUCKDB OR SQLGEN_CHECK_HEADERS)
100+
list(APPEND SQLGEN_SOURCES src/sqlgen_duckdb.cpp)
101+
if (NOT TARGET DuckDB)
102+
find_package(DuckDB REQUIRED)
103+
endif()
104+
target_link_libraries(sqlgen PUBLIC $<IF:$<TARGET_EXISTS:duckdb>,duckdb,duckdb_static>)
105+
endif()
106+
107+
92108
if(SQLGEN_MYSQL OR SQLGEN_CHECK_HEADERS)
93109
list(APPEND SQLGEN_SOURCES src/sqlgen_mysql.cpp)
94110
if (SQLGEN_USE_VCPKG)

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ The following table lists the databases currently supported by sqlgen and the un
3131

3232
| Database | Library | Version | License | Remarks |
3333
|---------------|--------------------------------------------------------------------------|--------------|---------------| -----------------------------------------------------|
34+
| DuckDB | [duckdb](https://github.com/duckdb/duckdb) | >= 1.4.1 | MIT | |
3435
| MySQL/MariaDB | [libmariadb](https://github.com/mariadb-corporation/mariadb-connector-c) | >= 3.4.5 | LGPL | |
3536
| PostgreSQL | [libpq](https://github.com/postgres/postgres) | >= 16.4 | PostgreSQL | Will work for all libpq-compatible databases |
3637
| sqlite | [sqlite](https://sqlite.org/index.html) | >= 3.49.1 | Public Domain | |

docs/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ Welcome to the sqlgen documentation. This guide provides detailed information ab
5959

6060
## Supported Databases
6161

62+
- [DuckDB](duckdb.md) - How to interact with DuckDB
6263
- [MySQL](mysql.md) - How to interact with MariaDB and MySQL
6364
- [PostgreSQL](postgres.md) - How to interact with PostgreSQL and compatible databases (Redshift, Aurora, Greenplum, CockroachDB, ...)
6465
- [SQLite](sqlite.md) - How to interact with SQLite3

docs/duckdb.md

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
∂# `sqlgen::duckdb`
2+
3+
The `sqlgen::duckdb` module provides a type-safe and efficient interface for interacting with DuckDB databases. It implements the core database operations through a connection-based API with support for prepared statements, transactions, and efficient data iteration.
4+
5+
## Usage
6+
7+
### Basic Connection
8+
9+
Create a connection to a DuckDB database:
10+
11+
```cpp
12+
// Connect to an in-memory database
13+
const auto conn = sqlgen::duckdb::connect();
14+
15+
// Connect to a file-based database
16+
const auto conn = sqlgen::duckdb::connect("database.db");
17+
```
18+
19+
The type of `conn` is `sqlgen::Result<sqlgen::Ref<sqlgen::duckdb::Connection>>`, which is useful for error handling:
20+
21+
```cpp
22+
// Handle connection errors
23+
const auto conn = sqlgen::duckdb::connect("database.db");
24+
if (!conn) {
25+
// Handle error...
26+
return;
27+
}
28+
29+
using namespace sqlgen;
30+
using namespace sqlgen::literals;
31+
32+
const auto query = sqlgen::read<std::vector<Person>> |
33+
where("age"_c < 18 and "first_name"_c != "Hugo");
34+
35+
// Use the connection
36+
const auto minors = query(conn);
37+
```
38+
39+
### Basic Operations
40+
41+
Write data to the database:
42+
43+
```cpp
44+
struct Person {
45+
sqlgen::PrimaryKey<uint32_t> id;
46+
std::string first_name;
47+
std::string last_name;
48+
int age;
49+
};
50+
51+
const auto people = std::vector<Person>{
52+
Person{.id = 0, .first_name = "Homer", .last_name = "Simpson", .age = 45},
53+
Person{.id = 1, .first_name = "Bart", .last_name = "Simpson", .age = 10},
54+
Person{.id = 2, .first_name = "Lisa", .last_name = "Simpson", .age = 8}
55+
};
56+
57+
// Write data to database
58+
const auto result = sqlgen::write(conn, people);
59+
```
60+
61+
Read data with filtering and ordering:
62+
63+
```cpp
64+
using namespace sqlgen;
65+
using namespace sqlgen::literals;
66+
67+
// Read all people ordered by age
68+
const auto all_people = sqlgen::read<std::vector<Person>> |
69+
order_by("age"_c);
70+
71+
// Read minors only
72+
const auto minors = sqlgen::read<std::vector<Person>> |
73+
where("age"_c < 18) |
74+
order_by("age"_c);
75+
76+
// Use the queries
77+
const auto result1 = all_people(conn);
78+
const auto result2 = minors(conn);
79+
```
80+
81+
### Transactions
82+
83+
Perform operations within transactions:
84+
85+
```cpp
86+
using namespace sqlgen;
87+
using namespace sqlgen::literals;
88+
89+
// Delete a person and update another in a transaction
90+
const auto delete_hugo = delete_from<Person> |
91+
where("first_name"_c == "Hugo");
92+
93+
const auto update_homer = update<Person>("age"_c.set(46)) |
94+
where("first_name"_c == "Homer");
95+
96+
const auto result = begin_transaction(conn)
97+
.and_then(delete_hugo)
98+
.and_then(update_homer)
99+
.and_then(commit)
100+
.value();
101+
```
102+
103+
### Update Operations
104+
105+
Update data in a table:
106+
107+
```cpp
108+
using namespace sqlgen;
109+
using namespace sqlgen::literals;
110+
111+
// Update multiple columns
112+
const auto query = update<Person>("first_name"_c.set("last_name"_c), "age"_c.set(100)) |
113+
where("first_name"_c == "Hugo");
114+
115+
query(conn).value();
116+
```
117+
118+
## Notes
119+
120+
- The module provides a type-safe interface for DuckDB operations
121+
- All operations return `sqlgen::Result<T>` for error handling
122+
- Prepared statements are used for efficient query execution
123+
- The iterator interface supports batch processing of results
124+
- SQL generation adapts to DuckDB's dialect
125+
- The module supports:
126+
- In-memory and file-based databases
127+
- Transactions (begin, commit, rollback)
128+
- Efficient batch operations
129+
- Type-safe SQL generation
130+
- Error handling through `Result<T>`
131+
- Resource management through `Ref<T>`
132+
- Auto-incrementing primary keys
133+
- Various data types including VARCHAR, TIMESTAMP, DATE
134+
- Complex queries with WHERE clauses, ORDER BY, LIMIT, JOINs
135+
- LIKE and pattern matching operations
136+
- Mathematical operations and string functions
137+
- JSON data types
138+
- Foreign keys and referential integrity
139+
- Unique constraints
140+
- Views and materialized views
141+
- Indexes
142+
```

docs/dynamic.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,50 @@ struct Parser<boost::uuids::uuid> {
4545
} // namespace sqlgen::parsing
4646
```
4747
48+
### DuckDB parser specialization
49+
50+
**Important:** If you're using DuckDB, you must also implement a separate parser specialization in the `sqlgen::duckdb::parsing` namespace. This is required for performance reasons, as DuckDB uses its own native types and appender interface.
51+
52+
The DuckDB parser has a different interface than the generic parser:
53+
54+
```cpp
55+
#include <boost/lexical_cast.hpp>
56+
#include <boost/uuid/uuid.hpp>
57+
#include <boost/uuid/uuid_io.hpp>
58+
#include <sqlgen/duckdb.hpp>
59+
#include <exception>
60+
61+
namespace sqlgen::duckdb::parsing {
62+
63+
template <>
64+
struct Parser<boost::uuids::uuid> {
65+
using ResultingType = duckdb_string_t;
66+
67+
static Result<boost::uuids::uuid> read(const ResultingType* _r) noexcept {
68+
return Parser<std::string>::read(_r).and_then(
69+
[&](const std::string& _str) -> Result<boost::uuids::uuid> {
70+
try {
71+
return boost::lexical_cast<boost::uuids::uuid>(_str);
72+
} catch (const std::exception& e) {
73+
return error(e.what());
74+
}
75+
});
76+
}
77+
78+
static Result<Nothing> write(const boost::uuids::uuid& _u,
79+
duckdb_appender _appender) noexcept {
80+
return Parser<std::string>::write(boost::uuids::to_string(_u), _appender);
81+
}
82+
};
83+
84+
} // namespace sqlgen::duckdb::parsing
85+
```
86+
87+
Key differences from the generic parser:
88+
- `read` takes `const ResultingType*` (where `ResultingType = duckdb_string_t`) instead of `const std::optional<std::string>&`
89+
- `write` takes a `duckdb_appender` parameter and returns `Result<Nothing>` instead of `std::optional<std::string>`
90+
- No `to_type()` method is required (the generic parser's `to_type()` is used for schema generation)
91+
4892
The second step is to specialize `sqlgen::transpilation::ToValue` for `boost::uuids::uuid` and implement `operator()`:
4993

5094
```cpp
@@ -157,6 +201,15 @@ static dynamic::Type to_type() noexcept {
157201
}
158202
```
159203

204+
- DuckDB:
205+
```cpp
206+
static dynamic::Type to_type() noexcept {
207+
return sqlgen::dynamic::types::Dynamic{"TEXT"};
208+
}
209+
```
210+
211+
Note: For DuckDB, you must also implement the `sqlgen::duckdb::parsing::Parser` specialization as shown in the DuckDB parser specialization section above.
212+
160213
## Parser specialization requirements
161214

162215
Specializing `sqlgen::parsing::Parser<T>` requires three methods. These guidelines help ensure correctness and portability:
@@ -200,4 +253,5 @@ Additional best practices:
200253
- Works with all operations: `create_table`, `insert`, `select`, `update`, `delete`
201254
- The type name is passed directly to the database; ensure it is valid for the target dialect
202255
- Keep specializations in the `sqlgen::parsing` namespace
256+
- **DuckDB users:** You must implement both `sqlgen::parsing::Parser` and `sqlgen::duckdb::parsing::Parser` specializations for your custom type
203257

docs/mysql.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ const auto creds = sqlgen::mysql::Credentials{
2323
const auto conn = sqlgen::mysql::connect(creds);
2424
```
2525
26-
The connection is wrapped in a `sqlgen::Result<Ref<Connection>>` for error handling:
26+
The type of `conn` is `sqlgen::Result<sqlgen::Ref<sqlgen::mysql::Connection>>`, which is useful for error handling:
2727
2828
```cpp
2929
// Handle connection errors

docs/postgres.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ const auto creds = sqlgen::postgres::Credentials{
2222
const auto conn = sqlgen::postgres::connect(creds);
2323
```
2424
25-
The connection is wrapped in a `sqlgen::Result<Ref<Connection>>` for error handling:
25+
The type of `conn` is `sqlgen::Result<sqlgen::Ref<sqlgen::postgres::Connection>>`, which is useful for error handling:
2626
2727
```cpp
2828
// Handle connection errors

docs/sqlite.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const auto conn = sqlgen::sqlite::connect();
1616
const auto conn = sqlgen::sqlite::connect("database.db");
1717
```
1818

19-
The connection is wrapped in a `sqlgen::Result<Ref<Connection>>` for error handling:
19+
The type of `conn` is `sqlgen::Result<sqlgen::Ref<sqlgen::sqlite::Connection>>`, which is useful for error handling:
2020

2121
```cpp
2222
// Handle connection errors

include/sqlgen/duckdb.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#ifndef SQLGEN_DUCKDB_HPP_
2+
#define SQLGEN_DUCKDB_HPP_
3+
4+
#include "../sqlgen.hpp"
5+
#include "duckdb/connect.hpp"
6+
#include "duckdb/to_sql.hpp"
7+
8+
#endif

0 commit comments

Comments
 (0)