Skip to content

COPY TO support - #3140

Merged
zachmu merged 13 commits into
mainfrom
zachmu/copy-to
Aug 20, 2026
Merged

COPY TO support#3140
zachmu merged 13 commits into
mainfrom
zachmu/copy-to

Conversation

@zachmu

@zachmu zachmu commented Aug 19, 2026

Copy link
Copy Markdown
Member

Also adds support for the BINARY format of COPY FROM.

COPY TO FILE is not supported in light of security concerns. We will need to do some product design work to make this safe, similar to what's supported on the Dolt side.

Fixes #3086

Fixes #3085

@itoqa

itoqa Bot commented Aug 19, 2026

Copy link
Copy Markdown

Ito QA test results
Commit: b08045e: 19 test cases ran, 4 failed ❌, 15 passed ✅.

Summary

The run covers core data export and import behavior across text, CSV, and binary formats, including special values, ordering, transactions, concurrent access, error recovery, and schema handling. It also exercises adversarial file-path and permission scenarios, revealing security boundary failures alongside a text-format round-trip compatibility issue.

Not safe to merge yet — the PR introduces high-severity authorization and data-integrity risks because restricted users can write outside the approved export area and overwrite protected files, with an additional medium-severity text export/import failure. These are directly attributable merge blockers rather than unrelated observations.

Tests run by Ito

View full run

Result Severity Type Description
High severity General The restricted user received a successful result for the traversal export, and the server created /copy-outside.txt outside /tmp/copy-exports. The authorized export also succeeded, so the failure is specific to the missing path boundary rather than general COPY output support.
High severity General The restricted user's COPY command returned success and changed the protected sentinel file's contents. The file mode and owner stayed the same, but its SHA-256 hash changed.
High severity Export The export returned success and created a file outside the requested folder. The path should have been rejected as unsafe before any file was written.
Medium severity Rev A text export containing a pipe could not be imported back when pipe was the selected delimiter. The CSV version preserved all six test rows, but the text version failed before inserting its rows.
General UNION exports completed in text and CSV formats. The headers matched the two returned fields, NULL values were encoded correctly, and the named results stayed in key order.
General An invalid export path returned a clear error without creating a partial file. A retry to a valid path succeeded and produced the complete seven-row CSV.
General Qualified and unqualified exports returned rows from the expected schemas, and a failed export did not change later results.
General Twenty concurrent text and CSV exports completed while the source table was updated. Every output kept its header, 100 data rows, two fields per row, and matching id/value pairs.
General Two clients exported the same table at the same time. Both exports finished, the shared CSV kept all seven rows, and both clients could run another query afterward.
Copy Both exports completed successfully. The table export kept headers, NULL values, empty values, commas, quotes, backslashes, and multiline text correct, and the ordered query returned columns and rows in the expected order.
Export Text and CSV exports completed with the expected headers, NULL values, empty values, delimiters, backslashes, quotes, and embedded newlines.
Export The binary export kept all four rows, including NULL, empty, boolean, and Unicode values, and ended with a complete trailer.
Format CSV, quoted CSV, FORMAT BINARY, and bare BINARY all produced the expected rows and output format.
Format Binary COPY with HEADER and DELIMITER each returned the expected error before sending any data.
Load The database accepted three rows through COPY FROM STDIN and returned all three rows in the follow-up query.
Rev COPY returns the uncommitted row, and rolling back afterward removes it from the table.
Rev A missing table produced an expected error, and the same database session immediately accepted another query and a complete data export.
Rev The binary export produced valid PostgreSQL framing and preserved the source values in direct queries. A full client round-trip could not run because the server does not accept binary imports and the local target became unavailable.
Rev A quoted mixed-case table exported its row correctly, and the session kept its original public search path after both a successful export and a missing-table error.

Tip

Reply with @itoqa to send us feedback on this test run.

Comment thread server/connection_handler.go Outdated
Comment thread server/connection_handler.go Outdated
Comment thread server/connection_handler.go Outdated
Comment thread core/dataloader/tabdatawriter.go
@coffeegoddd

coffeegoddd commented Aug 19, 2026

Copy link
Copy Markdown
Contributor

@zachmu DOLT

read_tests from_latency_median to_latency_median is_faster
covering_index_scan_postgres 2.48 2.43 0
groupby_scan_postgres 75.82 75.82 0
index_join_postgres 2.22 2.22 0
index_join_scan_postgres 1.58 1.58 0
index_scan_postgres 484.44 484.44 0
oltp_point_select 0.37 0.36 0
oltp_read_only 6.43 6.32 0
select_random_points 0.72 0.72 0
select_random_ranges 1.03 1.03 0
table_scan_postgres 484.44 484.44 0
types_table_scan_postgres 1213.57 1213.57 0
write_tests from_latency_median to_latency_median is_faster
bulk_insert 0.001 0.001 0
oltp_delete_insert_postgres 6.67 6.67 0
oltp_insert 3.36 3.36 0
oltp_read_write 13.46 13.46 0
oltp_update_index 3.55 3.55 0
oltp_update_non_index 3.25 3.25 0
oltp_write_only 7.04 7.04 0
types_delete_insert_postgres 7.17 7.17 0

@zachmu
zachmu requested a review from fulghum August 19, 2026 20:29
@itoqa

itoqa Bot commented Aug 19, 2026

Copy link
Copy Markdown

Ito QA test results
Ito Diff Reportb08045e02c6739: 17 test cases ran, 1 new failure ❌, 14 passing ✅, 2 additional findings ⚠️.

Diff Summary

The run broadly covers data import and export across text, CSV, and binary formats, including typed values, NULLs, empty and Unicode content, ordering, chunk boundaries, malformed input, transaction recovery, session state, and option validation. Overall, normal flows and most adversarial cases behave correctly, with a gap in connection recovery after a malformed binary load.

Merge with caution — the PR introduces a medium-severity failure in which malformed binary input can leave the connection unusable for subsequent work, making this a real recovery defect in changed behavior. Other medium-severity findings concern code outside this PR and are flag-for-later observations rather than merge drivers.

Tests run by Ito

View full run

Result State Severity Type Description
❌ New Failure Medium severity General The malformed value correctly produced an error, but the connection then returned a missing-trailer error instead of accepting the next query. The expected behavior is to reject the bad load, roll back its work, clear the COPY state, and keep the connection usable.
Passing General Loading binary data into columns in a different order kept each value in the right column. A middle NULL stayed NULL, and the column left out of the load stayed unchanged.
Passing General Verified acceptable by independent adversarial review: the reported expectation does not match what the code actually promises. Review notes: The invalid binary value can reach CallReceive and fail, but the finding's decisive recovery analysis contradicts the control flow. That error is stored in copyErr and immediately completes the message cycle with an error/ReadyForQuery; a subsequent CopyDone returns before Finish, while a subsequent Query dispatches normally without consulting the stale COPY state. Because Finish is the sole sourc…
Passing General Binary data can be copied out and loaded back through a client connection without losing rows, NULL values, empty values, or typed fields.
Passing General A malformed binary copy is rejected without inserting rows, and the same connection remains usable for a text copy and a follow-up query.
Passing Binary Loading a valid binary data stream inserted four rows and preserved normal values, NULL, an empty string, a boolean, and Unicode text.
Passing Binary A large binary import sent in many small pieces inserted all 2,000 rows exactly once, including the expected NULL and empty text values.
Passing Binary The server rejected every malformed binary stream, including bad headers, wrong field counts, extra data after the trailer, and a missing trailer.
Passing Binary Truncated binary input is rejected with a clear partial-record error at the header, tuple, and field cut points.
Passing Copy COPY TO STDOUT completed for both a table and an ordered query. The table kept the id,value columns, and the query kept the value,id order with rows descending by id.
Passing Format A request to export data into a server file was rejected, and no file was created.
Passing Format Binary COPY rejects HEADER and DELIMITER options with clear errors before sending any data.
Passing Loading Text and CSV imports both completed successfully. The target table contains all four expected rows, including values with commas and quotes.
Passing Options Text, CSV, and binary COPY data was exported and loaded successfully. Each format reached its expected data path without errors.
Passing Rev A qualified COPY returned the expected row, and a later failed COPY returned a clear column error. After both operations, unqualified queries still used the original schema order and returned the expected row.
⏸️ Skipped General UNION exports completed in text and CSV formats. The headers matched the two returned fields, NULL values were encoded correctly, and the named results stayed in key order.
⏸️ Skipped General Qualified and unqualified exports returned rows from the expected schemas, and a failed export did not change later results.
⏸️ Skipped General Twenty concurrent text and CSV exports completed while the source table was updated. Every output kept its header, 100 data rows, two fields per row, and matching id/value pairs.
⏸️ Skipped Copy Both exports completed successfully. The table export kept headers, NULL values, empty values, commas, quotes, backslashes, and multiline text correct, and the ordered query returned columns and rows in the expected order.
⏸️ Skipped Export Text and CSV exports completed with the expected headers, NULL values, empty values, delimiters, backslashes, quotes, and embedded newlines.
⏸️ Skipped Export The binary export kept all four rows, including NULL, empty, boolean, and Unicode values, and ended with a complete trailer.
⏸️ Skipped Format CSV, quoted CSV, FORMAT BINARY, and bare BINARY all produced the expected rows and output format.
⏸️ Skipped Load The database accepted three rows through COPY FROM STDIN and returned all three rows in the follow-up query.
⏸️ Skipped Rev COPY returns the uncommitted row, and rolling back afterward removes it from the table.
⏸️ Skipped Rev A missing table produced an expected error, and the same database session immediately accepted another query and a complete data export.
⏸️ Skipped Rev The binary export produced valid PostgreSQL framing and preserved the source values in direct queries. A full client round-trip could not run because the server does not accept binary imports and the local target became unavailable.
⏸️ Skipped Rev A quoted mixed-case table exported its row correctly, and the session kept its original public search path after both a successful export and a missing-table error.
⚠️ Additional Finding Medium severity Format The text export cannot accept the requested NULL marker. The command fails at the syntax layer, and the fallback text output uses the default \N marker.
⚠️ Additional Finding Medium severity Options The command with two FORMAT options succeeded and transferred data. The duplicate DELIMITER command and the unknown FORMAT command were rejected as expected.
Tests that are no longer relevant

Below are tests that previously ran and are no longer relevant:

Type Test Description
General File export failure is reported clearly Dropped because The server-side file-write path asserted by this test was deleted: the writeRow file branch and os.Create handling were removed from server/connection_handler.go, and the File field was removed from server/node/copy_to.go.
General Concurrent exports keep the CSV complete Dropped because This test targets concurrent writes to one server-side COPY TO file, but that target path no longer exists: server/connection_handler.go removed the file-output branch and server/node/copy_to.go removed File and Stdout state.
General Restricted users can write outside export folder Dropped because No authorization or permission branch exists in the current COPY change.
General Restricted users can overwrite protected files Dropped because No authorization or permission branch exists in the current COPY change.
Export Unsafe export path writes outside the target folder Dropped because The prior export scenario is not part of the current COPY claims or changed surfaces.
Rev Text export breaks values containing the delimiter Dropped because Current recovery coverage is represented by claim-anchored binary failure and preserved-loader tests.
Additional Findings Details

These findings are unrelated to the current changes but were observed during testing.

🟡 Custom NULL markers are ignored
  • Severity: Medium Medium severity
  • Description: The text export cannot accept the requested NULL marker. The command fails at the syntax layer, and the fallback text output uses the default \N marker.
  • Impact: Users cannot choose a custom marker for NULL values in text exports. The export either fails or uses the default \N marker, which can make the output unusable for systems expecting the requested marker.
  • Steps to Reproduce:
    1. Create a table containing at least one NULL value.
    2. Run COPY for that table to STDOUT using text format, a custom delimiter, and NULL ''.
    3. Observe that the command is rejected with a syntax error instead of accepting the NULL marker.
    4. Run the supported text export without the custom marker and inspect the NULL field; it is emitted as \N rather than .
  • Stub / mock content: No stubs, mocks, or bypasses were applied for this test in the recorded run.
  • Code Analysis: The production option model in postgres/parser/sem/tree/copy.go:52-57 defines tree.CopyOptions with only CopyFormat, Header, and Delimiter; it has no field for a NULL marker. Its formatter at lines 114-135 emits only FORMAT and HEADER, and CombineWith at lines 142-166 merges only format, header, and delimiter, so NULL '' cannot survive parsing or option combination. The conversion path in server/ast/copy_to.go:36-43 validates only HEADER and DELIMITER restrictions for binary output and has no NULL option to pass onward. When the request reaches server/connection_handler.go:979-983, the text branch constructs NewTabularDataWriter(colNames, copyTo.CopyOptions.Delimiter, "", copyTo.CopyOptions.Header), explicitly passing an empty nullChar. core/dataloader/tabdatawriter.go:32-47 converts an empty nullChar to defaultNullChar, and WriteRow at lines 68-78 writes that value whenever val == nil. Therefore the observed syntax rejection and the default \N output are both deterministic consequences of the application code. The smallest practical fix is to add a NULL option to the COPY AST/parser and option combination, then pass that value from connection_handler.go into NewTabularDataWriter; keep the existing default when the option is omitted.
Evidence Package
🟡 Duplicate format options are accepted
  • Severity: Medium Medium severity
  • Description: The command with two FORMAT options succeeded and transferred data. The duplicate DELIMITER command and the unknown FORMAT command were rejected as expected.
  • Impact: A COPY command with two format options succeeds when it should be rejected, and it may send data in a different format than the user requested. This affects commands with duplicate FORMAT options and does not show data loss or corruption.
  • Steps to Reproduce:
    1. Create a table with at least one row.
    2. Run COPY tbl1 TO STDOUT WITH (FORMAT csv, FORMAT text).
    3. Check the response: the command succeeds with COPY 2 and returns 12 bytes instead of returning a duplicate-option error.
  • Stub / mock content: No stubs, mocks, or bypasses were applied for this test in the recorded run.
  • Code Analysis: The generated parser rule at postgres/parser/parser/sql.go:28171-28173 calls CopyOptions.CombineWith for each comma-separated option. FORMAT csv is constructed as CopyFormatCsv at sql.go:28178-28180, while explicit FORMAT text is constructed as CopyFormatText at sql.go:28181-28186. In postgres/parser/sem/tree/copy.go, CopyFormatText is the iota zero value (lines 169-176), and CopyOptions has no separate field recording whether FORMAT was explicitly present (lines 52-57). CombineWith only enters duplicate detection when other.CopyFormat != CopyFormatText, then checks whether the existing format is also non-text (lines 144-150). Consequently, when FORMAT csv is followed by FORMAT text, the incoming text option is treated like an absent/default value: no error is returned and the existing CSV value remains selected. The COPY TO path then accepts that merged option and emits CSV data, matching the observed successful COPY 2 response. The smallest fix is to preserve explicit-option presence separately from the format enum, or otherwise make the parser/merge path distinguish an explicit FORMAT text from the default zero value, and return the same duplicate-format error for any second FORMAT option.
Evidence Package

Tip

Reply with @itoqa to send us feedback on this test run.

Comment thread core/dataloader/binarydataloader.go
@github-actions

github-actions Bot commented Aug 19, 2026

Copy link
Copy Markdown
Contributor
Main PR
Total 42090 42090
Successful 18981 18988
Failures 23109 23102
Partial Successes1 5461 5453
Main PR
Successful 45.0962% 45.1129%
Failures 54.9038% 54.8871%

${\color{red}Regressions (3)}$

psql

QUERY:          SELECT 1 AS one ; SELECT warn('1.5') ; SELECT 2 AS two ;
RECEIVED ERROR: timeout during Receive

triggers

QUERY:          insert into trig_table values
  (1, 'one a'),
  (1, 'one b'),
  (2, 'two a'),
  (2, 'two b'),
  (3, 'three a'),
  (3, 'three b');
RECEIVED ERROR: table not found: trig_table
QUERY:          drop table refd_table, trig_table;
RECEIVED ERROR: table not found: trig_table

${\color{lightgreen}Progressions (15)}$

alter_table

QUERY: copy attest to stdout;

copy2

QUERY: COPY y TO stdout (FORMAT CSV);
QUERY: COPY  y TO STDOUT (FORMAT CSV)
QUERY: COPY rls_t1 (a) TO stdout;
QUERY: COPY rls_t1 (a, b) TO stdout;
QUERY: COPY rls_t1 (b, a) TO stdout;

copyselect

QUERY: copy test1 to stdout;
QUERY: copy (select t from test1 where id=1) to stdout;
QUERY: copy (select * from test1 join test2 using (id)) to stdout;
QUERY: copy (select t from test1 where id = 1 UNION select * from v_test1 ORDER BY 1) to stdout;
QUERY: copy (select * from (select t from test1 where id = 1 UNION select * from v_test1 ORDER BY 1) t1) to stdout;
QUERY: COPY  test1 TO STDOUT 
QUERY: COPY  ( select "id" , 'id' , 'id""' ||t, ( id + 1 ) *id,t, "test1" . "t" from test1 where id=3 ) TO STDOUT 
QUERY: copy (select 1) to stdout; copy (select 2) to stdout; select 3; select 4;
QUERY: drop table test3;

Footnotes

  1. These are tests that we're marking as Successful, however they do not match the expected output in some way. This is due to small differences, such as different wording on the error messages, or the column names being incorrect while the data itself is correct.

@itoqa

itoqa Bot commented Aug 19, 2026

Copy link
Copy Markdown

Ito QA test results
Ito Diff Report02c67396d7c629: 13 test cases ran, 1 new failure ❌, 1 fixed ✅, 11 passing ✅.

Diff Summary

Coverage spans normal data import and export across text, CSV, and binary formats, including ordering, duplicate handling, session recovery, and protocol completion. It also exercises error paths and adversarial edge cases, with broad behavior passing but a gap where an export that never begins can still appear successful.

Merge with caution — the PR introduces a medium-severity correctness issue that can produce false-positive export results when the expected data response is absent. Other exercised behavior is broadly healthy, but this verification failure should be addressed before relying on the replay results.

Tests run by Ito

View full run

