home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / usr / share / python-support / python-rdflib / rdflib / store / Sleepycat.py < prev    next >
Encoding:
Python Source  |  2007-04-04  |  16.5 KB  |  483 lines

  1. from rdflib.store import Store
  2. from rdflib.URIRef import URIRef
  3.  
  4. from bsddb import db
  5.  
  6. from os import mkdir
  7. from os.path import exists, abspath
  8. from urllib import pathname2url
  9. from threading import Thread
  10.  
  11. import logging
  12. _logger = logging.getLogger(__name__)
  13.  
  14. class Sleepycat(Store):
  15.     context_aware = True
  16.     formula_aware = True
  17.  
  18.     def __init__(self, configuration=None, identifier=None):
  19.         self.__open = False
  20.         self.__identifier = identifier
  21.         super(Sleepycat, self).__init__(configuration)
  22.         self.configuration = configuration
  23.         self._loads = self.node_pickler.loads
  24.         self._dumps = self.node_pickler.dumps
  25.  
  26.     def __get_identifier(self):
  27.         return self.__identifier
  28.     identifier = property(__get_identifier)
  29.  
  30.     def open(self, path, create=True):
  31.         homeDir = path
  32.         envsetflags  = db.DB_CDB_ALLDB
  33.         envflags = db.DB_INIT_MPOOL | db.DB_INIT_CDB | db.DB_THREAD
  34.         if not exists(homeDir):
  35.             if create==True:
  36.                 mkdir(homeDir) # TODO: implement create method and refactor this to it
  37.                 self.create(path)
  38.             else:
  39.                 return -1
  40.         if self.__identifier is None:
  41.             self.__identifier = URIRef(pathname2url(abspath(homeDir)))
  42.         self.db_env = db_env = db.DBEnv()
  43.         db_env.set_cachesize(0, 1024*1024*50) # TODO
  44.         #db_env.set_lg_max(1024*1024)
  45.         db_env.set_flags(envsetflags, 1)
  46.         db_env.open(homeDir, envflags | db.DB_CREATE)
  47.  
  48.         self.__open = True
  49.  
  50.         dbname = None
  51.         dbtype = db.DB_BTREE
  52.         dbopenflags = db.DB_THREAD
  53.  
  54.         dbmode = 0660
  55.         dbsetflags   = 0
  56.  
  57.         # create and open the DBs
  58.         self.__indicies = [None,] * 3
  59.         self.__indicies_info = [None,] * 3
  60.         for i in xrange(0, 3):
  61.             index_name = to_key_func(i)(("s", "p", "o"), "c")
  62.             index = db.DB(db_env)
  63.             index.set_flags(dbsetflags)
  64.             index.open(index_name, dbname, dbtype, dbopenflags|db.DB_CREATE, dbmode)
  65.             self.__indicies[i] = index
  66.             self.__indicies_info[i] = (index, to_key_func(i), from_key_func(i))
  67.  
  68.         lookup = {}
  69.         for i in xrange(0, 8):
  70.             results = []
  71.             for start in xrange(0, 3):
  72.                 score = 1
  73.                 len = 0
  74.                 for j in xrange(start, start+3):
  75.                     if i & (1<<(j%3)):
  76.                         score = score << 1
  77.                         len += 1
  78.                     else:
  79.                         break
  80.                 tie_break = 2-start
  81.                 results.append(((score, tie_break), start, len))
  82.  
  83.             results.sort()
  84.             score, start, len = results[-1]
  85.  
  86.             def get_prefix_func(start, end):
  87.                 def get_prefix(triple, context):
  88.                     if context is None:
  89.                         yield ""
  90.                     else:
  91.                         yield context
  92.                     i = start
  93.                     while i<end:
  94.                         yield triple[i%3]
  95.                         i += 1
  96.                     yield ""
  97.                 return get_prefix
  98.  
  99.             lookup[i] = (self.__indicies[start], get_prefix_func(start, start + len), from_key_func(start), results_from_key_func(start, self._from_string))
  100.  
  101.  
  102.         self.__lookup_dict = lookup
  103.  
  104.         self.__contexts = db.DB(db_env)
  105.         self.__contexts.set_flags(dbsetflags)
  106.         self.__contexts.open("contexts", dbname, dbtype, dbopenflags|db.DB_CREATE, dbmode)
  107.  
  108.         self.__namespace = db.DB(db_env)
  109.         self.__namespace.set_flags(dbsetflags)
  110.         self.__namespace.open("namespace", dbname, dbtype, dbopenflags|db.DB_CREATE, dbmode)
  111.  
  112.         self.__prefix = db.DB(db_env)
  113.         self.__prefix.set_flags(dbsetflags)
  114.         self.__prefix.open("prefix", dbname, dbtype, dbopenflags|db.DB_CREATE, dbmode)
  115.  
  116.         self.__k2i = db.DB(db_env)
  117.         self.__k2i.set_flags(dbsetflags)
  118.         self.__k2i.open("k2i", dbname, db.DB_HASH, dbopenflags|db.DB_CREATE, dbmode)
  119.  
  120.         self.__i2k = db.DB(db_env)
  121.         self.__i2k.set_flags(dbsetflags)
  122.         self.__i2k.open("i2k", dbname, db.DB_RECNO, dbopenflags|db.DB_CREATE, dbmode)
  123.  
  124.         self.__needs_sync = False
  125.         t = Thread(target=self.__sync_run)
  126.         t.setDaemon(True)
  127.         t.start()
  128.         self.__sync_thread = t
  129.         return 1
  130.  
  131.  
  132.     def __sync_run(self):
  133.         from time import sleep, time
  134.         try:
  135.             min_seconds, max_seconds = 10, 300
  136.             while self.__open:
  137.                 if self.__needs_sync:
  138.                     t0 = t1 = time()
  139.                     self.__needs_sync = False
  140.                     while self.__open:
  141.                         sleep(.1)
  142.                         if self.__needs_sync:
  143.                             t1 = time()
  144.                             self.__needs_sync = False
  145.                         if time()-t1 > min_seconds or time()-t0 > max_seconds:
  146.                             self.__needs_sync = False
  147.                             _logger.debug("sync")
  148.                             self.sync()
  149.                             break
  150.                 else:
  151.                     sleep(1)
  152.         except Exception, e:
  153.             _logger.exception(e)
  154.  
  155.     def sync(self):
  156.         if self.__open:
  157.             for i in self.__indicies:
  158.                 i.sync()
  159.             self.__contexts.sync()
  160.             self.__namespace.sync()
  161.             self.__prefix.sync()
  162.             self.__i2k.sync()
  163.             self.__k2i.sync()
  164.  
  165.     def close(self, commit_pending_transaction=False):
  166.         self.__open = False
  167.         self.__sync_thread.join()
  168.         for i in self.__indicies:
  169.             i.close()
  170.         self.__contexts.close()
  171.         self.__namespace.close()
  172.         self.__prefix.close()
  173.         self.__i2k.close()
  174.         self.__k2i.close()
  175.         self.db_env.close()
  176.  
  177.     def add(self, (subject, predicate, object), context, quoted=False):
  178.         """\
  179.         Add a triple to the store of triples.
  180.         """
  181.         assert self.__open, "The Store must be open."
  182.         assert context!=self, "Can not add triple directly to store"
  183.         Store.add(self, (subject, predicate, object), context, quoted)
  184.  
  185.         _to_string = self._to_string
  186.  
  187.         s = _to_string(subject)
  188.         p = _to_string(predicate)
  189.         o = _to_string(object)
  190.         c = _to_string(context)
  191.  
  192.         cspo, cpos, cosp = self.__indicies
  193.  
  194.         value = cspo.get("%s^%s^%s^%s^" % (c, s, p, o))
  195.         if value is None:
  196.             self.__contexts.put(c, "")
  197.  
  198.             contexts_value = cspo.get("%s^%s^%s^%s^" % ("", s, p, o)) or ""
  199.             contexts = set(contexts_value.split("^"))
  200.             contexts.add(c)
  201.             contexts_value = "^".join(contexts)
  202.             assert contexts_value!=None
  203.  
  204.             cspo.put("%s^%s^%s^%s^" % (c, s, p, o), "")
  205.             cpos.put("%s^%s^%s^%s^" % (c, p, o, s), "")
  206.             cosp.put("%s^%s^%s^%s^" % (c, o, s, p), "")
  207.             if not quoted:
  208.                 cspo.put("%s^%s^%s^%s^" % ("", s, p, o), contexts_value)
  209.                 cpos.put("%s^%s^%s^%s^" % ("", p, o, s), contexts_value)
  210.                 cosp.put("%s^%s^%s^%s^" % ("", o, s, p), contexts_value)
  211.  
  212.             self.__needs_sync = True
  213.  
  214.     def __remove(self, (s, p, o), c, quoted=False):
  215.         cspo, cpos, cosp = self.__indicies
  216.         contexts_value = cspo.get("^".join(("", s, p, o, ""))) or ""
  217.         contexts = set(contexts_value.split("^"))
  218.         contexts.discard(c)
  219.         contexts_value = "^".join(contexts)
  220.         for i, _to_key, _from_key in self.__indicies_info:
  221.             i.delete(_to_key((s, p, o), c))
  222.         if not quoted:
  223.             if contexts_value:
  224.                 for i, _to_key, _from_key in self.__indicies_info:
  225.                     i.put(_to_key((s, p, o), ""), contexts_value)
  226.             else:
  227.                 for i, _to_key, _from_key in self.__indicies_info:
  228.                     try:
  229.                         i.delete(_to_key((s, p, o), ""))
  230.                     except db.DBNotFoundError, e: 
  231.                         pass # TODO: is it okay to ignore these?
  232.  
  233.     def remove(self, (subject, predicate, object), context):
  234.         assert self.__open, "The Store must be open."
  235.         Store.remove(self, (subject, predicate, object), context)
  236.         _to_string = self._to_string
  237.         if context is not None:
  238.             if context == self:
  239.                 context = None
  240.  
  241.         if subject is not None and predicate is not None and object is not None and context is not None:
  242.             s = _to_string(subject)
  243.             p = _to_string(predicate)
  244.             o = _to_string(object)
  245.             c = _to_string(context)
  246.             value = self.__indicies[0].get("%s^%s^%s^%s^" % (c, s, p, o))
  247.             if value is not None:
  248.                 self.__remove((s, p, o), c)
  249.                 self.__needs_sync = True
  250.         else:
  251.             cspo, cpos, cosp = self.__indicies
  252.             index, prefix, from_key, results_from_key = self.__lookup((subject, predicate, object), context)
  253.  
  254.             cursor = index.cursor()
  255.             try:
  256.                 current = cursor.set_range(prefix)
  257.                 needs_sync = True
  258.             except db.DBNotFoundError:
  259.                 current = None
  260.                 needs_sync = False
  261.             cursor.close()
  262.             while current:
  263.                 key, value = current
  264.                 cursor = index.cursor()
  265.                 try:
  266.                     cursor.set_range(key)
  267.                     current = cursor.next()
  268.                 except db.DBNotFoundError:
  269.                     current = None
  270.                 cursor.close()
  271.                 if key.startswith(prefix):
  272.                     c, s, p, o = from_key(key)
  273.                     if context is None:
  274.                         contexts_value = index.get(key) or ""
  275.                         contexts = set(contexts_value.split("^")) # remove triple from all non quoted contexts
  276.                         contexts.add("") # and from the conjunctive index
  277.                         for c in contexts:
  278.                             for i, _to_key, _ in self.__indicies_info:
  279.                                 i.delete(_to_key((s, p, o), c))
  280.                     else:
  281.                         self.__remove((s, p, o), c)
  282.                 else:
  283.                     break
  284.  
  285.             if context is not None:
  286.                 if subject is None and predicate is None and object is None:
  287.                     # TODO: also if context becomes empty and not just on remove((None, None, None), c)
  288.                     try:
  289.                         self.__contexts.delete(_to_string(context))
  290.                     except db.DBNotFoundError, e:
  291.                         pass
  292.  
  293.             self.__needs_sync = needs_sync
  294.  
  295.     def triples(self, (subject, predicate, object), context=None):
  296.         """A generator over all the triples matching """
  297.         assert self.__open, "The Store must be open."
  298.  
  299.         if context is not None:
  300.             if context == self:
  301.                 context = None
  302.  
  303.         _from_string = self._from_string
  304.         index, prefix, from_key, results_from_key = self.__lookup((subject, predicate, object), context)
  305.  
  306.         cursor = index.cursor()
  307.         try:
  308.             current = cursor.set_range(prefix)
  309.         except db.DBNotFoundError:
  310.             current = None
  311.         cursor.close()
  312.         while current:
  313.             key, value = current
  314.             cursor = index.cursor()
  315.             try:
  316.                 cursor.set_range(key)
  317.                 current = cursor.next()
  318.             except db.DBNotFoundError:
  319.                 current = None
  320.             cursor.close()
  321.             if key and key.startswith(prefix):
  322.                 contexts_value = index.get(key)
  323.                 yield results_from_key(key, subject, predicate, object, contexts_value)
  324.             else:
  325.                 break
  326.  
  327.     def __len__(self, context=None):
  328.         assert self.__open, "The Store must be open."
  329.         if context is not None:
  330.             if context == self:
  331.                 context = None
  332.  
  333.         if context is None:
  334.             prefix = "^"
  335.         else:
  336.             prefix = "%s^" % self._to_string(context)
  337.  
  338.         index = self.__indicies[0]
  339.         cursor = index.cursor()
  340.         current = cursor.set_range(prefix)
  341.         count = 0
  342.         while current:
  343.             key, value = current
  344.             if key.startswith(prefix):
  345.                 count +=1
  346.                 current = cursor.next()
  347.             else:
  348.                 break
  349.         cursor.close()
  350.         return count
  351.  
  352.     def bind(self, prefix, namespace):
  353.         prefix = prefix.encode("utf-8")
  354.         namespace = namespace.encode("utf-8")
  355.         bound_prefix = self.__prefix.get(namespace)
  356.         if bound_prefix:
  357.             self.__namespace.delete(bound_prefix)
  358.         self.__prefix[namespace] = prefix
  359.         self.__namespace[prefix] = namespace
  360.  
  361.     def namespace(self, prefix):
  362.         prefix = prefix.encode("utf-8")
  363.         return self.__namespace.get(prefix, None)
  364.  
  365.     def prefix(self, namespace):
  366.         namespace = namespace.encode("utf-8")
  367.         return self.__prefix.get(namespace, None)
  368.  
  369.     def namespaces(self):
  370.         cursor = self.__namespace.cursor()
  371.         results = []
  372.         current = cursor.first()
  373.         while current:
  374.             prefix, namespace = current
  375.             results.append((prefix, namespace))
  376.             current = cursor.next()
  377.         cursor.close()
  378.         for prefix, namespace in results:
  379.             yield prefix, URIRef(namespace)
  380.  
  381.     def contexts(self, triple=None):
  382.         _from_string = self._from_string
  383.         _to_string = self._to_string
  384.  
  385.         if triple:
  386.             s, p, o = triple
  387.             s = _to_string(s)
  388.             p = _to_string(p)
  389.             o = _to_string(o)
  390.             contexts = self.__indicies[0].get("%s^%s^%s^%s^" % ("", s, p, o))
  391.             if contexts:
  392.                 for c in contexts.split("^"):
  393.                     if c:
  394.                         yield _from_string(c)
  395.         else:
  396.             index = self.__contexts
  397.             cursor = index.cursor()
  398.             current = cursor.first()
  399.             cursor.close()
  400.             while current:
  401.                 key, value = current
  402.                 context = _from_string(key)
  403.                 yield context
  404.                 cursor = index.cursor()
  405.                 try:
  406.                     cursor.set_range(key)
  407.                     current = cursor.next()
  408.                 except db.DBNotFoundError:
  409.                     current = None
  410.                 cursor.close()
  411.  
  412.     def _from_string(self, i):
  413.         k = self.__i2k.get(int(i))
  414.         return self._loads(k)
  415.  
  416.     def _to_string(self, term):
  417.         k = self._dumps(term)
  418.         i = self.__k2i.get(k)
  419.         if i is None:
  420.             i = "%s" % self.__i2k.append(k)
  421.             self.__k2i.put(k, i)
  422.         return i
  423.  
  424.     def __lookup(self, (subject, predicate, object), context):
  425.         _to_string = self._to_string
  426.         if context is not None:
  427.             context = _to_string(context)
  428.         i = 0
  429.         if subject is not None:
  430.             i += 1
  431.             subject = _to_string(subject)
  432.         if predicate is not None:
  433.             i += 2
  434.             predicate = _to_string(predicate)
  435.         if object is not None:
  436.             i += 4
  437.             object = _to_string(object)
  438.         index, prefix_func, from_key, results_from_key = self.__lookup_dict[i]
  439.         prefix = "^".join(prefix_func((subject, predicate, object), context))
  440.         return index, prefix, from_key, results_from_key
  441.  
  442.  
  443. def to_key_func(i):
  444.     def to_key(triple, context):
  445.         "Takes a string; returns key"
  446.         return "^".join((context, triple[i%3], triple[(i+1)%3], triple[(i+2)%3], "")) # "" to tac on the trailing ^
  447.     return to_key
  448.  
  449. def from_key_func(i):
  450.     def from_key(key):
  451.         "Takes a key; returns string"
  452.         parts = key.split("^")
  453.         return parts[0], parts[(3-i+0)%3+1], parts[(3-i+1)%3+1], parts[(3-i+2)%3+1]
  454.     return from_key
  455.  
  456. def results_from_key_func(i, from_string):
  457.     def from_key(key, subject, predicate, object, contexts_value):
  458.         "Takes a key and subject, predicate, object; returns tuple for yield"
  459.         parts = key.split("^")
  460.         if subject is None:
  461.             # TODO: i & 1: # dis assemble and/or measure to see which is faster
  462.             # subject is None or i & 1
  463.             s = from_string(parts[(3-i+0)%3+1])
  464.         else:
  465.             s = subject
  466.         if predicate is None:#i & 2:
  467.             p = from_string(parts[(3-i+1)%3+1])
  468.         else:
  469.             p = predicate
  470.         if object is None:#i & 4:
  471.             o = from_string(parts[(3-i+2)%3+1])
  472.         else:
  473.             o = object
  474.         return (s, p, o), (from_string(c) for c in contexts_value.split("^") if c)
  475.     return from_key
  476.  
  477. def readable_index(i):
  478.     s, p, o = "?" * 3
  479.     if i & 1: s = "s"
  480.     if i & 2: p = "p"
  481.     if i & 4: o = "o"
  482.     return "%s,%s,%s" % (s, p, o)
  483.