Database independence stuff.

This commit is contained in:
Jeremy Fincher 2004-09-11 20:24:52 +00:00
parent cebf388513
commit 49c465c1c7
4 changed files with 85 additions and 58 deletions

View File

@ -82,8 +82,6 @@ def configure(advanced):
from supybot.questions import expect, anything, something, yn
conf.registerPlugin('Infobot', True)
filename = conf.supybot.directories.data.dirize('Infobot.db')
ends = ['!',
'.',
', $who.',]
@ -123,11 +121,12 @@ initialAre = {'who': '<reply>',
}
class PickleInfobotDB(object):
def __init__(self):
def __init__(self, filename):
self._changes = 0
self._responses = 0
self.filename = filename
try:
fd = file(filename)
fd = file(self.filename)
except EnvironmentError:
self._is = utils.InsensitivePreservingDict()
self._are = utils.InsensitivePreservingDict()
@ -143,7 +142,7 @@ class PickleInfobotDB(object):
raise dbi.InvalidDBError, str(e)
def flush(self):
fd = utils.transactionalFile(filename, 'wb')
fd = utils.transactionalFile(self.filename, 'wb')
pickle.dump((self._is, self._are), fd)
fd.close()
@ -228,7 +227,7 @@ class PickleInfobotDB(object):
return len(self._are.keys()) + len(self._is.keys())
class SqliteInfobotDB(object):
def __init__(self):
def __init__(self, filename):
try:
import sqlite
except ImportError:
@ -237,23 +236,13 @@ class SqliteInfobotDB(object):
'<http://pysqlite.sf.net/>'
self._changes = 0
self._responses = 0
self.db = None
def _getDb(self):
self.filename = filename
try:
import sqlite
except ImportError:
raise callbacks.Error, 'You need to have PySQLite installed to '\
'use this plugin. Download it at '\
'<http://pysqlite.sf.net/>'
if self.db is not None:
return self.db
try:
if os.path.exists(filename):
self.db = sqlite.connect(filename)
if os.path.exists(self.filename):
self.db = sqlite.connect(self.filename)
return self.db
#else:
self.db = sqlite.connect(filename)
self.db = sqlite.connect(self.filename)
cursor = self.db.cursor()
cursor.execute("""CREATE TABLE isFacts (
key TEXT UNIQUE ON CONFLICT REPLACE,
@ -269,17 +258,14 @@ class SqliteInfobotDB(object):
for (k, v) in initialAre.iteritems():
self.setAre(k, v)
self._changes = 0
return self.db
except sqlite.DatabaseError, e:
raise dbi.InvalidDBError, str(e)
def close(self):
if self.db is not None:
self.db.close()
self.db.close()
def changeIs(self, factoid, replacer):
db = self._getDb()
cursor = db.cursor()
cursor = self.db.cursor()
cursor.execute("""SELECT value FROM isFacts WHERE key=%s""", factoid)
if cursor.rowcount == 0:
raise dbi.NoRecordError
@ -287,42 +273,37 @@ class SqliteInfobotDB(object):
if replacer is not None:
cursor.execute("""UPDATE isFacts SET value=%s WHERE key=%s""",
replacer(old), factoid)
db.commit()
self.db.commit()
self._changes += 1
def getIs(self, factoid):
db = self._getDb()
cursor = db.cursor()
cursor = self.db.cursor()
cursor.execute("""SELECT value FROM isFacts WHERE key=%s""", factoid)
ret = cursor.fetchone()[0]
self._responses += 1
return ret
def setIs(self, fact, oid):
db = self._getDb()
cursor = db.cursor()
cursor = self.db.cursor()
cursor.execute("""INSERT INTO isFacts VALUES (%s, %s)""", fact, oid)
db.commit()
self.db.commit()
self._changes += 1
def delIs(self, factoid):
db = self._getDb()
cursor = db.cursor()
cursor = self.db.cursor()
cursor.execute("""DELETE FROM isFacts WHERE key=%s""", factoid)
if cursor.rowcount == 0:
raise dbi.NoRecordError
db.commit()
self.db.commit()
self._changes += 1
def hasIs(self, factoid):
db = self._getDb()
cursor = db.cursor()
cursor = self.db.cursor()
cursor.execute("""SELECT * FROM isFacts WHERE key=%s""", factoid)
return cursor.rowcount == 1
def changeAre(self, factoid, replacer):
db = self._getDb()
cursor = db.cursor()
cursor = self.db.cursor()
cursor.execute("""SELECT value FROM areFacts WHERE key=%s""", factoid)
if cursor.rowcount == 0:
raise dbi.NoRecordError
@ -330,36 +311,32 @@ class SqliteInfobotDB(object):
if replacer is not None:
cursor.execute("""UPDATE areFacts SET value=%s WHERE key=%s""",
replacer(old), factoid)
db.commit()
self.db.commit()
self._changes += 1
def getAre(self, factoid):
db = self._getDb()
cursor = db.cursor()
cursor = self.db.cursor()
cursor.execute("""SELECT value FROM areFacts WHERE key=%s""", factoid)
ret = cursor.fetchone()[0]
self._responses += 1
return ret
def setAre(self, fact, oid):
db = self._getDb()
cursor = db.cursor()
cursor = self.db.cursor()
cursor.execute("""INSERT INTO areFacts VALUES (%s, %s)""", fact, oid)
db.commit()
self.db.commit()
self._changes += 1
def delAre(self, factoid):
db = self._getDb()
cursor = db.cursor()
cursor = self.db.cursor()
cursor.execute("""DELETE FROM areFacts WHERE key=%s""", factoid)
if cursor.rowcount == 0:
raise dbi.NoRecordError
db.commit()
self.db.commit()
self._changes += 1
def hasAre(self, factoid):
db = self._getDb()
cursor = db.cursor()
cursor = self.db.cursor()
cursor.execute("""SELECT * FROM areFacts WHERE key=%s""", factoid)
return cursor.rowcount == 1
@ -376,16 +353,17 @@ class SqliteInfobotDB(object):
return self._responses
def getNumFacts(self):
db = self._getDb()
cursor = db.cursor()
cursor = self.db.cursor()
cursor.execute("""SELECT COUNT(*) FROM areFacts""")
areFacts = int(cursor.fetchone()[0])
cursor.execute("""SELECT COUNT(*) FROM isFacts""")
isFacts = int(cursor.fetchone()[0])
return areFacts + isFacts
def InfobotDB():
return SqliteInfobotDB()
InfobotDB = plugins.DB('Infobot',
{'sqlite': SqliteInfobotDB,
'pickle': PickleInfobotDB})
class Dunno(Exception):
pass
@ -397,7 +375,7 @@ class Infobot(callbacks.PrivmsgCommandAndRegexp):
try:
self.db = InfobotDB()
except Exception:
self.log.exception('Error loading %s:', filename)
self.log.exception('Error loading %s:', self.filename)
raise # So it doesn't get loaded without its database.
self.irc = None
self.msg = None

View File

@ -155,9 +155,10 @@ class DbiNoteDB(dbi.DB):
return id
def NoteDB():
# XXX This should eventually be smarter.
return DbiNoteDB(conf.supybot.directories.data.dirize('Note.db'))
NoteDB = plugins.DB('Note', {'flat': DbiNoteDB})
## def NoteDB():
## # XXX This should eventually be smarter.
## return DbiNoteDB(conf.supybot.directories.data.dirize('Note.db'))
class Note(callbacks.Privmsg):

View File

@ -55,14 +55,22 @@ import supybot.ircutils as ircutils
import supybot.webutils as webutils
try:
# We need to sweep away all that mx.* crap because our code doesn't account
# for PySQLite's arbitrary use of it. Whoever decided to change sqlite's
# behavior based on whether or not that module is installed was a *CRACK*
# **FIEND**, plain and simple.
mxCrap = {}
for (name, module) in sys.modules.items():
if name.startswith('mx'):
mxCrap[name] = module
sys.modules[name] = None
# Now that the mx crap is gone, we can import sqlite.
import sqlite
# And now we'll put it back, even though it sucks.
for (name, module) in mxCrap.items():
sys.modules[name] = module
# Just in case, we'll do this as well. It doesn't seem to work fine by
# itself, though, or else we'd just do this in the first place.
sqlite.have_datetime = False
Connection = sqlite.Connection
class MyConnection(sqlite.Connection):
@ -75,6 +83,30 @@ try:
except ImportError:
pass
class NoSuitableDatabase(Exception):
def __init__(self, suitable):
self.suitable = suitable
self.suitable.sort()
def __str__(self):
return 'No suitable databases were found. Suitable databases ' \
'include %s.' % utils.commaAndify(self.suitable)
def DB(filename, types):
filename = conf.supybot.directories.data.dirize(filename)
def MakeDB(*args, **kwargs):
for type in conf.supybot.databases():
# Can't do this because Python sucks. Go ahead, try it!
# filename = '.'.join([filename, type, 'db'])
fn = '.'.join([filename, type, 'db'])
try:
return types[type](fn, *args, **kwargs)
except KeyError:
continue
raise NoSuitableDatabase, types.keys()
return MakeDB
class DBHandler(object):
def __init__(self, name=None, suffix='.db'):
if name is None:

View File

@ -627,6 +627,7 @@ registerGlobalValue(supybot.directories.data, 'tmp',
# Remember, we're *meant* to replace this nice little wrapper.
def transactionalFile(*args, **kwargs):
kwargs['tmpDir'] = supybot.directories.data.tmp()
# ??? Should we offer an option not to makeBackupIfSmaller?
return utils.AtomicFile(*args, **kwargs)
utils.transactionalFile = transactionalFile
@ -658,7 +659,20 @@ registerGlobalValue(supybot.plugins, 'alwaysLoadDefault',
###
# supybot.databases. For stuff relating to Supybot's databases (duh!)
###
registerGroup(supybot, 'databases')
class Databases(registry.SpaceSeparatedListOfStrings):
def __call__(self):
v = super(Databases, self).__call__()
if not v:
v = ['flat', 'cdb', 'pickle']
if 'sqlite' in sys.modules:
v.insert(0, 'sqlite')
return v
registerGlobalValue(supybot, 'databases',
Databases([], """Determines what databases are available for use. If this
value is not configured (that is, if its value is empty) then sane defaults
will be provided."""))
registerGroup(supybot.databases, 'users')
registerGlobalValue(supybot.databases.users, 'filename',
registry.String('users.conf', """Determines what filename will be used for
@ -691,6 +705,8 @@ registerChannelValue(supybot.databases.plugins, 'channelSpecific',
can be channel-specific will be so. This can be overridden by individual
channels."""))
# XXX Configuration variables for dbi, sqlite, flat, mysql, etc.
###
# Protocol information.
###