All notable changes to DSMySQL are documented here.
Format follows Keep a Changelog.
Versioning follows Semantic Versioning.
4.8.0 – 2026-04-04
function_id<"name">— compile-time identifier for stored functionscreate_function(function_id<"name">{}, params, returns, body)— type-safe CREATE FUNCTION builder with optional.deterministic(),.no_sql(),.reads_sql_data(),.modifies_sql_data()characteristicsdrop_function(function_id<"name">{})— DROP FUNCTION builder with.if_exists()support- Composition API —
create(descriptor)/drop(descriptor)alternative syntax that separates verb from object:create(table(T{})),create(view(T{})),create(database(DB{})),create(procedure(...)),create(function(...)),create(trigger<T>(...)), and matchingdrop()overloads; all return the same builders as thecreate_*/drop_*functions - Table partitioning — instance-based
.partition_by_hash(col),.partition_by_key(cols...),.partition_by_range(col),.partition_by_list(col)oncreate_tablebuilders with.partitions(n)and.add_partition(name, clause)for partition definitions;partition_value::less_than(v),partition_value::less_than_maxvalue(),partition_value::in_list(values)helpers connection_pool— thread-safe connection pool withconnection_pool::create(config, size)factory,acquire()(blocking),try_acquire()(non-blocking), and RAIIpooled_connectionthat auto-returns to pool on destructionasync_query(pool, stmt)/async_execute(pool, stmt)— pool-based async query execution viastd::async; returnsstd::futureholding the result; multiple calls run concurrently on different connections from the pool- Composable FK action syntax — two new styles alongside the existing flat types:
fk_attr::on_delete(fk_attr::cascade)(intermediate) andfk_attr::on(fk_attr::delete_(fk_attr::cascade))(fully composed, reads like SQLON DELETE CASCADE); action tags:cascade,restrict_,set_null,no_action; all three styles produce identical attribute types set_case(col, case_when_builder)— CASE/WHEN expressions in UPDATE SET clauses via.set_case(col{}, case_when(cond, val).else_(val))onupdate_builderandupdate_set_builder; chainable with.set(),.where(),.order_by(),.limit()server_cursor<RowType>— streaming result set via MySQL server-side cursors; created viaconn.open_cursor<RowType>(sql, params...)or withprefetch_rows{n}hint;.fetch()returns one row at a time without loading the full result set into memory
4.7.0 – 2026-03-31
sql_default()sentinel andsql_default_ttype — represents the SQLDEFAULTkeyword in INSERT statements- Positional field-based insert —
insert_into(t{}).values(sql_default(), col2{"val"}, col3{"val"})allows specifying all struct fields in order withsql_default()as a placeholder for columns that should use their database default - Column-specific insert —
insert_into(t{}).columns(col1{}, col2{}).values(val1, val2)allows inserting a subset of columns; column list is instance-based (no template arguments needed) - Column field
sql_default()assignability — columns withauto_incrementordefault_valueattributes can be constructed from or assignedsql_default():row.id_ = sql_default(), then the struct-basedinsert_into(t{}).values(row)emitsDEFAULTfor those columns prepared_statement— RAII wrapper for MySQL prepared statements (MYSQL_STMT*); created viamysql_connection::prepare(sql)orprepare(builder); supports type-safe parameter binding viaexecute(params...)and typed result fetching viaquery<RowType>(params...)transaction_guard— RAII scoped transaction helper;transaction_guard::begin(conn)disables autocommit, destructor auto-rolls-back unlesscommit()is called; also provides explicitrollback()- CTE fluent API —
with(cte("name", query)).select(...).from(cte_ref{"name"})andwith(recursive(cte("name", sql))).select(...)replace the oldwith_cte()/with_recursive_cte()builders sql_predicates.hpp— predicates, operators, andcol_expr/col_refextracted fromsql_core.hppinto their own header for readabilitycolumn_attr::default_value(V)— typedDEFAULTattribute for any column type; value type is validated at compile time against the column type (e.g.default_value(0)forint32_t,default_value("active")forvarchar_type<N>,default_value(current_timestamp)for temporal types)column_attr::on_update(V)— typedON UPDATEattribute (e.g.on_update(current_timestamp))current_timestamp— sentinel value inds_mysqlnamespace, used withdefault_valueandon_updatefor temporal columns
ColumnDescriptorconcept replaced byColumnFieldTypeeverywhere — the dual-concept system is removedcolumn_traits<T>removed — callers now useT::column_name()andT::value_typedirectlyqual<Col>now derives the table name from the tag type via compile-time reflection, replacing the oldcol<T,I>approachsql_core.hppreduced from ~1 035 to ~340 lines via the predicate extraction- Column attributes are now instance-based NTTPs (
auto... Attrs) instead of type parameters (typename... Attrs) — marker attributes use{}syntax (e.g.column_attr::primary_key{}), parametric attributes use constructor syntax (e.g.column_attr::comment("..."),column_attr::collate("..."))
col<Table, Index>(col.hpp) — index-based column descriptor removed; usetagged_column_field(orCOLUMN_FIELDmacro) insteadcol_of<&T::field>— member-pointer column alias removed alongsidecol<T,I>cte_builder,with_cte(),with_recursive_cte()— replaced by the newwith(cte(...))fluent APIcolumn_attr::default_current_timestampandcolumn_attr::on_update_current_timestamp— replaced bydefault_value(current_timestamp)andon_update(current_timestamp)
4.6.3 – 2026-03-30
varchar_typenow owns its value (std::stringstorage instead ofstd::string_view), preventing use-after-free when values outlive the source buffer (e.g. query results).
4.6.2 – 2026-03-30
select_query_builder: replacestd::vector<order_by_item>with a singlestd::stringfor ORDER BY clause accumulation. Removealias_order_entrystruct andorder_by_itemvariant;order_by_alias()now resolves aliases eagerly at call time instead of deferring tobuild_sql().
4.6.1 – 2026-03-30
- Add default initialization for
alias_order_entrymembers.
4.6.0 – 2026-03-30
column_attr::primary_key— inline column-levelPRIMARY KEYattribute for single-column primary keys, e.g.COLUMN_FIELD(id, uint32_t, column_attr::primary_key, column_attr::auto_increment)date_typefor MySQLDATEcolumns — storesstd::chrono::sys_days, supports serialization ('YYYY-MM-DD'), deserialization, andstd::optional<date_type>for nullable columns
4.5.0 – 2026-03-29
table_inline_primary_key<T>trait and automaticPRIMARY KEY AUTO_INCREMENTon the first column — define primary keys explicitly viacolumn_attr::primary_key,table_constraints<T>, or both
4.4.1 – 2026-03-29
mysql_connection::execute()error messages now include the failing SQL statement for easier debugging of multi-statement operations likecreate_all_tables
4.4.0 – 2026-03-29
- Instance-based overloads for
mysql_connection::validate_tableandvalidate_database— e.g.db->validate_table(trade{})anddb->validate_database(trade_db{})as alternatives to the template-only forms
4.3.0 – 2026-03-29
col_exproperator==andoperator!=overloads for named ID types (index_id,check_id,trigger_id, etc.) — e.g.col_ref(statistics::index_name{}) == index_id<"uq_symbol_ticker">{}
4.2.0 – 2026-03-29
- Instance-based overload for
create_index_on— e.g.create_index_on(index_id<"idx">{}, table{}, col1{}, col2{})as an alternative to the template-only form
col_refis now instance-based —col_ref(col{})replacescol_ref<col>for consistency with the rest of the API
4.1.0 – 2026-03-29
- Instance-based overloads for
table_constraint::primary_key,key,unique_key,fulltext_key,spatial_key— e.g.primary_key(col{})andkey(index_id<"idx">{}, col{})as alternatives to the template-only forms
4.0.0 – 2026-03-29
mysql_connection::last_insert_id()— returns the AUTO_INCREMENT id generated by the most recent INSERT (mysql_insert_id())mysql_connection::autocommit(bool)— enable/disable autocommit mode (mysql_autocommit())mysql_connection::commit()/rollback()— direct C API transaction control (mysql_commit()/mysql_rollback())mysql_connection::ping()— check connection liveness and reconnect if down (mysql_ping())mysql_connection::select_db(database_name)— switch the default database (mysql_select_db())mysql_connection::reset_connection()— reset connection state without re-authenticating (mysql_reset_connection())mysql_connection::warning_count()— number of warnings from the last statement (mysql_warning_count())mysql_connection::info()— info string about the last INSERT/UPDATE/ALTER (mysql_info()); returnsstd::optional<std::string>mysql_connection::server_version()/server_info()— server version as integer or string (mysql_get_server_version()/mysql_get_server_info())mysql_connection::stat()— server uptime, threads, queries status string (mysql_stat())mysql_connection::thread_id()— connection thread ID (mysql_thread_id())mysql_connection::character_set()/set_character_set(charset_name)— get/set connection character set (mysql_character_set_name()/mysql_set_character_set())mysql_connection::escape_string(string_view)— escape special characters using connection charset (mysql_real_escape_string())charset_namestrong type — wrapsstd::stringfor type-safe character set name passingssl_modeenum —disabled,preferred,required,verify_ca,verify_identityconnect_options— fluent pre-connect options builder applied betweenmysql_init()andmysql_real_connect(); supports timeouts (connect_timeout,read_timeout,write_timeout), SSL/TLS (ssl,ssl_ca,ssl_cert,ssl_key,ssl_capath,ssl_cipher,tls_version,tls_ciphersuites), connection behaviour (charset,compress,reconnect,local_infile,init_command,default_auth,compression_algorithms,zstd_compression_level,max_allowed_packet,retry_count,bind_address), and connection attributes (attr)mysql_connection::connect()overloads acceptingconnect_options— available for both explicit-parameter andmysql_configformson_duplicate_key_updatetemplate form —.on_duplicate_key_update<T::col>("val")now works the same asupdate().set<T::col>("val")procedure_id<"name">— compile-time stored procedure name typesavepoint_id<"name">— compile-time savepoint name typeCollationenum — common MySQL collations (utf8mb4_general_ci,utf8mb4_unicode_ci,utf8mb4_bin,utf8_general_ci,utf8_unicode_ci,utf8_bin,latin1_swedish_ci,latin1_general_ci,latin1_bin,ascii_general_ci,ascii_bin);collate()now accepts bothCollationenum andstd::string_viewstats_auto_recalc_default()/stats_persistent_default()— explicit methods for emittingSTATS_AUTO_RECALC=DEFAULT/STATS_PERSISTENT=DEFAULT- Comprehensive unit tests for all
create_tablefluent table options (avg_row_length,checksum,comment,compression,connection,data_directory,index_directory,delay_key_write,encryption,insert_method,key_block_size,max_rows,min_rows,pack_keys,password,row_format,stats_auto_recalc,stats_persistent,stats_sample_pages,tablespace,union_tables)
table_constraint::foreign_key— unused vestigial function superseded byfk_attrcolumn attributesStatsPolicyenum — replaced bybooloverloads onstats_auto_recalc/stats_persistentand explicit_default()methods
- Aggregate projections renamed to consistent
_ofsuffix —sum→sum_of,avg→avg_of,count_col→count_of,count_distinct→count_distinct_of,group_concat→group_concat_of,stddev→stddev_of,stddev_pop→stddev_pop_of,stddev_samp→stddev_samp_of,variance→variance_of,var_pop→var_pop_of,var_samp→var_samp_of,json_arrayagg→json_arrayagg_of,json_objectagg→json_objectagg_of,any_value→any_value_of; old names preserved asusingaliases execute()now returns affected row count — return type changed fromstd::expected<void, std::string>tostd::expected<uint64_t, std::string>viamysql_affected_rows()from()auto-detects multi-table queries — when projections reference columns from multiple tables, the column membership check is skipped automatically;joined<T>is no longer required but still works as an explicit escape hatch- All public entry-point functions now require instance-based calls — template-only overloads (
create_table<T>(),insert_into<T>(), etc.) have been removed in favour ofcreate_table(T{}),insert_into(T{}), etc. This applies uniformly to allValidTable,Database, and id-type APIs for consistency and better IDE discoverability - Id-type functions enforce specific id types —
savepoint(savepoint_id<"sp1">{}),create_procedure(procedure_id<"usp_hello">{}, ...),create_index_on<cols...>(index_id<"idx">{}, table{}),table_constraint::check(check_id<"chk">{}, expr), etc. — replacing the genericNamedIdTypeconcept which has been removed - Procedure and trigger builder classes are now strongly typed — templated on
fixed_string Namewith constructors taking the specific id type (procedure_id<Name>,trigger_id<Name>), eliminatingstd::stringname members - Savepoint builder classes are now strongly typed — templated on
fixed_string Namewith constructors takingsavepoint_id<Name>, eliminatingstd::stringname members ddl_continuationchaining methods (.then().create_table(T{}),.then().use(DB{}), etc.) are now instance-based only- Moved all
*_idtypes fromsql_ddl.hpptosql_core.hppso they are available to both DDL and DML headers union_tablesnow takes variadicValidTableinstances instead ofstd::vector<std::string>— e.g..union_tables(table_a{}, table_b{})with type safety and compile-time table name resolutionstats_auto_recalc/stats_persistentnow takeboolinstead ofStatsPolicyenum —true/falsefor 1/0, with_default()methods for DEFAULTchecksumanddelay_key_write— removed redundantstd::size_toverloads,boolis the only accepted type
NamedIdTypeconcept — no longer needed; each function enforces the correct id type directly
3.2.0 – 2026-03-28
3.1.0 – 2026-03-28
- Instance-based overloads for all
ValidTableentry-point functions — e.g.delete_from(table{})as an alternative todelete_from<table>(); applies todescribe,insert_into,update,delete_from,count,truncate_table,insert_ignore_into,replace_into,insert_into_select,create_table,create_temporary_table,drop_table,drop_temporary_table,create_view,drop_view,rename_table,create_index_on,drop_index_on,alter_table,show_columns,show_create_table,update_join
3.0.0 – 2026-03-28
check_id<"name">— compile-time CHECK constraint name type; pass as second argument totable_constraint::checkto emit a named constraintindex_id<"name">— compile-time index name type; used as the first template parameter wherever an index name is required (create_index_on,drop_index_on,add_index,add_unique_index,add_fulltext_index,table_constraint::key,unique_key,fulltext_key,spatial_key)NamedIdTypeconcept — satisfied bycheck_id<N>,index_id<N>, and any user-defined type providing a staticname()→std::string_viewtable_constraint::check(check_expr)/check<check_id<"name">>(check_expr)— typed CHECK constraint from a check-safe predicate; constraint name is a template parameter, not a function argument; the oldcheck(string_view, string_view)overload has been removedcheck_expr— new predicate type for expressions valid in MySQL CHECK constraints (column-ref comparisons,BETWEEN,INwith literal lists,LIKE,REGEXP, logical&/|/!); produced by all column-ref predicate factories; implicitly converts tosql_predicateso it can be used everywhere a WHERE predicate is acceptedalter_table<T>().change_column<OldCol, NewCol>()—CHANGE COLUMN old new type [NOT NULL];NewColis aColumnDescriptor(e.g.column_field<"new_name", T>) providing the new column name and type; usestd::optional<T>to make the column nullablealter_table<T>().rename_column<OldCol, NewCol>()—RENAME COLUMN old TO new;NewColmust share the samevalue_typeasOldCol(enforced at compile time)alter_table<T>().add_column<Col>()and.modify_column<Col>()now infer SQL type and nullability from the column descriptor — no more string type or bool parameteralter_table<T>().add_index<Cols...>(name)—ADD INDEX name (col1, ...)alter_table<T>().add_unique_index<Cols...>(name)—ADD UNIQUE INDEX name (col1, ...)alter_table<T>().add_fulltext_index<Cols...>(name)—ADD FULLTEXT INDEX name (col1, ...)alter_table<T>().enable_keys()/.disable_keys()—ENABLE KEYS/DISABLE KEYSalter_table<T>().convert_to_charset(Charset)—CONVERT TO CHARACTER SET charsetalter_table<T>().set_engine(Engine)—ENGINE = enginealter_table<T>().set_auto_increment(n)—AUTO_INCREMENT = nsavepoint(name)—SAVEPOINT namerelease_savepoint(name)—RELEASE SAVEPOINT namerollback_to_savepoint(name)—ROLLBACK TO SAVEPOINT nameset_transaction_isolation_level(IsolationLevel)—SET TRANSACTION ISOLATION LEVEL ...withIsolationLevel::{ReadUncommitted, ReadCommitted, RepeatableRead, Serializable}show_databases()—SHOW DATABASES(queryable, result isstd::tuple<std::string>per row)show_tables()—SHOW TABLES(queryable, result isstd::tuple<std::string>per row)show_columns<T>()—SHOW COLUMNS FROM table(queryable, same row type asdescribe<T>())show_create_table<T>()—SHOW CREATE TABLE table(queryable, result isstd::tuple<std::string, std::string>)create_procedure(name, params, body)—CREATE PROCEDURE name(params) BEGIN body ENDdrop_procedure(name)/.if_exists()—DROP PROCEDURE [IF EXISTS] namecall_procedure(name [, args])—CALL name([args])trigger_id<"name">— compile-time trigger name type (satisfiesNamedIdType)create_trigger<trigger_id<"name">, T>(TriggerTiming, TriggerEvent, body)—CREATE TRIGGER ... ON table FOR EACH ROW body;TriggerTiming::{Before, After},TriggerEvent::{Insert, Update, Delete}drop_trigger<trigger_id<"name">, T>()/.if_exists()—DROP TRIGGER [IF EXISTS] namesql_dcl.hpp— new dedicated header for DCL (GRANT / REVOKE); included automatically byds_mysql.hppprivilegeenum — all MySQL privilege types:select,insert,update,delete_,create,drop,references,index,alter,create_view,show_view,create_routine,alter_routine,execute,trigger,event,create_temporary_tables,lock_tables,reload,shutdown,process,file,show_databases,super,replication_slave,replication_client,create_user,create_tablespace,allprivilege_list— composable runtime privilege set via rvalue-chained fluent builder:privileges().select().insert()on::global()— grant target for*.*(all databases)on::schema(database_name)— grant target fordb.*on::table<T>(database_name)— grant target fordb.table; table name derived at compile time fromValidTabletype viatable_name_for<T>on::table<DB, T>()— grant target fordb.table; both database and table names derived at compile time from their types viadatabase_name_for<DB>andtable_name_for<T>user(user_name).at(host_name)/.at_any_host()/.at_localhost()— compose a MySQL account ('user'@'host') as a strongly-typedgrant_usergrant(privilege_list, grant_target, grant_user)/.with_grant_option()—GRANT ... ON ... TO ... [WITH GRANT OPTION]grant<privilege::select, ...>(grant_target, grant_user)— compile-time privilege set overload ofgrantrevoke(privilege_list, grant_target, grant_user)—REVOKE ... ON ... FROM ...revoke<privilege::select, ...>(grant_target, grant_user)— compile-time privilege set overload ofrevokeSqlBuilderconcept — unified concept for any type that can produce SQL viabuild_sql() const → std::string; replaces the previousBuildsSql,AnySelectQuery, andSqlStatementconcepts which were structurally identicalsql_alias— strongly-typed SQL alias identifier (wrapsstd::string_view); used as the alias argument to.with_alias(),.lateral_join(), etc.no_prior/sql_string_builder— DDL-internal sentinel and bridge types for the typed prior chain
- CI: install
libstdc++-15-devintests-clangjob so Clang 20 finds<print>and other C++23 headers (theactrunner image ships an older libstdc++ without them)
-
Eliminated immediately invoked lambda expressions (IILEs) from fold expressions in
schema_generator.hpp,sql_ddl.hpp,sql_mutation_shared.hpp, andmysql_connection.hpp; each per-element body is now a named helper function (column_definition_for,column_def_for,fk_clause_for,validate_one_table), and separator logic uses a plain index loop over a stack-allocated array -
[[nodiscard]]added to all internal SQL-building helpers and traitget()/value()/sql_expr()static members:sql_type_name::value,sql_type_for,field_sql_type_override,column_definition_for,generate_create_table_impl,generate_create_table,generate_values_impl,generate_values,generate_column_list_impl,generate_column_list,column_def_for,fk_clause_for,table_constraints::get,table_attributes::get,escape_sql_string,format_datetime,format_time,to_sql_value, and allprojection_traits::sql_exprstatic members insql_dql.hpp -
Breaking: Uniform value-argument API — all query builder methods previously requiring template parameters now accept value arguments instead. Every column descriptor, table type, and expression type is passed as a default-constructed instance. No runtime cost: these are empty structs or trivially constructed types that are fully elided by the optimizer at
-O1or above.Old (template params) New (value args) select<col1, col2>()select(col1{}, col2{}).from<Table>().from(Table{}).from<joined<Table>>().from(joined<Table>{}).group_by<Col1, Col2>().group_by(Col1{}, Col2{}).order_by<Col>().order_by(Col{}).order_by<Col, sort_order::desc>().order_by(desc(Col{})).order_by_alias<Proj>().order_by_alias(Proj{}).order_by_alias<Proj, sort_order::desc>().order_by_alias(desc(Proj{})).with_alias<Proj>("name").with_alias(Proj{}, sql_alias{"name"}).inner_join<T, L, R>().inner_join(T{}, L{}, R{}).inner_join_on<T>(pred).inner_join(T{}, pred).left_join<T, L, R>().left_join(T{}, L{}, R{}).left_join_on<T>(pred).left_join(T{}, pred).cross_join<T>().cross_join(T{}).natural_join<T>().natural_join(T{}).inner_join_using<T, C1, C2>().inner_join_using(T{}, C1{}, C2{}).straight_join<T, L, R>().straight_join(T{}, L{}, R{}).straight_join_on<T>(pred).straight_join(T{}, pred)update<T>().order_by<Col, sort_order::desc>().order_by(desc(Col{}))delete_from<T>().order_by<Col>().order_by(Col{}) -
Breaking: Scalar projection functions previously using string/integer template parameters now take runtime constructor arguments. Types used as string values must be passed as instances of their column type; date intervals use the new
interval::*namespace.Old (template params) New (runtime constructor) left_of<Col, 3>left_of<Col>(3)right_of<Col, 3>right_of<Col>(3)replace_of<Col, "a", "b">replace_of<Col>(Col{"a"}, Col{"b"})lpad_of<Col, 10, "0">lpad_of<Col>(10, Col{"0"})date_format_of<Col, "%Y-%m-%d">date_format_of<Col>("%Y-%m-%d")date_add_of<Col, 7, day>date_add_of<Col>(interval::day{7})truncate_to<Col, 2>truncate_to<Col>(2)json_extract_of<Col, "$.field">json_extract_of<Col>("$.field")if_of<"cond", Then, Else>sql_if<Then, Else>(predicate) -
Breaking:
*_join_on<T>(pred)overloads removed; use*_join(T{}, pred)instead (same function,_onsuffix dropped). -
desc_order<T>anddesc()moved fromsql_dql.hpptosql_core.hpp; now available in DML and any other context that includessql_core.hpp. -
Breaking:
where_conditionrenamed tosql_predicate; update any code that names the type explicitly -
Breaking:
table_constraint::check()now acceptscheck_exprinstead ofsql_predicate; subquery predicates (in_subquery,exists, etc.) andmatch_againstdo not producecheck_exprand will no longer compile in a CHECK context -
Simple column-ref predicate factories (
equal,greater_than,like,between,in, etc.) now returncheck_exprinstead ofsql_predicate;check_exprimplicitly converts tosql_predicateso existing WHERE/HAVING/JOIN ON usage is unaffected -
Subquery predicate factories (
in_subquery,not_in_subquery,exists,not_exists) andmatch_againstreturnsql_predicatedirectly (not check-safe) -
Breaking:
column_aliasrenamed tosql_alias; replacesql_alias{"name"}withsql_alias{"name"} -
Breaking:
.with_alias()now requires asql_aliasvalue as its second argument instead of a plainstd::string; replace.with_alias(Proj{}, "name")with.with_alias(Proj{}, sql_alias{"name"}) -
Breaking: DDL builders now use a typed
Priortemplate parameter instead ofstd::string prior_sql_; builder constructors no longer accept raw strings — chaining is enforced at the type level viaddl_continuation<Prior> -
Breaking:
lateral_join/left_lateral_join/lateral_join_on/left_lateral_join_onnow take aSqlBuilder-constrained subquery andsql_aliasinstead ofstd::stringandstd::string_view; replace.lateral_join(q.build_sql(), "alias")with.lateral_join(q, sql_alias{"alias"}) -
Breaking:
union_querynow stores the two query builders instead of a pre-built SQL string;union_(),union_all(),intersect_(),except_()return deduced types -
Breaking:
case_when_exprnow stores the builder instead of a pre-built SQL string -
Breaking:
AnySelectQueryandSqlStatementconcepts removed; useSqlBuilderinstead -
TypedSelectQuerynow refinesSqlBuilder:SqlBuilder<T> && requires { typename T::result_row_type; } -
explain()andexplain_analyze()now useSqlBuilderconcept constraint instead of inline requires clause -
Breaking:
grantandrevokenow require strongly-typedprivilege_list,grant_target, andgrant_userarguments instead of plain strings; the old string-based overloads have been removed -
natural_join<T>(),natural_left_join<T>(),natural_right_join<T>()—NATURAL [LEFT|RIGHT] JOINwith no ON/USING clause -
inner_join_using<T, Cols...>(),left_join_using<T, Cols...>(),right_join_using<T, Cols...>(),full_join_using<T, Cols...>()—JOIN ... USING (col1, col2, ...)with one or more column descriptors -
lateral_join(sql, alias),left_lateral_join(sql, alias)—[LEFT] JOIN LATERAL (subquery) AS alias(MySQL 8.0+) -
lateral_join_on(sql, alias, cond),left_lateral_join_on(sql, alias, cond)— lateral join variants with an explicitONcondition -
straight_join<T, LeftCol, RightCol>(),straight_join_on<T>(cond)—STRAIGHT_JOINMySQL optimizer hint forcing left-to-right join evaluation order -
not_regexp<Col>(pattern)—col NOT REGEXP 'pattern' -
rlike<Col>(pattern)—col RLIKE 'pattern'(MySQL synonym forREGEXP) -
not_rlike<Col>(pattern)—col NOT RLIKE 'pattern' -
sounds_like<Col>(word)—col SOUNDS LIKE 'word' -
match_against<Cols...>(expr [, modifier])— full-text search viaMATCH(...) AGAINST (...), supportingmatch_search_modifier::boolean_mode,query_expansion, andnatural_language_with_query_expansion -
All of the above are also available as methods on
col_expr/col_ref<Col>(exceptmatch_against) -
MySQL alias free functions:
mysql_not_regexp,mysql_rlike,mysql_not_rlike -
ntile_over<N, PartitionCol, OrderCol>—NTILE(N) OVER (...) -
percent_rank_over<PartitionCol, OrderCol>—PERCENT_RANK() OVER (...) -
cume_dist_over<PartitionCol, OrderCol>—CUME_DIST() OVER (...) -
first_value_over<Col, PartitionCol, OrderCol>—FIRST_VALUE(col) OVER (...) -
last_value_over<Col, PartitionCol, OrderCol>—LAST_VALUE(col) OVER (...) -
nth_value_over<Col, N, PartitionCol, OrderCol>—NTH_VALUE(col, N) OVER (...) -
Conditional scalar functions:
nullif_of<A,B>,greatest_of<Ps...>,least_of<Ps...>,if_of<"cond",Then,Else>with short aliasesnullif,greatest,least,sql_if -
String scalar functions:
char_length_of<P>,left_of<P,N>,right_of<P,N>,replace_of<P,"from","to">,lpad_of<P,N,"pad">,rpad_of<P,N,"pad">,repeat_of<P,N>,reverse_of<P>,locate_of<"sub",P>,instr_of<P,"sub">,space_of<P>,strcmp_of<A,B>with matching short aliases -
Math scalar functions:
sqrt_of<P>,log_of<P>,log2_of<P>,log10_of<P>,exp_of<P>,sin_of<P>,cos_of<P>,tan_of<P>,degrees_of<P>,radians_of<P>,sign_of<P>,truncate_to<P,D>,pi_valwith matching short aliases -
Extended date/time scalar functions:
hour_of<P>,minute_of<P>,second_of<P>,microsecond_of<P>,quarter_of<P>,week_of<P>,weekday_of<P>,dayofweek_of<P>,dayofyear_of<P>,dayname_of<P>,monthname_of<P>,last_day_of<P>,str_to_date_of<P,"fmt">,from_unixtime_of<P>,unix_timestamp_of<P>,convert_tz_of<P,"from","to">,curtime_val,utc_timestamp_val,addtime_of<A,B>,subtime_of<A,B>,timediff_of<A,B>with matching short aliases -
JSON scalar functions:
json_extract_of<P,"$.path">,json_object_of<Ps...>,json_array_of<Ps...>,json_contains_of<P,"val">,json_length_of<P>,json_unquote_of<P>with matching short aliases -
stddev<Col>—STDDEV(col)→double -
std_of<Col>—STD(col)→double(MySQL synonym forSTDDEV) -
stddev_pop<Col>—STDDEV_POP(col)→double -
stddev_samp<Col>—STDDEV_SAMP(col)→double -
variance<Col>—VARIANCE(col)→double -
var_pop<Col>—VAR_POP(col)→double -
var_samp<Col>—VAR_SAMP(col)→double -
bit_and_of<Col>/bit_and<Col>—BIT_AND(col)→uint64_t -
bit_or_of<Col>/bit_or<Col>—BIT_OR(col)→uint64_t -
bit_xor_of<Col>/bit_xor<Col>—BIT_XOR(col)→uint64_t -
json_arrayagg<Col>—JSON_ARRAYAGG(col)→std::string -
json_objectagg<KeyCol, ValCol>—JSON_OBJECTAGG(key, val)→std::string -
any_value<Col>—ANY_VALUE(col)→value_typeofCol
2.1.1 – 2026-03-24
create_all_tables<DB>()now qualifies each table name as<db_name>.<table_name>, fixing table creation when no current database is selected in MySQL.
2.1.0 – 2026-03-24
- Add int_type, int_unsigned_type, bigint_type, and bigint_unsigned_type to allow display width similarly to floating point types.
- Fixed table_attributes specialization that was broken
2.0.0 – 2026-03-23
time_type— maps to SQLTIME; wraps astd::chrono::microsecondsduration with optional fractional-second precision (0–6). SupportsINSERT/UPDATEserialization andSELECTdeserialization, including negative durations.
- Breaking: Renamed temporal value types to align with the
float_type/double_type/decimal_typenaming scheme introduced in v1.1.0:sql_datetime→datetime_typesql_timestamp→timestamp_type
- Breaking: Removed monolithic
ds_mysql/sql.hpp. Includeds_mysql/ds_mysql.hppfor the umbrella API, or includeds_mysql/sql_ddl.hpp,ds_mysql/sql_dml.hpp, andds_mysql/sql_dql.hppdirectly.
1.1.0 - 2026-03-23
- Typed formatted numeric column wrappers:
float_type,double_type, anddecimal_type. - Support for
<>,<precision>, and<precision, scale>numeric wrapper forms in schema generation. - Nullable formatted numeric columns via
std::optional<...>wrappers. - Unit coverage for formatted numeric DDL generation and DML serialization/deserialization.
1.0.0 – 2026-03-21
- Header-only C++23 MySQL query builder and database wrapper.
- Type-safe SELECT / INSERT / UPDATE / DELETE query building with compile-time column verification.
- Compile-time schema generation (
CREATE TABLESQL derived from C++ structs). - Schema validation against a live database (
validate_schema). - Boost.PFR powered compile-time reflection — no macros required for reflection.
COLUMN_FIELDmacro for ergonomic per-table, per-column unique type definitions.- Strong-type wrappers:
host_name,database_name,port_number,user_name,user_password,varchar_type<N>,text_type. std::optional<T>support mapping to nullable SQL columns.std::expected-based error handling throughout — no exceptions in the query path.- SQL helper functions:
ROUND,FORMAT,COALESCE,IFNULL,DATE_FORMAT,NOW,CURDATE,CURRENT_TIMESTAMP, and more. - Runtime metadata helpers (
mysql_metadata) for column introspection. .envfile support for integration-test configuration (via laserpants/dotenv).- CI matrix covering GCC 15, Clang 20, and MSVC (Visual Studio 2022).
- Automated release workflow: tag validation, unit-test gate, and header-archive publishing.
ds_mysql::versionstruct providingmajor,minor,patch,value, andstringcompile-time constants.