Merge pull request #210 from kulath/bug9515

Bug9515
This commit is contained in:
Paul Franklin 2016-08-11 23:39:58 +00:00 committed by GitHub
commit 4f8559f365
4 changed files with 160 additions and 86 deletions

View File

@ -167,6 +167,10 @@ class DBAPI(DbGeneric):
code = compile(file.read(), settings_file, 'exec') code = compile(file.read(), settings_file, 'exec')
exec(code, globals(), settings) exec(code, globals(), settings)
self.dbapi = settings["dbapi"] self.dbapi = settings["dbapi"]
# We use the existence of the person table as a proxy for the database
# being new
if not self.dbapi.table_exists("person"):
self.update_schema() self.update_schema()
def update_schema(self): def update_schema(self):
@ -174,7 +178,7 @@ class DBAPI(DbGeneric):
Create and update schema. Create and update schema.
""" """
# make sure schema is up to date: # make sure schema is up to date:
self.dbapi.try_execute("""CREATE TABLE person ( self.dbapi.execute("""CREATE TABLE person (
handle VARCHAR(50) PRIMARY KEY NOT NULL, handle VARCHAR(50) PRIMARY KEY NOT NULL,
given_name TEXT , given_name TEXT ,
surname TEXT , surname TEXT ,
@ -183,171 +187,171 @@ class DBAPI(DbGeneric):
gramps_id TEXT , gramps_id TEXT ,
blob_data BLOB blob_data BLOB
);""") );""")
self.dbapi.try_execute("""CREATE TABLE family ( self.dbapi.execute("""CREATE TABLE family (
handle VARCHAR(50) PRIMARY KEY NOT NULL, handle VARCHAR(50) PRIMARY KEY NOT NULL,
father_handle VARCHAR(50), father_handle VARCHAR(50),
mother_handle VARCHAR(50), mother_handle VARCHAR(50),
gramps_id TEXT , gramps_id TEXT ,
blob_data BLOB blob_data BLOB
);""") );""")
self.dbapi.try_execute("""CREATE TABLE source ( self.dbapi.execute("""CREATE TABLE source (
handle VARCHAR(50) PRIMARY KEY NOT NULL, handle VARCHAR(50) PRIMARY KEY NOT NULL,
order_by TEXT , order_by TEXT ,
gramps_id TEXT , gramps_id TEXT ,
blob_data BLOB blob_data BLOB
);""") );""")
self.dbapi.try_execute("""CREATE TABLE citation ( self.dbapi.execute("""CREATE TABLE citation (
handle VARCHAR(50) PRIMARY KEY NOT NULL, handle VARCHAR(50) PRIMARY KEY NOT NULL,
order_by TEXT , order_by TEXT ,
gramps_id TEXT , gramps_id TEXT ,
blob_data BLOB blob_data BLOB
);""") );""")
self.dbapi.try_execute("""CREATE TABLE event ( self.dbapi.execute("""CREATE TABLE event (
handle VARCHAR(50) PRIMARY KEY NOT NULL, handle VARCHAR(50) PRIMARY KEY NOT NULL,
gramps_id TEXT , gramps_id TEXT ,
blob_data BLOB blob_data BLOB
);""") );""")
self.dbapi.try_execute("""CREATE TABLE media ( self.dbapi.execute("""CREATE TABLE media (
handle VARCHAR(50) PRIMARY KEY NOT NULL, handle VARCHAR(50) PRIMARY KEY NOT NULL,
order_by TEXT , order_by TEXT ,
gramps_id TEXT , gramps_id TEXT ,
blob_data BLOB blob_data BLOB
);""") );""")
self.dbapi.try_execute("""CREATE TABLE place ( self.dbapi.execute("""CREATE TABLE place (
handle VARCHAR(50) PRIMARY KEY NOT NULL, handle VARCHAR(50) PRIMARY KEY NOT NULL,
order_by TEXT , order_by TEXT ,
gramps_id TEXT , gramps_id TEXT ,
blob_data BLOB blob_data BLOB
);""") );""")
self.dbapi.try_execute("""CREATE TABLE repository ( self.dbapi.execute("""CREATE TABLE repository (
handle VARCHAR(50) PRIMARY KEY NOT NULL, handle VARCHAR(50) PRIMARY KEY NOT NULL,
gramps_id TEXT , gramps_id TEXT ,
blob_data BLOB blob_data BLOB
);""") );""")
self.dbapi.try_execute("""CREATE TABLE note ( self.dbapi.execute("""CREATE TABLE note (
handle VARCHAR(50) PRIMARY KEY NOT NULL, handle VARCHAR(50) PRIMARY KEY NOT NULL,
gramps_id TEXT , gramps_id TEXT ,
blob_data BLOB blob_data BLOB
);""") );""")
self.dbapi.try_execute("""CREATE TABLE tag ( self.dbapi.execute("""CREATE TABLE tag (
handle VARCHAR(50) PRIMARY KEY NOT NULL, handle VARCHAR(50) PRIMARY KEY NOT NULL,
order_by TEXT , order_by TEXT ,
blob_data BLOB blob_data BLOB
);""") );""")
# Secondary: # Secondary:
self.dbapi.try_execute("""CREATE TABLE reference ( self.dbapi.execute("""CREATE TABLE reference (
obj_handle VARCHAR(50), obj_handle VARCHAR(50),
obj_class TEXT, obj_class TEXT,
ref_handle VARCHAR(50), ref_handle VARCHAR(50),
ref_class TEXT ref_class TEXT
);""") );""")
self.dbapi.try_execute("""CREATE TABLE name_group ( self.dbapi.execute("""CREATE TABLE name_group (
name VARCHAR(50) PRIMARY KEY NOT NULL, name VARCHAR(50) PRIMARY KEY NOT NULL,
grouping TEXT grouping TEXT
);""") );""")
self.dbapi.try_execute("""CREATE TABLE metadata ( self.dbapi.execute("""CREATE TABLE metadata (
setting VARCHAR(50) PRIMARY KEY NOT NULL, setting VARCHAR(50) PRIMARY KEY NOT NULL,
value BLOB value BLOB
);""") );""")
self.dbapi.try_execute("""CREATE TABLE gender_stats ( self.dbapi.execute("""CREATE TABLE gender_stats (
given_name TEXT, given_name TEXT,
female INTEGER, female INTEGER,
male INTEGER, male INTEGER,
unknown INTEGER unknown INTEGER
);""") );""")
## Indices: ## Indices:
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
person_order_by ON person(order_by); person_order_by ON person(order_by);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
person_gramps_id ON person(gramps_id); person_gramps_id ON person(gramps_id);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
person_surname ON person(surname); person_surname ON person(surname);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
person_given_name ON person(given_name); person_given_name ON person(given_name);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
source_order_by ON source(order_by); source_order_by ON source(order_by);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
source_gramps_id ON source(gramps_id); source_gramps_id ON source(gramps_id);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
citation_order_by ON citation(order_by); citation_order_by ON citation(order_by);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
citation_gramps_id ON citation(gramps_id); citation_gramps_id ON citation(gramps_id);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
media_order_by ON media(order_by); media_order_by ON media(order_by);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
media_gramps_id ON media(gramps_id); media_gramps_id ON media(gramps_id);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
place_order_by ON place(order_by); place_order_by ON place(order_by);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
place_gramps_id ON place(gramps_id); place_gramps_id ON place(gramps_id);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
tag_order_by ON tag(order_by); tag_order_by ON tag(order_by);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
reference_ref_handle ON reference(ref_handle); reference_ref_handle ON reference(ref_handle);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
name_group_name ON name_group(name); name_group_name ON name_group(name);
""") """)
# Fixes: # Fixes:
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
place_handle ON place(handle); place_handle ON place(handle);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
citation_handle ON citation(handle); citation_handle ON citation(handle);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
media_handle ON media(handle); media_handle ON media(handle);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
person_handle ON person(handle); person_handle ON person(handle);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
family_handle ON family(handle); family_handle ON family(handle);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
event_handle ON event(handle); event_handle ON event(handle);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
repository_handle ON repository(handle); repository_handle ON repository(handle);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
tag_handle ON tag(handle); tag_handle ON tag(handle);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
note_handle ON note(handle); note_handle ON note(handle);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
source_handle ON source(handle); source_handle ON source(handle);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
family_gramps_id ON family(gramps_id); family_gramps_id ON family(gramps_id);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
event_gramps_id ON event(gramps_id); event_gramps_id ON event(gramps_id);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
repository_gramps_id ON repository(gramps_id); repository_gramps_id ON repository(gramps_id);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
note_gramps_id ON note(gramps_id); note_gramps_id ON note(gramps_id);
""") """)
self.dbapi.try_execute("""CREATE INDEX self.dbapi.execute("""CREATE INDEX
reference_obj_handle ON reference(obj_handle); reference_obj_handle ON reference(obj_handle);
""") """)
self.rebuild_secondary_fields() self.rebuild_secondary_fields()
@ -1898,23 +1902,25 @@ class DBAPI(DbGeneric):
def drop_tables(self): def drop_tables(self):
""" """
Useful in testing, reseting. Useful in testing, reseting. If the test is unsure whether the tables
already exist, then the caller will need to catch the appropriate
exception
""" """
self.dbapi.try_execute("""DROP TABLE person;""") self.dbapi.execute("""DROP TABLE person;""")
self.dbapi.try_execute("""DROP TABLE family;""") self.dbapi.execute("""DROP TABLE family;""")
self.dbapi.try_execute("""DROP TABLE source;""") self.dbapi.execute("""DROP TABLE source;""")
self.dbapi.try_execute("""DROP TABLE citation""") self.dbapi.execute("""DROP TABLE citation""")
self.dbapi.try_execute("""DROP TABLE event;""") self.dbapi.execute("""DROP TABLE event;""")
self.dbapi.try_execute("""DROP TABLE media;""") self.dbapi.execute("""DROP TABLE media;""")
self.dbapi.try_execute("""DROP TABLE place;""") self.dbapi.execute("""DROP TABLE place;""")
self.dbapi.try_execute("""DROP TABLE repository;""") self.dbapi.execute("""DROP TABLE repository;""")
self.dbapi.try_execute("""DROP TABLE note;""") self.dbapi.execute("""DROP TABLE note;""")
self.dbapi.try_execute("""DROP TABLE tag;""") self.dbapi.execute("""DROP TABLE tag;""")
# Secondary: # Secondary:
self.dbapi.try_execute("""DROP TABLE reference;""") self.dbapi.execute("""DROP TABLE reference;""")
self.dbapi.try_execute("""DROP TABLE name_group;""") self.dbapi.execute("""DROP TABLE name_group;""")
self.dbapi.try_execute("""DROP TABLE metadata;""") self.dbapi.execute("""DROP TABLE metadata;""")
self.dbapi.try_execute("""DROP TABLE gender_stats;""") self.dbapi.execute("""DROP TABLE gender_stats;""")
def _sql_type(self, python_type): def _sql_type(self, python_type):
""" """
@ -2002,7 +2008,7 @@ class DBAPI(DbGeneric):
for field in self.get_table_func( for field in self.get_table_func(
table, "class_func").get_index_fields(): table, "class_func").get_index_fields():
field = self._hash_name(table, field) field = self._hash_name(table, field)
self.dbapi.try_execute("CREATE INDEX %s_%s ON %s(%s);" self.dbapi.execute("CREATE INDEX %s_%s ON %s(%s);"
% (table, field, table_name, field)) % (table, field, table_name, field))
def update_secondary_values_all(self): def update_secondary_values_all(self):

View File

@ -87,13 +87,10 @@ class MySQL:
def rollback(self): def rollback(self):
self.connection.rollback() self.connection.rollback()
def try_execute(self, sql): def table_exists(self, table):
query = self._hack_query(sql) self.cursor.execute("SELECT COUNT(*) FROM information_schema.tables "
try: "WHERE table_name='%s';" % table)
self.cursor.execute(sql) return self.fetchone()[0] != 0
except Exception as exc:
pass
#print(str(exc))
def close(self): def close(self):
self.connection.close() self.connection.close()

View File

@ -94,15 +94,10 @@ class Postgresql:
def rollback(self): def rollback(self):
self.connection.rollback() self.connection.rollback()
def try_execute(self, sql): def table_exists(self, table):
sql = self._hack_query(sql) self.cursor.execute("SELECT COUNT(*) FROM information_schema.tables "
sql = sql.replace("BLOB", "bytea") "WHERE table_name=?;", [table])
try: return self.fetchone()[0] != 0
self.cursor.execute(sql)
except Exception as exc:
self.cursor.execute("rollback")
#print("ERROR:", sql)
#print(str(exc))
def close(self): def close(self):
self.connection.close() self.connection.close()

View File

@ -18,19 +18,35 @@
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
# #
import os """
Backend for sqlite database.
"""
#-------------------------------------------------------------------------
#
# standard python modules
#
#-------------------------------------------------------------------------
import sqlite3 import sqlite3
import logging import logging
import re import re
sqlite3.paramstyle = 'qmark' sqlite3.paramstyle = 'qmark'
#-------------------------------------------------------------------------
#
# Sqlite class
#
#-------------------------------------------------------------------------
class Sqlite: class Sqlite:
"""
The Sqlite class is an interface between the DBAPI class which is the Gramps
backend for the DBAPI interface and the sqlite3 python module.
"""
@classmethod @classmethod
def get_summary(cls): def get_summary(cls):
""" """
Return a diction of information about this database Return a dictionary of information about this database backend.
backend.
""" """
summary = { summary = {
"DB-API version": "2.0", "DB-API version": "2.0",
@ -43,6 +59,18 @@ class Sqlite:
return summary return summary
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"""
Create a new Sqlite instance.
This connects to a sqlite3 database and creates a cursor instance.
:param args: arguments to be passed to the sqlite3 connect class at
creation.
:type args: list
:param kwargs: arguments to be passed to the sqlite3 connect class at
creation.
:type kwargs: list
"""
self.log = logging.getLogger(".sqlite") self.log = logging.getLogger(".sqlite")
self.connection = sqlite3.connect(*args, **kwargs) self.connection = sqlite3.connect(*args, **kwargs)
self.cursor = self.connection.cursor() self.cursor = self.connection.cursor()
@ -50,36 +78,84 @@ class Sqlite:
self.connection.create_function("regexp", 2, regexp) self.connection.create_function("regexp", 2, regexp)
def execute(self, *args, **kwargs): def execute(self, *args, **kwargs):
"""
Executes an SQL statement.
:param args: arguments to be passed to the sqlite3 execute statement
:type args: list
:param kwargs: arguments to be passed to the sqlite3 execute statement
:type kwargs: list
"""
self.log.debug(args) self.log.debug(args)
self.cursor.execute(*args, **kwargs) self.cursor.execute(*args, **kwargs)
def fetchone(self): def fetchone(self):
"""
Fetches the next row of a query result set, returning a single sequence,
or None when no more data is available.
"""
return self.cursor.fetchone() return self.cursor.fetchone()
def fetchall(self): def fetchall(self):
"""
Fetches the next set of rows of a query result, returning a list. An
empty list is returned when no more rows are available.
"""
return self.cursor.fetchall() return self.cursor.fetchall()
def begin(self): def begin(self):
"""
Start a transaction manually. This transactions usually persist until
the next COMMIT or ROLLBACK command.
"""
self.log.debug("BEGIN TRANSACTION;")
self.execute("BEGIN TRANSACTION;") self.execute("BEGIN TRANSACTION;")
def commit(self): def commit(self):
"""
Commit the current transaction.
"""
self.log.debug("COMMIT;") self.log.debug("COMMIT;")
self.connection.commit() self.connection.commit()
def rollback(self): def rollback(self):
"""
Roll back any changes to the database since the last call to commit().
"""
self.log.debug("ROLLBACK;") self.log.debug("ROLLBACK;")
self.connection.rollback() self.connection.rollback()
def try_execute(self, sql): def table_exists(self, table):
try: """
self.cursor.execute(sql) Test whether the specified SQL database table exists.
except Exception as exc:
#print(str(exc)) :param table: table name to check.
pass :type table: str
:returns: True if the table exists, false otherwise.
:rtype: bool
"""
self.execute("SELECT COUNT(*) FROM sqlite_master "
"WHERE type='table' AND name='%s';" % table)
return self.fetchone()[0] != 0
def close(self): def close(self):
"""
Close the current database.
"""
self.log.debug("closing database...") self.log.debug("closing database...")
self.connection.close() self.connection.close()
def regexp(expr, value): def regexp(expr, value):
"""
A user defined function that can be called from within an SQL statement.
This function has two parameters.
:param expr: pattern to look for.
:type expr: str
:param value: the string to search.
:type value: list
:returns: True if the expr exists within the value, false otherwise.
:rtype: bool
"""
return re.search(expr, value, re.MULTILINE) is not None return re.search(expr, value, re.MULTILINE) is not None