Revision 84846143 pithos/backends/simple.py
b/pithos/backends/simple.py | ||
---|---|---|
65 | 65 |
os.makedirs(basepath) |
66 | 66 |
|
67 | 67 |
self.con = sqlite3.connect(db, check_same_thread=False) |
68 |
|
|
69 |
sql = '''pragma foreign_keys = on''' |
|
70 |
self.con.execute(sql) |
|
71 |
|
|
68 | 72 |
sql = '''create table if not exists versions ( |
69 | 73 |
version_id integer primary key, |
70 | 74 |
name text, |
71 | 75 |
user text, |
72 |
tstamp datetime default current_timestamp,
|
|
76 |
tstamp integer not null,
|
|
73 | 77 |
size integer default 0, |
74 | 78 |
hide integer default 0)''' |
75 | 79 |
self.con.execute(sql) |
76 | 80 |
sql = '''create table if not exists metadata ( |
77 |
version_id integer, key text, value text, primary key (version_id, key))''' |
|
81 |
version_id integer, |
|
82 |
key text, |
|
83 |
value text, |
|
84 |
primary key (version_id, key) |
|
85 |
foreign key (version_id) references versions(version_id) |
|
86 |
on delete cascade)''' |
|
87 |
self.con.execute(sql) |
|
88 |
sql = '''create table if not exists hashmaps ( |
|
89 |
version_id integer, |
|
90 |
pos integer, |
|
91 |
block_id text, |
|
92 |
primary key (version_id, pos) |
|
93 |
foreign key (version_id) references versions(version_id) |
|
94 |
on delete cascade)''' |
|
78 | 95 |
self.con.execute(sql) |
79 | 96 |
sql = '''create table if not exists blocks ( |
80 | 97 |
block_id text, data blob, primary key (block_id))''' |
81 | 98 |
self.con.execute(sql) |
82 |
sql = '''create table if not exists hashmaps ( |
|
83 |
version_id integer, pos integer, block_id text, primary key (version_id, pos))''' |
|
99 |
|
|
100 |
sql = '''create table if not exists policy ( |
|
101 |
name text, key text, value text, primary key (name, key))''' |
|
84 | 102 |
self.con.execute(sql) |
103 |
|
|
85 | 104 |
sql = '''create table if not exists groups ( |
86 | 105 |
account text, name text, users text, primary key (account, name))''' |
87 | 106 |
self.con.execute(sql) |
88 |
sql = '''create table if not exists policy ( |
|
89 |
name text, key text, value text, primary key (name, key))''' |
|
90 |
self.con.execute(sql) |
|
91 | 107 |
sql = '''create table if not exists permissions ( |
92 | 108 |
name text, read text, write text, primary key (name))''' |
93 | 109 |
self.con.execute(sql) |
... | ... | |
170 | 186 |
self.con.execute(sql, (account, k, ','.join(v))) |
171 | 187 |
self.con.commit() |
172 | 188 |
|
189 |
def put_account(self, user, account): |
|
190 |
"""Create a new account with the given name.""" |
|
191 |
|
|
192 |
logger.debug("put_account: %s", account) |
|
193 |
if user != account: |
|
194 |
raise NotAllowedError |
|
195 |
try: |
|
196 |
version_id, mtime = self._get_accountinfo(account) |
|
197 |
except NameError: |
|
198 |
pass |
|
199 |
else: |
|
200 |
raise NameError('Account already exists') |
|
201 |
version_id = self._put_version(account, user) |
|
202 |
self.con.commit() |
|
203 |
|
|
173 | 204 |
def delete_account(self, user, account): |
174 | 205 |
"""Delete the account with the given name.""" |
175 | 206 |
|
176 | 207 |
logger.debug("delete_account: %s", account) |
177 | 208 |
if user != account: |
178 | 209 |
raise NotAllowedError |
179 |
count, bytes, tstamp = self._get_pathstats(account)
|
|
210 |
count = self._get_pathstats(account)[0]
|
|
180 | 211 |
if count > 0: |
181 | 212 |
raise IndexError('Account is not empty') |
182 |
self._del_path(account) # Point of no return. |
|
213 |
sql = 'delete from versions where name = ?' |
|
214 |
self.con.execute(sql, (account,)) |
|
215 |
sql = 'delete from groups where name = ?' |
|
216 |
self.con.execute(sql, (account,)) |
|
217 |
self.con.commit() |
|
183 | 218 |
|
184 | 219 |
def list_containers(self, user, account, marker=None, limit=10000, until=None): |
185 | 220 |
"""Return a list of containers existing under an account.""" |
... | ... | |
271 | 306 |
self.con.execute(sql, (path, k, v)) |
272 | 307 |
self.con.commit() |
273 | 308 |
|
274 |
def delete_container(self, user, account, container): |
|
275 |
"""Delete the container with the given name.""" |
|
309 |
def delete_container(self, user, account, container, until=None):
|
|
310 |
"""Delete/purge the container with the given name."""
|
|
276 | 311 |
|
277 |
logger.debug("delete_container: %s %s", account, container)
|
|
312 |
logger.debug("delete_container: %s %s %s", account, container, until)
|
|
278 | 313 |
if user != account: |
279 | 314 |
raise NotAllowedError |
280 | 315 |
path, version_id, mtime = self._get_containerinfo(account, container) |
281 |
count, bytes, tstamp = self._get_pathstats(path) |
|
316 |
|
|
317 |
if until is not None: |
|
318 |
sql = '''select version_id from versions where name like ? and tstamp <= ?''' |
|
319 |
c = self.con.execute(sql, (path + '/%', until)) |
|
320 |
for v in [x[0] for x in c.fetchall()]: |
|
321 |
self._del_version(v) |
|
322 |
self.con.commit() |
|
323 |
return |
|
324 |
|
|
325 |
count = self._get_pathstats(path)[0] |
|
282 | 326 |
if count > 0: |
283 | 327 |
raise IndexError('Container is not empty') |
284 |
self._del_path(path) # Point of no return. |
|
328 |
sql = 'delete from versions where name like ?' # May contain hidden items. |
|
329 |
self.con.execute(sql, (path + '/%',)) |
|
330 |
sql = 'delete from policy where name = ?' |
|
331 |
self.con.execute(sql, (path,)) |
|
285 | 332 |
self._copy_version(user, account, account, True, True) # New account version (for timestamp update). |
286 | 333 |
|
287 | 334 |
def list_objects(self, user, account, container, prefix='', delimiter=None, marker=None, limit=10000, virtual=True, keys=[], until=None): |
288 | 335 |
"""Return a list of objects existing under a container.""" |
289 | 336 |
|
290 |
logger.debug("list_objects: %s %s %s %s %s %s %s", account, container, prefix, delimiter, marker, limit, until)
|
|
337 |
logger.debug("list_objects: %s %s %s %s %s %s %s %s %s", account, container, prefix, delimiter, marker, limit, virtual, keys, until)
|
|
291 | 338 |
if user != account: |
292 | 339 |
raise NotAllowedError |
293 | 340 |
path, version_id, mtime = self._get_containerinfo(account, container, until) |
... | ... | |
448 | 495 |
self.copy_object(user, account, src_container, src_name, dest_container, dest_name, dest_meta, replace_meta, permissions, None) |
449 | 496 |
self.delete_object(user, account, src_container, src_name) |
450 | 497 |
|
451 |
def delete_object(self, user, account, container, name): |
|
452 |
"""Delete an object.""" |
|
498 |
def delete_object(self, user, account, container, name, until=None):
|
|
499 |
"""Delete/purge an object."""
|
|
453 | 500 |
|
454 |
logger.debug("delete_object: %s %s %s", account, container, name)
|
|
501 |
logger.debug("delete_object: %s %s %s %s", account, container, name, until)
|
|
455 | 502 |
if user != account: |
456 | 503 |
raise NotAllowedError |
504 |
|
|
505 |
if until is not None: |
|
506 |
path = os.path.join(account, container, name) |
|
507 |
sql = '''select version_id from versions where name = ? and tstamp <= ?''' |
|
508 |
c = self.con.execute(sql, (path, until)) |
|
509 |
for v in [x[0] in c.fetchall()]: |
|
510 |
self._del_version(v) |
|
511 |
try: |
|
512 |
version_id = self._get_version(path)[0] |
|
513 |
except NameError: |
|
514 |
pass |
|
515 |
else: |
|
516 |
self._del_sharing(path) |
|
517 |
self.con.commit() |
|
518 |
return |
|
519 |
|
|
457 | 520 |
path = self._get_objectinfo(account, container, name)[0] |
458 | 521 |
self._put_version(path, user, 0, 1) |
459 |
sql = 'delete from permissions where name = ?' |
|
460 |
self.con.execute(sql, (path,)) |
|
461 |
sql = 'delete from public where name = ?' |
|
462 |
self.con.execute(sql, (path,)) |
|
463 |
self.con.commit() |
|
522 |
self._del_sharing(path) |
|
464 | 523 |
|
465 | 524 |
def list_versions(self, user, account, container, name): |
466 | 525 |
"""Return a list of all (version, version_timestamp) tuples for an object.""" |
... | ... | |
469 | 528 |
self._can_read(user, account, container, name) |
470 | 529 |
# This will even show deleted versions. |
471 | 530 |
path = os.path.join(account, container, name) |
472 |
sql = '''select distinct version_id, strftime('%s', tstamp) from versions where name = ? and hide = 0'''
|
|
531 |
sql = '''select distinct version_id, tstamp from versions where name = ? and hide = 0'''
|
|
473 | 532 |
c = self.con.execute(sql, (path,)) |
474 | 533 |
return [(int(x[0]), int(x[1])) for x in c.fetchall()] |
475 | 534 |
|
... | ... | |
513 | 572 |
"""Return the sql to get the latest versions until the timestamp given.""" |
514 | 573 |
if until is None: |
515 | 574 |
until = int(time.time()) |
516 |
sql = '''select version_id, name, strftime('%s', tstamp) as tstamp, size from versions v
|
|
575 |
sql = '''select version_id, name, tstamp, size from versions v |
|
517 | 576 |
where version_id = (select max(version_id) from versions |
518 |
where v.name = name and tstamp <= datetime(%s, 'unixepoch'))
|
|
577 |
where v.name = name and tstamp <= ?)
|
|
519 | 578 |
and hide = 0''' |
520 |
return sql % ('%s', until)
|
|
579 |
return sql % (until,)
|
|
521 | 580 |
|
522 | 581 |
def _get_pathstats(self, path, until=None): |
523 | 582 |
"""Return count and sum of size of everything under path and latest timestamp.""" |
... | ... | |
531 | 590 |
|
532 | 591 |
def _get_version(self, path, version=None): |
533 | 592 |
if version is None: |
534 |
sql = '''select version_id, user, strftime('%s', tstamp), size, hide from versions where name = ?
|
|
593 |
sql = '''select version_id, user, tstamp, size, hide from versions where name = ?
|
|
535 | 594 |
order by version_id desc limit 1''' |
536 | 595 |
c = self.con.execute(sql, (path,)) |
537 | 596 |
row = c.fetchone() |
... | ... | |
539 | 598 |
raise NameError('Object does not exist') |
540 | 599 |
else: |
541 | 600 |
# The database (sqlite) will not complain if the version is not an integer. |
542 |
sql = '''select version_id, user, strftime('%s', tstamp), size from versions where name = ?
|
|
601 |
sql = '''select version_id, user, tstamp, size from versions where name = ?
|
|
543 | 602 |
and version_id = ?''' |
544 | 603 |
c = self.con.execute(sql, (path, version)) |
545 | 604 |
row = c.fetchone() |
... | ... | |
548 | 607 |
return str(row[0]), str(row[1]), int(row[2]), int(row[3]) |
549 | 608 |
|
550 | 609 |
def _put_version(self, path, user, size=0, hide=0): |
551 |
sql = 'insert into versions (name, user, size, hide) values (?, ?, ?, ?)' |
|
552 |
id = self.con.execute(sql, (path, user, size, hide)).lastrowid |
|
610 |
tstamp = int(time.time()) |
|
611 |
sql = 'insert into versions (name, user, tstamp, size, hide) values (?, ?, ?, ?, ?)' |
|
612 |
id = self.con.execute(sql, (path, user, tstamp, size, hide)).lastrowid |
|
553 | 613 |
self.con.commit() |
554 | 614 |
return str(id) |
555 | 615 |
|
... | ... | |
753 | 813 |
self.con.execute(sql, (path,)) |
754 | 814 |
self.con.commit() |
755 | 815 |
|
816 |
def _del_sharing(self, path): |
|
817 |
sql = 'delete from permissions where name = ?' |
|
818 |
self.con.execute(sql, (path,)) |
|
819 |
sql = 'delete from public where name = ?' |
|
820 |
self.con.execute(sql, (path,)) |
|
821 |
self.con.commit() |
|
822 |
|
|
756 | 823 |
def _list_objects(self, path, prefix='', delimiter=None, marker=None, limit=10000, virtual=True, keys=[], until=None): |
757 | 824 |
cont_prefix = path + '/' |
758 | 825 |
if keys and len(keys) > 0: |
... | ... | |
797 | 864 |
limit = 10000 |
798 | 865 |
return objects[start:start + limit] |
799 | 866 |
|
800 |
def _del_path(self, path): |
|
801 |
sql = '''delete from hashmaps where version_id in |
|
802 |
(select version_id from versions where name = ?)''' |
|
803 |
self.con.execute(sql, (path,)) |
|
804 |
sql = '''delete from metadata where version_id in |
|
805 |
(select version_id from versions where name = ?)''' |
|
806 |
self.con.execute(sql, (path,)) |
|
807 |
sql = '''delete from versions where name = ?''' |
|
808 |
self.con.execute(sql, (path,)) |
|
809 |
self.con.commit() |
|
867 |
def _del_version(self, version): |
|
868 |
sql = 'delete from hashmaps where version_id = ?' |
|
869 |
self.con.execute(sql, (version,)) |
|
870 |
sql = 'delete from versions where version_id = ?' |
|
871 |
self.con.execute(sql, (version,)) |
Also available in: Unified diff