comparison src/memory/sqlite.py @ 2182:087eec4c6c07

memory (persistent, sqlite): better private values handling + new LazyPersistentBinaryDict: - merged private values method handling in sqlite, and added a keys arguments to get only some keys - LazyPersistentBinaryDict allow to get values in database only when they are needed, saving memory for big data
author Goffi <goffi@goffi.org>
date Sun, 12 Mar 2017 19:33:17 +0100
parents 1d3f73e065e1
children ea41cf1e6d29
comparison
equal deleted inserted replaced
2181:968b0d13bcc7 2182:087eec4c6c07
518 d.addCallback(self.sqliteHistoryToList) 518 d.addCallback(self.sqliteHistoryToList)
519 d.addCallback(self.listDict2listTuple) 519 d.addCallback(self.listDict2listTuple)
520 return d 520 return d
521 521
522 #Private values 522 #Private values
523 def loadGenPrivates(self, private_gen, namespace): 523
524 """Load general private values 524 def _privateDataEb(self, failure_, operation, namespace, key=None, profile=None):
525 525 """generic errback for data queries"""
526 @param private_gen: dictionary to fill 526 log.error(_(u"Can't {operation} data in database for namespace {namespace}{and_key}{for_profile}: {msg}").format(
527 @param namespace: namespace of the values 527 operation = operation,
528 @return: deferred 528 namespace = namespace,
529 """ 529 and_key = (u" and key " + key) if key is not None else u"",
530 530 for_profile = (u' [' + profile + u']') if profile is not None else u'',
531 def fillPrivates(result): 531 msg = failure_))
532 for private in result: 532
533 key, value = private 533 def _generateDataDict(self, query_result, binary):
534 private_gen[key] = value 534 if binary:
535 log.debug(_(u"loading general private values [namespace: %s] from database") % (namespace,)) 535 return {k: pickle.loads(str(v)) for k,v in query_result}
536 d = self.dbpool.runQuery("SELECT key,value FROM private_gen WHERE namespace=?", (namespace, )).addCallback(fillPrivates) 536 else:
537 return d.addErrback(lambda x: log.debug(_(u"No data present in database for namespace %s") % namespace)) 537 return dict(query_result)
538 538
539 def loadIndPrivates(self, private_ind, namespace, profile): 539 def _getPrivateTable(self, binary, profile):
540 """Load individual private values 540 """Get table to use for private values"""
541 541 table = [u'private']
542 @param privates_ind: dictionary to fill 542
543 @param namespace: namespace of the values 543 if profile is None:
544 @param profile: a profile which *must* exist 544 table.append(u'gen')
545 @return: deferred 545 else:
546 """ 546 table.append(u'ind')
547 547
548 def fillPrivates(result): 548 if binary:
549 for private in result: 549 table.append(u'bin')
550 key, value = private 550
551 private_ind[key] = value 551 return u'_'.join(table)
552 log.debug(_(u"loading individual private values [namespace: %s] from database") % (namespace,)) 552
553 d = self.dbpool.runQuery("SELECT key,value FROM private_ind WHERE namespace=? AND profile_id=?", (namespace, self.profiles[profile])) 553 def getPrivates(self, namespace, keys=None, binary=False, profile=None):
554 d.addCallback(fillPrivates) 554 """Get private value(s) from databases
555 return d.addErrback(lambda x: log.debug(_(u"No data present in database for namespace %s") % namespace)) 555
556 556 @param namespace(unicode): namespace of the values
557 def setGenPrivate(self, namespace, key, value): 557 @param keys(iterable, None): keys of the values to get
558 """Save the general private value in database 558 None to get all keys/values
559 @param binary(bool): True to deserialise binary values
560 @param profile(unicode, None): profile to use for individual values
561 None to use general values
562 @return (dict[unicode, object]): gotten keys/values
563 """
564 log.debug(_(u"getting {type}{binary} private values from database for namespace {namespace}{keys}".format(
565 type = u"general" if profile is None else "individual",
566 binary = u" binary" if binary else u"",
567 namespace = namespace,
568 keys = u" with keys {}".format(u", ".join(keys)) if keys is not None else u"")))
569 table = self._getPrivateTable(binary, profile)
570 query_parts = [u"SELECT key,value FROM", table, "WHERE namespace=?"]
571 args = [namespace]
572
573 if keys is not None:
574 query_parts.append(u'AND key IN ?')
575 args.append(keys)
576
577 if profile is not None:
578 query_parts.append(u'AND profile_id=?')
579 args.append(self.profiles[profile])
580
581 d = self.dbpool.runQuery(u" ".join(query_parts), args)
582 d.addCallback(self._generateDataDict, binary)
583 d.addErrback(self._privateDataEb, u"get", namespace, profile=profile)
584 return d
585
586 def setPrivateValue(self, namespace, key, value, binary=False, profile=None):
587 """Set a private value in database
588
589 @param namespace(unicode): namespace of the values
590 @param key(unicode): key of the value to set
591 @param value(object): value to set
592 @param binary(bool): True if it's a binary values
593 binary values need to be serialised, used for everything but strings
594 @param profile(unicode, None): profile to use for individual value
595 if None, it's a general value
596 """
597 table = self._getPrivateTable(binary, profile)
598 query_values_names = [u'namespace', u'key', u'value']
599 query_values = [namespace, key]
600
601 if binary:
602 value = sqlite3.Binary(pickle.dumps(value, 0))
603
604 query_values.append(value)
605
606 if profile is not None:
607 query_values_names.append(u'profile_id')
608 query_values.append(self.profiles[profile])
609
610 query_parts = [u"REPLACE INTO", table, u'(', u','.join(query_values_names), u')',
611 u"VALUES (", u",".join(u'?'*len(query_values_names)), u')']
612
613 d = self.dbpool.runQuery(u" ".join(query_parts), query_values)
614 d.addErrback(self._privateDataEb, u"set", namespace, key, profile=profile)
615 return d
616
617 def delPrivateValue(self, namespace, key, binary=False, profile=None):
618 """Delete private value from database
559 619
560 @param category: category of the privateeter 620 @param category: category of the privateeter
561 @param key: key of the private value 621 @param key: key of the private value
562 @param value: value to set 622 @param binary(bool): True if it's a binary values
563 @return: deferred 623 @param profile(unicode, None): profile to use for individual value
564 """ 624 if None, it's a general value
565 d = self.dbpool.runQuery("REPLACE INTO private_gen(namespace,key,value) VALUES (?,?,?)", (namespace, key, value)) 625 """
566 d.addErrback(lambda ignore: log.error(_(u"Can't set general private value (%(key)s) [namespace:%(namespace)s] in database" % 626 table = self._getPrivateTable(binary, profile)
567 {"namespace": namespace, "key": key}))) 627 query_parts = [u"DELETE FROM", table, u"WHERE namespace=? AND key=?"]
568 return d 628 args = [namespace, key]
569 629 if profile is not None:
570 def setIndPrivate(self, namespace, key, value, profile): 630 query_parts.append(u"AND profile_id=?")
571 """Save the individual private value in database 631 args.append(self.profiles[profile])
572 632 d = self.dbpool.runQuery(u" ".join(query_parts), args)
573 @param namespace: namespace of the value 633 d.addErrback(self._privateDataEb, u"delete", namespace, key, profile=profile)
574 @param key: key of the private value 634 return d
575 @param value: value to set 635
576 @param profile: a profile which *must* exist
577 @return: deferred
578 """
579 d = self.dbpool.runQuery("REPLACE INTO private_ind(namespace,key,profile_id,value) VALUES (?,?,?,?)", (namespace, key, self.profiles[profile], value))
580 d.addErrback(lambda ignore: log.error(_(u"Can't set individual private value (%(key)s) [namespace: %(namespace)s] for [%(profile)s] in database" %
581 {"namespace": namespace, "key": key, "profile": profile})))
582 return d
583
584 def delGenPrivate(self, namespace, key):
585 """Delete the general private value from database
586
587 @param category: category of the privateeter
588 @param key: key of the private value
589 @return: deferred
590 """
591 d = self.dbpool.runQuery("DELETE FROM private_gen WHERE namespace=? AND key=?", (namespace, key))
592 d.addErrback(lambda ignore: log.error(_(u"Can't delete general private value (%(key)s) [namespace:%(namespace)s] in database" %
593 {"namespace": namespace, "key": key})))
594 return d
595
596 def delIndPrivate(self, namespace, key, profile):
597 """Delete the individual private value from database
598
599 @param namespace: namespace of the value
600 @param key: key of the private value
601 @param profile: a profile which *must* exist
602 @return: deferred
603 """
604 d = self.dbpool.runQuery("DELETE FROM private_ind WHERE namespace=? AND key=? AND profile=?)", (namespace, key, self.profiles[profile]))
605 d.addErrback(lambda ignore: log.error(_(u"Can't delete individual private value (%(key)s) [namespace: %(namespace)s] for [%(profile)s] in database" %
606 {"namespace": namespace, "key": key, "profile": profile})))
607 return d
608
609 def loadGenPrivatesBinary(self, private_gen, namespace):
610 """Load general private binary values
611
612 @param private_gen: dictionary to fill
613 @param namespace: namespace of the values
614 @return: deferred
615 """
616
617 def fillPrivates(result):
618 for private in result:
619 key, value = private
620 private_gen[key] = pickle.loads(str(value))
621 log.debug(_(u"loading general private binary values [namespace: %s] from database") % (namespace,))
622 d = self.dbpool.runQuery("SELECT key,value FROM private_gen_bin WHERE namespace=?", (namespace, )).addCallback(fillPrivates)
623 return d.addErrback(lambda x: log.debug(_(u"No binary data present in database for namespace %s") % namespace))
624
625 def loadIndPrivatesBinary(self, private_ind, namespace, profile):
626 """Load individual private binary values
627
628 @param privates_ind: dictionary to fill
629 @param namespace: namespace of the values
630 @param profile: a profile which *must* exist
631 @return: deferred
632 """
633
634 def fillPrivates(result):
635 for private in result:
636 key, value = private
637 private_ind[key] = pickle.loads(str(value))
638 log.debug(_(u"loading individual private binary values [namespace: %s] from database") % (namespace,))
639 d = self.dbpool.runQuery("SELECT key,value FROM private_ind_bin WHERE namespace=? AND profile_id=?", (namespace, self.profiles[profile]))
640 d.addCallback(fillPrivates)
641 return d.addErrback(lambda x: log.debug(_(u"No binary data present in database for namespace %s") % namespace))
642
643 def setGenPrivateBinary(self, namespace, key, value):
644 """Save the general private binary value in database
645
646 @param category: category of the privateeter
647 @param key: key of the private value
648 @param value: value to set
649 @return: deferred
650 """
651 d = self.dbpool.runQuery("REPLACE INTO private_gen_bin(namespace,key,value) VALUES (?,?,?)", (namespace, key, sqlite3.Binary(pickle.dumps(value, 0))))
652 d.addErrback(lambda ignore: log.error(_(u"Can't set general private binary value (%(key)s) [namespace:%(namespace)s] in database" %
653 {"namespace": namespace, "key": key})))
654 return d
655
656 def setIndPrivateBinary(self, namespace, key, value, profile):
657 """Save the individual private binary value in database
658
659 @param namespace: namespace of the value
660 @param key: key of the private value
661 @param value: value to set
662 @param profile: a profile which *must* exist
663 @return: deferred
664 """
665 d = self.dbpool.runQuery("REPLACE INTO private_ind_bin(namespace,key,profile_id,value) VALUES (?,?,?,?)", (namespace, key, self.profiles[profile], sqlite3.Binary(pickle.dumps(value, 0))))
666 d.addErrback(lambda ignore: log.error(_(u"Can't set individual binary private value (%(key)s) [namespace: %(namespace)s] for [%(profile)s] in database" %
667 {"namespace": namespace, "key": key, "profile": profile})))
668 return d
669
670 def delGenPrivateBinary(self, namespace, key):
671 """Delete the general private binary value from database
672
673 @param category: category of the privateeter
674 @param key: key of the private value
675 @return: deferred
676 """
677 d = self.dbpool.runQuery("DELETE FROM private_gen_bin WHERE namespace=? AND key=?", (namespace, key))
678 d.addErrback(lambda ignore: log.error(_(u"Can't delete general private binary value (%(key)s) [namespace:%(namespace)s] in database" %
679 {"namespace": namespace, "key": key})))
680 return d
681
682 def delIndPrivateBinary(self, namespace, key, profile):
683 """Delete the individual private binary value from database
684
685 @param namespace: namespace of the value
686 @param key: key of the private value
687 @param profile: a profile which *must* exist
688 @return: deferred
689 """
690 d = self.dbpool.runQuery("DELETE FROM private_ind_bin WHERE namespace=? AND key=? AND profile=?)", (namespace, key, self.profiles[profile]))
691 d.addErrback(lambda ignore: log.error(_(u"Can't delete individual private binary value (%(key)s) [namespace: %(namespace)s] for [%(profile)s] in database" %
692 {"namespace": namespace, "key": key, "profile": profile})))
693 return d
694 ##Helper methods## 636 ##Helper methods##
695 637
696 def __getFirstResult(self, result): 638 def __getFirstResult(self, result):
697 """Return the first result of a database query 639 """Return the first result of a database query
698 Useful when we are looking for one specific value""" 640 Useful when we are looking for one specific value"""