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,
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:
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:
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).
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)
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()]
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:
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()]
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])
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)
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()]