diff --git a/pclsync/pbusinessaccount.c b/pclsync/pbusinessaccount.c index 1bdd454..6378644 100644 --- a/pclsync/pbusinessaccount.c +++ b/pclsync/pbusinessaccount.c @@ -31,6 +31,7 @@ #include "plibs.h" #include "pnetlibs.h" #include "psys.h" +#include "psql.h" #include diff --git a/pclsync/pcontacts.c b/pclsync/pcontacts.c index 414f776..eb70235 100644 --- a/pclsync/pcontacts.c +++ b/pclsync/pcontacts.c @@ -32,6 +32,7 @@ #include "pfileops.h" #include "plibs.h" #include "pnetlibs.h" +#include "psql.h" #include diff --git a/pclsync/pcryptofolder.c b/pclsync/pcryptofolder.c index be7d7cc..8233abc 100644 --- a/pclsync/pcryptofolder.c +++ b/pclsync/pcryptofolder.c @@ -53,6 +53,7 @@ #include "prun.h" #include "psys.h" #include "ptask.h" +#include "psql.h" #include diff --git a/pclsync/pdiff.c b/pclsync/pdiff.c index b31eed5..1700740 100644 --- a/pclsync/pdiff.c +++ b/pclsync/pdiff.c @@ -70,6 +70,7 @@ #include "ptools.h" #include "publiclinks.h" #include "putil.h" +#include "psql.h" #define PSYNC_SQL_DOWNLOAD \ "synctype&" NTO_STR(PSYNC_DOWNLOAD_ONLY) "=" NTO_STR(PSYNC_DOWNLOAD_ONLY) diff --git a/pclsync/pdownload.c b/pclsync/pdownload.c index eb14e37..6310124 100644 --- a/pclsync/pdownload.c +++ b/pclsync/pdownload.c @@ -48,6 +48,7 @@ #include "ptask.h" #include "ptimer.h" #include "pupload.h" +#include "psql.h" #include "ppath.h" #include "pfile.h" diff --git a/pclsync/pfileops.c b/pclsync/pfileops.c index 2183cb1..a0bae5a 100644 --- a/pclsync/pfileops.c +++ b/pclsync/pfileops.c @@ -33,6 +33,7 @@ #include "pdiff.h" #include "pfoldersync.h" #include "plibs.h" +#include "psql.h" void pfileops_create_fldr(const binresult *meta) { psync_sql_res *res; diff --git a/pclsync/pfolder.c b/pclsync/pfolder.c index 15d5ab4..515fef4 100644 --- a/pclsync/pfolder.c +++ b/pclsync/pfolder.c @@ -43,6 +43,7 @@ #include "psys.h" #include "ppath.h" #include "pfile.h" +#include "psql.h" #include #include diff --git a/pclsync/pfs.c b/pclsync/pfs.c index 8c8644b..888a3db 100644 --- a/pclsync/pfs.c +++ b/pclsync/pfs.c @@ -28,11 +28,14 @@ DAMAGE. */ -#include -#include -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include @@ -59,15 +62,8 @@ #include "psys.h" #include "ppath.h" #include "ptimer.h" +#include "psql.h" -#include -#include -#include -#include -#include -#include -#include -#include #ifndef FUSE_STAT #define FUSE_STAT stat diff --git a/pclsync/pfscrypto.c b/pclsync/pfscrypto.c index 0e42ce3..a8f27e2 100644 --- a/pclsync/pfscrypto.c +++ b/pclsync/pfscrypto.c @@ -33,12 +33,6 @@ #include #include -#include -#include -#include -#include -#include - #include "pcryptofolder.h" #include "pfile.h" #include "pfscrypto.h" @@ -47,6 +41,7 @@ #include "ppath.h" #include "prun.h" #include "psys.h" +#include "psql.h" // this is only for debug, adds needless checks of tree for local files #if IS_DEBUG diff --git a/pclsync/pfsfolder.c b/pclsync/pfsfolder.c index 441019a..13b7db2 100644 --- a/pclsync/pfsfolder.c +++ b/pclsync/pfsfolder.c @@ -29,12 +29,8 @@ DAMAGE. */ -#include -#include -#include -#include -#include #include +#include #include "pcryptofolder.h" #include "pfoldersync.h" @@ -43,7 +39,9 @@ #include "pfstasks.h" #include "plibs.h" #include "psys.h" -#include +#include "pdbg.h" +#include "psql.h" + static PSYNC_THREAD int cryptoerr = 0; diff --git a/pclsync/pfstasks.c b/pclsync/pfstasks.c index af3ed42..d2f66e1 100644 --- a/pclsync/pfstasks.c +++ b/pclsync/pfstasks.c @@ -35,12 +35,6 @@ #include #include -#include -#include -#include -#include -#include - #include "pcache.h" #include "pcryptofolder.h" #include "putil.h" @@ -52,6 +46,7 @@ #include "ppathstatus.h" #include "psettings.h" #include "ptimer.h" +#include "psql.h" typedef struct { diff --git a/pclsync/pfsupload.c b/pclsync/pfsupload.c index 8c6f95c..9aa6a4a 100644 --- a/pclsync/pfsupload.c +++ b/pclsync/pfsupload.c @@ -34,12 +34,6 @@ #include #include -#include -#include -#include -#include -#include - #include "pcache.h" #include "pdiff.h" #include "pfile.h" @@ -59,6 +53,7 @@ #include "ptimer.h" #include "pupload.h" #include "putil.h" +#include "psql.h" #include "pqevent.h" diff --git a/pclsync/pfsxattr.c b/pclsync/pfsxattr.c index 245200e..8df4fa3 100644 --- a/pclsync/pfsxattr.c +++ b/pclsync/pfsxattr.c @@ -31,15 +31,16 @@ #define FUSE_USE_VERSION 26 -#include "pfsxattr.h" -#include "pfsfolder.h" -#include "pfstasks.h" - -#include "plibs.h" #include #include #include +#include "pfsxattr.h" +#include "pfsfolder.h" +#include "pfstasks.h" +#include "psql.h" +#include "plibs.h" + // needed by psync_fs_set_thread_name extern PSYNC_THREAD const char *psync_thread_name; diff --git a/pclsync/plibs.c b/pclsync/plibs.c index 0cee901..098d322 100644 --- a/pclsync/plibs.c +++ b/pclsync/plibs.c @@ -35,39 +35,18 @@ #include #include -#include "pcache.h" -#include "pdatabase.h" #include "pdbg.h" #include "pfile.h" #include "plibs.h" -#include "plist.h" -#include "plocks.h" -#include "pnetlibs.h" -#include "prun.h" -#include "psettings.h" -#include "psynclib.h" -#include "psys.h" #include "ptimer.h" #include "putil.h" -extern PSYNC_THREAD const char *psync_thread_name; - -#define SQL_NO_LOCK 0 -#define SQL_READ_LOCK 1 -#define SQL_WRITE_LOCK 2 - struct run_after_ptr { struct run_after_ptr *next; psync_run_after_t run; void *ptr; }; -typedef struct { - psync_list list; - psync_transaction_callback_t commit_callback; - psync_transaction_callback_t rollback_callback; - void *ptr; -} tran_callback_t; static const uint8_t __hex_lookupl[513] = {"000102030405060708090a0b0c0d0e0f" "101112131415161718191a1b1c1d1e1f" @@ -88,8 +67,6 @@ static const uint8_t __hex_lookupl[513] = {"000102030405060708090a0b0c0d0e0f" uint16_t const *__hex_lookup = (uint16_t *)__hex_lookupl; -const static char *psync_typenames[] = { - "[invalid type]", "[number]", "[string]", "[float]", "[null]", "[bool]"}; char psync_my_auth[64] = "", psync_my_2fa_code[32], *psync_my_user = NULL, *psync_my_pass = NULL, *psync_my_2fa_token = NULL, @@ -99,1308 +76,6 @@ int psync_my_2fa_code_type = 0, psync_my_2fa_trust = 0, uint64_t psync_my_userid = 0; pthread_mutex_t psync_my_auth_mutex = PTHREAD_MUTEX_INITIALIZER; -psync_rwlock_t psync_db_lock; -sqlite3 *psync_db; -pstatus_t psync_status; -int psync_do_run = 1; -int psync_recache_contacts = 1; -PSYNC_THREAD uint32_t psync_error = 0; - -static pthread_mutex_t psync_db_checkpoint_mutex; - -static int in_transaction = 0; -static int transaction_failed = 0; -static psync_list tran_callbacks; - -void psync_sql_err_callback(void *ptr, int code, const char *msg) { - pdbg_logf(D_WARNING, "database warning %d: %s", code, msg); -} - -static void psync_sql_wal_checkpoint() { - int code; - psync_sql_lock(); - psync_sql_unlock(); - if (pthread_mutex_trylock(&psync_db_checkpoint_mutex)) { - pdbg_logf(D_NOTICE, "checkpoint already in progress"); - return; - } - pdbg_logf(D_NOTICE, "checkpointing database"); - code = sqlite3_wal_checkpoint(psync_db, NULL); - while (code == SQLITE_LOCKED) { - psys_sleep_milliseconds(2); - code = sqlite3_wal_checkpoint(psync_db, NULL); - } - pthread_mutex_unlock(&psync_db_checkpoint_mutex); - if (unlikely(code != SQLITE_OK)) - pdbg_logf(D_CRITICAL, "sqlite3_wal_checkpoint returned error %d", code); - else - pdbg_logf(D_NOTICE, "checkpoint finished"); -} - -static int psync_sql_wal_hook(void *ptr, sqlite3 *db, const char *name, - int numpages) { - if (numpages >= PSYNC_DB_CHECKPOINT_AT_PAGES) - prun_thread("checkpoint charlie", psync_sql_wal_checkpoint); - return SQLITE_OK; -} - -int psync_sql_connect(const char *db) { - static int initmutex = 1; - pthread_mutexattr_t mattr; - struct stat st; - uint64_t dbver; - int initdbneeded = 0; - int code; - - pdbg_assert(sqlite3_libversion_number() == SQLITE_VERSION_NUMBER); - pdbg_assert(!strcmp(sqlite3_sourceid(), SQLITE_SOURCE_ID)); - pdbg_assert(!strcmp(sqlite3_libversion(), SQLITE_VERSION)); - pdbg_logf(D_NOTICE, "Using sqlite version %s source %s", sqlite3_libversion(), - sqlite3_sourceid()); - if (!sqlite3_threadsafe()) { - pdbg_logf(D_CRITICAL, "sqlite is compiled without thread support"); - return -1; - } - if (stat(db, &st) != 0) - initdbneeded = 1; - - code = sqlite3_open(db, &psync_db); - if (likely(code == SQLITE_OK)) { - if (initmutex) { - plocks_init(&psync_db_lock); - pthread_mutexattr_init(&mattr); - pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE); - pthread_mutex_init(&psync_db_checkpoint_mutex, &mattr); - pthread_mutexattr_destroy(&mattr); - initmutex = 0; - } - if (IS_DEBUG) - sqlite3_config(SQLITE_CONFIG_LOG, psync_sql_err_callback, NULL); - sqlite3_wal_hook(psync_db, psync_sql_wal_hook, NULL); - psync_sql_statement(PSYNC_DATABASE_CONFIG); - if (initdbneeded == 1) - return psync_sql_statement(PSYNC_DATABASE_STRUCTURE); - else if (psync_sql_statement( - "DELETE FROM setting WHERE id='justcheckingiflocked'")) { - pdbg_logf(D_ERROR, "database is locked"); - sqlite3_close(psync_db); - plocks_destroy(&psync_db_lock); - return -1; - } - - dbver = - psync_sql_cellint("SELECT value FROM setting WHERE id='dbversion'", 0); - if (dbver < PSYNC_DATABASE_VERSION) { - uint64_t i; - pdbg_logf(D_NOTICE, "database version %d detected, upgrading to %d", - (int)dbver, (int)PSYNC_DATABASE_VERSION); - for (i = dbver; i < PSYNC_DATABASE_VERSION; i++) - if (psync_sql_statement(psync_db_upgrade[i])) { - pdbg_logf(D_ERROR, "error running statement %s on sqlite %s", - psync_db_upgrade[i], sqlite3_libversion()); - if (IS_DEBUG) { - psync_error = PERROR_DATABASE_OPEN; - return -1; - } - } - } - - return 0; - } else { - pdbg_logf(D_CRITICAL, "could not open sqlite database %s: %d", db, code); - return -1; - } -} - -int psync_sql_close() { - int code, tries; - tries = 0; - while (1) { - code = sqlite3_close(psync_db); - if (code == SQLITE_BUSY) { - pcache_clean(); - tries++; - if (tries > 100) { - psys_sleep_milliseconds_fast(tries - 90); - if (tries > 200) { - pdbg_logf(D_ERROR, "failed to close database"); - break; - } - } - } else - break; - } - psync_db = NULL; - if (unlikely(code != SQLITE_OK)) { - pdbg_logf(D_CRITICAL, "error when closing database: %d", code); - code = sqlite3_close_v2(psync_db); - if (unlikely(code != SQLITE_OK)) { - pdbg_logf(D_CRITICAL, - "error when closing database even with sqlite3_close_v2: %d", code); - return -1; - } - } - return 0; -} - -int psync_sql_reopen(const char *path) { - sqlite3 *db; - int code; - pdbg_logf(D_NOTICE, "reopening database %s", path); - code = sqlite3_open(path, &db); - if (likely(code == SQLITE_OK)) { - code = sqlite3_wal_checkpoint(db, NULL); - if (unlikely(code != SQLITE_OK)) { - pdbg_logf(D_CRITICAL, "sqlite3_wal_checkpoint returned error %d", code); - sqlite3_close(db); - return -1; - } - code = sqlite3_close(db); - if (unlikely(code != SQLITE_OK)) { - pdbg_logf(D_CRITICAL, "sqlite3_close returned error %d", code); - return -1; - } - return 0; - } else { - pdbg_logf(D_CRITICAL, "could not open sqlite dabase %s: %d", path, code); - return -1; - } -} - -void psync_sql_checkpoint_lock() { - pthread_mutex_lock(&psync_db_checkpoint_mutex); -} - -void psync_sql_checkpoint_unlock() { - pthread_mutex_unlock(&psync_db_checkpoint_mutex); -} - -#if IS_DEBUG -typedef struct { - psync_list list; - const char *file; - const char *thread; - struct timespec tm; - unsigned line; -} rd_lock_data; - -static PSYNC_THREAD rd_lock_data *rdlock = NULL; -static PSYNC_THREAD unsigned long sqlrdlockcnt = 0; -static PSYNC_THREAD struct timespec sqlrdlockstart; -unsigned long sqllockcnt = 0; -static struct timespec sqllockstart; -static const char *wrlockfile = "none"; -static const char *wrlockthread = ""; -static unsigned wrlockline = 0; -static unsigned wrlocked = 0; -static pthread_t wrlocker; -static psync_list rdlocks = PSYNC_LIST_STATIC_INIT(rdlocks); -static pthread_mutex_t rdmutex = PTHREAD_MUTEX_INITIALIZER; - -static void record_wrlock(const char *file, unsigned line) { - if (unlikely(rdlock)) { - pdbg_logf(D_BUG, - "trying to get write lock at %s:%u, but read lock is already taken " - "at %s:%u, aborting", - file, line, rdlock->file, rdlock->line); - sendpdbg_logf("trying to get write lock at %s:%u, but read lock is already " - "taken at %s:%u, aborting", - file, line, rdlock->file, rdlock->line); - abort(); - } - sendpdbg_assert(!wrlocked); - pdbg_assert(!wrlocked); - wrlockfile = file; - wrlockline = line; - wrlockthread = psync_thread_name; - wrlocked = 1; - wrlocker = pthread_self(); -} - -static void record_wrunlock() { - sendpdbg_assert(pthread_equal(pthread_self(), wrlocker)); - sendpdbg_assert(wrlocked); - pdbg_assert(pthread_equal(pthread_self(), wrlocker)); - pdbg_assert(wrlocked); - wrlocked = 0; -} - -static void record_rdlock(const char *file, unsigned line, - struct timespec *tm) { - rd_lock_data *lock; - lock = malloc(sizeof(rd_lock_data)); - lock->file = file; - lock->thread = psync_thread_name; - lock->line = line; - memcpy(&lock->tm, tm, sizeof(struct timespec)); - pthread_mutex_lock(&rdmutex); - psync_list_add_tail(&rdlocks, &lock->list); - pthread_mutex_unlock(&rdmutex); - rdlock = lock; -} - -static rd_lock_data *record_rdunlock() { - rd_lock_data *lock; - pdbg_assert(rdlock); - lock = rdlock; - rdlock = NULL; - pthread_mutex_lock(&rdmutex); - psync_list_del(&lock->list); - pthread_mutex_unlock(&rdmutex); - return lock; -} - -void psync_sql_dump_locks() { - rd_lock_data *lock; - char dttime[36]; - if (wrlocked) { - time_format(sqllockstart.tv_sec, sqllockstart.tv_nsec, dttime); - pdbg_logf(D_ERROR, "write lock taken by thread %s from %s:%u at %s", - wrlockthread, wrlockfile, wrlockline, dttime); - sendpdbg_logf("write lock taken by thread %s from %s:%u at %s", wrlockthread, - wrlockfile, wrlockline, dttime); - } - pthread_mutex_lock(&rdmutex); - psync_list_for_each_element(lock, &rdlocks, rd_lock_data, list) { - time_format(lock->tm.tv_sec, lock->tm.tv_nsec, dttime); - pdbg_logf(D_ERROR, "read lock taken by thread %s from %s:%u at %s", - lock->thread, lock->file, lock->line, dttime); - sendpdbg_logf("read lock taken by thread %s from %s:%u at %s", lock->thread, - lock->file, lock->line, dttime); - } - pthread_mutex_unlock(&rdmutex); -} - -#endif - -#if IS_DEBUG -int psync_sql_do_trylock(const char *file, unsigned line) { - if (plocks_trywrlock(&psync_db_lock)) - return -1; - if (++sqllockcnt == 1) { - clock_gettime(CLOCK_REALTIME, &sqllockstart); - record_wrlock(file, line); - } - return 0; -} -#else -int psync_sql_trylock() { return plocks_trywrlock(&psync_db_lock); } -#endif - -#if IS_DEBUG -void psync_sql_do_lock(const char *file, unsigned line) { - if (plocks_trywrlock(&psync_db_lock)) { - struct timespec start, end; - unsigned long msec; - clock_gettime(CLOCK_REALTIME, &start); - memcpy(&end, &start, sizeof(end)); - end.tv_sec += PSYNC_DEBUG_LOCK_TIMEOUT; - if (plocks_timedwrlock(&psync_db_lock, &end)) { - pdbg_logf(D_BUG, "sql write lock timed out called from %s:%u", file, line); - sendpdbg_logf("sql write lock timed out called from %s:%u", file, line); - psync_sql_dump_locks(); - abort(); - } - clock_gettime(CLOCK_REALTIME, &end); - msec = (end.tv_sec - start.tv_sec) * 1000 + end.tv_nsec / 1000000 - - start.tv_nsec / 1000000; - if (msec >= 5) - pdbg_logf(D_WARNING, "waited %lu milliseconds for database write lock", msec); - pdbg_assert(sqllockcnt == 0); - sqllockcnt++; - memcpy(&sqllockstart, &end, sizeof(struct timespec)); - record_wrlock(file, line); - } else if (++sqllockcnt == 1) { - clock_gettime(CLOCK_REALTIME, &sqllockstart); - record_wrlock(file, line); - } -} -#else -void psync_sql_lock() { plocks_wrlock(&psync_db_lock); } -#endif - -void psync_sql_unlock() { -#if IS_DEBUG - pdbg_assert(sqllockcnt > 0); - if (--sqllockcnt == 0) { - struct timespec end; - unsigned long msec; - clock_gettime(CLOCK_REALTIME, &end); - msec = (end.tv_sec - sqllockstart.tv_sec) * 1000 + end.tv_nsec / 1000000 - - sqllockstart.tv_nsec / 1000000; - if (msec >= 10) - pdbg_logf(D_WARNING, - "held database write lock for %lu milliseconds taken from %s:%u", - msec, wrlockfile, wrlockline); - record_wrunlock(); - plocks_unlock(&psync_db_lock); - } else - plocks_unlock(&psync_db_lock); -#else - plocks_unlock(&psync_db_lock); -#endif -} - -#if IS_DEBUG -void psync_sql_do_rdlock(const char *file, unsigned line) { - if (plocks_tryrdlock(&psync_db_lock)) { - struct timespec start, end; - unsigned long msec; - clock_gettime(CLOCK_REALTIME, &start); - memcpy(&end, &start, sizeof(end)); - end.tv_sec += PSYNC_DEBUG_LOCK_TIMEOUT; - if (plocks_timedrdlock(&psync_db_lock, &end)) { - pdbg_logf(D_BUG, "sql read lock timed out, called from %s:%u", file, line); - sendpdbg_logf("sql read lock timed out, called from %s:%u", file, line); - psync_sql_dump_locks(); - abort(); - } - clock_gettime(CLOCK_REALTIME, &end); - msec = (end.tv_sec - start.tv_sec) * 1000 + end.tv_nsec / 1000000 - - start.tv_nsec / 1000000; - if (msec >= 5) - pdbg_logf(D_WARNING, "waited %lu milliseconds for database read lock", msec); - sqlrdlockcnt++; - memcpy(&sqlrdlockstart, &end, sizeof(struct timespec)); - record_rdlock(file, line, &sqlrdlockstart); - } else if (++sqlrdlockcnt == 1) { - clock_gettime(CLOCK_REALTIME, &sqlrdlockstart); - record_rdlock(file, line, &sqlrdlockstart); - } -} -#else -void psync_sql_rdlock() { plocks_rdlock(&psync_db_lock); } -#endif - - -void psync_list_bulder_add_sql(psync_list_builder_t *builder, - psync_sql_res *res, - psync_list_builder_sql_callback callback) { - psync_variant_row row; - while ((row = psync_sql_fetch_row(res))) { - if (!builder->last_elements || - builder->last_elements->used >= builder->elements_per_list) { - builder->last_elements = (psync_list_element_list *)malloc( - offsetof(psync_list_element_list, elements) + - builder->element_size * builder->elements_per_list); - psync_list_add_tail(&builder->element_list, - &builder->last_elements->list); - builder->last_elements->used = 0; - } - builder->current_element = - builder->last_elements->elements + - builder->last_elements->used * builder->element_size; - builder->cstrcnt = psync_list_bulder_push_num(builder); - *builder->cstrcnt = 0; - while (callback(builder, builder->current_element, row)) { - row = psync_sql_fetch_row(res); - if (!row) - break; - *builder->cstrcnt = 0; - } - builder->last_elements->used++; - builder->cnt++; - } - psync_sql_free_result(res); -} - -void psync_sql_rdunlock() { -#if IS_DEBUG - if (unlikely(sqlrdlockcnt == 0)) { - psync_sql_unlock(); - return; - } - if (--sqlrdlockcnt == 0) { - struct timespec end; - unsigned long msec; - rd_lock_data *lock; - plocks_unlock(&psync_db_lock); - clock_gettime(CLOCK_REALTIME, &end); - lock = record_rdunlock(); - msec = (end.tv_sec - sqlrdlockstart.tv_sec) * 1000 + end.tv_nsec / 1000000 - - sqlrdlockstart.tv_nsec / 1000000; - if (msec >= 20) - pdbg_logf(D_WARNING, - "held database read lock for %lu milliseconds taken at %s:%u", msec, - lock->file, lock->line); - free(lock); - } else - plocks_unlock(&psync_db_lock); -#else - plocks_unlock(&psync_db_lock); -#endif -} - -int psync_sql_has_waiters() { - return plocks_num_waiters(&psync_db_lock) > 0; -} - -int psync_sql_isrdlocked() { - return plocks_holding_rdlock(&psync_db_lock); -} - -int psync_sql_islocked() { return plocks_holding_lock(&psync_db_lock); } - -int psync_sql_tryupgradelock() { -#if IS_DEBUG - if (plocks_holding_wrlock(&psync_db_lock)) - return 0; - pdbg_assert(plocks_holding_rdlock(&psync_db_lock)); - if (plocks_towrlock(&psync_db_lock)) - return -1; - else { - rd_lock_data *lock = record_rdunlock(); - sqllockcnt = sqlrdlockcnt; - sqlrdlockcnt = 0; - pdbg_assert(sqllockcnt == 1); - sqllockstart = sqlrdlockstart; - record_wrlock(lock->file, lock->line); - free(lock); - return 0; - } -#else - return plocks_towrlock(&psync_db_lock); -#endif -} - -int psync_sql_sync() { - int code; - pthread_mutex_lock(&psync_db_checkpoint_mutex); - code = sqlite3_wal_checkpoint(psync_db, NULL); - if (unlikely(code == SQLITE_BUSY || code == SQLITE_LOCKED)) { - psync_sql_lock(); - code = sqlite3_wal_checkpoint(psync_db, NULL); - psync_sql_unlock(); - } - pthread_mutex_unlock(&psync_db_checkpoint_mutex); - if (unlikely(code != SQLITE_OK)) { - pdbg_logf(D_CRITICAL, "sqlite3_wal_checkpoint returned error %d", code); - return -1; - } else - return 0; -} - -#if IS_DEBUG -int psync_sql_do_statement(const char *sql, const char *file, unsigned line) { - char *errmsg; - int code; - psync_sql_do_lock(file, line); -#else -int psync_sql_statement(const char *sql) { - char *errmsg; - int code; - psync_sql_lock(); -#endif - code = sqlite3_exec(psync_db, sql, NULL, NULL, &errmsg); - psync_sql_unlock(); - if (likely(code == SQLITE_OK)) - return 0; - else { -#if IS_DEBUG - pdbg_logf(D_ERROR, "error running sql statement: %s: %s called from %s:%u", sql, - errmsg, file, line); -#else - pdbg_logf(D_ERROR, "error running sql statement: %s: %s", sql, errmsg); -#endif - sqlite3_free(errmsg); - return -1; - } -} - -#if IS_DEBUG -int psync_sql_do_start_transaction(const char *file, unsigned line) { - psync_sql_res *res; - psync_sql_do_lock(file, line); - res = psync_sql_do_prep_statement("BEGIN", file, line); -#else -int psync_sql_start_transaction() { - psync_sql_res *res; - psync_sql_lock(); - res = psync_sql_prep_statement("BEGIN"); -#endif - pdbg_assert(!in_transaction); - if (unlikely(!res || psync_sql_run_free(res))) - return -1; - in_transaction = 1; - transaction_failed = 0; - psync_list_init(&tran_callbacks); - return 0; -} - -static void run_commit_callbacks(int success) { - tran_callback_t *cb; - psync_list *l1, *l2; - psync_list_for_each_safe(l1, l2, &tran_callbacks) { - cb = psync_list_element(l1, tran_callback_t, list); - if (success) - cb->commit_callback(cb->ptr); - else - cb->rollback_callback(cb->ptr); - free(cb); - } -} - -int psync_sql_commit_transaction() { - pdbg_assert(in_transaction); - if (likely(!transaction_failed)) { - psync_sql_res *res = psync_sql_prep_statement("COMMIT"); - if (likely(!psync_sql_run_free(res))) { - run_commit_callbacks(1); - in_transaction = 0; - psync_sql_unlock(); - return 0; - } - } else - pdbg_logf(D_ERROR, "rolling back transaction as some statements failed"); - psync_sql_rollback_transaction(); - return -1; -} - -int psync_sql_rollback_transaction() { - psync_sql_res *res = psync_sql_prep_statement("ROLLBACK"); - pdbg_assert(in_transaction); - psync_sql_run_free(res); - run_commit_callbacks(0); - in_transaction = 0; - psync_sql_unlock(); - return 0; -} - -void psync_sql_transation_add_callbacks( - psync_transaction_callback_t commit_callback, - psync_transaction_callback_t rollback_callback, void *ptr) { - tran_callback_t *cb; - pdbg_assert(in_transaction); - cb = malloc(sizeof(tran_callback_t)); - cb->commit_callback = commit_callback; - cb->rollback_callback = rollback_callback; - cb->ptr = ptr; - psync_list_add_tail(&tran_callbacks, &cb->list); -} - -char *psync_sql_cellstr(const char *sql) { - sqlite3_stmt *stmt; - int code; - psync_sql_rdlock(); - code = sqlite3_prepare_v2(psync_db, sql, -1, &stmt, NULL); - if (unlikely(code != SQLITE_OK)) { - psync_sql_rdunlock(); - pdbg_logf(D_ERROR, "error running sql statement: %s: %s", sql, - sqlite3_errmsg(psync_db)); - sendtpdbg_logf("error running sql statement: %s: %s", sql, - sqlite3_errmsg(psync_db)); - return NULL; - } - code = sqlite3_step(stmt); - if (code == SQLITE_ROW) { - char *ret; - ret = (char *)sqlite3_column_text(stmt, 0); - if (ret) - ret = psync_strdup(ret); - sqlite3_finalize(stmt); - psync_sql_rdunlock(); - return ret; - } else { - sqlite3_finalize(stmt); - psync_sql_rdunlock(); - if (unlikely(code != SQLITE_DONE)) { - pdbg_logf(D_ERROR, "sqlite3_step returned error: %s: %s", sql, - sqlite3_errmsg(psync_db)); - sendtpdbg_logf("sqlite3_step returned error: %s: %s", sql, - sqlite3_errmsg(psync_db)); - } - return NULL; - } -} - -int64_t psync_sql_cellint(const char *sql, int64_t dflt) { - sqlite3_stmt *stmt; - int code; - psync_sql_rdlock(); - code = sqlite3_prepare_v2(psync_db, sql, -1, &stmt, NULL); - if (unlikely(code != SQLITE_OK)) { - pdbg_logf(D_ERROR, "error running sql statement: %s: %s", sql, - sqlite3_errmsg(psync_db)); - sendtpdbg_logf("error running sql statement: %s: %s", sql, - sqlite3_errmsg(psync_db)); - } else { - code = sqlite3_step(stmt); - if (code == SQLITE_ROW) - dflt = sqlite3_column_int64(stmt, 0); - else if (unlikely(code != SQLITE_DONE)) { - pdbg_logf(D_ERROR, "sqlite3_step returned error: %s: %s", sql, - sqlite3_errmsg(psync_db)); - sendtpdbg_logf("sqlite3_step returned error: %s: %s", sql, - sqlite3_errmsg(psync_db)); - } - sqlite3_finalize(stmt); - } - psync_sql_rdunlock(); - return dflt; -} - -char **psync_sql_rowstr(const char *sql) { - sqlite3_stmt *stmt; - int code, cnt; - psync_sql_rdlock(); - code = sqlite3_prepare_v2(psync_db, sql, -1, &stmt, NULL); - if (unlikely(code != SQLITE_OK)) { - psync_sql_rdunlock(); - pdbg_logf(D_ERROR, "error running sql statement: %s: %s", sql, - sqlite3_errmsg(psync_db)); - sendtpdbg_logf("error running sql statement: %s: %s", sql, - sqlite3_errmsg(psync_db)); - return NULL; - } - cnt = sqlite3_column_count(stmt); - code = sqlite3_step(stmt); - if (code == SQLITE_ROW) { - char **arr, *nstr, *str; - size_t l, ln; - VAR_ARRAY(lens, size_t, cnt); - int i; - ln = 0; - for (i = 0; i < cnt; i++) { - l = sqlite3_column_bytes(stmt, i); - ln += l; - lens[i] = l; - } - ln += (sizeof(char *) + 1) * cnt; - arr = (char **)malloc(ln); - nstr = ((char *)arr) + sizeof(char *) * cnt; - for (i = 0; i < cnt; i++) { - str = (char *)sqlite3_column_blob(stmt, i); - if (str) { - ln = lens[i]; - memcpy(nstr, str, ln); - nstr[ln] = 0; - arr[i] = nstr; - nstr += ln + 1; - } else - arr[i] = NULL; - } - sqlite3_finalize(stmt); - psync_sql_rdunlock(); - return arr; - } else { - sqlite3_finalize(stmt); - psync_sql_rdunlock(); - if (unlikely(code != SQLITE_DONE)) { - pdbg_logf(D_ERROR, "sqlite3_step returned error: %s: %s", sql, - sqlite3_errmsg(psync_db)); - sendtpdbg_logf("sqlite3_step returned error: %s: %s", sql, - sqlite3_errmsg(psync_db)); - } - return NULL; - } -} - -psync_variant *psync_sql_row(const char *sql) { - sqlite3_stmt *stmt; - int code, cnt; - psync_sql_rdlock(); - code = sqlite3_prepare_v2(psync_db, sql, -1, &stmt, NULL); - if (unlikely(code != SQLITE_OK)) { - psync_sql_rdunlock(); - pdbg_logf(D_ERROR, "error running sql statement: %s: %s", sql, - sqlite3_errmsg(psync_db)); - sendtpdbg_logf("error running sql statement: %s: %s", sql, - sqlite3_errmsg(psync_db)); - return NULL; - } - cnt = sqlite3_column_count(stmt); - code = sqlite3_step(stmt); - if (code == SQLITE_ROW) { - psync_variant *arr; - char *nstr, *str; - size_t l, ln; - VAR_ARRAY(lens, size_t, cnt); - int i, t; - VAR_ARRAY(types, int, cnt); - ln = sizeof(psync_variant) * cnt; - for (i = 0; i < cnt; i++) { - t = sqlite3_column_type(stmt, i); - types[i] = t; - if (t == SQLITE_TEXT || t == SQLITE_BLOB) { - l = sqlite3_column_bytes(stmt, i); - ln += l + 1; - lens[i] = l; - } - } - arr = (psync_variant *)malloc(ln); - nstr = ((char *)arr) + sizeof(psync_variant) * cnt; - for (i = 0; i < cnt; i++) { - t = types[i]; - if (t == SQLITE_INTEGER) { - arr[i].type = PSYNC_TNUMBER; - arr[i].snum = sqlite3_column_int64(stmt, i); - } else if (t == SQLITE_TEXT || t == SQLITE_BLOB) { - str = (char *)sqlite3_column_blob(stmt, i); - ln = lens[i]; - memcpy(nstr, str, ln); - nstr[ln] = 0; - arr[i].type = PSYNC_TSTRING; - arr[i].str = nstr; - nstr += ln + 1; - } else if (t == SQLITE_FLOAT) { - arr[i].type = PSYNC_TREAL; - arr[i].real = sqlite3_column_double(stmt, i); - } else { - arr[i].type = PSYNC_TNULL; - } - } - sqlite3_finalize(stmt); - psync_sql_rdunlock(); - return arr; - } else { - sqlite3_finalize(stmt); - psync_sql_rdunlock(); - if (unlikely(code != SQLITE_DONE)) { - pdbg_logf(D_ERROR, "sqlite3_step returned error: %s: %s", sql, - sqlite3_errmsg(psync_db)); - sendtpdbg_logf("sqlite3_step returned error: %s: %s", sql, - sqlite3_errmsg(psync_db)); - } - return NULL; - } -} - -#if IS_DEBUG -psync_sql_res *psync_sql_do_query_nocache(const char *sql, const char *file, - unsigned line) { -#else -psync_sql_res *psync_sql_query_nocache(const char *sql) { -#endif - sqlite3_stmt *stmt; - psync_sql_res *res; - int code, cnt; -#if IS_DEBUG - psync_sql_do_lock(file, line); -#else - psync_sql_lock(); -#endif - code = sqlite3_prepare_v2(psync_db, sql, -1, &stmt, NULL); - if (unlikely(code != SQLITE_OK)) { - psync_sql_unlock(); -#if IS_DEBUG - pdbg_logf(D_ERROR, "error running sql statement: %s: %s called from %s:%u", sql, - sqlite3_errmsg(psync_db), file, line); - sendpdbg_logf("error running sql statement: %s: %s called from %s:%u", sql, - sqlite3_errmsg(psync_db), file, line); -#else - pdbg_logf(D_ERROR, "error running sql statement: %s: %s", sql, - sqlite3_errmsg(psync_db)); - sendpdbg_logf("error running sql statement: %s: %s", sql, - sqlite3_errmsg(psync_db)); -#endif - return NULL; - } - cnt = sqlite3_column_count(stmt); - res = (psync_sql_res *)malloc(sizeof(psync_sql_res) + - cnt * sizeof(psync_variant)); - res->stmt = stmt; - res->sql = sql; - res->column_count = cnt; - res->locked = SQL_WRITE_LOCK; - return res; -} - -#if IS_DEBUG -psync_sql_res *psync_sql_do_query(const char *sql, const char *file, - unsigned line) { -#else -psync_sql_res *psync_sql_query(const char *sql) { -#endif - psync_sql_res *ret; - ret = (psync_sql_res *)pcache_get(sql); - if (ret) { - // pdbg_logf(D_NOTICE, "got query %s from cache", sql); - ret->locked = SQL_WRITE_LOCK; - ret->sql = sql; -#if IS_DEBUG - psync_sql_do_lock(file, line); -#else - psync_sql_lock(); -#endif - return ret; - } else -#if IS_DEBUG - return psync_sql_do_query_nocache(sql, file, line); -#else - return psync_sql_query_nocache(sql); -#endif -} - -#if IS_DEBUG -psync_sql_res *psync_sql_do_query_rdlock_nocache(const char *sql, - const char *file, - unsigned line) { -#else -psync_sql_res *psync_sql_query_rdlock_nocache(const char *sql) { -#endif - sqlite3_stmt *stmt; - psync_sql_res *res; - int code, cnt; -#if IS_DEBUG - psync_sql_do_rdlock(file, line); -#else - psync_sql_rdlock(); -#endif - code = sqlite3_prepare_v2(psync_db, sql, -1, &stmt, NULL); - if (unlikely(code != SQLITE_OK)) { - psync_sql_rdunlock(); -#if IS_DEBUG - pdbg_logf(D_ERROR, "error running sql statement: %s: %s called from %s:%u", sql, - sqlite3_errmsg(psync_db), file, line); - sendpdbg_logf("error running sql statement: %s: %s called from %s:%u", sql, - sqlite3_errmsg(psync_db), file, line); -#else - pdbg_logf(D_ERROR, "error running sql statement: %s: %s", sql, - sqlite3_errmsg(psync_db)); - sendpdbg_logf("error running sql statement: %s: %s", sql, - sqlite3_errmsg(psync_db)); -#endif - return NULL; - } - cnt = sqlite3_column_count(stmt); - res = (psync_sql_res *)malloc(sizeof(psync_sql_res) + - cnt * sizeof(psync_variant)); - res->stmt = stmt; - res->sql = sql; - res->column_count = cnt; - res->locked = SQL_READ_LOCK; - return res; -} - -#if IS_DEBUG -psync_sql_res *psync_sql_do_query_rdlock(const char *sql, const char *file, - unsigned line) { -#else -psync_sql_res *psync_sql_query_rdlock(const char *sql) { -#endif - psync_sql_res *ret; - ret = (psync_sql_res *)pcache_get(sql); - if (ret) { - // pdbg_logf(D_NOTICE, "got query %s from cache", sql); - ret->locked = SQL_READ_LOCK; - ret->sql = sql; -#if IS_DEBUG - psync_sql_do_rdlock(file, line); -#else - psync_sql_rdlock(); -#endif - return ret; - } else -#if IS_DEBUG - return psync_sql_do_query_rdlock_nocache(sql, file, line); -#else - return psync_sql_query_rdlock_nocache(sql); -#endif -} - -#if IS_DEBUG -psync_sql_res *psync_sql_do_query_nolock_nocache(const char *sql, - const char *file, - unsigned line) { -#else -psync_sql_res *psync_sql_query_nolock_nocache(const char *sql) { -#endif - sqlite3_stmt *stmt; - psync_sql_res *res; - int code, cnt; -#if IS_DEBUG - if (!psync_sql_islocked()) { - pdbg_logf(D_BUG, - "illegal use of psync_sql_query_nolock, can only be used while " - "holding lock, invoked from %s:%u, sql: %s", - file, line, sql); - sendpdbg_logf("illegal use of psync_sql_query_nolock, can only be used while " - "holding lock, invoked from %s:%u, sql: %s", - file, line, sql); - abort(); - } -#endif - code = sqlite3_prepare_v2(psync_db, sql, -1, &stmt, NULL); - if (unlikely(code != SQLITE_OK)) { - pdbg_logf(D_ERROR, "error running sql statement: %s: %s", sql, - sqlite3_errmsg(psync_db)); - sendpdbg_logf("error running sql statement: %s: %s", sql, - sqlite3_errmsg(psync_db)); - return NULL; - } - cnt = sqlite3_column_count(stmt); - res = (psync_sql_res *)malloc(sizeof(psync_sql_res) + - cnt * sizeof(psync_variant)); - res->stmt = stmt; - res->sql = sql; - res->column_count = cnt; - res->locked = SQL_NO_LOCK; - return res; -} - -#if IS_DEBUG -psync_sql_res *psync_sql_do_query_nolock(const char *sql, const char *file, - unsigned line) { -#else -psync_sql_res *psync_sql_query_nolock(const char *sql) { -#endif - psync_sql_res *ret; -#if IS_DEBUG - if (!psync_sql_islocked()) { - pdbg_logf(D_BUG, - "illegal use of psync_sql_query_nolock, can only be used while " - "holding lock, invoked from %s:%u, sql: %s", - file, line, sql); - sendpdbg_logf("illegal use of psync_sql_query_nolock, can only be used while " - "holding lock, invoked from %s:%u, sql: %s", - file, line, sql); - abort(); - } -#endif - ret = (psync_sql_res *)pcache_get(sql); - if (ret) { - // pdbg_logf(D_NOTICE, "got query %s from cache", sql); - ret->locked = SQL_NO_LOCK; - ret->sql = sql; - return ret; - } else -#if IS_DEBUG - return psync_sql_do_query_nolock_nocache(sql, file, line); -#else - return psync_sql_query_nolock_nocache(sql); -#endif -} - -static void psync_sql_free_cache(void *ptr) { - psync_sql_res *res = (psync_sql_res *)ptr; - sqlite3_finalize(res->stmt); -#if IS_DEBUG - memset(res, 0xff, sizeof(psync_sql_res)); -#endif - free(res); -} - -static void psync_sql_res_unlock(psync_sql_res *res) { - switch (res->locked) { - case SQL_NO_LOCK: - break; - case SQL_READ_LOCK: - psync_sql_rdunlock(); - break; - case SQL_WRITE_LOCK: - psync_sql_unlock(); - break; -#if IS_DEBUG - default: - pdbg_logf(D_ERROR, "unknown value for locked %d", res->locked); - abort(); -#endif - } -} - -void psync_sql_free_result(psync_sql_res *res) { - int code = sqlite3_reset(res->stmt); - psync_sql_res_unlock(res); -#if IS_DEBUG - memset(res->row, 0xff, res->column_count * sizeof(psync_variant)); -#endif - if (code == SQLITE_OK) - pcache_add(res->sql, res, PSYNC_QUERY_CACHE_SEC, psync_sql_free_cache, - PSYNC_QUERY_MAX_CNT); - else - psync_sql_free_cache(res); -} - -void psync_sql_free_result_nocache(psync_sql_res *res) { - sqlite3_finalize(res->stmt); - psync_sql_res_unlock(res); -#if IS_DEBUG - memset(res, 0xff, sizeof(psync_sql_res)); -#endif - free(res); -} - -#if IS_DEBUG -psync_sql_res *psync_sql_do_prep_statement_nocache(const char *sql, - const char *file, - unsigned line) { -#else -psync_sql_res *psync_sql_prep_statement_nocache(const char *sql) { -#endif - sqlite3_stmt *stmt; - psync_sql_res *res; - int code; -#if IS_DEBUG - psync_sql_do_lock(file, line); -#else - psync_sql_lock(); -#endif - code = sqlite3_prepare_v2(psync_db, sql, -1, &stmt, NULL); - if (unlikely(code != SQLITE_OK)) { - psync_sql_unlock(); -#if IS_DEBUG - pdbg_logf(D_ERROR, "error running sql statement: %s: %s called from %s:%u", sql, - sqlite3_errmsg(psync_db), file, line); - sendpdbg_logf("error running sql statement: %s: %s called from %s:%u", sql, - sqlite3_errmsg(psync_db), file, line); -#else - pdbg_logf(D_ERROR, "error running sql statement: %s: %s", sql, - sqlite3_errmsg(psync_db)); - sendpdbg_logf("error running sql statement: %s: %s", sql, - sqlite3_errmsg(psync_db)); -#endif - return NULL; - } - res = malloc(sizeof(psync_sql_res)); - res->stmt = stmt; - res->sql = sql; -#if IS_DEBUG - res->column_count = 0; -#endif - res->locked = SQL_WRITE_LOCK; - return res; -} - -#if IS_DEBUG -psync_sql_res *psync_sql_do_prep_statement(const char *sql, const char *file, - unsigned line) { -#else -psync_sql_res *psync_sql_prep_statement(const char *sql) { -#endif - psync_sql_res *ret; - ret = pcache_get(sql); - if (ret) { - // pdbg_logf(D_NOTICE, "got statement %s from cache", sql); - ret->locked = SQL_WRITE_LOCK; -#if IS_DEBUG - psync_sql_do_lock(file, line); -#else - psync_sql_lock(); -#endif - return ret; - } else -#if IS_DEBUG - return psync_sql_do_prep_statement_nocache(sql, file, line); -#else - return psync_sql_prep_statement_nocache(sql); -#endif -} - -int psync_sql_reset(psync_sql_res *res) { - int code = sqlite3_reset(res->stmt); - if (unlikely(code != SQLITE_OK)) { - pdbg_logf(D_ERROR, "sqlite3_reset returned error: %s", - sqlite3_errmsg(psync_db)); - return -1; - } else - return 0; -} - -int psync_sql_run(psync_sql_res *res) { - int code = sqlite3_step(res->stmt); - if (unlikely(code != SQLITE_DONE)) { - pdbg_logf(D_ERROR, "sqlite3_step returned error: %s: %s", - sqlite3_errmsg(psync_db), res->sql); - sendtpdbg_logf("sqlite3_step returned error (in_transaction=%d): %s: %s", - in_transaction, sqlite3_errmsg(psync_db), res->sql); - transaction_failed = 1; - if (in_transaction) - pdbg_logf(D_BUG, "transaction query failed, this may lead to restarting " - "transaction over and over"); - return -1; - } - code = sqlite3_reset(res->stmt); - if (unlikely(code != SQLITE_OK)) - pdbg_logf(D_ERROR, "sqlite3_reset returned error: %s", - sqlite3_errmsg(psync_db)); - return 0; -} - -int psync_sql_run_free_nocache(psync_sql_res *res) { - int code = sqlite3_step(res->stmt); - if (unlikely(code != SQLITE_DONE)) { - pdbg_logf(D_ERROR, "sqlite3_step returned error: %s: %s", - sqlite3_errmsg(psync_db), res->sql); - sendtpdbg_logf("sqlite3_step returned error (in_transaction=%d): %s: %s", - in_transaction, sqlite3_errmsg(psync_db), res->sql); - code = -1; - transaction_failed = 1; - if (in_transaction) - pdbg_logf(D_BUG, "transaction query failed, this may lead to restarting " - "transaction over and over"); - } else - code = 0; - sqlite3_finalize(res->stmt); - psync_sql_res_unlock(res); - free(res); - return code; -} - -int psync_sql_run_free(psync_sql_res *res) { - int code = sqlite3_step(res->stmt); - if (unlikely(code != SQLITE_DONE || - (code = sqlite3_reset(res->stmt)) != SQLITE_OK)) { - pdbg_logf(D_ERROR, "sqlite3_step returned error: %s: %s", - sqlite3_errmsg(psync_db), res->sql); - sendtpdbg_logf("sqlite3_step returned error (in_transaction=%d): %s: %s", - in_transaction, sqlite3_errmsg(psync_db), res->sql); - sqlite3_finalize(res->stmt); - transaction_failed = 1; - if (in_transaction) - pdbg_logf(D_BUG, "transaction query failed, this may lead to restarting " - "transaction over and over"); - psync_sql_res_unlock(res); - free(res); - return -1; - } else { - psync_sql_res_unlock(res); - pcache_add(res->sql, res, PSYNC_QUERY_CACHE_SEC, psync_sql_free_cache, - PSYNC_QUERY_MAX_CNT); - return 0; - } -} - -void psync_sql_bind_int(psync_sql_res *res, int n, int64_t val) { - int code = sqlite3_bind_int64(res->stmt, n, val); - if (unlikely(code != SQLITE_OK)) - pdbg_logf(D_ERROR, "error binding value: %s", sqlite3_errmsg(psync_db)); -} - -void psync_sql_bind_uint(psync_sql_res *res, int n, uint64_t val) { - int code = sqlite3_bind_int64(res->stmt, n, val); - if (unlikely(code != SQLITE_OK)) - pdbg_logf(D_ERROR, "error binding value: %s", sqlite3_errmsg(psync_db)); -} - -void psync_sql_bind_double(psync_sql_res *res, int n, double val) { - int code = sqlite3_bind_double(res->stmt, n, val); - if (unlikely(code != SQLITE_OK)) - pdbg_logf(D_ERROR, "error binding value: %s", sqlite3_errmsg(psync_db)); -} - -void psync_sql_bind_string(psync_sql_res *res, int n, const char *str) { - int code = sqlite3_bind_text(res->stmt, n, str, -1, SQLITE_STATIC); - if (unlikely(code != SQLITE_OK)) - pdbg_logf(D_ERROR, "error binding value: %s", sqlite3_errmsg(psync_db)); -} - -void psync_sql_bind_lstring(psync_sql_res *res, int n, const char *str, - size_t len) { - int code = sqlite3_bind_text(res->stmt, n, str, len, SQLITE_STATIC); - if (unlikely(code != SQLITE_OK)) - pdbg_logf(D_ERROR, "error binding value: %s", sqlite3_errmsg(psync_db)); -} - -void psync_sql_bind_blob(psync_sql_res *res, int n, const char *str, - size_t len) { - int code = sqlite3_bind_blob(res->stmt, n, str, len, SQLITE_STATIC); - if (unlikely(code != SQLITE_OK)) - pdbg_logf(D_ERROR, "error binding value: %s", sqlite3_errmsg(psync_db)); -} - -void psync_sql_bind_null(psync_sql_res *res, int n) { - int code = sqlite3_bind_null(res->stmt, n); - if (unlikely(code != SQLITE_OK)) - pdbg_logf(D_ERROR, "error binding value: %s", sqlite3_errmsg(psync_db)); -} - -psync_variant_row psync_sql_fetch_row(psync_sql_res *res) { - int code, i; - code = sqlite3_step(res->stmt); - if (code == SQLITE_ROW) { - for (i = 0; i < res->column_count; i++) { - code = sqlite3_column_type(res->stmt, i); - if (code == SQLITE_INTEGER) { - res->row[i].type = PSYNC_TNUMBER; - res->row[i].snum = sqlite3_column_int64(res->stmt, i); - } else if (code == SQLITE_TEXT || code == SQLITE_BLOB) { - res->row[i].type = PSYNC_TSTRING; - res->row[i].length = sqlite3_column_bytes(res->stmt, i); - res->row[i].str = (char *)sqlite3_column_text(res->stmt, i); - } else if (code == SQLITE_FLOAT) { - res->row[i].type = PSYNC_TREAL; - res->row[i].real = sqlite3_column_double(res->stmt, i); - } else - res->row[i].type = PSYNC_TNULL; - } - return res->row; - } else { - if (unlikely(code != SQLITE_DONE)) - pdbg_logf(D_ERROR, "sqlite3_step returned error: %s", - sqlite3_errmsg(psync_db)); - return NULL; - } -} - -psync_str_row psync_sql_fetch_rowstr(psync_sql_res *res) { - int code, i; - const char **strs; - code = sqlite3_step(res->stmt); - if (code == SQLITE_ROW) { - strs = (const char **)res->row; - for (i = 0; i < res->column_count; i++) - strs[i] = (const char *)sqlite3_column_text(res->stmt, i); - return strs; - } else { - if (unlikely(code != SQLITE_DONE)) - pdbg_logf(D_ERROR, "sqlite3_step returned error: %s", - sqlite3_errmsg(psync_db)); - return NULL; - } -} - -const uint64_t *psync_sql_fetch_rowint(psync_sql_res *res) { - int code, i; - uint64_t *ret; - code = sqlite3_step(res->stmt); - if (code == SQLITE_ROW) { - ret = (uint64_t *)res->row; - for (i = 0; i < res->column_count; i++) - ret[i] = sqlite3_column_int64(res->stmt, i); - return ret; - } else { - if (unlikely(code != SQLITE_DONE)) - pdbg_logf(D_ERROR, "sqlite3_step returned error: %s", - sqlite3_errmsg(psync_db)); - return NULL; - } -} - -psync_full_result_int *psync_sql_fetchall_int(psync_sql_res *res) { - uint64_t *data; - psync_full_result_int *ret; - unsigned long rows, cols, off, i, all; - int code; - cols = res->column_count; - rows = 0; - off = 0; - all = 0; - data = NULL; - while ((code = sqlite3_step(res->stmt)) == SQLITE_ROW) { - if (rows >= all) { - all = 10 + all * 2; - data = (uint64_t *)realloc(data, sizeof(uint64_t) * cols * all); - } - for (i = 0; i < cols; i++) - data[off + i] = sqlite3_column_int64(res->stmt, i); - off += cols; - rows++; - } - if (unlikely(code != SQLITE_DONE)) - pdbg_logf(D_ERROR, "sqlite3_step returned error: %s", sqlite3_errmsg(psync_db)); - psync_sql_free_result(res); - ret = (psync_full_result_int *)malloc( - offsetof(psync_full_result_int, data) + sizeof(uint64_t) * off); - ret->rows = rows; - ret->cols = cols; - memcpy(ret->data, data, sizeof(uint64_t) * off); - free(data); - return ret; -} - -uint32_t psync_sql_affected_rows() { return sqlite3_changes(psync_db); } - -uint64_t psync_sql_insertid() { return sqlite3_last_insert_rowid(psync_db); } int psync_rename_conflicted_file(const char *path) { char *npath; @@ -1812,58 +487,3 @@ void psync_qpartition(void *base, size_t cnt, size_t sort_first, size_t size, return; } } - -void psync_try_free_memory() { - sqlite3_db_release_memory(psync_db); - pcache_clean(); -} - -static const char *PSYNC_CONST get_type_name(uint32_t t) { - if (unlikely(t >= ARRAY_SIZE(psync_typenames))) - t = 0; - return psync_typenames[t]; -} - -uint64_t psync_err_number_expected(const char *file, const char *function, - int unsigned line, const psync_variant *v) { - if (D_CRITICAL <= DEBUG_LEVEL) - pdbg_printf(file, function, line, D_CRITICAL, - "type error, wanted %s got %s", get_type_name(PSYNC_TNUMBER), - get_type_name(v->type)); - return 0; -} - -const char *psync_err_string_expected(const char *file, const char *function, - int unsigned line, - const psync_variant *v) { - if (D_CRITICAL <= DEBUG_LEVEL) - pdbg_printf(file, function, line, D_CRITICAL, - "type error, wanted %s got %s", get_type_name(PSYNC_TSTRING), - get_type_name(v->type)); - return ""; -} - -const char *psync_lstring_expected(const char *file, const char *function, - int unsigned line, const psync_variant *v, - size_t *len) { - if (likely(v->type == PSYNC_TSTRING)) { - *len = v->length; - return v->str; - } else { - if (D_CRITICAL <= DEBUG_LEVEL) - pdbg_printf(file, function, line, D_CRITICAL, - "type error, wanted %s got %s", get_type_name(PSYNC_TSTRING), - get_type_name(v->type)); - *len = 0; - return ""; - } -} - -double psync_err_real_expected(const char *file, const char *function, - int unsigned line, const psync_variant *v) { - if (D_CRITICAL <= DEBUG_LEVEL) - pdbg_printf(file, function, line, D_CRITICAL, - "type error, wanted %s got %s", get_type_name(PSYNC_TREAL), - get_type_name(v->type)); - return 0.0; -} diff --git a/pclsync/plibs.h b/pclsync/plibs.h index 5719eac..bad06eb 100644 --- a/pclsync/plibs.h +++ b/pclsync/plibs.h @@ -42,7 +42,6 @@ extern "C" { #include "pstatus.h" #include "pdbg.h" -#include #include #include @@ -142,39 +141,7 @@ extern "C" { #define psync_get_result_cell(res, row, col) \ (res)->data[(row) * (res)->cols + (col)] -typedef struct { - uint32_t type; - uint32_t length; - union { - uint64_t num; - int64_t snum; - const char *str; - double real; - }; -} psync_variant; - -typedef struct { - sqlite3_stmt *stmt; - const char *sql; - int column_count; - int locked; - psync_variant row[]; -} psync_sql_res; - -typedef struct { - uint32_t rows; - uint32_t cols; - uint64_t data[]; -} psync_full_result_int; - - -typedef const uint64_t *psync_uint_row; -typedef const char *const *psync_str_row; -typedef const psync_variant *psync_variant_row; - typedef void (*psync_run_after_t)(void *); -typedef int (*psync_list_builder_sql_callback)(psync_list_builder_t *, void *, psync_variant_row); -typedef void (*psync_transaction_callback_t)(void *); extern int psync_do_run; extern int psync_recache_contacts; @@ -188,98 +155,6 @@ extern pthread_mutex_t psync_my_auth_mutex; extern PSYNC_THREAD uint32_t psync_error; extern uint16_t const *__hex_lookup; -int psync_sql_connect(const char *db) PSYNC_NONNULL(1); -int psync_sql_close(); -int psync_sql_reopen(const char *path); -void psync_sql_checkpoint_lock(); -void psync_sql_checkpoint_unlock(); - -#if IS_DEBUG - -#define psync_sql_trylock() psync_sql_do_trylock(__FILE__, __LINE__) -#define psync_sql_lock() psync_sql_do_lock(__FILE__, __LINE__) -#define psync_sql_rdlock() psync_sql_do_rdlock(__FILE__, __LINE__) -#define psync_sql_statement(sql) psync_sql_do_statement(sql, __FILE__, __LINE__) -#define psync_sql_start_transaction() psync_sql_do_start_transaction(__FILE__, __LINE__) -#define psync_sql_query_nocache(sql) psync_sql_do_query_nocache(sql, __FILE__, __LINE__) -#define psync_sql_query(sql) psync_sql_do_query(sql, __FILE__, __LINE__) -#define psync_sql_query_rdlock_nocache(sql) psync_sql_do_query_rdlock_nocache(sql, __FILE__, __LINE__) -#define psync_sql_query_rdlock(sql) psync_sql_do_query_rdlock(sql, __FILE__, __LINE__) -#define psync_sql_query_nolock_nocache(sql) psync_sql_do_query_nolock_nocache(sql, __FILE__, __LINE__) -#define psync_sql_query_nolock(sql) psync_sql_do_query_nolock(sql, __FILE__, __LINE__) -#define psync_sql_prep_statement_nocache(sql) psync_sql_do_prep_statement_nocache(sql, __FILE__, __LINE__) -#define psync_sql_prep_statement(sql) psync_sql_do_prep_statement(sql, __FILE__, __LINE__) - -int psync_sql_do_trylock(const char *file, unsigned line); -void psync_sql_do_lock(const char *file, unsigned line); -void psync_sql_do_rdlock(const char *file, unsigned line); -int psync_sql_do_statement(const char *sql, const char *file, unsigned line) PSYNC_NONNULL(1); -int psync_sql_do_start_transaction(const char *file, unsigned line); -psync_sql_res *psync_sql_do_query_nocache(const char *sql, const char *file, unsigned line) PSYNC_NONNULL(1); -psync_sql_res *psync_sql_do_query(const char *sql, const char *file, unsigned line) PSYNC_NONNULL(1); -psync_sql_res *psync_sql_do_query_rdlock_nocache(const char *sql, const char *file, unsigned line) PSYNC_NONNULL(1); -psync_sql_res *psync_sql_do_query_rdlock(const char *sql, const char *file, unsigned line) PSYNC_NONNULL(1); -psync_sql_res *psync_sql_do_query_nolock_nocache(const char *sql, const char *file, unsigned line) PSYNC_NONNULL(1); -psync_sql_res *psync_sql_do_query_nolock(const char *sql, const char *file, unsigned line) PSYNC_NONNULL(1); -psync_sql_res *psync_sql_do_prep_statement_nocache(const char *sql, const char *file, unsigned line) PSYNC_NONNULL(1); -psync_sql_res *psync_sql_do_prep_statement(const char *sql, const char *file, unsigned line) PSYNC_NONNULL(1); -void psync_sql_dump_locks(); - -#else - -int psync_sql_trylock(); -void psync_sql_lock(); -void psync_sql_rdlock(); -int psync_sql_statement(const char *sql) PSYNC_NONNULL(1); -int psync_sql_start_transaction(); - -psync_sql_res *psync_sql_query_nocache(const char *sql) PSYNC_NONNULL(1); -psync_sql_res *psync_sql_query(const char *sql) PSYNC_NONNULL(1); -psync_sql_res *psync_sql_query_rdlock_nocache(const char *sql) PSYNC_NONNULL(1); -psync_sql_res *psync_sql_query_rdlock(const char *sql) PSYNC_NONNULL(1); -psync_sql_res *psync_sql_query_nolock(const char *sql) PSYNC_NONNULL(1); -psync_sql_res *psync_sql_query_nolock_nocache(const char *sql) PSYNC_NONNULL(1); -psync_sql_res *psync_sql_prep_statement(const char *sql) PSYNC_NONNULL(1); -psync_sql_res *psync_sql_prep_statement_nocache(const char *sql) PSYNC_NONNULL(1); - -#endif - -void psync_sql_unlock(); -void psync_sql_rdunlock(); -int psync_sql_has_waiters(); -int psync_sql_isrdlocked(); -int psync_sql_islocked(); -int psync_sql_tryupgradelock(); -int psync_sql_sync(); -int psync_sql_commit_transaction(); -int psync_sql_rollback_transaction(); - -void psync_sql_transation_add_callbacks(psync_transaction_callback_t commit_callback, psync_transaction_callback_t rollback_callback, void *ptr); - -char *psync_sql_cellstr(const char *sql) PSYNC_NONNULL(1); -int64_t psync_sql_cellint(const char *sql, int64_t dflt) PSYNC_NONNULL(1); -char **psync_sql_rowstr(const char *sql) PSYNC_NONNULL(1); -psync_variant *psync_sql_row(const char *sql) PSYNC_NONNULL(1); -int psync_sql_reset(psync_sql_res *res) PSYNC_NONNULL(1); -int psync_sql_run(psync_sql_res *res) PSYNC_NONNULL(1); -int psync_sql_run_free(psync_sql_res *res) PSYNC_NONNULL(1); -int psync_sql_run_free_nocache(psync_sql_res *res) PSYNC_NONNULL(1); -void psync_sql_bind_uint(psync_sql_res *res, int n, uint64_t val) PSYNC_NONNULL(1); -void psync_sql_bind_int(psync_sql_res *res, int n, int64_t val) PSYNC_NONNULL(1); -void psync_sql_bind_double(psync_sql_res *res, int n, double val) PSYNC_NONNULL(1); -void psync_sql_bind_string(psync_sql_res *res, int n, const char *str) PSYNC_NONNULL(1); -void psync_sql_bind_lstring(psync_sql_res *res, int n, const char *str, size_t len) PSYNC_NONNULL(1); -void psync_sql_bind_blob(psync_sql_res *res, int n, const char *str, size_t len) PSYNC_NONNULL(1); -void psync_sql_bind_null(psync_sql_res *res, int n) PSYNC_NONNULL(1); -void psync_sql_free_result(psync_sql_res *res) PSYNC_NONNULL(1); -void psync_sql_free_result_nocache(psync_sql_res *res) PSYNC_NONNULL(1); -psync_variant_row psync_sql_fetch_row(psync_sql_res *res) PSYNC_NONNULL(1); -psync_str_row psync_sql_fetch_rowstr(psync_sql_res *res) PSYNC_NONNULL(1); -psync_uint_row psync_sql_fetch_rowint(psync_sql_res *res) PSYNC_NONNULL(1); -psync_full_result_int *psync_sql_fetchall_int(psync_sql_res *res) PSYNC_NONNULL(1); -uint32_t psync_sql_affected_rows() PSYNC_PURE; -uint64_t psync_sql_insertid() PSYNC_PURE; - int psync_rename_conflicted_file(const char *path); void psync_run_after_sec(psync_run_after_t run, void *ptr, uint32_t seconds); @@ -287,26 +162,8 @@ void psync_free_after_sec(void *ptr, uint32_t seconds); int psync_match_pattern(const char *name, const char *pattern, size_t plen); -psync_list_builder_t *psync_list_builder_create(size_t element_size, - size_t offset); -void psync_list_bulder_add_sql(psync_list_builder_t *builder, - psync_sql_res *res, - psync_list_builder_sql_callback callback); -void *psync_list_bulder_add_element(psync_list_builder_t *builder); -void psync_list_add_string_offset(psync_list_builder_t *builder, size_t offset); -void psync_list_add_lstring_offset(psync_list_builder_t *builder, size_t offset, - size_t length); -void *psync_list_builder_finalize(psync_list_builder_t *builder); - - void psync_pqsort(void *base, size_t cnt, size_t sort_first, size_t size, int (*compar)(const void *, const void *)); void psync_qpartition(void *base, size_t cnt, size_t sort_first, size_t size, int (*compar)(const void *, const void *)); -void psync_try_free_memory(); - -uint64_t psync_err_number_expected(const char *file, const char *function, int unsigned line, const psync_variant *v) PSYNC_COLD; -const char *psync_err_string_expected(const char *file, const char *function, int unsigned line, const psync_variant *v) PSYNC_COLD; -const char *psync_lstring_expected(const char *file, const char *function, int unsigned line, const psync_variant *v, size_t *len) PSYNC_NONNULL(4, 5); -double psync_err_real_expected(const char *file, const char *function, int unsigned line, const psync_variant *v) PSYNC_COLD; /* needs 12 characters of buffer space on top of the length of the prefix */ static inline void psync_get_string_id(char *dst, const char *prefix, uint64_t id) { diff --git a/pclsync/plist.h b/pclsync/plist.h index 8189983..d85308b 100644 --- a/pclsync/plist.h +++ b/pclsync/plist.h @@ -82,8 +82,6 @@ struct psync_list_builder_t_ { struct psync_list_builder_t_; typedef struct psync_list_builder_t_ psync_list_builder_t; - - typedef int (*psync_list_compare)(const psync_list *, const psync_list *); #define psync_list_init(l) \ @@ -150,5 +148,10 @@ void psync_list_sort(psync_list *l, psync_list_compare cmp); void psync_list_extract_repeating(psync_list *l1, psync_list *l2, psync_list *extracted1, psync_list *extracted2, psync_list_compare cmp); uint32_t *psync_list_bulder_push_num(psync_list_builder_t *builder); uint32_t psync_list_bulder_pop_num(psync_list_builder_t *builder); +psync_list_builder_t *psync_list_builder_create(size_t element_size, size_t offset); +void *psync_list_bulder_add_element(psync_list_builder_t *builder); +void psync_list_add_string_offset(psync_list_builder_t *builder, size_t offset); +void psync_list_add_lstring_offset(psync_list_builder_t *builder, size_t offset, size_t length); +void *psync_list_builder_finalize(psync_list_builder_t *builder); #endif diff --git a/pclsync/plocalnotify.c b/pclsync/plocalnotify.c index de72a24..ff723ee 100644 --- a/pclsync/plocalnotify.c +++ b/pclsync/plocalnotify.c @@ -45,6 +45,7 @@ #include "prun.h" #include "plist.h" #include "plocalscan.h" +#include "psql.h" #define NOTIFY_MSG_ADD 0 #define NOTIFY_MSG_DEL 1 diff --git a/pclsync/plocalscan.c b/pclsync/plocalscan.c index a74044e..8ef9aa8 100644 --- a/pclsync/plocalscan.c +++ b/pclsync/plocalscan.c @@ -29,13 +29,9 @@ DAMAGE. */ -#include -#include -#include -#include -#include -#include #include +#include +#include #include "pdevice.h" #include "pfile.h" @@ -56,9 +52,8 @@ #include "pupload.h" #include "ppath.h" #include "putil.h" +#include "psql.h" -#include -#include typedef struct { psync_list list; diff --git a/pclsync/pmem.c b/pclsync/pmem.c index c62da6c..bf3ad63 100644 --- a/pclsync/pmem.c +++ b/pclsync/pmem.c @@ -1,5 +1,7 @@ #include "pcompiler.h" -#include "plibs.h" +#include "psql.h" +#include "pdbg.h" + #include void *pmem_mmap(size_t size) { diff --git a/pclsync/pnetlibs.c b/pclsync/pnetlibs.c index ba14238..ef15ea9 100644 --- a/pclsync/pnetlibs.c +++ b/pclsync/pnetlibs.c @@ -49,6 +49,7 @@ #include "ppath.h" #include "psys.h" #include "ptree.h" +#include "psql.h" #include "pfile.h" // required by psync_send_debug diff --git a/pclsync/pp2p.c b/pclsync/pp2p.c index d77c55d..3e2cb58 100644 --- a/pclsync/pp2p.c +++ b/pclsync/pp2p.c @@ -31,6 +31,7 @@ #include #include +#include #include "papi.h" #include "pfile.h" @@ -47,7 +48,7 @@ #include "psys.h" #include "ptimer.h" #include "putil.h" -#include +#include "psql.h" #define P2P_ENCTYPE_RSA_AES 0 diff --git a/pclsync/ppagecache.c b/pclsync/ppagecache.c index 7ee03f0..732242b 100644 --- a/pclsync/ppagecache.c +++ b/pclsync/ppagecache.c @@ -29,12 +29,10 @@ DAMAGE. */ -#include -#include -#include -#include -#include #include +#include +#include +#include #include "pcache.h" #include "pcrc32c.h" @@ -50,12 +48,9 @@ #include "psys.h" #include "putil.h" #include "pfile.h" - #include "pstatus.h" #include "ptimer.h" -#include -#include -#include +#include "psql.h" #define CACHE_PAGES (PSYNC_FS_MEMORY_CACHE / PSYNC_FS_PAGE_SIZE) #define CACHE_HASH (CACHE_PAGES / 2) diff --git a/pclsync/ppath.c b/pclsync/ppath.c index 5c77a88..e75b978 100644 --- a/pclsync/ppath.c +++ b/pclsync/ppath.c @@ -8,6 +8,7 @@ #include "plibs.h" #include "ppath.h" #include "psettings.h" +#include "psql.h" char *ppath_default_db() { char *pcdir, *dbp, *home, *oldp; diff --git a/pclsync/ppathstatus.c b/pclsync/ppathstatus.c index 84c1dab..6eb07ae 100644 --- a/pclsync/ppathstatus.c +++ b/pclsync/ppathstatus.c @@ -29,23 +29,19 @@ DAMAGE. */ -#include -#include -#include -#include -#include #include +#include #include "pcryptofolder.h" #include "pfoldersync.h" -#include "pfs.h" #include "plibs.h" #include "plist.h" #include "ppathstatus.h" #include "pstatus.h" #include "ptask.h" #include "ptree.h" -#include +#include "psql.h" +#include "pfstasks.h" #define PATH_CACHE_SIZE 512 #define PATH_HASH_SIZE 512 diff --git a/pclsync/prand.c b/pclsync/prand.c index d65966a..8be6a32 100644 --- a/pclsync/prand.c +++ b/pclsync/prand.c @@ -8,6 +8,7 @@ #include "ppath.h" #include "prand.h" #include "psettings.h" +#include "psql.h" static void add_file(const char *fn, psync_lhash_ctx *hctx, size_t max) { char buff[4096]; diff --git a/pclsync/psettings.c b/pclsync/psettings.c index e0a39f5..11b08ac 100644 --- a/pclsync/psettings.c +++ b/pclsync/psettings.c @@ -29,12 +29,9 @@ DAMAGE. */ -#include -#include -#include -#include -#include #include +#include +#include #include "pfs.h" #include "plibs.h" @@ -44,8 +41,7 @@ #include "psettings.h" #include "ppath.h" #include "ptimer.h" -#include -#include +#include "psql.h" typedef void (*setting_callback)(); typedef void (*filter_callback)(void *); diff --git a/pclsync/psock.c b/pclsync/psock.c index f390c39..ad5b614 100644 --- a/pclsync/psock.c +++ b/pclsync/psock.c @@ -39,6 +39,7 @@ #include "psettings.h" #include "psock.h" #include "ptimer.h" +#include "psql.h" #include "ptask.h" #define PROXY_NONE 0 diff --git a/pclsync/psql.c b/pclsync/psql.c new file mode 100644 index 0000000..909977d --- /dev/null +++ b/pclsync/psql.c @@ -0,0 +1,1395 @@ +#include "pdatabase.h" +#include "pdbg.h" +#include "plocks.h" +#include "prun.h" +#include "psettings.h" +#include "pstatus.h" +#include "psql.h" +#include "pcache.h" +#include "psys.h" +#include "pnetlibs.h" +#include +#include + +#define SQL_NO_LOCK 0 +#define SQL_READ_LOCK 1 +#define SQL_WRITE_LOCK 2 + +#define PSYNC_TNUMBER 1 +#define PSYNC_TSTRING 2 +#define PSYNC_TREAL 3 +#define PSYNC_TNULL 4 +#define PSYNC_TBOOL 5 + +extern PSYNC_THREAD const char *psync_thread_name; + +const static char *psync_typenames[] = { + "[invalid type]", "[number]", "[string]", "[float]", "[null]", "[bool]"}; + +typedef struct { + psync_list list; + psync_transaction_callback_t commit_callback; + psync_transaction_callback_t rollback_callback; + void *ptr; +} tran_callback_t; + +psync_rwlock_t psync_db_lock; +sqlite3 *psync_db; +pstatus_t psync_status; +int psync_do_run = 1; +int psync_recache_contacts = 1; +PSYNC_THREAD uint32_t psync_error = 0; + +static pthread_mutex_t psync_db_checkpoint_mutex; + +static int in_transaction = 0; +static int transaction_failed = 0; +static psync_list tran_callbacks; + +void psync_sql_err_callback(void *ptr, int code, const char *msg) { + pdbg_logf(D_WARNING, "database warning %d: %s", code, msg); +} + +static void psync_sql_wal_checkpoint() { + int code; + psync_sql_lock(); + psync_sql_unlock(); + if (pthread_mutex_trylock(&psync_db_checkpoint_mutex)) { + pdbg_logf(D_NOTICE, "checkpoint already in progress"); + return; + } + pdbg_logf(D_NOTICE, "checkpointing database"); + code = sqlite3_wal_checkpoint(psync_db, NULL); + while (code == SQLITE_LOCKED) { + psys_sleep_milliseconds(2); + code = sqlite3_wal_checkpoint(psync_db, NULL); + } + pthread_mutex_unlock(&psync_db_checkpoint_mutex); + if (unlikely(code != SQLITE_OK)) + pdbg_logf(D_CRITICAL, "sqlite3_wal_checkpoint returned error %d", code); + else + pdbg_logf(D_NOTICE, "checkpoint finished"); +} + +static int psync_sql_wal_hook(void *ptr, sqlite3 *db, const char *name, + int numpages) { + if (numpages >= PSYNC_DB_CHECKPOINT_AT_PAGES) + prun_thread("checkpoint charlie", psync_sql_wal_checkpoint); + return SQLITE_OK; +} + +int psync_sql_connect(const char *db) { + static int initmutex = 1; + pthread_mutexattr_t mattr; + struct stat st; + uint64_t dbver; + int initdbneeded = 0; + int code; + + pdbg_assert(sqlite3_libversion_number() == SQLITE_VERSION_NUMBER); + pdbg_assert(!strcmp(sqlite3_sourceid(), SQLITE_SOURCE_ID)); + pdbg_assert(!strcmp(sqlite3_libversion(), SQLITE_VERSION)); + pdbg_logf(D_NOTICE, "Using sqlite version %s source %s", sqlite3_libversion(), + sqlite3_sourceid()); + if (!sqlite3_threadsafe()) { + pdbg_logf(D_CRITICAL, "sqlite is compiled without thread support"); + return -1; + } + if (stat(db, &st) != 0) + initdbneeded = 1; + + code = sqlite3_open(db, &psync_db); + if (likely(code == SQLITE_OK)) { + if (initmutex) { + plocks_init(&psync_db_lock); + pthread_mutexattr_init(&mattr); + pthread_mutexattr_settype(&mattr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&psync_db_checkpoint_mutex, &mattr); + pthread_mutexattr_destroy(&mattr); + initmutex = 0; + } + if (IS_DEBUG) + sqlite3_config(SQLITE_CONFIG_LOG, psync_sql_err_callback, NULL); + sqlite3_wal_hook(psync_db, psync_sql_wal_hook, NULL); + psync_sql_statement(PSYNC_DATABASE_CONFIG); + if (initdbneeded == 1) + return psync_sql_statement(PSYNC_DATABASE_STRUCTURE); + else if (psync_sql_statement( + "DELETE FROM setting WHERE id='justcheckingiflocked'")) { + pdbg_logf(D_ERROR, "database is locked"); + sqlite3_close(psync_db); + plocks_destroy(&psync_db_lock); + return -1; + } + + dbver = + psync_sql_cellint("SELECT value FROM setting WHERE id='dbversion'", 0); + if (dbver < PSYNC_DATABASE_VERSION) { + uint64_t i; + pdbg_logf(D_NOTICE, "database version %d detected, upgrading to %d", + (int)dbver, (int)PSYNC_DATABASE_VERSION); + for (i = dbver; i < PSYNC_DATABASE_VERSION; i++) + if (psync_sql_statement(psync_db_upgrade[i])) { + pdbg_logf(D_ERROR, "error running statement %s on sqlite %s", + psync_db_upgrade[i], sqlite3_libversion()); + if (IS_DEBUG) { + psync_error = PERROR_DATABASE_OPEN; + return -1; + } + } + } + + return 0; + } else { + pdbg_logf(D_CRITICAL, "could not open sqlite database %s: %d", db, code); + return -1; + } +} + +int psync_sql_close() { + int code, tries; + tries = 0; + while (1) { + code = sqlite3_close(psync_db); + if (code == SQLITE_BUSY) { + pcache_clean(); + tries++; + if (tries > 100) { + psys_sleep_milliseconds_fast(tries - 90); + if (tries > 200) { + pdbg_logf(D_ERROR, "failed to close database"); + break; + } + } + } else + break; + } + psync_db = NULL; + if (unlikely(code != SQLITE_OK)) { + pdbg_logf(D_CRITICAL, "error when closing database: %d", code); + code = sqlite3_close_v2(psync_db); + if (unlikely(code != SQLITE_OK)) { + pdbg_logf(D_CRITICAL, + "error when closing database even with sqlite3_close_v2: %d", code); + return -1; + } + } + return 0; +} + +int psync_sql_reopen(const char *path) { + sqlite3 *db; + int code; + pdbg_logf(D_NOTICE, "reopening database %s", path); + code = sqlite3_open(path, &db); + if (likely(code == SQLITE_OK)) { + code = sqlite3_wal_checkpoint(db, NULL); + if (unlikely(code != SQLITE_OK)) { + pdbg_logf(D_CRITICAL, "sqlite3_wal_checkpoint returned error %d", code); + sqlite3_close(db); + return -1; + } + code = sqlite3_close(db); + if (unlikely(code != SQLITE_OK)) { + pdbg_logf(D_CRITICAL, "sqlite3_close returned error %d", code); + return -1; + } + return 0; + } else { + pdbg_logf(D_CRITICAL, "could not open sqlite dabase %s: %d", path, code); + return -1; + } +} + +void psync_sql_checkpoint_lock() { + pthread_mutex_lock(&psync_db_checkpoint_mutex); +} + +void psync_sql_checkpoint_unlock() { + pthread_mutex_unlock(&psync_db_checkpoint_mutex); +} + +#if IS_DEBUG +typedef struct { + psync_list list; + const char *file; + const char *thread; + struct timespec tm; + unsigned line; +} rd_lock_data; + +static PSYNC_THREAD rd_lock_data *rdlock = NULL; +static PSYNC_THREAD unsigned long sqlrdlockcnt = 0; +static PSYNC_THREAD struct timespec sqlrdlockstart; +unsigned long sqllockcnt = 0; +static struct timespec sqllockstart; +static const char *wrlockfile = "none"; +static const char *wrlockthread = ""; +static unsigned wrlockline = 0; +static unsigned wrlocked = 0; +static pthread_t wrlocker; +static psync_list rdlocks = PSYNC_LIST_STATIC_INIT(rdlocks); +static pthread_mutex_t rdmutex = PTHREAD_MUTEX_INITIALIZER; + +static void record_wrlock(const char *file, unsigned line) { + if (unlikely(rdlock)) { + pdbg_logf(D_BUG, + "trying to get write lock at %s:%u, but read lock is already taken " + "at %s:%u, aborting", + file, line, rdlock->file, rdlock->line); + sendpdbg_logf("trying to get write lock at %s:%u, but read lock is already " + "taken at %s:%u, aborting", + file, line, rdlock->file, rdlock->line); + abort(); + } + sendpdbg_assert(!wrlocked); + pdbg_assert(!wrlocked); + wrlockfile = file; + wrlockline = line; + wrlockthread = psync_thread_name; + wrlocked = 1; + wrlocker = pthread_self(); +} + +static void record_wrunlock() { + sendpdbg_assert(pthread_equal(pthread_self(), wrlocker)); + sendpdbg_assert(wrlocked); + pdbg_assert(pthread_equal(pthread_self(), wrlocker)); + pdbg_assert(wrlocked); + wrlocked = 0; +} + +static void record_rdlock(const char *file, unsigned line, + struct timespec *tm) { + rd_lock_data *lock; + lock = malloc(sizeof(rd_lock_data)); + lock->file = file; + lock->thread = psync_thread_name; + lock->line = line; + memcpy(&lock->tm, tm, sizeof(struct timespec)); + pthread_mutex_lock(&rdmutex); + psync_list_add_tail(&rdlocks, &lock->list); + pthread_mutex_unlock(&rdmutex); + rdlock = lock; +} + +static rd_lock_data *record_rdunlock() { + rd_lock_data *lock; + pdbg_assert(rdlock); + lock = rdlock; + rdlock = NULL; + pthread_mutex_lock(&rdmutex); + psync_list_del(&lock->list); + pthread_mutex_unlock(&rdmutex); + return lock; +} + +void psync_sql_dump_locks() { + rd_lock_data *lock; + char dttime[36]; + if (wrlocked) { + time_format(sqllockstart.tv_sec, sqllockstart.tv_nsec, dttime); + pdbg_logf(D_ERROR, "write lock taken by thread %s from %s:%u at %s", + wrlockthread, wrlockfile, wrlockline, dttime); + sendpdbg_logf("write lock taken by thread %s from %s:%u at %s", wrlockthread, + wrlockfile, wrlockline, dttime); + } + pthread_mutex_lock(&rdmutex); + psync_list_for_each_element(lock, &rdlocks, rd_lock_data, list) { + time_format(lock->tm.tv_sec, lock->tm.tv_nsec, dttime); + pdbg_logf(D_ERROR, "read lock taken by thread %s from %s:%u at %s", + lock->thread, lock->file, lock->line, dttime); + sendpdbg_logf("read lock taken by thread %s from %s:%u at %s", lock->thread, + lock->file, lock->line, dttime); + } + pthread_mutex_unlock(&rdmutex); +} + +#endif + + +#if IS_DEBUG +int psync_sql_do_trylock(const char *file, unsigned line) { + if (plocks_trywrlock(&psync_db_lock)) + return -1; + if (++sqllockcnt == 1) { + clock_gettime(CLOCK_REALTIME, &sqllockstart); + record_wrlock(file, line); + } + return 0; +} +#else +int psync_sql_trylock() { return plocks_trywrlock(&psync_db_lock); } +#endif + +#if IS_DEBUG +void psync_sql_do_lock(const char *file, unsigned line) { + if (plocks_trywrlock(&psync_db_lock)) { + struct timespec start, end; + unsigned long msec; + clock_gettime(CLOCK_REALTIME, &start); + memcpy(&end, &start, sizeof(end)); + end.tv_sec += PSYNC_DEBUG_LOCK_TIMEOUT; + if (plocks_timedwrlock(&psync_db_lock, &end)) { + pdbg_logf(D_BUG, "sql write lock timed out called from %s:%u", file, line); + sendpdbg_logf("sql write lock timed out called from %s:%u", file, line); + psync_sql_dump_locks(); + abort(); + } + clock_gettime(CLOCK_REALTIME, &end); + msec = (end.tv_sec - start.tv_sec) * 1000 + end.tv_nsec / 1000000 - + start.tv_nsec / 1000000; + if (msec >= 5) + pdbg_logf(D_WARNING, "waited %lu milliseconds for database write lock", msec); + pdbg_assert(sqllockcnt == 0); + sqllockcnt++; + memcpy(&sqllockstart, &end, sizeof(struct timespec)); + record_wrlock(file, line); + } else if (++sqllockcnt == 1) { + clock_gettime(CLOCK_REALTIME, &sqllockstart); + record_wrlock(file, line); + } +} +#else +void psync_sql_lock() { plocks_wrlock(&psync_db_lock); } +#endif + + +void psync_sql_unlock() { +#if IS_DEBUG + pdbg_assert(sqllockcnt > 0); + if (--sqllockcnt == 0) { + struct timespec end; + unsigned long msec; + clock_gettime(CLOCK_REALTIME, &end); + msec = (end.tv_sec - sqllockstart.tv_sec) * 1000 + end.tv_nsec / 1000000 - + sqllockstart.tv_nsec / 1000000; + if (msec >= 10) + pdbg_logf(D_WARNING, + "held database write lock for %lu milliseconds taken from %s:%u", + msec, wrlockfile, wrlockline); + record_wrunlock(); + plocks_unlock(&psync_db_lock); + } else + plocks_unlock(&psync_db_lock); +#else + plocks_unlock(&psync_db_lock); +#endif +} + +#if IS_DEBUG +void psync_sql_do_rdlock(const char *file, unsigned line) { + if (plocks_tryrdlock(&psync_db_lock)) { + struct timespec start, end; + unsigned long msec; + clock_gettime(CLOCK_REALTIME, &start); + memcpy(&end, &start, sizeof(end)); + end.tv_sec += PSYNC_DEBUG_LOCK_TIMEOUT; + if (plocks_timedrdlock(&psync_db_lock, &end)) { + pdbg_logf(D_BUG, "sql read lock timed out, called from %s:%u", file, line); + sendpdbg_logf("sql read lock timed out, called from %s:%u", file, line); + psync_sql_dump_locks(); + abort(); + } + clock_gettime(CLOCK_REALTIME, &end); + msec = (end.tv_sec - start.tv_sec) * 1000 + end.tv_nsec / 1000000 - + start.tv_nsec / 1000000; + if (msec >= 5) + pdbg_logf(D_WARNING, "waited %lu milliseconds for database read lock", msec); + sqlrdlockcnt++; + memcpy(&sqlrdlockstart, &end, sizeof(struct timespec)); + record_rdlock(file, line, &sqlrdlockstart); + } else if (++sqlrdlockcnt == 1) { + clock_gettime(CLOCK_REALTIME, &sqlrdlockstart); + record_rdlock(file, line, &sqlrdlockstart); + } +} +#else +void psync_sql_rdlock() { plocks_rdlock(&psync_db_lock); } +#endif + + +void psync_list_bulder_add_sql(psync_list_builder_t *builder, + psync_sql_res *res, + psync_list_builder_sql_callback callback) { + psync_variant_row row; + while ((row = psync_sql_fetch_row(res))) { + if (!builder->last_elements || + builder->last_elements->used >= builder->elements_per_list) { + builder->last_elements = (psync_list_element_list *)malloc( + offsetof(psync_list_element_list, elements) + + builder->element_size * builder->elements_per_list); + psync_list_add_tail(&builder->element_list, + &builder->last_elements->list); + builder->last_elements->used = 0; + } + builder->current_element = + builder->last_elements->elements + + builder->last_elements->used * builder->element_size; + builder->cstrcnt = psync_list_bulder_push_num(builder); + *builder->cstrcnt = 0; + while (callback(builder, builder->current_element, row)) { + row = psync_sql_fetch_row(res); + if (!row) + break; + *builder->cstrcnt = 0; + } + builder->last_elements->used++; + builder->cnt++; + } + psync_sql_free_result(res); +} + +void psync_sql_rdunlock() { +#if IS_DEBUG + if (unlikely(sqlrdlockcnt == 0)) { + psync_sql_unlock(); + return; + } + if (--sqlrdlockcnt == 0) { + struct timespec end; + unsigned long msec; + rd_lock_data *lock; + plocks_unlock(&psync_db_lock); + clock_gettime(CLOCK_REALTIME, &end); + lock = record_rdunlock(); + msec = (end.tv_sec - sqlrdlockstart.tv_sec) * 1000 + end.tv_nsec / 1000000 - + sqlrdlockstart.tv_nsec / 1000000; + if (msec >= 20) + pdbg_logf(D_WARNING, + "held database read lock for %lu milliseconds taken at %s:%u", msec, + lock->file, lock->line); + free(lock); + } else + plocks_unlock(&psync_db_lock); +#else + plocks_unlock(&psync_db_lock); +#endif +} + +int psync_sql_has_waiters() { + return plocks_num_waiters(&psync_db_lock) > 0; +} + +int psync_sql_isrdlocked() { + return plocks_holding_rdlock(&psync_db_lock); +} + +int psync_sql_islocked() { return plocks_holding_lock(&psync_db_lock); } + +int psync_sql_tryupgradelock() { +#if IS_DEBUG + if (plocks_holding_wrlock(&psync_db_lock)) + return 0; + pdbg_assert(plocks_holding_rdlock(&psync_db_lock)); + if (plocks_towrlock(&psync_db_lock)) + return -1; + else { + rd_lock_data *lock = record_rdunlock(); + sqllockcnt = sqlrdlockcnt; + sqlrdlockcnt = 0; + pdbg_assert(sqllockcnt == 1); + sqllockstart = sqlrdlockstart; + record_wrlock(lock->file, lock->line); + free(lock); + return 0; + } +#else + return plocks_towrlock(&psync_db_lock); +#endif +} + +int psync_sql_sync() { + int code; + pthread_mutex_lock(&psync_db_checkpoint_mutex); + code = sqlite3_wal_checkpoint(psync_db, NULL); + if (unlikely(code == SQLITE_BUSY || code == SQLITE_LOCKED)) { + psync_sql_lock(); + code = sqlite3_wal_checkpoint(psync_db, NULL); + psync_sql_unlock(); + } + pthread_mutex_unlock(&psync_db_checkpoint_mutex); + if (unlikely(code != SQLITE_OK)) { + pdbg_logf(D_CRITICAL, "sqlite3_wal_checkpoint returned error %d", code); + return -1; + } else + return 0; +} + +#if IS_DEBUG +int psync_sql_do_statement(const char *sql, const char *file, unsigned line) { + char *errmsg; + int code; + psync_sql_do_lock(file, line); +#else +int psync_sql_statement(const char *sql) { + char *errmsg; + int code; + psync_sql_lock(); +#endif + code = sqlite3_exec(psync_db, sql, NULL, NULL, &errmsg); + psync_sql_unlock(); + if (likely(code == SQLITE_OK)) + return 0; + else { +#if IS_DEBUG + pdbg_logf(D_ERROR, "error running sql statement: %s: %s called from %s:%u", sql, + errmsg, file, line); +#else + pdbg_logf(D_ERROR, "error running sql statement: %s: %s", sql, errmsg); +#endif + sqlite3_free(errmsg); + return -1; + } +} + +#if IS_DEBUG +int psync_sql_do_start_transaction(const char *file, unsigned line) { + psync_sql_res *res; + psync_sql_do_lock(file, line); + res = psync_sql_do_prep_statement("BEGIN", file, line); +#else +int psync_sql_start_transaction() { + psync_sql_res *res; + psync_sql_lock(); + res = psync_sql_prep_statement("BEGIN"); +#endif + pdbg_assert(!in_transaction); + if (unlikely(!res || psync_sql_run_free(res))) + return -1; + in_transaction = 1; + transaction_failed = 0; + psync_list_init(&tran_callbacks); + return 0; +} + +static void run_commit_callbacks(int success) { + tran_callback_t *cb; + psync_list *l1, *l2; + psync_list_for_each_safe(l1, l2, &tran_callbacks) { + cb = psync_list_element(l1, tran_callback_t, list); + if (success) + cb->commit_callback(cb->ptr); + else + cb->rollback_callback(cb->ptr); + free(cb); + } +} + +int psync_sql_commit_transaction() { + pdbg_assert(in_transaction); + if (likely(!transaction_failed)) { + psync_sql_res *res = psync_sql_prep_statement("COMMIT"); + if (likely(!psync_sql_run_free(res))) { + run_commit_callbacks(1); + in_transaction = 0; + psync_sql_unlock(); + return 0; + } + } else + pdbg_logf(D_ERROR, "rolling back transaction as some statements failed"); + psync_sql_rollback_transaction(); + return -1; +} + +int psync_sql_rollback_transaction() { + psync_sql_res *res = psync_sql_prep_statement("ROLLBACK"); + pdbg_assert(in_transaction); + psync_sql_run_free(res); + run_commit_callbacks(0); + in_transaction = 0; + psync_sql_unlock(); + return 0; +} + +void psync_sql_transation_add_callbacks( + psync_transaction_callback_t commit_callback, + psync_transaction_callback_t rollback_callback, void *ptr) { + tran_callback_t *cb; + pdbg_assert(in_transaction); + cb = malloc(sizeof(tran_callback_t)); + cb->commit_callback = commit_callback; + cb->rollback_callback = rollback_callback; + cb->ptr = ptr; + psync_list_add_tail(&tran_callbacks, &cb->list); +} + +char *psync_sql_cellstr(const char *sql) { + sqlite3_stmt *stmt; + int code; + psync_sql_rdlock(); + code = sqlite3_prepare_v2(psync_db, sql, -1, &stmt, NULL); + if (unlikely(code != SQLITE_OK)) { + psync_sql_rdunlock(); + pdbg_logf(D_ERROR, "error running sql statement: %s: %s", sql, + sqlite3_errmsg(psync_db)); + sendtpdbg_logf("error running sql statement: %s: %s", sql, + sqlite3_errmsg(psync_db)); + return NULL; + } + code = sqlite3_step(stmt); + if (code == SQLITE_ROW) { + char *ret; + ret = (char *)sqlite3_column_text(stmt, 0); + if (ret) + ret = psync_strdup(ret); + sqlite3_finalize(stmt); + psync_sql_rdunlock(); + return ret; + } else { + sqlite3_finalize(stmt); + psync_sql_rdunlock(); + if (unlikely(code != SQLITE_DONE)) { + pdbg_logf(D_ERROR, "sqlite3_step returned error: %s: %s", sql, + sqlite3_errmsg(psync_db)); + sendtpdbg_logf("sqlite3_step returned error: %s: %s", sql, + sqlite3_errmsg(psync_db)); + } + return NULL; + } +} + +int64_t psync_sql_cellint(const char *sql, int64_t dflt) { + sqlite3_stmt *stmt; + int code; + psync_sql_rdlock(); + code = sqlite3_prepare_v2(psync_db, sql, -1, &stmt, NULL); + if (unlikely(code != SQLITE_OK)) { + pdbg_logf(D_ERROR, "error running sql statement: %s: %s", sql, + sqlite3_errmsg(psync_db)); + sendtpdbg_logf("error running sql statement: %s: %s", sql, + sqlite3_errmsg(psync_db)); + } else { + code = sqlite3_step(stmt); + if (code == SQLITE_ROW) + dflt = sqlite3_column_int64(stmt, 0); + else if (unlikely(code != SQLITE_DONE)) { + pdbg_logf(D_ERROR, "sqlite3_step returned error: %s: %s", sql, + sqlite3_errmsg(psync_db)); + sendtpdbg_logf("sqlite3_step returned error: %s: %s", sql, + sqlite3_errmsg(psync_db)); + } + sqlite3_finalize(stmt); + } + psync_sql_rdunlock(); + return dflt; +} + +char **psync_sql_rowstr(const char *sql) { + sqlite3_stmt *stmt; + int code, cnt; + psync_sql_rdlock(); + code = sqlite3_prepare_v2(psync_db, sql, -1, &stmt, NULL); + if (unlikely(code != SQLITE_OK)) { + psync_sql_rdunlock(); + pdbg_logf(D_ERROR, "error running sql statement: %s: %s", sql, + sqlite3_errmsg(psync_db)); + sendtpdbg_logf("error running sql statement: %s: %s", sql, + sqlite3_errmsg(psync_db)); + return NULL; + } + cnt = sqlite3_column_count(stmt); + code = sqlite3_step(stmt); + if (code == SQLITE_ROW) { + char **arr, *nstr, *str; + size_t l, ln; + VAR_ARRAY(lens, size_t, cnt); + int i; + ln = 0; + for (i = 0; i < cnt; i++) { + l = sqlite3_column_bytes(stmt, i); + ln += l; + lens[i] = l; + } + ln += (sizeof(char *) + 1) * cnt; + arr = (char **)malloc(ln); + nstr = ((char *)arr) + sizeof(char *) * cnt; + for (i = 0; i < cnt; i++) { + str = (char *)sqlite3_column_blob(stmt, i); + if (str) { + ln = lens[i]; + memcpy(nstr, str, ln); + nstr[ln] = 0; + arr[i] = nstr; + nstr += ln + 1; + } else + arr[i] = NULL; + } + sqlite3_finalize(stmt); + psync_sql_rdunlock(); + return arr; + } else { + sqlite3_finalize(stmt); + psync_sql_rdunlock(); + if (unlikely(code != SQLITE_DONE)) { + pdbg_logf(D_ERROR, "sqlite3_step returned error: %s: %s", sql, + sqlite3_errmsg(psync_db)); + sendtpdbg_logf("sqlite3_step returned error: %s: %s", sql, + sqlite3_errmsg(psync_db)); + } + return NULL; + } +} + +psync_variant *psync_sql_row(const char *sql) { + sqlite3_stmt *stmt; + int code, cnt; + psync_sql_rdlock(); + code = sqlite3_prepare_v2(psync_db, sql, -1, &stmt, NULL); + if (unlikely(code != SQLITE_OK)) { + psync_sql_rdunlock(); + pdbg_logf(D_ERROR, "error running sql statement: %s: %s", sql, + sqlite3_errmsg(psync_db)); + sendtpdbg_logf("error running sql statement: %s: %s", sql, + sqlite3_errmsg(psync_db)); + return NULL; + } + cnt = sqlite3_column_count(stmt); + code = sqlite3_step(stmt); + if (code == SQLITE_ROW) { + psync_variant *arr; + char *nstr, *str; + size_t l, ln; + VAR_ARRAY(lens, size_t, cnt); + int i, t; + VAR_ARRAY(types, int, cnt); + ln = sizeof(psync_variant) * cnt; + for (i = 0; i < cnt; i++) { + t = sqlite3_column_type(stmt, i); + types[i] = t; + if (t == SQLITE_TEXT || t == SQLITE_BLOB) { + l = sqlite3_column_bytes(stmt, i); + ln += l + 1; + lens[i] = l; + } + } + arr = (psync_variant *)malloc(ln); + nstr = ((char *)arr) + sizeof(psync_variant) * cnt; + for (i = 0; i < cnt; i++) { + t = types[i]; + if (t == SQLITE_INTEGER) { + arr[i].type = PSYNC_TNUMBER; + arr[i].snum = sqlite3_column_int64(stmt, i); + } else if (t == SQLITE_TEXT || t == SQLITE_BLOB) { + str = (char *)sqlite3_column_blob(stmt, i); + ln = lens[i]; + memcpy(nstr, str, ln); + nstr[ln] = 0; + arr[i].type = PSYNC_TSTRING; + arr[i].str = nstr; + nstr += ln + 1; + } else if (t == SQLITE_FLOAT) { + arr[i].type = PSYNC_TREAL; + arr[i].real = sqlite3_column_double(stmt, i); + } else { + arr[i].type = PSYNC_TNULL; + } + } + sqlite3_finalize(stmt); + psync_sql_rdunlock(); + return arr; + } else { + sqlite3_finalize(stmt); + psync_sql_rdunlock(); + if (unlikely(code != SQLITE_DONE)) { + pdbg_logf(D_ERROR, "sqlite3_step returned error: %s: %s", sql, + sqlite3_errmsg(psync_db)); + sendtpdbg_logf("sqlite3_step returned error: %s: %s", sql, + sqlite3_errmsg(psync_db)); + } + return NULL; + } +} + +#if IS_DEBUG +psync_sql_res *psync_sql_do_query_nocache(const char *sql, const char *file, + unsigned line) { +#else +psync_sql_res *psync_sql_query_nocache(const char *sql) { +#endif + sqlite3_stmt *stmt; + psync_sql_res *res; + int code, cnt; +#if IS_DEBUG + psync_sql_do_lock(file, line); +#else + psync_sql_lock(); +#endif + code = sqlite3_prepare_v2(psync_db, sql, -1, &stmt, NULL); + if (unlikely(code != SQLITE_OK)) { + psync_sql_unlock(); +#if IS_DEBUG + pdbg_logf(D_ERROR, "error running sql statement: %s: %s called from %s:%u", sql, + sqlite3_errmsg(psync_db), file, line); + sendpdbg_logf("error running sql statement: %s: %s called from %s:%u", sql, + sqlite3_errmsg(psync_db), file, line); +#else + pdbg_logf(D_ERROR, "error running sql statement: %s: %s", sql, + sqlite3_errmsg(psync_db)); + sendpdbg_logf("error running sql statement: %s: %s", sql, + sqlite3_errmsg(psync_db)); +#endif + return NULL; + } + cnt = sqlite3_column_count(stmt); + res = (psync_sql_res *)malloc(sizeof(psync_sql_res) + + cnt * sizeof(psync_variant)); + res->stmt = stmt; + res->sql = sql; + res->column_count = cnt; + res->locked = SQL_WRITE_LOCK; + return res; +} + +#if IS_DEBUG +psync_sql_res *psync_sql_do_query(const char *sql, const char *file, + unsigned line) { +#else +psync_sql_res *psync_sql_query(const char *sql) { +#endif + psync_sql_res *ret; + ret = (psync_sql_res *)pcache_get(sql); + if (ret) { + // pdbg_logf(D_NOTICE, "got query %s from cache", sql); + ret->locked = SQL_WRITE_LOCK; + ret->sql = sql; +#if IS_DEBUG + psync_sql_do_lock(file, line); +#else + psync_sql_lock(); +#endif + return ret; + } else +#if IS_DEBUG + return psync_sql_do_query_nocache(sql, file, line); +#else + return psync_sql_query_nocache(sql); +#endif +} + +#if IS_DEBUG +psync_sql_res *psync_sql_do_query_rdlock_nocache(const char *sql, + const char *file, + unsigned line) { +#else +psync_sql_res *psync_sql_query_rdlock_nocache(const char *sql) { +#endif + sqlite3_stmt *stmt; + psync_sql_res *res; + int code, cnt; +#if IS_DEBUG + psync_sql_do_rdlock(file, line); +#else + psync_sql_rdlock(); +#endif + code = sqlite3_prepare_v2(psync_db, sql, -1, &stmt, NULL); + if (unlikely(code != SQLITE_OK)) { + psync_sql_rdunlock(); +#if IS_DEBUG + pdbg_logf(D_ERROR, "error running sql statement: %s: %s called from %s:%u", sql, + sqlite3_errmsg(psync_db), file, line); + sendpdbg_logf("error running sql statement: %s: %s called from %s:%u", sql, + sqlite3_errmsg(psync_db), file, line); +#else + pdbg_logf(D_ERROR, "error running sql statement: %s: %s", sql, + sqlite3_errmsg(psync_db)); + sendpdbg_logf("error running sql statement: %s: %s", sql, + sqlite3_errmsg(psync_db)); +#endif + return NULL; + } + cnt = sqlite3_column_count(stmt); + res = (psync_sql_res *)malloc(sizeof(psync_sql_res) + + cnt * sizeof(psync_variant)); + res->stmt = stmt; + res->sql = sql; + res->column_count = cnt; + res->locked = SQL_READ_LOCK; + return res; +} + +#if IS_DEBUG +psync_sql_res *psync_sql_do_query_rdlock(const char *sql, const char *file, + unsigned line) { +#else +psync_sql_res *psync_sql_query_rdlock(const char *sql) { +#endif + psync_sql_res *ret; + ret = (psync_sql_res *)pcache_get(sql); + if (ret) { + // pdbg_logf(D_NOTICE, "got query %s from cache", sql); + ret->locked = SQL_READ_LOCK; + ret->sql = sql; +#if IS_DEBUG + psync_sql_do_rdlock(file, line); +#else + psync_sql_rdlock(); +#endif + return ret; + } else +#if IS_DEBUG + return psync_sql_do_query_rdlock_nocache(sql, file, line); +#else + return psync_sql_query_rdlock_nocache(sql); +#endif +} + +#if IS_DEBUG +psync_sql_res *psync_sql_do_query_nolock_nocache(const char *sql, + const char *file, + unsigned line) { +#else +psync_sql_res *psync_sql_query_nolock_nocache(const char *sql) { +#endif + sqlite3_stmt *stmt; + psync_sql_res *res; + int code, cnt; +#if IS_DEBUG + if (!psync_sql_islocked()) { + pdbg_logf(D_BUG, + "illegal use of psync_sql_query_nolock, can only be used while " + "holding lock, invoked from %s:%u, sql: %s", + file, line, sql); + sendpdbg_logf("illegal use of psync_sql_query_nolock, can only be used while " + "holding lock, invoked from %s:%u, sql: %s", + file, line, sql); + abort(); + } +#endif + code = sqlite3_prepare_v2(psync_db, sql, -1, &stmt, NULL); + if (unlikely(code != SQLITE_OK)) { + pdbg_logf(D_ERROR, "error running sql statement: %s: %s", sql, + sqlite3_errmsg(psync_db)); + sendpdbg_logf("error running sql statement: %s: %s", sql, + sqlite3_errmsg(psync_db)); + return NULL; + } + cnt = sqlite3_column_count(stmt); + res = (psync_sql_res *)malloc(sizeof(psync_sql_res) + + cnt * sizeof(psync_variant)); + res->stmt = stmt; + res->sql = sql; + res->column_count = cnt; + res->locked = SQL_NO_LOCK; + return res; +} + +#if IS_DEBUG +psync_sql_res *psync_sql_do_query_nolock(const char *sql, const char *file, + unsigned line) { +#else +psync_sql_res *psync_sql_query_nolock(const char *sql) { +#endif + psync_sql_res *ret; +#if IS_DEBUG + if (!psync_sql_islocked()) { + pdbg_logf(D_BUG, + "illegal use of psync_sql_query_nolock, can only be used while " + "holding lock, invoked from %s:%u, sql: %s", + file, line, sql); + sendpdbg_logf("illegal use of psync_sql_query_nolock, can only be used while " + "holding lock, invoked from %s:%u, sql: %s", + file, line, sql); + abort(); + } +#endif + ret = (psync_sql_res *)pcache_get(sql); + if (ret) { + // pdbg_logf(D_NOTICE, "got query %s from cache", sql); + ret->locked = SQL_NO_LOCK; + ret->sql = sql; + return ret; + } else +#if IS_DEBUG + return psync_sql_do_query_nolock_nocache(sql, file, line); +#else + return psync_sql_query_nolock_nocache(sql); +#endif +} + +static void psync_sql_free_cache(void *ptr) { + psync_sql_res *res = (psync_sql_res *)ptr; + sqlite3_finalize(res->stmt); +#if IS_DEBUG + memset(res, 0xff, sizeof(psync_sql_res)); +#endif + free(res); +} + +static void psync_sql_res_unlock(psync_sql_res *res) { + switch (res->locked) { + case SQL_NO_LOCK: + break; + case SQL_READ_LOCK: + psync_sql_rdunlock(); + break; + case SQL_WRITE_LOCK: + psync_sql_unlock(); + break; +#if IS_DEBUG + default: + pdbg_logf(D_ERROR, "unknown value for locked %d", res->locked); + abort(); +#endif + } +} + +void psync_sql_free_result(psync_sql_res *res) { + int code = sqlite3_reset(res->stmt); + psync_sql_res_unlock(res); +#if IS_DEBUG + memset(res->row, 0xff, res->column_count * sizeof(psync_variant)); +#endif + if (code == SQLITE_OK) + pcache_add(res->sql, res, PSYNC_QUERY_CACHE_SEC, psync_sql_free_cache, + PSYNC_QUERY_MAX_CNT); + else + psync_sql_free_cache(res); +} + +void psync_sql_free_result_nocache(psync_sql_res *res) { + sqlite3_finalize(res->stmt); + psync_sql_res_unlock(res); +#if IS_DEBUG + memset(res, 0xff, sizeof(psync_sql_res)); +#endif + free(res); +} + +#if IS_DEBUG +psync_sql_res *psync_sql_do_prep_statement_nocache(const char *sql, + const char *file, + unsigned line) { +#else +psync_sql_res *psync_sql_prep_statement_nocache(const char *sql) { +#endif + sqlite3_stmt *stmt; + psync_sql_res *res; + int code; +#if IS_DEBUG + psync_sql_do_lock(file, line); +#else + psync_sql_lock(); +#endif + code = sqlite3_prepare_v2(psync_db, sql, -1, &stmt, NULL); + if (unlikely(code != SQLITE_OK)) { + psync_sql_unlock(); +#if IS_DEBUG + pdbg_logf(D_ERROR, "error running sql statement: %s: %s called from %s:%u", sql, + sqlite3_errmsg(psync_db), file, line); + sendpdbg_logf("error running sql statement: %s: %s called from %s:%u", sql, + sqlite3_errmsg(psync_db), file, line); +#else + pdbg_logf(D_ERROR, "error running sql statement: %s: %s", sql, + sqlite3_errmsg(psync_db)); + sendpdbg_logf("error running sql statement: %s: %s", sql, + sqlite3_errmsg(psync_db)); +#endif + return NULL; + } + res = malloc(sizeof(psync_sql_res)); + res->stmt = stmt; + res->sql = sql; +#if IS_DEBUG + res->column_count = 0; +#endif + res->locked = SQL_WRITE_LOCK; + return res; +} + +#if IS_DEBUG +psync_sql_res *psync_sql_do_prep_statement(const char *sql, const char *file, + unsigned line) { +#else +psync_sql_res *psync_sql_prep_statement(const char *sql) { +#endif + psync_sql_res *ret; + ret = pcache_get(sql); + if (ret) { + // pdbg_logf(D_NOTICE, "got statement %s from cache", sql); + ret->locked = SQL_WRITE_LOCK; +#if IS_DEBUG + psync_sql_do_lock(file, line); +#else + psync_sql_lock(); +#endif + return ret; + } else +#if IS_DEBUG + return psync_sql_do_prep_statement_nocache(sql, file, line); +#else + return psync_sql_prep_statement_nocache(sql); +#endif +} + +int psync_sql_reset(psync_sql_res *res) { + int code = sqlite3_reset(res->stmt); + if (unlikely(code != SQLITE_OK)) { + pdbg_logf(D_ERROR, "sqlite3_reset returned error: %s", + sqlite3_errmsg(psync_db)); + return -1; + } else + return 0; +} + +int psync_sql_run(psync_sql_res *res) { + int code = sqlite3_step(res->stmt); + if (unlikely(code != SQLITE_DONE)) { + pdbg_logf(D_ERROR, "sqlite3_step returned error: %s: %s", + sqlite3_errmsg(psync_db), res->sql); + sendtpdbg_logf("sqlite3_step returned error (in_transaction=%d): %s: %s", + in_transaction, sqlite3_errmsg(psync_db), res->sql); + transaction_failed = 1; + if (in_transaction) + pdbg_logf(D_BUG, "transaction query failed, this may lead to restarting " + "transaction over and over"); + return -1; + } + code = sqlite3_reset(res->stmt); + if (unlikely(code != SQLITE_OK)) + pdbg_logf(D_ERROR, "sqlite3_reset returned error: %s", + sqlite3_errmsg(psync_db)); + return 0; +} + +int psync_sql_run_free_nocache(psync_sql_res *res) { + int code = sqlite3_step(res->stmt); + if (unlikely(code != SQLITE_DONE)) { + pdbg_logf(D_ERROR, "sqlite3_step returned error: %s: %s", + sqlite3_errmsg(psync_db), res->sql); + sendtpdbg_logf("sqlite3_step returned error (in_transaction=%d): %s: %s", + in_transaction, sqlite3_errmsg(psync_db), res->sql); + code = -1; + transaction_failed = 1; + if (in_transaction) + pdbg_logf(D_BUG, "transaction query failed, this may lead to restarting " + "transaction over and over"); + } else + code = 0; + sqlite3_finalize(res->stmt); + psync_sql_res_unlock(res); + free(res); + return code; +} + +int psync_sql_run_free(psync_sql_res *res) { + int code = sqlite3_step(res->stmt); + if (unlikely(code != SQLITE_DONE || + (code = sqlite3_reset(res->stmt)) != SQLITE_OK)) { + pdbg_logf(D_ERROR, "sqlite3_step returned error: %s: %s", + sqlite3_errmsg(psync_db), res->sql); + sendtpdbg_logf("sqlite3_step returned error (in_transaction=%d): %s: %s", + in_transaction, sqlite3_errmsg(psync_db), res->sql); + sqlite3_finalize(res->stmt); + transaction_failed = 1; + if (in_transaction) + pdbg_logf(D_BUG, "transaction query failed, this may lead to restarting " + "transaction over and over"); + psync_sql_res_unlock(res); + free(res); + return -1; + } else { + psync_sql_res_unlock(res); + pcache_add(res->sql, res, PSYNC_QUERY_CACHE_SEC, psync_sql_free_cache, + PSYNC_QUERY_MAX_CNT); + return 0; + } +} + +void psync_sql_bind_int(psync_sql_res *res, int n, int64_t val) { + int code = sqlite3_bind_int64(res->stmt, n, val); + if (unlikely(code != SQLITE_OK)) + pdbg_logf(D_ERROR, "error binding value: %s", sqlite3_errmsg(psync_db)); +} + +void psync_sql_bind_uint(psync_sql_res *res, int n, uint64_t val) { + int code = sqlite3_bind_int64(res->stmt, n, val); + if (unlikely(code != SQLITE_OK)) + pdbg_logf(D_ERROR, "error binding value: %s", sqlite3_errmsg(psync_db)); +} + +void psync_sql_bind_double(psync_sql_res *res, int n, double val) { + int code = sqlite3_bind_double(res->stmt, n, val); + if (unlikely(code != SQLITE_OK)) + pdbg_logf(D_ERROR, "error binding value: %s", sqlite3_errmsg(psync_db)); +} + +void psync_sql_bind_string(psync_sql_res *res, int n, const char *str) { + int code = sqlite3_bind_text(res->stmt, n, str, -1, SQLITE_STATIC); + if (unlikely(code != SQLITE_OK)) + pdbg_logf(D_ERROR, "error binding value: %s", sqlite3_errmsg(psync_db)); +} + +void psync_sql_bind_lstring(psync_sql_res *res, int n, const char *str, + size_t len) { + int code = sqlite3_bind_text(res->stmt, n, str, len, SQLITE_STATIC); + if (unlikely(code != SQLITE_OK)) + pdbg_logf(D_ERROR, "error binding value: %s", sqlite3_errmsg(psync_db)); +} + +void psync_sql_bind_blob(psync_sql_res *res, int n, const char *str, + size_t len) { + int code = sqlite3_bind_blob(res->stmt, n, str, len, SQLITE_STATIC); + if (unlikely(code != SQLITE_OK)) + pdbg_logf(D_ERROR, "error binding value: %s", sqlite3_errmsg(psync_db)); +} + +void psync_sql_bind_null(psync_sql_res *res, int n) { + int code = sqlite3_bind_null(res->stmt, n); + if (unlikely(code != SQLITE_OK)) + pdbg_logf(D_ERROR, "error binding value: %s", sqlite3_errmsg(psync_db)); +} + +psync_variant_row psync_sql_fetch_row(psync_sql_res *res) { + int code, i; + code = sqlite3_step(res->stmt); + if (code == SQLITE_ROW) { + for (i = 0; i < res->column_count; i++) { + code = sqlite3_column_type(res->stmt, i); + if (code == SQLITE_INTEGER) { + res->row[i].type = PSYNC_TNUMBER; + res->row[i].snum = sqlite3_column_int64(res->stmt, i); + } else if (code == SQLITE_TEXT || code == SQLITE_BLOB) { + res->row[i].type = PSYNC_TSTRING; + res->row[i].length = sqlite3_column_bytes(res->stmt, i); + res->row[i].str = (char *)sqlite3_column_text(res->stmt, i); + } else if (code == SQLITE_FLOAT) { + res->row[i].type = PSYNC_TREAL; + res->row[i].real = sqlite3_column_double(res->stmt, i); + } else + res->row[i].type = PSYNC_TNULL; + } + return res->row; + } else { + if (unlikely(code != SQLITE_DONE)) + pdbg_logf(D_ERROR, "sqlite3_step returned error: %s", + sqlite3_errmsg(psync_db)); + return NULL; + } +} + +psync_str_row psync_sql_fetch_rowstr(psync_sql_res *res) { + int code, i; + const char **strs; + code = sqlite3_step(res->stmt); + if (code == SQLITE_ROW) { + strs = (const char **)res->row; + for (i = 0; i < res->column_count; i++) + strs[i] = (const char *)sqlite3_column_text(res->stmt, i); + return strs; + } else { + if (unlikely(code != SQLITE_DONE)) + pdbg_logf(D_ERROR, "sqlite3_step returned error: %s", + sqlite3_errmsg(psync_db)); + return NULL; + } +} + +const uint64_t *psync_sql_fetch_rowint(psync_sql_res *res) { + int code, i; + uint64_t *ret; + code = sqlite3_step(res->stmt); + if (code == SQLITE_ROW) { + ret = (uint64_t *)res->row; + for (i = 0; i < res->column_count; i++) + ret[i] = sqlite3_column_int64(res->stmt, i); + return ret; + } else { + if (unlikely(code != SQLITE_DONE)) + pdbg_logf(D_ERROR, "sqlite3_step returned error: %s", + sqlite3_errmsg(psync_db)); + return NULL; + } +} + +psync_full_result_int *psync_sql_fetchall_int(psync_sql_res *res) { + uint64_t *data; + psync_full_result_int *ret; + unsigned long rows, cols, off, i, all; + int code; + cols = res->column_count; + rows = 0; + off = 0; + all = 0; + data = NULL; + while ((code = sqlite3_step(res->stmt)) == SQLITE_ROW) { + if (rows >= all) { + all = 10 + all * 2; + data = (uint64_t *)realloc(data, sizeof(uint64_t) * cols * all); + } + for (i = 0; i < cols; i++) + data[off + i] = sqlite3_column_int64(res->stmt, i); + off += cols; + rows++; + } + if (unlikely(code != SQLITE_DONE)) + pdbg_logf(D_ERROR, "sqlite3_step returned error: %s", sqlite3_errmsg(psync_db)); + psync_sql_free_result(res); + ret = (psync_full_result_int *)malloc( + offsetof(psync_full_result_int, data) + sizeof(uint64_t) * off); + ret->rows = rows; + ret->cols = cols; + memcpy(ret->data, data, sizeof(uint64_t) * off); + free(data); + return ret; +} + +uint32_t psync_sql_affected_rows() { return sqlite3_changes(psync_db); } + +uint64_t psync_sql_insertid() { return sqlite3_last_insert_rowid(psync_db); } + + +static const char *PSYNC_CONST get_type_name(uint32_t t) { + if (unlikely(t >= ARRAY_SIZE(psync_typenames))) + t = 0; + return psync_typenames[t]; +} + +uint64_t psync_err_number_expected(const char *file, const char *function, + int unsigned line, const psync_variant *v) { + if (D_CRITICAL <= DEBUG_LEVEL) + pdbg_printf(file, function, line, D_CRITICAL, + "type error, wanted %s got %s", get_type_name(PSYNC_TNUMBER), + get_type_name(v->type)); + return 0; +} + +const char *psync_err_string_expected(const char *file, const char *function, + int unsigned line, + const psync_variant *v) { + if (D_CRITICAL <= DEBUG_LEVEL) + pdbg_printf(file, function, line, D_CRITICAL, + "type error, wanted %s got %s", get_type_name(PSYNC_TSTRING), + get_type_name(v->type)); + return ""; +} + +const char *psync_lstring_expected(const char *file, const char *function, + int unsigned line, const psync_variant *v, + size_t *len) { + if (likely(v->type == PSYNC_TSTRING)) { + *len = v->length; + return v->str; + } else { + if (D_CRITICAL <= DEBUG_LEVEL) + pdbg_printf(file, function, line, D_CRITICAL, + "type error, wanted %s got %s", get_type_name(PSYNC_TSTRING), + get_type_name(v->type)); + *len = 0; + return ""; + } +} + +double psync_err_real_expected(const char *file, const char *function, + int unsigned line, const psync_variant *v) { + if (D_CRITICAL <= DEBUG_LEVEL) + pdbg_printf(file, function, line, D_CRITICAL, + "type error, wanted %s got %s", get_type_name(PSYNC_TREAL), + get_type_name(v->type)); + return 0.0; +} + +void psync_try_free_memory() { + sqlite3_db_release_memory(psync_db); + pcache_clean(); +} diff --git a/pclsync/psql.h b/pclsync/psql.h new file mode 100644 index 0000000..f0eea77 --- /dev/null +++ b/pclsync/psql.h @@ -0,0 +1,152 @@ +// IMPORTANT: include pdbg.h before this file + +#ifndef __PSQL_H +#define __PSQL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include +#include + +#include "pcompiler.h" +#include "plist.h" + +typedef struct { + uint32_t type; + uint32_t length; + union { + uint64_t num; + int64_t snum; + const char *str; + double real; + }; +} psync_variant; + +typedef struct { + sqlite3_stmt *stmt; + const char *sql; + int column_count; + int locked; + psync_variant row[]; +} psync_sql_res; + +typedef struct { + uint32_t rows; + uint32_t cols; + uint64_t data[]; +} psync_full_result_int; + + +typedef const uint64_t *psync_uint_row; +typedef const char *const *psync_str_row; +typedef const psync_variant *psync_variant_row; + +typedef int (*psync_list_builder_sql_callback)(psync_list_builder_t *, void *, psync_variant_row); +typedef void (*psync_transaction_callback_t)(void *); + +int psync_sql_connect(const char *db) PSYNC_NONNULL(1); +int psync_sql_close(); +int psync_sql_reopen(const char *path); +void psync_sql_checkpoint_lock(); +void psync_sql_checkpoint_unlock(); + +#if IS_DEBUG + +#define psync_sql_trylock() psync_sql_do_trylock(__FILE__, __LINE__) +#define psync_sql_lock() psync_sql_do_lock(__FILE__, __LINE__) +#define psync_sql_rdlock() psync_sql_do_rdlock(__FILE__, __LINE__) +#define psync_sql_statement(sql) psync_sql_do_statement(sql, __FILE__, __LINE__) +#define psync_sql_start_transaction() psync_sql_do_start_transaction(__FILE__, __LINE__) +#define psync_sql_query_nocache(sql) psync_sql_do_query_nocache(sql, __FILE__, __LINE__) +#define psync_sql_query(sql) psync_sql_do_query(sql, __FILE__, __LINE__) +#define psync_sql_query_rdlock_nocache(sql) psync_sql_do_query_rdlock_nocache(sql, __FILE__, __LINE__) +#define psync_sql_query_rdlock(sql) psync_sql_do_query_rdlock(sql, __FILE__, __LINE__) +#define psync_sql_query_nolock_nocache(sql) psync_sql_do_query_nolock_nocache(sql, __FILE__, __LINE__) +#define psync_sql_query_nolock(sql) psync_sql_do_query_nolock(sql, __FILE__, __LINE__) +#define psync_sql_prep_statement_nocache(sql) psync_sql_do_prep_statement_nocache(sql, __FILE__, __LINE__) +#define psync_sql_prep_statement(sql) psync_sql_do_prep_statement(sql, __FILE__, __LINE__) + +int psync_sql_do_trylock(const char *file, unsigned line); +void psync_sql_do_lock(const char *file, unsigned line); +void psync_sql_do_rdlock(const char *file, unsigned line); +int psync_sql_do_statement(const char *sql, const char *file, unsigned line) PSYNC_NONNULL(1); +int psync_sql_do_start_transaction(const char *file, unsigned line); +psync_sql_res *psync_sql_do_query_nocache(const char *sql, const char *file, unsigned line) PSYNC_NONNULL(1); +psync_sql_res *psync_sql_do_query(const char *sql, const char *file, unsigned line) PSYNC_NONNULL(1); +psync_sql_res *psync_sql_do_query_rdlock_nocache(const char *sql, const char *file, unsigned line) PSYNC_NONNULL(1); +psync_sql_res *psync_sql_do_query_rdlock(const char *sql, const char *file, unsigned line) PSYNC_NONNULL(1); +psync_sql_res *psync_sql_do_query_nolock_nocache(const char *sql, const char *file, unsigned line) PSYNC_NONNULL(1); +psync_sql_res *psync_sql_do_query_nolock(const char *sql, const char *file, unsigned line) PSYNC_NONNULL(1); +psync_sql_res *psync_sql_do_prep_statement_nocache(const char *sql, const char *file, unsigned line) PSYNC_NONNULL(1); +psync_sql_res *psync_sql_do_prep_statement(const char *sql, const char *file, unsigned line) PSYNC_NONNULL(1); +void psync_sql_dump_locks(); + +#else + +int psync_sql_trylock(); +void psync_sql_lock(); +void psync_sql_rdlock(); +int psync_sql_statement(const char *sql) PSYNC_NONNULL(1); +int psync_sql_start_transaction(); + +psync_sql_res *psync_sql_query_nocache(const char *sql) PSYNC_NONNULL(1); +psync_sql_res *psync_sql_query(const char *sql) PSYNC_NONNULL(1); +psync_sql_res *psync_sql_query_rdlock_nocache(const char *sql) PSYNC_NONNULL(1); +psync_sql_res *psync_sql_query_rdlock(const char *sql) PSYNC_NONNULL(1); +psync_sql_res *psync_sql_query_nolock(const char *sql) PSYNC_NONNULL(1); +psync_sql_res *psync_sql_query_nolock_nocache(const char *sql) PSYNC_NONNULL(1); +psync_sql_res *psync_sql_prep_statement(const char *sql) PSYNC_NONNULL(1); +psync_sql_res *psync_sql_prep_statement_nocache(const char *sql) PSYNC_NONNULL(1); + +#endif + +void psync_sql_unlock(); +void psync_sql_rdunlock(); +int psync_sql_has_waiters(); +int psync_sql_isrdlocked(); +int psync_sql_islocked(); +int psync_sql_tryupgradelock(); +int psync_sql_sync(); +int psync_sql_commit_transaction(); +int psync_sql_rollback_transaction(); + +void psync_sql_transation_add_callbacks(psync_transaction_callback_t commit_callback, psync_transaction_callback_t rollback_callback, void *ptr); + +char *psync_sql_cellstr(const char *sql) PSYNC_NONNULL(1); +int64_t psync_sql_cellint(const char *sql, int64_t dflt) PSYNC_NONNULL(1); +char **psync_sql_rowstr(const char *sql) PSYNC_NONNULL(1); +psync_variant *psync_sql_row(const char *sql) PSYNC_NONNULL(1); +int psync_sql_reset(psync_sql_res *res) PSYNC_NONNULL(1); +int psync_sql_run(psync_sql_res *res) PSYNC_NONNULL(1); +int psync_sql_run_free(psync_sql_res *res) PSYNC_NONNULL(1); +int psync_sql_run_free_nocache(psync_sql_res *res) PSYNC_NONNULL(1); +void psync_sql_bind_uint(psync_sql_res *res, int n, uint64_t val) PSYNC_NONNULL(1); +void psync_sql_bind_int(psync_sql_res *res, int n, int64_t val) PSYNC_NONNULL(1); +void psync_sql_bind_double(psync_sql_res *res, int n, double val) PSYNC_NONNULL(1); +void psync_sql_bind_string(psync_sql_res *res, int n, const char *str) PSYNC_NONNULL(1); +void psync_sql_bind_lstring(psync_sql_res *res, int n, const char *str, size_t len) PSYNC_NONNULL(1); +void psync_sql_bind_blob(psync_sql_res *res, int n, const char *str, size_t len) PSYNC_NONNULL(1); +void psync_sql_bind_null(psync_sql_res *res, int n) PSYNC_NONNULL(1); +void psync_sql_free_result(psync_sql_res *res) PSYNC_NONNULL(1); +void psync_sql_free_result_nocache(psync_sql_res *res) PSYNC_NONNULL(1); +psync_variant_row psync_sql_fetch_row(psync_sql_res *res) PSYNC_NONNULL(1); +psync_str_row psync_sql_fetch_rowstr(psync_sql_res *res) PSYNC_NONNULL(1); +psync_uint_row psync_sql_fetch_rowint(psync_sql_res *res) PSYNC_NONNULL(1); +psync_full_result_int *psync_sql_fetchall_int(psync_sql_res *res) PSYNC_NONNULL(1); +uint32_t psync_sql_affected_rows() PSYNC_PURE; +uint64_t psync_sql_insertid() PSYNC_PURE; +void psync_list_bulder_add_sql(psync_list_builder_t *builder, psync_sql_res *res, psync_list_builder_sql_callback callback); +uint64_t psync_err_number_expected(const char *file, const char *function, int unsigned line, const psync_variant *v) PSYNC_COLD; +const char *psync_err_string_expected(const char *file, const char *function, int unsigned line, const psync_variant *v) PSYNC_COLD; +const char *psync_lstring_expected(const char *file, const char *function, int unsigned line, const psync_variant *v, size_t *len) PSYNC_NONNULL(4, 5); +double psync_err_real_expected(const char *file, const char *function, int unsigned line, const psync_variant *v) PSYNC_COLD; +void psync_try_free_memory(); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/pclsync/pstatus.c b/pclsync/pstatus.c index fe375f5..1a8d750 100644 --- a/pclsync/pstatus.c +++ b/pclsync/pstatus.c @@ -28,6 +28,8 @@ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include +#include #include "pfile.h" #include "pstatus.h" @@ -37,8 +39,7 @@ #include "psettings.h" #include "ptask.h" #include "putil.h" -#include -#include +#include "psql.h" static uint32_t statuses[PSTATUS_NUM_STATUSES] = { PSTATUS_INVALID, PSTATUS_ONLINE_OFFLINE, diff --git a/pclsync/psyncer.c b/pclsync/psyncer.c index 06a609a..5e20e73 100644 --- a/pclsync/psyncer.c +++ b/pclsync/psyncer.c @@ -29,13 +29,9 @@ DAMAGE. */ -#include -#include -#include -#include -#include #include #include +#include #include "pdownload.h" #include "pfile.h" @@ -50,7 +46,9 @@ #include "ptask.h" #include "ptree.h" #include "putil.h" -#include +#include "pdbg.h" +#include "psql.h" + extern const unsigned char pfile_invalid_chars[]; diff --git a/pclsync/psynclib.c b/pclsync/psynclib.c index 7081f75..8912337 100644 --- a/pclsync/psynclib.c +++ b/pclsync/psynclib.c @@ -35,12 +35,6 @@ #include #include -#include -#include -#include -#include -#include - #include "papi.h" #include "pbusinessaccount.h" #include "pcache.h" @@ -83,6 +77,7 @@ #include "publiclinks.h" #include "pupload.h" #include "putil.h" +#include "psql.h" // Variable containing UNIX time of the last backup file deleted event time_t lastBupDelEventTime = 0; diff --git a/pclsync/psys.c b/pclsync/psys.c index 07b18d9..9ada318 100644 --- a/pclsync/psys.c +++ b/pclsync/psys.c @@ -3,7 +3,8 @@ #include #include -#include "plibs.h" +#include "pdbg.h" +#include "psql.h" #include "psys.h" static uid_t psync_uid; diff --git a/pclsync/ptask.c b/pclsync/ptask.c index 526d955..a5494a9 100644 --- a/pclsync/ptask.c +++ b/pclsync/ptask.c @@ -32,12 +32,6 @@ #include #include #include - -#include -#include -#include -#include -#include #include #include "papi.h" @@ -55,6 +49,7 @@ #include "ptask.h" #include "ptree.h" #include "pupload.h" +#include "psql.h" #define get_len(t) (sizeof(t) - offsetof(t, request)) diff --git a/pclsync/ptools.c b/pclsync/ptools.c index 7cc0400..ef0a5b6 100644 --- a/pclsync/ptools.c +++ b/pclsync/ptools.c @@ -33,21 +33,20 @@ */ #include - -#include "papi.h" - -#include "plibs.h" -#include "pnetlibs.h" -#include "psettings.h" -#include "ptools.h" -#include "stdlib.h" -#include "string.h" - #include #include #include #include #include +#include +#include + +#include "papi.h" +#include "plibs.h" +#include "pnetlibs.h" +#include "psettings.h" +#include "ptools.h" +#include "psql.h" char *ptools_get_mac_addr() { char buffer[128]; diff --git a/pclsync/publiclinks.c b/pclsync/publiclinks.c index 1c77886..34257ba 100644 --- a/pclsync/publiclinks.c +++ b/pclsync/publiclinks.c @@ -26,6 +26,9 @@ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#include +#include + #include "publiclinks.h" #include "papi.h" #include "pfsfolder.h" @@ -33,8 +36,7 @@ #include "pnetlibs.h" #include "prun.h" #include "ptimer.h" -#include -#include +#include "psql.h" #define FOLDERID_ENTRY_SIZE 18 diff --git a/pclsync/pupload.c b/pclsync/pupload.c index 66225c4..1eba6bc 100644 --- a/pclsync/pupload.c +++ b/pclsync/pupload.c @@ -29,11 +29,6 @@ DAMAGE. */ -#include -#include -#include -#include -#include #include #include @@ -57,6 +52,7 @@ #include "ptools.h" #include "pupload.h" #include "putil.h" +#include "psql.h" extern const unsigned char pfile_invalid_chars[];