index eb1b7c9a33e777053775b656a1c759a54ba25be2..3a4d6f5c276ee9c7fe5a226a5bdd7bc5f9edbf38 100644 (file)
# no changes
return 0
+ if not self.config.RDBMS_ALLOW_ALTER:
+ raise DatabaseError(_('ALTER operation disallowed: %r -> %r.'%(old_spec, new_spec)))
+
logger = logging.getLogger('roundup.hyperdb')
logger.info('update_class %s'%spec.classname)
def create_class(self, spec):
""" Create a database table according to the given spec.
"""
+
+ if not self.config.RDBMS_ALLOW_CREATE:
+ raise DatabaseError(_('CREATE operation disallowed: "%s".'%spec.classname))
+
cols, mls = self.create_class_table(spec)
self.create_journal_table(spec)
Drop the journal and multilink tables too.
"""
+
+ if not self.config.RDBMS_ALLOW_DROP:
+ raise DatabaseError(_('DROP operation disallowed: "%s".'%cn))
+
properties = spec[1]
# figure the multilinks
mls = []
raise ValueError('%r is not a hyperdb property class' % propklass)
- def getnode(self, classname, nodeid):
+ def _materialize_multilink(self, classname, nodeid, node, propname):
+ """ evaluation of single Multilink (lazy eval may have skipped this)
+ """
+ if propname not in node:
+ sql = 'select linkid from %s_%s where nodeid=%s'%(classname,
+ propname, self.arg)
+ self.sql(sql, (nodeid,))
+ # extract the first column from the result
+ # XXX numeric ids
+ items = [int(x[0]) for x in self.cursor.fetchall()]
+ items.sort ()
+ node[propname] = [str(x) for x in items]
+
+ def _materialize_multilinks(self, classname, nodeid, node, props=None):
+ """ get all Multilinks of a node (lazy eval may have skipped this)
+ """
+ cl = self.classes[classname]
+ props = props or [pn for (pn, p) in cl.properties.iteritems()
+ if isinstance(p, Multilink)]
+ for propname in props:
+ if propname not in node:
+ self._materialize_multilink(classname, nodeid, node, propname)
+
+ def getnode(self, classname, nodeid, fetch_multilinks=True):
""" Get a node from the database.
+ For optimisation optionally we don't fetch multilinks
+ (lazy Multilinks).
+ But for internal database operations we need them.
"""
# see if we have this node cached
key = (classname, nodeid)
if __debug__:
self.stats['cache_hits'] += 1
# return the cached information
+ if fetch_multilinks:
+ self._materialize_multilinks(classname, nodeid, self.cache[key])
return self.cache[key]
if __debug__:
value = self.to_hyperdb_value(props[name].__class__)(value)
node[name] = value
+ if fetch_multilinks and mls:
+ self._materialize_multilinks(classname, nodeid, node, mls)
+
# save off in the cache
key = (classname, nodeid)
self._cache_save(key, node)
continue
cvt = self.to_hyperdb_value(property.__class__)
if isinstance(property, Password):
- params[param] = cvt(value)
+ params[param] = password.JournalPassword(value)
elif isinstance(property, Date):
params[param] = cvt(value)
elif isinstance(property, Interval):
return nodeid
# get the node's dict
- d = self.db.getnode(self.classname, nodeid)
-
- if propname == 'creation':
- if 'creation' in d:
- return d['creation']
- else:
- return date.Date()
- if propname == 'activity':
- if 'activity' in d:
- return d['activity']
- else:
- return date.Date()
- if propname == 'creator':
- if 'creator' in d:
- return d['creator']
- else:
- return self.db.getuid()
- if propname == 'actor':
- if 'actor' in d:
- return d['actor']
- else:
- return self.db.getuid()
+ d = self.db.getnode(self.classname, nodeid, fetch_multilinks=False)
+ # handle common case -- that property is in dict -- first
+ # if None and one of creator/creation actor/activity return None
+ if propname in d:
+ r = d [propname]
+ # return copy of our list
+ if isinstance (r, list):
+ return r[:]
+ if r is not None:
+ return r
+ elif propname in ('creation', 'activity', 'creator', 'actor'):
+ return r
+
+ # propname not in d:
+ if propname == 'creation' or propname == 'activity':
+ return date.Date()
+ if propname == 'creator' or propname == 'actor':
+ return self.db.getuid()
# get the property (raises KeyError if invalid)
prop = self.properties[propname]
# lazy evaluation of Multilink
if propname not in d and isinstance(prop, Multilink):
- sql = 'select linkid from %s_%s where nodeid=%s'%(self.classname,
- propname, self.db.arg)
- self.db.sql(sql, (nodeid,))
- # extract the first column from the result
- # XXX numeric ids
- items = [int(x[0]) for x in self.db.cursor.fetchall()]
- items.sort ()
- d[propname] = [str(x) for x in items]
+ self.db._materialize_multilink(self.classname, nodeid, d, propname)
# handle there being no value in the table for the property
if propname not in d or d[propname] is None:
if not isinstance(value, password.Password):
raise TypeError('new property "%s" not a Password'%propname)
propvalues[propname] = value
+ journalvalues[propname] = \
+ current and password.JournalPassword(current)
elif value is not None and isinstance(prop, Date):
if not isinstance(value, date.Date):
raise DatabaseError(_('Database open read-only'))
self.db.destroynode(self.classname, nodeid)
- def history(self, nodeid):
- """Retrieve the journal of edits on a particular node.
-
- 'nodeid' must be the id of an existing node of this class or an
- IndexError is raised.
-
- The returned list contains tuples of the form
-
- (nodeid, date, tag, action, params)
-
- 'date' is a Timestamp object specifying the time of the change and
- 'tag' is the journaltag specified when the database was opened.
- """
- if not self.do_journal:
- raise ValueError('Journalling is disabled for this class')
- return self.db.getjournal(self.classname, nodeid)
-
# Locating nodes:
def hasnode(self, nodeid):
"""Determine if the given nodeid actually exists
elif isinstance(prop, hyperdb.Interval):
value = date.Interval(value)
elif isinstance(prop, hyperdb.Password):
- pwd = password.Password()
- pwd.unpack(value)
- value = pwd
+ value = password.Password(encrypted=value)
elif isinstance(prop, String):
if isinstance(value, unicode):
value = value.encode('utf8')