1 #include "common.h"
2
3 #include "log.h"
4
5 #ifndef SEAFILE_SERVER
6 #include "db.h"
7 #else
8 #include "seaf-db.h"
9 #endif
10
11 #include "seafile-session.h"
12
13 #include "branch-mgr.h"
14
15 #define BRANCH_DB "branch.db"
16
17 SeafBranch *
seaf_branch_new(const char * name,const char * repo_id,const char * commit_id)18 seaf_branch_new (const char *name, const char *repo_id, const char *commit_id)
19 {
20 SeafBranch *branch;
21
22 branch = g_new0 (SeafBranch, 1);
23
24 branch->name = g_strdup (name);
25 memcpy (branch->repo_id, repo_id, 36);
26 branch->repo_id[36] = '\0';
27 memcpy (branch->commit_id, commit_id, 40);
28 branch->commit_id[40] = '\0';
29
30 branch->ref = 1;
31
32 return branch;
33 }
34
35 void
seaf_branch_free(SeafBranch * branch)36 seaf_branch_free (SeafBranch *branch)
37 {
38 if (branch == NULL) return;
39 g_free (branch->name);
40 g_free (branch);
41 }
42
43 void
seaf_branch_list_free(GList * blist)44 seaf_branch_list_free (GList *blist)
45 {
46 GList *ptr;
47
48 for (ptr = blist; ptr; ptr = ptr->next) {
49 seaf_branch_unref (ptr->data);
50 }
51 g_list_free (blist);
52 }
53
54
55 void
seaf_branch_set_commit(SeafBranch * branch,const char * commit_id)56 seaf_branch_set_commit (SeafBranch *branch, const char *commit_id)
57 {
58 memcpy (branch->commit_id, commit_id, 40);
59 branch->commit_id[40] = '\0';
60 }
61
62 void
seaf_branch_ref(SeafBranch * branch)63 seaf_branch_ref (SeafBranch *branch)
64 {
65 branch->ref++;
66 }
67
68 void
seaf_branch_unref(SeafBranch * branch)69 seaf_branch_unref (SeafBranch *branch)
70 {
71 if (!branch)
72 return;
73
74 if (--branch->ref <= 0)
75 seaf_branch_free (branch);
76 }
77
78 struct _SeafBranchManagerPriv {
79 sqlite3 *db;
80 #ifndef SEAFILE_SERVER
81 pthread_mutex_t db_lock;
82 #endif
83
84 #if defined( SEAFILE_SERVER ) && defined( FULL_FEATURE )
85 uint32_t cevent_id;
86 #endif
87 };
88
89 #if defined( SEAFILE_SERVER ) && defined( FULL_FEATURE )
90
91 #include "mq-mgr.h"
92 #include <ccnet/cevent.h>
93 static void publish_repo_update_event (CEvent *event, void *data);
94
95 #endif
96
97 static int open_db (SeafBranchManager *mgr);
98
99 SeafBranchManager *
seaf_branch_manager_new(struct _SeafileSession * seaf)100 seaf_branch_manager_new (struct _SeafileSession *seaf)
101 {
102 SeafBranchManager *mgr;
103
104 mgr = g_new0 (SeafBranchManager, 1);
105 mgr->priv = g_new0 (SeafBranchManagerPriv, 1);
106 mgr->seaf = seaf;
107
108 #ifndef SEAFILE_SERVER
109 pthread_mutex_init (&mgr->priv->db_lock, NULL);
110 #endif
111
112 return mgr;
113 }
114
115 int
seaf_branch_manager_init(SeafBranchManager * mgr)116 seaf_branch_manager_init (SeafBranchManager *mgr)
117 {
118 #if defined( SEAFILE_SERVER ) && defined( FULL_FEATURE )
119 mgr->priv->cevent_id = cevent_manager_register (seaf->ev_mgr,
120 (cevent_handler)publish_repo_update_event,
121 NULL);
122 #endif
123
124 return open_db (mgr);
125 }
126
127 static int
open_db(SeafBranchManager * mgr)128 open_db (SeafBranchManager *mgr)
129 {
130 #ifndef SEAFILE_SERVER
131
132 char *db_path;
133 const char *sql;
134
135 db_path = g_build_filename (mgr->seaf->seaf_dir, BRANCH_DB, NULL);
136 if (sqlite_open_db (db_path, &mgr->priv->db) < 0) {
137 g_critical ("[Branch mgr] Failed to open branch db\n");
138 g_free (db_path);
139 return -1;
140 }
141 g_free (db_path);
142
143 sql = "CREATE TABLE IF NOT EXISTS Branch ("
144 "name TEXT, repo_id TEXT, commit_id TEXT);";
145 if (sqlite_query_exec (mgr->priv->db, sql) < 0)
146 return -1;
147
148 sql = "CREATE INDEX IF NOT EXISTS branch_index ON Branch(repo_id, name);";
149 if (sqlite_query_exec (mgr->priv->db, sql) < 0)
150 return -1;
151
152 #elif defined FULL_FEATURE
153
154 char *sql;
155 switch (seaf_db_type (mgr->seaf->db)) {
156 case SEAF_DB_TYPE_MYSQL:
157 sql = "CREATE TABLE IF NOT EXISTS Branch ("
158 "name VARCHAR(10), repo_id CHAR(41), commit_id CHAR(41),"
159 "PRIMARY KEY (repo_id, name)) ENGINE = INNODB";
160 if (seaf_db_query (mgr->seaf->db, sql) < 0)
161 return -1;
162 break;
163 case SEAF_DB_TYPE_PGSQL:
164 sql = "CREATE TABLE IF NOT EXISTS Branch ("
165 "name VARCHAR(10), repo_id CHAR(40), commit_id CHAR(40),"
166 "PRIMARY KEY (repo_id, name))";
167 if (seaf_db_query (mgr->seaf->db, sql) < 0)
168 return -1;
169 break;
170 case SEAF_DB_TYPE_SQLITE:
171 sql = "CREATE TABLE IF NOT EXISTS Branch ("
172 "name VARCHAR(10), repo_id CHAR(41), commit_id CHAR(41),"
173 "PRIMARY KEY (repo_id, name))";
174 if (seaf_db_query (mgr->seaf->db, sql) < 0)
175 return -1;
176 break;
177 }
178
179 #endif
180
181 return 0;
182 }
183
184 int
seaf_branch_manager_add_branch(SeafBranchManager * mgr,SeafBranch * branch)185 seaf_branch_manager_add_branch (SeafBranchManager *mgr, SeafBranch *branch)
186 {
187 #ifndef SEAFILE_SERVER
188 char sql[256];
189
190 pthread_mutex_lock (&mgr->priv->db_lock);
191
192 sqlite3_snprintf (sizeof(sql), sql,
193 "SELECT 1 FROM Branch WHERE name=%Q and repo_id=%Q",
194 branch->name, branch->repo_id);
195 if (sqlite_check_for_existence (mgr->priv->db, sql))
196 sqlite3_snprintf (sizeof(sql), sql,
197 "UPDATE Branch SET commit_id=%Q WHERE "
198 "name=%Q and repo_id=%Q",
199 branch->commit_id, branch->name, branch->repo_id);
200 else
201 sqlite3_snprintf (sizeof(sql), sql,
202 "INSERT INTO Branch VALUES (%Q, %Q, %Q)",
203 branch->name, branch->repo_id, branch->commit_id);
204
205 sqlite_query_exec (mgr->priv->db, sql);
206
207 pthread_mutex_unlock (&mgr->priv->db_lock);
208
209 return 0;
210 #else
211 char *sql;
212 SeafDB *db = mgr->seaf->db;
213
214 if (seaf_db_type(db) == SEAF_DB_TYPE_PGSQL) {
215 gboolean exists, err;
216 int rc;
217
218 sql = "SELECT repo_id FROM Branch WHERE name=? AND repo_id=?";
219 exists = seaf_db_statement_exists(db, sql, &err,
220 2, "string", branch->name,
221 "string", branch->repo_id);
222 if (err)
223 return -1;
224
225 if (exists)
226 rc = seaf_db_statement_query (db,
227 "UPDATE Branch SET commit_id=? "
228 "WHERE name=? AND repo_id=?",
229 3, "string", branch->commit_id,
230 "string", branch->name,
231 "string", branch->repo_id);
232 else
233 rc = seaf_db_statement_query (db,
234 "INSERT INTO Branch VALUES (?, ?, ?)",
235 3, "string", branch->name,
236 "string", branch->repo_id,
237 "string", branch->commit_id);
238 if (rc < 0)
239 return -1;
240 } else {
241 int rc = seaf_db_statement_query (db,
242 "REPLACE INTO Branch VALUES (?, ?, ?)",
243 3, "string", branch->name,
244 "string", branch->repo_id,
245 "string", branch->commit_id);
246 if (rc < 0)
247 return -1;
248 }
249 return 0;
250 #endif
251 }
252
253 int
seaf_branch_manager_del_branch(SeafBranchManager * mgr,const char * repo_id,const char * name)254 seaf_branch_manager_del_branch (SeafBranchManager *mgr,
255 const char *repo_id,
256 const char *name)
257 {
258 #ifndef SEAFILE_SERVER
259 char *sql;
260
261 pthread_mutex_lock (&mgr->priv->db_lock);
262
263 sql = sqlite3_mprintf ("DELETE FROM Branch WHERE name = %Q AND "
264 "repo_id = '%s'", name, repo_id);
265 if (sqlite_query_exec (mgr->priv->db, sql) < 0)
266 seaf_warning ("Delete branch %s failed\n", name);
267 sqlite3_free (sql);
268
269 pthread_mutex_unlock (&mgr->priv->db_lock);
270
271 return 0;
272 #else
273 int rc = seaf_db_statement_query (mgr->seaf->db,
274 "DELETE FROM Branch WHERE name=? AND repo_id=?",
275 2, "string", name, "string", repo_id);
276 if (rc < 0)
277 return -1;
278 return 0;
279 #endif
280 }
281
282 int
seaf_branch_manager_update_branch(SeafBranchManager * mgr,SeafBranch * branch)283 seaf_branch_manager_update_branch (SeafBranchManager *mgr, SeafBranch *branch)
284 {
285 #ifndef SEAFILE_SERVER
286 sqlite3 *db;
287 char *sql;
288
289 pthread_mutex_lock (&mgr->priv->db_lock);
290
291 db = mgr->priv->db;
292 sql = sqlite3_mprintf ("UPDATE Branch SET commit_id = %Q "
293 "WHERE name = %Q AND repo_id = %Q",
294 branch->commit_id, branch->name, branch->repo_id);
295 sqlite_query_exec (db, sql);
296 sqlite3_free (sql);
297
298 pthread_mutex_unlock (&mgr->priv->db_lock);
299
300 return 0;
301 #else
302 int rc = seaf_db_statement_query (mgr->seaf->db,
303 "UPDATE Branch SET commit_id = ? "
304 "WHERE name = ? AND repo_id = ?",
305 3, "string", branch->commit_id,
306 "string", branch->name,
307 "string", branch->repo_id);
308 if (rc < 0)
309 return -1;
310 return 0;
311 #endif
312 }
313
314 #if defined( SEAFILE_SERVER ) && defined( FULL_FEATURE )
315
316 static gboolean
get_commit_id(SeafDBRow * row,void * data)317 get_commit_id (SeafDBRow *row, void *data)
318 {
319 char *out_commit_id = data;
320 const char *commit_id;
321
322 commit_id = seaf_db_row_get_column_text (row, 0);
323 memcpy (out_commit_id, commit_id, 41);
324 out_commit_id[40] = '\0';
325
326 return FALSE;
327 }
328
329 typedef struct {
330 char *repo_id;
331 char *commit_id;
332 } RepoUpdateEventData;
333
334 static void
publish_repo_update_event(CEvent * event,void * data)335 publish_repo_update_event (CEvent *event, void *data)
336 {
337 RepoUpdateEventData *rdata = event->data;
338
339 char buf[128];
340 snprintf (buf, sizeof(buf), "repo-update\t%s\t%s",
341 rdata->repo_id, rdata->commit_id);
342
343 seaf_mq_manager_publish_event (seaf->mq_mgr, buf);
344
345 g_free (rdata->repo_id);
346 g_free (rdata->commit_id);
347 g_free (rdata);
348 }
349
350 static void
on_branch_updated(SeafBranchManager * mgr,SeafBranch * branch)351 on_branch_updated (SeafBranchManager *mgr, SeafBranch *branch)
352 {
353 if (seaf_repo_manager_is_virtual_repo (seaf->repo_mgr, branch->repo_id))
354 return;
355
356 RepoUpdateEventData *rdata = g_new0 (RepoUpdateEventData, 1);
357
358 rdata->repo_id = g_strdup (branch->repo_id);
359 rdata->commit_id = g_strdup (branch->commit_id);
360
361 cevent_manager_add_event (seaf->ev_mgr, mgr->priv->cevent_id, rdata);
362 }
363
364 int
seaf_branch_manager_test_and_update_branch(SeafBranchManager * mgr,SeafBranch * branch,const char * old_commit_id)365 seaf_branch_manager_test_and_update_branch (SeafBranchManager *mgr,
366 SeafBranch *branch,
367 const char *old_commit_id)
368 {
369 SeafDBTrans *trans;
370 char *sql;
371 char commit_id[41] = { 0 };
372
373 trans = seaf_db_begin_transaction (mgr->seaf->db);
374 if (!trans)
375 return -1;
376
377 switch (seaf_db_type (mgr->seaf->db)) {
378 case SEAF_DB_TYPE_MYSQL:
379 case SEAF_DB_TYPE_PGSQL:
380 sql = "SELECT commit_id FROM Branch WHERE name=? "
381 "AND repo_id=? FOR UPDATE";
382 break;
383 case SEAF_DB_TYPE_SQLITE:
384 sql = "SELECT commit_id FROM Branch WHERE name=? "
385 "AND repo_id=?";
386 break;
387 default:
388 g_return_val_if_reached (-1);
389 }
390 if (seaf_db_trans_foreach_selected_row (trans, sql,
391 get_commit_id, commit_id,
392 2, "string", branch->name,
393 "string", branch->repo_id) < 0) {
394 seaf_db_rollback (trans);
395 seaf_db_trans_close (trans);
396 return -1;
397 }
398 if (strcmp (old_commit_id, commit_id) != 0) {
399 seaf_db_rollback (trans);
400 seaf_db_trans_close (trans);
401 return -1;
402 }
403
404 sql = "UPDATE Branch SET commit_id = ? "
405 "WHERE name = ? AND repo_id = ?";
406 if (seaf_db_trans_query (trans, sql, 3, "string", branch->commit_id,
407 "string", branch->name,
408 "string", branch->repo_id) < 0) {
409 seaf_db_rollback (trans);
410 seaf_db_trans_close (trans);
411 return -1;
412 }
413
414 if (seaf_db_commit (trans) < 0) {
415 seaf_db_rollback (trans);
416 seaf_db_trans_close (trans);
417 return -1;
418 }
419
420 seaf_db_trans_close (trans);
421
422 on_branch_updated (mgr, branch);
423
424 return 0;
425 }
426
427 #endif
428
429 #ifndef SEAFILE_SERVER
430 static SeafBranch *
real_get_branch(SeafBranchManager * mgr,const char * repo_id,const char * name)431 real_get_branch (SeafBranchManager *mgr,
432 const char *repo_id,
433 const char *name)
434 {
435 SeafBranch *branch = NULL;
436 sqlite3_stmt *stmt;
437 sqlite3 *db;
438 char *sql;
439 int result;
440
441 pthread_mutex_lock (&mgr->priv->db_lock);
442
443 db = mgr->priv->db;
444 sql = sqlite3_mprintf ("SELECT commit_id FROM Branch "
445 "WHERE name = %Q and repo_id='%s'",
446 name, repo_id);
447 if (!(stmt = sqlite_query_prepare (db, sql))) {
448 seaf_warning ("[Branch mgr] Couldn't prepare query %s\n", sql);
449 sqlite3_free (sql);
450 pthread_mutex_unlock (&mgr->priv->db_lock);
451 return NULL;
452 }
453 sqlite3_free (sql);
454
455 result = sqlite3_step (stmt);
456 if (result == SQLITE_ROW) {
457 char *commit_id = (char *)sqlite3_column_text (stmt, 0);
458
459 branch = seaf_branch_new (name, repo_id, commit_id);
460 pthread_mutex_unlock (&mgr->priv->db_lock);
461 sqlite3_finalize (stmt);
462 return branch;
463 } else if (result == SQLITE_ERROR) {
464 const char *str = sqlite3_errmsg (db);
465 seaf_warning ("Couldn't prepare query, error: %d->'%s'\n",
466 result, str ? str : "no error given");
467 }
468
469 sqlite3_finalize (stmt);
470 pthread_mutex_unlock (&mgr->priv->db_lock);
471 return NULL;
472 }
473
474 SeafBranch *
seaf_branch_manager_get_branch(SeafBranchManager * mgr,const char * repo_id,const char * name)475 seaf_branch_manager_get_branch (SeafBranchManager *mgr,
476 const char *repo_id,
477 const char *name)
478 {
479 SeafBranch *branch;
480
481 /* "fetch_head" maps to "local" or "master" on client (LAN sync) */
482 if (strcmp (name, "fetch_head") == 0) {
483 branch = real_get_branch (mgr, repo_id, "local");
484 if (!branch) {
485 branch = real_get_branch (mgr, repo_id, "master");
486 }
487 return branch;
488 } else {
489 return real_get_branch (mgr, repo_id, name);
490 }
491 }
492
493 #else
494
495 static gboolean
get_branch(SeafDBRow * row,void * vid)496 get_branch (SeafDBRow *row, void *vid)
497 {
498 char *ret = vid;
499 const char *commit_id;
500
501 commit_id = seaf_db_row_get_column_text (row, 0);
502 memcpy (ret, commit_id, 41);
503
504 return FALSE;
505 }
506
507 static SeafBranch *
real_get_branch(SeafBranchManager * mgr,const char * repo_id,const char * name)508 real_get_branch (SeafBranchManager *mgr,
509 const char *repo_id,
510 const char *name)
511 {
512 char commit_id[41];
513 char *sql;
514
515 commit_id[0] = 0;
516 sql = "SELECT commit_id FROM Branch WHERE name=? AND repo_id=?";
517 if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
518 get_branch, commit_id,
519 2, "string", name, "string", repo_id) < 0) {
520 seaf_warning ("[branch mgr] DB error when get branch %s.\n", name);
521 return NULL;
522 }
523
524 if (commit_id[0] == 0)
525 return NULL;
526
527 return seaf_branch_new (name, repo_id, commit_id);
528 }
529
530 SeafBranch *
seaf_branch_manager_get_branch(SeafBranchManager * mgr,const char * repo_id,const char * name)531 seaf_branch_manager_get_branch (SeafBranchManager *mgr,
532 const char *repo_id,
533 const char *name)
534 {
535 SeafBranch *branch;
536
537 /* "fetch_head" maps to "master" on server. */
538 if (strcmp (name, "fetch_head") == 0) {
539 branch = real_get_branch (mgr, repo_id, "master");
540 return branch;
541 } else {
542 return real_get_branch (mgr, repo_id, name);
543 }
544 }
545
546 #endif /* not SEAFILE_SERVER */
547
548 gboolean
seaf_branch_manager_branch_exists(SeafBranchManager * mgr,const char * repo_id,const char * name)549 seaf_branch_manager_branch_exists (SeafBranchManager *mgr,
550 const char *repo_id,
551 const char *name)
552 {
553 #ifndef SEAFILE_SERVER
554 char *sql;
555 gboolean ret;
556
557 pthread_mutex_lock (&mgr->priv->db_lock);
558
559 sql = sqlite3_mprintf ("SELECT name FROM Branch WHERE name = %Q "
560 "AND repo_id='%s'", name, repo_id);
561 ret = sqlite_check_for_existence (mgr->priv->db, sql);
562 sqlite3_free (sql);
563
564 pthread_mutex_unlock (&mgr->priv->db_lock);
565 return ret;
566 #else
567 gboolean db_err = FALSE;
568
569 return seaf_db_statement_exists (mgr->seaf->db,
570 "SELECT name FROM Branch WHERE name=? "
571 "AND repo_id=?", &db_err,
572 2, "string", name, "string", repo_id);
573 #endif
574 }
575
576 #ifndef SEAFILE_SERVER
577 GList *
seaf_branch_manager_get_branch_list(SeafBranchManager * mgr,const char * repo_id)578 seaf_branch_manager_get_branch_list (SeafBranchManager *mgr,
579 const char *repo_id)
580 {
581 sqlite3 *db = mgr->priv->db;
582
583 int result;
584 sqlite3_stmt *stmt;
585 char sql[256];
586 char *name;
587 char *commit_id;
588 GList *ret = NULL;
589 SeafBranch *branch;
590
591 snprintf (sql, 256, "SELECT name, commit_id FROM branch WHERE repo_id ='%s'",
592 repo_id);
593
594 pthread_mutex_lock (&mgr->priv->db_lock);
595
596 if ( !(stmt = sqlite_query_prepare(db, sql)) ) {
597 pthread_mutex_unlock (&mgr->priv->db_lock);
598 return NULL;
599 }
600
601 while (1) {
602 result = sqlite3_step (stmt);
603 if (result == SQLITE_ROW) {
604 name = (char *)sqlite3_column_text(stmt, 0);
605 commit_id = (char *)sqlite3_column_text(stmt, 1);
606 branch = seaf_branch_new (name, repo_id, commit_id);
607 ret = g_list_prepend (ret, branch);
608 }
609 if (result == SQLITE_DONE)
610 break;
611 if (result == SQLITE_ERROR) {
612 const gchar *str = sqlite3_errmsg (db);
613 seaf_warning ("Couldn't prepare query, error: %d->'%s'\n",
614 result, str ? str : "no error given");
615 sqlite3_finalize (stmt);
616 seaf_branch_list_free (ret);
617 pthread_mutex_unlock (&mgr->priv->db_lock);
618 return NULL;
619 }
620 }
621
622 sqlite3_finalize (stmt);
623 pthread_mutex_unlock (&mgr->priv->db_lock);
624 return g_list_reverse(ret);
625 }
626 #else
627 static gboolean
get_branches(SeafDBRow * row,void * vplist)628 get_branches (SeafDBRow *row, void *vplist)
629 {
630 GList **plist = vplist;
631 const char *commit_id;
632 const char *name;
633 const char *repo_id;
634 SeafBranch *branch;
635
636 name = seaf_db_row_get_column_text (row, 0);
637 repo_id = seaf_db_row_get_column_text (row, 1);
638 commit_id = seaf_db_row_get_column_text (row, 2);
639
640 branch = seaf_branch_new (name, repo_id, commit_id);
641 *plist = g_list_prepend (*plist, branch);
642
643 return TRUE;
644 }
645
646 GList *
seaf_branch_manager_get_branch_list(SeafBranchManager * mgr,const char * repo_id)647 seaf_branch_manager_get_branch_list (SeafBranchManager *mgr,
648 const char *repo_id)
649 {
650 GList *ret = NULL;
651 char *sql;
652
653 sql = "SELECT name, repo_id, commit_id FROM Branch WHERE repo_id=?";
654 if (seaf_db_statement_foreach_row (mgr->seaf->db, sql,
655 get_branches, &ret,
656 1, "string", repo_id) < 0) {
657 seaf_warning ("[branch mgr] DB error when get branch list.\n");
658 return NULL;
659 }
660
661 return ret;
662 }
663 #endif
664