Skip to content

Commit

Permalink
fix(copy): correctly escape non-printable chars
Browse files Browse the repository at this point in the history
Postgres was doing the right thing with them, but the format was wrong.
  • Loading branch information
dvarrazzo committed May 27, 2022
1 parent e6fccaa commit 76a528c
Show file tree
Hide file tree
Showing 2 changed files with 33 additions and 20 deletions.
10 changes: 10 additions & 0 deletions docs/news.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@
``psycopg`` release notes
=========================

Future releases
---------------

Psycopg 3.0.15 (unreleased)
^^^^^^^^^^^^^^^^^^^^^^^^^^^

- Fix wrong escaping of unprintable chars in COPY (nonetheless correctly
interpreted by PostgreSQL).


Current release
---------------

Expand Down
43 changes: 23 additions & 20 deletions psycopg_c/psycopg_c/_psycopg/copy.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -158,20 +158,23 @@ def format_row_text(
# Now from pos to pos + size there is a textual representation: it may
# contain chars to escape. Scan to find how many such chars there are.
for j in range(size):
nesc += copy_escape_lut[target[j]]
if copy_escape_lut[target[j]]:
nesc += 1

# If there is any char to escape, walk backwards pushing the chars
# forward and interspersing backslashes.
if nesc > 0:
tmpsize = size + nesc
target = <unsigned char *>CDumper.ensure_size(out, pos, tmpsize)
for j in range(<int>size - 1, -1, -1):
target[j + nesc] = target[j]
if copy_escape_lut[target[j]] != 0:
if copy_escape_lut[target[j]]:
target[j + nesc] = copy_escape_lut[target[j]]
nesc -= 1
target[j + nesc] = b"\\"
if nesc <= 0:
break
else:
target[j + nesc] = target[j]
pos += tmpsize
else:
pos += size
Expand Down Expand Up @@ -293,24 +296,24 @@ cdef extern from *:
/* handle chars to (un)escape in text copy representation */
/* '\b', '\t', '\n', '\v', '\f', '\r', '\\' */
/* Which char to prepend a backslash when escaping */
/* Escaping chars */
static const char copy_escape_lut[] = {
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 98, 116, 110, 118, 102, 114, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 92, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
};
/* Conversion of escaped to unescaped chars */
Expand Down

0 comments on commit 76a528c

Please sign in to comment.