Repository navigation
sqlite3.iterdump() incompatible with binary data #108590
Copy link
Copy link
Closed
Labels
3.11only security fixesonly security fixes3.12only security fixesonly security fixes3.13only security fixesonly security fixestopic-sqlite3type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
Description
Bug report
Checklist
- I am confident this is a bug in CPython, not a bug in a third-party project
- I have searched the CPython issue tracker,
and am confident this bug has not been reported before
CPython versions tested on:
3.11
Operating systems tested on:
Linux
Output from running 'python -VV' on the command line:
Python 3.11.5 (main, Aug 25 2023, 13:19:53) [GCC 9.4.0]
A clear and concise description of the bug:
Apologies if I'm misunderstanding. Please advice if I should post elsewhere. But shouldn't iterdump() properly detect VARCHAR columns with binary data and output X'' strings instead of throwing an error? This is what sqlite3 .dump does.
import sqlite3
with sqlite3.connect(db_path) as conn:
with open(dump_path, 'w') as dump:
for line in conn.iterdump():
passThe above will throw an error:
File "foo.py", line 79, in dump_sqlite_db
for line in conn.iterdump():
File "/usr/lib/python3.11/sqlite3/dump.py", line 63, in _iterdump
for row in query_res:
sqlite3.OperationalError: Could not decode to UTF-8 column ''INSERT INTO "sync_entities_metadata" VALUES('||quote("storage_key")||','||quote("metadata")||')'' with text 'INSERT INTO "sync_entities_metadata" VALUES(1,'v10����
I tried enabling conn.text_factory = bytes as a workaround, but now get a different error.
File "foo.py", line 79, in dump_sqlite_db
for line in conn.iterdump():
File "/usr/lib/python3.11/sqlite3/dump.py", line 43, in _iterdump
elif table_name.startswith('sqlite_'):
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
TypeError: startswith first arg must be bytes or a tuple of bytes, not str
Linked PRs
- gh-108590: Fix sqlite3.iterdump for invalid unicode in text columns. #108657
- [3.12] gh-108590: Fix sqlite3.iterdump for invalid Unicode in TEXT columns (GH-108657) #108673
- [3.11] gh-108590: Fix sqlite3.iterdump for invalid Unicode in TEXT columns (GH-108657) #108674
- gh-108590: Fix sqlite3 iterdump() for table columns containing invalid Unicode sequences #108683
- gh-108590: Revert gh-108657 (commit 400a1cebc) #108686
- [3.11] gh-108590: Revert gh-108657 (commit 400a1cebc) (#108686) #108694
- gh-108590: Fix sqlite3.iterdump for invalid unicode in text columns and reproducability. #108695
- gh-108590: Improve sqlite3 docs on encoding issues and how to handle those #108699
- [3.12] gh-108590: Improve sqlite3 docs on encoding issues and how to handle those (GH-108699) #111324
- [3.11] gh-108590: Improve sqlite3 docs on encoding issues and how to handle those (GH-108699) #111325
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
3.11only security fixesonly security fixes3.12only security fixesonly security fixes3.13only security fixesonly security fixestopic-sqlite3type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error
Activity
CorvinM commentedon Aug 29, 2023
I am not able to reproduce this on 3.11.5, could you provide an example database or the script/sql to create one that demonstrates the issue?
dotysan commentedon Aug 29, 2023
Hi @CorvinM, thanks for taking the time to attempt to reproduce.
My use case is fiddling around with existing settings in a Google Chrome profile. Your mileage may vary.
But here's a simplified/synthesized test that should be reproducible...
Notice a single non-ASCII/UTF byte in the data VARCHAR column.
Then do this to build the database:
sqlite3 foo.db <foo.sqlEverything works fine! The sqlite3 database is apparently restored and consistent.
But then in Python...
It fails thusly:
erlend-aasland commentedon Aug 29, 2023
I'm unable to reproduce this using the following:
erlend-aasland commentedon Aug 29, 2023
The SQLite shell does not special case VARCHAR (take a look at
shell.cin the SQLite sources); it simply ignores the unprintable character. On my computer, that is also the behaviour ofiterdump.erlend-aasland commentedon Aug 29, 2023
Unable to reproduce on Debian or Ubuntu as well.
erlend-aasland commentedon Aug 29, 2023
BTW, @dotysan, I see you are using the connection context manager. Note that the connection context manager does not close the database, it only makes sure that the transaction in the
withbody is committed;__exit__implicitly executesCOMMITorROLLBACK, depending on if an exception happened. So, from your use case, it seems to me you would be better off by usingitertools.closing.CorvinM commentedon Aug 29, 2023
I am able to reproduce using @dotysan's foo.sql (recreated from hex dump, my terminal/editor was giving problems trying to "fix" the invalid encoding). The sqlite3 CLI both accepts it and reproduces it in a dump so I do believe its a python sqlite bug (rather than a corrupt db). Usually there is not supposed to be invalid encoded characters in a VARCHAR/TEXT field but it seems sqlite takes the garbage in -> garbage out policy. Working on a patch for this.
Heres an alternative hexdump of foo.sql that can be imported a bit easier (with
xxd -r):erlend-aasland commentedon Aug 29, 2023
We can't use a hexdump for a regression test; please provide a Python only reproducer.
erlend-aasland commentedon Aug 29, 2023
@CorvinM, something like this should suffice:
CorvinM commentedon Aug 29, 2023
Unfortunately I don't think I can make it that pretty unless there is a API function that lets us send a query of bytes instead of str that I'm not aware of. The encode() down the chain is causing a problem as its turning the
'\x9f'into a'\xc2\x9f'before being handed to sqlite.To show the encode() problem:
Regardless, this works to show the original issue (albeit nasty):
erlend-aasland commentedon Aug 29, 2023
Hm, yes I noticed this, @CorvinM. I can reproduce using the hexdump.
33 remaining items
erlend-aasland commentedon Aug 30, 2023
@serhiy-storchaka, @CorvinM: As an alternative, I created #108699 in order to try to solve this in documentation only.
serhiy-storchaka commentedon Aug 31, 2023
Let see. It is a complex issue, like every encoding issue.
The SQLite database can contain non UTF-8 sequences of bytes as a text. To work it around, there is a special mechanism: you can set
text_factorytobytes,bytearrayor custom factory which callsstr()with other encoding or errors handler. What do you do with the result -- is your problem. If you getbytesorbytearray, you are responsible of decoding it. If you have a non-standard decoded string, you should use corresponding encoding when write it to a file or handle special characters in some way.iterdump()has its own issues. It is not compatible withbytesandbytearray. When you use non-standard string decoding method, you can write it to file (using the correct encoding method) and feed tosqlite3command, but you cannot feed it toexecute().So what can we do?
iterdump(), the workaround (seems, it is not well known) and the limitation of the workaround.execute()accepting non-UTF-8-encodable SQL statement and values. It should not be enabled by default, because it is easy to produce by accident a DB which breaks other programs. We can even use the same option to use surrogateescapes in results, it is more efficient thantext_factory = lambda x: str(x, errors='surrogateescape').iterdump()converting all surrogateescapes intoCAST(X'...' AS TEXT). Additional checks and transformations have a cost, so perhaps it should not be enabled by default. We can makeiterdump()compatible withtext_factory = bytes, but it will convert all TEXTs into BLOBs.(2) and (3) are complex tasks and may require separate discussions about details. For example, should
text_factoryand new options beConnectionorCursorattributes, or parameters ofexecute()?erlend-aasland commentedon Aug 31, 2023
+1
IMO, this is the preferred solution. See #108699 for a draft docs update.
I'm not sure this is a good idea. The sqlite3 module is a DB API (PEP-249) implementation, and the DB API dictates the param spec of
execute(). If we change that, we'd be drifting further away from the DB API, which IMO is not desirable.Using
CAST(X'...' AS TEXT)in the dump is a possibility. I'm not sure it is worth the added complexity, though.erlend-aasland commentedon Aug 31, 2023
I suggest we focus on 1. for now, the docs update. Well, in my opinion, docs are as hard to get right as encoding issues, so feel free to chime in 😄
serhiy-storchaka commentedon Aug 31, 2023
At least it works with older versions of Python.
gh-108590: Improve sqlite3 docs on encoding issues and how to handle …
pythongh-108590: Improve sqlite3 docs on encoding issues and how to h…
pythongh-108590: Improve sqlite3 docs on encoding issues and how to h…
[3.11] gh-108590: Improve sqlite3 docs on encoding issues and how to …
[3.12] gh-108590: Improve sqlite3 docs on encoding issues and how to …
hugovk commentedon Nov 9, 2023
I see lots of merged PRs for this issue. Can it be closed or is there more to be done?
erlend-aasland commentedon Nov 10, 2023
The sqlite3 docs now includes a section about encoding issues, so it should hopefully be easier to avoid issues like this. However, @serhiy-storchaka is not satisfied with how the docs turned out and has flagged that he wants to improve them further. Perhaps those improvements should be done in a separate issue, though.
I'm fine with closing this.
pythongh-108590: Improve sqlite3 docs on encoding issues and how to h…
pythongh-108590: Improve sqlite3 docs on encoding issues and how to h…