| """ |
| This is a database cache. Its a kind of proxy object for Python sqlite3 connection |
| which operates on a memory copy of the database. |
| |
| Upon object creation the database is "backed up" to memory. All subsequent |
| operations are then pefromed on this copy which yields in performance increase. |
| """ |
| import sqlite3 |
| from lib.progressbar_utils import ProgressBar |
| |
| # ============================================================================= |
| |
| |
| class DatabaseCache(object): |
| def __init__(self, file_name, read_only=False): |
| |
| self.file_name = file_name |
| self.read_only = read_only |
| self.bar = None |
| |
| def __enter__(self): |
| """ |
| Opens the database file and makes its copy in memory |
| """ |
| |
| # File URI |
| if self.read_only: |
| uri = "file:%s?mode=ro" % self.file_name |
| else: |
| uri = "file:%s?mode=rwc" % self.file_name |
| |
| # Open connections |
| self.memory_connection = sqlite3.connect(":memory:") |
| self.file_connection = sqlite3.connect(uri, uri=True) |
| |
| # Load the database |
| print("Loading database from '{}'".format(self.file_name)) |
| self.file_connection.backup( |
| self.memory_connection, pages=100, progress=self._progress |
| ) |
| |
| self.bar.finish() |
| self.bar = None |
| |
| # Return the connection |
| return self.memory_connection |
| |
| def __exit__(self, exc_type, exc_value, traceback): |
| """ |
| Writes back the database to file if the database was open as not read-only |
| """ |
| |
| # Write back only if not read-only |
| if not self.read_only: |
| if self.memory_connection.in_transaction: |
| assert exc_type is not None, "Outstanding transaction, but no exception?" |
| self.memory_connection.rollback() |
| |
| print("Dumping database to '{}'".format(self.file_name)) |
| self.memory_connection.backup( |
| self.file_connection, pages=100, progress=self._progress |
| ) |
| |
| self.bar.finish() |
| self.bar = None |
| |
| # Close connections |
| self.memory_connection.close() |
| self.file_connection.close() |
| |
| def _progress(self, status, remaining, total): |
| """ |
| Prints database copy progress. |
| """ |
| if self.bar is None: |
| self.bar = ProgressBar(max_value=total) |
| else: |
| self.bar.update(total - remaining) |