0

I want to import a database from disk to memory at application start and on exit export back from memory to disk. I converted this code from C++ to Python. It successfully exports the database from memory to disk:

import sys
from PySide6.QtSql import QSqlDatabase, QSqlQuery

# Create the connection
con_mem = QSqlDatabase.addDatabase("QSQLITE", 'con_mem')
con_mem.setDatabaseName("file::memory:")
con_mem.setConnectOptions('QSQLITE_OPEN_URI;QSQLITE_ENABLE_SHARED_CACHE')

con_disk = QSqlDatabase.addDatabase("QSQLITE", 'con_disk')
con_disk.setDatabaseName("db.sqlite")

# Open the connection
if not con_disk.open() and con_mem.open():
    print("Databases open error")
    sys.exit(1)

db_mem = QSqlDatabase.database('con_mem')
db_disk = QSqlDatabase.database('con_disk')

def clone_db(scr, des, des_string):
    print('Before creating table')
    print('scr: ', scr.tables())
    print('des: ', des.tables())

# Create a test table at scr
    createTableQuery = QSqlQuery(scr)
    createTableQuery.exec(
        """
        CREATE TABLE contacts (
            id INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE NOT NULL,
            name VARCHAR(40) NOT NULL,
            job VARCHAR(50),
            email VARCHAR(40) NOT NULL
        )
        """
    )

    print('After creating table')
    print('scr: ', scr.tables())
    print('des: ', des.tables())

    VacumQuery = QSqlQuery(scr)
    VacumQuery.exec(f'VACUUM main INTO "{des_string}"')

    print('After vacum')
    print('scr: ', scr.tables())
    print('des: ', des.tables())

clone_db(db_mem, db_disk, 'db.sqlite')

Output:

Before creating table
scr:  []
des:  []
After creating table
scr:  ['contacts', 'sqlite_sequence']
des:  []
After vacum
scr:  ['contacts', 'sqlite_sequence']
des:  ['contacts', 'sqlite_sequence']

But when I swap scr and des this function doesn't work:

clone_db(db_disk, db_mem, 'file::memory:')

Output:

Before creating table
scr:  []
des:  []
After creating table
scr:  ['contacts', 'sqlite_sequence']
des:  []
After vacum
scr:  ['contacts', 'sqlite_sequence']
des:  []

It does not clone the disk database to an in-memory one. What is the problem?

I changed VacumQuery:

VacumQuery = QSqlQuery(scr)
    if not VacumQuery.exec(f'VACUUM main INTO "{des_string}"'):
        print(VacumQuery.lastError().text())

Result:

unable to open database: file::memory: Unable to fetch row

Result of different combinations of in-memory database setup and vacuum statement:

# Test 1: No error. Database not imported. A 'file' is created in current directory.

con_mem.setDatabaseName('file:db?mode=memory&cache=shared')
con_mem.setConnectOptions('QSQLITE_OPEN_URI;QSQLITE_ENABLE_SHARED_CACHE')

......
clone_db(db_disk, db_mem, 'file:db?mode=memory&cache=shared')
# Test 2: No error. Database not imported.

con_mem.setDatabaseName(':memory:')
con_mem.setConnectOptions('QSQLITE_OPEN_URI;QSQLITE_ENABLE_SHARED_CACHE')

......
clone_db(db_disk, db_mem, ':memory:')
# Test 3: No error. Database not imported.

con_mem.setDatabaseName(':memory:')
con_mem.setConnectOptions('QSQLITE_ENABLE_SHARED_CACHE')

......
clone_db(db_disk, db_mem, ':memory:')
4
  • exec() returns False if the query wasn't successful, so you should add a check to see the possible error: if not VacumQuery.exec(): print(VacumQuery.lastError().text()). Commented Jul 28, 2023 at 19:02
  • Got unable to open database: file::memory: Unable to fetch row. Could not find a valid string to access the in-memory database with vacuum Commented Jul 29, 2023 at 18:09
  • 2
    What if you just use :memory:? Commented Jul 29, 2023 at 18:21
  • :memory: shows no error but database is not imported. Commented Jul 30, 2023 at 4:35

1 Answer 1

0

This is a work around solution I am using.

import os
from PySide6.QtSql import QSqlDatabase, QSqlQuery

# Create a new database connection with the database name set to "file::memory:"
con_mem = QSqlDatabase.addDatabase("QSQLITE", "con_mem")
con_mem.setDatabaseName("file::memory:")
con_mem.setConnectOptions("QSQLITE_OPEN_URI;QSQLITE_ENABLE_SHARED_CACHE")

# Open the database connection
if not con_mem.open():
    print("Failed to open database connection")
    exit(1)

# Execute a SQL statement to create the tables in the in-memory database
query = QSqlQuery(con_mem)
query.exec(f"ATTACH DATABASE '{os.path.abspath('database.db')}' AS disk_db;")
query.exec("SELECT sql FROM disk_db.sqlite_master WHERE type='table';")
create_table_sql = []
while query.next():
    create_table_sql.append(query.value(0))
for sql in create_table_sql:
    query.exec(sql)

# Disable constraints to import data
query.exec("PRAGMA foreign_keys = OFF;")
query.exec("PRAGMA ignore_check_constraints = ON;")

# Execute a SQL statement to copy the data from the disk database to the in-memory database
query.exec("SELECT name FROM disk_db.sqlite_master WHERE type='table';")
table_names = []
while query.next():
    table_names.append(query.value(0))
for table_name in table_names:
    query.exec(f"INSERT INTO {table_name} SELECT * FROM disk_db.{table_name}")
query.exec("DETACH DATABASE disk_db;")

# Enable constraints
query.exec("PRAGMA foreign_keys = ON;")
query.exec("PRAGMA ignore_check_constraints = OFF;")
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.