Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #202 - correctly escape backslashes in Postgres text copy #215

Merged
merged 3 commits into from
Apr 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 25 additions & 13 deletions src/include/postgres_text_writer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,34 +21,46 @@ class PostgresTextWriter {
stream.WriteData(const_data_ptr_cast("\b"), 1);
}

void WriteCharInternal(char c) {
stream.WriteData(const_data_ptr_cast(&c), 1);
}

void WriteChar(char c) {
switch (c) {
case '\n':
WriteChar('\\');
WriteChar('n');
WriteCharInternal('\\');
WriteCharInternal('n');
break;
case '\r':
WriteChar('\\');
WriteChar('r');
WriteCharInternal('\\');
WriteCharInternal('r');
break;
case '\b':
WriteChar('\\');
WriteChar('b');
WriteCharInternal('\\');
WriteCharInternal('b');
break;
case '\f':
WriteChar('\\');
WriteChar('f');
WriteCharInternal('\\');
WriteCharInternal('f');
break;
case '\t':
WriteChar('\\');
WriteChar('t');
WriteCharInternal('\\');
WriteCharInternal('t');
break;
case '\v':
WriteChar('\\');
WriteChar('v');
WriteCharInternal('\\');
WriteCharInternal('v');
break;
case '\\':
WriteCharInternal('\\');
WriteCharInternal('\\');
break;
case '"':
WriteCharInternal('\\');
WriteCharInternal('"');
break;
default:
stream.WriteData(const_data_ptr_cast(&c), 1);
WriteCharInternal(c);
break;
}
}
Expand Down
10 changes: 5 additions & 5 deletions src/postgres_copy_to.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,11 @@ void CastListToPostgresArray(ClientContext &context, Vector &input, Vector &varc
result += "NULL";
} else {
if (requires_quotes) {
result += StringUtil::Repeat("\\\"", depth);
result += StringUtil::Repeat("\"", depth);
}
result += child_entries[child_idx].GetString();
if (requires_quotes) {
result += StringUtil::Repeat("\\\"", depth);
result += StringUtil::Repeat("\"", depth);
}
}
}
Expand Down Expand Up @@ -175,11 +175,11 @@ void CastStructToPostgres(ClientContext &context, Vector &input, Vector &varchar
} else {
bool requires_quotes = child_requires_quotes[c];
if (requires_quotes) {
result += StringUtil::Repeat("\\\"", depth);
result += StringUtil::Repeat("\"", depth);
}
result += FlatVector::GetData<string_t>(child_varchar_vectors[c])[r].GetString();
if (requires_quotes) {
result += StringUtil::Repeat("\\\"", depth);
result += StringUtil::Repeat("\"", depth);
}
}
}
Expand All @@ -197,7 +197,7 @@ void CastBlobToPostgres(ClientContext &context, Vector &input, Vector &result, i
continue;
}
const char *HEX_STRING = "0123456789ABCDEF";
string blob_str = "\\\\x";
string blob_str = "\\x";
auto blob_data = const_data_ptr_cast(input_data[r].GetData());
auto blob_size = input_data[r].GetSize();
for (idx_t c = 0; c < blob_size; c++) {
Expand Down
29 changes: 29 additions & 0 deletions test/sql/storage/attach_backslash.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# name: test/sql/storage/attach_backslash.test
# description: Test inserting backslashes using the text copy
# group: [storage]

require postgres_scanner

require-env POSTGRES_TEST_DATABASE_AVAILABLE

statement ok
PRAGMA enable_verification

statement ok
SET pg_use_binary_copy=false;

statement ok
ATTACH 'dbname=postgresscanner' AS s1 (TYPE POSTGRES)

statement ok
CREATE OR REPLACE TABLE s1.varchar_data(v VARCHAR);

statement ok
INSERT INTO s1.varchar_data VALUES ('\42\'), ('"quoted value \ with backslashes ''\"');

query I
SELECT * FROM s1.varchar_data
----
\42\
"quoted value \ with backslashes '\"

Loading