+"""An sqlite database for storing nodes and key/value pairs."""
+
from datetime import datetime, timedelta
from pysqlite2 import dbapi2 as sqlite
from binascii import a2b_base64, b2a_base64
class dht_value(str):
"""Dummy class to convert all DHT values to base64 for storing in the DB."""
-
+
+# Initialize the database to work with 'khash' objects (binary strings)
sqlite.register_adapter(khash, b2a_base64)
sqlite.register_converter("KHASH", a2b_base64)
sqlite.register_converter("khash", a2b_base64)
+
+# Initialize the database to work with DHT values (binary strings)
sqlite.register_adapter(dht_value, b2a_base64)
sqlite.register_converter("DHT_VALUE", a2b_base64)
sqlite.register_converter("dht_value", a2b_base64)
class DB:
- """Database access for storing persistent data."""
+ """An sqlite database for storing persistent node info and key/value pairs.
+
+ @type db: C{string}
+ @ivar db: the database file to use
+ @type conn: L{pysqlite2.dbapi2.Connection}
+ @ivar conn: an open connection to the sqlite database
+ """
def __init__(self, db):
+ """Load or create the database file.
+
+ @type db: C{string}
+ @param db: the database file to use
+ """
self.db = db
try:
os.stat(db)
sqlite.register_converter("text", str)
else:
self.conn.text_factory = str
-
+
+ #{ Loading the DB
def _loadDB(self, db):
+ """Open a new connection to the existing database file"""
try:
self.conn = sqlite.connect(database=db, detect_types=sqlite.PARSE_DECLTYPES)
except:
raise DBExcept, "Couldn't open DB", traceback.format_exc()
def _createNewDB(self, db):
+ """Open a connection to a new database and create the necessary tables."""
self.conn = sqlite.connect(database=db, detect_types=sqlite.PARSE_DECLTYPES)
c = self.conn.cursor()
- c.execute("CREATE TABLE kv (key KHASH, value DHT_VALUE, last_refresh TIMESTAMP, PRIMARY KEY (key, value))")
+ c.execute("CREATE TABLE kv (key KHASH, value DHT_VALUE, last_refresh TIMESTAMP, "+
+ "PRIMARY KEY (key, value))")
c.execute("CREATE INDEX kv_key ON kv(key)")
c.execute("CREATE INDEX kv_last_refresh ON kv(last_refresh)")
c.execute("CREATE TABLE nodes (id KHASH PRIMARY KEY, host TEXT, port NUMBER)")
c.execute("CREATE TABLE self (num NUMBER PRIMARY KEY, id KHASH)")
self.conn.commit()
+ def close(self):
+ self.conn.close()
+
+ #{ This node's ID
def getSelfNode(self):
+ """Retrieve this node's ID from a previous run of the program."""
c = self.conn.cursor()
c.execute('SELECT id FROM self WHERE num = 0')
id = c.fetchone()
return None
def saveSelfNode(self, id):
+ """Store this node's ID for a subsequent run of the program."""
c = self.conn.cursor()
c.execute("INSERT OR REPLACE INTO self VALUES (0, ?)", (khash(id),))
self.conn.commit()
+ #{ Routing table
def dumpRoutingTable(self, buckets):
- """
- save routing table nodes to the database
- """
+ """Save routing table nodes to the database."""
c = self.conn.cursor()
c.execute("DELETE FROM nodes WHERE id NOT NULL")
for bucket in buckets:
self.conn.commit()
def getRoutingTable(self):
- """
- load routing table nodes from database
- it's usually a good idea to call refreshTable(force=1) after loading the table
+ """Load routing table nodes from database.
+
+ It's usually a good idea to call refreshTable(force=1) after loading the table.
"""
c = self.conn.cursor()
c.execute("SELECT * FROM nodes")
return c.fetchall()
-
+
+ #{ Key/value pairs
def retrieveValues(self, key):
"""Retrieve values from the database."""
c = self.conn.cursor()
c.execute("DELETE FROM kv WHERE last_refresh < ?", (t, ))
self.conn.commit()
- def close(self):
- self.conn.close()
-
class TestDB(unittest.TestCase):
"""Tests for the khashmir database."""