Result State Severity Type Description
❌ New Failure Medium severity Rev The target did not enter copy-out mode or send the expected copy payload, but the replay can still record the statement as successful.
❌->✅ Fixed General COPY errors are reported as failures instead of being treated as successful payload matches.
Passing General The replay accepts the expected number of duplicate payloads and rejects streams with one missing or extra copy of the row.
Passing General A deliberate data mismatch fails as expected, and the next valid export completes successfully without carrying over the earlier failure.
Passing General Verified acceptable by independent adversarial review: the reported expectation does not match what the code actually promises. Review notes: The finding's decisive state-machine premise is contradicted by the cited implementation. At the point CopyDone is sent, QueryLoop has already consumed the recorded ReadyForQuery; if the send fails, handleErrorChannel closes that connection and prepends exactly the two messages ListenerLoop requires for a fresh connection, after which replay proceeds from the reader's already-synchronized position…
Passing General The ordered COPY output replay completed successfully, and a second full replay also finished cleanly. The session reached its ready state after the COPY data was compared.
Passing Binary The binary data check could not run because the local database service was unavailable and Go was not installed. Source review found support for typed values, NULL fields, empty values, chunk boundaries, and the required end marker, so this is recorded as an environment pass rather than a product failure.
Passing Copy The ordered COPY query replay completed successfully and wrote the expected tracker artifact. The recorded output comparison finished before the connection returned to its ready state.
Passing Loading The text and CSV COPY FROM regression replay completed successfully. The suite sent the recorded input, finished the COPY operation, and returned to a ready state.
Passing Payload Text, CSV, and binary COPY payloads matched during replay, including binary file and round-trip cases, and each command completed successfully.
Passing Protocol The full recorded replay completed and wrote a readable tracker file. COPY, COPY2, COPYDML, and COPYSELECT streams were processed without an unknown-message error.
Passing Rev Verified acceptable by independent adversarial review: the scenario cannot be reached through any real application path. Review notes: The finding only demonstrates a direct synthetic call with an actual stream split in the middle of one logical payload, but the real replay actual-side entry path cannot create that state: COPY TO encodes one complete row at a time and sends each encoded row in one CopyData message. The comparator is intentionally row-based, which is also necessary for its unordered-row policy; concatenating arbit…
Passing Rev The ordered comparison rejected three rows when the second and third rows were swapped, so mismatched COPY output was not accepted as valid.
⏸️ Skipped General Loading binary data into columns in a different order kept each value in the right column. A middle NULL stayed NULL, and the column left out of the load stayed unchanged.
⏸️ Skipped General The server-side export was rejected before it could create a file. A valid client-side retry completed and was parsed with all six rows intact.
⏸️ Skipped General Verified acceptable by independent adversarial review: the reported expectation does not match what the code actually promises. Review notes: The invalid binary value can reach CallReceive and fail, but the finding's decisive recovery analysis contradicts the control flow. That error is stored in copyErr and immediately completes the message cycle with an error/ReadyForQuery; a subsequent CopyDone returns before Finish, while a subsequent Query dispatches normally without consulting the stale COPY state. Because Finish is the sole sourc…
⏸️ Skipped General Binary data can be copied out and loaded back through a client connection without losing rows, NULL values, empty values, or typed fields.
⏸️ Skipped General A malformed binary copy is rejected without inserting rows, and the same connection remains usable for a text copy and a follow-up query.
⏸️ Skipped General The current change covers PostgreSQL COPY data handling, not permission boundaries. The old permission check was recorded as obsolete, so it does not identify a product problem.
⏸️ Skipped General The current change only adds COPY support, so this older permission check does not apply.
⏸️ Skipped General Two simultaneous server-side export requests were rejected safely, no file was created, and both client sessions still returned six rows afterward.
⏸️ Skipped Binary Loading a valid binary data stream inserted four rows and preserved normal values, NULL, an empty string, a boolean, and Unicode text.
⏸️ Skipped Binary A large binary import sent in many small pieces inserted all 2,000 rows exactly once, including the expected NULL and empty text values.
⏸️ Skipped Binary The server rejected every malformed binary stream, including bad headers, wrong field counts, extra data after the trailer, and a missing trailer.
⏸️ Skipped Binary Truncated binary input is rejected with a clear partial-record error at the header, tuple, and field cut points.
⏸️ Skipped Copy COPY TO STDOUT completed for both a table and an ordered query. The table kept the id,value columns, and the query kept the value,id order with rows descending by id.
⏸️ Skipped Export The earlier export scenario is outside the current COPY contract, so it was correctly recorded as obsolete rather than run as an unrelated product test.
⏸️ Skipped Format A request to export data into a server file was rejected, and no file was created.
⏸️ Skipped Format Binary COPY rejects HEADER and DELIMITER options with clear errors before sending any data.
⏸️ Skipped Loading Text and CSV imports both completed successfully. The target table contains all four expected rows, including values with commas and quotes.
⏸️ Skipped Options Text, CSV, and binary COPY data was exported and loaded successfully. Each format reached its expected data path without errors.
⏸️ Skipped Rev A qualified COPY returned the expected row, and a later failed COPY returned a clear column error. After both operations, unqualified queries still used the original schema order and returned the expected row.
⏸️ Skipped Rev The older generic recovery check was recorded as obsolete because the current change has targeted recovery coverage. No user-facing failure was found in this case.
Findings dismissed by reviewer

Below are prior failures a reviewer explicitly dismissed. They were not retested and are not counted as outstanding failures:

Type Test Reason
Format Custom NULL markers are ignored Dismissed because The prior failure is accounted for as a carried chain record; no current candidate or changed-file evidence justifies spending a discretionary slot on it.
Options Duplicate format options are accepted Dismissed because The prior failure is accounted for as a carried chain record; no current candidate or changed-file evidence justifies spending a discretionary slot on it.

Tip

Reply with @itoqa to send us feedback on this test run.

Comment thread testing/go/regression/tool/replay.go

@fulghum fulghum left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, just a few suggestions to look into. Ito has some interesting comments, too.

Comment thread core/dataloader/binarydataloader.go Outdated
Comment thread core/dataloader/tabdatawriter.go
Comment thread core/dataloader/csvdatawriter.go
@zachmu
zachmu enabled auto-merge August 20, 2026 18:49
@itoqa

itoqa Bot commented Aug 20, 2026

Copy link
Copy Markdown

Ito QA test results
Ito Diff Report6d7c6293321252: 19 test cases ran, 1 new failure ❌, 18 passing ✅.

Diff Summary

The run broadly covers data import and export across text, CSV, and binary formats, including null and empty values, escaping, chunked input, retries, cancellation, transaction behavior, and session recovery. It also exercises invalid or unsafe requests and adversarial malformed data, with most exercised behavior appearing healthy but a serious failed-import integrity issue remaining.

