Releases: dolthub/dolt
Release list
2.2.3
Merged PRs
dolt
- 11366: Include full table scan in index statistics
Have StatsController also create an "empty" statistic entry to indicate no index for costing. - 11365: Add dolt_squash_history procedure
This new procedure will take the content of HEAD, and squash history before it. Default behavior is to squash every commit from height 2 (commit right after initial commit) to HEAD. It is impossible to rewrite the initial commit.
This will be much faster than rebasing all history. - 11358: Fix left outer merge join wrong results with residual filters
Tests added in dolthub/go-mysql-server#3655
Fixes #11350
Based on #11351 - 11354: add missing execution call for
sqlparser.Executestatements
We were missing calls toiter.Next()anditer.Close(), when executing prepared statements throughdolt sql -q "...",
some tests in: dolthub/go-mysql-server#3653
fixes: #11345 - 11351: kvexec: fix left outer merge join wrong results with residual filters
Fixes #11350 — wrong results frommergeJoinKvIterfor left outer merge joins whose key runs contain duplicates and whose join condition carries a residual (non-merge-key) filter.
Two one-line-class defects, both in thematch:loop:l.matchedLeft = okoverwrote earlier successes. A left row that matched mid-run was re-flagged unmatched by a later failed pairing in the same run, so the run transition emitted a spurious null-extended row for it. Changed tol.matchedLeft = l.matchedLeft || ok; the run-transition reset remains the sole reset point.io.EOFduring the left advance dropped the final owed null-extension. Thecompare:loop handles left-EOF viaoldLeftKey; thematch:loop's advance branch returned the error immediately, losing the null-extended row for a final unmatched left row. Now it emits that row (whenisLeftJoin && !matchedLeft) before surfacing EOF.
Minimal repro (returnsfr:app|NULLinstead ofnl:app|NULLbefore this patch):
The regression test registers the kvexeccreate table b2 (cat varchar(50) not null, code varchar(16) not null, lang varchar(20) not null, primary key(cat, code, lang), key(code)); create table t2 (id varchar(36) primary key, code varchar(16), lang varchar(16), key(code, lang)); insert into b2 values ('cat0','P1','de:app'),('cat1','P1','fr:app'),('cat2','P1','nl:app'); insert into t2 values ('t1','P1','de'),('t2','P1','es'),('t3','P1','fr'); select /*+ MERGE_JOIN(p,w) */ p.lang, w.lang from b2 p left join t2 w on w.code = p.code and w.lang = substring_index(p.lang, ':', 1);
ExecBuilderon the test engine (as the CLI/server engines do) so the kvexec iterator — not the GMS fallback, which was already correct — is what's exercised; the new test fails 7 assertions without the fix and passes with it.kvexecsuite andenginetestTestJoinQueries/TestJoinOpspass. Also verified against a 15k-row synthetic (anti-join now agrees exactly withNOT EXISTS: 5,000) and the production database where this was found (197-row orphan audit no longer inflates to ~15k when the planner picks the merge join path).
🤖 Generated with Claude Code
https://claude.ai/code/session_01VpTxMfzCXHoM8ojJn5aQiv - 11344: Fail amend commits when branch head has moved
When a SQL transaction ranCALL DOLT_COMMIT('--amend')after another connection had added a commit or merged a branch, the amend silently replaced the transactionHEADcommit up to the newHEADcommit. The data from the erased commits survived, but the commit itself, including merge records we're gone.datas.CommitOptions.Amendis replaced withAmendedCommit(hash.Hash), the address of the commit an amend replaces.datas.BuildNewCommitrejects an amend unlessAmendedCommitis still the datasetHEAD, relying on theCAS inCommitWithWorkingSet` for atomic comparison.dsess.doCommitrunsvalidateAmendedHeadbefore the working set merge to specify the head conflict over a row conflict.- Amend is refused mid
merge,cherry-pick, in parity withgitandrevert, a Dolt-specific addition because it follows the same merge state. - The admin createchunk command's
HEADcheck bypass moves from theAmendflag to a new explicitCommitOptions.Forcefield.
Fix #9072
- 11343: Add retries to branch creation on ErrOptimisticLockFailed errors
Adds a retry loop toNewBranchAtCommitso branch creation can recover from a concurrent optimistic-lock conflict on the working set (e.g. from a background auto-GC cycle). This matches the existing retry behavior of other write paths (e.g. SQL transaction commit). - 11333: Bug fixes for secondary indexes using virtual columns
When a virtual generated column is used in a secondary index, rebuilding the index can generate incorrect index data in some situations. For example, dropping a column in the table will trigger a table rewrite as well a rebuilding the secondary indexes. In this case, the virtual generated column expressions were not properly being evaluated to store the correct data in the index.
go-mysql-server
-
3657: check stats prov for empty stat
Instead of creating the (potentially same) empty stat every time, we should check if it has already been made in the StatsProvider. -
3656: Bug fix for
CASEexpressions withExtendedTypeinstances
Related to: dolthub/doltgresql#2980
Doltgres fix: dolthub/doltgresql#2987 -
3653: adding prepare tests for insert and update
-
3652: Allow aliasing column names in a table function
Depends on: dolthub/vitess#477
-
3644: add ExtendedTableFunction interface to support table function schema …
…on OUT parameters -
3643: Use
buildScalarto getGetFieldexpression forON UPDATEcolumns
fixes #11346
Building theGetFieldexpression for a column using its index in a table was causing indexing issues when there was also a CTE. Instead, we need to build the theGetFieldusingbuildScalarto properly resolve the column within the scope. -
3641: Bug fix for a panic when calling a stored procedure without a database selected
-
3639: have coster consider not picking secondary indexes
This PR adds a "no index" option to the coster and some additional heuristics for index costing.
Before, we would always pick any available index, which would sometimes be sub-optimal, especially if the index is a non-covering secondary index.
Additionally, thenormal_distandexp_disttables are now seeded to reduce random variability between test runs.
Benchmarks:
#11336 (comment) -
3638: Bug fixes for secondary indexes using virtual columns
When updating a row in a secondary index, any virtual generated columns need their generation expression evaluated to get the correct data stored in the secondary index. There were a few edge case with virtual columns and secondary indexes where this evaluation wasn't happening. This PR closes those gaps.
The first case is when a table rewrite is performed and secondary indexes are rebuilt (e.g. when dropping a column on a table). Not properly evaluating a virtual column's expression caused incorrect data to be stored in the index when it was rebuilt.
A second case is for referential actions on a foreign key that update a table and its secondary index. Not properly evaluating a virtual column's expression here can also cause incorrect data to be written to the secondary index.
Fix for failing Dolt CI integration test in: #11333 -
3634: Add support for multiple expressions in functional indexes
-
3623: memory: use stable sort for secondary index ordering
memory.TableData.sortSecondaryIndexessorted secondary index storage withsort.Slice, which uses Go's unstable pdqsort algorithm. Rows that tie on the indexed columns (same key, different primary key) could be reordered relative to each other on every sort, so repeated index rebuilds of the same data could produce different physical row orders.
This PR switches tosort.SliceStable, which preserves the relative (insertion) order of tied rows, making secondary index storage ordering deterministic across rebuilds.
Fixes #2877 -
3618: Error on SELECT @@SESSION., matching MySQL
GMS silently returned the global value when a GLOBAL-only system variable was read with an explicitly-qualified SESSION/LOCAL scope (e.g.SELECT @@SESSION.innodb_autoinc_lock_mode), instead of raising MySQL's ERROR 1238 (ErrSystemVariableGlobalOnly).
buildSysVar's case forSetScope_None/SetScope_Sessionalready carriesspecifiedScope, which is empty for a bare@@fooreference and non-empty ("session"/"local") only when the scope was expli...
2.2.2
Merged PRs
dolt
- 11332: /go/cmd/dolt/cli: only emit backspace characters in a tty
The CLI's progress spinners (dolt push, pull, fetch, assist, and import/sql progress output) redraw themselves in place by writing backspace control characters. This was done unconditionally, with no check for whether output was going to a terminal.
When stdout/stderr is captured to a file or pipe — a script, a CI job, dolt push > push.log 2>&1 — those backspaces become literal bytes in the log instead of cursor movements. Rendering the log then "erases" the surrounding text. The worst case: an error is printed while the spinner is active, and the spinner's trailing backspaces overwrite the error text, so the captured log looks empty exactly when something went wrong.
This PR fixes this so that TTY is detected and redraw is gated on it. - 11327: Add support for functional indexes with multiple expressions
Picks up support for functional indexes with multiple expressions from GMS (dolthub/go-mysql-server#3634) and adds additional tests for Dolt.
Depends on: dolthub/go-mysql-server#3634 - 11326: unsafe adaptive value
- 11325: gitblobstore: fix premature flush and prune of pending write that was not referenced in current manifest
CheckAndPut("manifest") flushed all queued pendingWrites into the commit, regardless of whether the manifest being written referenced them. That commits a table file into the git tree as an unreferenced entry, which a later manifest flush then legitimately prunes and evicts from the in-memory cache — while the file is still live (about to be added, or being re-read by addTableFiles' refCheckAllSources retry loop). The result is Blob not found. Stale pendingWrites from earlier failed pushes in the same process keep the condition sticky, which is why a restart clears it.
This PR only flushes the pending writes the manifest actually references. Unreferenced writes stay pending until a manifest that references them is written, so they're committed and referenced atomically. After this change a live table file is always either pending and cached (not in the tree, so unprunable) or committed and referenced (never pruned) — an unreferenced tree entry can no longer coexist with being live. - 11320: go: sql_server_driver: Fix a race on *SqlServer.Cmd access. Start the command before we spawn its wait goroutine.
- 11316: go: sqle/cluster: Improve dolt_cluster_ack_writes_timeout_secs circuit breaker behavior.
Previously, the circuit breaker only had an open and a closed state. It would only go from open to closed when the replication thread fully quiesced. This PR adds a half-open state, where it lets a single commit probe for replication success within the configured timeout. If that wait succeeds, the circuit breaker closes.
This lets high write throughput use cases reenable their configured replication waits even when writes arrive more requently than their typical replication delay. - 11314: go: sqle/cluster: controller.go: Make DROP DATABASE replication participate in dolt_cluster_ack_writes_timeout_secs.
- 11313: go: Fix some data races surfaced by running go-sql-server-driver tests under -race.
- 11312: go/store/nbs: Fix a race between the journal persister and PruneTableFiles.
When PruneTableFiles runs, it is willing to delete the journal file if there is no longer a reference to it in the manifest. This can race with bootstrapJournal making a new journal file. The journal file can be on disk, and seen by PruneTableFiles and thus selected for removal, before the journal has been added to the chunk store proper.
This change makes the journal writer participate in the retained files machinery, same as the table file persister, so that the journal is correctly considered not-safe-for-delete-by-PruneTableFiles before it is ever created on disk and potentially selected for deletion by PruneTableFiles. - 11305: fix stats functions to include schema
We lookup stats with empty string for schema. Since this is not always the case, we sometimes miss stats. - 11290: Bump golang.org/x/net v0.54.0 => v0.55.0 for Go 1.27 compatibility
Go 1.27 change:- https://github.com/golang/go/discussions/60746
- golang/go#67810
Problem found in - Homebrew/homebrew-core#288803
Build error:
$ go1.27rc1 build -v ./cmd/dolt google.golang.org/grpc/internal/transport # google.golang.org/grpc/internal/transport ../../../../go/pkg/mod/google.golang.org/grpc@v1.79.3/internal/transport/handler_server.go:271:18: undefined: http2.TrailerPrefixgolang.org/x/netupgradeBuild successful:$ go get golang.org/x/net@v0.55.0 && go mod tidy go: upgraded golang.org/x/net v0.54.0 => v0.55.0$ go1.27rc1 build -v ./cmd/dolt
go-mysql-server
- 3634: Add support for multiple expressions in functional indexes
- 3627: fix stats functions to include schema
We include the schema in the stats key, but only sometimes fill in the field. - 3626: Prevent index offset when comparing rows in
topRowsIter
Fixes #11300
Appending the row order number to the end of asql.Rowduring a Top-N Heap Sort intopRowsIterwas causing an index offset when evaluating SortFields that were subqueries, thus resulting in incorrect result.
This PR- modifies
topRowsHeapto instead take arowWithOrderstruct that separates out thesql.Rowfrom the order number while still taking the order number into account when sorting the heap. - refactoring
Sorter.LesserRowlogic into a newCompareRowsfunction to allow checking for row equality - moves top row(s) iterators to its own file as part of an effort to make our iterators more organized (#3620). This file also includes
topRowsHeapsince it is only ever used bytopRowIter(see dolthub/go-mysql-server#3622 (comment) for next steps) - removes
ValueRowSortersince it's actually never used anywhere and doesn't even fully implementsort.Interface
- modifies
- 3617: special case on extended type for getting compare type
Closed Issues
- 11265: Dolt INSERT … SELECT with CTE + JSON_TABLE disconnects
- 3622: Clean up: move sorters in
sql/expression/sort.goout ofexpressionpackage
Performance
| Read Tests | MySQL | Dolt | Multiple |
|---|---|---|---|
| covering_index_scan | 17.32 | 2.3 | 0.13 |
| groupby_scan | 134.9 | 142.39 | 1.06 |
| index_join | 3.43 | 1.89 | 0.55 |
| index_join_scan | 4.18 | 1.32 | 0.32 |
| index_scan | 350.33 | 215.44 | 0.61 |
| oltp_point_select | 0.19 | 0.25 | 1.32 |
| oltp_read_only | 3.62 | 4.91 | 1.36 |
| select_random_points | 0.35 | 0.51 | 1.46 |
| select_random_ranges | 0.39 | 0.64 | 1.64 |
| table_scan | 350.33 | 200.47 | 0.57 |
| types_table_scan | 746.32 | 467.3 | 0.63 |
| reads_mean_multiplier | 0.88 |
| Write Tests | MySQL | Dolt | Multiple |
|---|---|---|---|
| oltp_delete_insert | 7.7 | 6.21 | 0.81 |
| oltp_insert | 4.03 | 3.13 | 0.78 |
| oltp_read_write | 8.74 | 11.24 | 1.29 |
| oltp_update_index | 4.33 | 3.3 | 0.76 |
| oltp_update_non_index | 4.1 | 3.02 | 0.74 |
| oltp_write_only | 5.18 | 6.21 | 1.2 |
| types_delete_insert | 8.28 | 6.67 | 0.81 |
| writes_mean_multiplier | 0.91 |
| TPC-C TPS Tests | MySQL | Dolt | Multiple |
|---|---|---|---|
| tpcc-scale-factor-1 | 95.13 | 51.54 | 1.85 |
| tpcc_tps_multiplier | 1.85 |
| Overall Mean Multiple | 1.21 |
|---|
2.2.1
Merged PRs
dolt
- 11310: cluster: wait for outstanding DROP DATABASE replication during graceful transition to standby
dolt_cluster_transition_to_standby determines whether a standby replica is fully caught up by waiting on three replication subsystems: per-database commit hooks, users/grants, and branch control. It never accounted for outstanding DROP DATABASE replications, which are tracked separately in Controller.outstandingDropDatabases and driven by independent fire-and-forget goroutines.
As a result, the transition could report a standby as fully caught up and succeed while a DROP DATABASE had not yet replicated to it. This PR makes it so that dolt_cluster_transition_to_standby also waits for outstanding DROP DATABASE statements to replicate. - 11308: go.mod: bump eventsapi_schema to latest revision
go-mysql-server
- 3626: Prevent index offset when comparing rows in
topRowsIter
Fixes #11300
Appending the row order number to the end of asql.Rowduring a Top-N Heap Sort intopRowsIterwas causing an index offset when evaluating SortFields that were subqueries, thus resulting in incorrect result.
This PR- modifies
topRowsHeapto instead take arowWithOrderstruct that separates out thesql.Rowfrom the order number while still taking the order number into account when sorting the heap. - refactoring
Sorter.LesserRowlogic into a newCompareRowsfunction to allow checking for row equality - moves top row(s) iterators to its own file as part of an effort to make our iterators more organized (#3620). This file also includes
topRowsHeapsince it is only ever used bytopRowIter(see dolthub/go-mysql-server#3622 (comment) for next steps) - removes
ValueRowSortersince it's actually never used anywhere and doesn't even fully implementsort.Interface
- modifies
- 3621: bug fix: honor named window reference
An existing bug in GMS was not properly applying a named window reference. We had enginetests for named window references, but they weren't sufficient to catch this because the aggregate function they used produced identical values in both cases. New enginetest cases are added to prevent a regression.
These two issues were identified by Ito automated review, in dolthub/doltgresql#2913 - 3619: sql: Fix ALTER USER to allow the Identity field of the User record to be updated.
CREATE USER ... IDENTITY WITH <plugin> AS '<identity>'correctly parsed and persisted the Identity field.ALTER USERcorrectly parsed the Identity field but failed to persist the changes. The end result is that GMS had a bug where attempt to alter the identity field on an existing user seemed to succeed but was not reflected in the data going forward.
Fix the bug so that updates to Identity are reflected and persisted going forward.
Closed Issues
- 11300: ORDER BY subquery loses effect as soon as LIMIT and OFFSET become involved
Performance
| Read Tests | MySQL | Dolt | Multiple |
|---|---|---|---|
| covering_index_scan | 18.61 | 2.3 | 0.12 |
| groupby_scan | 134.9 | 144.97 | 1.07 |
| index_join | 3.49 | 1.93 | 0.55 |
| index_join_scan | 4.25 | 1.3 | 0.31 |
| index_scan | 350.33 | 219.36 | 0.63 |
| oltp_point_select | 0.19 | 0.25 | 1.32 |
| oltp_read_only | 3.62 | 4.91 | 1.36 |
| select_random_points | 0.35 | 0.51 | 1.46 |
| select_random_ranges | 0.38 | 0.64 | 1.68 |
| table_scan | 350.33 | 196.89 | 0.56 |
| types_table_scan | 759.88 | 442.73 | 0.58 |
| reads_mean_multiplier | 0.88 |
| Write Tests | MySQL | Dolt | Multiple |
|---|---|---|---|
| oltp_delete_insert | 7.7 | 6.21 | 0.81 |
| oltp_insert | 4.1 | 3.19 | 0.78 |
| oltp_read_write | 8.74 | 11.24 | 1.29 |
| oltp_update_index | 4.41 | 3.3 | 0.75 |
| oltp_update_non_index | 4.1 | 3.02 | 0.74 |
| oltp_write_only | 5.18 | 6.21 | 1.2 |
| types_delete_insert | 8.43 | 6.79 | 0.81 |
| writes_mean_multiplier | 0.91 |
| TPC-C TPS Tests | MySQL | Dolt | Multiple |
|---|---|---|---|
| tpcc-scale-factor-1 | 96.05 | 52.22 | 1.84 |
| tpcc_tps_multiplier | 1.84 |
| Overall Mean Multiple | 1.21 |
|---|
2.2.0
This release is nearly identical to 2.1.11. There are no novel merged PRs in this release.
This release is a minor version bump because the authentication of tokens with auth plugin authentication_dolt_jwt changed in 2.1.11. This could potentially effect deployments which are currently minting JWTs tokens for authentication to Dolt, but it is not expected to effect any existing deployment. See #11296 for more details.
Performance
| Read Tests | MySQL | Dolt | Multiple |
|---|---|---|---|
| covering_index_scan | 17.63 | 2.3 | 0.13 |
| groupby_scan | 134.9 | 144.97 | 1.07 |
| index_join | 3.55 | 1.93 | 0.54 |
| index_join_scan | 4.25 | 1.32 | 0.31 |
| index_scan | 350.33 | 219.36 | 0.63 |
| oltp_point_select | 0.19 | 0.25 | 1.32 |
| oltp_read_only | 3.68 | 5.0 | 1.36 |
| select_random_points | 0.36 | 0.52 | 1.44 |
| select_random_ranges | 0.39 | 0.65 | 1.67 |
| table_scan | 350.33 | 200.47 | 0.57 |
| types_table_scan | 746.32 | 450.77 | 0.6 |
| reads_mean_multiplier | 0.88 |
| Write Tests | MySQL | Dolt | Multiple |
|---|---|---|---|
| oltp_delete_insert | 7.7 | 6.21 | 0.81 |
| oltp_insert | 4.03 | 3.19 | 0.79 |
| oltp_read_write | 8.9 | 11.24 | 1.26 |
| oltp_update_index | 4.33 | 3.3 | 0.76 |
| oltp_update_non_index | 4.1 | 3.02 | 0.74 |
| oltp_write_only | 5.18 | 6.32 | 1.22 |
| types_delete_insert | 8.28 | 6.79 | 0.82 |
| writes_mean_multiplier | 0.91 |
| TPC-C TPS Tests | MySQL | Dolt | Multiple |
|---|---|---|---|
| tpcc-scale-factor-1 | 95.75 | 52.65 | 1.82 |
| tpcc_tps_multiplier | 1.82 |
| Overall Mean Multiple | 1.20 |
|---|
2.1.11
Merged PRs
dolt
- 11302: /go/libraries/doltcore/sqle/{dsess,enginetest}: add regression test and lazy load db
Creating databases and then querying them across multiple sessions intermittently fails with could not resolve initial root for database /.
A DoltTransaction snapshots the noms root of every known database when it begins. If one session creates a database after another session's transaction has started, the second session can resolve that database from the shared provider, but its transaction has no start-point for it, so TransactionRoot errors. The only code that added late-appearing databases to a transaction was the read-replica clone path; nothing handled the concurrent-create case.
This PR makes it so that in DoltSession.addDB, if a database isn't in the transaction's snapshot, lazily register it (tx.AddDb) with its current root on first reference — same behavior the clone path already relies on. Also normalized AddDb's map key to the base name to match NewDoltTransaction/GetInitialRoot. - 11298: fsck should accept stash objects
fixes: #11292 - 11296: go: jwtauth: When using auth plugin authentication_dolt_jwt, validate the Subject claim against the expected claims.
A bug in JWT token authentication meant that an incoming token's Subject claim was not validated against the set of expected claims listed in the User record.
The end result is that a given, valid JWT token was not scoped to authenticating a single user account. Any valid JWT token bearing the correct signature, audience and issuer could be used to connect as any user account which was configured to accept that JWKS source, issuer and audenience field.
The fungible token behavior is too big of a footgun to leave in place as the default. To restore fungible token behavior, leave thesub=claim off the expected claims in the User Identity field.
This is a backward incompatible change. Existing workflows expecting fungible tokens to work may need to change token issuance or User record Identity fields to upgrade. - 11286: New engine init interface
This lets integrators register engine initialization logic before connections are accepted. - 11285: Correctly convert foreign key lookups across encoding type boundaries
Tests are in Doltgres, should be a no-op for Dolt
Companion PR: dolthub/doltgresql#2914 - 11283: go: sqle: Cleanup comments and function names in DatabaseProvider around creatingDatabases.
Restructure the regression test to be a go-sql-server-driver test and to run a self-clone. - 11281: go: sqle: Fix concurrent-clone deadlock between two remotesapi sql-servers
DoltDatabaseProvider.CloneDatabaseFromRemoteheld the providerRWMutexwrite lock across the entire clone, including the network fetch of all chunks from the remote. When a server serves a--remotesapi-port, that fetch hits a peer's remotesapi whose handler needs a read lock on this same provider (viaSessionDatabase). Two sql-servers thatCALL DOLT_CLONEfrom each other's remotesapi at the same time therefore deadlock: each holds its own provider write lock while blocked on the peer, and neither peer can take the read lock needed to answer.DOLT_PULLis immune because it operates on an already-registered database and does not hold the write lock across its fetch. This is the lock the existing// TODO: This holds the database provider lock across the entire duration of the clone ...was warning about.
The fix narrows the lock scope so the network fetch does not run under the provider write lock:- Reserve the database name under a brief lock acquisition (a new
creatingDatabasesset), release the lock for the fetch, then re-acquire it only to register the finished database (registerNewDatabasestill requires the lock). - The reservation keeps concurrent
CREATE DATABASE/dolt_clone/undropof the same name mutually exclusive, so those races behave as before even though the lock is dropped for the fetch. - A shared
databaseNameUnavailableLockedhelper performs the availability checks for all creation paths and preserves the interrupted-create in-progress marker behaviour (ErrIncompleteDatabaseDir/dbfactory.MarkDatabaseInProgress). The in-memory reservation and the on-disk marker are complementary: the marker guards crash recovery across processes, whilecreatingDatabasesserialises concurrent in-process operations while the lock is dropped. - Names in
creatingDatabasesanddeletingDatabasesare normalised withformatDbMapKeyName(Dolt database names are case-insensitive), matching howp.databasesis keyed. UnlikedeletingDatabases,creatingDatabasesdeliberately does not gate database enumeration: an in-progress clone is simply invisible until it registers, so enumeration / remotesapi reads never block on it.
Tests: integration-tests/bats/sql-server-remotesrv.bats: a regression test that starts two--remotesapi-portservers cloning from each other concurrently. It hangs (clones time out) on the buggy build and passes on the fixed build; it also fails if a clone errors for any non-deadlock reason.go/libraries/doltcore/sqle/database_provider_test.go:TestCreatingDatabaseReservationcovering the reservation semantics — conflict, case-insensitivity across create/clone/undrop, release, and that a reservation does not blockAllDatabases.
Verified withgo test -race ./libraries/doltcore/sqle/and the new bats test;gofmt/goimportsclean.
Fixes #11280
- Reserve the database name under a brief lock acquisition (a new
- 11269: Fix spurious unique key error when staging workspace rows
Staging a delete and an insert that reuse the same unique key viadolt_workspace_*failed with a spurious duplicate unique key error, because rows were applied in workspace order and checked per row.- Updater now applies all deletes first and defers the puts to statement completion
Fix #11195
- Updater now applies all deletes first and defers the puts to statement completion
- 11267: go: grpcreresolve: Add a load balancer implementation which requests ResolveNow for endpoints when we see Unavailable or DeadlineExceeded.
When running Dolt within a k8s cluster with a service mesh like Istio, the L4 connection to the first-hop load balancer can stay open even when the endpoint associated with that name moves on. gRPC's default pick_first load balancer will not reresolve the endpoints associated with the name unless the L4 connection is lost, which causes persistent failures instead of quick recovery.
Here we implement a new load balancer strategy which just wraps pick_first. It listens for Done on the RPCs, and it calls resolver.ResolveNow if it sees Unavailable or DeadlineExceeded. The DNS resolver in gRPC already debounces resolve requests, so this does not spam reresolves too quickly in the case of true upstream unavailability. - 11266: Bug fix:adaptive encoding inserts to a unique index
Uses theValueStorefrom theExtendedTupleComparatorinstance when set, so that out-of-band data can be correctly loaded.
Related: dolthub/doltgresql#2886 - 11247: Fix slow clone/fetch from git remotes
Reading a git-backed table file went chunk by chunk, and because git cat-file cannot serve a byte range, each read re-streamed the whole object from the start. Fetching a large table spawned thousands of git processes and stalled.- Spool a git-backed table file to a local temp file on open and serve its chunk reads locally
- Look up the sizes of a chunked object's parts in a single
git cat-file --batch-checkcall - Detect an early reader close by end of stdout, so a partial read is torn down the same way on every platform
Fix #11236
- 11246: Skip incomplete database directories during discovery
Dolt can leave a half-written directory on disk when creating or cloning a database, affectingINFORMATION_SCHEMAand similar queries to error. Dolt now places a marker file in the directory before creation starts and durably removes it once the database is complete.- Dolt storage without a repo state file is now skipped with a warning at load time
- Write
.dolt_safe_to_ignoremarker file when creation a database - Skip marked files or incomplete directories with a warning
CREATE DATABASEor clone over a leftover directory now returns an error asking for removal
Fix #11206
- 11052: Support covering secondary indexes on DOLT_DIFF() table function.
DOLT_DIFF provides a way to efficiently compare the changes to a table at two commits. If the user only cares about changes to rows within a range of key values, DOLT_DIFF exposes an index that wraps the primary index on the underlying table.
This PR adds additional indexes to DOLT_DIFF that similarly wrap the underlying table's secondary indexes. This allows a user to efficiently compare changes to rows within a range of secondary key values.
This only works if the secondary index is covering, since otherwise the index can't be used to detect modified rows if the only modified columns are outside the index.
go-mysql-server
- 3619: sql: Fix ALTER USER to allow the Identity field of the User record to be updated.
CREATE USER ... IDENTITY WITH <plugin> AS '<identity>'correctly parsed and persisted the Identity field.ALTER USERcorrectly parsed the Identity field but failed to pe...
2.1.10
Merged PRs
dolt
- 11257: go: merge: Fix two bugs in merge related to adaptive encoding.
When the left and right side of a merge have identical columns but the columns have different encoding, the merge logic did the wrong thing.- When running check expressions, it would (try to) read the tuples using the wrong encoding in some cases.
- When performing a structural merge, it would fail to correctly rewrite the tuples in the resulting tables in some cases.
In most cases this resulted in failed merges. In some cases, this resulted in corrupted tables where the misencoded values could no longer be successfully read.
Developed with red/green tests which are left as regression tests.
- 11254: go: doltcore/merge,prolly/tree: Make merge paths recover from panics encountered during merge and return errors directly, instead of crashing.
Not recovering while in a goroutine causes the process to crash. Especially in server context, we would rather return the error. - 11252: go/store/types: serial_message: Fix a bug which caused a panic when debug printing a schema with a CheckConstraint.
- 11248: Add a global credential helper for remotesapi
- 11234: Fix dolt_log system table on shallow clones
On a shallow clone, theDOLT_LOGsystem table failed to properly show commits when ghost commits were encountered. The table now skips ghost commits so it stops at the shallow boundary.- Skip ghost commits in
DOLT_LOGwalk - Report the row count as inexact only on shallow clones; full clones keep the existing O(1) fast count
- Add
DoltDB.IsShallow(), backed by a newchunks.GhostChunkStoreinterface
Fix #11230
- Skip ghost commits in
go-mysql-server
- 3605: Use
TableIds instead of names when mapping filters to tables
Split off from #3591
Using table names prevented us from being able to distinguish between tables from different databases with the same unqualified name.
This PR also updates places where we were not using the correctTableId- when condensing a
SubqueryAliasinto aTableAlias, the newTableAliasshould have the sameTableIdas theSubqueryAlias - A view should be getting its
TableIdfrom the scope via its name, not the name of its first column
- when condensing a
- 3603: Only get projection expressions once when creating filterSet during filter pushdown
Split off from #3591
getProjectionExpressionsis only called once, instead of for eachFilternode.tableAliasesis also removed because it's never actually used anywhere. - 3602: Create single override point for
SplitConjunction
SplitConjunctionworks differently for Doltgres expressions. The integration with Doltgres relied onanalyzer.SplitConjunctionandmemo.SplitConjunctionbeing replaced and called instead ofexpression.SplitConjunction; however,expression.SplitConjunctionwas still being called in many places in the analyzer. Not calling the correct version ofSplitConjunctionwas preventing filter expressions from being properly pushed down in Doltgres. Furthermore,expression.SplitConjunctionwas still being called inrowexecand callinganalyzer.SplitConjunctionfrom therowexecpackage causes a cyclical import.
To avoid confusion between the variousSplitConjunctioninstances, this PR replaces the various override variables with a single one in theexpressionpackage. This allowsSplitConjunctionto be consistently replaced throughout GMS without requiring GMS developers to have knowledge ofSplitConjunctionworking differently in Doltgres.
This change is integrated into Dolgres in dolthub/doltgresql#2865.
This issue was originally encountered while working on #3591. - 3601: cache
GeneralizeTypesoutput and avoidfmt.SprintfinHashOfSimple
This PR adds some optimizations to CASE statements and HashLookups - 3598: Fix
JSON_LENGTHand member-access lookups to match MySQL
JSON_LENGTHreturnedNULLfor empty arrays and the JSON null literal, and member-access paths (.key/.*) on an array returned[]instead ofNULL. - 3594: allow using star expr after sql value for Doltgres
vitess
- 474: go/mysql: add Conn.WaitForClientActivity to detect a departed client
On context cancelation, the function returns with anilerror. If the client unexpectedly writes to the connection or closes it, then this method will return a non-nilerror.
Unlike the first attempt at this method, this new implementation accounts for LoadDataInFile. It uses a preempt-able read lease to let the handler read from the client socket even while WaitForClientActivity is outstanding. The Peek becomes active once again after the reads are issued and completed. - 473: Revert "Merge pull request #472 from dolthub/aaron/vitess-async-read-eof"
This reverts commit 02705d5447c2e7062c3ae31f6909d4e5e1c97812, reversing changes made to 0893abc805429d8a31ea9fa395ff118fa95474da.
Closed Issues
- 11244: Support refreshable custom gRPC metadata for remotes
- 11230: Select from dolt_log error When clone from repository with depth 1
- 11224: Dolt JSON_LENGTH('[]') Bug
Performance
| Read Tests | MySQL | Dolt | Multiple |
|---|---|---|---|
| covering_index_scan | 17.32 | 2.3 | 0.13 |
| groupby_scan | 134.9 | 139.85 | 1.04 |
| index_join | 3.68 | 2.0 | 0.54 |
| index_join_scan | 4.41 | 1.34 | 0.3 |
| index_scan | 350.33 | 215.44 | 0.61 |
| oltp_point_select | 0.19 | 0.24 | 1.26 |
| oltp_read_only | 3.62 | 4.91 | 1.36 |
| select_random_points | 0.35 | 0.55 | 1.57 |
| select_random_ranges | 0.38 | 0.64 | 1.68 |
| table_scan | 350.33 | 200.47 | 0.57 |
| types_table_scan | 759.88 | 450.77 | 0.59 |
| reads_mean_multiplier | 0.88 |
| Write Tests | MySQL | Dolt | Multiple |
|---|---|---|---|
| oltp_delete_insert | 7.7 | 6.21 | 0.81 |
| oltp_insert | 4.03 | 3.13 | 0.78 |
| oltp_read_write | 8.9 | 11.24 | 1.26 |
| oltp_update_index | 4.33 | 3.3 | 0.76 |
| oltp_update_non_index | 4.1 | 3.02 | 0.74 |
| oltp_write_only | 5.18 | 6.21 | 1.2 |
| types_delete_insert | 8.43 | 6.79 | 0.81 |
| writes_mean_multiplier | 0.91 |
| TPC-C TPS Tests | MySQL | Dolt | Multiple |
|---|---|---|---|
| tpcc-scale-factor-1 | 96.65 | 53.16 | 1.82 |
| tpcc_tps_multiplier | 1.82 |
| Overall Mean Multiple | 1.20 |
|---|
2.1.9
Merged PRs
dolt
- 11238: go: sqle/clusterdb: Fix spurious error logs. Interact with quiescable events database protocol.
This also enables quiesced event reloads even when cluster replication is enabled. - 11237: go: store/pull,remotestorage: Add optional logging to
dolt pull/fetch/push.
If you setPUSH_LOG=truewhen runningdolt pull,dolt fetchordolt push, some extra logging output will appear in.dolt/temptf/push-*.log.
go-mysql-server
- 3594: allow using star expr after sql value for Doltgres
Closed Issues
2.1.8
Merged PRs
dolt
- 11217: remove the dolt archive command
Thedolt archivecommand was a precursor to GC support using archives. It offered the ability to test and roll back is problems arose. Now that archives are the default and have been for awhile, this command seems unnecessary. - 11215: fix silent data loss for large JSON values with control characters
Large JSON values containing control characters were silently dropped on read. The stored text was re-parsed strictly, and an over-eager unescape step corrupted any non-HTML\uXXXXescape.- preserve non-HTML unicode escapes in
unescapeHTMLCodepoints - guard against truncated escapes
Fix #11210
Depends on dolthub/go-mysql-server#3589
Close #11218
- preserve non-HTML unicode escapes in
- 11214: go,integration-tests: Enable session aware safepoint controller by default.
Fixes a regression in the session-aware controller where fast-complete no-op GC was not working as intended. - 11192: reject column reference args in dolt_diff with a clear error
DOLT_DIFFrequires literal commit and table arguments, but an argument referencing a column (e.g.branch.namefrom a correlated subquery) panics at evaluation with an unknown field index.
Refs #11187
go-mysql-server
- 3595: Rollback event-driven stale-client detection.
- 3593: server/handler.go: Use event-driven notification to quickly cancel a running query after a client-disconnect, instead of polling socket state in a platform-dependent way.
- 3590: server: Use pollForClosedConnection on all query paths, including DML, DDL, OkResults, empty result schemas, etc.
- 3589: fix JSON control-character escaping and out-of-band text reads
Escape every control character in JSON output and unwrap lazily loaded text so JSON functions read it correctly.- escape
U+0000toU+001Fas\uXXXX, keeping the short forms for backspace, tab, newline, form feed, and carriage return. - unwrap StringWrapper before evaluating
JSONfunctions, fixingJSON_VALIDand friends on large keylessTEXT - add serializer, JSON_QUOTE, and function tests
Block #11215
- escape
- 3587: go.mod: Bump github.com/lestrrat-go/strftime to v1.2.0. Fixes a lock leak which can deadlock queries in Dolt.
- 3585: Add right side lookup overhead for LookupJoins
LookupJoins were getting costed lower than they should be, resulting in bad join strategies in some cases.
This PR adjusts the coster and includes an overhead for the right side of a lookup join.
An example of the bad join is:The more optimal plan uses a HashJoinexplain SELECT SUM(CASE WHEN s_quantity = 0 THEN 1 ELSE 0 END) AS stockouts, SUM(ol.ol_amount) AS total_cost FROM stock s JOIN order_line ol ON s.s_i_id = ol.ol_i_id WHERE s.s_i_id <= 7500; plan --------------------------------------------------------------------------------------------------------------------------------------------------------------- Project ├─ columns: [sum(case when s.s_quantity = 0 then 1 else 0 end as case when s_quantity = 0 then 1 else 0 end) as stockouts, sum(ol.ol_amount) as total_cost] └─ GroupBy ├─ select: SUM(CASE WHEN s.s_quantity = 0 THEN 1 ELSE 0 END as CASE WHEN s_quantity = 0 THEN 1 ELSE 0 END), SUM(ol.ol_amount) ├─ group: └─ LookupJoin ├─ TableAlias(ol) │ └─ Table │ ├─ name: order_line │ └─ columns: [ol_i_id ol_amount] └─ Filter ├─ s.s_i_id <= 7500 └─ TableAlias(s) └─ IndexedTableAccess(stock) ├─ index: [stock.s_i_id] ├─ columns: [s_i_id s_quantity] └─ keys: ol.ol_i_id (17 rows)Source: I made it up.explain SELECT /*+ HASH_JOIN(ol, s) */ HINT SUM(CASE WHEN s_quantity = 0 THEN 1 ELSE 0 END) AS stockouts, SUM(ol.ol_amount) AS total_cost FROM stock s JOIN order_line ol ON s.s_i_id = ol.ol_i_id WHERE s.s_i_id <= 7500; plan --------------------------------------------------------------------------------------------------------------------------------------------------------------- Project ├─ columns: [sum(case when s.s_quantity = 0 then 1 else 0 end as case when s_quantity = 0 then 1 else 0 end) as stockouts, sum(ol.ol_amount) as total_cost] └─ GroupBy ├─ select: SUM(CASE WHEN s.s_quantity = 0 THEN 1 ELSE 0 END as CASE WHEN s_quantity = 0 THEN 1 ELSE 0 END), SUM(ol.ol_amount) ├─ group: └─ HashJoin ├─ s.s_i_id = ol.ol_i_id ├─ TableAlias(ol) │ └─ Table │ ├─ name: order_line │ └─ columns: [ol_i_id ol_amount] └─ HashLookup ├─ left-key: (ol.ol_i_id) ├─ right-key: (s.s_i_id) └─ Filter ├─ s.s_i_id <= 7500 └─ TableAlias(s) └─ Table ├─ name: stock └─ columns: [s_i_id s_quantity]
vitess
- 473: Revert "Merge pull request #472 from dolthub/aaron/vitess-async-read-eof"
This reverts commit 02705d5447c2e7062c3ae31f6909d4e5e1c97812, reversing changes made to 0893abc805429d8a31ea9fa395ff118fa95474da. - 472: go/mysql: Conn: Add WaitForClientActivity, which has semantics that allow a server to quickly cancel inflight work if a client goes away.
- 471: add not valid on fkey and check constraints
Closed Issues
- 11210: 3 Dolt JSON Bugs for large JSON payloads / parallel execution of JSON payloads.
- 10854: Feature Request: git-backed dolt support for "gcrypt::" transport protocol
- 11205: Pre-2.1 ALTERed TEXT columns: mixed-row 'invalid hash length: 19' panics on 2.1.4; schema-encoding-drift check reports clean; rows not repairable via SQL
- 11209: sql-server: DOLT_CLONE from a file:// remote fails "manifest referenced table file for which there is no chunkSource" after the remote is rewritten in the same server process
2.1.7
Merged PRs
dolt
- 11200: fix: Improve remote interactions against file remotes from within long-lived processes.
Dolt currently has a singleton cache which can cache file remotes across calls to things likedolt_cloneanddolt_fetch. This change ensures that Dolt Rebases the store when it is interacting with the remote so that it always sees the latest novelty. - 11198: go/utils/publishrelease/buildindocker.sh: Bump CC toolchain. gcc 16.1.0, musl 1.2.6, binutils 2.46.0, mimalloc 3.3.2.
- 11197: bug fix for blame view
Recent change made results start including working set changes, which we don't want - 11135: fix: make findCommonCommit deterministic for criss-cross merges
- 10976: fix(sql): use LEFT JOIN in blame view to handle schema-only reverts
go-mysql-server
- 3587: go.mod: Bump github.com/lestrrat-go/strftime to v1.2.0. Fixes a lock leak which can deadlock queries in Dolt.
- 3582: fix: panic in regexp functions over large text in group by
REGEXP_REPLACEon a largeTEXTcolumn inside aGROUP BYpanic withSIGSEGV.- Add a
ScriptTestandRegexScriptTestsfor similar regressions onREGEXP_REPLACE,REGEXP_SUBSTR,REGEXP_LIKE, andREGEXP_INSTR.
Depends on dolthub/go-icu-regex#13
- Add a
- 3581: Fix
LIKE 'prefix%'dropping rows with multibyte text
LIKE 'prefix%'is rewritten into an index range for speed, but the upper bound used byte255 (U+00FF), which is not the largest character. Any value after prefix aboveU+00FFfalls outside the range and was silently dropped as aWHERE/JOINfilter. Instead, we build the bound per collation:utf8mb4_0900_binmatches are an exact code-point range, so emitGTE prefix AND LT next(prefix and last character incremented by one skipping the surrogate code points).- Any other collation
GTE prefix AND <LIKE>so the index still narrows the scan while the retainedLIKEkeeps results correct. These collations may not sort by raw character numbers, or have unique rules (i.e., treating trailing spaces as nothing, so "ab" and "ab " count as equal). - Fix TPCH and IMDB plan expectations with old baked-in bound.
Fix #11182
Close #11188
- 3572: Rewrite
CachedResultsnode andcachedResultsIterto not useMemoryManager
This PR is a follow-up to #3561 and further addresses the memory leak mentioned in #3560. While the initial fix in #3561 made it seem like the issue was that we were not properly disposing of theCachedResultsnode, the real issue was that acachedResultsIterthat never wrote to theCachedResultsnode was never disposed of in theMemoryManagerif it was never closed.
This PR rewrites theCachedResultsnode andcachedResultsIterto never actually make use of theMemoryManager. Since we do not make use of parallel partitions, there's no need to have a locking cache. Instead, the results of thecachedResultsIterare stored directly in theCachedResultsnode. This greatly simplifies both theCachedResultsnode and thecachedResultsIter.
This PR also moves theemptyCacheIterandEmptyIterout of theplanpackage and de-dupes any code related to empty iters.
Closed Issues
- 11131: Dolt 2.0.8: adaptive out-of-line TEXT written via migration is unreadable — "invalid hash length: 19" on read (regression vs 2.0.4)
- 11037: Allow passing --date to DOLT_MERGE (as is the case for DOLT_COMMIT)
- 11196: GC kill-connections safepoint leaks connection slots:
dss_concurrent_connectionscounts sessions whose client socket is already gone (pegs atmax_connections) - 7996: Dolt does not support CREATE TABLE ... AS ... with a table definition
- 3154: Undefined behavior for
dolt checkout <remote branch>when two remote branches have the same name - 10802: bug: DOLT_CHECKOUT -b ignores --no-overwrite-ignore when called directly via SQL
- 11042: dolt_merge_base is non-deterministic on multi-LCA criss-cross merges
- 11183: REGEXP_REPLACE over a TEXT column in a parallel GROUP BY grouping key SIGSEGVs in go-icu-regex (ICU not thread-safe)
- 11180: 3-way DOLT_MERGE fails on table with VECTOR INDEX: expected prollyIndex, found: durable.proximityIndex
- 11182: LIKE 'prefix %' gives wrong results in WHERE filter vs projection when matching rows have multibyte chars after the prefix
2.1.6
Merged PRs
dolt
- 11179: Set version string in initial server response
MySQL servers return a version string in the initial connection response. Some clients read this and expect certain versions of MySQL or MariaDB. We return8.0.31by default, since that corresponds with the MySQL version we advertise feature parity with.
Currently, we use the@@versionsystem variable as the source of truth for the version string, and this can be configued via a server config file. But we extract the config for the listener before we initialize system variables. Previously this meant that the version string on connection defaulted to the value specified in our vitess dependency, which was hardcoded to8.0.31.
This PR changes the behavior to extract a string from the config if it exists.
We need to use a client integration test for this, since the Go SQL driver ignores the version in the connection info and does not provide a way to view it. - 11153: Bug fix: Doltgres
JSONBuse withdolt_patch()
Fixes dolthub/doltgresql#2732
Closed Issues
- 11166: Expose go-mysql-server's server.Config.Version in dolt sql-server (advertise a MariaDB version for client compatibility)
- 11180: 3-way DOLT_MERGE fails on table with VECTOR INDEX: expected prollyIndex, found: durable.proximityIndex
Performance
| Read Tests | MySQL | Dolt | Multiple |
|---|---|---|---|
| covering_index_scan | 17.32 | 2.3 | 0.13 |
| groupby_scan | 134.9 | 139.85 | 1.04 |
| index_join | 3.49 | 2.0 | 0.57 |
| index_join_scan | 4.25 | 1.34 | 0.32 |
| index_scan | 350.33 | 215.44 | 0.61 |
| oltp_point_select | 0.2 | 0.26 | 1.3 |
| oltp_read_only | 3.82 | 5.09 | 1.33 |
| select_random_points | 0.37 | 0.56 | 1.51 |
| select_random_ranges | 0.39 | 0.65 | 1.67 |
| table_scan | 350.33 | 200.47 | 0.57 |
| types_table_scan | 759.88 | 450.77 | 0.59 |
| reads_mean_multiplier | 0.88 |
| Write Tests | MySQL | Dolt | Multiple |
|---|---|---|---|
| oltp_delete_insert | 8.43 | 6.32 | 0.75 |
| oltp_insert | 4.18 | 3.19 | 0.76 |
| oltp_read_write | 9.22 | 11.45 | 1.24 |
| oltp_update_index | 4.41 | 3.36 | 0.76 |
| oltp_update_non_index | 4.18 | 3.07 | 0.73 |
| oltp_write_only | 5.28 | 6.32 | 1.2 |
| types_delete_insert | 8.58 | 6.79 | 0.79 |
| writes_mean_multiplier | 0.89 |
| TPC-C TPS Tests | MySQL | Dolt | Multiple |
|---|---|---|---|
| tpcc-scale-factor-1 | 93.5 | 52.19 | 1.79 |
| tpcc_tps_multiplier | 1.79 |
| Overall Mean Multiple | 1.19 |
|---|