def __init__(self, connector=None): database.require(DBNAME, version='1') self.normalize_basedir() connector = BoundConnector(DBNAME, connector) self.DBFILENAME = connector.dblocation self.conn = connector.connection() self.db = self.conn.cursor() #I don't care about journaling! self.conn.execute('PRAGMA synchronous = OFF') self.conn.execute('PRAGMA journal_mode = MEMORY') self.load_db_to_memory()
def __init__(self, connector=None): database.require(DBNAME, version='1') self.normalize_basedir() connector = BoundConnector(DBNAME, connector) self.DBFILENAME = connector.dblocation self.conn = connector.connection() self.db = self.conn.cursor() #I don't care about journaling! self.conn.execute('PRAGMA synchronous = OFF') self.conn.execute('PRAGMA journal_mode = MEMORY') self.load_db_to_memory()
class Updater(object): '''Handle the versioning needs of a single database. name : str The name of the database to manage. dbdef : dict The corresponding definition. connector : :class:`cherrymusicserver.database.connect.AbstractConnector` To connect to the database. ''' _metatable = { 'create.sql': """CREATE TABLE IF NOT EXISTS _meta_version( version TEXT, _created INTEGER NOT NULL DEFAULT (datetime('now')) );""", 'drop.sql': """DROP TABLE IF EXISTS _meta_version;""" } _classlock = threading.RLock() _dblockers = {} def __init__(self, name, dbdef): assert name and dbdef self.name = name self.desc = dbdef self.db = BoundConnector(self.name) with self: self._init_meta() def __del__(self): self._unlock() def __repr__(self): return 'updater({0!r}, {1} -> {2})'.format( self.name, self._version, self._target, ) def __enter__(self): self._lock() return self def __exit__(self, exctype, exception, traceback): self._unlock() @property def _islocked(self): name, lockers = self.name, self._dblockers with self._classlock: return name in lockers and lockers[name] is self def _lock(self): name, lockers = self.name, self._dblockers with self._classlock: assert lockers.get( name, self) is self, (name + ': is locked by another updater') lockers[name] = self def _unlock(self): with self._classlock: if self._islocked: del self._dblockers[self.name] @property def needed(self): """ ``True`` if the database is unversioned or if its version is less then the maximum defined. """ self._validate_locked() version, target = self._version, self._target log.d('%s update check: version=[%s] target=[%s]', self.name, version, target) return version is None or version < target @property def requires_consent(self): """`True` if any missing updates require user consent.""" self._validate_locked() for version in self._updates_due: if 'prompt' in self.desc[version]: return True return False @property def prompts(self): """ Return an iterable of string prompts for updates that require user consent. """ self._validate_locked() for version in self._updates_due: if 'prompt' in self.desc[version]: yield self.desc[version]['prompt'] def run(self): """Update database schema to the highest possible version.""" self._validate_locked() log.i('%r: updating database schema', self.name) log.d('from version %r to %r', self._version, self._target) if None is self._version: self._init_with_version(self._target) else: for version in self._updates_due: self._update_to_version(version) def reset(self): """Delete all content from the database along with supporting structures.""" self._validate_locked() version = self._version log.i('%s: resetting database', self.name) log.d('version: %s', version) if None is version: log.d('nothing to reset.') return with self.db.connection() as cxn: cxn.executescript(self.desc[version]['drop.sql']) cxn.executescript(self._metatable['drop.sql']) cxn.executescript(self._metatable['create.sql']) self._setversion(None, cxn) cxn.close() def _validate_locked(self): assert self._islocked, 'must be called in updater context (use "with")' @property def _version(self): try: return self.__version except AttributeError: maxv = self.db.execute( 'SELECT MAX(version) FROM _meta_version').fetchone() maxv = maxv and maxv[0] self.__version = maxv if maxv is None else str(maxv) return self.__version def _setversion(self, value, conn=None): del self.__version conn = conn or self.db.connection log.d('{0}: set version to {1}'.format(self.name, value)) conn.execute('INSERT INTO _meta_version(version) VALUES (?)', (value, )) @property def _target(self): return max(self.desc) @property def _updates_due(self): if None is self._version: return () versions = sorted(self.desc) start = versions.index(self._version) + 1 return versions[start:] def _init_meta(self): content = self.db.execute( 'SELECT type, name FROM sqlite_master;').fetchall() content = [(t, n) for t, n in content if n != '_meta_version' and not n.startswith('sqlite')] with self.db.connection() as cxn: cxn.isolation_level = "EXCLUSIVE" cxn.executescript(self._metatable['create.sql']) if content and self._version is None: log.d('%s: unversioned content found: %r', self.name, content) self._setversion(0, cxn) cxn.isolation_level = '' cxn.close() def _init_with_version(self, vnum): log.d('initializing database %r to version %s', self.name, vnum) cxn = self.db.connection() cxn.isolation_level = None # autocommit self._runscript(vnum, 'create.sql', cxn) self._run_afterscript_if_exists(vnum, cxn) self._setversion(vnum, cxn) cxn.isolation_level = '' cxn.close() def _update_to_version(self, vnum): log.d('updating database %r to version %d', self.name, vnum) cxn = self.db.connection() cxn.isolation_level = None # autocommit self._runscript(vnum, 'update.sql', cxn) self._run_afterscript_if_exists(vnum, cxn) self._setversion(vnum, cxn) cxn.isolation_level = '' cxn.close() def _run_afterscript_if_exists(self, vnum, conn): try: self._runscript(vnum, 'after.sql', conn) except KeyError: pass def _runscript(self, version, name, cxn): try: cxn.executescript(self.desc[version][name]) except sqlite3.OperationalError: # update scripts are tested, so the problem's seems to be sqlite # itself log.x(_('Exception while updating database schema.')) log.e( _('Database error. This is probably due to your version of' ' sqlite being too old. Try updating sqlite3 and' ' updating python. If the problem persists, you will need' ' to delete the database at ' + self.db.dblocation)) import sys sys.exit(1)
class Updater(object): '''Handle the versioning needs of a single database. name : str The name of the database to manage. dbdef : dict The corresponding definition. connector : :class:`cherrymusicserver.database.connect.AbstractConnector` To connect to the database. ''' _metatable = { 'create.sql': """CREATE TABLE IF NOT EXISTS _meta_version( version TEXT, _created INTEGER NOT NULL DEFAULT (datetime('now')) );""", 'drop.sql': """DROP TABLE IF EXISTS _meta_version;""" } _classlock = threading.RLock() _dblockers = {} def __init__(self, name, dbdef): assert name and dbdef self.name = name self.desc = dbdef self.db = BoundConnector(self.name) with self: self._init_meta() def __del__(self): self._unlock() def __repr__(self): return 'updater({0!r}, {1} -> {2})'.format( self.name, self._version, self._target, ) def __enter__(self): self._lock() return self def __exit__(self, exctype, exception, traceback): self._unlock() @property def _islocked(self): name, lockers = self.name, self._dblockers with self._classlock: return name in lockers and lockers[name] is self def _lock(self): name, lockers = self.name, self._dblockers with self._classlock: assert lockers.get(name, self) is self, ( name + ': is locked by another updater') lockers[name] = self def _unlock(self): with self._classlock: if self._islocked: del self._dblockers[self.name] @property def needed(self): """ ``True`` if the database is unversioned or if its version is less then the maximum defined. """ self._validate_locked() version, target = self._version, self._target log.d('%s update check: version=[%s] target=[%s]', self.name, version, target) return version is None or version < target @property def requires_consent(self): """`True` if any missing updates require user consent.""" self._validate_locked() for version in self._updates_due: if 'prompt' in self.desc[version]: return True return False @property def prompts(self): """ Return an iterable of string prompts for updates that require user consent. """ self._validate_locked() for version in self._updates_due: if 'prompt' in self.desc[version]: yield self.desc[version]['prompt'] def run(self): """Update database schema to the highest possible version.""" self._validate_locked() log.i('%r: updating database schema', self.name) log.d('from version %r to %r', self._version, self._target) if None is self._version: self._init_with_version(self._target) else: for version in self._updates_due: self._update_to_version(version) def reset(self): """Delete all content from the database along with supporting structures.""" self._validate_locked() version = self._version log.i('%s: resetting database', self.name) log.d('version: %s', version) if None is version: log.d('nothing to reset.') return with self.db.connection() as cxn: cxn.executescript(self.desc[version]['drop.sql']) cxn.executescript(self._metatable['drop.sql']) cxn.executescript(self._metatable['create.sql']) self._setversion(None, cxn) cxn.close() def _validate_locked(self): assert self._islocked, 'must be called in updater context (use "with")' @property def _version(self): try: return self.__version except AttributeError: maxv = self.db.execute('SELECT MAX(version) FROM _meta_version').fetchone() maxv = maxv and maxv[0] self.__version = maxv if maxv is None else str(maxv) return self.__version def _setversion(self, value, conn=None): del self.__version conn = conn or self.db.connection log.d('{0}: set version to {1}'.format(self.name, value)) conn.execute('INSERT INTO _meta_version(version) VALUES (?)', (value,)) @property def _target(self): return max(self.desc) @property def _updates_due(self): if None is self._version: return () versions = sorted(self.desc) start = versions.index(self._version) + 1 return versions[start:] def _init_meta(self): content = self.db.execute('SELECT type, name FROM sqlite_master;').fetchall() content = [(t, n) for t, n in content if n != '_meta_version' and not n.startswith('sqlite')] with self.db.connection() as cxn: cxn.isolation_level = "EXCLUSIVE" cxn.executescript(self._metatable['create.sql']) if content and self._version is None: log.d('%s: unversioned content found: %r', self.name, content) self._setversion(0, cxn) cxn.isolation_level = '' cxn.close() def _init_with_version(self, vnum): log.d('initializing database %r to version %s', self.name, vnum) cxn = self.db.connection() cxn.isolation_level = None # autocommit self._runscript(vnum, 'create.sql', cxn) self._run_afterscript_if_exists(vnum, cxn) self._setversion(vnum, cxn) cxn.isolation_level = '' cxn.close() def _update_to_version(self, vnum): log.d('updating database %r to version %d', self.name, vnum) cxn = self.db.connection() cxn.isolation_level = None # autocommit self._runscript(vnum, 'update.sql', cxn) self._run_afterscript_if_exists(vnum, cxn) self._setversion(vnum, cxn) cxn.isolation_level = '' cxn.close() def _run_afterscript_if_exists(self, vnum, conn): try: self._runscript(vnum, 'after.sql', conn) except KeyError: pass def _runscript(self, version, name, cxn): try: cxn.executescript(self.desc[version][name]) except sqlite3.OperationalError: # update scripts are tested, so the problem's seems to be sqlite # itself log.x(_('Exception while updating database schema.')) log.e(_('Database error. This is probably due to your version of' ' sqlite being too old. Try updating sqlite3 and' ' updating python. If the problem persists, you will need' ' to delete the database at ' + self.db.dblocation)) import sys sys.exit(1)
class Updater(object): """Handle the versioning needs of a single database. name : str The name of the database to manage. dbdef : dict The corresponding definition. connector : :class:`cherrymusicserver.database.connect.AbstractConnector` To connect to the database. """ _metatable = { "create.sql": """CREATE TABLE IF NOT EXISTS _meta_version( version TEXT, _created INTEGER NOT NULL DEFAULT (datetime('now')) );""", "drop.sql": """DROP TABLE IF EXISTS _meta_version;""", } _classlock = threading.RLock() _dblockers = {} def __init__(self, name, dbdef): assert name and dbdef self.name = name self.desc = dbdef self.db = BoundConnector(self.name) with self: self._init_meta() def __del__(self): self._unlock() def __repr__(self): return "updater({0!r}, {1} -> {2})".format(self.name, self._version, self._target) def __enter__(self): self._lock() return self def __exit__(self, exctype, exception, traceback): self._unlock() @property def _islocked(self): name, lockers = self.name, self._dblockers with self._classlock: return name in lockers and lockers[name] is self def _lock(self): name, lockers = self.name, self._dblockers with self._classlock: assert lockers.get(name, self) is self, name + ": is locked by another updater" lockers[name] = self def _unlock(self): with self._classlock: if self._islocked: del self._dblockers[self.name] @property def needed(self): """ ``True`` if the database is unversioned or if its version is less then the maximum defined. """ self._validate_locked() version, target = self._version, self._target log.d("%s update check: version=[%s] target=[%s]", self.name, version, target) return version is None or version < target @property def requires_consent(self): """`True` if any missing updates require user consent.""" self._validate_locked() for version in self._updates_due: if "prompt" in self.desc[version]: return True return False @property def prompts(self): """ Return an iterable of string prompts for updates that require user consent. """ self._validate_locked() for version in self._updates_due: if "prompt" in self.desc[version]: yield self.desc[version]["prompt"] def run(self): """Update database schema to the highest possible version.""" self._validate_locked() log.i("%r: updating database schema", self.name) log.d("from version %r to %r", self._version, self._target) if None is self._version: self._init_with_version(self._target) else: for version in self._updates_due: self._update_to_version(version) def reset(self): """Delete all content from the database along with supporting structures.""" self._validate_locked() version = self._version log.i("%s: resetting database", self.name) log.d("version: %s", version) if None is version: log.d("nothing to reset.") return with self.db.connection() as cxn: cxn.executescript(self.desc[version]["drop.sql"]) cxn.executescript(self._metatable["drop.sql"]) cxn.executescript(self._metatable["create.sql"]) self._setversion(None, cxn) cxn.close() def _validate_locked(self): assert self._islocked, 'must be called in updater context (use "with")' @property def _version(self): try: return self.__version except AttributeError: maxv = self.db.execute("SELECT MAX(version) FROM _meta_version").fetchone() maxv = maxv and maxv[0] self.__version = maxv if maxv is None else str(maxv) return self.__version def _setversion(self, value, conn=None): del self.__version conn = conn or self.db.connection log.d("{0}: set version to {1}".format(self.name, value)) conn.execute("INSERT INTO _meta_version(version) VALUES (?)", (value,)) @property def _target(self): return max(self.desc) @property def _updates_due(self): if None is self._version: return () versions = sorted(self.desc) start = versions.index(self._version) + 1 return versions[start:] def _init_meta(self): content = self.db.execute("SELECT type, name FROM sqlite_master;").fetchall() content = [(t, n) for t, n in content if n != "_meta_version" and not n.startswith("sqlite")] with self.db.connection() as cxn: cxn.isolation_level = "EXCLUSIVE" cxn.executescript(self._metatable["create.sql"]) if content and self._version is None: log.d("%s: unversioned content found: %r", self.name, content) self._setversion(0, cxn) cxn.isolation_level = "" cxn.close() def _init_with_version(self, vnum): log.d("initializing database %r to version %s", self.name, vnum) cxn = self.db.connection() cxn.isolation_level = None # autocommit cxn.executescript(self.desc[vnum]["create.sql"]) self._run_afterscript_if_exists(vnum, cxn) self._setversion(vnum, cxn) cxn.isolation_level = "" cxn.close() def _update_to_version(self, vnum): log.d("updating database %r to version %d", self.name, vnum) cxn = self.db.connection() cxn.isolation_level = None # autocommit cxn.executescript(self.desc[vnum]["update.sql"]) self._run_afterscript_if_exists(vnum, cxn) self._setversion(vnum, cxn) cxn.isolation_level = "" cxn.close() def _run_afterscript_if_exists(self, vnum, conn): try: conn.executescript(self.desc[vnum]["after.sql"]) except KeyError: pass