Skip to content

Context collections accept a database#2847

Merged
Hydrocharged merged 1 commit into
mainfrom
daylon/context-accepts-db
Jun 16, 2026
Merged

Context collections accept a database#2847
Hydrocharged merged 1 commit into
mainfrom
daylon/context-accepts-db

Conversation

@Hydrocharged

Copy link
Copy Markdown
Collaborator

In Dolt, we don't allow multiple databases to be modified in the same statement. Normally this isn't possible, but by accessing certain collections during database creation (such as the cast collection, even if it's not used), we would load those collections. This then caused us to try and persist those collections, even though they're empty. That empty state is still different than "no data present", so it was a valid database update, and could therefore cause multiple databases to be updated.

This change makes it such that every collection is accessed by a database string. In the database creation case, we load a collection under an empty database string (as no database exists yet). This prevents that collection from being persisted (as we don't persist something attached to an empty database name), and works around the multi-database update issue. In addition, this paves the way for proper cross-database support of collection items, which are unsupported at the current moment.

@Hydrocharged Hydrocharged requested a review from fulghum June 15, 2026 12:52
@github-actions

Copy link
Copy Markdown
Contributor
Main PR
covering_index_scan_postgres 1816.62/s 1786.81/s -1.7%
groupby_scan_postgres 134.24/s 134.82/s +0.4%
index_join_postgres 644.19/s 640.66/s -0.6%
index_join_scan_postgres 784.37/s 780.45/s -0.5%
index_scan_postgres 24.16/s 24.31/s +0.6%
oltp_delete_insert_postgres 804.90/s 806.69/s +0.2%
oltp_insert 680.81/s 685.10/s +0.6%
oltp_point_select 2833.72/s 2839.68/s +0.2%
oltp_read_only 2882.38/s 2862.13/s -0.8%
oltp_read_write 2283.26/s 2289.08/s +0.2%
oltp_update_index 716.68/s 717.54/s +0.1%
oltp_update_non_index 750.53/s 747.70/s -0.4%
oltp_write_only 1702.85/s 1723.83/s +1.2%
select_random_points 1819.30/s 1823.69/s +0.2%
select_random_ranges 1086.37/s 1074.68/s -1.1%
table_scan_postgres 22.78/s 22.90/s +0.5%
types_delete_insert_postgres 757.62/s 761.33/s +0.4%
types_table_scan_postgres 7.87/s 8.04/s +2.1%

@itoqa

itoqa Bot commented Jun 15, 2026

Copy link
Copy Markdown

Ito QA test results
Commit: 2937c29: 9 test cases ran, 1 failed ❌, 7 passed ✅, 1 additional finding ⚠️.

Summary

This run exercised core database behavior around keeping schema metadata and type/procedure resolution isolated per database, plus cross-session stability of runtime type expressions and declaration handling. It also covered multi-database statement finalization edge cases that affect whether metadata changes are durably and consistently persisted.

Merge with caution — there is a likely medium-severity regression introduced by this PR in statement finalization logic, where some multi-database metadata updates may be skipped and leave incorrect persisted state. A separate high-severity finalization consistency issue was also observed but appears pre-existing, so it is an important caveat rather than the main merge blocker for this change.

Tests run by Ito

View full run

Result Severity Type Description
Medium severity Finalize The finalizer computes its database set from sequence keys plus current database, so secondary databases touched only through non-sequence collections may never be persisted.
Cache Alternating metadata operations between db_adv_a and db_adv_b kept pg_type and pg_cast entries scoped to their own database with no leakage.
Cache The custom type created in db_types_1 stayed isolated to that database and did not appear in db_types_2.
Context Runtime enum cast, comparison, and array expression results stayed consistent across repeated execution and a fresh session, with no context-contract lookup errors.
Context PL/pgSQL DECLARE variables and callback-backed type/cast resolution executed successfully across two sessions, and code inspection shows the callback contract wiring is consistently initialized across core, initialization, and interpreter paths.
Database Matching enum type names in two databases resolved to each database's local definition, and cross-database enum literals were rejected as expected.
Database Created same_proc with different default values in db_call_a and db_call_b, then CALL same_proc() in each database returned A-111 and B-222 respectively, confirming defaults resolved from active database procedure context.
Type Creating distinct user-defined types in db_iter_a and db_iter_b and querying pg_catalog.pg_type in each database showed only local type rows, with cross-database leakage checks returning 0 in both directions.
⚠️ High severity Finalize The finalizer does not provide atomic all-or-nothing behavior across the database set it processes.
Additional Findings Details

These findings are unrelated to the current changes but were observed during testing.

🟠 Multi-database finalization abort leaves partially persisted metadata
  • Severity: High High severity
  • Description: The finalizer does not provide atomic all-or-nothing behavior across the database set it processes.
  • Impact: Users may be unable to complete the authentication flow and reach the application after this change.
  • Steps to Reproduce:
    1. Prepare a statement path that mutates metadata in at least two databases that are included in the context finalization set.
    2. Allow the first database update to succeed, then trigger an error in a later database update (for example, from a later UpdateRoot or SetWorkingRoot call).
    3. Observe that the function returns immediately on that later error after earlier updates have already been applied, with no rollback of prior successful database writes.
  • Stub / mock content: A temporary authentication bypass was left enabled from earlier setup attempts, and the local QA server never became reachable for this retry, so this finding comes from source-code verification rather than runtime execution.
  • Code Analysis: CloseContextRootFinalizer iterates databases and calls updateSessionRootForDatabase one-by-one, returning immediately when any call errors. updateSessionRootForDatabase can commit a database working root through session.SetWorkingRoot; once that succeeds for an earlier database, a later database error exits without compensating rollback, producing mixed persisted/non-persisted outcomes. The smallest practical fix is to stage per-database root updates and commit only after all per-database UpdateRoot operations succeed, or add an explicit rollback/compensation path for already-applied roots when a later database fails.
Evidence Package

Tip

Reply with @itoqa to send us feedback on this test run.

Comment thread core/context.go
@github-actions

Copy link
Copy Markdown
Contributor
Main PR
Total 42090 42090
Successful 18268 18269
Failures 23822 23821
Partial Successes1 5333 5333
Main PR
Successful 43.4022% 43.4046%
Failures 56.5978% 56.5954%

${\color{lightgreen}Progressions (1)}$

random

QUERY: (SELECT unique1 AS random
  FROM onek ORDER BY random() LIMIT 1)
INTERSECT
(SELECT unique1 AS random
  FROM onek ORDER BY random() LIMIT 1)
INTERSECT
(SELECT unique1 AS random
  FROM onek ORDER BY random() LIMIT 1);

Footnotes

  1. These are tests that we're marking as Successful, however they do not match the expected output in some way. This is due to small differences, such as different wording on the error messages, or the column names being incorrect while the data itself is correct.

@fulghum fulghum left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing through database string looks good. I see what you mean about needing cross-database tests next to verify that behavior.

Comment thread server/analyzer/foreign_key.go
@Hydrocharged Hydrocharged merged commit 9434270 into main Jun 16, 2026
26 checks passed
@Hydrocharged Hydrocharged deleted the daylon/context-accepts-db branch June 16, 2026 11:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants