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

bpo-41930: Add support for SQLite serialise/deserialise API #26728

Merged
merged 73 commits into from
Apr 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
73 commits
Select commit Hold shift + click to select a range
98957b6
Expose sqlite3_serialize and sqlite3_deserialize
May 9, 2021
79ecb6c
Wrap in ifdef for now
Jun 14, 2021
2171869
Merge branch 'main' into sqlite-serialize
Jun 15, 2021
d37484e
Merge branch 'main' into sqlite-serialize
Jun 16, 2021
74ab8a7
Add basic tests
Jun 15, 2021
125a58c
Let AC convert data to buffer
Jun 16, 2021
42f8cd5
Test deserialize type errors
Jun 16, 2021
07d3b5c
Skip tests if serialize API is missing
Jun 16, 2021
6c5147d
Only reset pending statements if needed
Jun 16, 2021
d24fc61
Test deserialize with corrupt data
Jun 16, 2021
7db9dfb
Cast sqlite3_deserialize() arguments
Jun 16, 2021
527ee92
Refactor tests
Jun 16, 2021
6348eaa
Check thread and connection when deserializing
Jun 16, 2021
6e4344f
Simplify serialize return value
Jun 16, 2021
bde320b
Reset cursors and statements before deserialize
Jun 16, 2021
3d8c642
Add NEWS stub
Jun 16, 2021
c28f2bc
Merge branch 'main' into sqlite-serialize
Jun 20, 2021
ddf4da1
Merge branch 'main' into sqlite-serialize
Jun 23, 2021
1bdb611
Merge branch 'main' into sqlite-serialize
Jul 20, 2021
d6934f9
Merge branch 'main' into sqlite-serialize
Jul 29, 2021
fac7cdc
Free SQLite buffer after Py conversion
Jul 30, 2021
9df648f
Transfer ownership of the deserialized data to SQLite
Jul 30, 2021
02d0e45
Add docstrings
Jul 30, 2021
a22bb3d
Merge branch 'main' into sqlite-serialize
Jul 30, 2021
bf5ab3d
Add What's New entry
Jul 31, 2021
5a7f974
Add Docs
Aug 2, 2021
5c7cb03
Improve naming: schema => name
Aug 2, 2021
bea724f
Merge branch 'main' into sqlite-serialize
Aug 8, 2021
729778c
Add more tests
Aug 12, 2021
5214fad
Allow threads while serializing
Aug 12, 2021
5aa2ec5
skip test if SQLite version < 3.36.0
Aug 13, 2021
876e917
Merge branch 'main' into sqlite-serialize
Aug 31, 2021
74b3d63
Merge branch 'main' into sqlite-serialize
Sep 7, 2021
a95eb8a
Fix merge
Sep 12, 2021
d1a3e07
Merge branch 'main' into sqlite-serialize
Sep 12, 2021
03c4b6e
Merge branch 'main' into sqlite-serialize
Sep 14, 2021
179776b
Merge branch 'main' into sqlite-serialize
Oct 6, 2021
5a1f277
Merge branch 'main' into sqlite-serialize
Nov 1, 2021
0e22060
Merge branch 'main' into sqlite-serialize
Nov 2, 2021
f1105fb
Merge branch 'main' into sqlite-serialize
Nov 9, 2021
cebdc7a
Merge branch 'main' into sqlite-serialize
Nov 17, 2021
315d993
Improve tests
Nov 17, 2021
4d17725
Merge branch 'main' into sqlite-serialize
Nov 20, 2021
ce20059
Use autoconf to detect serialize API
Nov 20, 2021
516c59f
Regen clinic
Nov 20, 2021
3b59ec0
Fix test deps
Nov 20, 2021
eec9000
Build sqlite3 with serialize on Windows
Nov 20, 2021
a902bd9
Typo
Nov 20, 2021
c8e2637
Merge branch 'main' into sqlite-serialize
Nov 20, 2021
3b316c8
Fix skipIf decorator
Nov 20, 2021
4681c6e
Merge branch 'main' into sqlite-serialize
Nov 22, 2021
cde0b2e
Merge branch 'main' into sqlite-serialize
Nov 29, 2021
3babef8
Note that these API's are only available if the underlying SQLite lib…
Nov 29, 2021
306a0e2
Merge branch 'main' into sqlite-serialize
Jan 18, 2022
9dffda6
Fix merge: pysqlite_do_all_statements is an ex-function
Jan 18, 2022
e185cbc
Merge branch 'main' into sqlite-serialize
Jan 20, 2022
ac2f552
Use sqlite3_malloc64 and add bigmemtest
Jan 20, 2022
7cdc5ba
Remove spurious newline in VS docs
Jan 20, 2022
0f8a260
Merge branch 'main' into sqlite-serialize
Feb 26, 2022
45c3e3e
Nit: add missing punctuation
Feb 26, 2022
fb127a8
Allow threads while memcpy'ing
Feb 26, 2022
c762528
Remove extra newline
Feb 26, 2022
609a478
Regen clinic
Feb 26, 2022
f81ca8d
Update Doc/library/sqlite3.rst
Feb 27, 2022
013b4de
Address code review
Feb 27, 2022
f0f3c0a
Revert int max change
Feb 27, 2022
6d016f0
Remove redundant info from comment
Feb 27, 2022
cea7219
Update Doc/library/sqlite3.rst
Feb 28, 2022
7ddb4b7
Address review
Feb 28, 2022
7edccfe
Address review: try to avoid memory allocations when serializing
Feb 28, 2022
1db4093
Update PCbuild/_sqlite3.vcxproj
Mar 5, 2022
03b94b0
Merge branch 'main' and regen clinic
Mar 8, 2022
75c3168
Merge branch 'main' into sqlite-serialize
Apr 5, 2022
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: 38 additions & 0 deletions Doc/library/sqlite3.rst
Original file line number Diff line number Diff line change
Expand Up @@ -748,6 +748,44 @@ Connection Objects
.. versionadded:: 3.11


