Wednesday, April 15, 2026

What changed between MySQL 9.7.0-er and 9.7.0-er2

Oracle has published the second MySQL 9.7.0 early access release!

Previously I wrote about building and testing the first one under macOS. This time I won't do that, because I cannot, because of a showstopper bug #120246 (XCode 26.4 fails to build 9.7.0-er2). Using a different compiler does not help this time because some of the errors appear libc-related.

So, this time I'll be looking into a different thing: what exactly has changed from the first to the second early access release? When the 1st ER came out, the community noted that there are no release notes. Oracle has now published them, and they cover what has changed from 9.6.0, but not between the ER releases, and this is precisely the gap I wanted to cover.

I got the source diff, excluding the generated files:

diff -ruN -x 'sql_yacc.cc' -x 'sql_hints.yy.*' mysql-9.7.0-er mysql-9.7.0-er2

Diffstat shows 466 files changed, 17835 insertions(+), 2971 deletions(-)

Then I fed it, together with URLs of the release notes and WLs to Claude Opus 4.6, which produced the following, which I then iterated to remove obvious mistakes and lightly edited to remove some irrelevant ("test result files have been updated") stuff and re-ordered to list most user-visible changes first:

New feature: Fire child triggers during FK cascade (WL#17024)

  • New session variable enable_cascade_triggers, default OFF, SESSION_VAR + IN_BINLOG + CMD_LINE. When ON, BEFORE/AFTER triggers on a child table fire during ON DELETE CASCADE, ON UPDATE CASCADE, ON DELETE SET NULL and ON UPDATE SET NULL actions (SET NULL fires the child's UPDATE triggers in both cases, since the child row is not deleted). When OFF, the historical behaviour (no child triggers during cascade) is kept.
  • Introduced as already deprecated. The variable emits a deprecation warning regardless of value, and the command-line option --enable_cascade_triggers does the same:
    • set to OFF: warns "triggers on child table will not fire".
    • set to ON while innodb_native_foreign_keys=ON: warns that it is only supported with SQL foreign-key handling.
    • otherwise: generic deprecation warning.
  • Limits and safety rails:
    • Max depth of a cascade-trigger chain is 30 tables (FK_MAX_TABLES_IN_CASCADE_CHAIN).
    • Triggers that would modify the FK columns are rejected.
    • Generated columns and CHECK constraints are re-evaluated after BEFORE triggers.
    • Self-referencing FKs correctly prelock for UPDATE triggers on the cascade path.
  • Replication behaviour:
    • The applied value is recorded in each DML binlog event (new Q_ENABLE_CASCADE_TRIGGERS status variable) so replay is deterministic. mysqlbinlog prints the flag.
    • Row-based replication workers force enable_cascade_triggers=OFF — triggers are never re-fired from replica-side row events.
    • Statement-based applier honours the flag only if the source is 9.7.0 or newer, gated by replica_allow_higher_version_source (the gate is new; the sysvar itself already shipped in -er).
  • New tests: source × replica matrix (rpl_fk_enable_triggers_{off_off,off_on,on_off,on_on}), child-trigger tests, binlog tests that echo "WL#17024", refreshes of all_persisted_variables / mysqld--help-* / replica compatibility-rules results.

New feature: MySQL Router host-cache

A brand-new Router subsystem that caches DNS resolution results so routing connections do not re-query the resolver on every hop. (not called out in the draft release notes, but installed by packaging)

  • Three new Router plugins:
    • host_cache — the cache itself, configurable with: enabled, max_entries, ttl_success, ttl_negative, ttl_jitter_ratio.
    • rest_host_cache — lists / invalidates entries via the existing Router REST framework.
    • mock_host_resolver — test-only resolver used by component tests.
  • Routing integration: classic routing connections resolve hostnames via a new harness-level resolver framework that consults the cache (CachePolicy::UseIfPresent).
  • Packaging: host_cache.so and rest_host_cache.so ship in the Router package; default configuration_defaults_cluster{,set}.json files now include a [host_cache] section.

Optimizer / hint bug fixes

  • Bug#116084 "Uniform syntax of join order and table hints"JOIN_PREFIX / JOIN_SUFFIX / JOIN_ORDER now accept the same table-list syntax as the other table hints. sql_hints.yy collapses the *_empty_qb rules; opt_hints_join_order.test adds the covering cases.
  • Bug#38191248 — NULL detection for DATE-type columns in derived tables after derived merge. New cases in derived.test.
  • Bug#39062785 — hypergraph assertion `tl->join_cond_optim() ! nullptr'= failed. New case in hypergraph_bugs.test.
  • Bug#37804715 INTERSECT wrong result (release notes pair this with Bug#117911). When INTERSECT/EXCEPT produce a non-nullable result type but an operand is nullable, a NOT NULL filter is injected into HAVING/QUALIFY (for aggregates/window) or WHERE (for plain columns). make_join_hypergraph.cc preserves an explicit Item_func_true() sentinel so the fix survives hypergraph planning.
  • Correlated-subquery plan fixtest_quick_select() now receives the correct prev_tables / read_tables context instead of empty maps.

Prepared-statement bug fixes

  • Bug#39071552Rollback_item_tree_changes() stack use-after-free. The conds stack item in sql_delete.cc / sql_update.cc is moved off-stack (ArrayAlloc<Item *>(1)) so it survives prepared-statement re-execution while triggers mutate the item tree. Regression test in ps.test.
  • Bug#38600714 — SQL reprepare bug causing 100% CPU. Reprepare gives up with an error in release builds when it keeps hitting a parameter-type mismatch, instead of looping forever. Regression test covers KILL / PURGE / CREATE TABLE … DEFAULT(?) variants.
  • Digest split: Prepared_statement now carries separate m_execute_digest and m_deallocate_digest (and corresponding PSI instrumentation), fixing double-accounting of the DEALLOCATE PREPARE digest via the new PSI digest_set entry point.

Histogram fixes

  • Bug#38983545 — stale diagnostic-area conditions were being re-logged on every background histogram update. Each background update now resets the diagnostics area first, and repeated background-update errors are downgraded to warnings in the error log. histogram_background_error.test adds the regression case and flips the wait condition from Error to Warning.
  • Bug#37269033 — user-defined histogram import failed for BIGINT data containing unsigned values. Histogram::extract_json_dom_value<longlong> now also accepts J_UINT (range-checked against LLONG_MAX). histogram_update_using_data.test extended.

Performance Schema — binary_log_transaction_compression_stats thread-safety

  • Concurrent access to the P_S binary_log_transaction_compression_stats table could race with binlog stats updates.
  • binlog/monitoring/context now takes a snapshot under a std::mutex before rows are emitted, and the backing store uses std::unique_ptr<Compression_stats> (also plugs a leak). (not in release notes)

Clone plugin — protocol V4 (WL#16520 code delta)

The parent cross-LTS feature was already in -er (the four remote_basic_replace_across_lts_* tests ship there). What's new in -er2:

  • Protocol bump to V4: new COM_RES_CONFIG_V4 response command.
  • Version-and-maturity handshake: donor sends its version and maturity (LTS / INNOVATION); recipient caches and validates via new service method mysql_clone_validate_version(). The check moves out of mysql_clone_validate_configs() into its own service entry point.
  • One new test: remote_basic_replace_one_lts_to_next.{test,result,cnf}.

New component service: audit-log prune adapt

Two new header-only component-service declarations:

  • include/mysql/components/services/bits/audit_log_prune_adapt_bits.htypedef void (*audit_log_prune_adapt_v1_t)(size_t external_rotate_cycle);
  • include/mysql/components/services/mysql_audit_log_prune_adapt_service.h

These let an audit-log plugin adjust its pruning cycle to be slower than rotation, whether rotation is time-based or external. The audit-log plugin itself is enterprise and not in this source tree, so only the service headers ship here. (release notes advertise the consumer features WL#17213/WL#17179, but those land in the enterprise plugin, not in this diff.)

InnoDB

  • Parallel FTS index build refactor: ddl0fts.cc / ddl0impl-buffer.h / ddl0impl-builder.h reorganised. copy_fts_column() becomes enqueue_parsing(); Doc_item::m_field owns its dfield_t instead of pointing at parser state; new pop_unfinished_tuple() + will_fit() guarantee forward progress. No SQL-level semantics change. New regression test innodb_fts/articles_fts_words.
  • Symlinked datadir tablespaces: new static fil_ibd_same_as_default_path() in fil0fil.cc. When discovering tablespaces at startup, a .ibd symlinked into the datadir tree is recognised as "default location" (by inode comparison). Fixes the rewritten innodb_recovery_with_upper_case_names. (not in release notes)
  • Parallel scan: row0pread.cc record copy now accounts for the extra-bytes prefix (rec_offs_extra_size) so the saved pointer stays valid. (not in release notes)

Group Replication — GCS input-length hardening

Defensive hardening of the XCom state-exchange decoder:

  • Xcom_member_state::decode_header() now takes a buffer length and validates it before reading.
  • decode_snapshot() enforces element-count sanity against the size required by the node array.
  • decode() does an overall size check; return values changed from raw bool to ERROR / OK constants.
  • pipeline_interfaces.h: event_len < packet->len= check before deserialising a binlog event; logs ER_GRP_RPL_UNABLE_TO_CONVERT_PACKET_TO_EVENT on mismatch.

(not in release notes)

JSON schema — recursion / stack-overflow guard

sql-common/json_schema.cc:

  • New JSON_SCHEMA_MAX_EXPANSION_DEPTH = 100.
  • Helpers is_local_json_pointer_ref(), resolve_local_json_pointer_ref(), json_schema_exceeds_expansion_depth() walk a schema and abort with a proper error before rapidjson's recursive compile blows the stack.

Security adjacent — not in the release notes.

Smaller SQL-layer fixes

  • ALTER TABLE INPLACE — separate trackers old_field_index_without_vgc (stored) and old_field_index_vgc (virtual) so VGC reorder is detected correctly; reorder-flag comparison now uses new_field_index_vgc.
  • LOAD DATA — Bug#39045673: field-list setup rewritten to build set_fields / set_vars deques and feed a unified setup_fields(). New test data file std_data/bug39045673.csv.
  • sys-schema revoke_schema_privileges_from_all_accounts_except correctness fix (string-literal quoting); new assertions in pr_revoke_schema_privileges_from_all_accounts_except.
  • events_bugs.test--sorted_result on a last_executed-sensitive SELECT (non-determinism fix).

Build / toolchain

  • C++23 is now the project-wide default (was C++20). The top-level CMakeLists.txt adds SET(CMAKE_CXX_STANDARD 23); per-compiler -std=c++23 / /std:c++latest flags are dropped. (not in release notes)

Performance Schema — PSI statement service v6

  • New PSI_STATEMENT_VERSION_6 interface; PSI_STATEMENT_VERSION_5 remains resolvable (tagged obsolete) so older plugins still load.
  • New digest_set_v6_t callback lets the server hand a fully-computed statement digest to P_S in one call — used by the prepared-statement digest split above.

X plugin allocator rewrite

plugin/x/src/ngs/memory.hPFS_allocator<T> rewritten as a proper C++17 allocator: explicit value_type / size_type / difference_type / is_always_equal, operator==, noexcept, [[nodiscard]], throws std::bad_array_new_length / std::bad_alloc on overflow / my_malloc failure instead of silent UB inside standard containers. (not in release notes)

For completeness: NDB Cluster

(not in release notes)

  • New NDBINFO table TRANSPORTER_ACTIVITY (TABLEID 53) — histogram of activity on heartbeated transporters, for diagnosing link starvation / flapping.
  • TRANSPORTER_DETAILS grows two columns (19 → 21): heartbeat_interval and last_recv (time since last receive).
  • New "Late heartbeat" event in the cluster log; docs clarify that four missed heartbeats mark a node dead.
  • Node-ID hardening: API client connect / subscribe paths validate the node ID against MAX_NODES_ID (2048) before using it. New test ndb_high_api_node_id.
  • Transporter plumbing: Transporter gains get_last_recv() / set_last_recv() + m_last_recv; signal structs (EnableCom, CloseComReqConf, ApiRegSignalData) carry the new activity info; QMGR/trpman handshake updated; new unit-test driver trpman-t.cpp.
  • Portlib: NdbTick_AddMilliseconds signature changes Uint64Int64 with a range assertion.
  • Associated tests: ndb_trp_activity, refreshes of ndbinfo / ndbinfo_plans results.

1 comment:

Anonymous said...

Do the build issues persist if the missing headers are added?