Revision f77b150f pithos/backends/simple.py
b/pithos/backends/simple.py | ||
---|---|---|
73 | 73 |
version_id integer primary key, |
74 | 74 |
name text, |
75 | 75 |
user text, |
76 |
tstamp datetime default current_timestamp,
|
|
76 |
tstamp integer not null,
|
|
77 | 77 |
size integer default 0, |
78 |
hide integer default 0)''' |
|
78 |
trash integer default 0, |
|
79 |
until integer default null)''' |
|
79 | 80 |
self.con.execute(sql) |
80 | 81 |
sql = '''create table if not exists metadata ( |
81 | 82 |
version_id integer, |
... | ... | |
229 | 230 |
logger.debug("get_container_meta: %s %s %s", account, container, until) |
230 | 231 |
if user != account: |
231 | 232 |
raise NotAllowedError |
233 |
|
|
234 |
# TODO: Container meta for trash. |
|
232 | 235 |
path, version_id, mtime = self._get_containerinfo(account, container, until) |
233 | 236 |
count, bytes, tstamp = self._get_pathstats(path, until) |
234 | 237 |
if mtime > tstamp: |
... | ... | |
314 | 317 |
path, version_id, mtime = self._get_containerinfo(account, container) |
315 | 318 |
|
316 | 319 |
if until is not None: |
317 |
sql = '''select version_id from versions where name like ? and tstamp <= datetime(%s, 'unixepoch')'''
|
|
320 |
sql = '''select version_id from versions where name like ? and tstamp <= ?'''
|
|
318 | 321 |
c = self.con.execute(sql, (path + '/%', until)) |
319 | 322 |
versions = [x[0] for x in c.fetchall()] |
320 | 323 |
for v in versions: |
... | ... | |
327 | 330 |
|
328 | 331 |
if self._get_pathcount(path) > 0: |
329 | 332 |
raise IndexError('Container is not empty') |
330 |
sql = 'delete from versions where name = ?'
|
|
331 |
self.con.execute(sql, (path,)) |
|
333 |
sql = 'delete from versions where name like ?' # May contain hidden trash items.
|
|
334 |
self.con.execute(sql, (path + '/%',))
|
|
332 | 335 |
sql = 'delete from policy where name = ?' |
333 | 336 |
self.con.execute(sql, (path,)) |
334 | 337 |
self._copy_version(user, account, account, True, True) # New account version (for timestamp update). |
... | ... | |
342 | 345 |
path, version_id, mtime = self._get_containerinfo(account, container, until) |
343 | 346 |
return self._list_objects(path, prefix, delimiter, marker, limit, virtual, keys, trash, until) |
344 | 347 |
|
345 |
def list_object_meta(self, user, account, container, until=None): |
|
348 |
def list_object_meta(self, user, account, container, trash=False, until=None):
|
|
346 | 349 |
"""Return a list with all the container's object meta keys.""" |
347 | 350 |
|
348 | 351 |
logger.debug("list_object_meta: %s %s %s", account, container, until) |
... | ... | |
351 | 354 |
path, version_id, mtime = self._get_containerinfo(account, container, until) |
352 | 355 |
sql = '''select distinct m.key from (%s) o, metadata m |
353 | 356 |
where m.version_id = o.version_id and o.name like ?''' |
354 |
sql = sql % self._sql_until(until) |
|
357 |
sql = sql % self._sql_until(until, trash)
|
|
355 | 358 |
c = self.con.execute(sql, (path + '/%',)) |
356 | 359 |
return [x[0] for x in c.fetchall()] |
357 | 360 |
|
... | ... | |
509 | 512 |
c = self.con.execute(sql, (path,)) |
510 | 513 |
else: |
511 | 514 |
path = os.path.join(account, container, name) |
512 |
sql = '''select version_id from versions where name = ? and tstamp <= datetime(%s, 'unixepoch')'''
|
|
515 |
sql = '''select version_id from versions where name = ? and tstamp <= ?'''
|
|
513 | 516 |
c = self.con.execute(sql, (path, until)) |
514 | 517 |
versions = [x[0] for x in c.fetchall()] |
515 | 518 |
for v in versions: |
... | ... | |
518 | 521 |
sql = 'delete from versions where version_id = ?' |
519 | 522 |
self.con.execute(sql, (v,)) |
520 | 523 |
|
521 |
# If no more versions exist, delete permissions/public. |
|
522 |
sql = 'select version_id from versions where name = ?' |
|
524 |
# If no more normal versions exist, delete permissions/public.
|
|
525 |
sql = 'select version_id from versions where name = ? and trash = 0'
|
|
523 | 526 |
row = self.con.execute(sql, (path,)).fetchone() |
524 | 527 |
if row is None: |
525 | 528 |
self._del_sharing(path) |
526 | 529 |
self.con.commit() |
527 | 530 |
|
528 |
def update_object_trash(self, user, account, container, name, trash=True):
|
|
529 |
"""Trash/untrash an object."""
|
|
531 |
def trash_object(self, user, account, container, name):
|
|
532 |
"""Trash an object.""" |
|
530 | 533 |
|
531 | 534 |
logger.debug("trash_object: %s %s %s", account, container, name) |
532 | 535 |
if user != account: |
533 | 536 |
raise NotAllowedError |
534 |
if trash: |
|
535 |
path, version_id, muser, mtime, size = self._get_objectinfo(account, container, name) |
|
536 |
self._del_sharing(path) |
|
537 |
else: |
|
538 |
path = os.path.join(account, container, name) |
|
539 |
sql = '''select version_id, hide from versions where name = ? |
|
540 |
order by version_id desc limit 1''' |
|
541 |
c = self.con.execute(sql, (path,)) |
|
542 |
row = c.fetchone() |
|
543 |
if not row or not int(row[1]): |
|
544 |
raise NameError('Object not in trash') |
|
545 |
version_id = row[0] |
|
546 |
hide = 1 if trash else 0 |
|
547 |
sql = 'update versions set hide = ? where version_id = ?' |
|
548 |
self.con.execute(sql, (hide, version_id,)) |
|
549 |
seld.con.commit() |
|
537 |
path, version_id, muser, mtime, size = self._get_objectinfo(account, container, name) |
|
538 |
src_version_id, dest_version_id = self._copy_version(user, path, path, True, True, version_id) |
|
539 |
sql = 'update versions set trash = 1 where version_id = ?' |
|
540 |
self.con.execute(sql, (dest_version_id,)) |
|
541 |
self._del_sharing(path) |
|
542 |
self.con.commit() |
|
543 |
|
|
544 |
def untrash_object(self, user, account, container, name, version): |
|
545 |
"""Untrash an object.""" |
|
546 |
|
|
547 |
logger.debug("untrash_object: %s %s %s %s", account, container, name, version) |
|
548 |
if user != account: |
|
549 |
raise NotAllowedError |
|
550 |
|
|
551 |
path = os.path.join(account, container, name) |
|
552 |
sql = '''select version_id from versions where name = ? and version_id = ? and trash = 1''' |
|
553 |
c = self.con.execute(sql, (path, version)) |
|
554 |
row = c.fetchone() |
|
555 |
if not row or not int(row[1]): |
|
556 |
raise NameError('Object not in trash') |
|
557 |
sql = 'update versions set until = ? where version_id = ?' |
|
558 |
self.con.execute(sql, (int(time.time()), version)) |
|
559 |
self.con.commit() |
|
550 | 560 |
|
551 | 561 |
def list_versions(self, user, account, container, name): |
552 | 562 |
"""Return a list of all (version, version_timestamp) tuples for an object.""" |
553 | 563 |
|
554 | 564 |
logger.debug("list_versions: %s %s %s", account, container, name) |
555 | 565 |
self._can_read(user, account, container, name) |
556 |
# This will even show deleted versions. |
|
557 | 566 |
path = os.path.join(account, container, name) |
558 |
sql = '''select distinct version_id, strftime('%s', tstamp) from versions where name = ?'''
|
|
567 |
sql = '''select distinct version_id, tstamp from versions where name = ? and trash = 0'''
|
|
559 | 568 |
c = self.con.execute(sql, (path,)) |
560 | 569 |
return [(int(x[0]), int(x[1])) for x in c.fetchall()] |
561 | 570 |
|
... | ... | |
595 | 604 |
dest_data = src_data[:offset] + data + src_data[offset + len(data):] |
596 | 605 |
return self.put_block(dest_data) |
597 | 606 |
|
598 |
def _sql_until(self, until=None): |
|
607 |
def _sql_until(self, until=None, trash=False):
|
|
599 | 608 |
"""Return the sql to get the latest versions until the timestamp given.""" |
609 |
|
|
600 | 610 |
if until is None: |
601 | 611 |
until = int(time.time()) |
602 |
sql = '''select version_id, name, strftime('%s', tstamp) as tstamp, size from versions v |
|
603 |
where version_id = (select max(version_id) from versions |
|
604 |
where v.name = name and tstamp <= datetime(%s, 'unixepoch')) |
|
605 |
and hide = 0''' |
|
606 |
return sql % ('%s', until) |
|
612 |
if not trash: |
|
613 |
sql = '''select version_id, name, tstamp, size from versions v |
|
614 |
where version_id = (select max(version_id) from versions |
|
615 |
where v.name = name and tstamp <= ?) |
|
616 |
and trash = 0''' |
|
617 |
return sql % (until,) |
|
618 |
else: |
|
619 |
sql = '''select version_id, name, tstamp, size from versions v |
|
620 |
where trash = 1 and tstamp <= ? and (until is null or until > ?)''' |
|
621 |
return sql % (until, until) |
|
607 | 622 |
|
608 | 623 |
def _get_pathstats(self, path, until=None): |
609 | 624 |
"""Return count, sum of size and latest timestamp of everything under path (latest versions/no trash).""" |
610 | 625 |
|
611 |
sql = 'select count(version_id), total(size), max(tstamp) from (%s) where name like ?'
|
|
626 |
sql = 'select count(version_id), total(size) from (%s) where name like ?' |
|
612 | 627 |
sql = sql % self._sql_until(until) |
613 | 628 |
c = self.con.execute(sql, (path + '/%',)) |
629 |
total_count, total_size = c.fetchone() |
|
630 |
sql = 'select max(tstamp) from versions where name like ? and tstamp <= ?' # Include trash actions. |
|
631 |
c = self.con.execute(sql, (path + '/%', until)) |
|
614 | 632 |
row = c.fetchone() |
615 |
tstamp = row[2] if row[2] is not None else 0
|
|
616 |
return int(row[0]), int(row[1]), int(tstamp)
|
|
633 |
tstamp = row[0] if row[0] is not None else 0
|
|
634 |
return int(total_count), int(total_size), int(tstamp)
|
|
617 | 635 |
|
618 | 636 |
def _get_pathcount(self, path): |
619 | 637 |
"""Return count of everything under path (including versions/trash).""" |
620 | 638 |
|
621 |
sql = 'select count(version_id) from versions where name like ?' |
|
639 |
sql = 'select count(version_id) from versions where name like ? and until is null'
|
|
622 | 640 |
c = self.con.execute(sql, (path + '/%',)) |
623 | 641 |
row = c.fetchone() |
624 | 642 |
return int(row[0]) |
... | ... | |
642 | 660 |
return str(row[0]), str(row[1]), int(row[2]), int(row[3]) |
643 | 661 |
|
644 | 662 |
def _put_version(self, path, user, size=0): |
645 |
sql = 'insert into versions (name, user, size) values (?, ?, ?)' |
|
646 |
id = self.con.execute(sql, (path, user, size)).lastrowid |
|
663 |
tstamp = int(time.time()) |
|
664 |
sql = 'insert into versions (name, user, tstamp, size) values (?, ?, ?, ?)' |
|
665 |
id = self.con.execute(sql, (path, user, tstamp, size)).lastrowid |
|
647 | 666 |
self.con.commit() |
648 | 667 |
return str(id) |
649 | 668 |
|
... | ... | |
857 | 876 |
def _list_objects(self, path, prefix='', delimiter=None, marker=None, limit=10000, virtual=True, keys=[], trash=False, until=None): |
858 | 877 |
cont_prefix = path + '/' |
859 | 878 |
|
860 |
if trash: |
|
861 |
table = '''select version_id, name, strftime('%s', tstamp) as tstamp, size from versions v |
|
862 |
where hide = 1''' % ('%s',) |
|
863 |
if until is not None: |
|
864 |
table += ''' and tstamp <= datetime(%s, 'unixepoch')''' % (until,) |
|
865 |
else: |
|
866 |
table = self._sql_until(until) |
|
867 | 879 |
if keys and len(keys) > 0: |
868 | 880 |
sql = '''select distinct o.name, o.version_id from (%s) o, metadata m where o.name like ? and |
869 | 881 |
m.version_id = o.version_id and m.key in (%s) order by o.name''' |
870 |
sql = sql % (table, ', '.join('?' * len(keys)))
|
|
882 |
sql = sql % (self._sql_until(until, trash), ', '.join('?' * len(keys)))
|
|
871 | 883 |
param = (cont_prefix + prefix + '%',) + tuple(keys) |
872 | 884 |
else: |
873 | 885 |
sql = 'select name, version_id from (%s) where name like ? order by name' |
874 |
sql = sql % (table,)
|
|
886 |
sql = sql % (self._sql_until(until, trash),)
|
|
875 | 887 |
param = (cont_prefix + prefix + '%',) |
876 | 888 |
c = self.con.execute(sql, param) |
877 | 889 |
objects = [(x[0][len(cont_prefix):], x[1]) for x in c.fetchall()] |
Also available in: Unified diff