blob: 93d4a3eed7941531baab43147c0fb6344348fe05 [file] [log] [blame]
Alexander Afanasyeva199f972013-01-02 19:37:26 -08001/* -*- Mode: C++; c-file-style: "gnu"; indent-tabs-mode:nil -*- */
2/*
3 * Copyright (c) 2013 University of California, Los Angeles
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2 as
7 * published by the Free Software Foundation;
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 * Author: Alexander Afanasyev <alexander.afanasyev@ucla.edu>
19 * Zhenkai Zhu <zhenkai@cs.ucla.edu>
20 */
21
22#include "sync-log.h"
23
24#include <boost/make_shared.hpp>
25
26using namespace boost;
27using namespace std;
28
Alexander Afanasyev433ecda2013-01-02 22:13:45 -080029SyncLog::SyncLog (const std::string &path, const std::string &localName)
30 : DbHelper (path)
31 , m_localName (localName)
32{
33 UpdateDeviceSeqno (localName, 0);
34
35 sqlite3_stmt *stmt;
36 int res = sqlite3_prepare_v2 (m_db, "SELECT device_id, seq_no FROM SyncNodes WHERE device_name=?", -1, &stmt, 0);
37 sqlite3_bind_text (stmt, 1, m_localName.c_str (), m_localName.size (), SQLITE_STATIC);
38
39 if (sqlite3_step (stmt) == SQLITE_ROW)
40 {
41 m_localDeviceId = sqlite3_column_int64 (stmt, 0);
42 }
43 else
44 {
45 BOOST_THROW_EXCEPTION (Error::Db ()
46 << errmsg_info_str ("Impossible thing in SyncLog::SyncLog"));
47 }
48 sqlite3_finalize (stmt);
49}
50
51
52sqlite3_int64
53SyncLog::GetNextLocalSeqNo ()
54{
55 sqlite3_stmt *stmt_seq;
56 sqlite3_prepare_v2 (m_db, "SELECT seq_no FROM SyncNodes WHERE device_id = ?", -1, &stmt_seq, 0);
57 sqlite3_bind_int64 (stmt_seq, 1, m_localDeviceId);
58
59 if (sqlite3_step (stmt_seq) != SQLITE_ROW)
60 {
61 BOOST_THROW_EXCEPTION (Error::Db ()
62 << errmsg_info_str ("Impossible thing in ActionLog::AddActionUpdate"));
63 }
64
65 sqlite3_int64 seq_no = sqlite3_column_int64 (stmt_seq, 0) + 1;
66 sqlite3_finalize (stmt_seq);
67
68 UpdateDeviceSeqNo (m_localDeviceId, seq_no);
69
70 return seq_no;
71}
72
73
Alexander Afanasyeva199f972013-01-02 19:37:26 -080074HashPtr
75SyncLog::RememberStateInStateLog ()
76{
77 int res = sqlite3_exec (m_db, "BEGIN TRANSACTION;", 0,0,0);
78
79 res += sqlite3_exec (m_db, "\
80INSERT INTO SyncLog \
81 (state_hash, last_update) \
82 SELECT \
83 hash(device_name, seq_no), datetime('now') \
84 FROM SyncNodes \
85 ORDER BY device_name; \
86", 0,0,0);
87
88 if (res != SQLITE_OK)
89 {
90 BOOST_THROW_EXCEPTION (Error::Db ()
91 << errmsg_info_str ("1"));
92 }
93
94 sqlite3_int64 rowId = sqlite3_last_insert_rowid (m_db);
95
96 sqlite3_stmt *insertStmt;
97 res += sqlite3_prepare (m_db, "\
98INSERT INTO SyncStateNodes \
99 (state_id, device_id, seq_no) \
100 SELECT ?, device_id, seq_no \
101 FROM SyncNodes; \
102", -1, &insertStmt, 0);
103
104 res += sqlite3_bind_int64 (insertStmt, 1, rowId);
105 sqlite3_step (insertStmt);
106
107 if (res != SQLITE_OK)
108 {
109 BOOST_THROW_EXCEPTION (Error::Db ()
110 << errmsg_info_str ("4"));
111 }
112 sqlite3_finalize (insertStmt);
113
114 sqlite3_stmt *getHashStmt;
115 res += sqlite3_prepare (m_db, "\
116SELECT state_hash FROM SyncLog WHERE state_id = ?\
117", -1, &getHashStmt, 0);
118 res += sqlite3_bind_int64 (getHashStmt, 1, rowId);
119
120 HashPtr retval;
121 int stepRes = sqlite3_step (getHashStmt);
122 if (stepRes == SQLITE_ROW)
123 {
124 retval = make_shared<Hash> (sqlite3_column_blob (getHashStmt, 0),
125 sqlite3_column_bytes (getHashStmt, 0));
126 }
127 sqlite3_finalize (getHashStmt);
128 res += sqlite3_exec (m_db, "COMMIT;", 0,0,0);
129
130 if (res != SQLITE_OK)
131 {
132 BOOST_THROW_EXCEPTION (Error::Db ()
133 << errmsg_info_str ("Some error with rememberStateInStateLog"));
134 }
135
136 return retval;
137}
138
139sqlite3_int64
140SyncLog::LookupSyncLog (const std::string &stateHash)
141{
142 return LookupSyncLog (*Hash::FromString (stateHash));
143}
144
145sqlite3_int64
146SyncLog::LookupSyncLog (const Hash &stateHash)
147{
148 sqlite3_stmt *stmt;
149 int res = sqlite3_prepare (m_db, "SELECT state_id FROM SyncLog WHERE state_hash = ?",
150 -1, &stmt, 0);
151
152 if (res != SQLITE_OK)
153 {
154 BOOST_THROW_EXCEPTION (Error::Db ()
155 << errmsg_info_str ("Cannot prepare statement"));
156 }
157
158 res = sqlite3_bind_blob (stmt, 1, stateHash.GetHash (), stateHash.GetHashBytes (), SQLITE_STATIC);
159 if (res != SQLITE_OK)
160 {
161 BOOST_THROW_EXCEPTION (Error::Db ()
162 << errmsg_info_str ("Cannot bind"));
163 }
164
165 sqlite3_int64 row = 0; // something bad
166
167 if (sqlite3_step (stmt) == SQLITE_ROW)
168 {
169 row = sqlite3_column_int64 (stmt, 0);
170 }
171
172 sqlite3_finalize (stmt);
173
174 return row;
175}
176
177void
Alexander Afanasyev433ecda2013-01-02 22:13:45 -0800178SyncLog::UpdateDeviceSeqno (const std::string &name, sqlite3_int64 seqNo)
Alexander Afanasyeva199f972013-01-02 19:37:26 -0800179{
180 sqlite3_stmt *stmt;
181 // update is performed using trigger
182 int res = sqlite3_prepare (m_db, "INSERT INTO SyncNodes (device_name, seq_no) VALUES (?,?);",
183 -1, &stmt, 0);
184
185 res += sqlite3_bind_text (stmt, 1, name.c_str (), name.size (), SQLITE_STATIC);
186 res += sqlite3_bind_int64 (stmt, 2, seqNo);
187 sqlite3_step (stmt);
188
189 if (res != SQLITE_OK)
190 {
191 BOOST_THROW_EXCEPTION (Error::Db ()
Alexander Afanasyev433ecda2013-01-02 22:13:45 -0800192 << errmsg_info_str ("Some error with UpdateDeviceSeqno (name)"));
Alexander Afanasyeva199f972013-01-02 19:37:26 -0800193 }
194 sqlite3_finalize (stmt);
195}
196
Alexander Afanasyev433ecda2013-01-02 22:13:45 -0800197void
198SyncLog::UpdateDeviceSeqNo (sqlite3_int64 deviceId, sqlite3_int64 seqNo)
199{
200 sqlite3_stmt *stmt;
201 // update is performed using trigger
202 int res = sqlite3_prepare (m_db, "INSERT INTO SyncNodes (device_id, seq_no) VALUES (?,?);",
203 -1, &stmt, 0);
204
205 res += sqlite3_bind_int64 (stmt, 1, deviceId);
206 res += sqlite3_bind_int64 (stmt, 2, seqNo);
207 sqlite3_step (stmt);
208
209 if (res != SQLITE_OK)
210 {
211 BOOST_THROW_EXCEPTION (Error::Db ()
212 << errmsg_info_str ("Some error with UpdateDeviceSeqNo (id)"));
213 }
214 sqlite3_finalize (stmt);
215}
216
217
Alexander Afanasyev6f70a0f2013-01-02 20:44:09 -0800218SyncStateMsgPtr
Alexander Afanasyeva199f972013-01-02 19:37:26 -0800219SyncLog::FindStateDifferences (const std::string &oldHash, const std::string &newHash)
220{
Alexander Afanasyev6f70a0f2013-01-02 20:44:09 -0800221 return FindStateDifferences (*Hash::FromString (oldHash), *Hash::FromString (newHash));
Alexander Afanasyeva199f972013-01-02 19:37:26 -0800222}
223
Alexander Afanasyev6f70a0f2013-01-02 20:44:09 -0800224SyncStateMsgPtr
Alexander Afanasyeva199f972013-01-02 19:37:26 -0800225SyncLog::FindStateDifferences (const Hash &oldHash, const Hash &newHash)
226{
227 sqlite3_stmt *stmt;
228
229 int res = sqlite3_prepare_v2 (m_db, "\
230SELECT sn.device_name, s_old.seq_no, s_new.seq_no \
231 FROM (SELECT * \
232 FROM SyncStateNodes \
233 WHERE state_id=(SELECT state_id \
234 FROM SyncLog \
235 WHERE state_hash=:old_hash)) s_old \
236 LEFT JOIN (SELECT * \
237 FROM SyncStateNodes \
238 WHERE state_id=(SELECT state_id \
239 FROM SyncLog \
240 WHERE state_hash=:new_hash)) s_new \
241 \
242 ON s_old.device_id = s_new.device_id \
243 JOIN SyncNodes sn ON sn.device_id = s_old.device_id \
244 \
245 WHERE s_new.seq_no IS NULL OR \
246 s_old.seq_no != s_new.seq_no \
247 \
248UNION ALL \
249 \
250SELECT sn.device_name, s_old.seq_no, s_new.seq_no \
251 FROM (SELECT * \
252 FROM SyncStateNodes \
253 WHERE state_id=(SELECT state_id \
254 FROM SyncLog \
255 WHERE state_hash=:new_hash )) s_new \
256 LEFT JOIN (SELECT * \
257 FROM SyncStateNodes \
258 WHERE state_id=(SELECT state_id \
259 FROM SyncLog \
260 WHERE state_hash=:old_hash)) s_old \
261 \
262 ON s_old.device_id = s_new.device_id \
263 JOIN SyncNodes sn ON sn.device_id = s_new.device_id \
264 \
265 WHERE s_old.seq_no IS NULL \
266", -1, &stmt, 0);
267
268 if (res != SQLITE_OK)
269 {
270 BOOST_THROW_EXCEPTION (Error::Db ()
271 << errmsg_info_str ("Some error with FindStateDifferences"));
272 }
273
274 res += sqlite3_bind_blob (stmt, 1, oldHash.GetHash (), oldHash.GetHashBytes (), SQLITE_STATIC);
275 res += sqlite3_bind_blob (stmt, 2, newHash.GetHash (), newHash.GetHashBytes (), SQLITE_STATIC);
276
Alexander Afanasyev6f70a0f2013-01-02 20:44:09 -0800277 SyncStateMsgPtr msg = make_shared<SyncStateMsg> ();
278
Alexander Afanasyeva199f972013-01-02 19:37:26 -0800279 while (sqlite3_step (stmt) == SQLITE_ROW)
280 {
Alexander Afanasyev6f70a0f2013-01-02 20:44:09 -0800281 SyncState *state = msg->add_state ();
282
283 state->set_name (reinterpret_cast<const char*> (sqlite3_column_text (stmt, 0)));
284
285 sqlite3_int64 newSeqNo = sqlite3_column_int64 (stmt, 2);
286 if (newSeqNo > 0)
287 {
288 state->set_type (SyncState::UPDATE);
289 state->set_seq (newSeqNo);
290 }
291 else
292 state->set_type (SyncState::DELETE);
293
294 // std::cout << sqlite3_column_text (stmt, 0) <<
295 // ": from " << sqlite3_column_int64 (stmt, 1) <<
296 // " to " << sqlite3_column_int64 (stmt, 2) <<
297 // std::endl;
Alexander Afanasyeva199f972013-01-02 19:37:26 -0800298 }
Alexander Afanasyev6f70a0f2013-01-02 20:44:09 -0800299 sqlite3_finalize (stmt);
300
301 return msg;
Alexander Afanasyeva199f972013-01-02 19:37:26 -0800302}