Skip to content

Preserve code-only driver settings on DbConnection/DbDataSource paths - #51

Open
alex-clickhouse wants to merge 3 commits into
mainfrom
fix/preserve-code-only-connection-settings
Open

Preserve code-only driver settings on DbConnection/DbDataSource paths#51
alex-clickhouse wants to merge 3 commits into
mainfrom
fix/preserve-code-only-connection-settings

Conversation

@alex-clickhouse

Copy link
Copy Markdown
Collaborator

Summary

Fixes #50. When a user configures the provider via UseClickHouse(DbConnection) or UseClickHouse(DbDataSource) using a ClickHouseClientSettings built in code, settings that have no connection-string representation were silently dropped — most visibly SkipServerCertificateValidation, which is why connecting to a server with a self-signed certificate failed under EF Core even though the same settings work with a bare ClickHouseClient.

Root cause

The driver's ClickHouseConnection.ConnectionString setter does Settings = new ClickHouseClientSettings(value) — it rebuilds settings purely from the connection string. Anything not representable as a connection-string key is lost on a round-trip: SkipServerCertificateValidation, BearerToken, HttpClient, HttpClientFactory, HttpClientName, LoggerFactory, EnableDebugMode, CustomHeaders, ApplicationInfo, and the parameter/read converters.

Two sites in ClickHouseRelationalConnection round-tripped through the string:

  1. EnsureJoinUseNulls (called from Open/OpenAsync) — rewrote ConnectionString to inject join_use_nulls=1. This hit both the DbConnection and DbDataSource paths.
  2. CreateMasterConnection (used by ClickHouseDatabaseCreator for CREATE/DROP DATABASE, i.e. EnsureCreated/EnsureDeleted/migrations) — rebuilt the master connection from the connection string. The same self-signed-cert user would still fail on database create/drop even with add aggregate method translator #1 fixed.

Fix

  • EnsureJoinUseNulls now injects join_use_nulls by cloning Settings and setting CustomSettings["join_use_nulls"] = "1" for a ClickHouseConnection, rather than rewriting ConnectionString. The string rewrite remains only as a fallback for a non-ClickHouse DbConnection. The user opt-out (set_join_use_nulls=0) is still honored.
  • CreateMasterConnection now clones the driver settings and only swaps Database = "default", handing back a context-owned ClickHouseConnection, instead of reconstructing from the connection string. ApplicationInfo (the lib User-Agent tag added on main) is preserved via the copy constructor.

Testing

  • Added regression tests in ConnectionSettingsTests covering both fixed sites and both input paths, asserting SkipServerCertificateValidation, BearerToken, and CustomHeaders survive, plus that the join_use_nulls opt-out is preserved on the master path. These fail against pre-fix code.
  • All 11 ConnectionSettingsTests pass against a real ClickHouse container.

Notes

The durable guard against reintroducing this class of bug is the TryGetClickHouseClientSettings helper (clone-the-settings-object pattern). Any future code that reconstructs a connection from .ConnectionString would reintroduce the defect; a full-codebase review found no other current instances.

🤖 Generated with Claude Code

Settings that exist only in code and have no connection-string
representation (SkipServerCertificateValidation, BearerToken, HttpClient,
HttpClientFactory, LoggerFactory, CustomHeaders, ApplicationInfo,
EnableDebugMode, and the parameter/read converters) were silently dropped
whenever the provider round-tripped a connection through its connection
string. The driver's ClickHouseConnection.ConnectionString setter rebuilds
Settings purely from the string, so any code-only setting is lost.

Two sites in ClickHouseRelationalConnection did this:

- EnsureJoinUseNulls (Open/OpenAsync): now injects join_use_nulls via a
  cloned Settings object for a ClickHouseConnection instead of rewriting
  ConnectionString. The string rewrite remains only as a fallback for a
  non-ClickHouse DbConnection. The user opt-out (set_join_use_nulls=0) is
  still honored.

- CreateMasterConnection (CREATE/DROP DATABASE): now clones the driver
  settings and only swaps Database="default", handing back a context-owned
  ClickHouseConnection, instead of reconstructing from the connection
  string. ApplicationInfo (the lib User-Agent tag) is preserved via the
  copy constructor.

Fixes #50.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@codecov

codecov Bot commented Jul 16, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR fixes a provider bug where configuring ClickHouse via UseClickHouse(DbConnection) or UseClickHouse(DbDataSource) could silently drop driver settings that have no connection-string representation (e.g., SkipServerCertificateValidation), by avoiding round-trips through DbConnection.ConnectionString and instead cloning/preserving ClickHouseClientSettings.

Changes:

  • Update ClickHouseRelationalConnection.Open/OpenAsync to inject join_use_nulls via ClickHouseConnection.Settings.CustomSettings (with a connection-string fallback for non-ClickHouse connections).
  • Update CreateMasterConnection() to clone driver settings and only switch Database = "default" instead of rebuilding settings from a connection string.
  • Add regression tests ensuring code-only settings and join_use_nulls opt-out behavior are preserved across both DbConnection and DbDataSource configuration paths.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/EFCore.ClickHouse/Storage/Internal/ClickHouseRelationalConnection.cs Preserves code-only driver settings by cloning settings for join_use_nulls injection and master connections.
test/EFCore.ClickHouse.Tests/ConnectionSettingsTests.cs Adds regression coverage for preserving code-only settings and join_use_nulls opt-out across configuration paths.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +134 to +137
var masterConnectionString = new ClickHouseConnectionStringBuilder(ConnectionString)
{
Database = "default"
}.ConnectionString;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Good catch — fixed in 45edc2d. The fallback now uses _dataSource?.ConnectionString ?? ConnectionString, so a non-ClickHouse DbDataSource (which sets no connection string on the EF options) builds a valid master connection string. Added CreateMasterConnection_NonClickHouseDataSource_UsesDataSourceConnectionString to cover it.

Comment on lines +55 to +57
/// queries. Instead the driver applies <c>set_*</c> parameters (the driver's
/// <c>CustomSettings</c>) as URL parameters on every query, so we must record the setting on
/// the connection before it is opened.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Fixed in 45edc2d — reworded the summary to state that the setting is recorded on ClickHouseConnection.Settings, with the connection-string path used only as a fallback for a non-ClickHouse DbConnection.

alex-clickhouse and others added 2 commits July 16, 2026 13:21
Adds a minimal fake DbConnection to exercise the defensive fallback
branches in EnsureJoinUseNulls, CreateMasterConnection, and
TryGetClickHouseClientSettings that only run when the underlying
connection is not a ClickHouseConnection, bringing patch coverage to 100%.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- CreateMasterConnection fallback now uses `_dataSource?.ConnectionString
  ?? ConnectionString` so a non-ClickHouse DbDataSource (which sets no
  connection string on the EF options) builds a valid master connection
  string instead of an empty one.
- Corrected the EnsureJoinUseNulls XML doc to reflect that the setting is
  recorded on ClickHouseConnection.Settings, with the connection-string
  path used only as a fallback.
- Added a regression test covering the non-ClickHouse DbDataSource path.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.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.

SkipServerCertificateValidation doesn't seem to work when passing a DbConnection or DbDataSource to UseClickHouse

2 participants