Skip to content

fix(plugin-snowflake): key the shared session on the saved connection and scope Stop to its own driver - #2470

Merged
datlechin merged 1 commit into
fix/snowflake-temporal-decodingfrom
fix/snowflake-session-identity
Aug 26, 2026
Merged

fix(plugin-snowflake): key the shared session on the saved connection and scope Stop to its own driver#2470
datlechin merged 1 commit into
fix/snowflake-temporal-decodingfrom
fix/snowflake-session-identity

Conversation

@datlechin

Copy link
Copy Markdown
Member

Stacked on the Snowflake work. Base is fix/snowflake-temporal-decoding, which carries #2466's commit plus the merged #2467, #2468 and #2469. Retarget to main when that branch lands.

Found while investigating #2454. Two defects with one cause: several drivers share one Snowflake session, and nothing said which driver a session or a running statement belonged to.

One session for two connections

SnowflakeConnectionRegistry hands out a shared SnowflakeConnection keyed on sessionFingerprint, which was:

[host, params.user.uppercased(), params.authMethod, params.role.uppercased()]

That names the account, not the connection. Two saved connections to the same account, user and role, one defaulting to PROD and one to STAGING, got the same session. Opening the second showed the first's objects, switching database in either window moved the other window's current database, and a grid save in the untouched window then wrote to the database it was never pointed at.

The key now includes the saved connection's own identifier, which the app passes through additionalFields.

The database cannot be used for this, which is the whole reason a new identifier was needed. MetadataConnectionPool.openEntry copies the connection and rewrites connection.database to whatever database the metadata read targets, then builds a driver from that copy. A key that varied with the database would give the pooled driver a different key, a second login, and another MFA prompt, which is exactly what sharing the session exists to avoid. The connection's identifier survives that rewrite because the pool copies the connection rather than fabricating one.

Stop cancelled everybody's work

cancelQuery() called cancelAllQueries(), which aborted every request id on the session:

let (requestIDs, token) = lock.withLock { (activeRequestIDs, sessionToken) }

Since the session is shared by the driver the user sees and by every pooled metadata driver behind it, pressing Stop on a long query also aborted a sidebar schema refresh running beside it, which then reported the schema as unloadable. It cut both ways: stopping any query killed a save's remaining statements mid-batch.

Each driver now carries its own owner token, the connection tracks request ids per owner, and cancelQueries(owner:) aborts only that driver's statements. Statements the connection issues for itself, such as the connect-time USE calls, belong to sessionOwner and are never the target of a Stop.

This matches DatabaseDriver.cancelQuery's contract, which is about the currently running query, not the connection.

Tests

SnowflakeSessionKeyTests, 6 cases: two saved connections to one account do not share, the same connection always resolves to one session, the database is not part of the key, account identity still separates sessions on host, user, auth method and role, and user and role compare case insensitively.

The per-owner cancellation is structural rather than unit tested: the tracking lives on the shared connection behind its lock, and a test for it would restate the dictionary rather than pin behaviour.

Verified: build PASS, 48/48 across this and the neighbouring Snowflake suites, SwiftLint 0 violations across 5238 files.

Note on scope

This touches one app file, DatabaseDriver.swift, to pass connectionId in additionalFields. That is deliberately not a DriverConnectionConfig change: adding a field there would alter shared PluginKit ABI for all 32 plugins, and additionalFields already exists for exactly this. No ABI change, no version bump.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@datlechin
datlechin merged commit 2496e4e into fix/snowflake-temporal-decoding Aug 26, 2026
8 checks passed
@datlechin
datlechin deleted the fix/snowflake-session-identity branch August 26, 2026 17:07
datlechin added a commit that referenced this pull request Aug 26, 2026
…dpoint sends (#2466)

* fix(plugin-snowflake): decode the wire encodings the query-request endpoint sends

Claude-Session: https://claude.ai/code/session_01Qk1xfY3vnneRifC22eV2r7

* fix(plugin-snowflake): give every statement builder one SQL escaper (#2467)

Claude-Session: https://claude.ai/code/session_01Qk1xfY3vnneRifC22eV2r7

* fix(plugin-snowflake): keep a timestamp offset the date parser cannot represent as text (#2468)

Claude-Session: https://claude.ai/code/session_01Qk1xfY3vnneRifC22eV2r7

* fix(plugin-snowflake): take the affected row count from the statement type, not a column name (#2469)

Claude-Session: https://claude.ai/code/session_01Qk1xfY3vnneRifC22eV2r7

* fix(plugin-snowflake): key the shared session on the saved connection and scope Stop to its own driver (#2470)

Claude-Session: https://claude.ai/code/session_01Qk1xfY3vnneRifC22eV2r7

---------

Co-authored-by: Ngo Quoc Dat <datlechin@gmail.com>
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.

1 participant