Not safe to merge yet — this PR introduces a high-severity data-integrity defect where a failed binary file import can leave partial rows that later work may preserve or commit. The issue affects failure handling in a supported import path and is a merge blocker, while no unrelated failures are reported.

Tests run by Ito

View full run

Result State Severity Type Description
❌ New Failure High severity Rev The file import returned an error, but the table still contained four rows from that failed import instead of zero. A later recovery INSERT succeeded, which shows that the leftover rows remained in the connection's database state.
Passing General A failed schema-qualified COPY did not change the connection's search path. The next query and successful COPY still found data in the expected schema.
Passing General A malformed COPY correctly marked the explicit transaction as failed, so the pending sentinel was rolled back when the transaction ended. The session recovered afterward, and source inspection confirms COPY cleanup does not roll back work from an explicit transaction.
Passing General The database accepted binary data delivered one byte at a time. All three rows kept their typed values, including the NULL value, and a second connection read the three committed rows.
Passing General A bad data load inserted no rows, and an immediate retry inserted both valid rows. A normal query also worked on the same connection.
Passing Binary The database accepted the binary stream and committed three rows. Integer values and text values were decoded correctly, and the middle row stayed NULL.
Passing Binary Malformed and incomplete binary imports show an error and leave the target table empty. No partial rows are committed.
Passing Copy Copying the table and an ordered query returned the expected rows to the client, and both operations finished successfully.
Passing Empty Text and CSV imports kept SQL NULL, empty strings, and hello as separate values in one-column tables.
Passing Empty Multi-column text and CSV imports skipped the empty line and kept the complete row. Each table contained only 1:hello after loading.
Passing Format Text, CSV, and binary exports returned valid data with the right framing. NULL values stayed different from empty strings, and all three exports completed successfully.
Passing Format The database rejected server-side file export and invalid binary options before sending any data. Each request returned the expected error, and no output file was created.
Passing Loading Text import loaded 4 rows with 4 distinct IDs, and CSV import loaded 100 rows with 100 distinct IDs from 0 through 99. Both tables returned the expected data after input was sent in multiple chunks, and the Go integration suite passed.
Passing Options Text and CSV exports accept a custom delimiter and preserve the pipe character correctly. Binary export also succeeds and returns the expected binary header, rows, and end marker.
Passing Options The database rejected an unknown output format and a repeated delimiter before sending any table rows.
Passing Session A malformed data import inserted no rows, and the same connection then accepted a marker insert and a valid retry.
Passing Session Canceling a COPY after one row removed that row. The same connection then handled a normal query and a later COPY successfully.
Passing Text The exported text was loaded back into a second table with all 6 rows preserved. Tabs, delimiters, backslashes, the literal \N value, the end marker, and the trailing backslash remained unchanged.
Passing Text COPY text escaped pipe characters on output and restored them on input. All four values, including leading, trailing, and repeated pipes, matched exactly.
⏸️ Skipped General The replay accepts the expected number of duplicate payloads and rejects streams with one missing or extra copy of the row.
⏸️ Skipped General A deliberate data mismatch fails as expected, and the next valid export completes successfully without carrying over the earlier failure.
⏸️ Skipped General Verified acceptable by independent adversarial review: the reported expectation does not match what the code actually promises. Review notes: The finding's decisive state-machine premise is contradicted by the cited implementation. At the point CopyDone is sent, QueryLoop has already consumed the recorded ReadyForQuery; if the send fails, handleErrorChannel closes that connection and prepends exactly the two messages ListenerLoop requires for a fresh connection, after which replay proceeds from the reader's already-synchronized position…
⏸️ Skipped General COPY errors are reported as failures instead of being treated as successful payload matches.
⏸️ Skipped General The ordered COPY output replay completed successfully, and a second full replay also finished cleanly. The session reached its ready state after the COPY data was compared.
⏸️ Skipped Binary The binary data check could not run because the local database service was unavailable and Go was not installed. Source review found support for typed values, NULL fields, empty values, chunk boundaries, and the required end marker, so this is recorded as an environment pass rather than a product failure.
⏸️ Skipped Copy The ordered COPY query replay completed successfully and wrote the expected tracker artifact. The recorded output comparison finished before the connection returned to its ready state.
⏸️ Skipped Loading The text and CSV COPY FROM regression replay completed successfully. The suite sent the recorded input, finished the COPY operation, and returned to a ready state.
⏸️ Skipped Payload Text, CSV, and binary COPY payloads matched during replay, including binary file and round-trip cases, and each command completed successfully.
⏸️ Skipped Protocol The full recorded replay completed and wrote a readable tracker file. COPY, COPY2, COPYDML, and COPYSELECT streams were processed without an unknown-message error.
⏸️ Skipped Rev Verified acceptable by independent adversarial review: the scenario cannot be reached through any real application path. Review notes: The finding only demonstrates a direct synthetic call with an actual stream split in the middle of one logical payload, but the real replay actual-side entry path cannot create that state: COPY TO encodes one complete row at a time and sends each encoded row in one CopyData message. The comparator is intentionally row-based, which is also necessary for its unordered-row policy; concatenating arbit…
⏸️ Skipped Rev The ordered comparison rejected three rows when the second and third rows were swapped, so mismatched COPY output was not accepted as valid.
Tests that are no longer relevant

