Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I attach an in-memory SQLite database in Python?

Tags:

python

sqlite

I'd like to merge SQLite databases, and some may be in memory. I create the in-memory databases by specifying the database path as :memory:. Following this post, using the attach feature of SQLite seems like both a simple and efficient approach. But how can I specify my in-memory database as the source to attach?

For example, I'd want to do something like:

c1 = sqlite3.connect(":memory:")
c1.execute(...create table, insert a bunch, commit...)

c2 = sqlite3.connect(":memory:")
c2.execute("""
  ATTACH ? AS ToMerge;
  BEGIN; 
    INSERT INTO Records SELECT * FROM ToMerge.Records; 
  COMMIT;
""", (c1.get_attach_id(), ))

but, of course, c1.get_attach_id() is a method I made up for demonstration purposes, since using the string :memory: would be ambiguous. How can I specify the existing c1 database?

like image 598
Willi Ballenthin Avatar asked Sep 20 '15 16:09

Willi Ballenthin


People also ask

How do I connect to a memory in SQLite database?

The most common way to force an SQLite database to exist purely in memory is to open the database using the special filename ":memory:". In other words, instead of passing the name of a real disk file into one of the sqlite3_open(), sqlite3_open16(), or sqlite3_open_v2() functions, pass in the string ":memory:".

Does SQLite run in memory?

SQLite in-memory databases are databases stored entirely in memory, not on disk. Use the special data source filename :memory: to create an in-memory database. When the connection is closed, the database is deleted.

How do I create a memory database in Python?

The Python interface does support the in-memory implementation that the SQLite3 C API offers. From the spec: You can also supply the special name :memory: to create a database in RAM. You can then proceed like you were using a regular database.


1 Answers

A plain :memory: string connecting to an in-memory database cannot be shared or attached to from other connections.

You need to use a file: URI filename connection string with a ?cache=shared parameter to be able to share the in-memory database between connections; then you can also attach to it:

# first connection
c1 = sqlite3.connect("file::memory:?cache=shared", uri=True)

# second connection, to the *same database*
c2 = sqlite3.connect("file::memory:?cache=shared", uri=True)

# third connection, to a different database altogether
c3 = sqlite3.connect('/tmp/sqlite3.db', uri=True)
# can attach to the shared in-memory database, but only if you used
# uri=True on the original connection
c3.execute("ATTACH DATABASE 'file::memory:?cache=shared' AS inmem")

See the In-Memory Databases documentation.

Note that there can only be one such shared in-memory database; all other in-memory databases must remain private to their connection. Use databases with an actual filesystem storage if you need more complex setups; these are easy enough to clean up afterwards anyway if you create these in a tempfile.mkdtemp() temporary directory each.

Demo:

>>> import sqlite3
>>> c1 = sqlite3.connect("file::memory:?cache=shared", uri=True)
>>> c1.execute('CREATE TABLE foo (bar, baz)')
<sqlite3.Cursor object at 0x106839490>
>>> c1.execute("INSERT INTO foo VALUES ('spam', 'ham')")
<sqlite3.Cursor object at 0x106839500>
>>> c1.commit()
>>> c2 = sqlite3.connect("file::memory:?cache=shared", uri=True)
>>> list(c2.execute('SELECT * FROM foo'))
[(u'spam', u'ham')]
>>> c3 = sqlite3.connect('/tmp/sqlite3.db', uri=True)
>>> c3.execute("ATTACH DATABASE 'file::memory:?cache=shared' AS inmem")
<sqlite3.Cursor object at 0x1068395e0>
>>> list(c3.execute('SELECT * FROM inmem.foo'))
[(u'spam', u'ham')]

Support for in-memory shared-cache connections was added to SQLite version 3.7.13; for Python you can check the version of the underlying library with sqlite3.sqlite_version (string) or sqlite3.sqlite_version_info (tuple with integers):

>>> sqlite3.sqlite_version_info
(3, 8, 10, 2)
like image 122
Martijn Pieters Avatar answered Sep 18 '22 08:09

Martijn Pieters