Skip to content

Keep a DbType that was set explicitly when Value is assigned afterwards - #369

Open
alaricd wants to merge 1 commit into
Giorgi:developfrom
alaricd:parameter-keeps-explicit-dbtype
Open

alaricd wants to merge 1 commit into
Giorgi:developfrom
alaricd:parameter-keeps-explicit-dbtype

Conversation

@alaricd

@alaricd alaricd commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

DuckDBParameter.Value replaced DbType with the type inferred from the new value on every assignment, so an explicit DbType followed by a Value assignment was lost:

var p = new DuckDBParameter { DbType = DbType.Time };
p.Value = new TimeSpan(13, 45, 30);
// p.DbType is no longer DbType.Time

Why it matters. #360 binds a TimeSpan with no inferred type as an INTERVAL, except when DbType.Time says it is a time of day. Because the Time was lost, a TimeSpan for a TIME column inside a MERGE source (where DuckDB has no target column to infer from) is bound as an INTERVAL, and DuckDB cannot cast that to TIME:

MERGE INTO t USING (SELECT $1 AS id, $2 AS v) s ON t.id = s.id
WHEN MATCHED THEN UPDATE SET v = s.v
WHEN NOT MATCHED THEN INSERT (id, v) VALUES (s.id, s.v)
-- Conversion Error: Unimplemented type for cast (INTERVAL -> TIME) when casting from source column v

It worked with 1.5.6 (the value was sent as text) and fails with 1.5.6.1. The same applies to any explicit DbType set before Value, which is the usual order for a provider-neutral caller (CreateParameter(), set DbType, set Value).

Fix. A DbType that was assigned now stays. Until one is assigned, and after ResetDbType(), the type still follows the value, as it does for SqlParameter and NpgsqlParameter. The constructors that take a value without a type still infer it; the ones that take a DbType keep it.

Tests (Parameters/ParameterDbTypeTests): DbType set before and after the value is kept; inference when none was set and after ResetDbType(); the constructors; the MERGE case above, which fails without the change; and a bare TimeSpan is still bound as an interval. The new tests pass 5 of 5 runs, and the suite (everything except the VARINT and connection stress tests) passes: 1285 tests on net8.0.

Found while testing for a release of https://github.com/pengdows/pengdows.crud

DuckDBParameter.Value replaced DbType with the type inferred from the new value every time it was set, so
"parameter.DbType = DbType.Time; parameter.Value = timeSpan" lost the Time. The converter then saw a TimeSpan with no
DbType and bound it as an INTERVAL (the behavior added for a bare "?"), which DuckDB cannot cast to TIME where it has no
target column to infer from, such as a MERGE source:

    MERGE INTO t USING (SELECT $1 AS id, $2 AS v) s ON t.id = s.id ... INSERT (id, v) VALUES (s.id, s.v)
    -- Conversion Error: Unimplemented type for cast (INTERVAL -> TIME) when casting from source column v

The same applied to every explicit DbType followed by a Value assignment, which is the usual order for a provider-neutral
caller (CreateParameter, set DbType, set Value).

A DbType that was assigned now stays; until one is assigned, and after ResetDbType, the type still follows the value, as
it does for SqlParameter and NpgsqlParameter. The constructors that take a value without a type still infer it, and the
ones that take a DbType keep it.

Found while testing for a release of https://github.com/pengdows/pengdows.crud

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@coveralls

Copy link
Copy Markdown

Coverage Report for CI Build 39136404700

Coverage decreased (-0.03%) to 92.52%

Details

  • Coverage decreased (-0.03%) from the base build.
  • Patch coverage: 19 of 19 lines across 1 file are fully covered (100%).
  • 2 coverage regressions across 1 file.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

2 previously-covered lines in 1 file lost coverage.

File Lines Losing Coverage Coverage
DuckDB.NET.Data/Connection/ConnectionManager.cs 2 82.83%

Coverage Stats

Coverage Status
Relevant Lines: 4023
Covered Lines: 3786
Line Coverage: 94.11%
Relevant Branches: 2287
Covered Branches: 2052
Branch Coverage: 89.72%
Branches in Coverage %: Yes
Coverage Strength: 997737.26 hits per line

💛 - Coveralls

This branch is waiting to be deployed

1 waiting deployment
external — 5fa76e5c Waiting Oct 18, 2026 by alaricd via Authorize #671
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