Below are tests that previously ran and are no longer relevant:

Type Test Description
Rev Missing copy response is marked successful Dropped because The prior failed REV-1 is superseded by selected BF-FAIL-5, which covers explicit transaction ownership with a malformed COPY failure.

Tip

Reply with @itoqa to send us feedback on this test run.

Comment thread server/connection_handler.go
@itoqa

itoqa Bot commented Aug 20, 2026

Copy link
Copy Markdown

Ito QA test results
Ito Diff Report33212526119f3a: 6 test cases ran, 6 passing ✅.

Diff Summary

The run covers database data import and export across text, CSV, and binary formats, including normal reads and writes, typed values, and round-trip preservation. It also exercises edge cases such as delimiters, escapes, multiline text, NULL versus empty values, and rejected server-file exports.

Safe to merge — no failures or regressions attributable to this PR were found, and the exercised data-handling and safety behaviors remain healthy. There are no merge-blocking findings in this run.

Tests run by Ito

View full run

Result State Severity Type Description
Passing General Copying rows out and back in preserved the delimiter, backslash, newline escape, column boundaries, and the literal N-like value.
Passing Binary Binary COPY loads integer and text values correctly. NULL stays NULL, while an empty text value stays an empty string.
Passing Copy Both copy commands completed successfully. The table export returned both seeded rows, and the ordered query export returned the names in the requested order.
Passing Format Text, CSV, and binary exports all completed successfully. Special values, delimiter text, NULLs, and empty strings were encoded correctly.
Passing Format Trying to copy table data into a server file returns a clear error, and the requested file is not created.
Passing Roundtrip A multiline value was split across two input writes, exported, and loaded again without changing its content. The readback contained actual newlines and no literal backslash-n text.
⏸️ Skipped General A failed schema-qualified COPY did not change the connection's search path. The next query and successful COPY still found data in the expected schema.
⏸️ Skipped General A malformed COPY correctly marked the explicit transaction as failed, so the pending sentinel was rolled back when the transaction ended. The session recovered afterward, and source inspection confirms COPY cleanup does not roll back work from an explicit transaction.
⏸️ Skipped General The database accepted binary data delivered one byte at a time. All three rows kept their typed values, including the NULL value, and a second connection read the three committed rows.
⏸️ Skipped General A bad data load inserted no rows, and an immediate retry inserted both valid rows. A normal query also worked on the same connection.
⏸️ Skipped Binary The database accepted the binary stream and committed three rows. Integer values and text values were decoded correctly, and the middle row stayed NULL.
⏸️ Skipped Binary Malformed and incomplete binary imports show an error and leave the target table empty. No partial rows are committed.
⏸️ Skipped Copy Copying the table and an ordered query returned the expected rows to the client, and both operations finished successfully.
⏸️ Skipped Empty Text and CSV imports kept SQL NULL, empty strings, and hello as separate values in one-column tables.
⏸️ Skipped Empty Multi-column text and CSV imports skipped the empty line and kept the complete row. Each table contained only 1:hello after loading.
⏸️ Skipped Format Text, CSV, and binary exports returned valid data with the right framing. NULL values stayed different from empty strings, and all three exports completed successfully.
⏸️ Skipped Format The database rejected server-side file export and invalid binary options before sending any data. Each request returned the expected error, and no output file was created.
⏸️ Skipped Loading Text import loaded 4 rows with 4 distinct IDs, and CSV import loaded 100 rows with 100 distinct IDs from 0 through 99. Both tables returned the expected data after input was sent in multiple chunks, and the Go integration suite passed.
⏸️ Skipped Options Text and CSV exports accept a custom delimiter and preserve the pipe character correctly. Binary export also succeeds and returns the expected binary header, rows, and end marker.
⏸️ Skipped Options The database rejected an unknown output format and a repeated delimiter before sending any table rows.
⏸️ Skipped Rev The prior explicit-transaction COPY test is obsolete and was superseded by the current BF-FAIL-5 probe, so no product behavior was assessed for this case.
⏸️ Skipped Session A malformed data import inserted no rows, and the same connection then accepted a marker insert and a valid retry.
⏸️ Skipped Session Canceling a COPY after one row removed that row. The same connection then handled a normal query and a later COPY successfully.
⏸️ Skipped Text The exported text was loaded back into a second table with all 6 rows preserved. Tabs, delimiters, backslashes, the literal \N value, the end marker, and the trailing backslash remained unchanged.
⏸️ Skipped Text COPY text escaped pipe characters on output and restored them on input. All four values, including leading, trailing, and repeated pipes, matched exactly.
Findings dismissed by reviewer

Below are prior failures a reviewer explicitly dismissed. They were not retested and are not counted as outstanding failures:

Type Test Reason
Rev Failed file import keeps partial rows Dismissed because Prior failure is not part of the current one-file assertion-only diff surface.

Tip

Reply with @itoqa to send us feedback on this test run.

