Problem
Since the 1.6.4 release, every byte-valued result cell renders as a hex string
such as 0x7b2261223a20317d. This includes blobs that hold plain text. A common
case is JSON stored in a MySQL mediumblob column.
In earlier versions these cells rendered as readable text, and the V value
viewer detected the JSON and opened tree mode. Now the cell shows hex and the
value viewer shows the same hex string. There is no way inside the TUI to see
the actual content.
Environment
- sqlit-tui: 1.6.4 (also
main at 9db49c2)
- Provider: MySQL, column type
mediumblob containing UTF-8 JSON
- Also affects any driver that returns
bytes for text-like content
(Postgres bytea, SQLite BLOB, Oracle BLOB, etc.)
Reproduction
CREATE TABLE t (payload MEDIUMBLOB);
INSERT INTO t VALUES ('{"a": 1, "b": [1, 2, 3]}');
SELECT payload FROM t;
Expected cell: {"a": 1, "b": [1, 2, 3]}
Actual cell: 0x7b2261223a20312c202262223a205b312c20322c20335d7d
Press V on the cell: the viewer shows the hex string, not the JSON tree.
Root cause
normalize_arrow_value in sqlit/shared/ui/widgets_tables.py converts all
bytes / bytearray / memoryview values to hex unconditionally before the
Arrow backend is built:
if isinstance(value, (bytes, bytearray, memoryview)):
return f"0x{bytes(value).hex()}"
This was added to stop a UnicodeDecodeError crash on invalid UTF-8 binary
data (#322). In earlier versions textual-fastdatatable cast the raw binary
Arrow column to string with safe=False, so valid UTF-8 bytes rendered as text.
The hex conversion fixes the crash but also removes that text path.
The value viewer is affected for the same reason. action_view_cell_full in
sqlit/domains/results/ui/mixins/results.py reads the cell back from the table
via get_cell_at, and the table only holds the hex string.
Proposed fix
Decode as UTF-8 first; fall back to hex only when decoding fails:
if isinstance(value, (bytes, bytearray, memoryview)):
raw = bytes(value)
try:
return raw.decode("utf-8")
except UnicodeDecodeError:
return f"0x{raw.hex()}"
This restores text rendering for UTF-8 blobs while keeping the crash fix. The
value viewer then receives the decoded text and parse_json_value detects JSON
as before.
Existing tests in tests/ui/test_uuid_fastdatatable.py still pass: the test
payload contains 0x81 and 0xff, which fail a strict UTF-8 decode and still
render as hex.
Related: v tooltip crash on bracketed text
Restoring text rendering exposes a separate pre-existing bug in the lowercase
v cell preview. _show_cell_tooltip assigns the cell string directly to
table.tooltip, and Textual renders string tooltips as Rich markup. JSON that
contains a closing-tag shape such as "[/api/v1]" raises MarkupError when
Textual's hover timer redraws the tooltip, and the app exits. Text such as
"[bold]" is silently stripped instead.
Fix: wrap the value so Rich treats it as literal text:
table.tooltip = Text(tooltip_value)
The uppercase V value viewer already uses markup=False and is not affected.
Test plan
- Add cases to
tests/ui/test_uuid_fastdatatable.py with a UTF-8 JSON
bytes value and assert get_cell_at returns the decoded string, for both
the initial table and the incremental add_rows path.
- Keep the existing invalid-UTF-8 cases asserting hex output.
- Add a case that displays a JSON blob containing
[/api/v1], triggers
action_view_cell, and asserts the tooltip is a Text with the decoded
payload.
- Manual: run the MySQL reproduction above, confirm the cell shows JSON,
v shows the preview without crashing, and V opens tree mode.
Problem
Since the 1.6.4 release, every byte-valued result cell renders as a hex string
such as
0x7b2261223a20317d. This includes blobs that hold plain text. A commoncase is JSON stored in a MySQL
mediumblobcolumn.In earlier versions these cells rendered as readable text, and the
Vvalueviewer detected the JSON and opened tree mode. Now the cell shows hex and the
value viewer shows the same hex string. There is no way inside the TUI to see
the actual content.
Environment
mainat9db49c2)mediumblobcontaining UTF-8 JSONbytesfor text-like content(Postgres
bytea, SQLiteBLOB, OracleBLOB, etc.)Reproduction
Expected cell:
{"a": 1, "b": [1, 2, 3]}Actual cell:
0x7b2261223a20312c202262223a205b312c20322c20335d7dPress
Von the cell: the viewer shows the hex string, not the JSON tree.Root cause
normalize_arrow_valueinsqlit/shared/ui/widgets_tables.pyconverts allbytes/bytearray/memoryviewvalues to hex unconditionally before theArrow backend is built:
This was added to stop a
UnicodeDecodeErrorcrash on invalid UTF-8 binarydata (#322). In earlier versions
textual-fastdatatablecast the raw binaryArrow column to string with
safe=False, so valid UTF-8 bytes rendered as text.The hex conversion fixes the crash but also removes that text path.
The value viewer is affected for the same reason.
action_view_cell_fullinsqlit/domains/results/ui/mixins/results.pyreads the cell back from the tablevia
get_cell_at, and the table only holds the hex string.Proposed fix
Decode as UTF-8 first; fall back to hex only when decoding fails:
This restores text rendering for UTF-8 blobs while keeping the crash fix. The
value viewer then receives the decoded text and
parse_json_valuedetects JSONas before.
Existing tests in
tests/ui/test_uuid_fastdatatable.pystill pass: the testpayload contains
0x81and0xff, which fail a strict UTF-8 decode and stillrender as hex.
Related:
vtooltip crash on bracketed textRestoring text rendering exposes a separate pre-existing bug in the lowercase
vcell preview._show_cell_tooltipassigns the cell string directly totable.tooltip, and Textual renders string tooltips as Rich markup. JSON thatcontains a closing-tag shape such as
"[/api/v1]"raisesMarkupErrorwhenTextual's hover timer redraws the tooltip, and the app exits. Text such as
"[bold]"is silently stripped instead.Fix: wrap the value so Rich treats it as literal text:
The uppercase
Vvalue viewer already usesmarkup=Falseand is not affected.Test plan
tests/ui/test_uuid_fastdatatable.pywith a UTF-8 JSONbytesvalue and assertget_cell_atreturns the decoded string, for boththe initial table and the incremental
add_rowspath.[/api/v1], triggersaction_view_cell, and asserts the tooltip is aTextwith the decodedpayload.
vshows the preview without crashing, andVopens tree mode.