[ 
https://issues.apache.org/jira/browse/CASSANDRA-21131?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18091383#comment-18091383
 ] 

Brad Schoening edited comment on CASSANDRA-21131 at 6/25/26 4:48 AM:
---------------------------------------------------------------------

A clean conceptual fix is to recognize that the backslash-doubling belongs to 
{_}display rendering{_}, not to representing the canonical value in plain text.

Formatting currently has two modes, Plain and Color.  Instead of adding a new 
boolean parameter, I suggest recognizing we have different output modes and use 
an enum.
{code:java}
class OutputMode(Enum):
    COLOR_DISPLAY = auto()   # ANSI palette (still needs the colormap dict), 
single backslash, FormattedValue
    PLAIN_DISPLAY = auto()   # no ANSI, backslash doubled to disambiguate, bare 
str
    TEXT   = auto()   # no ANSI, raw bytes, tbd how to handle 
U+0000–U+001F{code}
The consumer or producer of CSV may be an unknown external parser or exporter, 
not just COPY TO/COPY FROM round-tripping. We shouldn't rely on a private 
contract between cqlsh's writer and reader — the bytes have to be safe and 
unambiguous to a stranger.

JSON is unambiguous precisely where RFC 4180 is silent. Per RFC 8259, a JSON 
string's {{unescaped}} production starts at U+0020 — every control character 
U+0000–U+001F _must_ be escaped. A literal NUL or backspace inside a JSON 
string is invalid JSON; conformant parsers reject it, lenient ones tolerate it. 
JSON does not allow raw backspaces or nulls.


was (Author: bschoeni):
A clean conceptual fix is to recognize that the backslash-doubling belongs to 
{_}display rendering{_}, not to representing the canonical value in plain text.

Formatting currently has two modes, Plain and Color.  Instead of adding a new 
boolean parameter, I suggest recognizing we have different output modes and use 
an enum.
{code:java}
class OutputMode(Enum):
    COLOR_DISPLAY = auto()   # ANSI palette (still needs the colormap dict), 
single backslash, FormattedValue
    PLAIN_DISPLAY = auto()   # no ANSI, backslash doubled to disambiguate, bare 
str
    TEXT   = auto()   # no ANSI, raw bytes, tbd how to handle 
U+0000–U+001F{code}

The consumer or producer of CSV may be an unknown external parser or exporter, 
not just COPY TO/COPY FROM round-tripping. We shouldn't rely on a private 
contract between cqlsh's writer and reader — the bytes have to be safe and 
unambiguous to a stranger.

> CSV COPY TO/FROM double-escapes backslashes in text columns, corrupting data 
> on round-trip
> ------------------------------------------------------------------------------------------
>
>                 Key: CASSANDRA-21131
>                 URL: https://issues.apache.org/jira/browse/CASSANDRA-21131
>             Project: Apache Cassandra
>          Issue Type: Bug
>          Components: CQL/Interpreter
>            Reporter: Jens Geyer
>            Priority: Normal
>         Attachments: console.txt, cyclist.csv, test_cassandra_21131.py, 
> test_cassandra_21131_collections.py
>
>          Time Spent: 2.5h
>  Remaining Estimate: 0h
>
> I'm following the example from the docs, just slightly changed: 
> https://docs.datastax.com/en/cql-oss/3.x/cql/cql_reference/cqlshCopy.html
> {code}
> CREATE KEYSPACE cycling  WITH REPLICATION = { 'class' : 
> 'NetworkTopologyStrategy', 'datacenter1' : 1 } ;
> CREATE TABLE cycling.cyclist_name ( id UUID PRIMARY KEY, lastname text, 
> firstname text ) ;
> INSERT INTO cycling.cyclist_name (id, lastname, firstname) 
>    VALUES (5b6962dd-3f90-4c93-8f61-eabfa4a803e2, 'VOS','Marianne');
> INSERT INTO cycling.cyclist_name (id, lastname, firstname) 
>    VALUES (88888888-8888-8888-8888-888888888888, 'V\S','\"Marianne"\');
> SELECT * FROM cycling.cyclist_name ;
> {code}
> This gives:
> {code}
>  id                                   | firstname      | lastname
> --------------------------------------+----------------+----------
>  5b6962dd-3f90-4c93-8f61-eabfa4a803e2 |       Marianne |      VOS
>  88888888-8888-8888-8888-888888888888 | \\"Marianne"\\ |     V\\S
> (2 rows)
> {code}
> Now export to a file, then import from the very same file:
> {code}
> COPY cycling.cyclist_name TO 'cyclist.csv' WITH HEADER = TRUE ;
> TRUNCATE cycling.cyclist_name ;
> COPY cycling.cyclist_name FROM 'cyclist.csv' WITH HEADER = TRUE ;
> SELECT * FROM cycling.cyclist_name ;
> {code}
> Now the result is this:
> {code}
>  id                                   | firstname          | lastname
> --------------------------------------+--------------------+----------
>  5b6962dd-3f90-4c93-8f61-eabfa4a803e2 |           Marianne |      VOS
>  88888888-8888-8888-8888-888888888888 | \\\\"Marianne"\\\\ |   V\\\\S
> (2 rows)
> {code}
> I did not change any data in between. Just export and reimport.



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to