ClickHouseCommand: CommandBehavior matched by equality, so SchemaOnly/SingleRow combined with any other flag applies no row limit
Maintainers usually reply within 1 day
Assessment
This issue has not been assessed yet.
Description
Describe the bug
ClickHouseCommand.ExecuteDbDataReaderAsync decides whether to append LIMIT 0 / LIMIT 1 with an exact-equality switch on CommandBehavior (ClickHouse.Driver/ADO/ClickHouseCommand.cs:197-207, on main @ d5ae56c):
switch (behavior)
{
case CommandBehavior.SingleRow: sqlBuilder.Append(" LIMIT 1"); break;
case CommandBehavior.SchemaOnly: sqlBuilder.Append(" LIMIT 0"); break;
default: break;
}
System.Data.CommandBehavior is a [Flags] enum, and ADO.NET consumers routinely pass combinations. Any combination fails the equality test, falls to default, and no row limit is applied at all:
SchemaOnly | KeyInfo(whatDbDataAdapter.FillSchemasends) executes the query and returns all data rows, even thoughSchemaOnlymeans the query should not be executed.SingleRow | SequentialAccess | SingleResult(what Dapper'sQueryFirst/QueryFirstOrDefaultsends) streams the entire result set instead ofLIMIT 1.
There is no error and no warning — the limit is silently dropped. Since the ADO.NET layer exists specifically for ORM compatibility (Dapper / EF Core / linq2db, per AGENTS.md), the combined-flag form is the common case for those consumers, not an edge case.
Steps to reproduce
- Point the driver at any ClickHouse server.
- Run a reader with a bare flag and then with the same flag OR'd with any other flag, and count rows.
- Or: call Dapper's
QueryFirstOrDefaultAsyncon a query whose later rows fail, and observe the failure.
Expected behaviour
The flags should be tested as flags: SchemaOnly should suppress rows and SingleRow should limit to one row regardless of which additional, orthogonal flags (KeyInfo, SequentialAccess, SingleResult, CloseConnection) accompany them. SchemaOnly should take precedence over SingleRow when both are present.
Code example
Row counts against SELECT number FROM numbers(5) (NUnit, net10.0, driver built from main @ d5ae56c):
| behavior | numeric value | rows returned | expected |
|---|---|---|---|
SchemaOnly |
2 | 0 | 0 ✅ |
SingleRow |
8 | 1 | 1 ✅ |
SchemaOnly | KeyInfo |
6 | 5 | 0 ❌ |
SchemaOnly | SequentialAccess |
18 | 5 | 0 ❌ |
SchemaOnly | SingleResult |
3 | 5 | 0 ❌ |
SingleRow | SequentialAccess |
24 | 5 | 1 ❌ |
SingleRow | CloseConnection |
40 | 5 | 1 ❌ |
using var command = connection.CreateCommand();
command.CommandText = "SELECT number FROM numbers(5)";
using var reader = await command.ExecuteReaderAsync(CommandBehavior.SchemaOnly | CommandBehavior.KeyInfo);
var rows = 0;
while (await reader.ReadAsync())
rows++;
// rows == 5; SchemaOnly should have produced 0
A second probe proves the LIMIT never reaches the server (and shows the ORM impact). throwIf fires only if the server reads past row 0:
const string probe = "SELECT throwIf(number = 3, 'boom') FROM numbers(5)";
// passes: LIMIT 1 is appended, the server never reads row 3
await command.ExecuteReaderAsync(CommandBehavior.SingleRow);
// throws Code 395 FUNCTION_THROW_IF_VALUE_IS_NON_ZERO: no LIMIT was appended
await command.ExecuteReaderAsync(CommandBehavior.SingleRow | CommandBehavior.SequentialAccess);
// throws the same: Dapper 2.1.79 sends a flag combination for QueryFirst
await connection.QueryFirstOrDefaultAsync<byte>(probe);
Error log
ClickHouse.Driver.ClickHouseServerException : Code: 395. DB::Exception: boom: while executing
'FUNCTION throwIf(equals(__table1.number, 3_UInt8) :: 0, 'boom'_String :: 2) -> throwIf(...) UInt8 : 1'.
(FUNCTION_THROW_IF_VALUE_IS_NON_ZERO) (version 26.5.1.882 (official build))
at ClickHouse.Driver.ADO.ClickHouseCommand.ExecuteDbDataReaderAsync(CommandBehavior behavior, CancellationToken cancellationToken)
Suggested fix
Match on the flag bits rather than the whole value, in ExecuteDbDataReaderAsync:
behavior.HasFlag(CommandBehavior.SchemaOnly)→LIMIT 0, checked first so it wins overSingleRow;- else
behavior.HasFlag(CommandBehavior.SingleRow)→LIMIT 1; - else no limit.
Contrast case that must keep its current behavior: CommandBehavior.Default (0) must send CommandText verbatim — note HasFlag returns true for a zero flag, so Default has to stay an exact/zero check rather than a HasFlag test.
Worth pinning in tests: each bare flag, each of the combinations above, SchemaOnly | SingleRow (SchemaOnly wins), and Default.
Related
- #471 / #473 concern the same two behaviors but a different root cause (the
LIMITbeing appended verbatim after a trailing comment or semicolon). This defect is independent of that one and is not addressed by #473, which preserves the equality match. If both land, the flag check and the append mechanism are separate changes.
Configuration
Environment
- Client version: built from
main@d5ae56c - Language version: C# / .NET SDK 10.0.203
- .NET version:
net10.0test target (the code path is framework-independent) - OS: Ubuntu 24.04 (container)
ClickHouse server
- ClickHouse Server version: 26.5.1.882 (official build, Docker)
- ClickHouse Server non-default settings, if any: none
CREATE TABLEstatements for tables involved: none — reproduction uses thenumbers()table function- Sample data: n/a
Found by automated analysis of this client while working on #471, and verified against a live ClickHouse server (not by code inspection alone). Filed for triage; no fix has been pushed.
- Dominant language
- C#
- Stars
- 100
- Forks
- 25
- Avg merge
- 1d 2h
- Merged PRs (30d)
- 8
Getting set up
- No Dockerfile or Docker Compose file
- Has a pull request template
- Read the contributing guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
More from ClickHouse/clickhouse-cs
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
ClickHouse/clickhouse-cs#689 ·
Maintainers usually reply within 1 day
-
Every query fails with Code 164 (READONLY) for a readonly = 1 user: the driver always sends enable_http_compression and input_format_binary_read_json_as_string as settingsPossibly taken @polyglotAI-bot claimed this 27 days ago. Open
Difficulty 2/5 1-3 hours Newbie friendliness 78/100
ClickHouse/clickhouse-cs#659 · 1 comment ·
Maintainers usually reply within 1 day
-
ClickHouseDecimal: Floor() rounds negative non-integers toward zero (-1.5 → -1, server floor gives -2)Possibly taken @polyglotAI-bot claimed this 26 days ago. Openbug
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
ClickHouse/clickhouse-cs#645 · 1 assignee ·
Maintainers usually reply within 1 day
-
ClickHouseDecimal: GetHashCode depends on Scale, but Equals ignores ScalePossibly taken @bjsneto claimed this 9 days ago. Openbug
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
ClickHouse/clickhouse-cs#639 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 88/100
ClickHouse/clickhouse-cs#627 ·
Maintainers usually reply within 1 day
All issues in ClickHouse/clickhouse-cs
Similar issues
-
Difficulty 1/5 1-3 hours Newbie friendliness 78/100
QuantConnect/Documentation#2838 ·
Maintainers usually reply within 1 day
-
"Create your app": .sln/.slnx step and Solution Explorer instructions don't match current C# Dev KitOpenarea-tutorials needs-triage
Difficulty 1/5 1-3 hours Newbie friendliness 72/100
dotnet/website-feedback#1844 ·
-
Needs: Attention :wave: v5 v5-preview5
Difficulty 2/5 1-3 hours Newbie friendliness 62/100
Azure/azure-functions-core-tools#5852 ·
Maintainers usually reply within 2 days
-
Difficulty 2/5 1-3 hours Newbie friendliness 85/100
OrchardCMS/OrchardCore#20194 ·
Maintainers usually reply within 1 day
-
bug
Difficulty 2/5 1-3 hours Newbie friendliness 69/100
Gothic-Unity-Project/Gothic-Unity#487 ·
Maintainers usually reply within 1 day