Skip to content

fix(plugin-redis): stop truncating values and keep binary values byte exact - #2008

Merged
datlechin merged 1 commit into
mainfrom
fix/redis-value-truncation
Aug 1, 2026
Merged

fix(plugin-redis): stop truncating values and keep binary values byte exact#2008
datlechin merged 1 commit into
mainfrom
fix/redis-value-truncation

Conversation

@datlechin

Copy link
Copy Markdown
Member

Redis string values were cut at 1,000 characters with ... appended, and that cut value was the only copy the app held.

Root cause

truncatePreview in RedisPluginDriver+ResultBuilding.swift capped every Value cell at 1,000 UTF-16 units. For a STRING key the Value column is not a preview, it is the value, and there is no lazy cell fetch in PluginDatabaseDriver, so the truncated string reached the grid, the JSON tab, Copy JSON, the row inspector and exports. Editing such a cell and saving issued SET key <cut value + "..."> and destroyed the rest in Redis.

All three browse entry points (KEYBROWSE, SCAN, KEYS) fed the same builder, and the export stream had a second copy of the same pipeline.

Values

Strings now return complete. GET is O(1), so there is no reason to bound it, and this matches every other driver in the app, which returns a whole TEXT column and lets the grid truncate at display time. With no incomplete value in the row, the write-back data loss goes away by construction rather than by a guard.

Collections stay bounded because unbounded HGETALL and LRANGE 0 -1 block Redis's single thread. The byte cut is gone: previews are built with JSONSerialization instead of hand-rolled escaping and string concatenation, so a cell always holds valid JSON. Slashes are left unescaped so URLs stay readable.

A new Length column carries what Redis itself reports (STRLEN, LLEN, HLEN, SCARD, ZCARD, XLEN), so you can see how much a collection preview leaves out. It costs one extra pipelined O(1) command in a pipeline that was already being issued, and it is read-only through the existing immutableColumns mechanism that MongoDB and Etcd already use.

The export stream and the paged browse now share one row builder, which is why this deletes more than it adds in that file.

Sidebar

The key tree ran KEYS * through the browse builder, so with full values it would have retained the value of every key just to draw a tree. It now uses a KEYTREE command returning Key and Type only, and scans with SCAN rather than KEYS, which blocks the server for its whole run.

Binary

A value that is not valid UTF-8 was shown as base64 with no indication, and saving wrote the base64 back. parseReply already preserved exact bytes; only redisReplyToString lost them.

The Redis command path is now byte exact end to end:

  • RedisArgumentCodec implements Redis's own argument grammar (sdssplitargs, what redis-cli uses). split decodes \xHH, \n, \r, \t, \b, \a and both quote styles, and rejects unbalanced quotes instead of guessing. quote emits the same grammar back. This replaces the hand-rolled escapeArgument and the old Character-based tokenizer, which could not represent arbitrary bytes at all.
  • The connection layer takes [Data]. hiredis was always length aware (redisCommandArgv with argvlen); only the Swift side narrowed it. Text overloads sit on the byte primitive so the 59 ASCII call sites are unchanged.
  • Value positions in SET, HSET, LPUSH, RPUSH, SADD, SREM, ZADD and ZREM carry Data. Keys, patterns, flags and numbers stay String.
  • The grid picks blob mode from the cell being .bytes, not only from the column type, so a binary value in Redis's mixed-type Value column opens the hex editor instead of the inline text editor.

Behaviour change

A command with unbalanced quotes now errors instead of being silently mangled, matching redis-cli. \xHH in the query editor writes a raw byte, so commands paste both ways.

Tests

New: RedisArgumentCodecTests, RedisBinaryValueTests, RedisKeySummaryTests, RedisKeyTreeCommandTests, plus binary cases in CellInteractionResolverTests and browse-column cases in RedisStatementGeneratorTests. RedisCommandParser is now symlinked into PluginTestSources, so the tests exercise the real parser rather than a copy.

Coverage includes all 256 byte values through quote then split, 200 random blobs, gzip and MessagePack headers, the full redis-cli grammar, generator output re-parsed back to the original bytes, and a hostile value that tries to append DEL victim to the command.

No PluginKit ABI change, and Redis is a bundled plugin, so no registry release is needed.

Not covered

Keys stay UTF-8 text. The sidebar tree, namespace grouping, filters and layout persistence all treat keys as strings, so binary keys are a separate change of similar size. It is now documented as a limitation rather than a silent drop.

Inside a collection preview a binary element still renders as base64, since JSON cannot carry raw bytes. Those cells are previews and are not writable.

No UI automation was added. The grid change is a resolver decision covered by unit tests; driving a binary Redis value through a UI test would need a fixture server.

@mintlify

mintlify Bot commented Aug 1, 2026

Copy link
Copy Markdown

Preview deployment for your docs. Learn more about Mintlify Previews.

Project Status Preview Updated (UTC)
TablePro 🟢 Ready View Preview Aug 1, 2026, 6:11 PM

💡 Tip: Enable Workflows to automatically generate PRs for you.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@datlechin
datlechin merged commit 5093d57 into main Aug 1, 2026
2 of 3 checks passed
@datlechin
datlechin deleted the fix/redis-value-truncation branch August 1, 2026 18:12
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.

1 participant