From 676b73839ca1fa7ac6aa7989c8bcce1c4ed175cf Mon Sep 17 00:00:00 2001 From: Janne Mareike Koschinski Date: Sun, 12 Jun 2022 19:55:31 +0200 Subject: [PATCH 1/9] Reintroduce postgres support --- ci/serverctl.py | 66 ++++- common/branch-mgr.c | 43 ++- common/config-mgr.c | 35 ++- common/group-mgr.c | 57 ++-- common/org-mgr.c | 65 +++-- common/seaf-db.c | 380 ++++++++++++++++++++++++- common/seaf-db.h | 7 +- common/seaf-utils.c | 115 ++++++-- common/user-mgr.c | 91 +++--- configure.ac | 7 + scripts/sql/pgsql/ccnet.sql | 108 +++++++ scripts/sql/pgsql/seafile.sql | 304 ++++++++++++++++++++ server/Makefile.am | 3 +- server/gc/repo-mgr.c | 20 +- server/quota-mgr.c | 112 +++----- server/repo-mgr.c | 519 +++++++++++++++++----------------- server/seafile-session.c | 24 +- server/share-mgr.c | 41 ++- 18 files changed, 1479 insertions(+), 518 deletions(-) create mode 100644 scripts/sql/pgsql/ccnet.sql create mode 100644 scripts/sql/pgsql/seafile.sql diff --git a/ci/serverctl.py b/ci/serverctl.py index 09cf05e9..13ed8789 100755 --- a/ci/serverctl.py +++ b/ci/serverctl.py @@ -49,6 +49,8 @@ def __init__(self, topdir, projectdir, datadir, fileserver, db='sqlite3', seaf_s def setup(self): if self.db == 'mysql': create_mysql_dbs() + elif self.db == 'pgsql': + create_pgsql_dbs() os.mkdir (self.central_conf_dir, 0o755) os.mkdir (self.seafile_conf_dir, 0o755) @@ -59,7 +61,9 @@ def setup(self): def init_ccnet(self): if self.db == 'mysql': - self.add_ccnet_db_conf() + self.add_ccnet_mysql_db_conf() + elif self.db == 'pgsql': + self.add_ccnet_pgsql_db_conf() else: self.add_ccnet_sqlite_db_conf() @@ -72,7 +76,7 @@ def add_ccnet_sqlite_db_conf(self): fp.write('\n') fp.write(ccnet_db_conf) - def add_ccnet_db_conf(self): + def add_ccnet_mysql_db_conf(self): ccnet_conf = join(self.central_conf_dir, 'ccnet.conf') ccnet_db_conf = '''\ [Database] @@ -83,6 +87,21 @@ def add_ccnet_db_conf(self): PASSWD = seafile DB = ccnet CONNECTION_CHARSET = utf8 +''' + with open(ccnet_conf, 'a+') as fp: + fp.write('\n') + fp.write(ccnet_db_conf) + + def add_ccnet_pgsql_db_conf(self): + ccnet_conf = join(self.central_conf_dir, 'ccnet.conf') + ccnet_db_conf = '''\ +[Database] +ENGINE = pgsql +HOST = 127.0.0.1 +PORT = 5432 +USER = seafile +PASSWD = seafile +DB = ccnet ''' with open(ccnet_conf, 'a+') as fp: fp.write('\n') @@ -119,7 +138,7 @@ def add_seafile_sqlite_db_conf(self): fp.write('\n') fp.write(seafile_db_conf) - def add_seafile_db_conf(self): + def add_seafile_mysql_db_conf(self): seafile_conf = join(self.central_conf_dir, 'seafile.conf') seafile_db_conf = '''\ [database] @@ -130,6 +149,21 @@ def add_seafile_db_conf(self): password = seafile db_name = seafile connection_charset = utf8 +''' + with open(seafile_conf, 'a+') as fp: + fp.write('\n') + fp.write(seafile_db_conf) + + def add_seafile_pgsql_db_conf(self): + seafile_conf = join(self.central_conf_dir, 'seafile.conf') + seafile_db_conf = '''\ +[database] +type = pgsql +host = 127.0.0.1 +port = 5432 +user = seafile +password = seafile +db_name = seafile ''' with open(seafile_conf, 'a+') as fp: fp.write('\n') @@ -169,6 +203,11 @@ def create_database_tables(self): seafile_sql_path = join(self.sql_dir, 'mysql', 'seafile.sql') sql = f'USE ccnet; source {ccnet_sql_path}; USE seafile; source {seafile_sql_path};'.encode() shell('sudo mysql -u root -proot', inputdata=sql, wait=False) + elif self.db == 'pgsql': + ccnet_sql_path = join(self.sql_dir, 'pgsql', 'ccnet.sql') + seafile_sql_path = join(self.sql_dir, 'pgsql', 'seafile.sql') + shell(f'sudo psql "postgres://seafile:seafile@localhost/ccnet" -f {ccnet_sql_path}', wait=False) + shell(f'sudo psql "postgres://seafile:seafile@localhost/ccnet" -f {seafile_sql_path}', wait=False) else: config_sql_path = join(self.sql_dir, 'sqlite', 'config.sql') groupmgr_sql_path = join(self.sql_dir, 'sqlite', 'groupmgr.sql') @@ -257,6 +296,8 @@ def stop(self): self.fileserver_proc.kill() if self.db == 'mysql': del_mysql_dbs() + if self.db == 'pgsql': + del_pgsql_dbs() def get_seaserv_envs(self): envs = dict(os.environ) @@ -289,3 +330,22 @@ def del_mysql_dbs(): ''' shell('sudo mysql -u root -proot', inputdata=sql) + + +def create_pgsql_dbs(): + sql = b'''\ +create user "seafile" with password 'seafile'; +create database "ccnet" with owner "seafile" encoding 'utf8'; +create database "seafile" with owner "seafile" encoding 'utf8'; + ''' + + shell('sudo psql -u root -proot', inputdata=sql) + +def del_pgsql_dbs(): + sql = b'''\ +drop database "ccnet"; +drop database "seafile"; +drop user "seafile"; + ''' + + shell('sudo psql -u root -proot', inputdata=sql) diff --git a/common/branch-mgr.c b/common/branch-mgr.c index b7964f0c..c26af182 100644 --- a/common/branch-mgr.c +++ b/common/branch-mgr.c @@ -146,9 +146,14 @@ open_db (SeafBranchManager *mgr) return -1; break; case SEAF_DB_TYPE_PGSQL: - sql = "CREATE TABLE IF NOT EXISTS Branch (" - "name VARCHAR(10), repo_id CHAR(40), commit_id CHAR(40)," - "PRIMARY KEY (repo_id, name))"; + sql = "CREATE TABLE IF NOT EXISTS Branch(" + " id BIGSERIAL PRIMARY KEY," + " name VARCHAR(10)," + " repo_id CHAR(41)," + " commit_id CHAR(41));"; + if (seaf_db_query (mgr->seaf->db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX branch_repoidname_idx ON Branch (repo_id, name);"; if (seaf_db_query (mgr->seaf->db, sql) < 0) return -1; break; @@ -197,29 +202,15 @@ seaf_branch_manager_add_branch (SeafBranchManager *mgr, SeafBranch *branch) SeafDB *db = mgr->seaf->db; if (seaf_db_type(db) == SEAF_DB_TYPE_PGSQL) { - gboolean exists, err; - int rc; - - sql = "SELECT repo_id FROM Branch WHERE name=? AND repo_id=?"; - exists = seaf_db_statement_exists(db, sql, &err, - 2, "string", branch->name, - "string", branch->repo_id); - if (err) - return -1; - - if (exists) - rc = seaf_db_statement_query (db, - "UPDATE Branch SET commit_id=? " - "WHERE name=? AND repo_id=?", - 3, "string", branch->commit_id, - "string", branch->name, - "string", branch->repo_id); - else - rc = seaf_db_statement_query (db, - "INSERT INTO Branch (name, repo_id, commit_id) VALUES (?, ?, ?)", - 3, "string", branch->name, - "string", branch->repo_id, - "string", branch->commit_id); + int rc = seaf_db_statement_query (db, + "INSERT INTO Branch (name, repo_id, commit_id)" + " VALUES (?, ?, ?)" + " ON CONFLICT (name, repo_id)" + " DO UPDATE SET commit_id=?", + 4, "string", branch->name, + "string", branch->repo_id, + "string", branch->commit_id, + "string", branch->commit_id); if (rc < 0) return -1; } else { diff --git a/common/config-mgr.c b/common/config-mgr.c index 9bf7951e..0127eba4 100644 --- a/common/config-mgr.c +++ b/common/config-mgr.c @@ -9,16 +9,31 @@ seaf_cfg_manager_init (SeafCfgManager *mgr) char *sql; int db_type = seaf_db_type(mgr->db); - if (db_type == SEAF_DB_TYPE_MYSQL) - sql = "CREATE TABLE IF NOT EXISTS SeafileConf (" - "id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, cfg_group VARCHAR(255) NOT NULL," - "cfg_key VARCHAR(255) NOT NULL, value VARCHAR(255), property INTEGER) ENGINE=INNODB"; - else - sql = "CREATE TABLE IF NOT EXISTS SeafileConf (cfg_group VARCHAR(255) NOT NULL," - "cfg_key VARCHAR(255) NOT NULL, value VARCHAR(255), property INTEGER)"; - - if (seaf_db_query (mgr->db, sql) < 0) - return -1; + switch (db_type) { + case SEAF_DB_TYPE_MYSQL: + sql = "CREATE TABLE IF NOT EXISTS SeafileConf (" + "id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, cfg_group VARCHAR(255) NOT NULL," + "cfg_key VARCHAR(255) NOT NULL, value VARCHAR(255), property INTEGER) ENGINE=INNODB"; + if (seaf_db_query (mgr->db, sql) < 0) + return -1; + break; + case SEAF_DB_TYPE_PGSQL: + sql = "CREATE TABLE IF NOT EXISTS SeafileConf(" + " id BIGSERIAL PRIMARY KEY," + " cfg_group VARCHAR(255) NOT NULL," + " cfg_key VARCHAR(255) NOT NULL," + " value VARCHAR(255)," + " property INTEGER);"; + if (seaf_db_query (mgr->db, sql) < 0) + return -1; + break; + case SEAF_DB_TYPE_SQLITE: + sql = "CREATE TABLE IF NOT EXISTS SeafileConf (cfg_group VARCHAR(255) NOT NULL," + "cfg_key VARCHAR(255) NOT NULL, value VARCHAR(255), property INTEGER)"; + if (seaf_db_query (mgr->db, sql) < 0) + return -1; + break; + } return 0; } diff --git a/common/group-mgr.c b/common/group-mgr.c index 580363df..02aa65e4 100644 --- a/common/group-mgr.c +++ b/common/group-mgr.c @@ -185,46 +185,53 @@ static int check_db_table (CcnetGroupManager *manager, CcnetDB *db) "`GroupStructure` (`path`)"; if (seaf_db_query (db, sql) < 0) return -1; - } else if (db_type == SEAF_DB_TYPE_PGSQL) { g_string_printf (group_sql, - "CREATE TABLE IF NOT EXISTS \"%s\" (group_id SERIAL" - " PRIMARY KEY, group_name VARCHAR(255)," - " creator_name VARCHAR(255), timestamp BIGINT," - " type VARCHAR(32), parent_group_id INTEGER)", table_name); + "CREATE TABLE IF NOT EXISTS \"%s\" (" + " group_id BIGSERIAL PRIMARY KEY," + " group_name VARCHAR(255)," + " creator_name VARCHAR(255)," + " timestamp BIGINT," + " type VARCHAR(32)," + " parent_group_id INTEGER)", table_name); if (seaf_db_query (db, group_sql->str) < 0) { g_string_free (group_sql, TRUE); return -1; } - sql = "CREATE TABLE IF NOT EXISTS GroupUser (group_id INTEGER," - " user_name VARCHAR(255), is_staff smallint, UNIQUE " - " (group_id, user_name))"; + sql = "CREATE TABLE IF NOT EXISTS GroupUser (" + " id BIGSERIAL PRIMARY KEY," + " group_id BIGINT," + " user_name VARCHAR(255)," + " is_staff BOOLEAN)"; if (seaf_db_query (db, sql) < 0) return -1; - - //if (!pgsql_index_exists (db, "groupuser_username_idx")) { - // sql = "CREATE INDEX groupuser_username_idx ON GroupUser (user_name)"; - // if (seaf_db_query (db, sql) < 0) - // return -1; - //} - - sql = "CREATE TABLE IF NOT EXISTS GroupDNPair (group_id INTEGER," - " dn VARCHAR(255))"; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS groupuser_group_id_user_name_key ON GroupUser (group_id, user_name)"; if (seaf_db_query (db, sql) < 0) return -1; - - sql = "CREATE TABLE IF NOT EXISTS GroupStructure (group_id INTEGER PRIMARY KEY, " - "path VARCHAR(1024))"; + sql = "CREATE INDEX IF NOT EXISTS groupuser_username_idx ON GroupUser (user_name)"; if (seaf_db_query (db, sql) < 0) return -1; - //if (!pgsql_index_exists (db, "structure_path_idx")) { - // sql = "CREATE INDEX structure_path_idx ON GroupStructure (path)"; - // if (seaf_db_query (db, sql) < 0) - // return -1; - //} + sql = "CREATE TABLE IF NOT EXISTS GroupDNPair (" + " id BIGSERIAL PRIMARY KEY," + " group_id INTEGER," + " dn VARCHAR(255))"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE TABLE IF NOT EXISTS GroupStructure (" + " id BIGSERIAL PRIMARY KEY," + " group_id INTEGER," + " path VARCHAR(1024));"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS groupstructure_groupid_idx ON GroupStructure (group_id)"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE INDEX IF NOT EXISTS structure_path_idx ON GroupStructure (path)"; + if (seaf_db_query (db, sql) < 0) + return -1; } g_string_free (group_sql, TRUE); diff --git a/common/org-mgr.c b/common/org-mgr.c index 0b336ef6..ed8b6eef 100644 --- a/common/org-mgr.c +++ b/common/org-mgr.c @@ -173,36 +173,44 @@ static int check_db_table (CcnetDB *db) if (seaf_db_query (db, sql) < 0) return -1; } else if (db_type == SEAF_DB_TYPE_PGSQL) { - sql = "CREATE TABLE IF NOT EXISTS Organization (org_id SERIAL" - " PRIMARY KEY, org_name VARCHAR(255)," - " url_prefix VARCHAR(255), creator VARCHAR(255), ctime BIGINT," - " UNIQUE (url_prefix))"; + sql = "CREATE TABLE IF NOT EXISTS Organization (" + " org_id BIGSERIAL PRIMARY KEY," + " org_name VARCHAR(255)," + " url_prefix VARCHAR(255)," + " creator VARCHAR(255)," + " ctime BIGINT);"; if (seaf_db_query (db, sql) < 0) return -1; - - sql = "CREATE TABLE IF NOT EXISTS OrgUser (org_id INTEGER, " - "email VARCHAR(255), is_staff INTEGER NOT NULL, " - "UNIQUE (org_id, email))"; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS organization_url_prefix_key ON Organization (url_prefix)"; if (seaf_db_query (db, sql) < 0) return -1; - //if (!pgsql_index_exists (db, "orguser_email_idx")) { - // sql = "CREATE INDEX orguser_email_idx ON OrgUser (email)"; - // if (seaf_db_query (db, sql) < 0) - // return -1; - //} - - sql = "CREATE TABLE IF NOT EXISTS OrgGroup (org_id INTEGER, " - "group_id INTEGER, " - "UNIQUE (org_id, group_id))"; + sql = "CREATE TABLE IF NOT EXISTS OrgUser (" + " id BIGSERIAL PRIMARY KEY," + " org_id INTEGER," + " email VARCHAR(255)," + " is_staff BOOLEAN NOT NULL);"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS orguser_org_id_email_key ON OrgUser (org_id, email)"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE INDEX IF NOT EXISTS orguser_email_idx ON OrgUser (email)"; if (seaf_db_query (db, sql) < 0) return -1; - //if (!pgsql_index_exists (db, "orggroup_groupid_idx")) { - // sql = "CREATE INDEX orggroup_groupid_idx ON OrgGroup (group_id)"; - // if (seaf_db_query (db, sql) < 0) - // return -1; - //} + sql = "CREATE TABLE IF NOT EXISTS OrgGroup (" + " id BIGSERIAL PRIMARY KEY," + " org_id INTEGER," + " group_id INTEGER);"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS orggroup_org_id_group_id_key ON OrgGroup (org_id, group_id)"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE INDEX IF NOT EXISTS orggroup_groupid_idx ON OrgGroup (group_id)"; + if (seaf_db_query (db, sql) < 0) + return -1; } return 0; @@ -663,9 +671,16 @@ ccnet_org_manager_get_org_top_groups (CcnetOrgManager *mgr, int org_id, GError * char *sql; int rc; - sql = "SELECT g.group_id, group_name, creator_name, timestamp, parent_group_id FROM " - "`OrgGroup` o, `Group` g WHERE o.group_id = g.group_id AND " - "org_id=? AND parent_group_id=-1 ORDER BY timestamp DESC"; + int db_type = seaf_db_type (db); + if (db_type == SEAF_DB_TYPE_PGSQL) { + sql = "SELECT g.group_id, group_name, creator_name, timestamp, parent_group_id FROM " + "\"OrgGroup\" o, \"Group\" g WHERE o.group_id = g.group_id AND " + "org_id=? AND parent_group_id=-1 ORDER BY timestamp DESC"; + } else { + sql = "SELECT g.group_id, group_name, creator_name, timestamp, parent_group_id FROM " + "`OrgGroup` o, `Group` g WHERE o.group_id = g.group_id AND " + "org_id=? AND parent_group_id=-1 ORDER BY timestamp DESC"; + } rc = seaf_db_statement_foreach_row (db, sql, get_org_groups, &ret, diff --git a/common/seaf-db.c b/common/seaf-db.c index 9e03bd0e..9f477ba8 100644 --- a/common/seaf-db.c +++ b/common/seaf-db.c @@ -9,6 +9,9 @@ #ifdef HAVE_MYSQL #include #endif +#ifdef HAVE_POSTGRES +#include +#endif #include #include @@ -91,7 +94,7 @@ static gboolean mysql_db_connection_ping (DBConnection *vconn); static DBConnPool * -init_conn_pool_common (int max_connections) +mysql_init_conn_pool_common (int max_connections) { DBConnPool *pool = g_new0(DBConnPool, 1); pool->connections = g_ptr_array_sized_new (max_connections); @@ -215,7 +218,7 @@ seaf_db_new_mysql (const char *host, db_ops.row_get_column_int = mysql_db_row_get_column_int; db_ops.row_get_column_int64 = mysql_db_row_get_column_int64; - db->pool = init_conn_pool_common (max_connections); + db->pool = mysql_init_conn_pool_common (max_connections); pthread_t tid; int ret = pthread_create (&tid, NULL, mysql_conn_keepalive, db->pool); @@ -230,6 +233,180 @@ seaf_db_new_mysql (const char *host, #endif +#ifdef HAVE_POSTGRES + +/* Popstgres Ops */ +static SeafDB * +pgsql_db_new (const char *host, + int port, + const char *user, + const char *password, + const char *db_name, + const char *unix_socket, + gboolean use_ssl); +static DBConnection * +pgsql_db_get_connection (SeafDB *db); +static void +pgsql_db_release_connection (DBConnection *vconn); +static int +pgsql_db_execute_sql_no_stmt (DBConnection *vconn, const char *sql); +static int +pgsql_db_execute_sql (DBConnection *vconn, const char *sql, int n, va_list args); +static int +pgsql_db_query_foreach_row (DBConnection *vconn, const char *sql, + SeafDBRowFunc callback, void *data, + int n, va_list args); +static int +pgsql_db_row_get_column_count (SeafDBRow *row); +static const char * +pgsql_db_row_get_column_string (SeafDBRow *row, int idx); +static int +pgsql_db_row_get_column_int (SeafDBRow *row, int idx); +static gint64 +pgsql_db_row_get_column_int64 (SeafDBRow *row, int idx); +static gboolean +pgsql_db_connection_ping (DBConnection *vconn); + +static DBConnPool * +pgsql_init_conn_pool_common (int max_connections) +{ + DBConnPool *pool = g_new0(DBConnPool, 1); + pool->connections = g_ptr_array_sized_new (max_connections); + pthread_mutex_init (&pool->lock, NULL); + pool->max_connections = max_connections; + + return pool; +} + +static DBConnection * +pgsql_conn_pool_get_connection (SeafDB *db) +{ + DBConnPool *pool = db->pool; + DBConnection *conn = NULL; + + if (pool->max_connections == 0) { + conn = pgsql_db_get_connection (db); + conn->pool = pool; + return conn; + } + + pthread_mutex_lock (&pool->lock); + + guint i, size = pool->connections->len; + for (i = 0; i < size; ++i) { + conn = g_ptr_array_index (pool->connections, i); + if (conn->is_available && pgsql_db_connection_ping (conn)) { + conn->is_available = FALSE; + goto out; + } + } + conn = NULL; + if (size < pool->max_connections) { + conn = pgsql_db_get_connection (db); + if (conn) { + conn->pool = pool; + conn->is_available = FALSE; + g_ptr_array_add (pool->connections, conn); + } + } + +out: + pthread_mutex_unlock (&pool->lock); + return conn; +} + +static void +pgsql_conn_pool_release_connection (DBConnection *conn, gboolean need_close) +{ + if (!conn) + return; + + if (conn->pool->max_connections == 0) { + pgsql_db_release_connection (conn); + return; + } + + if (need_close) { + pthread_mutex_lock (&conn->pool->lock); + g_ptr_array_remove (conn->pool->connections, conn); + pthread_mutex_unlock (&conn->pool->lock); + pgsql_db_release_connection (conn); + return; + } + + pthread_mutex_lock (&conn->pool->lock); + conn->is_available = TRUE; + pthread_mutex_unlock (&conn->pool->lock); +} + +#define KEEPALIVE_INTERVAL 30 +static void * +pgsql_conn_keepalive (void *arg) +{ + DBConnPool *pool = arg; + DBConnection *conn = NULL; + + while (1) { + pthread_mutex_lock (&pool->lock); + + guint i, size = pool->connections->len; + for (i = 0; i < size; ++i) { + conn = g_ptr_array_index (pool->connections, i); + if (conn->is_available) { + pgsql_db_connection_ping (conn); + } + } + pthread_mutex_unlock (&pool->lock); + + sleep (KEEPALIVE_INTERVAL); + } + + return NULL; +} + +SeafDB * +seaf_db_new_pgsql (const char *host, + int port, + const char *user, + const char *passwd, + const char *db_name, + const char *unix_socket, + gboolean use_ssl, + const char *charset, + int max_connections) +{ + SeafDB *db; + + db = pgsql_db_new (host, port, user, passwd, db_name, unix_socket, use_ssl, charset); + if (!db) + return NULL; + db->type = SEAF_DB_TYPE_PGSQL; + + db_ops.get_connection = pgsql_conn_pool_get_connection; + db_ops.release_connection = pgsql_conn_pool_release_connection; + db_ops.execute_sql_no_stmt = pgsql_db_execute_sql_no_stmt; + db_ops.execute_sql = pgsql_db_execute_sql; + db_ops.query_foreach_row = pgsql_db_query_foreach_row; + db_ops.row_get_column_count = pgsql_db_row_get_column_count; + db_ops.row_get_column_string = pgsql_db_row_get_column_string; + db_ops.row_get_column_int = pgsql_db_row_get_column_int; + db_ops.row_get_column_int64 = pgsql_db_row_get_column_int64; + + db->pool = pgsql_init_conn_pool_common (max_connections); + + pthread_t tid; + int ret = pthread_create (&tid, NULL, pgsql_conn_keepalive, db->pool); + if (ret != 0) { + seaf_warning ("Failed to create pgsql connection keepalive thread.\n"); + return NULL; + } + pthread_detach (tid); + + return db; +} + +#endif + /* SQLite Ops */ static SeafDB * sqlite_db_new (const char *db_path); @@ -1095,6 +1272,205 @@ mysql_db_row_get_column_int64 (SeafDBRow *vrow, int idx) #endif /* HAVE_MYSQL */ +#ifdef HAVE_POSTGRES + +/* Postgres DB */ + +typedef struct PostgresDB { + struct SeafDB parent; + char *host; + char *user; + char *password; + unsigned int port; + char *db_name; + char *unix_socket; + gboolean use_ssl; +} PostgresDB; + +typedef struct PostgresDBConnection { + struct DBConnection parent; + PGconn *db_conn; +} PostgresDBConnection; + +static gboolean +pgsql_db_connection_ping (DBConnection *vconn) +{ + PostgresDBConnection *conn = (PostgresDBConnection *)vconn; + + return (PQstatus(conn->db_conn) == CONNECTION_OK); +} + +static SeafDB * +pgsql_db_new (const char *host, + int port, + const char *user, + const char *password, + const char *db_name, + const char *unix_socket, + gboolean use_ssl) +{ + PostgresDB *db = g_new0 (PostgresDB, 1); + + db->host = g_strdup (host); + db->user = g_strdup (user); + db->password = g_strdup (password); + db->port = port; + db->db_name = g_strdup(db_name); + db->unix_socket = g_strdup(unix_socket); + db->use_ssl = use_ssl; + + return (SeafDB *)db; +} + +static char * +_pgsql_escape_connect_string (const char *str) +{ + GString *buf = g_string_new (NULL); + const char *p; + + for (p = str; *p != '\0'; ++p) { + if (*p == '\'' || *p == '\\') { + g_string_append_c (buf, '\\'); + g_string_append_c (buf, *p); + } else { + g_string_append_c (buf, *p); + } + } + + return g_string_free (buf, FALSE); +} + +static DBConnection * +pgsql_db_get_connection (SeafDB *vdb) +{ + PostgresDB *db = (PostgresDB *)vdb; + int conn_timeout = 1; + int read_write_timeout = 5; + PGconn *db_conn; + PostgresDBConnection *conn = NULL; + + g_string_append_printf (buf, "user='%s' ", db->user); + + esc_password = _pgsql_escape_connect_string (db->password); + g_string_append_printf (buf, "password='%s' ", esc_password); + g_free (esc_password); + + if (pool->unix_socket) { + g_string_append_printf (buf, "host='%s' ", db->unix_socket); + } else { + g_string_append_printf (buf, "host='%s' ", db->host); + } + + if (pool->port > 0) { + g_string_append_printf (buf, "port=%u ", db->port); + } + + g_string_append_printf (buf, "dbname='%s' ", db->db_name); + + if (pool->use_ssl) { + g_string_append_printf (buf, "sslmode=require"); + } + + db_conn = PQconnectdb (buf->str); + if (PQstatus (db) != CONNECTION_OK) { + seaf_warning ("Failed to init pgsql connection object.\n"); + PQfinish (db); + return NULL; + } + + conn = g_new0 (PostgresDBConnection, 1); + conn->db_conn = db_conn; + + return (DBConnection *)conn; +} + +static void +pgsql_db_release_connection (DBConnection *vconn) +{ + if (!vconn) + return; + + PostgresDBConnection *conn = (PostgresDBConnection *)vconn; + + PQfinish (conn->db_conn); + + g_free (conn); +} + +static int +pgsql_db_execute_sql_no_stmt (DBConnection *vconn, const char *sql) +{ + PostgresDBConnection *conn = (PostgresDBConnection *)vconn; + + res = PQexec(conn->db_conn, sql); + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + seaf_warning ("Failed to execute sql %s: %s\n", sql, PQresultErrorMessage(res)); + PQclear (res); + return -1; + } + + PQclear (res); + return 0; +} + +static int +pgsql_db_execute_sql (DBConnection *vconn, const char *sql, int n, va_list args) +{ + // TODO + return -1; +} + +typedef struct PgSQLDBRow { + SeafDBRow parent; + int column_count; + // TODO +} PgSQLDBRow; + +static int +pgsql_db_query_foreach_row (DBConnection *vconn, const char *sql, + SeafDBRowFunc callback, void *data, + int n, va_list args) +{ + // TODO + return 0; +} + +static int +pgsql_db_row_get_column_count (SeafDBRow *vrow) +{ + PgSQLDBRow *row = (PgSQLDBRow *)vrow; + return row->column_count; +} + +static const char * +pgsql_db_row_get_column_string (SeafDBRow *vrow, int i) +{ + PgSQLDBRow *row = (PgSQLDBRow *)vrow; + + // TODO + return NULL; +} + +static int +pgsql_db_row_get_column_int (SeafDBRow *vrow, int idx) +{ + PgSQLDBRow *row = (PgSQLDBRow *)vrow; + + // TODO + return 0; +} + +static gint64 +pgsql_db_row_get_column_int64 (SeafDBRow *vrow, int idx) +{ + PgSQLDBRow *row = (PgSQLDBRow *)vrow; + + // TODO + return 0; +} + +#endif /* HAVE_POSTGRES */ + /* SQLite DB */ /* SQLite thread synchronization rountines. diff --git a/common/seaf-db.h b/common/seaf-db.h index 3f58a4c4..5eac3416 100644 --- a/common/seaf-db.h +++ b/common/seaf-db.h @@ -28,14 +28,15 @@ seaf_db_new_mysql (const char *host, const char *charset, int max_connections); -#if 0 +#ifdef HAVE_POSTGRES SeafDB * seaf_db_new_pgsql (const char *host, - unsigned int port, + int port, const char *user, const char *passwd, - const char *db_name, + const char *db, const char *unix_socket, + gboolean use_ssl, int max_connections); #endif diff --git a/common/seaf-utils.c b/common/seaf-utils.c index 092b67eb..e3ff8013 100644 --- a/common/seaf-utils.c +++ b/common/seaf-utils.c @@ -136,8 +136,10 @@ mysql_db_start (SeafileSession *session) static int pgsql_db_start (SeafileSession *session) { - char *host, *user, *passwd, *db, *unix_socket; - unsigned int port; + char *host, *user, *passwd, *db, *unix_socket, *charset; + int port; + gboolean use_ssl = FALSE; + int max_connections = 0; GError *error = NULL; host = seaf_key_file_get_string (session->config, "database", "host", &error); @@ -146,6 +148,11 @@ pgsql_db_start (SeafileSession *session) return -1; } + port = g_key_file_get_integer (session->config, "database", "port", &error); + if (error) { + port = MYSQL_DEFAULT_PORT; + } + user = seaf_key_file_get_string (session->config, "database", "user", &error); if (!user) { seaf_warning ("DB user not set in config.\n"); @@ -163,20 +170,26 @@ pgsql_db_start (SeafileSession *session) seaf_warning ("DB name not set in config.\n"); return -1; } - port = g_key_file_get_integer (session->config, - "database", "port", &error); - if (error) { - port = 0; - g_clear_error (&error); - } unix_socket = seaf_key_file_get_string (session->config, - "database", "unix_socket", &error); + "database", "unix_socket", NULL); + + use_ssl = g_key_file_get_boolean (session->config, + "database", "use_ssl", NULL); - session->db = seaf_db_new_pgsql (host, port, user, passwd, db, unix_socket, - DEFAULT_MAX_CONNECTIONS); + if (error) + g_clear_error (&error); + max_connections = g_key_file_get_integer (session->config, + "database", "max_connections", + &error); + if (error || max_connections < 0) { + g_clear_error (&error); + max_connections = DEFAULT_MAX_CONNECTIONS; + } + + session->db = seaf_db_new_mpgql (host, port, user, passwd, db, unix_socket, use_ssl, max_connections); if (!session->db) { - seaf_warning ("Failed to start pgsql db.\n"); + seaf_warning ("Failed to start mysql db.\n"); return -1; } @@ -185,6 +198,7 @@ pgsql_db_start (SeafileSession *session) g_free (passwd); g_free (db); g_free (unix_socket); + g_free (charset); return 0; } @@ -316,6 +330,75 @@ ccnet_init_mysql_database (SeafileSession *session) #endif +#ifdef HAVE_POSTGRES + +static int +ccnet_init_pgsql_database (SeafileSession *session) +{ + char *host, *user, *passwd, *db, *unix_socket, *charset; + int port; + gboolean use_ssl = FALSE; + int max_connections = 0; + + host = ccnet_key_file_get_string (session->ccnet_config, "Database", "HOST"); + user = ccnet_key_file_get_string (session->ccnet_config, "Database", "USER"); + passwd = ccnet_key_file_get_string (session->ccnet_config, "Database", "PASSWD"); + db = ccnet_key_file_get_string (session->ccnet_config, "Database", "DB"); + + if (!host) { + seaf_warning ("DB host not set in config.\n"); + return -1; + } + if (!user) { + seaf_warning ("DB user not set in config.\n"); + return -1; + } + if (!passwd) { + seaf_warning ("DB passwd not set in config.\n"); + return -1; + } + if (!db) { + seaf_warning ("DB name not set in config.\n"); + return -1; + } + + GError *error = NULL; + port = g_key_file_get_integer (session->ccnet_config, "Database", "PORT", &error); + if (error) { + g_clear_error (&error); + port = MYSQL_DEFAULT_PORT; + } + + unix_socket = ccnet_key_file_get_string (session->ccnet_config, + "Database", "UNIX_SOCKET"); + use_ssl = g_key_file_get_boolean (session->ccnet_config, "Database", "USE_SSL", NULL); + + max_connections = g_key_file_get_integer (session->ccnet_config, + "Database", "MAX_CONNECTIONS", + &error); + if (error || max_connections < 0) { + max_connections = DEFAULT_MAX_CONNECTIONS; + g_clear_error (&error); + } + + session->ccnet_db = seaf_db_new_pgsql (host, port, user, passwd, db, unix_socket, use_ssl, max_connections); + if (!session->ccnet_db) { + seaf_warning ("Failed to open ccnet database.\n"); + return -1; + } + + g_free (host); + g_free (user); + g_free (passwd); + g_free (db); + g_free (unix_socket); + g_free (charset); + + return 0; +} + +#endif + int load_ccnet_database_config (SeafileSession *session) { @@ -334,10 +417,10 @@ load_ccnet_database_config (SeafileSession *session) ret = ccnet_init_mysql_database (session); } #endif -#if 0 - else if (strncasecmp (engine, DB_PGSQL, sizeof(DB_PGSQL)) == 0) { - ccnet_debug ("Use database PostgreSQL\n"); - ret = init_pgsql_database (session); +#ifdef HAVE_POSTGRES + else if (strcasecmp (engine, "pgsql") == 0) { + seaf_message("Use database Postgres\n"); + ret = ccnet_init_pgsql_database (session); } #endif else { diff --git a/common/user-mgr.c b/common/user-mgr.c index 74d14d13..e6cb5eb6 100644 --- a/common/user-mgr.c +++ b/common/user-mgr.c @@ -658,57 +658,72 @@ static int check_db_table (SeafDB *db) } else if (db_type == SEAF_DB_TYPE_PGSQL) { sql = "CREATE TABLE IF NOT EXISTS EmailUser (" - "id SERIAL PRIMARY KEY, " - "email VARCHAR(255), passwd VARCHAR(256), " - "is_staff INTEGER NOT NULL, is_active INTEGER NOT NULL, " - "ctime BIGINT, reference_id VARCHAR(255), UNIQUE (email))"; + " id BIGSERIAL PRIMARY KEY," + " email VARCHAR(255)," + " passwd VARCHAR(256)," + " is_staff BOOLEAN NOT NULL," + " is_active BOOLEAN NOT NULL," + " ctime BIGINT," + " reference_id VARCHAR(255))"; if (seaf_db_query (db, sql) < 0) return -1; - - //if (!pgsql_index_exists (db, "emailuser_reference_id_idx")) { - // sql = "CREATE UNIQUE INDEX emailuser_reference_id_idx ON EmailUser (reference_id)"; - // if (seaf_db_query (db, sql) < 0) - // return -1; - //} - - sql = "CREATE TABLE IF NOT EXISTS Binding (email VARCHAR(255), peer_id CHAR(41)," - "UNIQUE (peer_id))"; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS emailuser_email_key ON EmailUser (email)"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS emailuser_reference_id_idx ON EmailUser (reference_id)"; if (seaf_db_query (db, sql) < 0) return -1; - sql = "CREATE TABLE IF NOT EXISTS UserRole (email VARCHAR(255), " - " role VARCHAR(255), UNIQUE (email, role))"; + sql = "CREATE TABLE IF NOT EXISTS Binding (" + " id BIGSERIAL PRIMARY KEY," + " email VARCHAR(255)," + " peer_id CHAR(41))"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS binding_peerid_idx ON Binding (peer_id)"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE INDEX IF NOT EXISTS binding_email_idx ON Binding (email)"; if (seaf_db_query (db, sql) < 0) return -1; - //if (!pgsql_index_exists (db, "userrole_email_idx")) { - // sql = "CREATE INDEX userrole_email_idx ON UserRole (email)"; - // if (seaf_db_query (db, sql) < 0) - // return -1; - //} + sql = "CREATE TABLE IF NOT EXISTS UserRole (" + " id BIGSERIAL PRIMARY KEY," + " email VARCHAR(255)," + " \"role\" VARCHAR(255)," + " is_manual_set BOOLEAN DEFAULT false)"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS userrole_email_role_key ON UserRole (email, role)"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE INDEX IF NOT EXISTS userrole_email_idx ON UserRole (email)"; + if (seaf_db_query (db, sql) < 0) + return -1; sql = "CREATE TABLE IF NOT EXISTS LDAPUsers (" - "id SERIAL PRIMARY KEY, " - "email VARCHAR(255) NOT NULL, password VARCHAR(255) NOT NULL, " - "is_staff SMALLINT NOT NULL, is_active SMALLINT NOT NULL, extra_attrs TEXT," - "reference_id VARCHAR(255))"; + " id BIGSERIAL PRIMARY KEY," + " email VARCHAR(255) NOT NULL," + " password varchar(255) NOT NULL," + " is_staff BOOLEAN NOT NULL," + " is_active BOOLEAN NOT NULL," + " extra_attrs TEXT," + " reference_id VARCHAR(255))"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS ldapusers_email_idx ON LDAPUsers (email)"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS ldapusers_reference_id_idx ON LDAPUsers (reference_id)"; if (seaf_db_query (db, sql) < 0) return -1; - //if (!pgsql_index_exists (db, "ldapusers_email_idx")) { - // sql = "CREATE UNIQUE INDEX ldapusers_email_idx ON LDAPUsers (email)"; - // if (seaf_db_query (db, sql) < 0) - // return -1; - //} - - //if (!pgsql_index_exists (db, "ldapusers_reference_id_idx")) { - // sql = "CREATE UNIQUE INDEX ldapusers_reference_id_idx ON LDAPUsers (reference_id)"; - // if (seaf_db_query (db, sql) < 0) - // return -1; - //} - - sql = "CREATE TABLE IF NOT EXISTS LDAPConfig (cfg_group VARCHAR(255) NOT NULL," - "cfg_key VARCHAR(255) NOT NULL, value VARCHAR(255), property INTEGER)"; + sql = "CREATE TABLE IF NOT EXISTS LDAPConfig (" + " id BIGSERIAL PRIMARY KEY," + " cfg_group VARCHAR(255) NOT NULL," + " cfg_key VARCHAR(255) NOT NULL," + " value VARCHAR(255)," + " property INTEGER)"; if (seaf_db_query (db, sql) < 0) return -1; } diff --git a/configure.ac b/configure.ac index ce44fffc..bc7afe55 100644 --- a/configure.ac +++ b/configure.ac @@ -242,6 +242,13 @@ else AC_DEFINE([HAVE_MYSQL], 1, [Define to 1 if MySQL support is enabled]) fi +PKG_CHECK_MODULES(LIBPQ, [libpq], [have_pgsql="yes"], [have_pgsql="no"]) +if test "x${have_libpq}" = "xyes"; then + AC_SUBST(LIBPQ_CFLAGS) + AC_SUBST(LIBPQ_LIBS) + AC_DEFINE([HAVE_POSTGRES], 1, [Define to 1 if PostgreSQL support is enabled]) +fi + if test "${compile_ldap}" = "yes"; then if test "$bwin32" != true; then AC_CHECK_LIB(ldap, ldap_init, [have_ldap="yes"], diff --git a/scripts/sql/pgsql/ccnet.sql b/scripts/sql/pgsql/ccnet.sql new file mode 100644 index 00000000..d975a91c --- /dev/null +++ b/scripts/sql/pgsql/ccnet.sql @@ -0,0 +1,108 @@ +CREATE TABLE IF NOT EXISTS Binding ( + id BIGSERIAL PRIMARY KEY, + email VARCHAR(255), + peer_id CHAR(41)); +CREATE UNIQUE INDEX IF NOT EXISTS binding_peerid_idx ON Binding (peer_id); +CREATE INDEX IF NOT EXISTS binding_email_idx ON Binding (email); + +CREATE TABLE IF NOT EXISTS EmailUser ( + id BIGSERIAL PRIMARY KEY, + email VARCHAR(255), + passwd VARCHAR(256), + is_staff BOOLEAN NOT NULL, + is_active BOOLEAN NOT NULL, + ctime BIGINT, + reference_id VARCHAR(255)); +-- existing index, reuse old name +CREATE UNIQUE INDEX IF NOT EXISTS emailuser_email_key ON EmailUser (email); +-- existing index, reuse old name +CREATE UNIQUE INDEX IF NOT EXISTS emailuser_reference_id_idx ON EmailUser (reference_id); + +CREATE TABLE IF NOT EXISTS "Group" ( + group_id BIGSERIAL PRIMARY KEY, + group_name VARCHAR(255), + creator_name VARCHAR(255), + timestamp BIGINT, + type VARCHAR(32), + parent_group_id INTEGER); + +CREATE TABLE IF NOT EXISTS GroupDNPair ( + id BIGSERIAL PRIMARY KEY, + group_id INTEGER, + dn VARCHAR(255)); + +CREATE TABLE IF NOT EXISTS GroupStructure ( + id BIGSERIAL PRIMARY KEY, + group_id INTEGER, + path VARCHAR(1024)); +CREATE UNIQUE INDEX IF NOT EXISTS groupstructure_groupid_idx ON GroupStructure (group_id); +-- existing index, reuse old name +CREATE INDEX IF NOT EXISTS structure_path_idx ON GroupStructure (path); + +CREATE TABLE IF NOT EXISTS GroupUser ( + id BIGSERIAL PRIMARY KEY, + group_id BIGINT, + user_name VARCHAR(255), + is_staff BOOLEAN); +-- existing index, reuse old name +CREATE UNIQUE INDEX IF NOT EXISTS groupuser_group_id_user_name_key ON GroupUser (group_id, user_name); +-- existing index, reuse old name +CREATE INDEX IF NOT EXISTS groupuser_username_idx ON GroupUser (user_name); + +CREATE TABLE IF NOT EXISTS LDAPConfig ( + id BIGSERIAL PRIMARY KEY, + cfg_group VARCHAR(255) NOT NULL, + cfg_key VARCHAR(255) NOT NULL, + value VARCHAR(255), + property INTEGER); + +CREATE TABLE IF NOT EXISTS LDAPUsers ( + id BIGSERIAL PRIMARY KEY, + email VARCHAR(255) NOT NULL, + password varchar(255) NOT NULL, + is_staff BOOLEAN NOT NULL, + is_active BOOLEAN NOT NULL, + extra_attrs TEXT, + reference_id VARCHAR(255)); +-- existing index, reuse old name +CREATE UNIQUE INDEX IF NOT EXISTS ldapusers_email_idx ON LDAPUsers (email); +-- existing index, reuse old name +CREATE UNIQUE INDEX IF NOT EXISTS ldapusers_reference_id_idx ON LDAPUsers (reference_id); + +CREATE TABLE IF NOT EXISTS OrgGroup ( + id BIGSERIAL PRIMARY KEY, + org_id INTEGER, + group_id INTEGER); +-- existing index, reuse old name +CREATE UNIQUE INDEX IF NOT EXISTS orggroup_org_id_group_id_key ON OrgGroup (org_id, group_id); +-- existing index, reuse old name +CREATE INDEX IF NOT EXISTS orggroup_groupid_idx ON OrgGroup (group_id); + +CREATE TABLE IF NOT EXISTS OrgUser ( + id BIGSERIAL PRIMARY KEY, + org_id INTEGER, + email VARCHAR(255), + is_staff BOOLEAN NOT NULL); +-- existing index, reuse old name +CREATE UNIQUE INDEX IF NOT EXISTS orguser_org_id_email_key ON OrgUser (org_id, email); +-- existing index, reuse old name +CREATE INDEX IF NOT EXISTS orguser_email_idx ON OrgUser (email); + +CREATE TABLE IF NOT EXISTS Organization ( + org_id BIGSERIAL PRIMARY KEY, + org_name VARCHAR(255), + url_prefix VARCHAR(255), + creator VARCHAR(255), + ctime BIGINT); +-- existing index, reuse old name +CREATE UNIQUE INDEX IF NOT EXISTS organization_url_prefix_key ON Organization (url_prefix); + +CREATE TABLE IF NOT EXISTS UserRole ( + id BIGSERIAL PRIMARY KEY, + email VARCHAR(255), + "role" VARCHAR(255), + is_manual_set BOOLEAN DEFAULT false); +-- existing index, reuse old name +CREATE UNIQUE INDEX IF NOT EXISTS userrole_email_role_key ON UserRole (email, role); +-- existing index, reuse old name +CREATE INDEX IF NOT EXISTS userrole_email_idx ON UserRole (email); diff --git a/scripts/sql/pgsql/seafile.sql b/scripts/sql/pgsql/seafile.sql new file mode 100644 index 00000000..79142432 --- /dev/null +++ b/scripts/sql/pgsql/seafile.sql @@ -0,0 +1,304 @@ +CREATE TABLE IF NOT EXISTS Branch ( + id BIGSERIAL PRIMARY KEY, + name VARCHAR(10), + repo_id CHAR(41), + commit_id CHAR(41)); +CREATE UNIQUE INDEX IF NOT EXISTS branch_repoidname_idx ON Branch (repo_id, name); + +CREATE TABLE IF NOT EXISTS FileLockTimestamp ( + id BIGSERIAL PRIMARY KEY, + repo_id CHAR(40), + update_time BIGINT NOT NULL); +CREATE UNIQUE INDEX IF NOT EXISTS filelocktimestamp_repoid_idx ON FileLockTimestamp (repo_id); + +CREATE TABLE IF NOT EXISTS FileLocks ( + id BIGSERIAL PRIMARY KEY, + repo_id CHAR(40) NOT NULL, + path TEXT NOT NULL, + user_name VARCHAR(255) NOT NULL, + lock_time BIGINT, + expire BIGINT); +CREATE INDEX IF NOT EXISTS filelocks_repoid_idx ON FileLocks (repo_id); + +CREATE TABLE IF NOT EXISTS FolderGroupPerm ( + id BIGSERIAL PRIMARY KEY, + repo_id CHAR(36) NOT NULL, + path TEXT NOT NULL, + permission CHAR(15), + group_id INTEGER NOT NULL); +CREATE INDEX IF NOT EXISTS foldergroupperm_repoid_idx ON FolderGroupPerm (repo_id); + +CREATE TABLE IF NOT EXISTS FolderPermTimestamp ( + id BIGSERIAL PRIMARY KEY, + repo_id CHAR(36), + timestamp BIGINT); +CREATE UNIQUE INDEX IF NOT EXISTS folderpermtimestamp_repoid_idx ON FolderPermTimestamp (repo_id); + +CREATE TABLE IF NOT EXISTS FolderUserPerm ( + id BIGSERIAL PRIMARY KEY, + repo_id CHAR(36) NOT NULL, + path TEXT NOT NULL, + permission CHAR(15), + "user" VARCHAR(255) NOT NULL); +CREATE INDEX IF NOT EXISTS folderuserperm_repoid_idx ON FolderUserPerm (repo_id); + +CREATE TABLE IF NOT EXISTS GCID ( + id BIGSERIAL PRIMARY KEY, + repo_id CHAR(36), + gc_id CHAR(36)); +CREATE UNIQUE INDEX IF NOT EXISTS gcid_repoid_idx ON GCID (repo_id); + +CREATE TABLE IF NOT EXISTS GarbageRepos ( + id BIGSERIAL PRIMARY KEY, + repo_id CHAR(36)); +CREATE UNIQUE INDEX IF NOT EXISTS garbagerepos_repoid_idx ON GarbageRepos (repo_id); + +CREATE TABLE IF NOT EXISTS InnerPubRepo ( + id BIGSERIAL PRIMARY KEY, + repo_id CHAR(37), + permission CHAR(15)); +CREATE UNIQUE INDEX IF NOT EXISTS innerpubrepo_repoid_idx ON InnerPubRepo (repo_id); + +CREATE TABLE IF NOT EXISTS LastGCID ( + id BIGSERIAL PRIMARY KEY, + repo_id CHAR(36), + client_id VARCHAR(128), + gc_id CHAR(36)); +CREATE UNIQUE INDEX IF NOT EXISTS lastgcid_repoid_clientid_idx ON LastGCID (repo_id, client_id); + +CREATE TABLE IF NOT EXISTS OrgGroupRepo ( + id BIGSERIAL PRIMARY KEY, + org_id INTEGER, + repo_id CHAR(37), + group_id INTEGER, + "owner" VARCHAR(255), + permission CHAR(15)); +CREATE UNIQUE INDEX IF NOT EXISTS orggrouprepo_orgid_groupid_repoid_idx ON OrgGroupRepo (org_id, group_id, repo_id); +CREATE INDEX IF NOT EXISTS orggrouprepo_repoid_idx ON OrgGroupRepo (repo_id); +CREATE INDEX IF NOT EXISTS orggrouprepo_owner_idx ON OrgGroupRepo (owner); + +CREATE TABLE IF NOT EXISTS OrgInnerPubRepo ( + id BIGSERIAL PRIMARY KEY, + org_id INTEGER, + repo_id CHAR(37), + permission CHAR(15)); +CREATE UNIQUE INDEX IF NOT EXISTS orginnerpubrepo_orgid_repoid_idx ON OrgInnerPubRepo (org_id, repo_id); + +CREATE TABLE IF NOT EXISTS OrgQuota ( + id BIGSERIAL PRIMARY KEY, + org_id INTEGER, + quota BIGINT); +CREATE UNIQUE INDEX IF NOT EXISTS orgquota_orgid_idx ON OrgQuota (org_id); + +CREATE TABLE IF NOT EXISTS OrgRepo ( + id BIGSERIAL PRIMARY KEY, + org_id INTEGER, + repo_id CHAR(37), + "user" VARCHAR(255)); +CREATE UNIQUE INDEX IF NOT EXISTS orgrepo_orgid_repoid_idx ON OrgRepo (org_id, repo_id); +CREATE UNIQUE INDEX IF NOT EXISTS orgrepo_repoid_idx ON OrgRepo (repo_id); +CREATE INDEX IF NOT EXISTS orgrepo_orgid_user_idx ON OrgRepo (org_id, "user"); +CREATE INDEX IF NOT EXISTS orgrepo_user_idx ON OrgRepo ("user"); + +CREATE TABLE IF NOT EXISTS OrgSharedRepo ( + id SERIAL PRIMARY KEY, + org_id INT, + repo_id CHAR(37), + from_email VARCHAR(255), + to_email VARCHAR(255), + permission CHAR(15)); +CREATE INDEX IF NOT EXISTS orgsharedrepo_repoid_idx ON OrgSharedRepo (repo_id); +CREATE INDEX IF NOT EXISTS orgsharedrepo_orgid_repoid_idx ON OrgSharedRepo (org_id, repo_id); +CREATE INDEX IF NOT EXISTS orgsharedrepo_from_email_idx ON OrgSharedRepo (from_email); +CREATE INDEX IF NOT EXISTS orgsharedrepo_to_email_idx ON OrgSharedRepo (to_email); + +CREATE TABLE IF NOT EXISTS OrgUserQuota ( + id BIGSERIAL PRIMARY KEY, + org_id INTEGER, + "user" VARCHAR(255), + quota BIGINT); +CREATE UNIQUE INDEX IF NOT EXISTS orguserquota_orgid_user_idx ON OrgUserQuota (org_id, "user"); + +CREATE TABLE IF NOT EXISTS Repo ( + id BIGSERIAL PRIMARY KEY, + repo_id CHAR(37)); +CREATE UNIQUE INDEX IF NOT EXISTS repo_repoid_idx ON Repo (repo_id); + +CREATE TABLE IF NOT EXISTS RepoFileCount ( + id BIGSERIAL PRIMARY KEY, + repo_id CHAR(36), + file_count BIGINT); +CREATE UNIQUE INDEX IF NOT EXISTS repofilecount_repoid_idx ON RepoFileCount (repo_id); + +CREATE TABLE IF NOT EXISTS RepoGroup ( + id BIGSERIAL PRIMARY KEY, + repo_id CHAR(37), + group_id INTEGER, + user_name VARCHAR(255), + permission CHAR(15)); +-- existing index, reuse old name +CREATE UNIQUE INDEX IF NOT EXISTS repogroup_group_id_repo_id_idx ON RepoGroup (group_id, repo_id); +-- existing index, reuse old name +CREATE INDEX IF NOT EXISTS repogroup_repoid_idx ON RepoGroup (repo_id); +-- existing index, reuse old name +CREATE INDEX IF NOT EXISTS repogroup_username_idx ON RepoGroup (user_name); + +CREATE TABLE IF NOT EXISTS RepoHead ( + id BIGSERIAL PRIMARY KEY, + repo_id CHAR(37), + branch_name VARCHAR(10)); +CREATE UNIQUE INDEX IF NOT EXISTS repohead_repoid_idx ON RepoHead (repo_id); + +CREATE TABLE IF NOT EXISTS RepoHistoryLimit ( + id BIGSERIAL PRIMARY KEY, + repo_id CHAR(37), + days INTEGER); +CREATE UNIQUE INDEX IF NOT EXISTS repohistorylimit_repoid_idx ON RepoHistoryLimit (repo_id); + +CREATE TABLE IF NOT EXISTS RepoInfo ( + id BIGSERIAL PRIMARY KEY, + repo_id CHAR(36), + name VARCHAR(255) NOT NULL, + update_time BIGINT, + version INTEGER, + is_encrypted INTEGER, + last_modifier VARCHAR(255), + status INTEGER DEFAULT 0); +CREATE UNIQUE INDEX IF NOT EXISTS repoinfo_repoid_idx ON RepoInfo (repo_id); + +CREATE TABLE IF NOT EXISTS RepoOwner ( + id BIGSERIAL PRIMARY KEY, + repo_id CHAR(37), + owner_id VARCHAR(255)); +CREATE UNIQUE INDEX IF NOT EXISTS repoowner_repoid_idx ON RepoOwner (repo_id); +-- existing index, reuse old name +CREATE INDEX IF NOT EXISTS repoowner_owner_idx ON RepoOwner (owner_id); + +CREATE TABLE IF NOT EXISTS RepoSize ( + id BIGSERIAL PRIMARY KEY, + repo_id CHAR(37), + size BIGINT, + head_id CHAR(41)); +CREATE UNIQUE INDEX IF NOT EXISTS reposize_repoid_idx ON RepoSize (repo_id); + +CREATE TABLE IF NOT EXISTS RepoStorageId ( + id BIGSERIAL PRIMARY KEY, + repo_id CHAR(40) NOT NULL, + storage_id VARCHAR(255) NOT NULL); +CREATE UNIQUE INDEX IF NOT EXISTS repostorageid_repoid_idx ON RepoStorageId (repo_id); + +CREATE TABLE IF NOT EXISTS RepoSyncError ( + id BIGSERIAL PRIMARY KEY, + token CHAR(41), + error_time BIGINT, + error_con VARCHAR(1024)); +-- existing index, reuse old name +CREATE UNIQUE INDEX IF NOT EXISTS reposyncerror_token_key ON RepoSyncError (token); + +CREATE TABLE IF NOT EXISTS RepoTokenPeerInfo ( + id BIGSERIAL PRIMARY KEY, + token CHAR(41), + peer_id CHAR(41), + peer_ip VARCHAR(41), + peer_name VARCHAR(255), + sync_time BIGINT, + client_ver VARCHAR(20)); +CREATE UNIQUE INDEX IF NOT EXISTS repotokenpeerinfo_token_idx ON RepoTokenPeerInfo (token); +CREATE INDEX IF NOT EXISTS repotokenpeerinfo_peerid_idx ON RepoTokenPeerInfo (peer_id); + +CREATE TABLE IF NOT EXISTS RepoTrash ( + id BIGSERIAL PRIMARY KEY, + repo_id CHAR(36), + repo_name VARCHAR(255), + head_id CHAR(40), + owner_id VARCHAR(255), + size BIGINT, + org_id INTEGER, + del_time BIGINT); +CREATE UNIQUE INDEX IF NOT EXISTS repotrash_repoid_idx ON RepoTrash (repo_id); +-- existing index, reuse old name +CREATE INDEX IF NOT EXISTS repotrash_owner_id ON RepoTrash (owner_id); +-- existing index, reuse old name +CREATE INDEX IF NOT EXISTS repotrash_org_id ON RepoTrash (org_id); + +CREATE TABLE IF NOT EXISTS RepoUserToken ( + id BIGSERIAL PRIMARY KEY, + repo_id CHAR(37), + email VARCHAR(255), + token CHAR(41)); +-- existing index, reuse old name +CREATE UNIQUE INDEX IF NOT EXISTS repousertoken_repo_id_token_key ON RepoUserToken (repo_id, token); +CREATE INDEX IF NOT EXISTS repousertoken_token_idx ON RepoUserToken (token); +-- existing index, reuse old name +CREATE INDEX IF NOT EXISTS repousertoken_email_idx ON RepoUserToken (email); + +CREATE TABLE IF NOT EXISTS RepoValidSince ( + id BIGSERIAL PRIMARY KEY, + repo_id CHAR(37), + timestamp BIGINT); +CREATE UNIQUE INDEX IF NOT EXISTS repovalidsince_repoid_idx ON RepoValidSince (repo_id); + +CREATE TABLE IF NOT EXISTS RoleQuota ( + id BIGSERIAL PRIMARY KEY, + "role" VARCHAR(255), + quota BIGINT); +CREATE UNIQUE INDEX IF NOT EXISTS rolequota_role_idx ON RoleQuota ("role"); + +CREATE TABLE IF NOT EXISTS SeafileConf ( + id BIGSERIAL PRIMARY KEY, + cfg_group VARCHAR(255) NOT NULL, + cfg_key VARCHAR(255) NOT NULL, + value VARCHAR(255), + property INTEGER); + +CREATE TABLE IF NOT EXISTS SharedRepo ( + id BIGSERIAL PRIMARY KEY, + repo_id CHAR(37), + from_email VARCHAR(255), + to_email VARCHAR(255), + permission CHAR(15)); +-- existing index, reuse old name +CREATE INDEX IF NOT EXISTS sharedrepo_from_email_idx ON SharedRepo (from_email); +-- existing index, reuse old name +CREATE INDEX IF NOT EXISTS sharedrepo_repoid_idx ON SharedRepo (repo_id); +-- existing index, reuse old name +CREATE INDEX IF NOT EXISTS sharedrepo_to_email_idx ON SharedRepo (to_email); + +CREATE TABLE IF NOT EXISTS SystemInfo ( + id BIGSERIAL PRIMARY KEY, + info_key VARCHAR(256), + info_value VARCHAR(1024)); + +CREATE TABLE IF NOT EXISTS UserQuota ( + id BIGSERIAL PRIMARY KEY, + "user" VARCHAR(255), + quota BIGINT); +CREATE UNIQUE INDEX IF NOT EXISTS userquota_user_idx ON UserQuota ("user"); + +CREATE TABLE IF NOT EXISTS UserShareQuota ( + id BIGSERIAL PRIMARY KEY, + "user" VARCHAR(255), + quota BIGINT); +CREATE UNIQUE INDEX IF NOT EXISTS usersharequota_user_idx ON UserShareQuota ("user"); + +CREATE TABLE IF NOT EXISTS VirtualRepo ( + id BIGSERIAL PRIMARY KEY, + repo_id CHAR(36), + origin_repo CHAR(36), + path TEXT, + base_commit CHAR(40)); +CREATE UNIQUE INDEX IF NOT EXISTS virtualrepo_repoid_idx ON VirtualRepo (repo_id); +-- existing index, reuse old name +CREATE INDEX IF NOT EXISTS virtualrepo_origin_repo_idx ON VirtualRepo (origin_repo); + +CREATE TABLE IF NOT EXISTS WebAP ( + id BIGSERIAL PRIMARY KEY, + repo_id CHAR(37), + access_property CHAR(10)); +CREATE UNIQUE INDEX IF NOT EXISTS webap_repoid_idx ON WebAP (repo_id); + +CREATE TABLE IF NOT EXISTS WebUploadTempFiles ( + id BIGSERIAL PRIMARY KEY, + repo_id CHAR(40) NOT NULL, + file_path TEXT NOT NULL, + tmp_file_path TEXT NOT NULL); diff --git a/server/Makefile.am b/server/Makefile.am index 49150c2f..31a7d74f 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -13,6 +13,7 @@ AM_CFLAGS = -DPKGDATADIR=\"$(pkgdatadir)\" \ @MSVC_CFLAGS@ \ @LIBARCHIVE_CFLAGS@ \ @MYSQL_CFLAGS@ \ + @LIBPQ_CFLAGS@ \ -Wall bin_PROGRAMS = seaf-server @@ -81,4 +82,4 @@ seaf_server_LDADD = $(top_builddir)/lib/libseafile_common.la \ $(top_builddir)/common/cdc/libcdc.la \ @SEARPC_LIBS@ @JANSSON_LIBS@ ${LIB_WS32} @ZLIB_LIBS@ \ @LIBARCHIVE_LIBS@ @LIB_ICONV@ \ - @LDAP_LIBS@ @MYSQL_LIBS@ -lsqlite3 + @LDAP_LIBS@ @MYSQL_LIBS@ @LIBPQ_LIBS@ -lsqlite3 diff --git a/server/gc/repo-mgr.c b/server/gc/repo-mgr.c index 58f21cce..ab5b6c17 100644 --- a/server/gc/repo-mgr.c +++ b/server/gc/repo-mgr.c @@ -428,21 +428,13 @@ seaf_repo_manager_set_repo_history_limit (SeafRepoManager *mgr, } 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) + if (seaf_db_statement_query (db, + "INSERT INTO RepoHistoryLimit (repo_id, days)" + " VALUES (?, ?)" + " ON CONFLICT (repo_id)" + " DO UPDATE SET days=?", + 2, "string", repo_id, "int", days, "int", days) < 0) return -1; - return seaf_db_query(db, sql); } else { snprintf (sql, sizeof(sql), "REPLACE INTO RepoHistoryLimit (repo_id, days) VALUES ('%s', %d)", diff --git a/server/quota-mgr.c b/server/quota-mgr.c index e33097f4..633fe5dc 100644 --- a/server/quota-mgr.c +++ b/server/quota-mgr.c @@ -85,23 +85,44 @@ seaf_quota_manager_init (SeafQuotaManager *mgr) switch (seaf_db_type(db)) { case SEAF_DB_TYPE_PGSQL: - sql = "CREATE TABLE IF NOT EXISTS UserQuota (\"user\" VARCHAR(255) PRIMARY KEY," - "quota BIGINT)"; + sql = "CREATE TABLE IF NOT EXISTS UserQuota (" + " id BIGSERIAL PRIMARY KEY," + " \"user\" VARCHAR(255)," + " quota BIGINT)"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS userquota_user_idx ON UserQuota (\"user\");"; if (seaf_db_query (db, sql) < 0) return -1; - sql = "CREATE TABLE IF NOT EXISTS UserShareQuota (\"user\" VARCHAR(255) PRIMARY KEY," - "quota BIGINT)"; + sql = "CREATE TABLE IF NOT EXISTS UserShareQuota (" + " id BIGSERIAL PRIMARY KEY," + " \"user\" VARCHAR(255)," + " quota BIGINT)"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS usersharequota_user_idx ON UserShareQuota (\"user\")"; if (seaf_db_query (db, sql) < 0) return -1; - sql = "CREATE TABLE IF NOT EXISTS OrgQuota (org_id INTEGER PRIMARY KEY," - "quota BIGINT)"; + sql = "CREATE TABLE IF NOT EXISTS OrgQuota (" + " id BIGSERIAL PRIMARY KEY," + " org_id INTEGER," + " quota BIGINT)"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS orgquota_orgid_idx ON OrgQuota (org_id)"; if (seaf_db_query (db, sql) < 0) return -1; - sql = "CREATE TABLE IF NOT EXISTS OrgUserQuota (org_id INTEGER," - "\"user\" VARCHAR(255), quota BIGINT, PRIMARY KEY (org_id, \"user\"))"; + sql = "CREATE TABLE IF NOT EXISTS OrgUserQuota (" + " id BIGSERIAL PRIMARY KEY," + " org_id INTEGER," + " \"user\" VARCHAR(255)," + " quota BIGINT)"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS orguserquota_orgid_user_idx ON OrgUserQuota (org_id, \"user\")"; if (seaf_db_query (db, sql) < 0) return -1; @@ -167,25 +188,13 @@ seaf_quota_manager_set_user_quota (SeafQuotaManager *mgr, { SeafDB *db = mgr->session->db; if (seaf_db_type(db) == SEAF_DB_TYPE_PGSQL) { - gboolean exists, err; int rc; - - exists = seaf_db_statement_exists (db, - "SELECT 1 FROM UserQuota WHERE \"user\"=?", - &err, 1, "string", user); - if (err) - return -1; - - if (exists) - rc = seaf_db_statement_query (db, - "UPDATE UserQuota SET quota=? " - "WHERE \"user\"=?", - 2, "int64", quota, "string", user); - else - rc = seaf_db_statement_query (db, - "INSERT INTO UserQuota (\"user\", quota) VALUES " - "(?, ?)", - 2, "string", user, "int64", quota); + rc = seaf_db_statement_query (db, + "INSERT INTO UserQuota (user, quota)" + " VALUES (?, ?)" + " ON CONFLICT (user)" + " DO UPDATE SET quota=?", + 3, "string", user, "int64", quota, "int64", quota); return rc; } else { int rc; @@ -224,24 +233,12 @@ seaf_quota_manager_set_org_quota (SeafQuotaManager *mgr, SeafDB *db = mgr->session->db; if (seaf_db_type(db) == SEAF_DB_TYPE_PGSQL) { - gboolean exists, err; - int rc; - - exists = seaf_db_statement_exists (db, - "SELECT 1 FROM OrgQuota WHERE org_id=?", - &err, 1, "int", org_id); - if (err) - return -1; - - if (exists) - rc = seaf_db_statement_query (db, - "UPDATE OrgQuota SET quota=? WHERE org_id=?", - 2, "int64", quota, "int", org_id); - else - rc = seaf_db_statement_query (db, - "INSERT INTO OrgQuota (org_id, quota) VALUES (?, ?)", - 2, "int", org_id, "int64", quota); - return rc; + int rc = seaf_db_statement_query (db, + "INSERT INTO OrgQuota (org_id, quota)" + " VALUES (?, ?)" + " ON CONFLICT (org_id)" + " DO UPDATE SET quota=?", + 2, "int", org_id, "int64", quota, "int64", quota); } else { int rc = seaf_db_statement_query (db, "REPLACE INTO OrgQuota (org_id, quota) VALUES (?, ?)", @@ -275,27 +272,12 @@ seaf_quota_manager_set_org_user_quota (SeafQuotaManager *mgr, int rc; if (seaf_db_type(db) == SEAF_DB_TYPE_PGSQL) { - gboolean exists, err; - - exists = seaf_db_statement_exists (db, - "SELECT 1 FROM OrgUserQuota " - "WHERE org_id=? AND \"user\"=?", - &err, 2, "int", org_id, "string", user); - if (err) - return -1; - - if (exists) - rc = seaf_db_statement_query (db, - "UPDATE OrgUserQuota SET quota=?" - " WHERE org_id=? AND \"user\"=?", - 3, "int64", quota, "int", org_id, - "string", user); - else - rc = seaf_db_statement_query (db, - "INSERT INTO OrgUserQuota (org_id, \"user\", quota) VALUES " - "(?, ?, ?)", - 3, "int", org_id, "string", user, - "int64", quota); + rc = seaf_db_statement_query (db, + "INSERT INTO OrgUserQuota (org_id, user, quota)" + " VALUES (?, ?, ?)" + " ON CONFLICT (org_id, user)" + " DO UPDATE SET quota=?", + 3, "int", org_id, "string", user, "int64", quota, "int64", quota); return rc; } else { rc = seaf_db_statement_query (db, diff --git a/server/repo-mgr.c b/server/repo-mgr.c index e9a76686..66a17d3f 100644 --- a/server/repo-mgr.c +++ b/server/repo-mgr.c @@ -381,22 +381,11 @@ static int add_deleted_repo_record (SeafRepoManager *mgr, const char *repo_id) { if (seaf_db_type(seaf->db) == SEAF_DB_TYPE_PGSQL) { - gboolean exists, err; - - exists = seaf_db_statement_exists (seaf->db, - "SELECT repo_id FROM GarbageRepos " - "WHERE repo_id=?", - &err, 1, "string", repo_id); - if (err) - return -1; - - if (!exists) { - return seaf_db_statement_query(seaf->db, - "INSERT INTO GarbageRepos (repo_id) VALUES (?)", - 1, "string", repo_id); - } - - return 0; + return seaf_db_statement_query (seaf->db, + "INSERT INTO GarbageRepos (repo_id)" + " VALUES (?)" + " ON CONFLICT DO NOTHING", + 1, "string", repo_id); } else { return seaf_db_statement_query (seaf->db, "REPLACE INTO GarbageRepos (repo_id) VALUES (?)", @@ -804,27 +793,14 @@ static int save_branch_repo_map (SeafRepoManager *manager, SeafBranch *branch) { if (seaf_db_type(seaf->db) == SEAF_DB_TYPE_PGSQL) { - gboolean exists, err; - int rc; - - exists = seaf_db_statement_exists (seaf->db, - "SELECT repo_id FROM RepoHead WHERE repo_id=?", - &err, 1, "string", branch->repo_id); - if (err) - return -1; - - if (exists) - rc = seaf_db_statement_query (seaf->db, - "UPDATE RepoHead SET branch_name=? " - "WHERE repo_id=?", - 2, "string", branch->name, - "string", branch->repo_id); - else - rc = seaf_db_statement_query (seaf->db, - "INSERT INTO RepoHead (repo_id, branch_name) VALUES (?, ?)", - 2, "string", branch->repo_id, - "string", branch->name); - return rc; + return seaf_db_statement_query (seaf->db, + "INSERT INTO RepoHead (repo_id, branch_name)" + " VALUES (?, ?)" + " ON CONFLICT (repo_id)" + " DO UPDATE SET branch_name=?", + 2, "string", branch->repo_id, + "string", branch->name, + "string", branch->name); } else { return seaf_db_statement_query (seaf->db, "REPLACE INTO RepoHead (repo_id, branch_name) VALUES (?, ?)", @@ -1236,154 +1212,227 @@ create_tables_sqlite (SeafRepoManager *mgr) return 0; } -/* static int */ -/* create_tables_pgsql (SeafRepoManager *mgr) */ -/* { */ -/* SeafDB *db = mgr->seaf->db; */ -/* char *sql; */ - -/* sql = "CREATE TABLE IF NOT EXISTS Repo (repo_id CHAR(36) PRIMARY KEY)"; */ -/* if (seaf_db_query (db, sql) < 0) */ -/* return -1; */ - -/* sql = "CREATE TABLE IF NOT EXISTS RepoOwner (" */ -/* "repo_id CHAR(36) PRIMARY KEY, " */ -/* "owner_id VARCHAR(255))"; */ -/* if (seaf_db_query (db, sql) < 0) */ -/* return -1; */ - -/* if (!pgsql_index_exists (db, "repoowner_owner_idx")) { */ -/* sql = "CREATE INDEX repoowner_owner_idx ON RepoOwner (owner_id)"; */ -/* if (seaf_db_query (db, sql) < 0) */ -/* return -1; */ -/* } */ - -/* sql = "CREATE TABLE IF NOT EXISTS RepoGroup (repo_id CHAR(36), " */ -/* "group_id INTEGER, user_name VARCHAR(255), permission VARCHAR(15), " */ -/* "UNIQUE (group_id, repo_id))"; */ -/* if (seaf_db_query (db, sql) < 0) */ -/* return -1; */ - -/* if (!pgsql_index_exists (db, "repogroup_repoid_idx")) { */ -/* sql = "CREATE INDEX repogroup_repoid_idx ON RepoGroup (repo_id)"; */ -/* if (seaf_db_query (db, sql) < 0) */ -/* return -1; */ -/* } */ - -/* if (!pgsql_index_exists (db, "repogroup_username_idx")) { */ -/* sql = "CREATE INDEX repogroup_username_idx ON RepoGroup (user_name)"; */ -/* if (seaf_db_query (db, sql) < 0) */ -/* return -1; */ -/* } */ - -/* sql = "CREATE TABLE IF NOT EXISTS InnerPubRepo (" */ -/* "repo_id CHAR(36) PRIMARY KEY," */ -/* "permission VARCHAR(15))"; */ -/* if (seaf_db_query (db, sql) < 0) */ -/* return -1; */ - -/* sql = "CREATE TABLE IF NOT EXISTS RepoUserToken (" */ -/* "repo_id CHAR(36), " */ -/* "email VARCHAR(255), " */ -/* "token CHAR(40), " */ -/* "UNIQUE (repo_id, token))"; */ -/* if (seaf_db_query (db, sql) < 0) */ -/* return -1; */ - -/* if (!pgsql_index_exists (db, "repousertoken_email_idx")) { */ -/* sql = "CREATE INDEX repousertoken_email_idx ON RepoUserToken (email)"; */ -/* if (seaf_db_query (db, sql) < 0) */ -/* return -1; */ -/* } */ - -/* sql = "CREATE TABLE IF NOT EXISTS RepoTokenPeerInfo (" */ -/* "token CHAR(40) PRIMARY KEY, " */ -/* "peer_id CHAR(40), " */ -/* "peer_ip VARCHAR(40), " */ -/* "peer_name VARCHAR(255), " */ -/* "sync_time BIGINT, " */ -/* "client_ver VARCHAR(20))"; */ -/* if (seaf_db_query (db, sql) < 0) */ -/* return -1; */ - -/* sql = "CREATE TABLE IF NOT EXISTS RepoHead (" */ -/* "repo_id CHAR(36) PRIMARY KEY, branch_name VARCHAR(10))"; */ -/* if (seaf_db_query (db, sql) < 0) */ -/* return -1; */ - -/* sql = "CREATE TABLE IF NOT EXISTS RepoSize (" */ -/* "repo_id CHAR(36) PRIMARY KEY," */ -/* "size BIGINT," */ -/* "head_id CHAR(40))"; */ -/* if (seaf_db_query (db, sql) < 0) */ -/* return -1; */ - -/* sql = "CREATE TABLE IF NOT EXISTS RepoHistoryLimit (" */ -/* "repo_id CHAR(36) PRIMARY KEY, days INTEGER)"; */ -/* if (seaf_db_query (db, sql) < 0) */ -/* return -1; */ - -/* sql = "CREATE TABLE IF NOT EXISTS RepoValidSince (" */ -/* "repo_id CHAR(36) PRIMARY KEY, timestamp BIGINT)"; */ -/* if (seaf_db_query (db, sql) < 0) */ -/* return -1; */ - -/* sql = "CREATE TABLE IF NOT EXISTS WebAP (repo_id CHAR(36) PRIMARY KEY, " */ -/* "access_property VARCHAR(10))"; */ -/* if (seaf_db_query (db, sql) < 0) */ -/* return -1; */ - -/* sql = "CREATE TABLE IF NOT EXISTS VirtualRepo (repo_id CHAR(36) PRIMARY KEY," */ -/* "origin_repo CHAR(36), path TEXT, base_commit CHAR(40))"; */ -/* if (seaf_db_query (db, sql) < 0) */ -/* return -1; */ - -/* if (!pgsql_index_exists (db, "virtualrepo_origin_repo_idx")) { */ -/* sql = "CREATE INDEX virtualrepo_origin_repo_idx ON VirtualRepo (origin_repo)"; */ -/* if (seaf_db_query (db, sql) < 0) */ -/* return -1; */ -/* } */ - -/* sql = "CREATE TABLE IF NOT EXISTS GarbageRepos (repo_id CHAR(36) PRIMARY KEY)"; */ -/* if (seaf_db_query (db, sql) < 0) */ -/* return -1; */ - -/* sql = "CREATE TABLE IF NOT EXISTS RepoTrash (repo_id CHAR(36) PRIMARY KEY," */ -/* "repo_name VARCHAR(255), head_id CHAR(40), owner_id VARCHAR(255), size bigint," */ -/* "org_id INTEGER, del_time BIGINT)"; */ -/* if (seaf_db_query (db, sql) < 0) */ -/* return -1; */ - -/* if (!pgsql_index_exists (db, "repotrash_owner_id")) { */ -/* sql = "CREATE INDEX repotrash_owner_id on RepoTrash(owner_id)"; */ -/* if (seaf_db_query (db, sql) < 0) */ -/* return -1; */ -/* } */ -/* if (!pgsql_index_exists (db, "repotrash_org_id")) { */ -/* sql = "CREATE INDEX repotrash_org_id on RepoTrash(org_id)"; */ -/* if (seaf_db_query (db, sql) < 0) */ -/* return -1; */ -/* } */ - -/* sql = "CREATE TABLE IF NOT EXISTS RepoFileCount (" */ -/* "repo_id CHAR(36) PRIMARY KEY," */ -/* "file_count BIGINT)"; */ -/* if (seaf_db_query (db, sql) < 0) */ -/* return -1; */ - -/* sql = "CREATE TABLE IF NOT EXISTS WebUploadTempFiles (repo_id CHAR(40) NOT NULL, " */ -/* "file_path TEXT NOT NULL, tmp_file_path TEXT NOT NULL)"; */ -/* if (seaf_db_query (db, sql) < 0) */ -/* return -1; */ - -/* sql = "CREATE TABLE IF NOT EXISTS RepoInfo (repo_id CHAR(36) PRIMARY KEY, " */ -/* "name VARCHAR(255) NOT NULL, update_time BIGINT, version INTEGER, " */ -/* "is_encrypted INTEGER, last_modifier VARCHAR(255), status INTEGER DEFAULT 0)"; */ -/* if (seaf_db_query (db, sql) < 0) */ -/* return -1; */ - -/* return 0; */ -/* } */ +static int +create_tables_pgsql (SeafRepoManager *mgr) +{ + SeafDB *db = mgr->seaf->db; + char *sql; + + sql = "CREATE TABLE IF NOT EXISTS Repo (" + " id BIGSERIAL PRIMARY KEY," + " repo_id CHAR(37))"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS repo_repoid_idx ON Repo (repo_id)"; + if (seaf_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS RepoOwner (" + " id BIGSERIAL PRIMARY KEY," + " repo_id CHAR(37)," + " owner_id VARCHAR(255))"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS repoowner_repoid_idx ON RepoOwner (repo_id)"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE INDEX IF NOT EXISTS repoowner_owner_idx ON RepoOwner (owner_id)"; + if (seaf_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS RepoGroup (" + " id BIGSERIAL PRIMARY KEY," + " repo_id CHAR(37)," + " group_id INTEGER," + " user_name VARCHAR(255)," + " permission CHAR(15))"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS repogroup_group_id_repo_id_idx ON RepoGroup (group_id, repo_id)"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE INDEX IF NOT EXISTS repogroup_repoid_idx ON RepoGroup (repo_id)"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE INDEX IF NOT EXISTS repogroup_username_idx ON RepoGroup (user_name)"; + if (seaf_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS InnerPubRepo (" + " id BIGSERIAL PRIMARY KEY," + " repo_id CHAR(37)," + " permission CHAR(15))"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS innerpubrepo_repoid_idx ON InnerPubRepo (repo_id);"; + if (seaf_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS RepoUserToken (" + " id BIGSERIAL PRIMARY KEY," + " repo_id CHAR(37)," + " email VARCHAR(255)," + " token CHAR(41))"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS repousertoken_repo_id_token_key ON RepoUserToken (repo_id, token)"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE INDEX IF NOT EXISTS repousertoken_token_idx ON RepoUserToken (token)"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE INDEX IF NOT EXISTS repousertoken_email_idx ON RepoUserToken (email)"; + if (seaf_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS RepoTokenPeerInfo (" + " id BIGSERIAL PRIMARY KEY," + " token CHAR(41)," + " peer_id CHAR(41)," + " peer_ip VARCHAR(41)," + " peer_name VARCHAR(255)," + " sync_time BIGINT," + " client_ver VARCHAR(20))"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS repotokenpeerinfo_token_idx ON RepoTokenPeerInfo (token)"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE INDEX IF NOT EXISTS repotokenpeerinfo_peerid_idx ON RepoTokenPeerInfo (peer_id)"; + if (seaf_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS RepoHead (" + " id BIGSERIAL PRIMARY KEY," + " repo_id CHAR(37)," + " branch_name VARCHAR(10))"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS repohead_repoid_idx ON RepoHead (repo_id)"; + if (seaf_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS RepoSize (" + " id BIGSERIAL PRIMARY KEY," + " repo_id CHAR(37)," + " size BIGINT," + " head_id CHAR(41))"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS reposize_repoid_idx ON RepoSize (repo_id);"; + if (seaf_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS RepoHistoryLimit (" + " id BIGSERIAL PRIMARY KEY," + " repo_id CHAR(37)," + " days INTEGER)"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS repohistorylimit_repoid_idx ON RepoHistoryLimit (repo_id);"; + if (seaf_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS RepoValidSince (" + " id BIGSERIAL PRIMARY KEY," + " repo_id CHAR(37)," + " timestamp BIGINT)"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS repovalidsince_repoid_idx ON RepoValidSince (repo_id);"; + if (seaf_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS WebAP (" + " id BIGSERIAL PRIMARY KEY," + " repo_id CHAR(37)," + " access_property CHAR(10))"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS webap_repoid_idx ON WebAP (repo_id)"; + if (seaf_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS VirtualRepo (" + " id BIGSERIAL PRIMARY KEY," + " repo_id CHAR(36)," + " origin_repo CHAR(36)," + " path TEXT," + " base_commit CHAR(40))"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS virtualrepo_repoid_idx ON VirtualRepo (repo_id)"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE INDEX IF NOT EXISTS virtualrepo_origin_repo_idx ON VirtualRepo (origin_repo)"; + if (seaf_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS GarbageRepos (" + " id BIGSERIAL PRIMARY KEY," + " repo_id CHAR(36))"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS garbagerepos_repoid_idx ON GarbageRepos (repo_id);"; + if (seaf_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS RepoTrash (" + " id BIGSERIAL PRIMARY KEY," + " repo_id CHAR(36)," + " repo_name VARCHAR(255)," + " head_id CHAR(40)," + " owner_id VARCHAR(255)," + " size BIGINT," + " org_id INTEGER," + " del_time BIGINT)"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS repotrash_repoid_idx ON RepoTrash (repo_id)"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE INDEX IF NOT EXISTS repotrash_owner_id ON RepoTrash (owner_id)"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE INDEX IF NOT EXISTS repotrash_org_id ON RepoTrash (org_id)"; + if (seaf_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS RepoFileCount (" + " id BIGSERIAL PRIMARY KEY," + " repo_id CHAR(36)," + " file_count BIGINT)"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS repofilecount_repoid_idx ON RepoFileCount (repo_id)"; + if (seaf_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS RepoInfo (" + " id BIGSERIAL PRIMARY KEY," + " repo_id CHAR(36)," + " name VARCHAR(255) NOT NULL," + " update_time BIGINT," + " version INTEGER," + " is_encrypted INTEGER," + " last_modifier VARCHAR(255)," + " status INTEGER DEFAULT 0)"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS repoinfo_repoid_idx ON RepoInfo (repo_id);"; + if (seaf_db_query (db, sql) < 0) + return -1; + + sql = "CREATE TABLE IF NOT EXISTS WebUploadTempFiles (" + " id BIGSERIAL PRIMARY KEY," + " repo_id CHAR(40) NOT NULL," + " file_path TEXT NOT NULL," + " tmp_file_path TEXT NOT NULL)"; + if (seaf_db_query (db, sql) < 0) + return -1; + + return 0; +} static int create_db_tables_if_not_exist (SeafRepoManager *mgr) @@ -1398,8 +1447,8 @@ create_db_tables_if_not_exist (SeafRepoManager *mgr) return create_tables_mysql (mgr); else if (db_type == SEAF_DB_TYPE_SQLITE) return create_tables_sqlite (mgr); - /* else if (db_type == SEAF_DB_TYPE_PGSQL) */ - /* return create_tables_pgsql (mgr); */ + else if (db_type == SEAF_DB_TYPE_PGSQL) + return create_tables_pgsql (mgr); g_return_val_if_reached (-1); } @@ -1871,27 +1920,13 @@ seaf_repo_manager_set_repo_history_limit (SeafRepoManager *mgr, } if (seaf_db_type(db) == SEAF_DB_TYPE_PGSQL) { - gboolean exists, err; - int rc; - - exists = seaf_db_statement_exists (db, - "SELECT repo_id FROM RepoHistoryLimit " - "WHERE repo_id=?", - &err, 1, "string", repo_id); - if (err) + if (seaf_db_statement_query (db, + "INSERT INTO RepoHistoryLimit (repo_id, days)" + " VALUES (?, ?)" + " ON CONFLICT (repo_id)" + " DO UPDATE SET days=?", + 2, "string", repo_id, "int", days, "int", days) < 0) return -1; - - if (exists) - rc = seaf_db_statement_query (db, - "UPDATE RepoHistoryLimit SET days=? " - "WHERE repo_id=?", - 2, "int", days, "string", repo_id); - else - rc = seaf_db_statement_query (db, - "INSERT INTO RepoHistoryLimit (repo_id, days) VALUES " - "(?, ?)", - 2, "string", repo_id, "int", days); - return rc; } else { if (seaf_db_statement_query (db, "REPLACE INTO RepoHistoryLimit (repo_id, days) VALUES (?, ?)", @@ -1953,26 +1988,12 @@ seaf_repo_manager_set_repo_valid_since (SeafRepoManager *mgr, SeafDB *db = mgr->seaf->db; if (seaf_db_type(db) == SEAF_DB_TYPE_PGSQL) { - gboolean exists, err; - int rc; - - exists = seaf_db_statement_exists (db, - "SELECT repo_id FROM RepoValidSince WHERE " - "repo_id=?", &err, 1, "string", repo_id); - if (err) - return -1; - - if (exists) - rc = seaf_db_statement_query (db, - "UPDATE RepoValidSince SET timestamp=?" - " WHERE repo_id=?", - 2, "int64", timestamp, "string", repo_id); - else - rc = seaf_db_statement_query (db, - "INSERT INTO RepoValidSince (repo_id, timestamp) VALUES " - "(?, ?)", 2, "string", repo_id, - "int64", timestamp); - if (rc < 0) + if (seaf_db_statement_query (db, + "INSERT INTO RepoValidSince (repo_id, timestamp)" + " VALUES (?, ?)" + " ON CONFLICT (repo_id)" + " DO UPDATE SET timestamp=?", + 2, "string", repo_id, "int64", timestamp, "int64", timestamp) < 0) return -1; } else { if (seaf_db_statement_query (db, @@ -2035,24 +2056,11 @@ seaf_repo_manager_set_repo_owner (SeafRepoManager *mgr, goto out; if (seaf_db_type(db) == SEAF_DB_TYPE_PGSQL) { - gboolean err; - snprintf(sql, sizeof(sql), - "SELECT repo_id FROM RepoOwner WHERE repo_id=?"); - if (seaf_db_statement_exists (db, sql, &err, - 1, "string", repo_id)) - snprintf(sql, sizeof(sql), - "UPDATE RepoOwner SET owner_id='%s' WHERE " - "repo_id='%s'", email, repo_id); - else - snprintf(sql, sizeof(sql), - "INSERT INTO RepoOwner (repo_id, owner_id) VALUES ('%s', '%s')", - repo_id, email); - if (err) { - ret = -1; - goto out; - } - - if (seaf_db_query (db, sql) < 0) { + if (seaf_db_statement_query (db, "INSERT INTO RepoOwner (repo_id, owner_id)" + " VALUES (?, ?)" + " ON CONFLICT (repo_id)" + " DO UPDATE SET owner_id=?", + 2, "string", repo_id, "string", email, "string", email) < 0) { ret = -1; goto out; } @@ -3444,21 +3452,12 @@ seaf_repo_manager_set_inner_pub_repo (SeafRepoManager *mgr, char sql[256]; if (seaf_db_type(db) == SEAF_DB_TYPE_PGSQL) { - gboolean err; - snprintf(sql, sizeof(sql), - "SELECT repo_id FROM InnerPubRepo WHERE repo_id=?"); - if (seaf_db_statement_exists (db, sql, &err, - 1, "string", repo_id)) - snprintf(sql, sizeof(sql), - "UPDATE InnerPubRepo SET permission='%s' " - "WHERE repo_id='%s'", permission, repo_id); - else - snprintf(sql, sizeof(sql), - "INSERT INTO InnerPubRepo (repo_id, permission) VALUES " - "('%s', '%s')", repo_id, permission); - if (err) - return -1; - return seaf_db_query (db, sql); + return seaf_db_statement_query (db, + "INSERT INTO InnerPubRepo (repo_id, permission)" + " VALUES (?, ?)" + " ON CONFLICT (repo_id)" + " DO UPDATE SET permission=?", + 2, "string", repo_id, "string", permission, "string", permission); } else { return seaf_db_statement_query (db, "REPLACE INTO InnerPubRepo (repo_id, permission) VALUES (?, ?)", diff --git a/server/seafile-session.c b/server/seafile-session.c index dfe7e69d..30109a00 100644 --- a/server/seafile-session.c +++ b/server/seafile-session.c @@ -437,13 +437,23 @@ schedule_create_system_default_repo (SeafileSession *session) int db_type = seaf_db_type (session->db); char *sql; - if (db_type == SEAF_DB_TYPE_MYSQL) - sql = "CREATE TABLE IF NOT EXISTS SystemInfo " - "(id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, " - "info_key VARCHAR(256), info_value VARCHAR(1024))"; - else - sql = "CREATE TABLE IF NOT EXISTS SystemInfo( " - "info_key VARCHAR(256), info_value VARCHAR(1024))"; + switch (db_type) { + case SEAF_DB_TYPE_MYSQL: + sql = "CREATE TABLE IF NOT EXISTS SystemInfo " + "(id BIGINT NOT NULL PRIMARY KEY AUTO_INCREMENT, " + "info_key VARCHAR(256), info_value VARCHAR(1024))"; + break; + case SEAF_DB_TYPE_SQLITE: + sql = "CREATE TABLE IF NOT EXISTS SystemInfo( " + "info_key VARCHAR(256), info_value VARCHAR(1024))"; + break; + case SEAF_DB_TYPE_PGSQL: + sql = "CREATE TABLE IF NOT EXISTS SystemInfo (" + " id BIGSERIAL PRIMARY KEY," + " info_key VARCHAR(256)," + " info_value VARCHAR(1024))"; + break; + } if ((session->create_tables || db_type == SEAF_DB_TYPE_PGSQL) && seaf_db_query (session->db, sql) < 0) diff --git a/server/share-mgr.c b/server/share-mgr.c index 47a78539..861a8789 100644 --- a/server/share-mgr.c +++ b/server/share-mgr.c @@ -56,30 +56,25 @@ seaf_share_manager_start (SeafShareManager *mgr) sql = "CREATE INDEX IF NOT EXISTS ToEmailIndex on SharedRepo (to_email)"; if (seaf_db_query (db, sql) < 0) return -1; + } else if (db_type == SEAF_DB_TYPE_PGSQL) { + sql = "CREATE TABLE IF NOT EXISTS SharedRepo (" + " id BIGSERIAL PRIMARY KEY," + " repo_id CHAR(37)," + " from_email VARCHAR(255)," + " to_email VARCHAR(255)," + " permission CHAR(15))"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE INDEX IF NOT EXISTS sharedrepo_from_email_idx ON SharedRepo (from_email);"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE INDEX IF NOT EXISTS sharedrepo_repoid_idx ON SharedRepo (repo_id);"; + if (seaf_db_query (db, sql) < 0) + return -1; + sql = "CREATE INDEX IF NOT EXISTS sharedrepo_to_email_idx ON SharedRepo (to_email);"; + if (seaf_db_query (db, sql) < 0) + return -1; } - /* else if (db_type == SEAF_DB_TYPE_PGSQL) { */ - /* sql = "CREATE TABLE IF NOT EXISTS SharedRepo " */ - /* "(repo_id CHAR(36) , from_email VARCHAR(255), to_email VARCHAR(255), " */ - /* "permission VARCHAR(15))"; */ - /* if (seaf_db_query (db, sql) < 0) */ - /* return -1; */ - - /* if (!pgsql_index_exists (db, "sharedrepo_repoid_idx")) { */ - /* sql = "CREATE INDEX sharedrepo_repoid_idx ON SharedRepo (repo_id)"; */ - /* if (seaf_db_query (db, sql) < 0) */ - /* return -1; */ - /* } */ - /* if (!pgsql_index_exists (db, "sharedrepo_from_email_idx")) { */ - /* sql = "CREATE INDEX sharedrepo_from_email_idx ON SharedRepo (from_email)"; */ - /* if (seaf_db_query (db, sql) < 0) */ - /* return -1; */ - /* } */ - /* if (!pgsql_index_exists (db, "sharedrepo_to_email_idx")) { */ - /* sql = "CREATE INDEX sharedrepo_to_email_idx ON SharedRepo (to_email)"; */ - /* if (seaf_db_query (db, sql) < 0) */ - /* return -1; */ - /* } */ - /* } */ return 0; } From 07ba52ff9c62774710858447e16faed2946899f5 Mon Sep 17 00:00:00 2001 From: Janne Mareike Koschinski Date: Mon, 13 Jun 2022 23:31:02 +0200 Subject: [PATCH 2/9] Implement actual queries for postgresql --- common/seaf-db.c | 160 +++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 149 insertions(+), 11 deletions(-) diff --git a/common/seaf-db.c b/common/seaf-db.c index 9f477ba8..57a63fea 100644 --- a/common/seaf-db.c +++ b/common/seaf-db.c @@ -10,7 +10,9 @@ #include #endif #ifdef HAVE_POSTGRES +#include #include +#include #endif #include #include @@ -1397,6 +1399,20 @@ pgsql_db_release_connection (DBConnection *vconn) g_free (conn); } +static PQResult* +_pgsql_db_execute(PostgresDBConnection *conn, const char *sql, int n, va_list args) { + PQResult *res; + if (n == 0) { + res = PQexec(conn->db_conn, sql); + } + if (PQresultStatus(res) != PGRES_COMMAND_OK) { + seaf_warning ("Failed to execute sql %s: %s\n", sql, PQresultErrorMessage(res)); + PQclear (res); + return NULL; + } + return res; +} + static int pgsql_db_execute_sql_no_stmt (DBConnection *vconn, const char *sql) { @@ -1413,17 +1429,92 @@ pgsql_db_execute_sql_no_stmt (DBConnection *vconn, const char *sql) return 0; } +/* Convert '?' in the query string to $1, $2, etc. */ +static char * +_pgsql_db_format_query_string (const char *sql) +{ + GString *buf = g_string_new (NULL); + const char *p; + int i = 0; + + for (p = sql; *p != '\0'; ++p) { + if (*p == '?') { + ++i; + g_string_append_c (buf, '$'); + g_string_append_printf (buf, "%d", i); + } else { + g_string_append_c (buf, *p); + } + } + + return g_string_free (buf, FALSE); +} + +static *PGResult +_pgsql_db_execute_stmt(PGconn *db, const char *sql, int n, va_list args) +{ + Oid *paramTypes; + const char* paramValues; + if (n > 0) { + paramTypes = g_new0(Oid, n); + for (int i = 0; i < n; i++) { + type = va_arg (args, const char *); + if (strcmp(type, "int") == 0) { + int value = va_arg (args, int); + paramTypes[i] = INT4OID; + paramValues[i] = g_strdup_printf("%d", x); + } else if (strcmp (type, "int64") == 0) { + gint64 value = va_arg (args, gint64); + paramTypes[i] = INT8OID; + paramValues[i] = g_strdup_printf("%"G_GINT64_FORMAT, x); + } else if (strcmp (type, "string") == 0) { + const char* value = va_arg (args, const char *); + paramTypes[i] = TEXTOID; + paramValues[i] = g_strdup(value); + } else { + seaf_warning ("BUG: invalid prep stmt parameter type %s.\n", type); + g_return_val_if_reached (-1); + } + } + } + + return PQexecParams ( + conn->db, + _pgsql_db_format_query_string(sql), + n, + paramTypes, + paramValues, + NULL, + NULL, + 0 + ); +} + static int pgsql_db_execute_sql (DBConnection *vconn, const char *sql, int n, va_list args) { - // TODO - return -1; + PostgresDBConnection *conn = (PostgresDBConnection *)vconn; + + PGResult *res = _pgsql_db_execute_stmt(conn->db, sql, n, args); + if (!res) { + seaf_warning ("Failed to execute sql %s: unknown error\n", sql); + return -1; + } + if (status != PGRES_EMPTY_QUERY && status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { + seaf_warning ("Failed to execute sql %s: %s\n", sql, PQresultErrorMessage(res)); + PQclear (res); + return -1; + } + + PQclear (res); + return 0; } typedef struct PgSQLDBRow { SeafDBRow parent; int column_count; - // TODO + int row_index; + PGresult *results; } PgSQLDBRow; static int @@ -1431,8 +1522,32 @@ pgsql_db_query_foreach_row (DBConnection *vconn, const char *sql, SeafDBRowFunc callback, void *data, int n, va_list args) { - // TODO - return 0; + PostgresDBConnection *conn = (PostgresDBConnection *)vconn; + + PGResult *res = _pgsql_db_execute_stmt(conn->db, sql, n, args); + if (!res) { + seaf_warning ("Failed to execute sql %s: unknown error\n", sql); + return -1; + } + if (status != PGRES_EMPTY_QUERY && status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { + seaf_warning ("Failed to execute sql %s: %s\n", sql, PQresultErrorMessage(res)); + PQclear (res); + return -1; + } + + PgSQLDBRow row; + memset(&row, 0, sizeof(row)); + row.column_count = PQntuples(res); + row.results = res; + int row_count = PQntuples(res); + for (int i = 0; i < row_count; i++) { + row->row_index = i; + if (callback && !callback((SeafDBRow *) &row, data)) { + break; + } + }; + PQclear(res); + return row_count; } static int @@ -1447,8 +1562,7 @@ pgsql_db_row_get_column_string (SeafDBRow *vrow, int i) { PgSQLDBRow *row = (PgSQLDBRow *)vrow; - // TODO - return NULL; + return PQgetvalue(r->res, r->row_index, i); } static int @@ -1456,8 +1570,20 @@ pgsql_db_row_get_column_int (SeafDBRow *vrow, int idx) { PgSQLDBRow *row = (PgSQLDBRow *)vrow; - // TODO - return 0; + const char* data = PQgetvalue(r->res, r->row_index, i); + if (!data) { + return 0; + } + + char *e; + errno = 0; + int ret = strtol (str, &e, 10); + if (errno || (e == str)) { + seaf_warning ("Number conversion failed.\n"); + return -1; + } + + return ret; } static gint64 @@ -1465,8 +1591,20 @@ pgsql_db_row_get_column_int64 (SeafDBRow *vrow, int idx) { PgSQLDBRow *row = (PgSQLDBRow *)vrow; - // TODO - return 0; + const char* data = PQgetvalue(r->res, r->row_index, i); + if (!data) { + return 0; + } + + char *e; + errno = 0; + int ret = strtoll (str, &e, 10); + if (errno || (e == str)) { + seaf_warning ("Number conversion failed.\n"); + return -1; + } + + return ret; } #endif /* HAVE_POSTGRES */ From 047832c88f5baa295d56417165c18ce6269cc4be Mon Sep 17 00:00:00 2001 From: Janne Mareike Koschinski Date: Tue, 14 Jun 2022 00:45:34 +0200 Subject: [PATCH 3/9] Fix makefile issues --- configure.ac | 2 +- fuse/Makefile.am | 3 ++- server/gc/Makefile.am | 5 +++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/configure.ac b/configure.ac index bc7afe55..3db47f47 100644 --- a/configure.ac +++ b/configure.ac @@ -242,7 +242,7 @@ else AC_DEFINE([HAVE_MYSQL], 1, [Define to 1 if MySQL support is enabled]) fi -PKG_CHECK_MODULES(LIBPQ, [libpq], [have_pgsql="yes"], [have_pgsql="no"]) +PKG_CHECK_MODULES(LIBPQ, [libpq], [have_libpq="yes"], [have_libpq="no"]) if test "x${have_libpq}" = "xyes"; then AC_SUBST(LIBPQ_CFLAGS) AC_SUBST(LIBPQ_LIBS) diff --git a/fuse/Makefile.am b/fuse/Makefile.am index 3f3f1766..9854e8f9 100644 --- a/fuse/Makefile.am +++ b/fuse/Makefile.am @@ -9,6 +9,7 @@ AM_CFLAGS = -DPKGDATADIR=\"$(pkgdatadir)\" \ @GLIB2_CFLAGS@ \ @FUSE_CFLAGS@ \ @MYSQL_CFLAGS@ \ + @LIBPQ_CFLAGS@ \ -Wall bin_PROGRAMS = seaf-fuse @@ -42,5 +43,5 @@ seaf_fuse_LDADD = @GLIB2_LIBS@ @GOBJECT_LIBS@ @SSL_LIBS@ @LIB_RT@ @LIB_UUID@ \ -lsqlite3 @LIBEVENT_LIBS@ \ $(top_builddir)/common/cdc/libcdc.la \ @SEARPC_LIBS@ @JANSSON_LIBS@ @FUSE_LIBS@ @ZLIB_LIBS@ \ - @LDAP_LIBS@ @MYSQL_LIBS@ -lsqlite3 + @LDAP_LIBS@ @MYSQL_LIBS@ @LIBPQ_LIBS@ -lsqlite3 diff --git a/server/gc/Makefile.am b/server/gc/Makefile.am index e45a4cbb..4a308157 100644 --- a/server/gc/Makefile.am +++ b/server/gc/Makefile.am @@ -10,6 +10,7 @@ AM_CFLAGS = -DPKGDATADIR=\"$(pkgdatadir)\" \ @GLIB2_CFLAGS@ \ @MSVC_CFLAGS@ \ @MYSQL_CFLAGS@ \ + @LIBPQ_CFLAGS@ \ -Wall bin_PROGRAMS = seafserv-gc seaf-fsck @@ -48,7 +49,7 @@ seafserv_gc_LDADD = $(top_builddir)/common/cdc/libcdc.la \ $(top_builddir)/lib/libseafile_common.la \ @GLIB2_LIBS@ @GOBJECT_LIBS@ @SSL_LIBS@ @LIB_RT@ @LIB_UUID@ -lsqlite3 @LIBEVENT_LIBS@ \ @SEARPC_LIBS@ @JANSSON_LIBS@ ${LIB_WS32} @ZLIB_LIBS@ \ - @MYSQL_LIBS@ -lsqlite3 + @MYSQL_LIBS@ @LIBPQ_LIBS@ -lsqlite3 seaf_fsck_SOURCES = \ seaf-fsck.c \ @@ -59,4 +60,4 @@ seaf_fsck_LDADD = $(top_builddir)/common/cdc/libcdc.la \ $(top_builddir)/lib/libseafile_common.la \ @GLIB2_LIBS@ @GOBJECT_LIBS@ @SSL_LIBS@ @LIB_RT@ @LIB_UUID@ -lsqlite3 @LIBEVENT_LIBS@ \ @SEARPC_LIBS@ @JANSSON_LIBS@ ${LIB_WS32} @ZLIB_LIBS@ \ - @MYSQL_LIBS@ -lsqlite3 + @MYSQL_LIBS@ @LIBPQ_LIBS@ -lsqlite3 From d4ee3410110fc828c2737d66633d848a5debd6b6 Mon Sep 17 00:00:00 2001 From: Janne Mareike Koschinski Date: Tue, 14 Jun 2022 00:45:58 +0200 Subject: [PATCH 4/9] Fix compiler warnings and errors --- common/branch-mgr.c | 1 - common/seaf-db.c | 80 +++++++++++++++++----------------------- common/seaf-utils.c | 6 +-- server/gc/repo-mgr.c | 23 ++++-------- server/quota-mgr.c | 2 +- server/repo-mgr.c | 13 +++---- server/seafile-session.c | 4 +- 7 files changed, 52 insertions(+), 77 deletions(-) diff --git a/common/branch-mgr.c b/common/branch-mgr.c index c26af182..52aaf31c 100644 --- a/common/branch-mgr.c +++ b/common/branch-mgr.c @@ -198,7 +198,6 @@ seaf_branch_manager_add_branch (SeafBranchManager *mgr, SeafBranch *branch) return 0; #else - char *sql; SeafDB *db = mgr->seaf->db; if (seaf_db_type(db) == SEAF_DB_TYPE_PGSQL) { diff --git a/common/seaf-db.c b/common/seaf-db.c index 57a63fea..eacfcc45 100644 --- a/common/seaf-db.c +++ b/common/seaf-db.c @@ -10,9 +10,7 @@ #include #endif #ifdef HAVE_POSTGRES -#include #include -#include #endif #include #include @@ -374,12 +372,11 @@ seaf_db_new_pgsql (const char *host, const char *db_name, const char *unix_socket, gboolean use_ssl, - const char *charset, int max_connections) { SeafDB *db; - db = pgsql_db_new (host, port, user, passwd, db_name, unix_socket, use_ssl, charset); + db = pgsql_db_new (host, port, user, passwd, db_name, unix_socket, use_ssl); if (!db) return NULL; db->type = SEAF_DB_TYPE_PGSQL; @@ -1346,37 +1343,36 @@ static DBConnection * pgsql_db_get_connection (SeafDB *vdb) { PostgresDB *db = (PostgresDB *)vdb; - int conn_timeout = 1; - int read_write_timeout = 5; PGconn *db_conn; PostgresDBConnection *conn = NULL; + GString *buf = g_string_new (NULL); g_string_append_printf (buf, "user='%s' ", db->user); - esc_password = _pgsql_escape_connect_string (db->password); + char* esc_password = _pgsql_escape_connect_string (db->password); g_string_append_printf (buf, "password='%s' ", esc_password); g_free (esc_password); - if (pool->unix_socket) { + if (db->unix_socket) { g_string_append_printf (buf, "host='%s' ", db->unix_socket); } else { g_string_append_printf (buf, "host='%s' ", db->host); } - if (pool->port > 0) { + if (db->port > 0) { g_string_append_printf (buf, "port=%u ", db->port); } g_string_append_printf (buf, "dbname='%s' ", db->db_name); - if (pool->use_ssl) { + if (db->use_ssl) { g_string_append_printf (buf, "sslmode=require"); } db_conn = PQconnectdb (buf->str); - if (PQstatus (db) != CONNECTION_OK) { + if (PQstatus (db_conn) != CONNECTION_OK) { seaf_warning ("Failed to init pgsql connection object.\n"); - PQfinish (db); + PQfinish (db_conn); return NULL; } @@ -1399,26 +1395,12 @@ pgsql_db_release_connection (DBConnection *vconn) g_free (conn); } -static PQResult* -_pgsql_db_execute(PostgresDBConnection *conn, const char *sql, int n, va_list args) { - PQResult *res; - if (n == 0) { - res = PQexec(conn->db_conn, sql); - } - if (PQresultStatus(res) != PGRES_COMMAND_OK) { - seaf_warning ("Failed to execute sql %s: %s\n", sql, PQresultErrorMessage(res)); - PQclear (res); - return NULL; - } - return res; -} - static int pgsql_db_execute_sql_no_stmt (DBConnection *vconn, const char *sql) { PostgresDBConnection *conn = (PostgresDBConnection *)vconn; - res = PQexec(conn->db_conn, sql); + PGresult* res = PQexec(conn->db_conn, sql); if (PQresultStatus(res) != PGRES_COMMAND_OK) { seaf_warning ("Failed to execute sql %s: %s\n", sql, PQresultErrorMessage(res)); PQclear (res); @@ -1450,36 +1432,40 @@ _pgsql_db_format_query_string (const char *sql) return g_string_free (buf, FALSE); } -static *PGResult +#define INT4OID 23 +#define INT8OID 20 +#define TEXTOID 25 + +static PGresult* _pgsql_db_execute_stmt(PGconn *db, const char *sql, int n, va_list args) { - Oid *paramTypes; - const char* paramValues; + Oid *paramTypes = g_new0(Oid, n); + const char** paramValues = g_new0(const char*, n); if (n > 0) { paramTypes = g_new0(Oid, n); for (int i = 0; i < n; i++) { - type = va_arg (args, const char *); + const char* type = va_arg (args, const char *); if (strcmp(type, "int") == 0) { int value = va_arg (args, int); paramTypes[i] = INT4OID; - paramValues[i] = g_strdup_printf("%d", x); + paramValues[i] = g_strdup_printf("%d", value); } else if (strcmp (type, "int64") == 0) { gint64 value = va_arg (args, gint64); paramTypes[i] = INT8OID; - paramValues[i] = g_strdup_printf("%"G_GINT64_FORMAT, x); + paramValues[i] = g_strdup_printf("%"G_GINT64_FORMAT, value); } else if (strcmp (type, "string") == 0) { const char* value = va_arg (args, const char *); paramTypes[i] = TEXTOID; paramValues[i] = g_strdup(value); } else { seaf_warning ("BUG: invalid prep stmt parameter type %s.\n", type); - g_return_val_if_reached (-1); + g_return_val_if_reached (NULL); } } } return PQexecParams ( - conn->db, + db, _pgsql_db_format_query_string(sql), n, paramTypes, @@ -1495,11 +1481,12 @@ pgsql_db_execute_sql (DBConnection *vconn, const char *sql, int n, va_list args) { PostgresDBConnection *conn = (PostgresDBConnection *)vconn; - PGResult *res = _pgsql_db_execute_stmt(conn->db, sql, n, args); + PGresult *res = _pgsql_db_execute_stmt(conn->db_conn, sql, n, args); if (!res) { seaf_warning ("Failed to execute sql %s: unknown error\n", sql); return -1; } + ExecStatusType status = PQresultStatus(res); if (status != PGRES_EMPTY_QUERY && status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { seaf_warning ("Failed to execute sql %s: %s\n", sql, PQresultErrorMessage(res)); PQclear (res); @@ -1524,11 +1511,12 @@ pgsql_db_query_foreach_row (DBConnection *vconn, const char *sql, { PostgresDBConnection *conn = (PostgresDBConnection *)vconn; - PGResult *res = _pgsql_db_execute_stmt(conn->db, sql, n, args); + PGresult *res = _pgsql_db_execute_stmt(conn->db_conn, sql, n, args); if (!res) { seaf_warning ("Failed to execute sql %s: unknown error\n", sql); return -1; } + ExecStatusType status = PQresultStatus(res); if (status != PGRES_EMPTY_QUERY && status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { seaf_warning ("Failed to execute sql %s: %s\n", sql, PQresultErrorMessage(res)); PQclear (res); @@ -1541,7 +1529,7 @@ pgsql_db_query_foreach_row (DBConnection *vconn, const char *sql, row.results = res; int row_count = PQntuples(res); for (int i = 0; i < row_count; i++) { - row->row_index = i; + row.row_index = i; if (callback && !callback((SeafDBRow *) &row, data)) { break; } @@ -1558,11 +1546,11 @@ pgsql_db_row_get_column_count (SeafDBRow *vrow) } static const char * -pgsql_db_row_get_column_string (SeafDBRow *vrow, int i) +pgsql_db_row_get_column_string (SeafDBRow *vrow, int idx) { PgSQLDBRow *row = (PgSQLDBRow *)vrow; - return PQgetvalue(r->res, r->row_index, i); + return PQgetvalue(row->results, row->row_index, idx); } static int @@ -1570,15 +1558,15 @@ pgsql_db_row_get_column_int (SeafDBRow *vrow, int idx) { PgSQLDBRow *row = (PgSQLDBRow *)vrow; - const char* data = PQgetvalue(r->res, r->row_index, i); + const char* data = PQgetvalue(row->results, row->row_index, idx); if (!data) { return 0; } char *e; errno = 0; - int ret = strtol (str, &e, 10); - if (errno || (e == str)) { + int ret = strtol (data, &e, 10); + if (errno || (e == data)) { seaf_warning ("Number conversion failed.\n"); return -1; } @@ -1591,15 +1579,15 @@ pgsql_db_row_get_column_int64 (SeafDBRow *vrow, int idx) { PgSQLDBRow *row = (PgSQLDBRow *)vrow; - const char* data = PQgetvalue(r->res, r->row_index, i); + const char* data = PQgetvalue(row->results, row->row_index, idx); if (!data) { return 0; } char *e; errno = 0; - int ret = strtoll (str, &e, 10); - if (errno || (e == str)) { + int ret = strtoll (data, &e, 10); + if (errno || (e == data)) { seaf_warning ("Number conversion failed.\n"); return -1; } diff --git a/common/seaf-utils.c b/common/seaf-utils.c index e3ff8013..58915823 100644 --- a/common/seaf-utils.c +++ b/common/seaf-utils.c @@ -131,7 +131,7 @@ mysql_db_start (SeafileSession *session) #endif -#ifdef HAVE_POSTGRESQL +#ifdef HAVE_POSTGRES static int pgsql_db_start (SeafileSession *session) @@ -187,7 +187,7 @@ pgsql_db_start (SeafileSession *session) max_connections = DEFAULT_MAX_CONNECTIONS; } - session->db = seaf_db_new_mpgql (host, port, user, passwd, db, unix_socket, use_ssl, max_connections); + session->db = seaf_db_new_pgsql (host, port, user, passwd, db, unix_socket, use_ssl, max_connections); if (!session->db) { seaf_warning ("Failed to start mysql db.\n"); return -1; @@ -223,7 +223,7 @@ load_database_config (SeafileSession *session) ret = mysql_db_start (session); } #endif -#ifdef HAVE_POSTGRESQL +#ifdef HAVE_POSTGRES else if (strcasecmp (type, "pgsql") == 0) { ret = pgsql_db_start (session); } diff --git a/server/gc/repo-mgr.c b/server/gc/repo-mgr.c index ab5b6c17..04dacfc2 100644 --- a/server/gc/repo-mgr.c +++ b/server/gc/repo-mgr.c @@ -433,7 +433,7 @@ seaf_repo_manager_set_repo_history_limit (SeafRepoManager *mgr, " VALUES (?, ?)" " ON CONFLICT (repo_id)" " DO UPDATE SET days=?", - 2, "string", repo_id, "int", days, "int", days) < 0) + 3, "string", repo_id, "int", days, "int", days) < 0) return -1; } else { snprintf (sql, sizeof(sql), @@ -505,21 +505,12 @@ seaf_repo_manager_set_repo_valid_since (SeafRepoManager *mgr, 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) + if (seaf_db_statement_query (db, + "INSERT INTO RepoValidSince (repo_id, timestamp)" + " VALUES (?, ?)" + " ON CONFLICT (repo_id)" + " DO UPDATE SET timestamp=?", + 3, "string", repo_id, "int64", timestamp, "int64", timestamp) < 0) return -1; } else { snprintf (sql, sizeof(sql), diff --git a/server/quota-mgr.c b/server/quota-mgr.c index 633fe5dc..3f53abd9 100644 --- a/server/quota-mgr.c +++ b/server/quota-mgr.c @@ -238,7 +238,7 @@ seaf_quota_manager_set_org_quota (SeafQuotaManager *mgr, " VALUES (?, ?)" " ON CONFLICT (org_id)" " DO UPDATE SET quota=?", - 2, "int", org_id, "int64", quota, "int64", quota); + 3, "int", org_id, "int64", quota, "int64", quota); } else { int rc = seaf_db_statement_query (db, "REPLACE INTO OrgQuota (org_id, quota) VALUES (?, ?)", diff --git a/server/repo-mgr.c b/server/repo-mgr.c index 66a17d3f..e3e4a01c 100644 --- a/server/repo-mgr.c +++ b/server/repo-mgr.c @@ -798,7 +798,7 @@ save_branch_repo_map (SeafRepoManager *manager, SeafBranch *branch) " VALUES (?, ?)" " ON CONFLICT (repo_id)" " DO UPDATE SET branch_name=?", - 2, "string", branch->repo_id, + 3, "string", branch->repo_id, "string", branch->name, "string", branch->name); } else { @@ -1925,7 +1925,7 @@ seaf_repo_manager_set_repo_history_limit (SeafRepoManager *mgr, " VALUES (?, ?)" " ON CONFLICT (repo_id)" " DO UPDATE SET days=?", - 2, "string", repo_id, "int", days, "int", days) < 0) + 3, "string", repo_id, "int", days, "int", days) < 0) return -1; } else { if (seaf_db_statement_query (db, @@ -1993,7 +1993,7 @@ seaf_repo_manager_set_repo_valid_since (SeafRepoManager *mgr, " VALUES (?, ?)" " ON CONFLICT (repo_id)" " DO UPDATE SET timestamp=?", - 2, "string", repo_id, "int64", timestamp, "int64", timestamp) < 0) + 3, "string", repo_id, "int64", timestamp, "int64", timestamp) < 0) return -1; } else { if (seaf_db_statement_query (db, @@ -2047,7 +2047,6 @@ seaf_repo_manager_set_repo_owner (SeafRepoManager *mgr, const char *email) { SeafDB *db = mgr->seaf->db; - char sql[256]; char *orig_owner = NULL; int ret = 0; @@ -2060,7 +2059,7 @@ seaf_repo_manager_set_repo_owner (SeafRepoManager *mgr, " VALUES (?, ?)" " ON CONFLICT (repo_id)" " DO UPDATE SET owner_id=?", - 2, "string", repo_id, "string", email, "string", email) < 0) { + 3, "string", repo_id, "string", email, "string", email) < 0) { ret = -1; goto out; } @@ -2337,7 +2336,6 @@ seaf_repo_manager_get_repos_by_id_prefix (SeafRepoManager *mgr, { GList *repo_list = NULL, *ptr; char *sql; - SeafRepo *repo = NULL; int len = strlen(id_prefix); if (len >= 37) @@ -3449,7 +3447,6 @@ seaf_repo_manager_set_inner_pub_repo (SeafRepoManager *mgr, const char *permission) { SeafDB *db = mgr->seaf->db; - char sql[256]; if (seaf_db_type(db) == SEAF_DB_TYPE_PGSQL) { return seaf_db_statement_query (db, @@ -3457,7 +3454,7 @@ seaf_repo_manager_set_inner_pub_repo (SeafRepoManager *mgr, " VALUES (?, ?)" " ON CONFLICT (repo_id)" " DO UPDATE SET permission=?", - 2, "string", repo_id, "string", permission, "string", permission); + 3, "string", repo_id, "string", permission, "string", permission); } else { return seaf_db_statement_query (db, "REPLACE INTO InnerPubRepo (repo_id, permission) VALUES (?, ?)", diff --git a/server/seafile-session.c b/server/seafile-session.c index 30109a00..f5147651 100644 --- a/server/seafile-session.c +++ b/server/seafile-session.c @@ -435,7 +435,7 @@ void schedule_create_system_default_repo (SeafileSession *session) { int db_type = seaf_db_type (session->db); - char *sql; + char *sql = NULL; switch (db_type) { case SEAF_DB_TYPE_MYSQL: @@ -455,7 +455,7 @@ schedule_create_system_default_repo (SeafileSession *session) break; } - if ((session->create_tables || db_type == SEAF_DB_TYPE_PGSQL) + if (sql && (session->create_tables || db_type == SEAF_DB_TYPE_PGSQL) && seaf_db_query (session->db, sql) < 0) return; From f5aebb4c0c320967d4be9961f790e74d2a429a2e Mon Sep 17 00:00:00 2001 From: Janne Mareike Koschinski Date: Tue, 14 Jun 2022 01:58:31 +0200 Subject: [PATCH 5/9] Fix bug where number conversion would fail --- common/seaf-db.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/common/seaf-db.c b/common/seaf-db.c index eacfcc45..742b1516 100644 --- a/common/seaf-db.c +++ b/common/seaf-db.c @@ -1525,7 +1525,7 @@ pgsql_db_query_foreach_row (DBConnection *vconn, const char *sql, PgSQLDBRow row; memset(&row, 0, sizeof(row)); - row.column_count = PQntuples(res); + row.column_count = PQnfields(res); row.results = res; int row_count = PQntuples(res); for (int i = 0; i < row_count; i++) { @@ -1559,7 +1559,7 @@ pgsql_db_row_get_column_int (SeafDBRow *vrow, int idx) PgSQLDBRow *row = (PgSQLDBRow *)vrow; const char* data = PQgetvalue(row->results, row->row_index, idx); - if (!data) { + if (!data || 0 == strlen(data)) { return 0; } @@ -1580,7 +1580,7 @@ pgsql_db_row_get_column_int64 (SeafDBRow *vrow, int idx) PgSQLDBRow *row = (PgSQLDBRow *)vrow; const char* data = PQgetvalue(row->results, row->row_index, idx); - if (!data) { + if (!data || 0 == strlen(data)) { return 0; } From 07b1364805222f1e5c3c47105777ada6d7cdabc6 Mon Sep 17 00:00:00 2001 From: Janne Mareike Koschinski Date: Tue, 14 Jun 2022 01:58:43 +0200 Subject: [PATCH 6/9] Fix bug where existing indexes would be recreated --- common/branch-mgr.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/branch-mgr.c b/common/branch-mgr.c index 52aaf31c..5c6af516 100644 --- a/common/branch-mgr.c +++ b/common/branch-mgr.c @@ -153,7 +153,7 @@ open_db (SeafBranchManager *mgr) " commit_id CHAR(41));"; if (seaf_db_query (mgr->seaf->db, sql) < 0) return -1; - sql = "CREATE UNIQUE INDEX branch_repoidname_idx ON Branch (repo_id, name);"; + sql = "CREATE UNIQUE INDEX IF NOT EXISTS branch_repoidname_idx ON Branch (repo_id, name);"; if (seaf_db_query (mgr->seaf->db, sql) < 0) return -1; break; From 35741873a5c12ea9595811325ae7de68e06846aa Mon Sep 17 00:00:00 2001 From: Janne Mareike Koschinski Date: Tue, 14 Jun 2022 02:17:26 +0200 Subject: [PATCH 7/9] =?UTF-8?q?Revert=20boolean=20to=20smallint=20as=20the?= =?UTF-8?q?=20other=20backends=20currently=20don=E2=80=99t=20support=20it?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/group-mgr.c | 2 +- common/org-mgr.c | 2 +- common/user-mgr.c | 10 +++++----- scripts/sql/pgsql/ccnet.sql | 14 +++++++------- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/common/group-mgr.c b/common/group-mgr.c index 02aa65e4..41d29ea3 100644 --- a/common/group-mgr.c +++ b/common/group-mgr.c @@ -203,7 +203,7 @@ static int check_db_table (CcnetGroupManager *manager, CcnetDB *db) " id BIGSERIAL PRIMARY KEY," " group_id BIGINT," " user_name VARCHAR(255)," - " is_staff BOOLEAN)"; + " is_staff SMALLINT)"; if (seaf_db_query (db, sql) < 0) return -1; sql = "CREATE UNIQUE INDEX IF NOT EXISTS groupuser_group_id_user_name_key ON GroupUser (group_id, user_name)"; diff --git a/common/org-mgr.c b/common/org-mgr.c index ed8b6eef..52939f42 100644 --- a/common/org-mgr.c +++ b/common/org-mgr.c @@ -189,7 +189,7 @@ static int check_db_table (CcnetDB *db) " id BIGSERIAL PRIMARY KEY," " org_id INTEGER," " email VARCHAR(255)," - " is_staff BOOLEAN NOT NULL);"; + " is_staff SMALLINT NOT NULL);"; if (seaf_db_query (db, sql) < 0) return -1; sql = "CREATE UNIQUE INDEX IF NOT EXISTS orguser_org_id_email_key ON OrgUser (org_id, email)"; diff --git a/common/user-mgr.c b/common/user-mgr.c index e6cb5eb6..53913f0d 100644 --- a/common/user-mgr.c +++ b/common/user-mgr.c @@ -661,8 +661,8 @@ static int check_db_table (SeafDB *db) " id BIGSERIAL PRIMARY KEY," " email VARCHAR(255)," " passwd VARCHAR(256)," - " is_staff BOOLEAN NOT NULL," - " is_active BOOLEAN NOT NULL," + " is_staff SMALLINT NOT NULL," + " is_active SMALLINT NOT NULL," " ctime BIGINT," " reference_id VARCHAR(255))"; if (seaf_db_query (db, sql) < 0) @@ -691,7 +691,7 @@ static int check_db_table (SeafDB *db) " id BIGSERIAL PRIMARY KEY," " email VARCHAR(255)," " \"role\" VARCHAR(255)," - " is_manual_set BOOLEAN DEFAULT false)"; + " is_manual_set smallint DEFAULT 0)"; if (seaf_db_query (db, sql) < 0) return -1; sql = "CREATE UNIQUE INDEX IF NOT EXISTS userrole_email_role_key ON UserRole (email, role)"; @@ -705,8 +705,8 @@ static int check_db_table (SeafDB *db) " id BIGSERIAL PRIMARY KEY," " email VARCHAR(255) NOT NULL," " password varchar(255) NOT NULL," - " is_staff BOOLEAN NOT NULL," - " is_active BOOLEAN NOT NULL," + " is_staff SMALLINT NOT NULL," + " is_active SMALLINT NOT NULL," " extra_attrs TEXT," " reference_id VARCHAR(255))"; if (seaf_db_query (db, sql) < 0) diff --git a/scripts/sql/pgsql/ccnet.sql b/scripts/sql/pgsql/ccnet.sql index d975a91c..b8460284 100644 --- a/scripts/sql/pgsql/ccnet.sql +++ b/scripts/sql/pgsql/ccnet.sql @@ -9,8 +9,8 @@ CREATE TABLE IF NOT EXISTS EmailUser ( id BIGSERIAL PRIMARY KEY, email VARCHAR(255), passwd VARCHAR(256), - is_staff BOOLEAN NOT NULL, - is_active BOOLEAN NOT NULL, + is_staff SMALLINT NOT NULL, + is_active SMALLINT NOT NULL, ctime BIGINT, reference_id VARCHAR(255)); -- existing index, reuse old name @@ -43,7 +43,7 @@ CREATE TABLE IF NOT EXISTS GroupUser ( id BIGSERIAL PRIMARY KEY, group_id BIGINT, user_name VARCHAR(255), - is_staff BOOLEAN); + is_staff SMALLINT); -- existing index, reuse old name CREATE UNIQUE INDEX IF NOT EXISTS groupuser_group_id_user_name_key ON GroupUser (group_id, user_name); -- existing index, reuse old name @@ -60,8 +60,8 @@ CREATE TABLE IF NOT EXISTS LDAPUsers ( id BIGSERIAL PRIMARY KEY, email VARCHAR(255) NOT NULL, password varchar(255) NOT NULL, - is_staff BOOLEAN NOT NULL, - is_active BOOLEAN NOT NULL, + is_staff SMALLINT NOT NULL, + is_active SMALLINT NOT NULL, extra_attrs TEXT, reference_id VARCHAR(255)); -- existing index, reuse old name @@ -82,7 +82,7 @@ CREATE TABLE IF NOT EXISTS OrgUser ( id BIGSERIAL PRIMARY KEY, org_id INTEGER, email VARCHAR(255), - is_staff BOOLEAN NOT NULL); + is_staff SMALLINT NOT NULL); -- existing index, reuse old name CREATE UNIQUE INDEX IF NOT EXISTS orguser_org_id_email_key ON OrgUser (org_id, email); -- existing index, reuse old name @@ -101,7 +101,7 @@ CREATE TABLE IF NOT EXISTS UserRole ( id BIGSERIAL PRIMARY KEY, email VARCHAR(255), "role" VARCHAR(255), - is_manual_set BOOLEAN DEFAULT false); + is_manual_set smallint DEFAULT 0); -- existing index, reuse old name CREATE UNIQUE INDEX IF NOT EXISTS userrole_email_role_key ON UserRole (email, role); -- existing index, reuse old name From 54a133ed6b6943665b6c4947c49ece110b171ccd Mon Sep 17 00:00:00 2001 From: Janne Mareike Koschinski Date: Tue, 14 Jun 2022 21:43:37 +0200 Subject: [PATCH 8/9] Fix several small query issues --- common/branch-mgr.c | 2 +- common/seaf-db.c | 31 +++++++++++++++++++++++++------ server/repo-mgr.c | 14 +++++++++++--- server/size-sched.c | 10 +++++----- 4 files changed, 42 insertions(+), 15 deletions(-) diff --git a/common/branch-mgr.c b/common/branch-mgr.c index 5c6af516..94dcac43 100644 --- a/common/branch-mgr.c +++ b/common/branch-mgr.c @@ -339,10 +339,10 @@ seaf_branch_manager_test_and_update_branch (SeafBranchManager *mgr, switch (seaf_db_type (mgr->seaf->db)) { case SEAF_DB_TYPE_MYSQL: - case SEAF_DB_TYPE_PGSQL: sql = "SELECT commit_id FROM Branch WHERE name=? " "AND repo_id=? FOR UPDATE"; break; + case SEAF_DB_TYPE_PGSQL: case SEAF_DB_TYPE_SQLITE: sql = "SELECT commit_id FROM Branch WHERE name=? " "AND repo_id=?"; diff --git a/common/seaf-db.c b/common/seaf-db.c index 742b1516..b578db7c 100644 --- a/common/seaf-db.c +++ b/common/seaf-db.c @@ -1401,7 +1401,12 @@ pgsql_db_execute_sql_no_stmt (DBConnection *vconn, const char *sql) PostgresDBConnection *conn = (PostgresDBConnection *)vconn; PGresult* res = PQexec(conn->db_conn, sql); - if (PQresultStatus(res) != PGRES_COMMAND_OK) { + if (!res) { + seaf_warning ("Failed to execute sql %s: unknown error\n", sql); + return -1; + } + ExecStatusType status = PQresultStatus(res); + if (status != PGRES_EMPTY_QUERY && status != PGRES_COMMAND_OK && status != PGRES_TUPLES_OK) { seaf_warning ("Failed to execute sql %s: %s\n", sql, PQresultErrorMessage(res)); PQclear (res); return -1; @@ -1432,9 +1437,14 @@ _pgsql_db_format_query_string (const char *sql) return g_string_free (buf, FALSE); } -#define INT4OID 23 -#define INT8OID 20 -#define TEXTOID 25 +#define INT2OID 21 +#define INT4OID 23 +#define INT8OID 20 +#define TEXTOID 25 +#define VARCHAROID 1043 +#define BPCHAROID 1042 +#define TIMESTAMPOID 1114 +#define TIMESTAMPTZOID 1184 static PGresult* _pgsql_db_execute_stmt(PGconn *db, const char *sql, int n, va_list args) @@ -1550,6 +1560,15 @@ pgsql_db_row_get_column_string (SeafDBRow *vrow, int idx) { PgSQLDBRow *row = (PgSQLDBRow *)vrow; + if (idx >= row->column_count) { + seaf_warning("Tried retrieving column %d but only have %d columns\n", idx, row->column_count); + return NULL; + } + + if (PQgetisnull(row->results, row->row_index, idx)) { + return NULL; + } + return PQgetvalue(row->results, row->row_index, idx); } @@ -1558,7 +1577,7 @@ pgsql_db_row_get_column_int (SeafDBRow *vrow, int idx) { PgSQLDBRow *row = (PgSQLDBRow *)vrow; - const char* data = PQgetvalue(row->results, row->row_index, idx); + const char* data = pgsql_db_row_get_column_string(row, idx); if (!data || 0 == strlen(data)) { return 0; } @@ -1579,7 +1598,7 @@ pgsql_db_row_get_column_int64 (SeafDBRow *vrow, int idx) { PgSQLDBRow *row = (PgSQLDBRow *)vrow; - const char* data = PQgetvalue(row->results, row->row_index, idx); + const char* data = pgsql_db_row_get_column_string(row, idx); if (!data || 0 == strlen(data)) { return 0; } diff --git a/server/repo-mgr.c b/server/repo-mgr.c index e3e4a01c..4daf9eb0 100644 --- a/server/repo-mgr.c +++ b/server/repo-mgr.c @@ -604,9 +604,17 @@ seaf_repo_manager_del_repo (SeafRepoManager *mgr, remove_virtual_repo_ondisk (mgr, (char *)ptr->data); string_list_free (vrepos); - seaf_db_statement_query (mgr->seaf->db, "DELETE r, v FROM RepoInfo r, VirtualRepo v " - "WHERE r.repo_id=v.repo_id and v.origin_repo=?", - 1, "string", repo_id); + int db_type = seaf_db_type(mgr->seaf->db); + if (db_type == SEAF_DB_TYPE_PGSQL) { + seaf_db_statement_query (mgr->seaf->db, "DELETE FROM RepoInfo r " + "USING VirtualRepo v " + "WHERE r.repo_id=v.repo_id and v.origin_repo=?", + 1, "string", repo_id); + } else { + seaf_db_statement_query (mgr->seaf->db, "DELETE r, v FROM RepoInfo r, VirtualRepo v " + "WHERE r.repo_id=v.repo_id and v.origin_repo=?", + 1, "string", repo_id); + } seaf_db_statement_query (mgr->seaf->db, "DELETE FROM RepoInfo " "WHERE repo_id=?", diff --git a/server/size-sched.c b/server/size-sched.c index 64807434..44fe61da 100644 --- a/server/size-sched.c +++ b/server/size-sched.c @@ -255,12 +255,12 @@ get_old_repo_info_from_db (SeafDB *db, const char *repo_id, gboolean *is_db_err) switch (seaf_db_type (db)) { case SEAF_DB_TYPE_MYSQL: - case SEAF_DB_TYPE_PGSQL: sql = "select s.head_id,s.size,f.file_count FROM " - "RepoSize s LEFT JOIN RepoFileCount f ON " - "s.repo_id=f.repo_id WHERE " - "s.repo_id=? FOR UPDATE"; - break; + "RepoSize s LEFT JOIN RepoFileCount f ON " + "s.repo_id=f.repo_id WHERE " + "s.repo_id=? FOR UPDATE"; + break; + case SEAF_DB_TYPE_PGSQL: case SEAF_DB_TYPE_SQLITE: sql = "select s.head_id,s.size,f.file_count FROM " "RepoSize s LEFT JOIN RepoFileCount f ON " From 11bea1ee40aca22fcf55e83c167a04bcb956fe6b Mon Sep 17 00:00:00 2001 From: Janne Mareike Koschinski Date: Thu, 16 Jun 2022 20:33:07 +0200 Subject: [PATCH 9/9] Fix several small query issues --- common/seaf-db.c | 2 +- server/quota-mgr.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/common/seaf-db.c b/common/seaf-db.c index b578db7c..2570dafb 100644 --- a/common/seaf-db.c +++ b/common/seaf-db.c @@ -1605,7 +1605,7 @@ pgsql_db_row_get_column_int64 (SeafDBRow *vrow, int idx) char *e; errno = 0; - int ret = strtoll (data, &e, 10); + gint64 ret = strtoll (data, &e, 10); if (errno || (e == data)) { seaf_warning ("Number conversion failed.\n"); return -1; diff --git a/server/quota-mgr.c b/server/quota-mgr.c index 3f53abd9..471bb357 100644 --- a/server/quota-mgr.c +++ b/server/quota-mgr.c @@ -190,9 +190,9 @@ seaf_quota_manager_set_user_quota (SeafQuotaManager *mgr, if (seaf_db_type(db) == SEAF_DB_TYPE_PGSQL) { int rc; rc = seaf_db_statement_query (db, - "INSERT INTO UserQuota (user, quota)" + "INSERT INTO UserQuota (\"user\", quota)" " VALUES (?, ?)" - " ON CONFLICT (user)" + " ON CONFLICT (\"user\")" " DO UPDATE SET quota=?", 3, "string", user, "int64", quota, "int64", quota); return rc; @@ -273,9 +273,9 @@ seaf_quota_manager_set_org_user_quota (SeafQuotaManager *mgr, if (seaf_db_type(db) == SEAF_DB_TYPE_PGSQL) { rc = seaf_db_statement_query (db, - "INSERT INTO OrgUserQuota (org_id, user, quota)" + "INSERT INTO OrgUserQuota (org_id, \"user\", quota)" " VALUES (?, ?, ?)" - " ON CONFLICT (org_id, user)" + " ON CONFLICT (org_id, \"user\")" " DO UPDATE SET quota=?", 3, "int", org_id, "string", user, "int64", quota, "int64", quota); return rc;