|
H5CPP
v1.14.0
Modern C++ templates for HDF5 serial and parallel I/O
|
|
The SQL backend emits CREATE TABLE DDL statements from annotated C++ structs. Three dialects are supported: PostgreSQL, MySQL, and SQLite3. Each backend gets a boolean flag (--sql-postgres, --sql-mysql, --sql-lite3) and uses the unified -o <file> output path.
| Surface today (C++17 standard-attribute) | C++26 reflection form |
|---|---|
[[sql::table("users")]] | [[=sql::table{"users"}]] |
[[sql::column("user_id")]] | [[=sql::column{"user_id"}]] |
[[sql::primary_key]] | [[=sql::primary_key{}]] |
[[sql::unique]] | [[=sql::unique{}]] |
[[sql::not_null]] | [[=sql::not_null{}]] |
[[sql::default_(42)]] | [[=sql::default_{42}]] |
[[sql::foreign_key("other_table.col")]] | [[=sql::foreign_key{"other_table.col"}]] |
[[sql::check("value > 0")]] | [[=sql::check{"value > 0"}]] |
[[sql::index]] | [[=sql::index{}]] |
[[sql::type_override("VARCHAR(255)")]] | [[=sql::type_override{"VARCHAR(255)"}]] |
[[sql::nested("jsonb")]] | [[=sql::nested{"jsonb"}]] |
Only syntactic shift is (args) → {args} under the [[=...]] form. Names stay put.
The SQL backend is integrated into the unified --format dispatcher (issue #30). Each dialect is a distinct enum value in OutputFormat and gets its own CLI flag.
sql:: namespaceThese attributes use vocabulary identical to h5::* where the concept overlaps (rename, ignore, doc, alias). They live in sql:: so the namespace stays self-contained for SQL-only users.
| Attribute | Purpose | Example |
|---|---|---|
[[sql::name("col_name")]] | Rename a field for the SQL column. Defaults to the C++ field name. | [[sql::name("created_at")]] std::chrono::time_point_t ts; |
[[sql::ignore]] | Skip this field entirely. Column absent from the emitted table. | [[sql::ignore]] int debug_counter; |
| Attribute | Purpose | Example |
|---|---|---|
[[sql::doc("description")]] | Emitted as a SQL comment on the column or table. | [[sql::doc("nanoseconds since epoch")]] std::uint64_t ts; |
[[sql::alias("Name")]] | Class-level. Overrides the table name. Defaults to sanitized C++ qualified name (:: → _). | struct [[sql::alias("Users")]] user_t { ... }; |
| Attribute | Purpose | Example |
|---|---|---|
[[sql::table("name")]] | Class-level. Overrides the generated table name. | struct [[sql::table("app_users")]] user_t { ... }; |
[[sql::column("name")]] | Field-level. Overrides the generated column name. | [[sql::column("user_id")]] int id; |
[[sql::primary_key]] | Emits PRIMARY KEY constraint. | [[sql::primary_key]] int id; |
[[sql::not_null]] | Emits NOT NULL constraint. Default for all fields (C++ POD fields are always present). | [[sql::not_null]] int id; |
[[sql::unique]] | Emits UNIQUE constraint. | [[sql::unique]] std::string email; |
| Attribute | Purpose | Example |
|---|---|---|
[[sql::default_(value)]] | Emits DEFAULT value clause. Dialect-aware quoting (strings quoted, numbers bare). | [[sql::default_("active")]] std::string status; |
[[sql::foreign_key("table.col")]] | Emits REFERENCES table(col) clause. | [[sql::foreign_key("orders.id")]] int order_id; |
[[sql::check("expr")]] | Emits CHECK (expr) clause. | [[sql::check("value > 0")]] int value; |
[[sql::index]] | Emits a CREATE INDEX statement after the table. | [[sql::index]] std::string email; |
| Attribute | Purpose | Example |
|---|---|---|
[[sql::type_override("TYPE")]] | Overrides the dialect-specific type mapping for this field. | [[sql::type_override("VARCHAR(255)")]] std::string name; |
[[sql::nested("jsonb")]] | Controls how nested structs are represented. Values: "jsonb" (PostgreSQL), "json" (MySQL), "text" (SQLite3), or "table" (create a separate table with FK). | [[sql::nested("jsonb")]] address_t addr; |
| C++ type | PostgreSQL | MySQL | SQLite3 |
|---|---|---|---|
bool / _Bool | BOOLEAN | TINYINT(1) | INTEGER |
char | SMALLINT | TINYINT | INTEGER |
unsigned char | SMALLINT | TINYINT UNSIGNED | INTEGER |
short | SMALLINT | SMALLINT | INTEGER |
unsigned short | SMALLINT | SMALLINT UNSIGNED | INTEGER |
int | INTEGER | INT | INTEGER |
unsigned int | INTEGER | INT UNSIGNED | INTEGER |
long | BIGINT | BIGINT | INTEGER |
unsigned long | BIGINT | BIGINT UNSIGNED | INTEGER |
long long | BIGINT | BIGINT | INTEGER |
unsigned long long | BIGINT | BIGINT UNSIGNED | INTEGER |
float | REAL | FLOAT | REAL |
double | DOUBLE PRECISION | DOUBLE | REAL |
long double | DOUBLE PRECISION | DOUBLE | REAL |
enum / enum class | INTEGER | INT | INTEGER |
| C++ type | PostgreSQL | MySQL | SQLite3 | Notes |
|---|---|---|---|---|
Nested struct S | JSONB | JSON | TEXT | One column holding the nested struct as JSON. |
T[N] (C array) | T[] | JSON | TEXT | PostgreSQL native arrays; MySQL/SQLite fall back to JSON/TEXT. |
T[M][N] (multi-dim array) | T[][] | JSON | TEXT | PostgreSQL supports [][] syntax; MySQL/SQLite fall back. |
std::string | TEXT | TEXT | TEXT | No length limit assumed; override with sql::type_override. |
std::vector<T> | JSONB | JSON | TEXT | No native variable-length array in standard SQL. |
std::map<K,V> | JSONB | JSON | TEXT | No native map type; JSON fallback. |
std::optional<T> | T (nullable) | T (nullable) | T (nullable) | Emitted without NOT NULL; runtime nulls allowed. |
std::chrono::time_point | TIMESTAMPTZ | DATETIME(6) | TEXT | Auto-detected; can override with sql::type_override. |
Input:
PostgreSQL output:
MySQL output:
SQLite3 output:
Input:
PostgreSQL output:
Key behavior: Nested structs produce a separate CREATE TABLE for the inner struct, AND the outer struct references it as JSONB (or JSON / TEXT depending on dialect). This is a pragmatic compromise: the inner table exists for introspection and potential FK usage, but the outer table stores the nested data as JSON for query simplicity.
Input:
PostgreSQL output:
| Dialect | Table/column quoting | :: → _ | Case sensitivity |
|---|---|---|---|
| PostgreSQL | "identifier" | Yes | Preserved (quoted) |
| MySQL | `identifier` | Yes | Preserved (quoted, but depends on OS/file system) |
| SQLite3 | "identifier" | Yes | Preserved (quoted) |
The sanitize_name() function replaces all :: with _ so C++ qualified names like sn::typecheck::Record become sn__typecheck__Record.
All 11 existing fixtures are reused for SQL testing, yielding 33 SQL-specific tests (11 fixtures × 3 dialects). The HDF5 tests (11 tests) remain unchanged.
| Fixture | What it exercises | PostgreSQL | MySQL | SQLite3 |
|---|---|---|---|---|
primitives | All primitive type mappings | ✓ | ✓ | ✓ |
typedef | Typedef resolution | ✓ | ✓ | ✓ |
nested-ns | Namespace nesting → table naming | ✓ | ✓ | ✓ |
embedded-pod | Nested structs + 1D arrays | ✓ | ✓ | ✓ |
array-of-arrays | Multi-dim arrays of structs | ✓ | ✓ | ✓ |
ignored-non-pod | Non-POD skipped | ✓ | ✓ | ✓ |
unreferenced | Unreferenced types skipped | ✓ | ✓ | ✓ |
topological | Multiple related structs | ✓ | ✓ | ✓ |
enum-member | Enum → INTEGER | ✓ | ✓ | ✓ |
multi-array | Multi-dim primitive arrays | ✓ | ✓ | ✓ |
inheritance | Inheritance skipped | ✓ | ✓ | ✓ |
The sql:: attribute namespace is defined in this taxonomy but not yet implemented in the attribute rewriter (h5_attr_translator.hpp) or consumed in the SQL producer. The staging branch uses the producer/consumer pattern; attribute integration would follow the same h5_attr_reader + clang::annotate rewrite path used for h5:: attributes (issue #32).
| Attribute | Where read (planned) | Where emitted (planned) |
|---|---|---|
sql::table | h5_attr_reader::read_class_string(node, "sql::table") | Overrides table name in record_decl_impl |
sql::column | h5_attr_reader::read_field_string(fld, "sql::column") | Overrides column name in type_insert_impl |
sql::primary_key | h5_attr_reader::has_attr(fld, "sql::primary_key") | Appends PRIMARY KEY to column definition |
sql::unique | h5_attr_reader::has_attr(fld, "sql::unique") | Appends UNIQUE to column definition |
sql::not_null | h5_attr_reader::has_attr(fld, "sql::not_null") | Emits NOT NULL (default anyway) |
sql::default_ | h5_attr_reader::read_field_string(fld, "sql::default_") | Appends DEFAULT value |
sql::foreign_key | h5_attr_reader::read_field_string(fld, "sql::foreign_key") | Appends REFERENCES ... |
sql::check | h5_attr_reader::read_field_string(fld, "sql::check") | Emits CHECK (expr) on column or table |
sql::index | h5_attr_reader::has_attr(fld, "sql::index") | Emits CREATE INDEX after table DDL |
sql::type_override | h5_attr_reader::read_field_string(fld, "sql::type_override") | Replaces mapped type in type_insert_impl |
sql::nested | h5_attr_reader::read_field_string(fld, "sql::nested") | Controls JSONB/JSON/TEXT vs separate table |