sqlpp11 Documentation
repository·main·Indexed 25 days ago
https://github.com/rbock/sqlpp11A type-safe, embedded domain-specific language (EDSL) for SQL queries and results in C++. It enables compile-time checking of SQL syntax, types, and names by defining tables and columns as C++ types. The library supports static and dynamic queries, connection pooling, and provides connectors for MySQL, MariaDB, PostgreSQL, sqlite3, and SQLCipher. It includes the ddl2cpp tool for generating C++ headers from SQL DDL files.
What's inside sqlpp11
- sqlpp11 is a type-safe C++ library that allows you to write SQL queries directly in C++ code. It provides a way to use tables, columns, and functions with strong typing, enabling the compiler to catch common SQL errors at compile time, such as typos, type mismatches in comparisons, or missing tables in a SELECT statement. Query results are returned as type-safe ranges with strongly typed members, facilitating modern C++ development patterns.
Understand the sqlpp11 architecture
mainTo use
sqlpp11, you need three distinct components working together:- Table Structs: Representations of your database tables (usually generated by
sql2cpp). These structs provide compile-time metadata about column names and data types. - Core Library: Provides the SQL embedded language for C++. It enables writing SQL statements that are checked at compile-time against your table structs.
- Connector Library: The bridge to the physical database. It handles the connection, query execution, and result retrieval.
Ensure you have a connector library installed that matches your target database.
- Table Structs: Representations of your database tables (usually generated by
Handle potential NULL values in query results
mainsqlpp11 attempts to determine if a result field can be NULL based on the query structure. If the library cannot guarantee a value is non-null, it assumes the field can be NULL.
To safely process results:
- Use
.is_null()to check if the field contains a NULL value. - Use
.value()to retrieve the actual data. If.is_null()is true,.value()returns a default-constructed value of the underlying type.
for (const auto& row : db(select(all_of(tab)).from(tab).unconditionally())) { if (not row.alpha.is_null()) { const auto a = row.alpha.value(); } }- Use
Install sqlpp11 via Homebrew or vcpkg
mainYou can use package managers to install sqlpp11.
Homebrew (macOS): Use the
marvin182/zapfhahn/sqlpp11tap.vcpkg: Install using the
sqlpp11port with specific database features (e.g.,[mysql]).# Homebrew brew install marvin182/zapfhahn/sqlpp11 # vcpkg ./vcpkg install 'sqlpp11[mysql]'Perform dynamic insert statements
mainUse
dynamic_insert_intowhen the structure of your insert statement depends on runtime information (e.g., user input) or when you only want to provide values for a subset of the table's fields, leaving others to their default values orNULL.Unlike static inserts,
dynamic_insert_intorequires a database connection as its first argument to evaluate dynamic parts as they are added. You can use.dynamic_set()for initial fields and.insert_list.add()to append additional fields at runtime.// Initialize with the database connection and the target table auto s = dynamic_insert_into(db, foo).dynamic_set( foo.name = name, ); // Add fields dynamically based on runtime logic s.insert_list.add(foo.id = runtimeStatement.id); s.insert_list.add(foo.hasFun = runtimeStatement.hasFun); // Execute the statement const int64_t result = db(s);Manage thread-safety with connection pools
mainConnection pools can prevent multiple threads from using the same connection simultaneously using two main patterns:
1. New connection per request
Fetch a connection handle immediately before each operation. This is safe for individual queries but requires care with transactions: all queries within a single transaction must use the same connection object.
pool.get()(insert_into(mytable)....) pool.get()(remove_from(mytable)....)2. One connection per thread
Use a
thread_localwrapper that lazily fetches a connection from the pool the first time it is used. This ensures each thread has its own dedicated connection from the pool, avoiding multi-threading conflicts. Seeexamples/connection_poolin the repository for a concrete implementation.Perform dynamic JOINs with dynamic_from
mainTo add tables or joins to a query at runtime, use
.dynamic_from(table)to prepare the clause. You can then add joins usings.from.add(dynamic_join(table).on(condition)).auto s = dynamic_select(db, all_of(foo)).dynamic_from(foo).dynamic_where(); if (someOtherCondition) s.from.add(dynamic_join(bar).on(foo.barId == bar.id));Represent tables and columns in sqlpp11
mainTo build SQL statements with
sqlpp11, you must represent your database tables and columns as C++ structs. This allows the library to understand the schema during compile time.The recommended approach is to use a code generator to translate your Data Definition Language (DDL) into C++ code, rather than writing these structs manually.
Assign NULL in INSERT or UPDATE statements
mainWhen performing
insertorupdateoperations, you can represent a database NULL by usingsqlpp::null.Warning: You cannot assign
sqlpp::nullto columns that are defined as non-nullable in your table schema.Manage transactions with start_transaction()
mainTo manage database transactions, use the
start_transaction(db)function, wheredbis your connection object.To successfully complete a transaction, you must explicitly call
.commit(). If you need to abort changes, call.rollback().Automatic Rollback Behavior: If a transaction object goes out of scope without
commit()orrollback()being called, it will automatically trigger arollback()in its destructor. This automatic rollback is reported by the connection by default.auto tx = start_transaction(db); // do something tx.commit();Generate C++ headers from DDL files
mainTo use sqlpp11, you must first define your tables as C++ types. You can generate these headers from SQL DDL files using the provided
ddl2cppPython script.- Export your table schema (e.g., using
SHOW CREATE TABLEin MySQL ormysqldump). - Run the
ddl2cppscript against the.ddlfile.
Command Syntax:
%sqlpp11_dir%/scripts/ddl2cpp <input_ddl_file> <output_header_prefix> <namespace>- Export your table schema (e.g., using
Create a connection pool
mainSQLPP11 provides connection pools as centralized caches of database connections to improve performance. Each connector has its own pool class:
sqlpp::mysql::connection_poolsqlpp::postgresql::connection_poolsqlpp::sqlite3::connection_pool
Constructors accept two parameters:
- A
std::shared_ptrto a configuration object (the same type used for non-pooled connections). - An integer specifying the initial size of the connection cache (the cache grows automatically).
You can also instantiate a pool without a configuration and call
.initialize(config, initial_size)later.auto config = std::make_shared<sqlpp::postgresql::connection_config>(); config->dbname = "my_database"; config->user = "my_user"; config->password = "my_password"; config->debug = true; auto pool = sqlpp::postgresql::connection_pool{config, 5};