mirror of
https://github.com/haiwen/seafile-server.git
synced 2025-04-27 19:15:07 +00:00
* Add and del ccnet compilation dependency in seafile * Del import ccnet * Del extra ccnet compilation dependencies * Del support WIN32
682 lines
17 KiB
C
682 lines
17 KiB
C
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
|
|
#include "common.h"
|
|
#include <glib/gstdio.h>
|
|
|
|
#include "utils.h"
|
|
#include "log.h"
|
|
|
|
#include "seafile-session.h"
|
|
#include "commit-mgr.h"
|
|
#include "branch-mgr.h"
|
|
#include "repo-mgr.h"
|
|
#include "fs-mgr.h"
|
|
#include "seafile-error.h"
|
|
|
|
#include "seaf-db.h"
|
|
|
|
#define INDEX_DIR "index"
|
|
|
|
struct _SeafRepoManagerPriv {
|
|
|
|
};
|
|
|
|
static SeafRepo *
|
|
load_repo (SeafRepoManager *manager, const char *repo_id, gboolean ret_corrupt);
|
|
|
|
gboolean
|
|
is_repo_id_valid (const char *id)
|
|
{
|
|
if (!id)
|
|
return FALSE;
|
|
|
|
return is_uuid_valid (id);
|
|
}
|
|
|
|
SeafRepo*
|
|
seaf_repo_new (const char *id, const char *name, const char *desc)
|
|
{
|
|
SeafRepo* repo;
|
|
|
|
/* valid check */
|
|
|
|
|
|
repo = g_new0 (SeafRepo, 1);
|
|
memcpy (repo->id, id, 36);
|
|
repo->id[36] = '\0';
|
|
|
|
repo->name = g_strdup(name);
|
|
repo->desc = g_strdup(desc);
|
|
|
|
repo->ref_cnt = 1;
|
|
|
|
return repo;
|
|
}
|
|
|
|
void
|
|
seaf_repo_free (SeafRepo *repo)
|
|
{
|
|
if (repo->name) g_free (repo->name);
|
|
if (repo->desc) g_free (repo->desc);
|
|
if (repo->category) g_free (repo->category);
|
|
if (repo->head) seaf_branch_unref (repo->head);
|
|
g_free (repo);
|
|
}
|
|
|
|
void
|
|
seaf_repo_ref (SeafRepo *repo)
|
|
{
|
|
g_atomic_int_inc (&repo->ref_cnt);
|
|
}
|
|
|
|
void
|
|
seaf_repo_unref (SeafRepo *repo)
|
|
{
|
|
if (!repo)
|
|
return;
|
|
|
|
if (g_atomic_int_dec_and_test (&repo->ref_cnt))
|
|
seaf_repo_free (repo);
|
|
}
|
|
|
|
static void
|
|
set_head_common (SeafRepo *repo, SeafBranch *branch)
|
|
{
|
|
if (repo->head)
|
|
seaf_branch_unref (repo->head);
|
|
repo->head = branch;
|
|
seaf_branch_ref(branch);
|
|
}
|
|
|
|
void
|
|
seaf_repo_from_commit (SeafRepo *repo, SeafCommit *commit)
|
|
{
|
|
repo->name = g_strdup (commit->repo_name);
|
|
repo->desc = g_strdup (commit->repo_desc);
|
|
repo->encrypted = commit->encrypted;
|
|
repo->repaired = commit->repaired;
|
|
if (repo->encrypted) {
|
|
repo->enc_version = commit->enc_version;
|
|
if (repo->enc_version == 1)
|
|
memcpy (repo->magic, commit->magic, 32);
|
|
else if (repo->enc_version == 2) {
|
|
memcpy (repo->magic, commit->magic, 64);
|
|
memcpy (repo->random_key, commit->random_key, 96);
|
|
}
|
|
}
|
|
repo->no_local_history = commit->no_local_history;
|
|
repo->version = commit->version;
|
|
}
|
|
|
|
void
|
|
seaf_repo_to_commit (SeafRepo *repo, SeafCommit *commit)
|
|
{
|
|
commit->repo_name = g_strdup (repo->name);
|
|
commit->repo_desc = g_strdup (repo->desc);
|
|
commit->encrypted = repo->encrypted;
|
|
commit->repaired = repo->repaired;
|
|
if (commit->encrypted) {
|
|
commit->enc_version = repo->enc_version;
|
|
if (commit->enc_version == 1)
|
|
commit->magic = g_strdup (repo->magic);
|
|
else if (commit->enc_version == 2) {
|
|
commit->magic = g_strdup (repo->magic);
|
|
commit->random_key = g_strdup (repo->random_key);
|
|
}
|
|
}
|
|
commit->no_local_history = repo->no_local_history;
|
|
commit->version = repo->version;
|
|
}
|
|
|
|
static gboolean
|
|
collect_commit (SeafCommit *commit, void *vlist, gboolean *stop)
|
|
{
|
|
GList **commits = vlist;
|
|
|
|
/* The traverse function will unref the commit, so we need to ref it.
|
|
*/
|
|
seaf_commit_ref (commit);
|
|
*commits = g_list_prepend (*commits, commit);
|
|
return TRUE;
|
|
}
|
|
|
|
GList *
|
|
seaf_repo_get_commits (SeafRepo *repo)
|
|
{
|
|
GList *branches;
|
|
GList *ptr;
|
|
SeafBranch *branch;
|
|
GList *commits = NULL;
|
|
|
|
branches = seaf_branch_manager_get_branch_list (seaf->branch_mgr, repo->id);
|
|
if (branches == NULL) {
|
|
seaf_warning ("Failed to get branch list of repo %s.\n", repo->id);
|
|
return NULL;
|
|
}
|
|
|
|
for (ptr = branches; ptr != NULL; ptr = ptr->next) {
|
|
branch = ptr->data;
|
|
gboolean res = seaf_commit_manager_traverse_commit_tree (seaf->commit_mgr,
|
|
repo->id,
|
|
repo->version,
|
|
branch->commit_id,
|
|
collect_commit,
|
|
&commits,
|
|
FALSE);
|
|
if (!res) {
|
|
for (ptr = commits; ptr != NULL; ptr = ptr->next)
|
|
seaf_commit_unref ((SeafCommit *)(ptr->data));
|
|
g_list_free (commits);
|
|
goto out;
|
|
}
|
|
}
|
|
|
|
commits = g_list_reverse (commits);
|
|
|
|
out:
|
|
for (ptr = branches; ptr != NULL; ptr = ptr->next) {
|
|
seaf_branch_unref ((SeafBranch *)ptr->data);
|
|
}
|
|
return commits;
|
|
}
|
|
|
|
SeafRepoManager*
|
|
seaf_repo_manager_new (SeafileSession *seaf)
|
|
{
|
|
SeafRepoManager *mgr = g_new0 (SeafRepoManager, 1);
|
|
|
|
mgr->priv = g_new0 (SeafRepoManagerPriv, 1);
|
|
mgr->seaf = seaf;
|
|
|
|
return mgr;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_init (SeafRepoManager *mgr)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_start (SeafRepoManager *mgr)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static gboolean
|
|
repo_exists_in_db (SeafDB *db, const char *id)
|
|
{
|
|
char sql[256];
|
|
gboolean db_err = FALSE;
|
|
|
|
snprintf (sql, sizeof(sql), "SELECT repo_id FROM Repo WHERE repo_id = '%s'",
|
|
id);
|
|
return seaf_db_check_for_existence (db, sql, &db_err);
|
|
}
|
|
|
|
static gboolean
|
|
repo_exists_in_db_ex (SeafDB *db, const char *id, gboolean *db_err)
|
|
{
|
|
char sql[256];
|
|
|
|
snprintf (sql, sizeof(sql), "SELECT repo_id FROM Repo WHERE repo_id = '%s'",
|
|
id);
|
|
return seaf_db_check_for_existence (db, sql, db_err);
|
|
}
|
|
|
|
SeafRepo*
|
|
seaf_repo_manager_get_repo (SeafRepoManager *manager, const gchar *id)
|
|
{
|
|
SeafRepo repo;
|
|
int len = strlen(id);
|
|
|
|
if (len >= 37)
|
|
return NULL;
|
|
|
|
memcpy (repo.id, id, len + 1);
|
|
|
|
if (repo_exists_in_db (manager->seaf->db, id)) {
|
|
SeafRepo *ret = load_repo (manager, id, FALSE);
|
|
if (!ret)
|
|
return NULL;
|
|
/* seaf_repo_ref (ret); */
|
|
return ret;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
SeafRepo*
|
|
seaf_repo_manager_get_repo_ex (SeafRepoManager *manager, const gchar *id)
|
|
{
|
|
int len = strlen(id);
|
|
gboolean db_err = FALSE, exists;
|
|
SeafRepo *ret = NULL;
|
|
|
|
if (len >= 37)
|
|
return NULL;
|
|
|
|
exists = repo_exists_in_db_ex (manager->seaf->db, id, &db_err);
|
|
|
|
if (db_err) {
|
|
ret = seaf_repo_new(id, NULL, NULL);
|
|
ret->is_corrupted = TRUE;
|
|
return ret;
|
|
}
|
|
|
|
if (exists) {
|
|
ret = load_repo (manager, id, TRUE);
|
|
return ret;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
gboolean
|
|
seaf_repo_manager_repo_exists (SeafRepoManager *manager, const gchar *id)
|
|
{
|
|
SeafRepo repo;
|
|
memcpy (repo.id, id, 37);
|
|
|
|
return repo_exists_in_db (manager->seaf->db, id);
|
|
}
|
|
|
|
static void
|
|
load_repo_commit (SeafRepoManager *manager,
|
|
SeafRepo *repo,
|
|
SeafBranch *branch)
|
|
{
|
|
SeafCommit *commit;
|
|
|
|
commit = seaf_commit_manager_get_commit_compatible (manager->seaf->commit_mgr,
|
|
repo->id,
|
|
branch->commit_id);
|
|
if (!commit) {
|
|
seaf_warning ("Commit %s is missing\n", branch->commit_id);
|
|
repo->is_corrupted = TRUE;
|
|
return;
|
|
}
|
|
|
|
set_head_common (repo, branch);
|
|
seaf_repo_from_commit (repo, commit);
|
|
|
|
seaf_commit_unref (commit);
|
|
}
|
|
|
|
static SeafRepo *
|
|
load_repo (SeafRepoManager *manager, const char *repo_id, gboolean ret_corrupt)
|
|
{
|
|
SeafRepo *repo;
|
|
SeafBranch *branch;
|
|
SeafVirtRepo *vinfo = NULL;
|
|
|
|
repo = seaf_repo_new(repo_id, NULL, NULL);
|
|
if (!repo) {
|
|
seaf_warning ("[repo mgr] failed to alloc repo.\n");
|
|
return NULL;
|
|
}
|
|
|
|
repo->manager = manager;
|
|
|
|
branch = seaf_branch_manager_get_branch (seaf->branch_mgr, repo_id, "master");
|
|
if (!branch) {
|
|
seaf_warning ("Failed to get master branch of repo %.8s.\n", repo_id);
|
|
repo->is_corrupted = TRUE;
|
|
} else {
|
|
load_repo_commit (manager, repo, branch);
|
|
seaf_branch_unref (branch);
|
|
}
|
|
|
|
if (repo->is_corrupted) {
|
|
if (!ret_corrupt) {
|
|
seaf_repo_free (repo);
|
|
return NULL;
|
|
}
|
|
return repo;
|
|
}
|
|
|
|
vinfo = seaf_repo_manager_get_virtual_repo_info (manager, repo_id);
|
|
if (vinfo) {
|
|
repo->is_virtual = TRUE;
|
|
memcpy (repo->store_id, vinfo->origin_repo_id, 36);
|
|
} else {
|
|
repo->is_virtual = FALSE;
|
|
memcpy (repo->store_id, repo->id, 36);
|
|
}
|
|
seaf_virtual_repo_info_free (vinfo);
|
|
|
|
return repo;
|
|
}
|
|
|
|
static gboolean
|
|
collect_repo_id (SeafDBRow *row, void *data)
|
|
{
|
|
GList **p_ids = data;
|
|
const char *repo_id;
|
|
|
|
repo_id = seaf_db_row_get_column_text (row, 0);
|
|
*p_ids = g_list_prepend (*p_ids, g_strdup(repo_id));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GList *
|
|
seaf_repo_manager_get_repo_id_list (SeafRepoManager *mgr)
|
|
{
|
|
GList *ret = NULL;
|
|
char sql[256];
|
|
|
|
snprintf (sql, 256, "SELECT repo_id FROM Repo");
|
|
|
|
if (seaf_db_foreach_selected_row (mgr->seaf->db, sql,
|
|
collect_repo_id, &ret) < 0)
|
|
return NULL;
|
|
|
|
return ret;
|
|
}
|
|
|
|
GList *
|
|
seaf_repo_manager_get_repo_list (SeafRepoManager *mgr,
|
|
int start, int limit,
|
|
gboolean *error)
|
|
{
|
|
char sql[256];
|
|
GList *id_list = NULL, *ptr;
|
|
GList *ret = NULL;
|
|
SeafRepo *repo;
|
|
|
|
*error = FALSE;
|
|
|
|
if (start == -1 && limit == -1)
|
|
snprintf (sql, 256, "SELECT repo_id FROM Repo");
|
|
else
|
|
snprintf (sql, 256, "SELECT repo_id FROM Repo LIMIT %d, %d", start, limit);
|
|
|
|
if (seaf_db_foreach_selected_row (mgr->seaf->db, sql,
|
|
collect_repo_id, &id_list) < 0)
|
|
goto error;
|
|
|
|
for (ptr = id_list; ptr; ptr = ptr->next) {
|
|
char *repo_id = ptr->data;
|
|
repo = seaf_repo_manager_get_repo_ex (mgr, repo_id);
|
|
if (repo)
|
|
ret = g_list_prepend (ret, repo);
|
|
}
|
|
|
|
string_list_free (id_list);
|
|
return ret;
|
|
|
|
error:
|
|
*error = TRUE;
|
|
string_list_free (id_list);
|
|
return NULL;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_set_repo_history_limit (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
int days)
|
|
{
|
|
SeafVirtRepo *vinfo;
|
|
SeafDB *db = mgr->seaf->db;
|
|
char sql[256];
|
|
|
|
vinfo = seaf_repo_manager_get_virtual_repo_info (mgr, repo_id);
|
|
if (vinfo) {
|
|
seaf_virtual_repo_info_free (vinfo);
|
|
return 0;
|
|
}
|
|
|
|
if (seaf_db_type(db) == SEAF_DB_TYPE_PGSQL) {
|
|
gboolean err;
|
|
snprintf(sql, sizeof(sql),
|
|
"SELECT repo_id FROM RepoHistoryLimit "
|
|
"WHERE repo_id='%s'", repo_id);
|
|
if (seaf_db_check_for_existence(db, sql, &err))
|
|
snprintf(sql, sizeof(sql),
|
|
"UPDATE RepoHistoryLimit SET days=%d"
|
|
"WHERE repo_id='%s'", days, repo_id);
|
|
else
|
|
snprintf(sql, sizeof(sql),
|
|
"INSERT INTO RepoHistoryLimit (repo_id, days) VALUES "
|
|
"('%s', %d)", repo_id, days);
|
|
if (err)
|
|
return -1;
|
|
return seaf_db_query(db, sql);
|
|
} else {
|
|
snprintf (sql, sizeof(sql),
|
|
"REPLACE INTO RepoHistoryLimit (repo_id, days) VALUES ('%s', %d)",
|
|
repo_id, days);
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static gboolean
|
|
get_limit (SeafDBRow *row, void *vdays)
|
|
{
|
|
int *days = vdays;
|
|
|
|
*days = seaf_db_row_get_column_int (row, 0);
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_get_repo_history_limit (SeafRepoManager *mgr,
|
|
const char *repo_id)
|
|
{
|
|
SeafVirtRepo *vinfo;
|
|
const char *r_repo_id = repo_id;
|
|
char sql[256];
|
|
int per_repo_days = -1;
|
|
int ret;
|
|
|
|
vinfo = seaf_repo_manager_get_virtual_repo_info (mgr, repo_id);
|
|
if (vinfo)
|
|
r_repo_id = vinfo->origin_repo_id;
|
|
|
|
snprintf (sql, sizeof(sql),
|
|
"SELECT days FROM RepoHistoryLimit WHERE repo_id='%s'",
|
|
r_repo_id);
|
|
seaf_virtual_repo_info_free (vinfo);
|
|
|
|
/* We don't use seaf_db_get_int() because we need to differ DB error
|
|
* from not exist.
|
|
* We can't just return global config value if DB error occured,
|
|
* since the global value may be smaller than per repo one.
|
|
* This can lead to data lose in GC.
|
|
*/
|
|
ret = seaf_db_foreach_selected_row (mgr->seaf->db, sql,
|
|
get_limit, &per_repo_days);
|
|
if (ret == 0) {
|
|
/* If per repo value is not set, return the global one. */
|
|
per_repo_days = seaf_cfg_manager_get_config_int (mgr->seaf->cfg_mgr,
|
|
"history", "keep_days");
|
|
}
|
|
|
|
if (per_repo_days < 0) {
|
|
per_repo_days = -1;
|
|
}
|
|
|
|
return per_repo_days;
|
|
}
|
|
|
|
int
|
|
seaf_repo_manager_set_repo_valid_since (SeafRepoManager *mgr,
|
|
const char *repo_id,
|
|
gint64 timestamp)
|
|
{
|
|
SeafDB *db = mgr->seaf->db;
|
|
char sql[256];
|
|
|
|
if (seaf_db_type(db) == SEAF_DB_TYPE_PGSQL) {
|
|
gboolean err;
|
|
snprintf(sql, sizeof(sql),
|
|
"SELECT repo_id FROM RepoValidSince WHERE "
|
|
"repo_id='%s'", repo_id);
|
|
if (seaf_db_check_for_existence(db, sql, &err))
|
|
snprintf(sql, sizeof(sql),
|
|
"UPDATE RepoValidSince SET timestamp=%"G_GINT64_FORMAT
|
|
" WHERE repo_id='%s'", timestamp, repo_id);
|
|
else
|
|
snprintf(sql, sizeof(sql),
|
|
"INSERT INTO RepoValidSince (repo_id, timestamp) VALUES "
|
|
"('%s', %"G_GINT64_FORMAT")", repo_id, timestamp);
|
|
if (err)
|
|
return -1;
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
} else {
|
|
snprintf (sql, sizeof(sql),
|
|
"REPLACE INTO RepoValidSince (repo_id, timestamp) VALUES ('%s', %"G_GINT64_FORMAT")",
|
|
repo_id, timestamp);
|
|
if (seaf_db_query (db, sql) < 0)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
gint64
|
|
seaf_repo_manager_get_repo_valid_since (SeafRepoManager *mgr,
|
|
const char *repo_id)
|
|
{
|
|
char sql[256];
|
|
|
|
snprintf (sql, sizeof(sql),
|
|
"SELECT timestamp FROM RepoValidSince WHERE repo_id='%s'",
|
|
repo_id);
|
|
/* Also return -1 if DB error. */
|
|
return seaf_db_get_int64 (mgr->seaf->db, sql);
|
|
}
|
|
|
|
gint64
|
|
seaf_repo_manager_get_repo_truncate_time (SeafRepoManager *mgr,
|
|
const char *repo_id)
|
|
{
|
|
int days;
|
|
gint64 timestamp;
|
|
|
|
days = seaf_repo_manager_get_repo_history_limit (mgr, repo_id);
|
|
timestamp = seaf_repo_manager_get_repo_valid_since (mgr, repo_id);
|
|
|
|
gint64 now = (gint64)time(NULL);
|
|
if (days > 0)
|
|
return MAX (now - days * 24 * 3600, timestamp);
|
|
else if (days < 0)
|
|
return timestamp;
|
|
else
|
|
return 0;
|
|
}
|
|
|
|
static gboolean
|
|
load_virtual_info (SeafDBRow *row, void *p_vinfo)
|
|
{
|
|
SeafVirtRepo *vinfo;
|
|
const char *origin_repo_id, *path, *base_commit;
|
|
|
|
origin_repo_id = seaf_db_row_get_column_text (row, 0);
|
|
path = seaf_db_row_get_column_text (row, 1);
|
|
base_commit = seaf_db_row_get_column_text (row, 2);
|
|
|
|
vinfo = g_new0 (SeafVirtRepo, 1);
|
|
memcpy (vinfo->origin_repo_id, origin_repo_id, 36);
|
|
vinfo->path = g_strdup(path);
|
|
memcpy (vinfo->base_commit, base_commit, 40);
|
|
|
|
*((SeafVirtRepo **)p_vinfo) = vinfo;
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
SeafVirtRepo *
|
|
seaf_repo_manager_get_virtual_repo_info (SeafRepoManager *mgr,
|
|
const char *repo_id)
|
|
{
|
|
char sql[256];
|
|
SeafVirtRepo *vinfo = NULL;
|
|
|
|
snprintf (sql, 256,
|
|
"SELECT origin_repo, path, base_commit FROM VirtualRepo "
|
|
"WHERE repo_id = '%s'", repo_id);
|
|
seaf_db_foreach_selected_row (seaf->db, sql, load_virtual_info, &vinfo);
|
|
|
|
return vinfo;
|
|
}
|
|
|
|
void
|
|
seaf_virtual_repo_info_free (SeafVirtRepo *vinfo)
|
|
{
|
|
if (!vinfo) return;
|
|
|
|
g_free (vinfo->path);
|
|
g_free (vinfo);
|
|
}
|
|
|
|
static gboolean
|
|
collect_virtual_repo_ids (SeafDBRow *row, void *data)
|
|
{
|
|
GList **p_ids = data;
|
|
const char *repo_id;
|
|
|
|
repo_id = seaf_db_row_get_column_text (row, 0);
|
|
*p_ids = g_list_prepend (*p_ids, g_strdup(repo_id));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GList *
|
|
seaf_repo_manager_get_virtual_repo_ids_by_origin (SeafRepoManager *mgr,
|
|
const char *origin_repo)
|
|
{
|
|
GList *ret = NULL;
|
|
char sql[256];
|
|
|
|
snprintf (sql, 256,
|
|
"SELECT repo_id FROM VirtualRepo WHERE origin_repo='%s'",
|
|
origin_repo);
|
|
if (seaf_db_foreach_selected_row (mgr->seaf->db, sql,
|
|
collect_virtual_repo_ids, &ret) < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
return g_list_reverse (ret);
|
|
}
|
|
|
|
static gboolean
|
|
get_garbage_repo_id (SeafDBRow *row, void *vid_list)
|
|
{
|
|
GList **ret = vid_list;
|
|
char *repo_id;
|
|
|
|
repo_id = g_strdup(seaf_db_row_get_column_text (row, 0));
|
|
*ret = g_list_prepend (*ret, repo_id);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
GList *
|
|
seaf_repo_manager_list_garbage_repos (SeafRepoManager *mgr)
|
|
{
|
|
GList *repo_ids = NULL;
|
|
|
|
seaf_db_foreach_selected_row (seaf->db,
|
|
"SELECT repo_id FROM GarbageRepos",
|
|
get_garbage_repo_id, &repo_ids);
|
|
|
|
return repo_ids;
|
|
}
|
|
|
|
void
|
|
seaf_repo_manager_remove_garbage_repo (SeafRepoManager *mgr, const char *repo_id)
|
|
{
|
|
char sql[256];
|
|
|
|
snprintf (sql, sizeof(sql), "DELETE FROM GarbageRepos WHERE repo_id='%s'",
|
|
repo_id);
|
|
seaf_db_query (seaf->db, sql);
|
|
}
|