Description
The server keeps SimpleAggregateFunction(func, T) as a concrete Dynamic subtype and encodes it in the binary type encoding as:
0x2E <function_name> <var_uint number_of_parameters><parameters> <var_uint number_of_arguments><argument_type_encodings>
BinaryTypeDecoder.FromByteCode dispatches BinaryTypeIndex.SimpleAggregateFunction (0x2E) to DecodeSimpleAggregateFunction, which is an unimplemented stub:
// ClickHouse.Driver/Types/BinaryTypeDecoder.cs:300
private static SimpleAggregateFunctionType DecodeSimpleAggregateFunction(ExtendedBinaryReader reader)
{
throw new NotImplementedException("SimpleAggregateFunction decoding not implemented.");
}
So reading a SimpleAggregateFunction value out of a Dynamic column throws and the query fails. The same FromByteCode path is used by VariantType, JsonType and nested Dynamic, so those are affected too.
Note that a top-level SimpleAggregateFunction column works fine — that path goes through the textual type-name grammar (SimpleAggregateFunctionType.Parse), not the binary decoder. Only the binary-type-encoding path is broken.
Related: DecodeAggregateFunction (0x1E) is the same kind of stub at BinaryTypeDecoder.cs:295.
ClickHouse server version
26.7.2.59 (verified against a running server)
Reproduction
using ClickHouse.Driver.Utility;
public class SafDynamicTests : AbstractConnectionTestFixture
{
[Test]
public async Task ShouldReadSimpleAggregateFunctionInDynamic()
{
using var reader = await connection.ExecuteReaderAsync(
"SELECT CAST(CAST(42, 'SimpleAggregateFunction(sum, UInt64)') AS Dynamic) AS d, 42::Int32 AS tail");
Assert.That(reader.Read(), Is.True);
Assert.That(reader.GetValue(0), Is.EqualTo((ulong)42));
Assert.That(reader.GetValue(1), Is.EqualTo(42));
}
[Test] // this one passes today (textual type-name path)
public async Task ShouldReadTopLevelSimpleAggregateFunction()
{
using var reader = await connection.ExecuteReaderAsync(
"SELECT CAST(42, 'SimpleAggregateFunction(sum, UInt64)') AS d, 42::Int32 AS tail");
Assert.That(reader.Read(), Is.True);
Assert.That(reader.GetValue(0), Is.EqualTo((ulong)42));
Assert.That(reader.GetValue(1), Is.EqualTo(42));
}
}
Expected: both tests pass, d reads as 42UL and tail as 42.
Actual: ShouldReadTopLevelSimpleAggregateFunction passes, ShouldReadSimpleAggregateFunctionInDynamic fails:
Failed ShouldReadSimpleAggregateFunctionInDynamic [17 ms]
System.NotImplementedException : SimpleAggregateFunction decoding not implemented.
at ClickHouse.Driver.Types.BinaryTypeDecoder.DecodeSimpleAggregateFunction(ExtendedBinaryReader reader) in ClickHouse.Driver/Types/BinaryTypeDecoder.cs:line 302
at ClickHouse.Driver.Types.BinaryTypeDecoder.FromByteCode(ExtendedBinaryReader reader, TypeSettings typeSettings) in ClickHouse.Driver/Types/BinaryTypeDecoder.cs:line 176
at ClickHouse.Driver.Types.DynamicType.Read(ExtendedBinaryReader reader) in ClickHouse.Driver/Types/DynamicType.cs:line 21
at ClickHouse.Driver.ADO.Readers.ClickHouseDataReader.Read() in ClickHouse.Driver/ADO/Readers/ClickHouseDataReader.cs:line 458
Suggested fix
Implement DecodeSimpleAggregateFunction in ClickHouse.Driver/Types/BinaryTypeDecoder.cs:300 to consume the full encoding and return a usable type:
reader.ReadString() — function name
reader.Read7BitEncodedInt() parameters, each a field-value encoding — must be consumed even when ignored
reader.Read7BitEncodedInt() arguments, each FromByteCode(reader, typeSettings)
Return new SimpleAggregateFunctionType { AggregateFunction = name, UnderlyingType = arguments[0] } (its Read already delegates to the underlying type). Consuming the whole encoding matters independently of the exception: if the tag were skipped without consuming the payload, the function name and argument encodings would be interpreted as row data and desynchronize the rest of the RowBinary stream. Note the signature needs TypeSettings threaded in to decode argument types.
Nothing (0x00) parameters aside, ClickHouse currently only emits parameterized SimpleAggregateFunction for a few functions, but the parameter section must still be skipped correctly.
Link
Relayed from ClickHouse/clickhouse-java#3005
Description
The server keeps
SimpleAggregateFunction(func, T)as a concreteDynamicsubtype and encodes it in the binary type encoding as:BinaryTypeDecoder.FromByteCodedispatchesBinaryTypeIndex.SimpleAggregateFunction(0x2E) toDecodeSimpleAggregateFunction, which is an unimplemented stub:So reading a
SimpleAggregateFunctionvalue out of aDynamiccolumn throws and the query fails. The sameFromByteCodepath is used byVariantType,JsonTypeand nestedDynamic, so those are affected too.Note that a top-level
SimpleAggregateFunctioncolumn works fine — that path goes through the textual type-name grammar (SimpleAggregateFunctionType.Parse), not the binary decoder. Only the binary-type-encoding path is broken.Related:
DecodeAggregateFunction(0x1E) is the same kind of stub atBinaryTypeDecoder.cs:295.ClickHouse server version
26.7.2.59 (verified against a running server)
Reproduction
Expected: both tests pass,
dreads as42ULandtailas42.Actual:
ShouldReadTopLevelSimpleAggregateFunctionpasses,ShouldReadSimpleAggregateFunctionInDynamicfails:Suggested fix
Implement
DecodeSimpleAggregateFunctioninClickHouse.Driver/Types/BinaryTypeDecoder.cs:300to consume the full encoding and return a usable type:reader.ReadString()— function namereader.Read7BitEncodedInt()parameters, each a field-value encoding — must be consumed even when ignoredreader.Read7BitEncodedInt()arguments, eachFromByteCode(reader, typeSettings)Return
new SimpleAggregateFunctionType { AggregateFunction = name, UnderlyingType = arguments[0] }(itsReadalready delegates to the underlying type). Consuming the whole encoding matters independently of the exception: if the tag were skipped without consuming the payload, the function name and argument encodings would be interpreted as row data and desynchronize the rest of the RowBinary stream. Note the signature needsTypeSettingsthreaded in to decode argument types.Nothing(0x00) parameters aside, ClickHouse currently only emits parameterizedSimpleAggregateFunctionfor a few functions, but the parameter section must still be skipped correctly.Link
Relayed from ClickHouse/clickhouse-java#3005