4.2 KiB
sqlgen::...::to_sql
The to_sql functions in each dialect namespace (e.g., sqlgen::postgres::to_sql, sqlgen::sqlite::to_sql) provide a way to transpile C++ SQL operations into dialect-specific SQL strings. This is particularly useful for testing, debugging, logging, and understanding the exact SQL that will be executed by sqlgen.
Usage
Basic Usage
Transpile any SQL operation to a dialect-specific SQL string:
using namespace sqlgen;
// Define a query
const auto query = sqlgen::read<std::vector<Person>> |
where("age"_c < 18 and "first_name"_c != "Hugo") |
order_by("age"_c);
// Transpile to PostgreSQL dialect
const auto postgres_sql = postgres::to_sql(query);
This generates the following PostgreSQL SQL:
SELECT "id", "first_name", "last_name", "age"
FROM "Person"
WHERE ("age" < 18) AND ("first_name" != 'Hugo')
ORDER BY "age";
Or for SQLite:
// Transpile to SQLite dialect
const auto sqlite_sql = sqlite::to_sql(query);
Supported Operations
to_sql supports transpilation of all sqlgen operations:
SELECT Queries
using namespace sqlgen;
// Basic select
const auto select_query = sqlgen::read<std::vector<Person>>;
const auto sql = postgres::to_sql(select_query);
This generates the following SQL:
SELECT "id", "first_name", "last_name", "age"
FROM "Person";
With a where clause:
// With where clause
const auto filtered_query = sqlgen::read<std::vector<Person>> |
where("age"_c >= 18);
const auto sql = postgres::to_sql(filtered_query);
This generates the following SQL:
SELECT "id", "first_name", "last_name", "age"
FROM "Person"
WHERE "age" >= 18;
CREATE TABLE Operations
Note that every call to sqlgen::write calls a CREATE TABLE operation
before actually inserting the data.
using namespace sqlgen;
// Define a table structure
struct TestTable {
std::string field1;
int32_t field2;
sqlgen::PrimaryKey<uint32_t> id;
std::optional<std::string> nullable;
};
// Create table query
const auto create_query = create_table<TestTable> | if_not_exists;
const auto sql = postgres::to_sql(create_query);
This generates the following PostgreSQL SQL:
CREATE TABLE IF NOT EXISTS "TestTable" (
"field1" TEXT NOT NULL,
"field2" INTEGER NOT NULL,
"id" INTEGER NOT NULL,
"nullable" TEXT,
PRIMARY KEY ("id")
);
INSERT Operations
using namespace sqlgen;
const auto insert_query = Insert<TestTable>{};
const auto sql = postgres::to_sql(insert_query);
This generates the following PostgreSQL SQL:
COPY "public"."TestTable"("field1", "field2", "id", "nullable")
FROM STDIN WITH DELIMITER '\t' NULL '\e' QUOTE '\a';
For SQLite, the same operation generates:
INSERT INTO "TestTable"("field1", "field2", "id", "nullable")
VALUES (?, ?, ?, ?);
DELETE Operations
using namespace sqlgen;
const auto delete_query = delete_from<TestTable> |
where("field2"_c > 0);
const auto sql = postgres::to_sql(delete_query);
This generates the following SQL:
DELETE FROM "TestTable"
WHERE "field2" > 0;
Dialect-Specific Features
Each database dialect implements its own version of to_sql with dialect-specific features:
PostgreSQL (sqlgen::postgres::to_sql)
- Uses
COPYfor bulk inserts - Supports schema names (e.g., "public")
- Uses PostgreSQL-specific escaping rules
SQLite (sqlgen::sqlite::to_sql)
- Uses
INSERT INTOfor inserts - Uses SQLite-specific escaping rules
- Simpler schema (no schema names)
Notes
- Each dialect's
to_sqlis a template function that works with any sqlgen operation - The functions are
noexceptand return astd::string - The generated SQL is properly escaped and safe to use
- The functions are particularly useful for:
- Debugging complex queries
- Logging SQL operations
- Understanding the exact SQL that will be executed
- Testing and validation
- Each dialect's implementation ensures the generated SQL is compatible with that specific database
- The functions preserve all SQL operations' semantics while adapting to dialect-specific syntax