16aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis#-----------------------------------------------------------------------
26aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis#
36aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis# Copyright (C) 2000, 2001 by Autonomous Zone Industries
4b2c7affbaab984915b9401105334afffeedf706dMartin v. Löwis# Copyright (C) 2002 Gregory P. Smith
56aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis#
66aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis# License:      This is free software.  You may use this software for any
76aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis#               purpose including modification/redistribution, so long as
86aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis#               this header remains intact and that you do not claim any
96aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis#               rights of ownership or authorship of this software.  This
106aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis#               software has been tested, but no warranty is expressed or
116aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis#               implied.
126aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis#
13f80578548d46dbe6dad87b8b8f1ac0002bf6aef8Gregory P. Smith#   --  Gregory P. Smith <greg@krypto.org>
146aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
156aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis# This provides a simple database table interface built on top of
16ca3939cd52664cce10283cb47b360f1df1552fa0Jesus Cea# the Python Berkeley DB 3 interface.
176aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis#
186aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis_cvsid = '$Id$'
196aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
206aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwisimport re
21f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsawimport sys
226aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwisimport copy
2395334a5d1e224548d16b358c1314be444625b925Tim Petersimport random
24afed3a4552e0df61477c322f60b10d3ae59edf1cGregory P. Smithimport struct
256557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea
266557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea
276557aac599f1c22051ee61ba61c8c43add406e94Jesus Ceaif sys.version_info[0] >= 3 :
286557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea    import pickle
296557aac599f1c22051ee61ba61c8c43add406e94Jesus Ceaelse :
306557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea    if sys.version_info < (2, 6) :
316557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea        import cPickle as pickle
326557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea    else :
334950a3b6ccb91e0ffe937374836caa825e93c742doko@ubuntu.com        # When we drop support for python 2.4
346557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea        # we could use: (in 2.5 we need a __future__ statement)
356557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea        #
366557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea        #    with warnings.catch_warnings():
376557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea        #        warnings.filterwarnings(...)
386557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea        #        ...
396557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea        #
406557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea        # We can not use "with" as is, because it would be invalid syntax
414950a3b6ccb91e0ffe937374836caa825e93c742doko@ubuntu.com        # in python 2.4 and (with no __future__) 2.5.
426557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea        # Here we simulate "with" following PEP 343 :
436557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea        import warnings
446557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea        w = warnings.catch_warnings()
456557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea        w.__enter__()
466557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea        try :
476557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea            warnings.filterwarnings('ignore',
486557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea                message='the cPickle module has been removed in Python 3.0',
496557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea                category=DeprecationWarning)
506557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea            import cPickle as pickle
516557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea        finally :
526557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea            w.__exit__()
536557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea        del w
546aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
55f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsawtry:
5641631e8f668c9c2c724731d207edcc39c6fcf38cGregory P. Smith    # For Pythons w/distutils pybsddb
574907d27c1fcc7bd990715d3023932433076e152fJesus Cea    from bsddb3 import db
5841631e8f668c9c2c724731d207edcc39c6fcf38cGregory P. Smithexcept ImportError:
59f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw    # For Python 2.3
604907d27c1fcc7bd990715d3023932433076e152fJesus Cea    from bsddb import db
616aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
62f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsawclass TableDBError(StandardError):
63f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw    pass
64f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsawclass TableAlreadyExists(TableDBError):
65f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw    pass
666aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
676aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
686aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwisclass Cond:
696aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    """This condition matches everything"""
706aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    def __call__(self, s):
716aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        return 1
726aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
736aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwisclass ExactCond(Cond):
746aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    """Acts as an exact match condition function"""
756aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    def __init__(self, strtomatch):
766aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        self.strtomatch = strtomatch
776aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    def __call__(self, s):
786aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        return s == self.strtomatch
796aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
806aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwisclass PrefixCond(Cond):
816aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    """Acts as a condition function for matching a string prefix"""
826aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    def __init__(self, prefix):
836aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        self.prefix = prefix
846aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    def __call__(self, s):
856aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        return s[:len(self.prefix)] == self.prefix
866aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
87b2c7affbaab984915b9401105334afffeedf706dMartin v. Löwisclass PostfixCond(Cond):
88b2c7affbaab984915b9401105334afffeedf706dMartin v. Löwis    """Acts as a condition function for matching a string postfix"""
89b2c7affbaab984915b9401105334afffeedf706dMartin v. Löwis    def __init__(self, postfix):
90b2c7affbaab984915b9401105334afffeedf706dMartin v. Löwis        self.postfix = postfix
91b2c7affbaab984915b9401105334afffeedf706dMartin v. Löwis    def __call__(self, s):
92b2c7affbaab984915b9401105334afffeedf706dMartin v. Löwis        return s[-len(self.postfix):] == self.postfix
93b2c7affbaab984915b9401105334afffeedf706dMartin v. Löwis
946aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwisclass LikeCond(Cond):
956aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    """
966aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    Acts as a function that will match using an SQL 'LIKE' style
976aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    string.  Case insensitive and % signs are wild cards.
986aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    This isn't perfect but it should work for the simple common cases.
996aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    """
1006aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    def __init__(self, likestr, re_flags=re.IGNORECASE):
1016aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        # escape python re characters
1026aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        chars_to_escape = '.*+()[]?'
1036aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        for char in chars_to_escape :
104f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw            likestr = likestr.replace(char, '\\'+char)
1056aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        # convert %s to wildcards
106f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw        self.likestr = likestr.replace('%', '.*')
1076aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        self.re = re.compile('^'+self.likestr+'$', re_flags)
1086aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    def __call__(self, s):
1096aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        return self.re.match(s)
1106aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
1116aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis#
1126aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis# keys used to store database metadata
1136aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis#
1146aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis_table_names_key = '__TABLE_NAMES__'  # list of the tables in this db
1156aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis_columns = '._COLUMNS__'  # table_name+this key contains a list of columns
116f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw
117f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsawdef _columns_key(table):
118f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw    return table + _columns
1196aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
1206aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis#
1216aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis# these keys are found within table sub databases
1226aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis#
1236aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis_data =  '._DATA_.'  # this+column+this+rowid key contains table data
1246aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis_rowid = '._ROWID_.' # this+rowid+this key contains a unique entry for each
1256aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                     # row in the table.  (no data is stored)
1266aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis_rowid_str_len = 8   # length in bytes of the unique rowid strings
127f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw
1284907d27c1fcc7bd990715d3023932433076e152fJesus Cea
129f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsawdef _data_key(table, col, rowid):
130f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw    return table + _data + col + _data + rowid
131f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw
132f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsawdef _search_col_data_key(table, col):
133f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw    return table + _data + col + _data
134f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw
135f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsawdef _search_all_data_key(table):
136f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw    return table + _data
137f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw
138f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsawdef _rowid_key(table, rowid):
139f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw    return table + _rowid + rowid + _rowid
140f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw
141f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsawdef _search_rowid_key(table):
142f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw    return table + _rowid
1436aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
1446aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwisdef contains_metastrings(s) :
1456aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    """Verify that the given string does not contain any
1466aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    metadata strings that might interfere with dbtables database operation.
1476aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    """
148f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw    if (s.find(_table_names_key) >= 0 or
149f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw        s.find(_columns) >= 0 or
150f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw        s.find(_data) >= 0 or
151f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw        s.find(_rowid) >= 0):
152f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw        # Then
1536aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        return 1
154f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw    else:
1556aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        return 0
1566aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
1576aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
1586aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwisclass bsdTableDB :
1599a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw    def __init__(self, filename, dbhome, create=0, truncate=0, mode=0600,
160f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw                 recover=0, dbflags=0):
161ff7d991a07f21a0a368f422efcf17b27c363e1d2Gregory P. Smith        """bsdTableDB(filename, dbhome, create=0, truncate=0, mode=0600)
162ff7d991a07f21a0a368f422efcf17b27c363e1d2Gregory P. Smith
163ca3939cd52664cce10283cb47b360f1df1552fa0Jesus Cea        Open database name in the dbhome Berkeley DB directory.
1646aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        Use keyword arguments when calling this constructor.
1656aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        """
1669a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw        self.db = None
1674907d27c1fcc7bd990715d3023932433076e152fJesus Cea        myflags = db.DB_THREAD
1689a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw        if create:
1694907d27c1fcc7bd990715d3023932433076e152fJesus Cea            myflags |= db.DB_CREATE
1704907d27c1fcc7bd990715d3023932433076e152fJesus Cea        flagsforenv = (db.DB_INIT_MPOOL | db.DB_INIT_LOCK | db.DB_INIT_LOG |
1714907d27c1fcc7bd990715d3023932433076e152fJesus Cea                       db.DB_INIT_TXN | dbflags)
1729a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw        # DB_AUTO_COMMIT isn't a valid flag for env.open()
1739a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw        try:
1744907d27c1fcc7bd990715d3023932433076e152fJesus Cea            dbflags |= db.DB_AUTO_COMMIT
1759a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw        except AttributeError:
1769a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw            pass
1779a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw        if recover:
1784907d27c1fcc7bd990715d3023932433076e152fJesus Cea            flagsforenv = flagsforenv | db.DB_RECOVER
1794907d27c1fcc7bd990715d3023932433076e152fJesus Cea        self.env = db.DBEnv()
1809a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw        # enable auto deadlock avoidance
1814907d27c1fcc7bd990715d3023932433076e152fJesus Cea        self.env.set_lk_detect(db.DB_LOCK_DEFAULT)
1826aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        self.env.open(dbhome, myflags | flagsforenv)
1839a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw        if truncate:
1844907d27c1fcc7bd990715d3023932433076e152fJesus Cea            myflags |= db.DB_TRUNCATE
1854907d27c1fcc7bd990715d3023932433076e152fJesus Cea        self.db = db.DB(self.env)
186455d46f0d921337405ebd3a098c210f3614ae77cGregory P. Smith        # this code relies on DBCursor.set* methods to raise exceptions
187455d46f0d921337405ebd3a098c210f3614ae77cGregory P. Smith        # rather than returning None
188455d46f0d921337405ebd3a098c210f3614ae77cGregory P. Smith        self.db.set_get_returns_none(1)
1899a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw        # allow duplicate entries [warning: be careful w/ metadata]
1904907d27c1fcc7bd990715d3023932433076e152fJesus Cea        self.db.set_flags(db.DB_DUP)
1914907d27c1fcc7bd990715d3023932433076e152fJesus Cea        self.db.open(filename, db.DB_BTREE, dbflags | myflags, mode)
1926aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        self.dbfilename = filename
1934907d27c1fcc7bd990715d3023932433076e152fJesus Cea
1944907d27c1fcc7bd990715d3023932433076e152fJesus Cea        if sys.version_info[0] >= 3 :
1954907d27c1fcc7bd990715d3023932433076e152fJesus Cea            class cursor_py3k(object) :
1964907d27c1fcc7bd990715d3023932433076e152fJesus Cea                def __init__(self, dbcursor) :
1974907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    self._dbcursor = dbcursor
1984907d27c1fcc7bd990715d3023932433076e152fJesus Cea
1994907d27c1fcc7bd990715d3023932433076e152fJesus Cea                def close(self) :
2004907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    return self._dbcursor.close()
2014907d27c1fcc7bd990715d3023932433076e152fJesus Cea
2024907d27c1fcc7bd990715d3023932433076e152fJesus Cea                def set_range(self, search) :
2034907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    v = self._dbcursor.set_range(bytes(search, "iso8859-1"))
2048d3f130d41d0c3f437f37a18c624955ce8bf65d3Ezio Melotti                    if v is not None :
2054907d27c1fcc7bd990715d3023932433076e152fJesus Cea                        v = (v[0].decode("iso8859-1"),
2064907d27c1fcc7bd990715d3023932433076e152fJesus Cea                                v[1].decode("iso8859-1"))
2074907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    return v
2084907d27c1fcc7bd990715d3023932433076e152fJesus Cea
2094907d27c1fcc7bd990715d3023932433076e152fJesus Cea                def __next__(self) :
2104907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    v = getattr(self._dbcursor, "next")()
2118d3f130d41d0c3f437f37a18c624955ce8bf65d3Ezio Melotti                    if v is not None :
2124907d27c1fcc7bd990715d3023932433076e152fJesus Cea                        v = (v[0].decode("iso8859-1"),
2134907d27c1fcc7bd990715d3023932433076e152fJesus Cea                                v[1].decode("iso8859-1"))
2144907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    return v
2154907d27c1fcc7bd990715d3023932433076e152fJesus Cea
2164907d27c1fcc7bd990715d3023932433076e152fJesus Cea            class db_py3k(object) :
2174907d27c1fcc7bd990715d3023932433076e152fJesus Cea                def __init__(self, db) :
2184907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    self._db = db
2194907d27c1fcc7bd990715d3023932433076e152fJesus Cea
2204907d27c1fcc7bd990715d3023932433076e152fJesus Cea                def cursor(self, txn=None) :
2214907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    return cursor_py3k(self._db.cursor(txn=txn))
2224907d27c1fcc7bd990715d3023932433076e152fJesus Cea
2234907d27c1fcc7bd990715d3023932433076e152fJesus Cea                def has_key(self, key, txn=None) :
2244907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    return getattr(self._db,"has_key")(bytes(key, "iso8859-1"),
2254907d27c1fcc7bd990715d3023932433076e152fJesus Cea                            txn=txn)
2264907d27c1fcc7bd990715d3023932433076e152fJesus Cea
2274907d27c1fcc7bd990715d3023932433076e152fJesus Cea                def put(self, key, value, flags=0, txn=None) :
2284907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    key = bytes(key, "iso8859-1")
2298d3f130d41d0c3f437f37a18c624955ce8bf65d3Ezio Melotti                    if value is not None :
2304907d27c1fcc7bd990715d3023932433076e152fJesus Cea                        value = bytes(value, "iso8859-1")
2314907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    return self._db.put(key, value, flags=flags, txn=txn)
2324907d27c1fcc7bd990715d3023932433076e152fJesus Cea
2334907d27c1fcc7bd990715d3023932433076e152fJesus Cea                def put_bytes(self, key, value, txn=None) :
2344907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    key = bytes(key, "iso8859-1")
2354907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    return self._db.put(key, value, txn=txn)
2364907d27c1fcc7bd990715d3023932433076e152fJesus Cea
2374907d27c1fcc7bd990715d3023932433076e152fJesus Cea                def get(self, key, txn=None, flags=0) :
2384907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    key = bytes(key, "iso8859-1")
2394907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    v = self._db.get(key, txn=txn, flags=flags)
2408d3f130d41d0c3f437f37a18c624955ce8bf65d3Ezio Melotti                    if v is not None :
2414907d27c1fcc7bd990715d3023932433076e152fJesus Cea                        v = v.decode("iso8859-1")
2424907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    return v
2434907d27c1fcc7bd990715d3023932433076e152fJesus Cea
2444907d27c1fcc7bd990715d3023932433076e152fJesus Cea                def get_bytes(self, key, txn=None, flags=0) :
2454907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    key = bytes(key, "iso8859-1")
2464907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    return self._db.get(key, txn=txn, flags=flags)
2474907d27c1fcc7bd990715d3023932433076e152fJesus Cea
2484907d27c1fcc7bd990715d3023932433076e152fJesus Cea                def delete(self, key, txn=None) :
2494907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    key = bytes(key, "iso8859-1")
2504907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    return self._db.delete(key, txn=txn)
2514907d27c1fcc7bd990715d3023932433076e152fJesus Cea
2524907d27c1fcc7bd990715d3023932433076e152fJesus Cea                def close (self) :
2534907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    return self._db.close()
2544907d27c1fcc7bd990715d3023932433076e152fJesus Cea
2554907d27c1fcc7bd990715d3023932433076e152fJesus Cea            self.db = db_py3k(self.db)
2564907d27c1fcc7bd990715d3023932433076e152fJesus Cea        else :  # Python 2.x
2574907d27c1fcc7bd990715d3023932433076e152fJesus Cea            pass
2584907d27c1fcc7bd990715d3023932433076e152fJesus Cea
2596aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        # Initialize the table names list if this is a new database
2609a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw        txn = self.env.txn_begin()
2619a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw        try:
2624907d27c1fcc7bd990715d3023932433076e152fJesus Cea            if not getattr(self.db, "has_key")(_table_names_key, txn):
2634907d27c1fcc7bd990715d3023932433076e152fJesus Cea                getattr(self.db, "put_bytes", self.db.put) \
2644907d27c1fcc7bd990715d3023932433076e152fJesus Cea                        (_table_names_key, pickle.dumps([], 1), txn=txn)
2659a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw        # Yes, bare except
2669a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw        except:
2679a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw            txn.abort()
2689a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw            raise
2699a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw        else:
2709a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw            txn.commit()
2716aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        # TODO verify more of the database's metadata?
2726aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        self.__tablecolumns = {}
2736aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
2746aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    def __del__(self):
2756aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        self.close()
2766aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
2776aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    def close(self):
2786aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        if self.db is not None:
2796aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            self.db.close()
2806aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            self.db = None
2816aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        if self.env is not None:
2826aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            self.env.close()
2836aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            self.env = None
2846aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
2856aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    def checkpoint(self, mins=0):
2866557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea        self.env.txn_checkpoint(mins)
2876aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
2886aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    def sync(self):
2896557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea        self.db.sync()
2906aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
2916aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    def _db_print(self) :
2926aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        """Print the database to stdout for debugging"""
2936aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        print "******** Printing raw database for debugging ********"
2946aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        cur = self.db.cursor()
2956aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        try:
2966aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            key, data = cur.first()
297f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw            while 1:
29870a6b49821a3226f55e9716f32d802d06640cb89Walter Dörwald                print repr({key: data})
2996aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                next = cur.next()
3006aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                if next:
3016aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                    key, data = next
3026aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                else:
3036aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                    cur.close()
3046aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                    return
3054907d27c1fcc7bd990715d3023932433076e152fJesus Cea        except db.DBNotFoundError:
3066aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            cur.close()
3076aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
3086aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
3099a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw    def CreateTable(self, table, columns):
310ff7d991a07f21a0a368f422efcf17b27c363e1d2Gregory P. Smith        """CreateTable(table, columns) - Create a new table in the database.
311ff7d991a07f21a0a368f422efcf17b27c363e1d2Gregory P. Smith
3126aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        raises TableDBError if it already exists or for other DB errors.
3136aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        """
314c5a11fabdb03d032f278f86e600bcdaafdb5d783Jesus Cea        assert isinstance(columns, list)
3154907d27c1fcc7bd990715d3023932433076e152fJesus Cea
3166aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        txn = None
3176aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        try:
3186aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            # checking sanity of the table and column names here on
3196aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            # table creation will prevent problems elsewhere.
3209a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw            if contains_metastrings(table):
3219a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                raise ValueError(
3229a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                    "bad table name: contains reserved metastrings")
3236aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            for column in columns :
3249a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                if contains_metastrings(column):
3259a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                    raise ValueError(
3269a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                        "bad column name: contains reserved metastrings")
3276aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
3286aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            columnlist_key = _columns_key(table)
3294907d27c1fcc7bd990715d3023932433076e152fJesus Cea            if getattr(self.db, "has_key")(columnlist_key):
3306aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                raise TableAlreadyExists, "table already exists"
3316aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
3326aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            txn = self.env.txn_begin()
3336aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            # store the table's column info
3344907d27c1fcc7bd990715d3023932433076e152fJesus Cea            getattr(self.db, "put_bytes", self.db.put)(columnlist_key,
3354907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    pickle.dumps(columns, 1), txn=txn)
3366aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
3376aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            # add the table name to the tablelist
3384907d27c1fcc7bd990715d3023932433076e152fJesus Cea            tablelist = pickle.loads(getattr(self.db, "get_bytes",
3394907d27c1fcc7bd990715d3023932433076e152fJesus Cea                self.db.get) (_table_names_key, txn=txn, flags=db.DB_RMW))
3406aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            tablelist.append(table)
3419a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw            # delete 1st, in case we opened with DB_DUP
342afed3a4552e0df61477c322f60b10d3ae59edf1cGregory P. Smith            self.db.delete(_table_names_key, txn=txn)
3434907d27c1fcc7bd990715d3023932433076e152fJesus Cea            getattr(self.db, "put_bytes", self.db.put)(_table_names_key,
3444907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    pickle.dumps(tablelist, 1), txn=txn)
3456aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
3466aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            txn.commit()
3476aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            txn = None
3484907d27c1fcc7bd990715d3023932433076e152fJesus Cea        except db.DBError, dberror:
349f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw            if txn:
3506aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                txn.abort()
3515d62cfe070b63cdb3c80744ef9970facc008750cEzio Melotti            if sys.version_info < (2, 6) :
3524907d27c1fcc7bd990715d3023932433076e152fJesus Cea                raise TableDBError, dberror[1]
3534907d27c1fcc7bd990715d3023932433076e152fJesus Cea            else :
3544907d27c1fcc7bd990715d3023932433076e152fJesus Cea                raise TableDBError, dberror.args[1]
3556aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
3566aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
3576aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    def ListTableColumns(self, table):
3589a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw        """Return a list of columns in the given table.
3599a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw        [] if the table doesn't exist.
3606aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        """
361c5a11fabdb03d032f278f86e600bcdaafdb5d783Jesus Cea        assert isinstance(table, str)
362f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw        if contains_metastrings(table):
3636aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            raise ValueError, "bad table name: contains reserved metastrings"
3646aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
3656aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        columnlist_key = _columns_key(table)
3664907d27c1fcc7bd990715d3023932433076e152fJesus Cea        if not getattr(self.db, "has_key")(columnlist_key):
3676aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            return []
3684907d27c1fcc7bd990715d3023932433076e152fJesus Cea        pickledcolumnlist = getattr(self.db, "get_bytes",
3694907d27c1fcc7bd990715d3023932433076e152fJesus Cea                self.db.get)(columnlist_key)
3706aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        if pickledcolumnlist:
3716aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            return pickle.loads(pickledcolumnlist)
3726aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        else:
3736aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            return []
3746aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
3756aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    def ListTables(self):
3766aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        """Return a list of tables in this database."""
3774907d27c1fcc7bd990715d3023932433076e152fJesus Cea        pickledtablelist = self.db.get_get(_table_names_key)
3786aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        if pickledtablelist:
3796aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            return pickle.loads(pickledtablelist)
3806aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        else:
3816aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            return []
3826aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
3836aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    def CreateOrExtendTable(self, table, columns):
3849a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw        """CreateOrExtendTable(table, columns)
3859a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw
386ff7d991a07f21a0a368f422efcf17b27c363e1d2Gregory P. Smith        Create a new table in the database.
387ff7d991a07f21a0a368f422efcf17b27c363e1d2Gregory P. Smith
3886aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        If a table of this name already exists, extend it to have any
3896aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        additional columns present in the given list as well as
3906aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        all of its current columns.
3916aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        """
392c5a11fabdb03d032f278f86e600bcdaafdb5d783Jesus Cea        assert isinstance(columns, list)
3934907d27c1fcc7bd990715d3023932433076e152fJesus Cea
3946aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        try:
3956aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            self.CreateTable(table, columns)
3966aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        except TableAlreadyExists:
3976aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            # the table already existed, add any new columns
3986aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            txn = None
3996aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            try:
4006aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                columnlist_key = _columns_key(table)
4016aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                txn = self.env.txn_begin()
4026aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
4036aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                # load the current column list
4049a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                oldcolumnlist = pickle.loads(
4054907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    getattr(self.db, "get_bytes",
4064907d27c1fcc7bd990715d3023932433076e152fJesus Cea                        self.db.get)(columnlist_key, txn=txn, flags=db.DB_RMW))
4079a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                # create a hash table for fast lookups of column names in the
4089a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                # loop below
4096aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                oldcolumnhash = {}
4106aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                for c in oldcolumnlist:
4116aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                    oldcolumnhash[c] = c
4126aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
4139a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                # create a new column list containing both the old and new
4149a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                # column names
4156aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                newcolumnlist = copy.copy(oldcolumnlist)
4166aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                for c in columns:
41763b0cb2f39e26be7f6b636ddd40cc8d6a1400d51Antoine Pitrou                    if not c in oldcolumnhash:
4186aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                        newcolumnlist.append(c)
4196aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
4206aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                # store the table's new extended column list
4216aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                if newcolumnlist != oldcolumnlist :
4226aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                    # delete the old one first since we opened with DB_DUP
423afed3a4552e0df61477c322f60b10d3ae59edf1cGregory P. Smith                    self.db.delete(columnlist_key, txn=txn)
4244907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    getattr(self.db, "put_bytes", self.db.put)(columnlist_key,
4259a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                                pickle.dumps(newcolumnlist, 1),
4269a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                                txn=txn)
4276aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
4286aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                txn.commit()
4296aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                txn = None
4306aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
4316aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                self.__load_column_info(table)
4324907d27c1fcc7bd990715d3023932433076e152fJesus Cea            except db.DBError, dberror:
4336aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                if txn:
4346aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                    txn.abort()
4355d62cfe070b63cdb3c80744ef9970facc008750cEzio Melotti                if sys.version_info < (2, 6) :
4364907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    raise TableDBError, dberror[1]
4374907d27c1fcc7bd990715d3023932433076e152fJesus Cea                else :
4384907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    raise TableDBError, dberror.args[1]
4396aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
4406aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
4416aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    def __load_column_info(self, table) :
4426aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        """initialize the self.__tablecolumns dict"""
4436aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        # check the column names
4446aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        try:
4454907d27c1fcc7bd990715d3023932433076e152fJesus Cea            tcolpickles = getattr(self.db, "get_bytes",
4464907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    self.db.get)(_columns_key(table))
4474907d27c1fcc7bd990715d3023932433076e152fJesus Cea        except db.DBNotFoundError:
44870a6b49821a3226f55e9716f32d802d06640cb89Walter Dörwald            raise TableDBError, "unknown table: %r" % (table,)
4496aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        if not tcolpickles:
45070a6b49821a3226f55e9716f32d802d06640cb89Walter Dörwald            raise TableDBError, "unknown table: %r" % (table,)
4516aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        self.__tablecolumns[table] = pickle.loads(tcolpickles)
4526aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
4539a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw    def __new_rowid(self, table, txn) :
4546aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        """Create a new unique row identifier"""
4556aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        unique = 0
456f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw        while not unique:
4576aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            # Generate a random 64-bit row ID string
4586d331ca9a49245c5d18d3aa9b0f72cc54f9521c3Gregory P. Smith            # (note: might have <64 bits of true randomness
4596aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            # but it's plenty for our database id needs!)
4603ef21cb7c61236453b257c30ce08f8f58489e802Gregory P. Smith            blist = []
4613ef21cb7c61236453b257c30ce08f8f58489e802Gregory P. Smith            for x in xrange(_rowid_str_len):
4626d331ca9a49245c5d18d3aa9b0f72cc54f9521c3Gregory P. Smith                blist.append(random.randint(0,255))
4633ef21cb7c61236453b257c30ce08f8f58489e802Gregory P. Smith            newid = struct.pack('B'*_rowid_str_len, *blist)
4646aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
4654907d27c1fcc7bd990715d3023932433076e152fJesus Cea            if sys.version_info[0] >= 3 :
4664907d27c1fcc7bd990715d3023932433076e152fJesus Cea                newid = newid.decode("iso8859-1")  # 8 bits
4674907d27c1fcc7bd990715d3023932433076e152fJesus Cea
4686aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            # Guarantee uniqueness by adding this key to the database
4696aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            try:
4709a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                self.db.put(_rowid_key(table, newid), None, txn=txn,
4714907d27c1fcc7bd990715d3023932433076e152fJesus Cea                            flags=db.DB_NOOVERWRITE)
4724907d27c1fcc7bd990715d3023932433076e152fJesus Cea            except db.DBKeyExistError:
4736aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                pass
4746aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            else:
4756aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                unique = 1
4766aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
4776aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        return newid
4786aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
4796aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
4806aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis    def Insert(self, table, rowdict) :
4816aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        """Insert(table, datadict) - Insert a new row into the table
4826aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        using the keys+values from rowdict as the column values.
4836aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        """
4844907d27c1fcc7bd990715d3023932433076e152fJesus Cea
4856aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        txn = None
4866aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        try:
4874907d27c1fcc7bd990715d3023932433076e152fJesus Cea            if not getattr(self.db, "has_key")(_columns_key(table)):
4886aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                raise TableDBError, "unknown table"
4896aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
4906aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            # check the validity of each column name
49163b0cb2f39e26be7f6b636ddd40cc8d6a1400d51Antoine Pitrou            if not table in self.__tablecolumns:
4926aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                self.__load_column_info(table)
4936aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            for column in rowdict.keys() :
494f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw                if not self.__tablecolumns[table].count(column):
49570a6b49821a3226f55e9716f32d802d06640cb89Walter Dörwald                    raise TableDBError, "unknown column: %r" % (column,)
4966aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
4976aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            # get a unique row identifier for this row
4986aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            txn = self.env.txn_begin()
4999a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw            rowid = self.__new_rowid(table, txn=txn)
5006aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
5016aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            # insert the row values into the table database
502f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw            for column, dataitem in rowdict.items():
5036aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                # store the value
5046aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                self.db.put(_data_key(table, column, rowid), dataitem, txn=txn)
5056aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
5066aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            txn.commit()
5076aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            txn = None
5086aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
5094907d27c1fcc7bd990715d3023932433076e152fJesus Cea        except db.DBError, dberror:
5109a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw            # WIBNI we could just abort the txn and re-raise the exception?
5119a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw            # But no, because TableDBError is not related to DBError via
5129a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw            # inheritance, so it would be backwards incompatible.  Do the next
5139a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw            # best thing.
5149a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw            info = sys.exc_info()
5159a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw            if txn:
5166aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                txn.abort()
5176aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                self.db.delete(_rowid_key(table, rowid))
5185d62cfe070b63cdb3c80744ef9970facc008750cEzio Melotti            if sys.version_info < (2, 6) :
5194907d27c1fcc7bd990715d3023932433076e152fJesus Cea                raise TableDBError, dberror[1], info[2]
5204907d27c1fcc7bd990715d3023932433076e152fJesus Cea            else :
5214907d27c1fcc7bd990715d3023932433076e152fJesus Cea                raise TableDBError, dberror.args[1], info[2]
5226aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
5236aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
524f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw    def Modify(self, table, conditions={}, mappings={}):
525ff7d991a07f21a0a368f422efcf17b27c363e1d2Gregory P. Smith        """Modify(table, conditions={}, mappings={}) - Modify items in rows matching 'conditions' using mapping functions in 'mappings'
526ff7d991a07f21a0a368f422efcf17b27c363e1d2Gregory P. Smith
527ff7d991a07f21a0a368f422efcf17b27c363e1d2Gregory P. Smith        * table - the table name
528ff7d991a07f21a0a368f422efcf17b27c363e1d2Gregory P. Smith        * conditions - a dictionary keyed on column names containing
529ff7d991a07f21a0a368f422efcf17b27c363e1d2Gregory P. Smith          a condition callable expecting the data string as an
530ff7d991a07f21a0a368f422efcf17b27c363e1d2Gregory P. Smith          argument and returning a boolean.
531ff7d991a07f21a0a368f422efcf17b27c363e1d2Gregory P. Smith        * mappings - a dictionary keyed on column names containing a
532ff7d991a07f21a0a368f422efcf17b27c363e1d2Gregory P. Smith          condition callable expecting the data string as an argument and
533ff7d991a07f21a0a368f422efcf17b27c363e1d2Gregory P. Smith          returning the new string for that column.
5346aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        """
5354907d27c1fcc7bd990715d3023932433076e152fJesus Cea
5366aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        try:
5376aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            matching_rowids = self.__Select(table, [], conditions)
5386aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
5396aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            # modify only requested columns
5406aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            columns = mappings.keys()
541f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw            for rowid in matching_rowids.keys():
5426aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                txn = None
5436aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                try:
544f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw                    for column in columns:
5456aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                        txn = self.env.txn_begin()
5466aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                        # modify the requested column
5476aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                        try:
5489a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                            dataitem = self.db.get(
5499a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                                _data_key(table, column, rowid),
550afed3a4552e0df61477c322f60b10d3ae59edf1cGregory P. Smith                                txn=txn)
5519a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                            self.db.delete(
5529a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                                _data_key(table, column, rowid),
553afed3a4552e0df61477c322f60b10d3ae59edf1cGregory P. Smith                                txn=txn)
5544907d27c1fcc7bd990715d3023932433076e152fJesus Cea                        except db.DBNotFoundError:
5559a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                             # XXXXXXX row key somehow didn't exist, assume no
5569a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                             # error
5579a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                            dataitem = None
5586aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                        dataitem = mappings[column](dataitem)
5598d3f130d41d0c3f437f37a18c624955ce8bf65d3Ezio Melotti                        if dataitem is not None:
5609a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                            self.db.put(
5619a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                                _data_key(table, column, rowid),
5629a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                                dataitem, txn=txn)
5636aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                        txn.commit()
5646aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                        txn = None
5656aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
566ff7d991a07f21a0a368f422efcf17b27c363e1d2Gregory P. Smith                # catch all exceptions here since we call unknown callables
567ff7d991a07f21a0a368f422efcf17b27c363e1d2Gregory P. Smith                except:
568f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw                    if txn:
5696aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                        txn.abort()
5706aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                    raise
5716aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
5724907d27c1fcc7bd990715d3023932433076e152fJesus Cea        except db.DBError, dberror:
5735d62cfe070b63cdb3c80744ef9970facc008750cEzio Melotti            if sys.version_info < (2, 6) :
5744907d27c1fcc7bd990715d3023932433076e152fJesus Cea                raise TableDBError, dberror[1]
5754907d27c1fcc7bd990715d3023932433076e152fJesus Cea            else :
5764907d27c1fcc7bd990715d3023932433076e152fJesus Cea                raise TableDBError, dberror.args[1]
5776aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
578f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw    def Delete(self, table, conditions={}):
5796aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        """Delete(table, conditions) - Delete items matching the given
5806aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        conditions from the table.
581ff7d991a07f21a0a368f422efcf17b27c363e1d2Gregory P. Smith
582ff7d991a07f21a0a368f422efcf17b27c363e1d2Gregory P. Smith        * conditions - a dictionary keyed on column names containing
583ff7d991a07f21a0a368f422efcf17b27c363e1d2Gregory P. Smith          condition functions expecting the data string as an
584ff7d991a07f21a0a368f422efcf17b27c363e1d2Gregory P. Smith          argument and returning a boolean.
5856aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        """
5864907d27c1fcc7bd990715d3023932433076e152fJesus Cea
5876aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        try:
5886aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            matching_rowids = self.__Select(table, [], conditions)
5896aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
5906aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            # delete row data from all columns
5916aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            columns = self.__tablecolumns[table]
592f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw            for rowid in matching_rowids.keys():
5936aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                txn = None
5946aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                try:
5956aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                    txn = self.env.txn_begin()
596f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw                    for column in columns:
5976aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                        # delete the data key
5986aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                        try:
5999a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                            self.db.delete(_data_key(table, column, rowid),
600afed3a4552e0df61477c322f60b10d3ae59edf1cGregory P. Smith                                           txn=txn)
6014907d27c1fcc7bd990715d3023932433076e152fJesus Cea                        except db.DBNotFoundError:
6029a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                            # XXXXXXX column may not exist, assume no error
6039a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                            pass
6046aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
6056aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                    try:
606afed3a4552e0df61477c322f60b10d3ae59edf1cGregory P. Smith                        self.db.delete(_rowid_key(table, rowid), txn=txn)
6074907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    except db.DBNotFoundError:
6089a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                        # XXXXXXX row key somehow didn't exist, assume no error
6099a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                        pass
6106aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                    txn.commit()
6116aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                    txn = None
6124907d27c1fcc7bd990715d3023932433076e152fJesus Cea                except db.DBError, dberror:
613f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw                    if txn:
6146aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                        txn.abort()
6156aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                    raise
6164907d27c1fcc7bd990715d3023932433076e152fJesus Cea        except db.DBError, dberror:
6175d62cfe070b63cdb3c80744ef9970facc008750cEzio Melotti            if sys.version_info < (2, 6) :
6184907d27c1fcc7bd990715d3023932433076e152fJesus Cea                raise TableDBError, dberror[1]
6194907d27c1fcc7bd990715d3023932433076e152fJesus Cea            else :
6204907d27c1fcc7bd990715d3023932433076e152fJesus Cea                raise TableDBError, dberror.args[1]
6216aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
6226aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
623f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw    def Select(self, table, columns, conditions={}):
624ff7d991a07f21a0a368f422efcf17b27c363e1d2Gregory P. Smith        """Select(table, columns, conditions) - retrieve specific row data
6256aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        Returns a list of row column->value mapping dictionaries.
626ff7d991a07f21a0a368f422efcf17b27c363e1d2Gregory P. Smith
627ff7d991a07f21a0a368f422efcf17b27c363e1d2Gregory P. Smith        * columns - a list of which column data to return.  If
6286aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis          columns is None, all columns will be returned.
629ff7d991a07f21a0a368f422efcf17b27c363e1d2Gregory P. Smith        * conditions - a dictionary keyed on column names
6306aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis          containing callable conditions expecting the data string as an
6316aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis          argument and returning a boolean.
6326aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        """
6336aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        try:
63463b0cb2f39e26be7f6b636ddd40cc8d6a1400d51Antoine Pitrou            if not table in self.__tablecolumns:
6356aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                self.__load_column_info(table)
636f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw            if columns is None:
6376aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                columns = self.__tablecolumns[table]
6386aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            matching_rowids = self.__Select(table, columns, conditions)
6394907d27c1fcc7bd990715d3023932433076e152fJesus Cea        except db.DBError, dberror:
6405d62cfe070b63cdb3c80744ef9970facc008750cEzio Melotti            if sys.version_info < (2, 6) :
6414907d27c1fcc7bd990715d3023932433076e152fJesus Cea                raise TableDBError, dberror[1]
6424907d27c1fcc7bd990715d3023932433076e152fJesus Cea            else :
6434907d27c1fcc7bd990715d3023932433076e152fJesus Cea                raise TableDBError, dberror.args[1]
6446aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        # return the matches as a list of dictionaries
6456aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        return matching_rowids.values()
6466aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
6476aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
648f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw    def __Select(self, table, columns, conditions):
6496aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        """__Select() - Used to implement Select and Delete (above)
6506aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        Returns a dictionary keyed on rowids containing dicts
6516aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        holding the row data for columns listed in the columns param
6526aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        that match the given conditions.
6536aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        * conditions is a dictionary keyed on column names
6546aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        containing callable conditions expecting the data string as an
6556aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        argument and returning a boolean.
6566aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        """
6576aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        # check the validity of each column name
65863b0cb2f39e26be7f6b636ddd40cc8d6a1400d51Antoine Pitrou        if not table in self.__tablecolumns:
6596aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            self.__load_column_info(table)
660f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw        if columns is None:
6616aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            columns = self.tablecolumns[table]
662f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw        for column in (columns + conditions.keys()):
663f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw            if not self.__tablecolumns[table].count(column):
66470a6b49821a3226f55e9716f32d802d06640cb89Walter Dörwald                raise TableDBError, "unknown column: %r" % (column,)
6656aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
6666aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        # keyed on rows that match so far, containings dicts keyed on
6676aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        # column names containing the data for that row and column.
6686aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        matching_rowids = {}
669f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw        # keys are rowids that do not match
670f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw        rejected_rowids = {}
6716aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
6729a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw        # attempt to sort the conditions in such a way as to minimize full
6739a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw        # column lookups
6746aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        def cmp_conditions(atuple, btuple):
6756aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            a = atuple[1]
6766aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            b = btuple[1]
677f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw            if type(a) is type(b):
6786557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea
6796557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea                # Needed for python 3. "cmp" vanished in 3.0.1
6806557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea                def cmp(a, b) :
6816557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea                    if a==b : return 0
6826557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea                    if a<b : return -1
6836557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea                    return 1
6846557aac599f1c22051ee61ba61c8c43add406e94Jesus Cea
6856aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                if isinstance(a, PrefixCond) and isinstance(b, PrefixCond):
6869a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                    # longest prefix first
6879a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                    return cmp(len(b.prefix), len(a.prefix))
6886aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                if isinstance(a, LikeCond) and isinstance(b, LikeCond):
6899a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                    # longest likestr first
6909a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                    return cmp(len(b.likestr), len(a.likestr))
6916aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                return 0
6926aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            if isinstance(a, ExactCond):
6936aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                return -1
6946aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            if isinstance(b, ExactCond):
6956aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                return 1
6966aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            if isinstance(a, PrefixCond):
6976aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                return -1
6986aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            if isinstance(b, PrefixCond):
6996aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                return 1
7006aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            # leave all unknown condition callables alone as equals
7016aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            return 0
7026aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
7035d62cfe070b63cdb3c80744ef9970facc008750cEzio Melotti        if sys.version_info < (2, 6) :
7044907d27c1fcc7bd990715d3023932433076e152fJesus Cea            conditionlist = conditions.items()
7054907d27c1fcc7bd990715d3023932433076e152fJesus Cea            conditionlist.sort(cmp_conditions)
7064907d27c1fcc7bd990715d3023932433076e152fJesus Cea        else :  # Insertion Sort. Please, improve
7074907d27c1fcc7bd990715d3023932433076e152fJesus Cea            conditionlist = []
7084907d27c1fcc7bd990715d3023932433076e152fJesus Cea            for i in conditions.items() :
7094907d27c1fcc7bd990715d3023932433076e152fJesus Cea                for j, k in enumerate(conditionlist) :
7104907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    r = cmp_conditions(k, i)
7114907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    if r == 1 :
7124907d27c1fcc7bd990715d3023932433076e152fJesus Cea                        conditionlist.insert(j, i)
7134907d27c1fcc7bd990715d3023932433076e152fJesus Cea                        break
7144907d27c1fcc7bd990715d3023932433076e152fJesus Cea                else :
7154907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    conditionlist.append(i)
7166aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
7176aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        # Apply conditions to column data to find what we want
7186aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        cur = self.db.cursor()
7196aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        column_num = -1
720f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw        for column, condition in conditionlist:
7216aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            column_num = column_num + 1
7226aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            searchkey = _search_col_data_key(table, column)
7236aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            # speedup: don't linear search columns within loop
724f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw            if column in columns:
7256aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                savethiscolumndata = 1  # save the data for return
726f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw            else:
7276aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                savethiscolumndata = 0  # data only used for selection
7286aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
7296aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            try:
7306aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                key, data = cur.set_range(searchkey)
731f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw                while key[:len(searchkey)] == searchkey:
7326aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                    # extract the rowid from the key
7336aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                    rowid = key[-_rowid_str_len:]
7346aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
73563b0cb2f39e26be7f6b636ddd40cc8d6a1400d51Antoine Pitrou                    if not rowid in rejected_rowids:
7366aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                        # if no condition was specified or the condition
7376aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                        # succeeds, add row to our match list.
738f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw                        if not condition or condition(data):
73963b0cb2f39e26be7f6b636ddd40cc8d6a1400d51Antoine Pitrou                            if not rowid in matching_rowids:
740b2c7affbaab984915b9401105334afffeedf706dMartin v. Löwis                                matching_rowids[rowid] = {}
741f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw                            if savethiscolumndata:
742b2c7affbaab984915b9401105334afffeedf706dMartin v. Löwis                                matching_rowids[rowid][column] = data
743f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw                        else:
74463b0cb2f39e26be7f6b636ddd40cc8d6a1400d51Antoine Pitrou                            if rowid in matching_rowids:
7456aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                                del matching_rowids[rowid]
7466aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                            rejected_rowids[rowid] = rowid
7476aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
7486aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                    key, data = cur.next()
7496aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
7504907d27c1fcc7bd990715d3023932433076e152fJesus Cea            except db.DBError, dberror:
75163b0cb2f39e26be7f6b636ddd40cc8d6a1400d51Antoine Pitrou                if dberror.args[0] != db.DB_NOTFOUND:
75263b0cb2f39e26be7f6b636ddd40cc8d6a1400d51Antoine Pitrou                    raise
7536aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                continue
7546aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
7556aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        cur.close()
7566aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
7576aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        # we're done selecting rows, garbage collect the reject list
7586aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        del rejected_rowids
7596aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
7606aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        # extract any remaining desired column data from the
7616aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        # database for the matching rows.
762f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw        if len(columns) > 0:
763f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw            for rowid, rowdata in matching_rowids.items():
764f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw                for column in columns:
76563b0cb2f39e26be7f6b636ddd40cc8d6a1400d51Antoine Pitrou                    if column in rowdata:
7666aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                        continue
7676aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                    try:
7689a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                        rowdata[column] = self.db.get(
7699a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                            _data_key(table, column, rowid))
7704907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    except db.DBError, dberror:
7715d62cfe070b63cdb3c80744ef9970facc008750cEzio Melotti                        if sys.version_info < (2, 6) :
7724907d27c1fcc7bd990715d3023932433076e152fJesus Cea                            if dberror[0] != db.DB_NOTFOUND:
7734907d27c1fcc7bd990715d3023932433076e152fJesus Cea                                raise
7744907d27c1fcc7bd990715d3023932433076e152fJesus Cea                        else :
7754907d27c1fcc7bd990715d3023932433076e152fJesus Cea                            if dberror.args[0] != db.DB_NOTFOUND:
7764907d27c1fcc7bd990715d3023932433076e152fJesus Cea                                raise
7776aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                        rowdata[column] = None
7786aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
7796aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        # return the matches
7806aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        return matching_rowids
7816aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
7826aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
783f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw    def Drop(self, table):
784f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw        """Remove an entire table from the database"""
7856aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        txn = None
7866aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis        try:
7876aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            txn = self.env.txn_begin()
7886aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
7896aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            # delete the column list
790afed3a4552e0df61477c322f60b10d3ae59edf1cGregory P. Smith            self.db.delete(_columns_key(table), txn=txn)
7916aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
7926aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            cur = self.db.cursor(txn)
7936aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
7946aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            # delete all keys containing this tables column and row info
7956aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            table_key = _search_all_data_key(table)
796f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw            while 1:
7976aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                try:
7986aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                    key, data = cur.set_range(table_key)
7994907d27c1fcc7bd990715d3023932433076e152fJesus Cea                except db.DBNotFoundError:
8006aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                    break
8016aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                # only delete items in this table
802f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw                if key[:len(table_key)] != table_key:
8036aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                    break
8046aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                cur.delete()
8056aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
8066aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            # delete all rowids used by this table
8076aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            table_key = _search_rowid_key(table)
808f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw            while 1:
8096aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                try:
8106aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                    key, data = cur.set_range(table_key)
8114907d27c1fcc7bd990715d3023932433076e152fJesus Cea                except db.DBNotFoundError:
8126aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                    break
8136aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                # only delete items in this table
814f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw                if key[:len(table_key)] != table_key:
8156aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                    break
8166aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                cur.delete()
8176aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
8186aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            cur.close()
8196aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
8206aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            # delete the tablename from the table name list
8219a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw            tablelist = pickle.loads(
8224907d27c1fcc7bd990715d3023932433076e152fJesus Cea                getattr(self.db, "get_bytes", self.db.get)(_table_names_key,
8234907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    txn=txn, flags=db.DB_RMW))
8246aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            try:
8256aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                tablelist.remove(table)
8266aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            except ValueError:
8279a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                # hmm, it wasn't there, oh well, that's what we want.
8289a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw                pass
8299a0d779c7d39ba5f4666eac7c3f913720198e0f8Barry Warsaw            # delete 1st, incase we opened with DB_DUP
830afed3a4552e0df61477c322f60b10d3ae59edf1cGregory P. Smith            self.db.delete(_table_names_key, txn=txn)
8314907d27c1fcc7bd990715d3023932433076e152fJesus Cea            getattr(self.db, "put_bytes", self.db.put)(_table_names_key,
8324907d27c1fcc7bd990715d3023932433076e152fJesus Cea                    pickle.dumps(tablelist, 1), txn=txn)
8336aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
8346aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            txn.commit()
8356aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis            txn = None
8366aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
83763b0cb2f39e26be7f6b636ddd40cc8d6a1400d51Antoine Pitrou            if table in self.__tablecolumns:
8386aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                del self.__tablecolumns[table]
8396aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis
8404907d27c1fcc7bd990715d3023932433076e152fJesus Cea        except db.DBError, dberror:
841f71de3e9a017d698be8ee4db3e2ec5fbc68bfc8cBarry Warsaw            if txn:
8426aa4a1f29ca575e25fc595857b2a5168a02c9780Martin v. Löwis                txn.abort()
84363b0cb2f39e26be7f6b636ddd40cc8d6a1400d51Antoine Pitrou            raise TableDBError(dberror.args[1])
844