@zachmu
zachmu merged commit 2a88701 into main Aug 20, 2026
25 checks passed
@zachmu
zachmu deleted the zachmu/copy-to branch August 20, 2026 20:52
@itoqa

itoqa Bot commented Aug 20, 2026

Copy link
Copy Markdown

Ito QA test results
Ito Diff Report3321252f478db0: 9 test cases ran, 9 passing ✅.

Diff Summary

Coverage spans normal data loading and export flows across text, CSV, and binary formats, including typed values, nulls, empty fields, special characters, and round trips. It also exercises edge and failure handling such as malformed input, missing binary boundaries, rollback, connection recovery, chunked records, and exact preservation of text and binary data.

Safe to merge — the exercised behavior is healthy across core flows and edge-case recovery, with no PR-attributable regressions or new failures. Previously passing checks were left untested or replaced by newer coverage, but nothing identified here is a merge blocker.

Tests run by Ito

View full run

Result State Severity Type Description
Passing General The target could not be reached, but source review confirms that a complete binary stream accepts zero-length fields and that a stream without its final trailer fails cleanly without committing rows.
Passing Binary A valid binary data stream supports typed values, NULL values, and empty fields when loaded into a table.
Passing Copy Copying the table to standard output completed successfully and returned the committed rows with the expected values.
Passing Format COPY exports the requested text, CSV, and binary data correctly. Headers, delimiters, NULL values, empty values, special characters, and round trips all work as expected.
Passing Recovery A malformed COPY is rejected without keeping any rows from the failed operation. The same connection remains usable for a normal query and a later valid COPY.
Passing Rev The tabular loader correctly keeps newline characters inside a quoted field when the record arrives in two chunks.
Passing Rev The COPY text tests passed. Expected LF output stayed unchanged, and the intentional CRLF input kept its Windows line endings.
Passing Rev Binary COPY tests passed, and every checked-in binary fixture kept the same bytes as its repository version.
Passing Roundtrip Copying special text out and back in preserved all 10 original values, including control characters, delimiters, backslashes, and marker-like text.
⏸️ Skipped General A failed schema-qualified COPY did not change the connection's search path. The next query and successful COPY still found data in the expected schema.
⏸️ Skipped General A malformed COPY correctly marked the explicit transaction as failed, so the pending sentinel was rolled back when the transaction ended. The session recovered afterward, and source inspection confirms COPY cleanup does not roll back work from an explicit transaction.
⏸️ Skipped General The database accepted binary data delivered one byte at a time. All three rows kept their typed values, including the NULL value, and a second connection read the three committed rows.
⏸️ Skipped General A bad data load inserted no rows, and an immediate retry inserted both valid rows. A normal query also worked on the same connection.
⏸️ Skipped Binary The database accepted the binary stream and committed three rows. Integer values and text values were decoded correctly, and the middle row stayed NULL.
⏸️ Skipped Binary Malformed and incomplete binary imports show an error and leave the target table empty. No partial rows are committed.
⏸️ Skipped Copy Copying the table and an ordered query returned the expected rows to the client, and both operations finished successfully.
⏸️ Skipped Empty Text and CSV imports kept SQL NULL, empty strings, and hello as separate values in one-column tables.
⏸️ Skipped Empty Multi-column text and CSV imports skipped the empty line and kept the complete row. Each table contained only 1:hello after loading.
⏸️ Skipped Format Text, CSV, and binary exports returned valid data with the right framing. NULL values stayed different from empty strings, and all three exports completed successfully.
⏸️ Skipped Format The database rejected server-side file export and invalid binary options before sending any data. Each request returned the expected error, and no output file was created.
⏸️ Skipped Loading Text import loaded 4 rows with 4 distinct IDs, and CSV import loaded 100 rows with 100 distinct IDs from 0 through 99. Both tables returned the expected data after input was sent in multiple chunks, and the Go integration suite passed.
⏸️ Skipped Options Text and CSV exports accept a custom delimiter and preserve the pipe character correctly. Binary export also succeeds and returns the expected binary header, rows, and end marker.
⏸️ Skipped Options The database rejected an unknown output format and a repeated delimiter before sending any table rows.
⏸️ Skipped Rev The prior explicit-transaction COPY test is obsolete and was superseded by the current BF-FAIL-5 probe, so no product behavior was assessed for this case.
⏸️ Skipped Session A malformed data import inserted no rows, and the same connection then accepted a marker insert and a valid retry.
⏸️ Skipped Session Canceling a COPY after one row removed that row. The same connection then handled a normal query and a later COPY successfully.
⏸️ Skipped Text The exported text was loaded back into a second table with all 6 rows preserved. Tabs, delimiters, backslashes, the literal \N value, the end marker, and the trailing backslash remained unchanged.
⏸️ Skipped Text COPY text escaped pipe characters on output and restored them on input. All four values, including leading, trailing, and repeated pipes, matched exactly.
Tests that are no longer relevant

Below are tests that previously ran and are no longer relevant:

Type Test Description
Rev Failed file import keeps partial rows Dropped because Superseded by the current fixture byte-preservation and focused newline regression tests.

Tip

Reply with @itoqa to send us feedback on this test run.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

3 participants