Files
sqlgen/docs/literals.md
2025-07-23 22:32:26 +02:00

3.9 KiB

sqlgen::literals

The sqlgen::literals namespace provides user-defined literal operators for referencing columns and table aliases in a type-safe, expressive, and concise way when building SQL queries with sqlgen. These literals are essential for writing readable and maintainable query expressions, especially when working with complex queries involving joins and table aliases.

Usage

Column Literals: _c

Reference a column by name using the _c literal:

using namespace sqlgen;
using namespace sqlgen::literals;

// Reference the "age" column
const auto age_col = "age"_c;

// Use in a query
const auto query = sqlgen::read<std::vector<Person>> | where("age"_c < 18);

This generates SQL like:

SELECT "id", "first_name", "last_name", "age"
FROM "Person"
WHERE "age" < 18;

If the column name does not exist in the struct, the code will fail to compile, ensuring type safety.

Table Alias Literals: _t1, _t2, ..., _t99

When working with joins or subqueries, you often need to disambiguate columns by table alias. The sqlgen::literals namespace provides _tN suffixes for this purpose, where N ranges from 1 to 99:

using namespace sqlgen;
using namespace sqlgen::literals;

// Reference the "id" column from table alias t1
const auto id_t1 = "id"_t1;

// Reference the "first_name" column from table alias t2
const auto first_name_t2 = "first_name"_t2;

Example: Joins with Aliases

const auto get_people =
    select_from<Person, "t1">(
        "last_name"_t1 | as<"last_name">,
        "first_name"_t1 | as<"first_name_parent">,
        "first_name"_t3 | as<"first_name_child">,
        ("age"_t1 - "age"_t3) | as<"parent_age_at_birth">
    )
    | inner_join<Relationship, "t2">("id"_t1 == "parent_id"_t2)
    | left_join<Person, "t3">("id"_t3 == "child_id"_t2)
    | order_by("id"_t1, "id"_t3)
    | to<std::vector<ParentAndChild>>;

This produces SQL like:

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"
LEFT JOIN "Person" t3 ON t3."id" = t2."child_id"
ORDER BY t1."id", t3."id"

Supported Suffixes

  • _c — Reference a column by name (e.g., "age"_c)
  • _t1, _t2, ..., _t99 — Reference a column by name and table alias (e.g., "id"_t2)

All these literals are defined in the sqlgen::literals namespace. It is strongly recommended to use:

using namespace sqlgen::literals;

in your query files for convenience.

Type Safety

  • Column names are checked at compile time against the struct definition.
  • Table aliases are enforced by the literal suffix, preventing ambiguous references in joins.
  • If you use a column or alias that does not exist, the code will fail to compile.

Advanced: Custom Aliases

If you need an alias outside the provided _tN range, you can use the col template directly:

const auto custom_col = col<"id", "custom_alias">;

Notes

  • The _c and _tN literals return Col objects, which support all standard SQL operations (comparison, ordering, etc.).
  • These literals are composable with all sqlgen query builder functions, such as where, order_by, group_by, and join conditions.
  • The use of these literals is required for type-safe, readable, and maintainable query construction in sqlgen.
  • All operations are thread-safe and have no mutable state.

Example: Full Query Composition

using namespace sqlgen;
using namespace sqlgen::literals;

const auto query = sqlgen::read<std::vector<Person>>
                   | where("age"_c >= 18 and "last_name"_c == "Simpson")
                   | order_by("first_name"_c.desc())
                   | limit(10);

This generates:

SELECT "id", "first_name", "last_name", "age"
FROM "Person"
WHERE ("age" >= 18) AND ("last_name" = 'Simpson')
ORDER BY "first_name" DESC
LIMIT 10;