"""
A dbm clone using sqlite under the covers.

XXX TO DO:

* Obvious speed problems (all tests performed on 2.2GHz MacBook Pro running
  OSX 10.5.4 with SQLite 3.6.2).  See sqliteperf.sh and sqliteperf.out.
  Note the poor performance compared to dbm.dumb while reading keys.
"""

import sqlite3
import collections

__all__ = ["error", "open"]

error = sqlite3.OperationalError

class _Database(collections.MutableMapping):
    def __init__(self, filename, mode):
        self._mode = mode
        self._filename = filename
        self._conn = sqlite3.connect(filename)
        self.initialize_table()
        self._writes = 0

    def initialize_table(self):
        c = self._conn.cursor()
        try:
            c.execute("select count(key) from dict")
        except sqlite3.OperationalError:
            c.execute("create table dict (key blob primary key, value blob)")
            self._conn.commit()

    def __getitem__(self, key):
        c = self._conn.cursor()
        c.execute("select value from dict"
                  "  where key = ?", (key,))
        rows = list(c)
        if not rows:
            raise KeyError(key)
        return rows[0][0]

    def __setitem__(self, key, val):
        c = self._conn.cursor()
        c.execute("replace into dict (key, value) values (?, ?)", (key, val))
        self._writes += 1
        if self._writes % 100 == 0:
            self._conn.commit()

    def __delitem__(self, key):
        # Complain if it's not there.
        self.__getitem__(key)
        c = self._conn.cursor()
        c.execute("delete from dict where key = ?", (key,))
        self._conn.commit()

    def iterkeys(self):
        c = self._conn.cursor()
        c.execute("select key from dict order by rowid")
        return (e[0] for e in c)
    __iter__ = iterkeys

    def itervalues(self):
        c = self._conn.cursor()
        c.execute("select value from dict order by rowid")
        return (e[0] for e in c)

    def iteritems(self):
        c = self._conn.cursor()
        c.execute("select key, value from dict order by rowid")
        return (e for e in c)

    def __contains__(self, key):
        try:
            self.__getitem__(key)
        except KeyError:
            return False
        else:
            return True

    def keys(self):
        return list(self.iterkeys())

    def items(self):
        return list(self.iteritems())

    def values(self):
        return list(self.itervalues())

    def __len__(self):
        c = self._conn.cursor()
        c.execute("select count(key) from dict")
        return list(c)[0][0]

    def close(self):
        if self._conn is not None:
            self._conn.commit()
            self._conn.close()
        self._conn = None

def open(filename, _flag=None, mode=0o666):
    return _Database(filename, mode)
