From 92dbcc21cacba4ceaa79022e5fe9cd95d7ecd3e7 Mon Sep 17 00:00:00 2001 From: hippi345 <13539685+hippi345@users.noreply.github.com> Date: Fri, 30 Oct 2026 15:22:20 +0000 Subject: [PATCH 1/2] Quote column identifiers in binary_copy_to_table Use quote_ident for COPY column lists so names like a-b work without manual double-quoting, matching copy_records_to_table behavior. --- .../tests/test_binary_copy_quoted_columns.py | 49 +++++++++++++++++++ src/driver/common.rs | 8 ++- 2 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 python/tests/test_binary_copy_quoted_columns.py diff --git a/python/tests/test_binary_copy_quoted_columns.py b/python/tests/test_binary_copy_quoted_columns.py new file mode 100644 index 00000000..fe6d52dd --- /dev/null +++ b/python/tests/test_binary_copy_quoted_columns.py @@ -0,0 +1,49 @@ +import typing +from io import BytesIO + +import pytest +import pyarrow as pa +from pgpq import ArrowToPostgresBinaryEncoder +from psqlpy import ConnectionPool + +pytestmark = pytest.mark.anyio + +QUOTED_COLUMN: typing.Final = "a-b" + + +async def test_binary_copy_to_table_quotes_column_names( + psql_pool: ConnectionPool, +) -> None: + """Column names that require quoting work without manual escaping.""" + table_name: typing.Final = "binary_copy_quoted_cols" + connection = await psql_pool.connection() + await connection.execute(f'DROP TABLE IF EXISTS "{table_name}"') + await connection.execute( + f""" + CREATE TABLE "{table_name}" ( + "{QUOTED_COLUMN}" TEXT + ) + """, + ) + + arrow_table = pa.table({QUOTED_COLUMN: ["hello"]}) + encoder = ArrowToPostgresBinaryEncoder(arrow_table.schema) + buf = BytesIO() + buf.write(encoder.write_header()) + for batch in arrow_table.to_batches(): + buf.write(encoder.write_batch(batch)) + buf.write(encoder.finish()) + buf.seek(0) + + inserted_rows = await connection.binary_copy_to_table( + source=buf, + table_name=table_name, + columns=[QUOTED_COLUMN], + ) + + assert inserted_rows == 1 + + result = await connection.execute( + f'SELECT "{QUOTED_COLUMN}" FROM "{table_name}"', + ) + assert result.result()[0][QUOTED_COLUMN] == "hello" diff --git a/src/driver/common.rs b/src/driver/common.rs index c897e307..92d607e0 100644 --- a/src/driver/common.rs +++ b/src/driver/common.rs @@ -300,10 +300,14 @@ macro_rules! impl_binary_copy_method { let copy_qs = match columns { Some(ref cols) if !cols.is_empty() => { + let cols_sql = cols + .iter() + .map(|c| quote_ident(c)) + .collect::>() + .join(", "); format!( "COPY {}({}) FROM STDIN (FORMAT binary)", - full_table_name, - cols.join(", ") + full_table_name, cols_sql ) } _ => format!("COPY {} FROM STDIN (FORMAT binary)", full_table_name), From 1e9e32e61945f201f03f8b8261669386c489a909 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 30 Oct 2026 15:23:03 +0000 Subject: [PATCH 2/2] [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci --- python/tests/test_binary_copy_quoted_columns.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/tests/test_binary_copy_quoted_columns.py b/python/tests/test_binary_copy_quoted_columns.py index fe6d52dd..0f6c0e9a 100644 --- a/python/tests/test_binary_copy_quoted_columns.py +++ b/python/tests/test_binary_copy_quoted_columns.py @@ -1,8 +1,8 @@ import typing from io import BytesIO -import pytest import pyarrow as pa +import pytest from pgpq import ArrowToPostgresBinaryEncoder from psqlpy import ConnectionPool