@@ -875,7 +875,10 @@ def test_decode_continuation_roundtrip(self) -> None:
875875 msg1_bytes = header1 .encode () + body1
876876
877877 # Build continuation ROWS message with DONE marker
878- body2 = encode_row_header (types )
878+ # (C server always includes column_count + column_names)
879+ body2 = encode_uint64 (2 )
880+ body2 += encode_text ("id" ) + encode_text ("name" )
881+ body2 += encode_row_header (types )
879882 body2 += encode_row_values ([2 , "bob" ], types )
880883 body2 += encode_uint64 (ROW_DONE_MARKER )
881884 header2 = Header (size_words = len (body2 ) // 8 , msg_type = 7 , schema = 0 )
@@ -893,20 +896,59 @@ def test_decode_continuation_roundtrip(self) -> None:
893896 assert initial .rows [0 ] == [1 , "alice" ]
894897
895898 # Decode continuation
896- continuation = decoder .decode_continuation (
897- column_names = initial .column_names ,
898- column_count = len (initial .column_names ),
899- )
899+ continuation = decoder .decode_continuation ()
900900 assert isinstance (continuation , RowsResponse )
901901 assert continuation .has_more is False
902902 assert len (continuation .rows ) == 1
903903 assert continuation .rows [0 ] == [2 , "bob" ]
904904
905+ def test_decode_continuation_with_column_header (self ) -> None :
906+ """Continuation frames from the C server include column_count +
907+ column_names (same layout as the initial frame). Verify that
908+ decode_continuation handles this correctly.
909+ """
910+ from dqlitewire .constants import ROW_DONE_MARKER , ROW_PART_MARKER , ValueType
911+ from dqlitewire .messages .base import Header
912+ from dqlitewire .messages .responses import RowsResponse
913+ from dqlitewire .tuples import encode_row_header , encode_row_values
914+ from dqlitewire .types import encode_text , encode_uint64
915+
916+ types = [ValueType .INTEGER , ValueType .TEXT ]
917+
918+ # Initial ROWS message (PART marker)
919+ body1 = encode_uint64 (2 )
920+ body1 += encode_text ("id" ) + encode_text ("name" )
921+ body1 += encode_row_header (types )
922+ body1 += encode_row_values ([1 , "alice" ], types )
923+ body1 += encode_uint64 (ROW_PART_MARKER )
924+ h1 = Header (size_words = len (body1 ) // 8 , msg_type = 7 , schema = 0 )
925+
926+ # Continuation WITH column header (matching C server output)
927+ body2 = encode_uint64 (2 )
928+ body2 += encode_text ("id" ) + encode_text ("name" )
929+ body2 += encode_row_header (types )
930+ body2 += encode_row_values ([2 , "bob" ], types )
931+ body2 += encode_uint64 (ROW_DONE_MARKER )
932+ h2 = Header (size_words = len (body2 ) // 8 , msg_type = 7 , schema = 0 )
933+
934+ decoder = MessageDecoder (is_request = False )
935+ decoder .feed (h1 .encode () + body1 + h2 .encode () + body2 )
936+
937+ initial = decoder .decode ()
938+ assert isinstance (initial , RowsResponse )
939+ assert initial .has_more is True
940+ assert initial .rows [0 ] == [1 , "alice" ]
941+
942+ cont = decoder .decode_continuation ()
943+ assert isinstance (cont , RowsResponse )
944+ assert cont .has_more is False
945+ assert cont .rows [0 ] == [2 , "bob" ]
946+
905947 def test_decode_continuation_returns_none_when_no_data (self ) -> None :
906948 """decode_continuation should return None when no message is available."""
907949 decoder = MessageDecoder (is_request = False )
908950 decoder ._continuation_expected = True
909- result = decoder .decode_continuation (column_names = [ "x" ], column_count = 1 )
951+ result = decoder .decode_continuation ()
910952 assert result is None
911953
912954 def test_decode_continuation_raises_on_failure_response (self ) -> None :
@@ -922,7 +964,7 @@ def test_decode_continuation_raises_on_failure_response(self) -> None:
922964 decoder .feed (failure_bytes )
923965
924966 with pytest .raises (ProtocolError , match = "disk I/O error" ) as exc_info :
925- decoder .decode_continuation (column_names = [ "id" ], column_count = 1 )
967+ decoder .decode_continuation ()
926968 # Must be a ProtocolError, NOT a DecodeError (which would mean the
927969 # failure body was misinterpreted as row data).
928970 assert type (exc_info .value ) is ProtocolError
@@ -939,7 +981,7 @@ def test_decode_continuation_raises_on_unexpected_type(self) -> None:
939981 decoder .feed (result_bytes )
940982
941983 with pytest .raises (ProtocolError , match = "Expected ROWS continuation" ):
942- decoder .decode_continuation (column_names = [ "id" ], column_count = 1 )
984+ decoder .decode_continuation ()
943985
944986
945987class TestDecoderContinuationExpected :
@@ -1007,8 +1049,10 @@ def test_decode_continuation_clears_flag(self) -> None:
10071049 body1 += encode_uint64 (ROW_PART_MARKER )
10081050 h1 = Header (size_words = len (body1 ) // 8 , msg_type = 7 , schema = 0 )
10091051
1010- # Continuation frame (has_more=False)
1011- body2 = encode_row_header (types )
1052+ # Continuation frame (has_more=False) — includes column header
1053+ body2 = encode_uint64 (1 )
1054+ body2 += encode_text ("id" )
1055+ body2 += encode_row_header (types )
10121056 body2 += encode_row_values ([2 ], types )
10131057 body2 += encode_uint64 (ROW_DONE_MARKER )
10141058 h2 = Header (size_words = len (body2 ) // 8 , msg_type = 7 , schema = 0 )
@@ -1024,10 +1068,7 @@ def test_decode_continuation_clears_flag(self) -> None:
10241068 assert isinstance (initial , RowsResponse ) and initial .has_more
10251069
10261070 # decode continuation
1027- cont = decoder .decode_continuation (
1028- column_names = initial .column_names ,
1029- column_count = len (initial .column_names ),
1030- )
1071+ cont = decoder .decode_continuation ()
10311072 assert isinstance (cont , RowsResponse ) and not cont .has_more
10321073
10331074 # Now decode() should work again
@@ -1107,7 +1148,7 @@ def test_decode_continuation_raises_when_not_expected(self) -> None:
11071148 decoder .feed (result .encode ())
11081149
11091150 with pytest .raises (ProtocolError , match = "no ROWS continuation" ):
1110- decoder .decode_continuation (column_names = [ "x" ], column_count = 1 )
1151+ decoder .decode_continuation ()
11111152
11121153 # The message must NOT have been consumed
11131154 assert decoder .has_message (), (
@@ -1339,7 +1380,7 @@ def test_decode_continuation_poisons_on_wrong_type(self) -> None:
13391380 decoder .feed (empty )
13401381
13411382 with pytest .raises (ProtocolError , match = "Expected ROWS continuation" ):
1342- decoder .decode_continuation (column_names = [ "x" ], column_count = 1 )
1383+ decoder .decode_continuation ()
13431384 assert decoder .is_poisoned is True
13441385
13451386 # Subsequent decode() also fails poisoned.
0 commit comments