Make DB utils more general
This commit is contained in:
@@ -3,123 +3,69 @@ import os
|
||||
import sqlite3
|
||||
import subprocess
|
||||
|
||||
from helpers.file_utils import FileUtils
|
||||
|
||||
def from_sql(path, sql):
|
||||
"""
|
||||
Creates an SQLite db and executes the passed sql statements on it.
|
||||
|
||||
:param path: the path to the created db file
|
||||
:param sql: the sql statements to execute on the newly created db
|
||||
"""
|
||||
|
||||
connection = sqlite3.connect(path)
|
||||
cursor = connection.cursor()
|
||||
cursor.executescript(sql)
|
||||
connection.commit()
|
||||
connection.close()
|
||||
|
||||
|
||||
class DBUtils(object):
|
||||
"""Provides methods for creating and comparing sqlite databases."""
|
||||
def to_sql(database):
|
||||
"""
|
||||
Returns a string containing the sql export of the database. Used for
|
||||
debugging.
|
||||
|
||||
def __init__(self):
|
||||
self.fileutils = FileUtils()
|
||||
:param database: either the path to the SQLite db file or an open
|
||||
connection to it
|
||||
:return: a string representing the sql export of the database
|
||||
"""
|
||||
|
||||
def clean_up(self):
|
||||
self.fileutils.clean_up()
|
||||
if type(database) == str:
|
||||
connection = sqlite3.connect(database)
|
||||
else:
|
||||
connection = database
|
||||
|
||||
def create_sqlite_db_with_sql(self, sql_string):
|
||||
"""
|
||||
Creates an SQLite db and executes the passed sql statements on it.
|
||||
res = '\n'.join(connection.iterdump())
|
||||
|
||||
:param sql_string: the sql statements to execute on the newly created
|
||||
db
|
||||
:return: the path to the created db file
|
||||
"""
|
||||
|
||||
db_path = self.fileutils.create_file_path(suffix=".anki2")
|
||||
connection = sqlite3.connect(db_path)
|
||||
cursor = connection.cursor()
|
||||
cursor.executescript(sql_string)
|
||||
connection.commit()
|
||||
if type(database) == str:
|
||||
connection.close()
|
||||
|
||||
return db_path
|
||||
return res
|
||||
|
||||
@staticmethod
|
||||
def sqlite_db_to_sql_string(database):
|
||||
"""
|
||||
Returns a string containing the sql export of the database. Used for
|
||||
debugging.
|
||||
|
||||
:param database: either the path to the SQLite db file or an open
|
||||
connection to it
|
||||
:return: a string representing the sql export of the database
|
||||
"""
|
||||
def diff(left_db_path, right_db_path):
|
||||
"""
|
||||
Uses the sqldiff cli tool to compare two sqlite files for equality.
|
||||
Returns True if the databases differ, False if they don't.
|
||||
|
||||
if type(database) == str:
|
||||
connection = sqlite3.connect(database)
|
||||
else:
|
||||
connection = database
|
||||
:param left_db_path: path to the left db file
|
||||
:param right_db_path: path to the right db file
|
||||
:return: True if the specified databases differ, False else
|
||||
"""
|
||||
|
||||
res = '\n'.join(connection.iterdump())
|
||||
command = ["/bin/sqldiff", left_db_path, right_db_path]
|
||||
|
||||
if type(database) == str:
|
||||
connection.close()
|
||||
child_process = subprocess.Popen(command,
|
||||
shell=False,
|
||||
stdout=subprocess.PIPE)
|
||||
stdout, stderr = child_process.communicate()
|
||||
exit_code = child_process.returncode
|
||||
|
||||
return res
|
||||
if exit_code != 0 or stderr is not None:
|
||||
raise RuntimeError("Command {} encountered an error, exit "
|
||||
"code: {}, stderr: {}"
|
||||
.format(" ".join(command),
|
||||
exit_code,
|
||||
stderr))
|
||||
|
||||
def media_dbs_differ(self, left_db_path, right_db_path, compare_timestamps=False):
|
||||
"""
|
||||
Compares two media sqlite database files for equality. mtime and dirMod
|
||||
timestamps are not considered when comparing.
|
||||
|
||||
:param left_db_path: path to the left db file
|
||||
:param right_db_path: path to the right db file
|
||||
:param compare_timestamps: flag determining if timestamp values
|
||||
(media.mtime and meta.dirMod) are included
|
||||
in the comparison
|
||||
:return: True if the specified databases differ, False else
|
||||
"""
|
||||
|
||||
if not os.path.isfile(left_db_path):
|
||||
raise IOError("file '" + left_db_path + "' does not exist")
|
||||
elif not os.path.isfile(right_db_path):
|
||||
raise IOError("file '" + right_db_path + "' does not exist")
|
||||
|
||||
# Create temporary copies of the files to act on.
|
||||
left_db_path = self.fileutils.create_file_copy(left_db_path)
|
||||
right_db_path = self.fileutils.create_file_copy(right_db_path)
|
||||
|
||||
if not compare_timestamps:
|
||||
# Set all timestamps that are not NULL to 0.
|
||||
for dbPath in [left_db_path, right_db_path]:
|
||||
connection = sqlite3.connect(dbPath)
|
||||
|
||||
connection.execute("""UPDATE media SET mtime=0
|
||||
WHERE mtime IS NOT NULL""")
|
||||
|
||||
connection.execute("""UPDATE meta SET dirMod=0
|
||||
WHERE rowid=1""")
|
||||
connection.commit()
|
||||
connection.close()
|
||||
|
||||
return self.__sqlite_dbs_differ(left_db_path, right_db_path)
|
||||
|
||||
def __sqlite_dbs_differ(self, left_db_path, right_db_path):
|
||||
"""
|
||||
Uses the sqldiff cli tool to compare two sqlite files for equality.
|
||||
Returns True if the databases differ, False if they don't.
|
||||
|
||||
:param left_db_path: path to the left db file
|
||||
:param right_db_path: path to the right db file
|
||||
:return: True if the specified databases differ, False else
|
||||
"""
|
||||
|
||||
command = ["/bin/sqldiff", left_db_path, right_db_path]
|
||||
|
||||
try:
|
||||
child_process = subprocess.Popen(command,
|
||||
shell=False,
|
||||
stdout=subprocess.PIPE)
|
||||
stdout, stderr = child_process.communicate()
|
||||
exit_code = child_process.returncode
|
||||
|
||||
if exit_code != 0 or stderr is not None:
|
||||
raise RuntimeError("Command {} encountered an error, exit "
|
||||
"code: {}, stderr: {}"
|
||||
.format(" ".join(command),
|
||||
exit_code,
|
||||
stderr))
|
||||
|
||||
# Any output from sqldiff means the databases differ.
|
||||
return stdout != ""
|
||||
except OSError as err:
|
||||
raise err
|
||||
# Any output from sqldiff means the databases differ.
|
||||
return stdout != ""
|
||||
|
||||
Reference in New Issue
Block a user