storage-ng / storage / stores / sqla.py

# Copyright: 2011 MoinMoin:ThomasWaldmann
# License: GNU GPL v2 (or any later version), see LICENSE.txt for details.

"""
MoinMoin - sqlalchemy store

Stores k/v pairs into any database supported by sqlalchemy.
"""


from __future__ import absolute_import, division

from StringIO import StringIO

from sqlalchemy import create_engine, select, MetaData, Table, Column, String, Binary
from sqlalchemy.pool import StaticPool

from . import MutableStoreBase, BytesMutableStoreBase, FileMutableStoreBase

KEY_LEN = 128
VALUE_LEN = 1024 * 1024 # 1MB binary data


class _Store(MutableStoreBase):
    """
    A simple dict-based in-memory store. No persistence!
    """
    @classmethod
    def from_uri(cls, uri):
        return cls(uri)

    def __init__(self, db_uri=None, table_name='store', verbose=False):
        """
        :param db_uri: The database uri that we pass on to SQLAlchemy.
                       May contain user/password/host/port/etc.
        :param verbose: Verbosity setting. If set to True this will print all SQL queries
                        to the console.
        """
        self.db_uri = db_uri
        self.verbose = verbose
        self.engine = None
        self.table = None
        self.table_name = table_name

    def open(self):
        db_uri = self.db_uri
        if db_uri is None:
            # These are settings that apply only for development / testing only. The additional args are necessary
            # due to some limitations of the in-memory sqlite database.
            db_uri = 'sqlite:///:memory:'
            self.engine = create_engine(db_uri, poolclass=StaticPool, connect_args={'check_same_thread': False})
        else:
            self.engine = create_engine(db_uri, echo=self.verbose, echo_pool=self.verbose)

        metadata = MetaData()
        metadata.bind = self.engine
        self.table = Table(self.table_name, metadata,
                           Column('key', String(KEY_LEN), primary_key=True),
                           Column('value', Binary(VALUE_LEN)),
                          )

    def close(self):
        self.engine.dispose()
        self.table = None

    def create(self):
        self.open()
        self.table.create()
        self.close()

    def destroy(self):
        self.open()
        self.table.drop()
        self.close()

    def __iter__(self):
        rows = select([self.table.c.key]).execute().fetchall()
        for row in rows:
            yield row[0]

    def __delitem__(self, key):
        self.table.delete().where(self.table.c.key == key).execute()


class BytesStore(_Store, BytesMutableStoreBase):
    def __getitem__(self, key):
        value = select([self.table.c.value], self.table.c.key == key).execute().fetchone()
        if value is not None:
            return value[0]
        else:
            raise KeyError(key)

    def __setitem__(self, key, value):
        self.table.insert().execute(key=key, value=value)


class FileStore(_Store, FileMutableStoreBase):
    def __getitem__(self, key):
        value = select([self.table.c.value], self.table.c.key == key).execute().fetchone()
        if value is not None:
            return StringIO(value[0])
        else:
            raise KeyError(key)

    def __setitem__(self, key, stream):
        self.table.insert().execute(key=key, value=stream.read())
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.