
If you’ve worked on Oracle, you know Flashback. Run a SELECT ... AS OF TIMESTAMP, get the row as it existed yesterday at 2pm. Find who touched it with Flashback Transaction Query. Recover a bad UPDATE without restoring a backup.
If you’ve worked on SQL Server 2016 or later, you know Temporal Tables. Add PERIOD FOR SYSTEM_TIME and every UPDATE and DELETE gets versioned automatically into a history table. Query any row at any point in the past with FOR SYSTEM_TIME AS OF.
If you’ve worked on MySQL, you know what you have. Binary log files sitting on disk, parseable with mysqlbinlog, and no native way to ask “what did this row look like an hour ago?”
That gap is the entire reason this post exists.
The short comparison
| Engine | Native row-level history | Mechanism |
|---|---|---|
| Oracle | Flashback Query, Version Query, Transaction Query | Undo tablespace, Flashback Data Archive |
| SQL Server | System-Versioned Temporal Tables (2016+) | Parallel history table, auto-maintained |
| PostgreSQL | None native. Extensions like temporal_tables, or hand-rolled triggers | N/A |
| CockroachDB | AS OF SYSTEM TIME, within the GC window | MVCC |
| MySQL | None | Binlog files |
Oracle describes Flashback as a way to view and rewind data through time, inspect past versions of objects, analyze change, and repair logical corruption without taking the database offline. Microsoft describes Temporal Tables in almost the same words: auditing, forensics, reconstructing the state of any row at any past point. Two different vendors, two different codebases, converging on the same feature because the feature is table stakes for a serious OLTP at this point.
MySQL doesn’t have it.
Why it matters
Pick any week in a DBA’s life and one of these shows up:
- “This customer record is wrong. What did it look like before the overnight job?”
- “Who updated this field, and when?”
- “We need to roll back these 40 rows without touching the rest of the table.”
- “Compliance needs the state of this invoice as of March 14th.”
On Oracle or SQL Server, each of those is a SELECT away. On MySQL, each of those is a ticket.
The usual MySQL answer is some combination of mysqlbinlog on a tarball of binlogs, restoring last night’s backup to a staging box to read one row, custom audit triggers that doubled write amplification years ago and nobody wants to touch, or some variation of “we don’t actually know what happened, but we fixed it forward.” Tools like binlog2sql and MariaDB’s mysqlbinlog --flashback do exist and they do generate rollback SQL from ROW-based events, but generating rollback SQL from a log file is not the same thing as querying history. You cannot write SELECT ... AS OF. You cannot answer “what did this row look like at 14:32” without shipping binlogs somewhere, running them through a parser, and reading the output.
Flashback and Temporal Tables are queryable history. MySQL has a parseable log. The gap between those two things is where most of the incidents actually live.
“But MariaDB has it. Doesn’t that count?”
This is the question people ask, and it deserves a real answer.
MariaDB 10.3.4, released in 2018, added system-versioned tables. WITH SYSTEM VERSIONING, FOR SYSTEM_TIME AS OF, hidden ROW_START and ROW_END columns. The syntax is almost identical to SQL Server’s. It works, it’s been in production for years, and it’s a genuinely good implementation.
But MariaDB is not MySQL, and hasn’t been for a long time.
MariaDB forked in 2009, right before Oracle closed the Sun acquisition. Since then the two codebases have drifted in a lot of directions that actually matter in production: different GTID implementations, different JSON handling, different authentication defaults, different binlog compression, storage engines on one side that don’t exist on the other. System-versioned tables are one more item on a list that gets longer with every release. At some point the honest framing stops being “MariaDB is a MySQL replacement” and starts being “MariaDB is a separate database that shares an ancestor with MySQL.”
Row-level history is one of the cleanest illustrations of that drift. MariaDB built it. MySQL didn’t. Not in 8.0, not on the 8.4 LTS roadmap, no public signal from Oracle that it’s coming.
There’s a reason, and it’s not technical.
Oracle acquired MySQL through the Sun deal in 2010. Oracle also sells Oracle Database, where Flashback has been a flagship feature since 2001. It is not in Oracle’s commercial interest for MySQL to reach parity with Oracle on row-level history. The incentive points the other way. MariaDB, governed by a foundation explicitly set up to do what Oracle wouldn’t, built the feature. Oracle MySQL hasn’t, and the bet that it eventually will is a bet against twenty-five years of incentives pointing in the opposite direction.
So everyone actually running MySQL in production (Oracle MySQL Community, Percona Server, Aurora MySQL, RDS MySQL) still has the same gap they had on day one.
The architectural counterpoint
There’s one fair objection worth addressing because DBAs will raise it anyway. Flashback and Temporal Tables are integrated into the engine. dbtrail is not. AS OF TIMESTAMP in Oracle lives inside the SQL dialect, so the application doesn’t care that history exists, it just queries backwards in time.
That’s true. But “integrated into the engine” actually covers two very different things, and pulling them apart is where this gets interesting.
Oracle Flashback and CockroachDB’s AS OF SYSTEM TIME work on any table in the database without opt-in. You never had to declare in advance that a given table might need history someday. If undo retention (or the GC window) covers the range, you can query the past on any row. They’re retroactive.
SQL Server Temporal Tables, MariaDB system-versioned tables, and the PostgreSQL temporal_tables extension all require per-table opt-in, ahead of the incident. You have to add a PERIOD FOR SYSTEM_TIME clause, declare a history table, turn on versioning, and do it before the data you want to recover is modified. If you didn’t opt in, the feature isn’t there when you need it. Those are proactive.
That distinction matters more than most product pages admit. When an overnight job corrupts customer data, you don’t go back in time and enable temporal tables retroactively. You either had it on for that table, or you didn’t.
dbtrail reads binlogs that MySQL already writes. Every table gets captured, with no opt-in, no schema change, no trigger. That makes it retroactive in the same sense as Flashback: if the binlog retention covers the range, any row on any table is queryable in the past, whether or not you anticipated needing it.
The other side of the comparison is transactional cost. Flashback depends on undo retention, and pushing queries too far back gives ORA-01555: snapshot too old. Temporal Tables write to a history table on every UPDATE and DELETE with full index overhead, on the hot path. MariaDB’s implementation has the same shape. PostgreSQL’s trigger-based extension is worse again, because it runs PL/pgSQL on every write. None of those are free.
dbtrail doesn’t touch the transactional hot path because binlogs are already being written. The history lives outside the engine, which means no index churn, no undo pressure, no “snapshot too old.” The trade-off is that queries happen in a separate surface (MCP, dashboard, API) instead of inline SQL. For the real use cases above, that trade is better than most DBAs initially assume.
Where this leaves us
Flashback has been in Oracle for a quarter century. Temporal Tables landed in SQL Server a decade ago. CockroachDB shipped time travel on day one. PostgreSQL users reach for extensions because the gap is too obvious to ignore. MariaDB forked away from MySQL and built it there.
Oracle MySQL didn’t, won’t, and has no incentive to.
dbtrail is what you reach for when you stayed on MySQL and still want a retroactive answer to “what did this row look like yesterday?”