Goal and current scope
Add a single human-display string quoting policy, CLI_STRING_QUOTE_MODE = NONE | AUTO | ALWAYS, following the requested three-mode design. This replaces the unpublished CLI_SHOW_NULLS proposal in #992; do not retain overlapping boolean controls or change SQL NULL's spelling. The default is NONE, preserving today's bytes. This body supersedes the earlier marker/NULL-only proposals.
SQLite demonstrates independent table-layout and quote-policy controls; Spanner Studio's 2025-12-03 release note establishes an all-STRING display-only precedent. These are references, not a promise to copy their escaping, defaults or dialect semantics.
Accepted display contract
Apply the mode only in TABLE, TABLE_COMMENT, TABLE_DETAIL_COMMENT and VERTICAL, including typed client-side/INFORMATION_SCHEMA SHOW results, DML returned rows, streaming/buffered results and STRING children within ARRAY/STRUCT.
- NONE: preserve the existing formatter unchanged, including raw STRINGs and existing NULL styling.
- ALWAYS: quote every non-NULL STRING, including empty STRING.
- AUTO: quote a STRING if any of the following holds; otherwise retain its raw spelling:
- It is empty, or has leading/trailing Unicode whitespace.
- It exactly equals one of
NULL, null, true, false, NaN, +Inf, -Inf. These include the actual current SQL NULL, BOOL, FLOAT and JSON-null display tokens. The comparisons are case-sensitive; Null and TRUE remain raw.
- Its entire contents match the decimal-number-like grammar
^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$. Examples: 123, 0123, +1, 1e3, .5, 1.. Exclude 0xFF, 1+1 and partial matches. Use a precompiled expression, not per-cell SQL parsing or float conversion/range checks.
- It contains a double quote, backslash, comma, square bracket or curly brace, or any rune for which
strconv.IsPrint is false. Brackets and comma matter for the existing recursive ARRAY/STRUCT display, so check them anywhere, not only at the start. Single quotes alone do not trigger quoting because this policy uses a fixed double-quote delimiter.
AUTO is a documented readability heuristic, not the mathematically smallest set and not full cross-type roundtripping (dates, BYTES, JSON and PROTO/ENUM retain their existing formats). The explicit set is preferable to guessing the type of every cell. Ordinary abc and <NULL> remain raw; empty STRING becomes "".
AUTO and ALWAYS share exactly one quoting operation: strconv.Quote. It provides fixed double quotes with escaped quotes, backslashes and nonprintable characters, preserving NUL and arbitrary Go string bytes. This is a display escape grammar, not a claim of JSON or SQL literal syntax; do not label Go-only escapes such as \x00 as JSON. Every input containing a double quote is itself quoted in AUTO, preventing newly quoted output from colliding with an unchanged already-quoted input.
SQL NULL always remains NULL, with typed no-wrap/dim styling preserved. Non-STRING formatting stays unchanged, including nil/empty BYTES and arrays, JSON's own null, and PROTO/ENUM plugins. A STRING NULL renders as "NULL"; a STRING containing the quotes around NULL renders as "\"NULL\"" in AUTO/ALWAYS.
Implementation boundaries
Use the existing enums/enumer and EnumVar conventions, normal SET/SHOW/RESET/SET LOCAL behavior, and the existing narrow cloned WithComplexPlugin / PluginFromNullable STRING seam over pinned spanvalue v0.8.4. Do not mutate shared presets, change the whole literal format, add a renderer framework or upgrade dependencies. Reuse the held PR's formatting integration; remove obsolete boolean code/tests/docs.
Keep CSV, TSV, JSONL, SQL export, TAB, HTML and XML byte-for-byte unchanged, including SQL-configured client-side export fallbacks. Preformatted plan/stat/presentation rows, headers and metadata remain outside this typed STRING policy. Do not broaden #790 exports or #284 truncation.
Acceptance and verification
- Verify the policy through actual typed format/render paths in every mode: SQL NULL; ordinary/string NULL/quoted NULL; empty/whitespace; booleans, finite/nonfinite number-like strings; decimal grammar boundaries; quotes/backslashes/NUL/newline/nonprinting Unicode; punctuation in recursive ARRAY/STRUCT; literal strings that equal another value's quoted output.
- Compare a one-element STRING array containing
a,b with two elements a and b; AUTO must keep those displayed contents distinct. Preserve nested NULLs, JSON null and PROTO/ENUM behavior.
- Cover normal/colorless/color-enabled output, streaming/buffered tables, typed SHOW, DML returned rows and normal SET/RESET/LOCAL lifecycle. Assert actual failures/causes for invalid ENUM values; preserve state on rejection.
- Keep exact-byte NONE/default and excluded-format regressions. Prefer representative real-path table tests over duplicated helper-only assertions.
- Update user documentation with the three modes, AUTO's documented rules, shared display escapes and unchanged exports. Regenerate reference metadata with
make docs-update.
- One PR,
Fixes #762. Before every additive push, integrate current main, run focused tests, make check and make check-race, capture output and check exit codes. Coverage floor remains 80. Do not merge before exact-head independent acceptance.
References: https://www.sqlite.org/climode.html ; https://docs.cloud.google.com/spanner/docs/release-notes?hl=en#December_03_2025 . Local survey and actual formatter token proof were completed on 2026-09-15 JST; the latter observed true, false, NaN, +Inf, -Inf, and 0.500000 using spanvalue v0.8.4.
Goal and current scope
Add a single human-display string quoting policy,
CLI_STRING_QUOTE_MODE = NONE | AUTO | ALWAYS, following the requested three-mode design. This replaces the unpublishedCLI_SHOW_NULLSproposal in #992; do not retain overlapping boolean controls or change SQL NULL's spelling. The default is NONE, preserving today's bytes. This body supersedes the earlier marker/NULL-only proposals.SQLite demonstrates independent table-layout and quote-policy controls; Spanner Studio's 2025-12-03 release note establishes an all-STRING display-only precedent. These are references, not a promise to copy their escaping, defaults or dialect semantics.
Accepted display contract
Apply the mode only in TABLE, TABLE_COMMENT, TABLE_DETAIL_COMMENT and VERTICAL, including typed client-side/INFORMATION_SCHEMA SHOW results, DML returned rows, streaming/buffered results and STRING children within ARRAY/STRUCT.
NULL,null,true,false,NaN,+Inf,-Inf. These include the actual current SQL NULL, BOOL, FLOAT and JSON-null display tokens. The comparisons are case-sensitive;NullandTRUEremain raw.^[+-]?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?$. Examples:123,0123,+1,1e3,.5,1.. Exclude0xFF,1+1and partial matches. Use a precompiled expression, not per-cell SQL parsing or float conversion/range checks.strconv.IsPrintis false. Brackets and comma matter for the existing recursive ARRAY/STRUCT display, so check them anywhere, not only at the start. Single quotes alone do not trigger quoting because this policy uses a fixed double-quote delimiter.AUTO is a documented readability heuristic, not the mathematically smallest set and not full cross-type roundtripping (dates, BYTES, JSON and PROTO/ENUM retain their existing formats). The explicit set is preferable to guessing the type of every cell. Ordinary
abcand<NULL>remain raw; empty STRING becomes"".AUTO and ALWAYS share exactly one quoting operation:
strconv.Quote. It provides fixed double quotes with escaped quotes, backslashes and nonprintable characters, preserving NUL and arbitrary Go string bytes. This is a display escape grammar, not a claim of JSON or SQL literal syntax; do not label Go-only escapes such as\x00as JSON. Every input containing a double quote is itself quoted in AUTO, preventing newly quoted output from colliding with an unchanged already-quoted input.SQL NULL always remains
NULL, with typed no-wrap/dim styling preserved. Non-STRING formatting stays unchanged, including nil/empty BYTES and arrays, JSON's own null, and PROTO/ENUM plugins. A STRINGNULLrenders as"NULL"; a STRING containing the quotes around NULL renders as"\"NULL\""in AUTO/ALWAYS.Implementation boundaries
Use the existing enums/enumer and EnumVar conventions, normal SET/SHOW/RESET/SET LOCAL behavior, and the existing narrow cloned
WithComplexPlugin/PluginFromNullableSTRING seam over pinned spanvalue v0.8.4. Do not mutate shared presets, change the whole literal format, add a renderer framework or upgrade dependencies. Reuse the held PR's formatting integration; remove obsolete boolean code/tests/docs.Keep CSV, TSV, JSONL, SQL export, TAB, HTML and XML byte-for-byte unchanged, including SQL-configured client-side export fallbacks. Preformatted plan/stat/presentation rows, headers and metadata remain outside this typed STRING policy. Do not broaden #790 exports or #284 truncation.
Acceptance and verification
a,bwith two elementsaandb; AUTO must keep those displayed contents distinct. Preserve nested NULLs, JSON null and PROTO/ENUM behavior.make docs-update.Fixes #762. Before every additive push, integrate current main, run focused tests,make checkandmake check-race, capture output and check exit codes. Coverage floor remains 80. Do not merge before exact-head independent acceptance.References: https://www.sqlite.org/climode.html ; https://docs.cloud.google.com/spanner/docs/release-notes?hl=en#December_03_2025 . Local survey and actual formatter token proof were completed on 2026-09-15 JST; the latter observed
true,false,NaN,+Inf,-Inf, and0.500000using spanvalue v0.8.4.