X-Git-Url: https://code.grnet.gr/git/pithos/blobdiff_plain/bf90312067d8ea1ed2087ddbd0f1a5e954cf5665..f77b150fc377c30cdc809031ad392f7024c8f5be:/pithos/backends/simple.py diff --git a/pithos/backends/simple.py b/pithos/backends/simple.py index 1ba4367..0aecb68 100644 --- a/pithos/backends/simple.py +++ b/pithos/backends/simple.py @@ -73,9 +73,10 @@ class SimpleBackend(BaseBackend): version_id integer primary key, name text, user text, - tstamp datetime default current_timestamp, + tstamp integer not null, size integer default 0, - hide integer default 0)''' + trash integer default 0, + until integer default null)''' self.con.execute(sql) sql = '''create table if not exists metadata ( version_id integer, @@ -229,6 +230,8 @@ class SimpleBackend(BaseBackend): logger.debug("get_container_meta: %s %s %s", account, container, until) if user != account: raise NotAllowedError + + # TODO: Container meta for trash. path, version_id, mtime = self._get_containerinfo(account, container, until) count, bytes, tstamp = self._get_pathstats(path, until) if mtime > tstamp: @@ -314,7 +317,7 @@ class SimpleBackend(BaseBackend): path, version_id, mtime = self._get_containerinfo(account, container) if until is not None: - sql = '''select version_id from versions where name like ? and tstamp <= datetime(%s, 'unixepoch')''' + sql = '''select version_id from versions where name like ? and tstamp <= ?''' c = self.con.execute(sql, (path + '/%', until)) versions = [x[0] for x in c.fetchall()] for v in versions: @@ -327,8 +330,8 @@ class SimpleBackend(BaseBackend): if self._get_pathcount(path) > 0: raise IndexError('Container is not empty') - sql = 'delete from versions where name = ?' - self.con.execute(sql, (path,)) + sql = 'delete from versions where name like ?' # May contain hidden trash items. + self.con.execute(sql, (path + '/%',)) sql = 'delete from policy where name = ?' self.con.execute(sql, (path,)) self._copy_version(user, account, account, True, True) # New account version (for timestamp update). @@ -342,7 +345,7 @@ class SimpleBackend(BaseBackend): path, version_id, mtime = self._get_containerinfo(account, container, until) return self._list_objects(path, prefix, delimiter, marker, limit, virtual, keys, trash, until) - def list_object_meta(self, user, account, container, until=None): + def list_object_meta(self, user, account, container, trash=False, until=None): """Return a list with all the container's object meta keys.""" logger.debug("list_object_meta: %s %s %s", account, container, until) @@ -351,7 +354,7 @@ class SimpleBackend(BaseBackend): path, version_id, mtime = self._get_containerinfo(account, container, until) sql = '''select distinct m.key from (%s) o, metadata m where m.version_id = o.version_id and o.name like ?''' - sql = sql % self._sql_until(until) + sql = sql % self._sql_until(until, trash) c = self.con.execute(sql, (path + '/%',)) return [x[0] for x in c.fetchall()] @@ -509,7 +512,7 @@ class SimpleBackend(BaseBackend): c = self.con.execute(sql, (path,)) else: path = os.path.join(account, container, name) - sql = '''select version_id from versions where name = ? and tstamp <= datetime(%s, 'unixepoch')''' + sql = '''select version_id from versions where name = ? and tstamp <= ?''' c = self.con.execute(sql, (path, until)) versions = [x[0] for x in c.fetchall()] for v in versions: @@ -518,44 +521,50 @@ class SimpleBackend(BaseBackend): sql = 'delete from versions where version_id = ?' self.con.execute(sql, (v,)) - # If no more versions exist, delete permissions/public. - sql = 'select version_id from versions where name = ?' + # If no more normal versions exist, delete permissions/public. + sql = 'select version_id from versions where name = ? and trash = 0' row = self.con.execute(sql, (path,)).fetchone() if row is None: self._del_sharing(path) self.con.commit() - def update_object_trash(self, user, account, container, name, trash=True): - """Trash/untrash an object.""" + def trash_object(self, user, account, container, name): + """Trash an object.""" logger.debug("trash_object: %s %s %s", account, container, name) if user != account: raise NotAllowedError - if trash: - path, version_id, muser, mtime, size = self._get_objectinfo(account, container, name) - self._del_sharing(path) - else: - path = os.path.join(account, container, name) - sql = '''select version_id, hide from versions where name = ? - order by version_id desc limit 1''' - c = self.con.execute(sql, (path,)) - row = c.fetchone() - if not row or not int(row[1]): - raise NameError('Object not in trash') - version_id = row[0] - hide = 1 if trash else 0 - sql = 'update versions set hide = ? where version_id = ?' - self.con.execute(sql, (hide, version_id,)) - seld.con.commit() + path, version_id, muser, mtime, size = self._get_objectinfo(account, container, name) + src_version_id, dest_version_id = self._copy_version(user, path, path, True, True, version_id) + sql = 'update versions set trash = 1 where version_id = ?' + self.con.execute(sql, (dest_version_id,)) + self._del_sharing(path) + self.con.commit() + + def untrash_object(self, user, account, container, name, version): + """Untrash an object.""" + + logger.debug("untrash_object: %s %s %s %s", account, container, name, version) + if user != account: + raise NotAllowedError + + path = os.path.join(account, container, name) + sql = '''select version_id from versions where name = ? and version_id = ? and trash = 1''' + c = self.con.execute(sql, (path, version)) + row = c.fetchone() + if not row or not int(row[1]): + raise NameError('Object not in trash') + sql = 'update versions set until = ? where version_id = ?' + self.con.execute(sql, (int(time.time()), version)) + self.con.commit() def list_versions(self, user, account, container, name): """Return a list of all (version, version_timestamp) tuples for an object.""" logger.debug("list_versions: %s %s %s", account, container, name) self._can_read(user, account, container, name) - # This will even show deleted versions. path = os.path.join(account, container, name) - sql = '''select distinct version_id, strftime('%s', tstamp) from versions where name = ?''' + sql = '''select distinct version_id, tstamp from versions where name = ? and trash = 0''' c = self.con.execute(sql, (path,)) return [(int(x[0]), int(x[1])) for x in c.fetchall()] @@ -595,30 +604,39 @@ class SimpleBackend(BaseBackend): dest_data = src_data[:offset] + data + src_data[offset + len(data):] return self.put_block(dest_data) - def _sql_until(self, until=None): + def _sql_until(self, until=None, trash=False): """Return the sql to get the latest versions until the timestamp given.""" + if until is None: until = int(time.time()) - sql = '''select version_id, name, strftime('%s', tstamp) as tstamp, size from versions v - where version_id = (select max(version_id) from versions - where v.name = name and tstamp <= datetime(%s, 'unixepoch')) - and hide = 0''' - return sql % ('%s', until) + if not trash: + sql = '''select version_id, name, tstamp, size from versions v + where version_id = (select max(version_id) from versions + where v.name = name and tstamp <= ?) + and trash = 0''' + return sql % (until,) + else: + sql = '''select version_id, name, tstamp, size from versions v + where trash = 1 and tstamp <= ? and (until is null or until > ?)''' + return sql % (until, until) def _get_pathstats(self, path, until=None): """Return count, sum of size and latest timestamp of everything under path (latest versions/no trash).""" - sql = 'select count(version_id), total(size), max(tstamp) from (%s) where name like ?' + sql = 'select count(version_id), total(size) from (%s) where name like ?' sql = sql % self._sql_until(until) c = self.con.execute(sql, (path + '/%',)) + total_count, total_size = c.fetchone() + sql = 'select max(tstamp) from versions where name like ? and tstamp <= ?' # Include trash actions. + c = self.con.execute(sql, (path + '/%', until)) row = c.fetchone() - tstamp = row[2] if row[2] is not None else 0 - return int(row[0]), int(row[1]), int(tstamp) + tstamp = row[0] if row[0] is not None else 0 + return int(total_count), int(total_size), int(tstamp) def _get_pathcount(self, path): """Return count of everything under path (including versions/trash).""" - sql = 'select count(version_id) from versions where name like ?' + sql = 'select count(version_id) from versions where name like ? and until is null' c = self.con.execute(sql, (path + '/%',)) row = c.fetchone() return int(row[0]) @@ -642,8 +660,9 @@ class SimpleBackend(BaseBackend): return str(row[0]), str(row[1]), int(row[2]), int(row[3]) def _put_version(self, path, user, size=0): - sql = 'insert into versions (name, user, size) values (?, ?, ?)' - id = self.con.execute(sql, (path, user, size)).lastrowid + tstamp = int(time.time()) + sql = 'insert into versions (name, user, tstamp, size) values (?, ?, ?, ?)' + id = self.con.execute(sql, (path, user, tstamp, size)).lastrowid self.con.commit() return str(id) @@ -857,21 +876,14 @@ class SimpleBackend(BaseBackend): def _list_objects(self, path, prefix='', delimiter=None, marker=None, limit=10000, virtual=True, keys=[], trash=False, until=None): cont_prefix = path + '/' - if trash: - table = '''select version_id, name, strftime('%s', tstamp) as tstamp, size from versions v - where hide = 1''' % ('%s',) - if until is not None: - table += ''' and tstamp <= datetime(%s, 'unixepoch')''' % (until,) - else: - table = self._sql_until(until) if keys and len(keys) > 0: sql = '''select distinct o.name, o.version_id from (%s) o, metadata m where o.name like ? and m.version_id = o.version_id and m.key in (%s) order by o.name''' - sql = sql % (table, ', '.join('?' * len(keys))) + sql = sql % (self._sql_until(until, trash), ', '.join('?' * len(keys))) param = (cont_prefix + prefix + '%',) + tuple(keys) else: sql = 'select name, version_id from (%s) where name like ? order by name' - sql = sql % (table,) + sql = sql % (self._sql_until(until, trash),) param = (cont_prefix + prefix + '%',) c = self.con.execute(sql, param) objects = [(x[0][len(cont_prefix):], x[1]) for x in c.fetchall()]