1
0
mirror of https://github.com/haiwen/ccnet-server.git synced 2025-04-28 02:30:08 +00:00
ccnet-server/net/common/db-wrapper/sqlite-db-ops.c
2016-10-30 10:24:13 +08:00

478 lines
12 KiB
C

#include "common.h"
#include "db-wrapper.h"
#include "sqlite-db-ops.h"
#include <sqlite3.h>
#include <pthread.h>
/* SQLite thread synchronization rountines. */
typedef struct UnlockNotification {
int fired;
pthread_cond_t cond;
pthread_mutex_t mutex;
} UnlockNotification;
static void
unlock_notify_cb(void **ap_arg, int n_arg)
{
int i;
for (i = 0; i < n_arg; i++) {
UnlockNotification *p = (UnlockNotification *)ap_arg[i];
pthread_mutex_lock (&p->mutex);
p->fired = 1;
pthread_cond_signal (&p->cond);
pthread_mutex_unlock (&p->mutex);
}
}
static int
wait_for_unlock_notify(sqlite3 *db)
{
UnlockNotification un;
un.fired = 0;
pthread_mutex_init (&un.mutex, NULL);
pthread_cond_init (&un.cond, NULL);
int rc = sqlite3_unlock_notify(db, unlock_notify_cb, (void *)&un);
if (rc == SQLITE_OK) {
pthread_mutex_lock(&un.mutex);
if (!un.fired)
pthread_cond_wait (&un.cond, &un.mutex);
pthread_mutex_unlock(&un.mutex);
}
pthread_cond_destroy (&un.cond);
pthread_mutex_destroy (&un.mutex);
return rc;
}
static int
sqlite3_blocking_step(sqlite3_stmt *stmt)
{
int rc;
while (SQLITE_LOCKED == (rc = sqlite3_step(stmt))) {
rc = wait_for_unlock_notify(sqlite3_db_handle(stmt));
if (rc != SQLITE_OK)
break;
sqlite3_reset(stmt);
}
return rc;
}
static int
sqlite3_blocking_prepare_v2(sqlite3 *db, const char *sql, int sql_len, sqlite3_stmt **pstmt, const char **pz)
{
int rc;
while (SQLITE_LOCKED == (rc = sqlite3_prepare_v2(db, sql, sql_len, pstmt, pz))) {
rc = wait_for_unlock_notify(db);
if (rc != SQLITE_OK)
break;
}
return rc;
}
static int
sqlite3_blocking_exec(sqlite3 *db, const char *sql, int (*callback)(void *, int, char **, char **), void *arg, char **errmsg)
{
int rc;
while (SQLITE_LOCKED == (rc = sqlite3_exec(db, sql, callback, arg, errmsg))) {
rc = wait_for_unlock_notify(db);
if (rc != SQLITE_OK)
break;
}
return rc;
}
#if 0
#define SQLITE_QUERY_TIMEOUT 3
#define EXEC_SQLITE(status, action) \
do {\
long t = (SQLITE_QUERY_TIMEOUT * G_USEC_PER_SEC);\
int x = 0;\
do {\
status = (action);\
if (((status == SQLITE_BUSY) || (status == SQLITE_LOCKED)) && (x++ <= 9))\
g_usleep(t/(rand() % 3 + 10));\
else\
break;\
} while (1);\
} while (0)
#endif /* 0 */
typedef struct SQLiteDBConnPool {
DBConnPool parent;
char *db_path;
} SQLiteDBConnPool;
DBConnPool *
sqlite_db_conn_pool_new (const char *db_path)
{
SQLiteDBConnPool *pool = g_new0 (SQLiteDBConnPool, 1);
pool->db_path = g_strdup(db_path);
return (DBConnPool *)pool;
}
void
sqlite_db_conn_pool_free (DBConnPool *vpool)
{
if (!vpool)
return;
SQLiteDBConnPool *pool = (SQLiteDBConnPool *)vpool;
g_free (pool->db_path);
g_free (pool);
}
typedef struct SQLiteDBConnection {
DBConnection parent;
sqlite3 *db;
} SQLiteDBConnection;
DBConnection *
sqlite_get_db_connection (DBConnPool *vpool, GError **error)
{
SQLiteDBConnPool *pool = (SQLiteDBConnPool *)vpool;
sqlite3 *db;
int result;
const char *errmsg;
SQLiteDBConnection *conn;
result = sqlite3_open_v2 (pool->db_path, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_SHAREDCACHE, NULL);
if (result != SQLITE_OK) {
errmsg = sqlite3_errmsg(db);
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
"Failed to open sqlite db: %s",
errmsg ? errmsg : "no error given");
return NULL;
}
conn = g_new0 (SQLiteDBConnection, 1);
conn->db = db;
return (DBConnection *)conn;
}
void
sqlite_db_connection_close (DBConnection *vconn)
{
if (!vconn)
return;
SQLiteDBConnection *conn = (SQLiteDBConnection *)vconn;
sqlite3_close (conn->db);
g_free (conn);
}
gboolean
sqlite_db_connection_ping (DBConnection *vconn)
{
return TRUE;
}
gboolean
sqlite_db_connection_execute (DBConnection *vconn, const char *sql, GError **error)
{
SQLiteDBConnection *conn = (SQLiteDBConnection *)vconn;
char *errmsg = NULL;
int rc;
rc = sqlite3_blocking_exec (conn->db, sql, NULL, NULL, &errmsg);
if (rc != SQLITE_OK) {
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
"sqlite3_exec failed: %s",
errmsg ? errmsg : "no error given");
if (errmsg)
sqlite3_free (errmsg);
return FALSE;
}
return TRUE;
}
typedef struct SQLiteResultSet {
ResultSet parent;
sqlite3 *db;
sqlite3_stmt *stmt;
int column_count;
} SQLiteResultSet;
void
sqlite_result_set_free (ResultSet *vr)
{
if (!vr)
return;
SQLiteResultSet *r = (SQLiteResultSet *)vr;
sqlite3_finalize (r->stmt);
g_free (r);
}
ResultSet *
sqlite_execute_query (DBConnection *vconn, const char *sql, GError **error)
{
SQLiteDBConnection *conn = (SQLiteDBConnection *)vconn;
sqlite3_stmt *stmt;
int rc;
SQLiteResultSet *r;
rc = sqlite3_blocking_prepare_v2 (conn->db, sql, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
"sqlite3_prepare_v2 failed: %s", sqlite3_errmsg(conn->db));
return NULL;
}
r = g_new0 (SQLiteResultSet, 1);
r->db = conn->db;
r->stmt = stmt;
r->column_count = sqlite3_column_count (stmt);
return (ResultSet *)r;
}
gboolean
sqlite_result_set_next (ResultSet *vr, GError **error)
{
SQLiteResultSet *r = (SQLiteResultSet *)vr;
int rc;
rc = sqlite3_blocking_step (r->stmt);
if (rc != SQLITE_ROW && rc != SQLITE_DONE) {
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
"sqlite3_step failed: %s", sqlite3_errmsg(r->db));
return FALSE;
}
return (rc == SQLITE_ROW);
}
const char *
sqlite_result_set_get_string (ResultSet *vr, int i, GError **error)
{
SQLiteResultSet *r = (SQLiteResultSet *)vr;
if (i < 0 || i >= r->column_count) {
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
"Column index is out of range.");
return NULL;
}
return (const char *)sqlite3_column_text (r->stmt, i);
}
int
sqlite_result_set_get_column_count (ResultSet *vr)
{
return ((SQLiteResultSet *)vr)->column_count;
}
typedef struct SQLiteDBStmt {
DBStmt parent;
int param_count;
sqlite3 *db;
sqlite3_stmt *stmt;
} SQLiteDBStmt;
DBStmt *
sqlite_prepare_statement (DBConnection *vconn, const char *sql, GError **error)
{
SQLiteDBConnection *conn = (SQLiteDBConnection *)vconn;
sqlite3_stmt *stmt;
int rc;
SQLiteDBStmt *ret;
rc = sqlite3_blocking_prepare_v2 (conn->db, sql, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
"sqlite3_prepare_v2 failed: %s", sqlite3_errmsg(conn->db));
return NULL;
}
ret = g_new0 (SQLiteDBStmt, 1);
ret->stmt = stmt;
ret->db = conn->db;
ret->param_count = sqlite3_bind_parameter_count (stmt);
return (DBStmt *)ret;
}
gboolean
sqlite_stmt_set_int (DBStmt *vstmt, int i, int x, GError **error)
{
SQLiteDBStmt *stmt = (SQLiteDBStmt *)vstmt;
int rc;
if (i < 0 || i >= stmt->param_count) {
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
"Column index is out of range.");
return FALSE;
}
rc = sqlite3_bind_int (stmt->stmt, i+1, x);
if (rc != SQLITE_OK) {
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
"sqlite3_bind_int failed: %s", sqlite3_errmsg(stmt->db));
return FALSE;
}
return TRUE;
}
gboolean
sqlite_stmt_set_int64 (DBStmt *vstmt, int i, gint64 x, GError **error)
{
SQLiteDBStmt *stmt = (SQLiteDBStmt *)vstmt;
int rc;
if (i < 0 || i >= stmt->param_count) {
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
"Column index is out of range.");
return FALSE;
}
rc = sqlite3_bind_int64 (stmt->stmt, i+1, x);
if (rc != SQLITE_OK) {
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
"sqlite3_bind_int failed: %s", sqlite3_errmsg(stmt->db));
return FALSE;
}
return TRUE;
}
gboolean
sqlite_stmt_set_string (DBStmt *vstmt, int i, const char *s, GError **error)
{
SQLiteDBStmt *stmt = (SQLiteDBStmt *)vstmt;
int rc;
if (i < 0 || i >= stmt->param_count) {
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
"Column index is out of range.");
return FALSE;
}
rc = sqlite3_bind_text (stmt->stmt, i+1, s, -1, SQLITE_TRANSIENT);
if (rc != SQLITE_OK) {
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
"sqlite3_bind_int failed: %s", sqlite3_errmsg(stmt->db));
return FALSE;
}
return TRUE;
}
gboolean
sqlite_db_stmt_execute (DBStmt *vstmt, GError **error)
{
SQLiteDBStmt *stmt = (SQLiteDBStmt *)vstmt;
int rc;
rc = sqlite3_blocking_step (stmt->stmt);
if (rc == SQLITE_DONE) {
sqlite3_reset (stmt->stmt);
return TRUE;
} else if (rc == SQLITE_ROW) {
sqlite3_reset (stmt->stmt);
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
"Select statement not allowed in db_stmt_execute.");
return FALSE;
} else {
sqlite3_reset (stmt->stmt);
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
"sqlite3_step failed: %s", sqlite3_errmsg(stmt->db));
return FALSE;
}
}
ResultSet *
sqlite_db_stmt_execute_query (DBStmt *vstmt, GError **error)
{
SQLiteDBStmt *stmt = (SQLiteDBStmt *)vstmt;
SQLiteResultSet *r;
r = g_new0 (SQLiteResultSet, 1);
r->db = stmt->db;
r->stmt = stmt->stmt;
r->column_count = sqlite3_column_count (r->stmt);
return (ResultSet *)r;
}
void
sqlite_db_stmt_free (DBStmt *vstmt)
{
if (!vstmt)
return;
SQLiteDBStmt *stmt = (SQLiteDBStmt *)vstmt;
if (!stmt->parent.result_set) {
sqlite3_finalize (stmt->stmt);
}
g_free (stmt);
}
gboolean
sqlite_db_begin_transaction (DBConnection *vconn, GError **error)
{
SQLiteDBConnection *conn = (SQLiteDBConnection *)vconn;
int rc;
rc = sqlite3_blocking_exec (conn->db, "BEGIN TRANSACTION;", NULL, NULL, NULL);
if (rc != SQLITE_OK) {
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
"begin transaction failed: %s", sqlite3_errmsg(conn->db));
return FALSE;
}
return TRUE;
}
gboolean
sqlite_db_commit (DBConnection *vconn, GError **error)
{
SQLiteDBConnection *conn = (SQLiteDBConnection *)vconn;
int rc;
rc = sqlite3_blocking_exec (conn->db, "COMMIT TRANSACTION;", NULL, NULL, NULL);
if (rc != SQLITE_OK) {
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
"commit transaction failed: %s", sqlite3_errmsg(conn->db));
return FALSE;
}
return TRUE;
}
gboolean
sqlite_db_rollback (DBConnection *vconn, GError **error)
{
SQLiteDBConnection *conn = (SQLiteDBConnection *)vconn;
int rc;
rc = sqlite3_blocking_exec (conn->db, "ROLLBACK TRANSACTION;", NULL, NULL, NULL);
if (rc != SQLITE_OK) {
g_set_error (error, SEAF_DB_ERROR_DOMAIN, SEAF_DB_ERROR_CODE,
"rollback transaction failed: %s", sqlite3_errmsg(conn->db));
return FALSE;
}
return TRUE;
}