ClickHouse / ClickHouse/ClickHouse
`parseOptions` copy-paste error maps TSV format to `ASTCopyQuery::Formats::CSV` at line 146
- Dominant language
- C++
- Stars
- 49.9k
- Forks
- 9k
- Avg merge
- 21h 32m
- Merged PRs (30d)
- 515
Description
_Found via ClickGap automated review. Please close or comment if this is incorrect or needs adjustment._
_Retrospective finding from a historical scan of [PR #74344](https://github.com/ClickHouse/ClickHouse/pull/74344) (merged 2025-08-12). Confirmed on current codebase — close with a note if already fixed._
### Describe what's wrong
PostgreSQL COPY commands with explicit `FORMAT tsv` produce CSV output (comma-separated with quoted strings) instead of TSV output (tab-separated). COPY FROM with FORMAT tsv rejects tab-separated input expecting commas.
**Root cause:** ParserCopyQuery.cpp:146: `node->format = ASTCopyQuery::Formats::CSV;` is a copy-paste error from line 144. Should be `ASTCopyQuery::Formats::TSV`. Additionally, lines 141-142 compute a lowercased `format_name` variable but all comparisons on lines 143-148 use the original `full_name`, making format names case-sensitive (uppercase FORMAT TSV throws BAD_ARGUMENTS).
**Why we believe this is a bug:** PostgreSQLHandler::processCopyQuery (PostgreSQLHandler.cpp:423) → parses query via ParserCopyQuery → parseOptions (ParserCopyQuery.cpp:122) → format comparison at line 145-146 maps "tsv" to `ASTCopyQuery::Formats::CSV` → handler's switch/toString converts to "CSV" format string → FormatFactory produces CSV output
**Affected locations:**
- `src/Parsers/ParserCopyQuery.cpp:146` — TSV format maps to Formats::CSV instead of Formats::TSV
- `src/Parsers/ParserCopyQuery.cpp:141` — format_name lowercased but never used for comparison
**Impact:** Any PostgreSQL client using COPY with explicit FORMAT tsv gets CSV data instead of TSV, causing data corruption on round-trips. COPY FROM with FORMAT tsv rejects valid TSV input with parse errors.
### Does it reproduce on most recent release?
Yes — confirmed on current `master` (commit `9678bc3a5d5e`).
### How to reproduce
```sql
Connect to ClickHouse via PostgreSQL protocol with psycopg2. Create table: CREATE TABLE t (x UInt32, y String) ENGINE=Memory(). Insert: INSERT INTO t VALUES (1,'a'). Run: cur.copy_expert('COPY t TO STDOUT WITH FORMAT tsv', f). Output will have commas instead of tabs.
```
### Expected behavior
```
COPY TO STDOUT WITH FORMAT tsv should produce: '1\thello\n2\tworld\n' (TSV with tabs, no quoting). COPY FROM STDIN WITH FORMAT tsv should successfully parse tab-separated data.
```
### Error message and/or stacktrace
```
COPY TO STDOUT WITH FORMAT tsv produces: '1,"hello"\n2,"world"\n' (CSV with commas and quoted strings). COPY FROM STDIN WITH FORMAT tsv with TSV data fails with: 'Cannot parse input: expected ',' before: 'hello\n20\tworld\n'
```
### Additional context
**Open risks:**
- The secondary case-sensitivity bug means FORMAT TSV, FORMAT Tsv, etc. all fail with BAD_ARGUMENTS exception
**Suggested fix:** Change line 146 to `node->format = ASTCopyQuery::Formats::TSV;`. Also change comparisons on lines 143-148 to use `format_name` (the lowercased variable) instead of `format->as()->full_name` to make format names case-insensitive.
**Analysis details:** Confidence HIGH | Severity P1 | Testability: `INTEGRATION_TEST`
Found during automated review of [PR #74344](https://github.com/ClickHouse/ClickHouse/pull/74344).
---
_ClickGapAI · Confidence: HIGH · Severity: P1 · Finding: `h_pr74344_001`_
Contributor guide
Assessment
This issue has not been assessed yet.