]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/magicfolderdb.py
Add last uploaded timestamp comparison for remote conflict detection
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / magicfolderdb.py
1
2 import sys
3
4 from allmydata.util.dbutil import get_db, DBError
5
6
7 # magic-folder db schema version 1
8 SCHEMA_v1 = """
9 CREATE TABLE version
10 (
11  version INTEGER  -- contains one row, set to 1
12 );
13
14 CREATE TABLE local_files
15 (
16  path                VARCHAR(1024) PRIMARY KEY, -- UTF-8 filename relative to local magic folder dir
17  -- note that size is before mtime and ctime here, but after in function parameters
18  size                INTEGER,                   -- ST_SIZE, or NULL if the file has been deleted
19  mtime               NUMBER,                      -- ST_MTIME
20  ctime               NUMBER,                      -- ST_CTIME
21  version             INTEGER,
22  last_uploaded_uri   VARCHAR(256) UNIQUE,       -- URI:CHK:...
23  last_downloaded_uri VARCHAR(256) UNIQUE,       -- URI:CHK:...
24  last_downloaded_timestamp TIMESTAMP
25 );
26 """
27
28
29 def get_magicfolderdb(dbfile, stderr=sys.stderr,
30                       create_version=(SCHEMA_v1, 1), just_create=False):
31     # Open or create the given backupdb file. The parent directory must
32     # exist.
33     try:
34         (sqlite3, db) = get_db(dbfile, stderr, create_version,
35                                just_create=just_create, dbname="magicfolderdb")
36         if create_version[1] in (1, 2):
37             return MagicFolderDB(sqlite3, db)
38         else:
39             print >>stderr, "invalid magicfolderdb schema version specified"
40             return None
41     except DBError, e:
42         print >>stderr, e
43         return None
44
45
46 class MagicFolderDB(object):
47     VERSION = 1
48
49     def __init__(self, sqlite_module, connection):
50         self.sqlite_module = sqlite_module
51         self.connection = connection
52         self.cursor = connection.cursor()
53
54     def check_file_db_exists(self, path):
55         """I will tell you if a given file has an entry in my database or not
56         by returning True or False.
57         """
58         c = self.cursor
59         c.execute("SELECT size,mtime,ctime"
60                   " FROM local_files"
61                   " WHERE path=?",
62                   (path,))
63         row = self.cursor.fetchone()
64         if not row:
65             return False
66         else:
67             return True
68
69     def get_all_relpaths(self):
70         """
71         Retrieve a set of all relpaths of files that have had an entry in magic folder db
72         (i.e. that have been downloaded at least once).
73         """
74         self.cursor.execute("SELECT path FROM local_files")
75         rows = self.cursor.fetchall()
76         return set([r[0] for r in rows])
77
78     def get_last_downloaded_uri(self, relpath_u):
79         """
80         Return the last downloaded uri recorded in the magic folder db.
81         If none are found then return None.
82         """
83         c = self.cursor
84         c.execute("SELECT last_downloaded_uri"
85                   " FROM local_files"
86                   " WHERE path=?",
87                   (relpath_u,))
88         row = self.cursor.fetchone()
89         if not row:
90             return None
91         else:
92             return row[0]
93
94     def get_last_uploaded_uri(self, relpath_u):
95         """
96         Return the last downloaded uri recorded in the magic folder db.
97         If none are found then return None.
98         """
99         c = self.cursor
100         c.execute("SELECT last_uploaded_uri"
101                   " FROM local_files"
102                   " WHERE path=?",
103                   (relpath_u,))
104         row = self.cursor.fetchone()
105         if not row:
106             return None
107         else:
108             return row[0]
109
110     def get_local_file_version(self, relpath_u):
111         """
112         Return the version of a local file tracked by our magic folder db.
113         If no db entry is found then return None.
114         """
115         c = self.cursor
116         c.execute("SELECT version"
117                   " FROM local_files"
118                   " WHERE path=?",
119                   (relpath_u,))
120         row = self.cursor.fetchone()
121         if not row:
122             return None
123         else:
124             return row[0]
125
126     def did_upload_version(self, relpath_u, version, last_uploaded_uri, last_downloaded_uri, last_downloaded_timestamp, pathinfo):
127         print "%r.did_upload_version(%r, %r, %r, %r, %r, %r)" % (self, relpath_u, version, last_uploaded_uri, last_downloaded_uri, last_downloaded_timestamp, pathinfo)
128         try:
129             print "insert"
130             self.cursor.execute("INSERT INTO local_files VALUES (?,?,?,?,?,?,?,?)",
131                                 (relpath_u, pathinfo.size, pathinfo.mtime, pathinfo.ctime, version, last_uploaded_uri, last_downloaded_uri, last_downloaded_timestamp))
132         except (self.sqlite_module.IntegrityError, self.sqlite_module.OperationalError):
133             print "err... update"
134             self.cursor.execute("UPDATE local_files"
135                                 " SET size=?, mtime=?, ctime=?, version=?, last_uploaded_uri=?, last_downloaded_uri=?, last_downloaded_timestamp=?"
136                                 " WHERE path=?",
137                                 (pathinfo.size, pathinfo.mtime, pathinfo.ctime, version, last_uploaded_uri, last_downloaded_uri, last_downloaded_timestamp, relpath_u))
138         self.connection.commit()
139         print "committed"
140
141     def is_new_file(self, pathinfo, relpath_u):
142         """
143         Returns true if the file's current pathinfo (size, mtime, and ctime) has
144         changed from the pathinfo previously stored in the db.
145         """
146         c = self.cursor
147         c.execute("SELECT size, mtime, ctime"
148                   " FROM local_files"
149                   " WHERE path=?",
150                   (relpath_u,))
151         row = self.cursor.fetchone()
152         if not row:
153             return True
154         if not pathinfo.exists and row[0] is None:
155             return False
156         return (pathinfo.size, pathinfo.mtime, pathinfo.ctime) != row