.. method:: serialize(*, name="main")

This method serializes a database into a :class:`bytes` object. For an
ordinary on-disk database file, the serialization is just a copy of the
disk file. For an in-memory database or a "temp" database, the
serialization is the same sequence of bytes which would be written to
disk if that database were backed up to disk.

*name* is the database to be serialized, and defaults to the main
database.

.. note::

This method is only available if the underlying SQLite library has the
serialize API.

.. versionadded:: 3.11


.. method:: deserialize(data, /, *, name="main")

This method causes the database connection to disconnect from database
*name*, and reopen *name* as an in-memory database based on the
serialization contained in *data*. Deserialization will raise
:exc:`OperationalError` if the database connection is currently involved
in a read transaction or a backup operation. :exc:`DataError` will be
raised if ``len(data)`` is larger than ``2**63 - 1``, and
:exc:`DatabaseError` will be raised if *data* does not contain a valid
SQLite database.

.. note::

This method is only available if the underlying SQLite library has the
deserialize API.

.. versionadded:: 3.11


.. _sqlite3-cursor-objects:

Cursor Objects
Expand Down
5 changes: 5 additions & 0 deletions Doc/whatsnew/3.11.rst
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,11 @@ sqlite3
Instead we leave it to the SQLite library to handle these cases.
(Contributed by Erlend E. Aasland in :issue:`44092`.)

* Add :meth:`~sqlite3.Connection.serialize` and
:meth:`~sqlite3.Connection.deserialize` to :class:`sqlite3.Connection` for
serializing and deserializing databases.
(Contributed by Erlend E. Aasland in :issue:`41930`.)


sys
---
Expand Down
55 changes: 55 additions & 0 deletions Lib/test/test_sqlite3/test_dbapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@

from test.support import (
SHORT_TIMEOUT,
bigmemtest,
check_disallow_instantiation,
threading_helper,
)
Expand Down Expand Up @@ -603,6 +604,56 @@ def test_uninit_operations(self):
func)


@unittest.skipUnless(hasattr(sqlite.Connection, "serialize"),
"Needs SQLite serialize API")
class SerializeTests(unittest.TestCase):
def test_serialize_deserialize(self):
with memory_database() as cx:
with cx:
cx.execute("create table t(t)")
data = cx.serialize()
self.assertEqual(len(data), 8192)

# Remove test table, verify that it was removed.
with cx:
cx.execute("drop table t")
regex = "no such table"
with self.assertRaisesRegex(sqlite.OperationalError, regex):
cx.execute("select t from t")

# Deserialize and verify that test table is restored.
cx.deserialize(data)
cx.execute("select t from t")

def test_deserialize_wrong_args(self):
dataset = (
(BufferError, memoryview(b"blob")[::2]),
(TypeError, []),
(TypeError, 1),
(TypeError, None),
)
for exc, arg in dataset:
with self.subTest(exc=exc, arg=arg):
with memory_database() as cx:
self.assertRaises(exc, cx.deserialize, arg)

def test_deserialize_corrupt_database(self):
with memory_database() as cx:
regex = "file is not a database"
with self.assertRaisesRegex(sqlite.DatabaseError, regex):
cx.deserialize(b"\0\1\3")
# SQLite does not generate an error until you try to query the
# deserialized database.
cx.execute("create table fail(f)")

@unittest.skipUnless(sys.maxsize > 2**32, 'requires 64bit platform')
@bigmemtest(size=2**63, memuse=3, dry_run=False)
def test_deserialize_too_much_data_64bit(self):
with memory_database() as cx:
with self.assertRaisesRegex(OverflowError, "'data' is too large"):
cx.deserialize(b"b" * size)


class OpenTests(unittest.TestCase):
_sql = "create table test(id integer)"

Expand Down Expand Up @@ -1030,6 +1081,10 @@ def test_check_connection_thread(self):
lambda: self.con.setlimit(sqlite.SQLITE_LIMIT_LENGTH, -1),
lambda: self.con.getlimit(sqlite.SQLITE_LIMIT_LENGTH),
]
if hasattr(sqlite.Connection, "serialize"):
fns.append(lambda: self.con.serialize())
fns.append(lambda: self.con.deserialize(b""))

for fn in fns:
with self.subTest(fn=fn):
self._run_test(fn)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Add :meth:`~sqlite3.Connection.serialize` and
:meth:`~sqlite3.Connection.deserialize` support to :mod:`sqlite3`. Patch by
Erlend E. Aasland.
160 changes: 159 additions & 1 deletion Modules/_sqlite/clinic/connection.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading