pcloudcc-lneely/pCloudCC/lib/pclsync/plibs.c

2534 lines
72 KiB
C

/* Copyright (c) 2013-2014 Anton Titov.
* Copyright (c) 2013-2014 pCloud Ltd.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of pCloud Ltd nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL pCloud Ltd BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "psettings.h"
#include "plibs.h"
#include "ptimer.h"
#include "pcache.h"
#include "ptree.h"
#include "pdatabase.h"
#include "plocks.h"
#include "pnetlibs.h"
#include <string.h>
#include <stdarg.h>
#include <stdio.h>
#include <stddef.h>
#define return_error(err) do {psync_error=err; return -1;} while (0)
#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"
"202122232425262728292a2b2c2d2e2f"
"303132333435363738393a3b3c3d3e3f"
"404142434445464748494a4b4c4d4e4f"
"505152535455565758595a5b5c5d5e5f"
"606162636465666768696a6b6c6d6e6f"
"707172737475767778797a7b7c7d7e7f"
"808182838485868788898a8b8c8d8e8f"
"909192939495969798999a9b9c9d9e9f"
"a0a1a2a3a4a5a6a7a8a9aaabacadaeaf"
"b0b1b2b3b4b5b6b7b8b9babbbcbdbebf"
"c0c1c2c3c4c5c6c7c8c9cacbcccdcecf"
"d0d1d2d3d4d5d6d7d8d9dadbdcdddedf"
"e0e1e2e3e4e5e6e7e8e9eaebecedeeef"
"f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff"
};
const char base64_table[]={
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
};
static const char base64_reverse_table[256]={
-2, -2, -2, -2, -2, -2, -2, -2, -2, -1, -1, -2, -2, -1, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-1, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, 62, -2, 62, -2, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -2, -2, -2, -1, -2, -2,
-2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -2, -2, -2, -2, 63,
-2, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2,
-2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2
};
uint16_t const *__hex_lookup=(uint16_t *)__hex_lookupl;
static char normalize_table[256];
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, *psync_my_verify_token=NULL;
int psync_my_2fa_code_type=0, psync_my_2fa_trust=0, psync_my_2fa_has_devices=0, psync_my_2fa_type=1;
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;
char *psync_strdup(const char *str){
size_t len;
len=strlen(str)+1;
return (char *)memcpy(psync_new_cnt(char, len), str, len);
}
char *psync_strnormalize_filename(const char *str){
size_t len, i;
char *ptr;
len=strlen(str)+1;
ptr=psync_new_cnt(char, len);
for (i=0; i<len; i++)
ptr[i]=normalize_table[(unsigned char)str[i]];
return ptr;
}
char *psync_strndup(const char *str, size_t len){
char *ptr;
ptr=(char *)memcpy(psync_new_cnt(char, len+1), str, len);
ptr[len]=0;
return ptr;
}
char *psync_strcat(const char *str, ...){
size_t i, size, len;
const char *strs[64];
size_t lengths[64];
const char *ptr;
char *ptr2, *ptr3;
va_list ap;
va_start(ap, str);
strs[0]=str;
len=strlen(str);
lengths[0]=len;
size=len+1;
i=1;
while ((ptr=va_arg(ap, const char *))){
assert(i<ARRAY_SIZE(strs));
len=strlen(ptr);
lengths[i]=len;
strs[i++]=ptr;
size+=len;
}
va_end(ap);
ptr2=ptr3=(char *)psync_malloc(size);
for (size=0; size<i; size++){
memcpy(ptr2, strs[size], lengths[size]);
ptr2+=lengths[size];
}
*ptr2=0;
return ptr3;
}
int psync_slprintf(char *str, size_t size, const char *format, ...){
va_list ap;
int ret;
va_start(ap, format);
ret=vsnprintf(str, size, format, ap);
va_end(ap);
if (unlikely_log(ret>=size))
str[size-1]=0;
return ret;
}
unsigned char *psync_base32_encode(const unsigned char *str, size_t length, size_t *ret_length){
static const unsigned char *table=(const unsigned char *)"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
unsigned char *result;
unsigned char *p;
uint32_t bits, buff;
result=(unsigned char *)psync_malloc(((length+4)/5)*8+1);
p=result;
bits=0;
buff=0; // don't really have to initialize this one, but a compiler that will detect that this is safe is yet to be born
while (length){
if (bits<5){
buff=(buff<<8)|(*str++);
length--;
bits+=8;
}
bits-=5;
*p++=table[0x1f&(buff>>bits)];
}
while (bits){
if (bits<5){
buff<<=(5-bits);
bits=5;
}
bits-=5;
*p++=table[0x1f&(buff>>bits)];
}
*ret_length=p-result;
*p=0;
return result;
}
unsigned char *psync_base32_decode(const unsigned char *str, size_t length, size_t *ret_length){
unsigned char *result, *p;
uint32_t bits, buff;
unsigned char ch;
result=(unsigned char *)psync_malloc((length+7)/8*5+1);
p=result;
bits=0;
buff=0;
while (length){
ch=*str++;
length--;
if (ch>='A' && ch<='Z')
ch=(ch&0x1f)-1;
else if (ch>='2'&&ch<='7')
ch-='2'-26;
else{
psync_free(result);
return NULL;
}
buff=(buff<<5)+ch;
bits+=5;
if (bits>=8){
bits-=8;
*p++=buff>>bits;
}
}
*p=0;
*ret_length=p-result;
return result;
}
unsigned char *psync_base64_encode(const unsigned char *str, size_t length, size_t *ret_length){
const unsigned char *current = str;
unsigned char *p;
unsigned char *result;
result=(unsigned char *)psync_malloc(((length+2)/3)*4+1);
p=result;
while(length>2){
*p++=base64_table[current[0] >> 2];
*p++=base64_table[((current[0] & 0x03) << 4) + (current[1] >> 4)];
*p++=base64_table[((current[1] & 0x0f) << 2) + (current[2] >> 6)];
*p++=base64_table[current[2] & 0x3f];
current+=3;
length-=3;
}
if (length!=0){
*p++=base64_table[current[0] >> 2];
if (length>1){
*p++=base64_table[((current[0] & 0x03) << 4) + (current[1] >> 4)];
*p++=base64_table[(current[1] & 0x0f) << 2];
}
else
*p++=base64_table[(current[0] & 0x03) << 4];
}
*ret_length=p-result;
*p=0;
return result;
}
unsigned char *psync_base64_decode(const unsigned char *str, size_t length, size_t *ret_length){
const unsigned char *current = str;
unsigned char *result;
size_t i=0, j=0;
ssize_t ch;
result=(unsigned char *)psync_malloc((length+3)/4*3+1);
while (length-- > 0){
ch=base64_reverse_table[*current++];
if (ch==-1)
continue;
else if (ch==-2) {
psync_free(result);
return NULL;
}
switch(i%4) {
case 0:
result[j]=ch<<2;
break;
case 1:
result[j++]|=ch>>4;
result[j]=(ch&0x0f)<<4;
break;
case 2:
result[j++]|=ch>>2;
result[j]=(ch&0x03)<<6;
break;
case 3:
result[j++]|=ch;
break;
}
i++;
}
*ret_length=j;
result[j]=0;
return result;
}
int psync_is_valid_utf8(const char *str){
static const int8_t trailing[]={
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
3, 3, 3, 3, 3, 3, 3, 3, -1, -1, -1, -1, -1, -1, -1, -1
};
int8_t t;
while (*str) {
t=trailing[(unsigned char)*str++];
if (unlikely(t)){
if (t<0)
return 0;
while (t--)
if ((((unsigned char)*str++)&0xc0)!=0x80)
return 0;
}
}
return 1;
}
void psync_sql_err_callback(void *ptr, int code, const char *msg){
debug(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)){
debug(D_NOTICE, "checkpoint already in progress");
return;
}
debug(D_NOTICE, "checkpointing database");
code=sqlite3_wal_checkpoint(psync_db, NULL);
while (code==SQLITE_LOCKED){
psync_milisleep(2);
code=sqlite3_wal_checkpoint(psync_db, NULL);
}
pthread_mutex_unlock(&psync_db_checkpoint_mutex);
if (unlikely(code!=SQLITE_OK))
debug(D_CRITICAL, "sqlite3_wal_checkpoint returned error %d", code);
else
debug(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)
psync_run_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;
psync_stat_t st;
uint64_t dbver;
int initdbneeded=0;
int code;
assert(sqlite3_libversion_number()==SQLITE_VERSION_NUMBER);
assert(!strcmp(sqlite3_sourceid(), SQLITE_SOURCE_ID));
assert(!strcmp(sqlite3_libversion(), SQLITE_VERSION));
debug(D_NOTICE, "Using sqlite version %s source %s", sqlite3_libversion(), sqlite3_sourceid());
if (!sqlite3_threadsafe()){
debug(D_CRITICAL, "sqlite is compiled without thread support");
return -1;
}
if (psync_stat(db, &st)!=0)
initdbneeded=1;
code=sqlite3_open(db, &psync_db);
if (likely(code==SQLITE_OK)){
if (initmutex){
psync_rwlock_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'")){
debug(D_ERROR, "database is locked");
sqlite3_close(psync_db);
psync_rwlock_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;
debug(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])){
debug(D_ERROR, "error running statement %s on sqlite %s", psync_db_upgrade[i], sqlite3_libversion());
if (IS_DEBUG)
return_error(PERROR_DATABASE_OPEN);
}
}
return 0;
}
else{
debug(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){
psync_cache_clean_all();
tries++;
if (tries>100){
psync_milisleep_nosqlcheck(tries-90);
if (tries>200){
debug(D_ERROR, "failed to close database");
break;
}
}
}
else
break;
}
psync_db=NULL;
if (unlikely(code!=SQLITE_OK)){
debug(D_CRITICAL, "error when closing database: %d", code);
code=sqlite3_close_v2(psync_db);
if (unlikely(code!=SQLITE_OK)){
debug(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;
debug(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)){
debug(D_CRITICAL, "sqlite3_wal_checkpoint returned error %d", code);
sqlite3_close(db);
return -1;
}
code=sqlite3_close(db);
if (unlikely(code!=SQLITE_OK)){
debug(D_CRITICAL, "sqlite3_close returned error %d", code);
return -1;
}
return 0;
}
else{
debug(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)){
debug(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);
senddebug("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();
}
sendassert(!wrlocked);
assert(!wrlocked);
wrlockfile=file;
wrlockline=line;
wrlockthread=psync_thread_name;
wrlocked=1;
wrlocker=pthread_self();
}
static void record_wrunlock(){
sendassert(pthread_equal(pthread_self(), wrlocker));
sendassert(wrlocked);
assert(pthread_equal(pthread_self(), wrlocker));
assert(wrlocked);
wrlocked=0;
}
static void record_rdlock(const char *file, unsigned line, struct timespec *tm){
rd_lock_data *lock;
lock=psync_new(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;
assert(rdlock);
lock=rdlock;
rdlock=NULL;
pthread_mutex_lock(&rdmutex);
psync_list_del(&lock->list);
pthread_mutex_unlock(&rdmutex);
return lock;
}
static void time_format(time_t tm, unsigned long ns, char *result);
void psync_sql_dump_locks(){
rd_lock_data *lock;
char dttime[36];
if (wrlocked){
time_format(sqllockstart.tv_sec, sqllockstart.tv_nsec, dttime);
debug(D_ERROR, "write lock taken by thread %s from %s:%u at %s", wrlockthread, wrlockfile, wrlockline, dttime);
senddebug("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);
debug(D_ERROR, "read lock taken by thread %s from %s:%u at %s", lock->thread, lock->file, lock->line, dttime);
senddebug("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 (psync_rwlock_trywrlock(&psync_db_lock))
return -1;
if (++sqllockcnt==1){
psync_nanotime(&sqllockstart);
record_wrlock(file, line);
}
return 0;
}
#else
int psync_sql_trylock(){
return psync_rwlock_trywrlock(&psync_db_lock);
}
#endif
#if IS_DEBUG
void psync_sql_do_lock(const char *file, unsigned line){
if (psync_rwlock_trywrlock(&psync_db_lock)){
struct timespec start, end;
unsigned long msec;
psync_nanotime(&start);
memcpy(&end, &start, sizeof(end));
end.tv_sec+=PSYNC_DEBUG_LOCK_TIMEOUT;
if (psync_rwlock_timedwrlock(&psync_db_lock, &end)){
debug(D_BUG, "sql write lock timed out called from %s:%u", file, line);
senddebug("sql write lock timed out called from %s:%u", file, line);
psync_sql_dump_locks();
abort();
}
psync_nanotime(&end);
msec=(end.tv_sec-start.tv_sec)*1000+end.tv_nsec/1000000-start.tv_nsec/1000000;
if (msec>=5)
debug(D_WARNING, "waited %lu milliseconds for database write lock", msec);
assert(sqllockcnt==0);
sqllockcnt++;
memcpy(&sqllockstart, &end, sizeof(struct timespec));
record_wrlock(file, line);
}
else if (++sqllockcnt==1){
psync_nanotime(&sqllockstart);
record_wrlock(file, line);
}
}
#else
void psync_sql_lock(){
psync_rwlock_wrlock(&psync_db_lock);
}
#endif
void psync_sql_unlock(){
#if IS_DEBUG
assert(sqllockcnt>0);
if (--sqllockcnt==0){
struct timespec end;
unsigned long msec;
psync_nanotime(&end);
msec=(end.tv_sec-sqllockstart.tv_sec)*1000+end.tv_nsec/1000000-sqllockstart.tv_nsec/1000000;
if (msec>=10)
debug(D_WARNING, "held database write lock for %lu milliseconds taken from %s:%u", msec, wrlockfile, wrlockline);
record_wrunlock();
psync_rwlock_unlock(&psync_db_lock);
}
else
psync_rwlock_unlock(&psync_db_lock);
#else
psync_rwlock_unlock(&psync_db_lock);
#endif
}
#if IS_DEBUG
void psync_sql_do_rdlock(const char *file, unsigned line){
if (psync_rwlock_tryrdlock(&psync_db_lock)){
struct timespec start, end;
unsigned long msec;
psync_nanotime(&start);
memcpy(&end, &start, sizeof(end));
end.tv_sec+=PSYNC_DEBUG_LOCK_TIMEOUT;
if (psync_rwlock_timedrdlock(&psync_db_lock, &end)){
debug(D_BUG, "sql read lock timed out, called from %s:%u", file, line);
senddebug("sql read lock timed out, called from %s:%u", file, line);
psync_sql_dump_locks();
abort();
}
psync_nanotime(&end);
msec=(end.tv_sec-start.tv_sec)*1000+end.tv_nsec/1000000-start.tv_nsec/1000000;
if (msec>=5)
debug(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){
psync_nanotime(&sqlrdlockstart);
record_rdlock(file, line, &sqlrdlockstart);
}
}
#else
void psync_sql_rdlock(){
psync_rwlock_rdlock(&psync_db_lock);
}
#endif
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;
psync_rwlock_unlock(&psync_db_lock);
psync_nanotime(&end);
lock=record_rdunlock();
msec=(end.tv_sec-sqlrdlockstart.tv_sec)*1000+end.tv_nsec/1000000-sqlrdlockstart.tv_nsec/1000000;
if (msec>=20)
debug(D_WARNING, "held database read lock for %lu milliseconds taken at %s:%u", msec, lock->file, lock->line);
psync_free(lock);
}
else
psync_rwlock_unlock(&psync_db_lock);
#else
psync_rwlock_unlock(&psync_db_lock);
#endif
}
int psync_sql_has_waiters(){
return psync_rwlock_num_waiters(&psync_db_lock)>0;
}
int psync_sql_isrdlocked(){
return psync_rwlock_holding_rdlock(&psync_db_lock);
}
int psync_sql_islocked(){
return psync_rwlock_holding_lock(&psync_db_lock);
}
int psync_sql_tryupgradelock(){
#if IS_DEBUG
if (psync_rwlock_holding_wrlock(&psync_db_lock))
return 0;
assert(psync_rwlock_holding_rdlock(&psync_db_lock));
if (psync_rwlock_towrlock(&psync_db_lock))
return -1;
else{
rd_lock_data *lock=record_rdunlock();
sqllockcnt=sqlrdlockcnt;
sqlrdlockcnt=0;
assert(sqllockcnt==1);
sqllockstart=sqlrdlockstart;
record_wrlock(lock->file, lock->line);
psync_free(lock);
return 0;
}
#else
return psync_rwlock_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)){
debug(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
debug(D_ERROR, "error running sql statement: %s: %s called from %s:%u", sql, errmsg, file, line);
#else
debug(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
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);
psync_free(cb);
}
}
int psync_sql_commit_transaction(){
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
debug(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");
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;
assert(in_transaction);
cb=psync_new(tran_callback_t);
cb->commit_callback=commit_callback;
cb->rollback_callback=rollback_callback;
cb->ptr=ptr;
psync_list_add_tail(&tran_callbacks, &cb->list);
}
#if IS_DEBUG && 0
typedef struct {
psync_tree tree;
const char *sql;
} psync_sql_tree_t;
static psync_tree *sql_tree=PSYNC_TREE_EMPTY;
static void psync_sql_do_check_query_plan(const char *sql){
sqlite3_stmt *stmt;
char *exsql;
const char *detail;
int code;
exsql=psync_strcat("EXPLAIN QUERY PLAN ", sql, NULL);
code=sqlite3_prepare_v2(psync_db, exsql, -1, &stmt, 0);
psync_free(exsql);
if (code!=SQLITE_OK){
debug(D_ERROR, "EXPLAIN QUERY PLAN %s returned error: %d", sql, code);
return;
}
while (sqlite3_step(stmt)==SQLITE_ROW){
detail=(const char *)sqlite3_column_text(stmt, 3);
if (!strncmp(detail, "SCAN TABLE", strlen("SCAN TABLE")))
debug(D_WARNING, "doing %s on sql %s", detail, sql);
}
sqlite3_finalize(stmt);
}
static psync_tree *psync_sql_new_tree_node(const char *sql){
psync_sql_tree_t *node;
node=psync_new(psync_sql_tree_t);
node->sql=sql;
return &node->tree;
}
static void psync_sql_check_query_plan_locked(const char *sql){
psync_tree *node;
int cmp;
if (!sql_tree){
psync_tree_add_after(&sql_tree, NULL, psync_sql_new_tree_node(sql));
return;
}
node=sql_tree;
while (1){
cmp=strcmp(sql, psync_tree_element(node, psync_sql_tree_t, tree)->sql);
if (cmp<0){
if (node->left)
node=node->left;
else{
psync_tree_add_before(&sql_tree, node, psync_sql_new_tree_node(sql));
break;
}
}
else if (cmp>0){
if (node->right)
node=node->right;
else{
psync_tree_add_after(&sql_tree, node, psync_sql_new_tree_node(sql));
break;
}
}
else
return;
}
psync_sql_do_check_query_plan(sql);
}
static void psync_sql_check_query_plan(const char *sql){
psync_sql_lock();
psync_sql_check_query_plan_locked(sql);
psync_sql_unlock();
}
#else
#define psync_sql_check_query_plan(s) ((void)0)
#endif
char *psync_sql_cellstr(const char *sql){
sqlite3_stmt *stmt;
int code;
psync_sql_check_query_plan(sql);
psync_sql_rdlock();
code=sqlite3_prepare_v2(psync_db, sql, -1, &stmt, NULL);
if (unlikely(code!=SQLITE_OK)){
psync_sql_rdunlock();
debug(D_ERROR, "error running sql statement: %s: %s", sql, sqlite3_errmsg(psync_db));
sendtdebug("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)){
debug(D_ERROR, "sqlite3_step returned error: %s: %s", sql, sqlite3_errmsg(psync_db));
sendtdebug("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_check_query_plan(sql);
psync_sql_rdlock();
code=sqlite3_prepare_v2(psync_db, sql, -1, &stmt, NULL);
if (unlikely(code!=SQLITE_OK)){
debug(D_ERROR, "error running sql statement: %s: %s", sql, sqlite3_errmsg(psync_db));
sendtdebug("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)){
debug(D_ERROR, "sqlite3_step returned error: %s: %s", sql, sqlite3_errmsg(psync_db));
sendtdebug("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_check_query_plan(sql);
psync_sql_rdlock();
code=sqlite3_prepare_v2(psync_db, sql, -1, &stmt, NULL);
if (unlikely(code!=SQLITE_OK)){
psync_sql_rdunlock();
debug(D_ERROR, "error running sql statement: %s: %s", sql, sqlite3_errmsg(psync_db));
sendtdebug("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;
psync_def_var_arr(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 **)psync_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)){
debug(D_ERROR, "sqlite3_step returned error: %s: %s", sql, sqlite3_errmsg(psync_db));
sendtdebug("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_check_query_plan(sql);
psync_sql_rdlock();
code=sqlite3_prepare_v2(psync_db, sql, -1, &stmt, NULL);
if (unlikely(code!=SQLITE_OK)){
psync_sql_rdunlock();
debug(D_ERROR, "error running sql statement: %s: %s", sql, sqlite3_errmsg(psync_db));
sendtdebug("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;
psync_def_var_arr(lens, size_t, cnt);
int i, t;
psync_def_var_arr(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 *)psync_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)){
debug(D_ERROR, "sqlite3_step returned error: %s: %s", sql, sqlite3_errmsg(psync_db));
sendtdebug("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;
psync_sql_check_query_plan(sql);
#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
debug(D_ERROR, "error running sql statement: %s: %s called from %s:%u", sql, sqlite3_errmsg(psync_db), file, line);
senddebug("error running sql statement: %s: %s called from %s:%u", sql, sqlite3_errmsg(psync_db), file, line);
#else
debug(D_ERROR, "error running sql statement: %s: %s", sql, sqlite3_errmsg(psync_db));
senddebug("error running sql statement: %s: %s", sql, sqlite3_errmsg(psync_db));
#endif
return NULL;
}
cnt=sqlite3_column_count(stmt);
res=(psync_sql_res *)psync_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 *)psync_cache_get(sql);
if (ret){
// debug(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;
psync_sql_check_query_plan(sql);
#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
debug(D_ERROR, "error running sql statement: %s: %s called from %s:%u", sql, sqlite3_errmsg(psync_db), file, line);
senddebug("error running sql statement: %s: %s called from %s:%u", sql, sqlite3_errmsg(psync_db), file, line);
#else
debug(D_ERROR, "error running sql statement: %s: %s", sql, sqlite3_errmsg(psync_db));
senddebug("error running sql statement: %s: %s", sql, sqlite3_errmsg(psync_db));
#endif
return NULL;
}
cnt=sqlite3_column_count(stmt);
res=(psync_sql_res *)psync_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 *)psync_cache_get(sql);
if (ret){
// debug(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()){
debug(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);
senddebug("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
psync_sql_check_query_plan(sql);
code=sqlite3_prepare_v2(psync_db, sql, -1, &stmt, NULL);
if (unlikely(code!=SQLITE_OK)){
debug(D_ERROR, "error running sql statement: %s: %s", sql, sqlite3_errmsg(psync_db));
senddebug("error running sql statement: %s: %s", sql, sqlite3_errmsg(psync_db));
return NULL;
}
cnt=sqlite3_column_count(stmt);
res=(psync_sql_res *)psync_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()){
debug(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);
senddebug("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 *)psync_cache_get(sql);
if (ret){
// debug(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
psync_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:
debug(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)
psync_cache_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
psync_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;
psync_sql_check_query_plan(sql);
#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
debug(D_ERROR, "error running sql statement: %s: %s called from %s:%u", sql, sqlite3_errmsg(psync_db), file, line);
senddebug("error running sql statement: %s: %s called from %s:%u", sql, sqlite3_errmsg(psync_db), file, line);
#else
debug(D_ERROR, "error running sql statement: %s: %s", sql, sqlite3_errmsg(psync_db));
senddebug("error running sql statement: %s: %s", sql, sqlite3_errmsg(psync_db));
#endif
return NULL;
}
res=psync_new(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=psync_cache_get(sql);
if (ret){
// debug(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)){
debug(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)){
debug(D_ERROR, "sqlite3_step returned error: %s: %s", sqlite3_errmsg(psync_db), res->sql);
sendtdebug("sqlite3_step returned error (in_transaction=%d): %s: %s", in_transaction, sqlite3_errmsg(psync_db), res->sql);
transaction_failed=1;
if (in_transaction)
debug(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))
debug(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)){
debug(D_ERROR, "sqlite3_step returned error: %s: %s", sqlite3_errmsg(psync_db), res->sql);
sendtdebug("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)
debug(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);
psync_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)){
debug(D_ERROR, "sqlite3_step returned error: %s: %s", sqlite3_errmsg(psync_db), res->sql);
sendtdebug("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)
debug(D_BUG, "transaction query failed, this may lead to restarting transaction over and over");
psync_sql_res_unlock(res);
psync_free(res);
return -1;
}
else{
psync_sql_res_unlock(res);
psync_cache_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))
debug(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))
debug(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))
debug(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))
debug(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))
debug(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))
debug(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))
debug(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))
debug(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))
debug(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))
debug(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;
psync_uint_t 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 *)psync_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))
debug(D_ERROR, "sqlite3_step returned error: %s", sqlite3_errmsg(psync_db));
psync_sql_free_result(res);
ret=(psync_full_result_int *)psync_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);
psync_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;
size_t plen, dotidx;
psync_stat_t st;
psync_int_t num, l;
plen=strlen(path);
dotidx=plen;
while (dotidx && path[dotidx]!='.')
dotidx--;
if (!dotidx)
dotidx=plen;
npath=(char *)psync_malloc(plen+32);
memcpy(npath, path, dotidx);
num=0;
while (1){
if (num)
l=psync_slprintf(npath+dotidx, 32, " (conflicted %"P_PRI_I")", num);
else{
l=13;
memcpy(npath+dotidx, " (conflicted)", l);
}
memcpy(npath+dotidx+l, path+dotidx, plen-dotidx+1);
if (psync_stat(npath, &st)){
debug(D_NOTICE, "renaming conflict %s to %s", path, npath);
l=psync_file_rename(path, npath);
psync_free(npath);
return l;
}
num++;
}
}
void psync_libs_init(){
psync_uint_t i;
for (i=0; i<256; i++)
normalize_table[i]=i;
normalize_table[':']='_';
normalize_table['/']='_';
normalize_table['\\']='_';
}
static void run_after_sec(psync_timer_t timer, void *ptr){
struct run_after_ptr *fp=(struct run_after_ptr *)ptr;
psync_timer_stop(timer);
fp->run(fp->ptr);
psync_free(fp);
}
void psync_run_after_sec(psync_run_after_t run, void *ptr, uint32_t seconds){
struct run_after_ptr *fp;
fp=psync_new(struct run_after_ptr);
fp->run=run;
fp->ptr=ptr;
psync_timer_register(run_after_sec, seconds, fp);
}
static void free_after_sec(psync_timer_t timer, void *ptr){
psync_timer_stop(timer);
psync_free(ptr);
}
void psync_free_after_sec(void *ptr, uint32_t seconds){
psync_timer_register(free_after_sec, seconds, ptr);
}
int psync_match_pattern(const char *name, const char *pattern, size_t plen){
size_t i;
for (i=0; i<plen; i++){
if (pattern[i]=='*'){
name+=i;
while (1){
if (++i==plen)
return 1;
switch (pattern[i]){
case '?':
if (!*name++)
return 0;
case '*':
break;
default:
name=strchr(name, pattern[i]);
pattern+=i+1;
plen-=i+1;
while (name){
name++;
if (psync_match_pattern(name, pattern, plen))
return 1;
name=strchr(name, *(pattern-1));
}
return 0;
}
}
}
else if (!name[i] || (pattern[i]!=name[i] && pattern[i]!='?'))
return 0;
}
return name[i]==0;
}
uint64_t psync_ato64(const char *str){
uint64_t n=0;
while (*str>='0' && *str<='9')
n=n*10+(*str++)-'0';
return n;
}
uint32_t psync_ato32(const char *str){
uint32_t n=0;
while (*str>='0' && *str<='9')
n=n*10+(*str++)-'0';
return n;
}
typedef struct {
psync_list list;
psync_uint_t used;
char elements[];
} psync_list_element_list;
typedef struct {
psync_list list;
char *next;
char *end;
} psync_list_string_list;
typedef struct {
psync_list list;
psync_uint_t used;
uint32_t numbers[1000];
} psync_list_num_list;
struct psync_list_builder_t_{
size_t element_size;
size_t elements_offset;
size_t elements_per_list;
size_t stringalloc;
uint64_t cnt;
psync_list element_list;
psync_list_element_list *last_elements;
psync_list string_list;
psync_list_string_list *last_strings;
psync_list number_list;
psync_list_num_list *last_numbers;
psync_uint_t popoff;
char *current_element;
uint32_t *cstrcnt;
};
psync_list_builder_t *psync_list_builder_create(size_t element_size, size_t offset){
psync_list_builder_t *builder;
builder=psync_new(psync_list_builder_t);
builder->element_size=element_size;
builder->elements_offset=offset;
if (element_size<=200)
builder->elements_per_list=40;
else
builder->elements_per_list=12;
builder->cnt=0;
builder->stringalloc=0;
psync_list_init(&builder->element_list);
builder->last_elements=NULL;
psync_list_init(&builder->string_list);
builder->last_strings=NULL;
psync_list_init(&builder->number_list);
builder->last_numbers=NULL;
return builder;
}
static uint32_t *psync_list_bulder_push_num(psync_list_builder_t *builder){
if (!builder->last_numbers || builder->last_numbers->used>=sizeof(builder->last_numbers->numbers)/sizeof(uint32_t)){
psync_list_num_list *l=psync_new(psync_list_num_list);
l->used=0;
builder->last_numbers=l;
psync_list_add_tail(&builder->number_list, &l->list);
}
return &builder->last_numbers->numbers[builder->last_numbers->used++];
}
static uint32_t psync_list_bulder_pop_num(psync_list_builder_t *builder){
uint32_t ret;
ret=builder->last_numbers->numbers[builder->popoff++];
if (builder->popoff>=builder->last_numbers->used){
builder->last_numbers=psync_list_element(builder->last_numbers->list.next, psync_list_num_list, list);
builder->popoff=0;
}
return ret;
}
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 *)psync_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_list_bulder_add_element(psync_list_builder_t *builder){
if (!builder->last_elements || builder->last_elements->used>=builder->elements_per_list){
builder->last_elements=(psync_list_element_list *)psync_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;
builder->last_elements->used++;
builder->cnt++;
return builder->current_element;
}
void psync_list_add_lstring_offset(psync_list_builder_t *builder, size_t offset, size_t length){
char **str, *s;
psync_list_string_list *l;
length++;
str=(char **)(builder->current_element+offset);
builder->stringalloc+=length;
if (unlikely(length>2000)){
l=(psync_list_string_list *)psync_malloc(sizeof(psync_list_string_list)+length);
s=(char *)(l+1);
psync_list_add_tail(&builder->string_list, &l->list);
}
else if (!builder->last_strings || builder->last_strings->next+length>builder->last_strings->end){
l=(psync_list_string_list *)psync_malloc(sizeof(psync_list_string_list)+4000);
s=(char *)(l+1);
l->next=s+length;
l->end=s+4000;
psync_list_add_tail(&builder->string_list, &l->list);
builder->last_strings=l;
}
else {
s=builder->last_strings->next;
builder->last_strings->next+=length;
}
memcpy(s, *str, length);
*str=s;
*(psync_list_bulder_push_num(builder))=offset;
*(psync_list_bulder_push_num(builder))=length;
(*builder->cstrcnt)++;
}
void psync_list_add_string_offset(psync_list_builder_t *builder, size_t offset){
psync_list_add_lstring_offset(builder, offset, strlen(*((char **)(builder->current_element+offset))));
}
void *psync_list_builder_finalize(psync_list_builder_t *builder){
char *ret, *elem, *str;
char **pstr;
psync_list_element_list *el;
psync_uint_t i;
uint32_t j, scnt, offset, length;
size_t sz;
sz=builder->elements_offset+builder->element_size*builder->cnt+builder->stringalloc;
debug(D_NOTICE, "allocating %lu bytes, %lu of which for strings", (unsigned long)sz, (unsigned long)builder->stringalloc);
ret=psync_new_cnt(char, sz);
if (builder->elements_offset<=sizeof(builder->cnt))
memcpy(ret, &builder->cnt, builder->elements_offset);
else
memcpy(ret, &builder->cnt, sizeof(builder->cnt));
elem=ret+builder->elements_offset;
str=elem+builder->element_size*builder->cnt;
builder->last_numbers=psync_list_element(builder->number_list.next, psync_list_num_list, list);
builder->popoff=0;
psync_list_for_each_element(el, &builder->element_list, psync_list_element_list, list){
for (i=0; i<el->used; i++){
memcpy(elem, el->elements+(i*builder->element_size), builder->element_size);
scnt=psync_list_bulder_pop_num(builder);
for (j=0; j<scnt; j++){
offset=psync_list_bulder_pop_num(builder);
length=psync_list_bulder_pop_num(builder);
pstr=(char **)(elem+offset);
memcpy(str, *pstr, length);
*pstr=str;
str+=length;
}
elem+=builder->element_size;
}
}
psync_list_for_each_element_call(&builder->element_list, psync_list_element_list, list, psync_free);
psync_list_for_each_element_call(&builder->string_list, psync_list_string_list, list, psync_free);
psync_list_for_each_element_call(&builder->number_list, psync_list_num_list, list, psync_free);
psync_free(builder);
return ret;
}
#define PSYNC_TASK_STATUS_RUNNING 0
#define PSYNC_TASK_STATUS_READY 1
#define PSYNC_TASK_STATUS_DONE 2
#define PSYNC_TASK_STATUS_RETURNED 3
struct psync_task_t_{
psync_task_callback_t callback;
void *param;
pthread_cond_t cond;
int id;
int status;
};
#define PSYNC_WAIT_ANYBODY -1
#define PSYNC_WAIT_NOBODY -2
#define PSYNC_WAIT_FREED -3
struct psync_task_manager_t_{
pthread_mutex_t mutex;
int taskcnt;
int refcnt;
int waitfor;
struct psync_task_t_ tasks[];
};
static void psync_task_destroy(psync_task_manager_t tm){
int i;
for (i=0; i<tm->taskcnt; i++)
pthread_cond_destroy(&tm->tasks[i].cond);
pthread_mutex_destroy(&tm->mutex);
psync_free(tm);
}
static void psync_task_dec_refcnt(psync_task_manager_t tm){
int refcnt;
pthread_mutex_lock(&tm->mutex);
refcnt=--tm->refcnt;
pthread_mutex_unlock(&tm->mutex);
if (!refcnt)
psync_task_destroy(tm);
}
static psync_task_manager_t psync_get_manager_of_task(struct psync_task_t_ *t){
return (psync_task_manager_t)(((char *)(t-t->id))-offsetof(struct psync_task_manager_t_, tasks));
}
static void psync_task_entry(void *ptr){
struct psync_task_t_ *t;
t=(struct psync_task_t_*)ptr;
t->callback(ptr, t->param);
psync_task_dec_refcnt(psync_get_manager_of_task(t));
}
psync_task_manager_t psync_task_run_tasks(psync_task_callback_t const *callbacks, void *const *params, int cnt){
psync_task_manager_t ret;
struct psync_task_t_ *t;
int i;
ret=(psync_task_manager_t)psync_malloc(offsetof(struct psync_task_manager_t_, tasks)+sizeof(struct psync_task_t_)*cnt);
pthread_mutex_init(&ret->mutex, NULL);
ret->taskcnt=cnt;
ret->refcnt=cnt+1;
ret->waitfor=PSYNC_WAIT_NOBODY;
for (i=0; i<cnt; i++){
t=&ret->tasks[i];
t->callback=callbacks[i];
t->param=params[i];
pthread_cond_init(&t->cond, NULL);
t->id=i;
t->status=PSYNC_TASK_STATUS_RUNNING;
psync_run_thread1("task", psync_task_entry, t);
}
return ret;
}
void *psync_task_get_result(psync_task_manager_t tm, int id){
void *ret;
pthread_mutex_lock(&tm->mutex);
if (tm->tasks[id].status==PSYNC_TASK_STATUS_RUNNING){
do {
tm->waitfor=id;
pthread_cond_wait(&tm->tasks[id].cond, &tm->mutex);
tm->waitfor=PSYNC_WAIT_NOBODY;
} while (tm->tasks[id].status==PSYNC_TASK_STATUS_RUNNING);
ret=tm->tasks[id].param;
tm->tasks[id].status=PSYNC_TASK_STATUS_DONE;
}
else if (tm->tasks[id].status==PSYNC_TASK_STATUS_READY){
ret=tm->tasks[id].param;
tm->tasks[id].status=PSYNC_TASK_STATUS_DONE;
pthread_cond_signal(&tm->tasks[id].cond);
}
else{
debug(D_BUG, "invalid status %d of task id %d", (int)tm->tasks[id].status, id);
ret=NULL;
}
pthread_mutex_unlock(&tm->mutex);
return ret;
}
void psync_task_free(psync_task_manager_t tm){
if (tm->refcnt==1)
psync_task_destroy(tm);
else{
int refcnt, i;
pthread_mutex_lock(&tm->mutex);
tm->waitfor=PSYNC_WAIT_FREED;
for (i=0; i<tm->taskcnt; i++)
if (tm->tasks[i].status==PSYNC_TASK_STATUS_READY){
tm->tasks[i].status=PSYNC_TASK_STATUS_RETURNED;
pthread_cond_signal(&tm->tasks[i].cond);
}
refcnt=--tm->refcnt;
pthread_mutex_unlock(&tm->mutex);
if (!refcnt)
psync_task_destroy(tm);
}
}
int psync_task_complete(void *h, void *data){
psync_task_manager_t tm;
struct psync_task_t_ *t;
int ret;
t=(struct psync_task_t_ *)h;
tm=psync_get_manager_of_task(t);
pthread_mutex_lock(&tm->mutex);
if (tm->waitfor==t->id){
t->param=data;
t->status=PSYNC_TASK_STATUS_READY;
pthread_cond_signal(&t->cond);
ret=0;
}
else if (tm->waitfor==PSYNC_WAIT_NOBODY || tm->waitfor>=0){
t->param=data;
t->status=PSYNC_TASK_STATUS_READY;
do {
pthread_cond_wait(&t->cond, &tm->mutex);
} while (t->status==PSYNC_TASK_STATUS_READY);
if (t->status==PSYNC_TASK_STATUS_RETURNED)
ret=-1;
else
ret=0;
}
else if (tm->waitfor==PSYNC_WAIT_FREED)
ret=-1;
else{
debug(D_BUG, "invalid waitfor value %d", tm->waitfor);
ret=-1;
}
pthread_mutex_unlock(&tm->mutex);
return ret;
}
#define rot(x,k) (((x)<<(k))|((x)>>(32-(k))))
static uint32_t pq_rnd() {
static uint32_t a=0x95ae3d25, b=0xe225d755, c=0xc63a2ae7, d=0xe4556265;
uint32_t e=a-rot(b, 27);
a=b^rot(c, 17);
b=c+d;
c=d+e;
d=e+a;
return d;
}
#define QSORT_TRESH 8
#define QSORT_MTR 64
#define QSORT_REC_M (16*1024)
static inline void sw2(unsigned char **a, unsigned char **b) {
unsigned char *tmp=*a;
*a=*b;
*b=tmp;
}
static unsigned char *med5(unsigned char *a, unsigned char *b, unsigned char *c, unsigned char *d, unsigned char *e,
int (*compar)(const void *, const void *)) {
if (compar(b, a)<0)
sw2(&a, &b);
if (compar(d, c)<0)
sw2(&c, &d);
if (compar(a, c)<0) {
a=e;
if (compar(b, a)<0)
sw2(&a, &b);
} else {
c=e;
if (compar(d, c)<0)
sw2(&c, &d);
}
if (compar(a, c)<0)
a=b;
else
c=d;
if (compar(a, c)<0)
return a;
else
return c;
}
unsigned char *pq_choose_part(unsigned char *base, size_t cnt, size_t size, int (*compar)(const void *, const void *)) {
if (cnt>=QSORT_REC_M) {
cnt/=5;
return med5(pq_choose_part(base, cnt, size, compar),
pq_choose_part(base+cnt*size, cnt, size, compar),
pq_choose_part(base+cnt*size*2, cnt, size, compar),
pq_choose_part(base+cnt*size*3, cnt, size, compar),
pq_choose_part(base+cnt*size*4, cnt, size, compar),
compar);
} else {
return med5(base+(pq_rnd()%cnt)*size, base+(pq_rnd()%cnt)*size, base+(pq_rnd()%cnt)*size, base+(pq_rnd()%cnt)*size, base+(pq_rnd()%cnt)*size, compar);
}
}
static inline void pqsswap(unsigned char *a, unsigned char *b, size_t size) {
unsigned char tmp;
do {
tmp=*a;
*a++=*b;
*b++=tmp;
} while (--size);
}
static inline void pqsswap32(unsigned char *a, unsigned char *b, size_t size) {
uint32_t tmp;
do {
tmp=*(uint32_t *)a;
*(uint32_t *)a=*(uint32_t *)b;
*(uint32_t *)b=tmp;
a+=sizeof(uint32_t);
b+=sizeof(uint32_t);
} while (--size);
}
typedef struct {
unsigned char *lo;
unsigned char *hi;
} psq_stack_t;
void psync_pqsort(void *base, size_t cnt, size_t sort_first, size_t size, int (*compar)(const void *, const void *)) {
psq_stack_t stack[sizeof(size_t)*8];
psq_stack_t *top;
unsigned char *lo, *hi, *mid, *l, *r, *sf;
size_t tresh, n, u32size;
tresh=QSORT_TRESH*size;
sf=(unsigned char *)base+sort_first*size;
if (size%sizeof(uint32_t)==0 && (uintptr_t)base%sizeof(uint32_t)==0)
u32size=size/sizeof(uint32_t);
else
u32size=0;
if (cnt>QSORT_TRESH) {
top=stack+1;
lo=(unsigned char *)base;
hi=lo+(cnt-1)*size;
do {
n=(hi-lo)/size;
if (n<=QSORT_MTR) {
mid=lo+(n>>1)*size;
if (compar(mid, lo)<0)
pqsswap(mid, lo, size);
if (compar(hi, mid)<0) {
pqsswap(mid, hi, size);
if (compar(mid, lo)<0)
pqsswap(mid, lo, size);
}
// we already sure *hi and *lo are good, so they will be skipped without checking
l=lo;
r=hi;
} else {
mid=pq_choose_part(lo, n, size, compar);
l=lo-size;
r=hi+size;
}
if (u32size) {
do {
do {
l+=size;
} while (compar(l, mid)<0);
do {
r-=size;
} while (compar(mid, r)<0);
if (l>=r)
break;
pqsswap32(l, r, u32size);
if (mid==l) {
mid=r;
r+=size;
} else if (mid==r) {
mid=l;
l-=size;
}
} while (1);
} else {
do {
do {
l+=size;
} while (compar(l, mid)<0);
do {
r-=size;
} while (compar(mid, r)<0);
if (l>=r)
break;
pqsswap(l, r, size);
if (mid==l) {
mid=r;
r+=size;
} else if (mid==r) {
mid=l;
l-=size;
}
} while (1);
}
if (hi-mid<=tresh || mid>=sf) {
if (mid-lo<=tresh) {
top--;
lo=top->lo;
hi=top->hi;
} else {
hi=mid-size;
}
} else if (mid-lo<=tresh) {
lo=mid+size;
} else if (hi-mid<mid-lo) {
top->lo=lo;
top->hi=mid-size;
top++;
lo=mid+size;
} else {
top->lo=mid+size;
top->hi=hi;
top++;
hi=mid-size;
}
} while (top!=stack);
} else if (cnt<=1) {
return;
}
lo=(unsigned char *)base;
hi=lo+(cnt-1)*size;
sf+=size*QSORT_TRESH;
if (sf<hi)
hi=sf;
r=lo+QSORT_TRESH*size+4;
if (r>hi)
r=hi;
for (l=lo+size; l<=r; l+=size)
if (compar(l, lo)<0)
lo=l;
pqsswap((unsigned char *)base, lo, size);
l=(unsigned char *)base+size;
hi-=size;
while (l<=hi) {
lo=l;
l+=size;
while (compar(l, lo)<0)
lo-=size;
lo+=size;
if (lo!=l) {
unsigned char *t=l+size;
if (u32size) {
while ((t-=sizeof(uint32_t))>=l) {
uint32_t tmp=*(uint32_t *)t;
for (r=mid=t; (mid-=size)>=lo; r=mid)
*(uint32_t *)r=*(uint32_t *)mid;
*(uint32_t *)r=tmp;
}
} else {
while (--t>=l) {
unsigned char tmp=*t;
for (r=mid=t; (mid-=size)>=lo; r=mid)
*r=*mid;
*r=tmp;
}
}
}
}
}
void psync_qpartition(void *base, size_t cnt, size_t sort_first, size_t size, int (*compar)(const void *, const void *)) {
unsigned char *lo, *hi, *mid, *l, *r, *sf;
size_t n, u32size;
sf=(unsigned char *)base+sort_first*size;
if (size%sizeof(uint32_t)==0 && (uintptr_t)base%sizeof(uint32_t)==0)
u32size=size/sizeof(uint32_t);
else
u32size=0;
if (cnt<=1) // otherwise cnt-1 will underflow
return;
lo=(unsigned char *)base;
hi=lo+(cnt-1)*size;
while (1) {
n=(hi-lo)/size;
if (n<=QSORT_MTR) {
mid=lo+(n>>1)*size;
if (compar(mid, lo)<0)
pqsswap(mid, lo, size);
if (compar(hi, mid)<0) {
pqsswap(mid, hi, size);
if (compar(mid, lo)<0)
pqsswap(mid, lo, size);
}
// we already sure *hi and *lo are good, so they will be skipped without checking
if (n<=2) // when n is 2, we have 3 elements
return;
l=lo;
r=hi;
} else {
mid=pq_choose_part(lo, n, size, compar);
l=lo-size;
r=hi+size;
}
if (u32size) {
do {
do {
l+=size;
} while (compar(l, mid)<0);
do {
r-=size;
} while (compar(mid, r)<0);
if (l>=r)
break;
pqsswap32(l, r, u32size);
if (mid==l) {
mid=r;
r+=size;
} else if (mid==r) {
mid=l;
l-=size;
}
} while (1);
} else {
do {
do {
l+=size;
} while (compar(l, mid)<0);
do {
r-=size;
} while (compar(mid, r)<0);
if (l>=r)
break;
pqsswap(l, r, size);
if (mid==l) {
mid=r;
r+=size;
} else if (mid==r) {
mid=l;
l-=size;
}
} while (1);
}
if (mid<sf)
lo=mid+size;
else if (mid>sf)
hi=mid-size;
else
return;
}
}
void psync_try_free_memory(){
sqlite3_db_release_memory(psync_db);
psync_cache_clean_all();
}
static void time_format(time_t tm, unsigned long ns, char *result){
static const char month_names[12][4]={"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
static const char day_names[7][4] ={"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
struct tm dt;
psync_uint_t y;
ns/=1000000;
gmtime_r(&tm, &dt);
memcpy(result, day_names[dt.tm_wday], 3);
result+=3;
*result++=',';
*result++=' ';
*result++=dt.tm_mday/10+'0';
*result++=dt.tm_mday%10+'0';
*result++=' ';
memcpy(result, month_names[dt.tm_mon], 3);
result+=3;
*result++=' ';
y=dt.tm_year+1900;
*result++='0'+y/1000;
y=y%1000;
*result++='0'+y/100;
y=y%100;
*result++='0'+y/10;
y=y%10;
*result++='0'+y;
*result++=' ';
*result++=dt.tm_hour/10+'0';
*result++=dt.tm_hour%10+'0';
*result++=':';
*result++=dt.tm_min/10+'0';
*result++=dt.tm_min%10+'0';
*result++=':';
*result++=dt.tm_sec/10+'0';
*result++=dt.tm_sec%10+'0';
*result++='.';
*result++=ns/100+'0';
*result++=(ns/10)%10+'0';
*result++=ns%10+'0';
memcpy(result, " +0000", 7); // copies the null byte
}
int psync_debug(const char *file, const char *function, int unsigned line, int unsigned level, const char *fmt, ...){
static const struct {
psync_uint_t level;
const char *name;
} debug_levels[]=DEBUG_LEVELS;
static FILE *log=NULL;
struct timespec ts;
char dttime[36], format[512];
va_list ap;
const char *errname;
psync_uint_t i;
unsigned int u;
pthread_t threadid;
#if defined(P_OS_WINDOWS)
errname=strrchr(file, '\\');
if (errname)
file=errname+1;
#endif
errname="BAD_ERROR_CODE";
for (i=0; i<ARRAY_SIZE(debug_levels); i++)
if (debug_levels[i].level==level){
errname=debug_levels[i].name;
break;
}
if (unlikely(!log)){
log=fopen(DEBUG_FILE, "a+");
if (!log)
return 1;
}
psync_nanotime(&ts);
time_format(ts.tv_sec, ts.tv_nsec, dttime);
threadid=pthread_self();
memcpy(&u, &threadid, sizeof(u));
snprintf(format, sizeof(format), "%s %u %s %s: %s:%u (function %s): %s\n", dttime, u, psync_thread_name, errname, file, line, function, fmt);
format[sizeof(format)-1]=0;
va_start(ap, fmt);
vfprintf(log, format, ap);
va_end(ap);
fflush(log);
return 1;
}
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)
psync_debug(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)
psync_debug(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)
psync_debug(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)
psync_debug(file, function, line, D_CRITICAL, "type error, wanted %s got %s", get_type_name(PSYNC_TREAL), get_type_name(v->type));
return 0.0;
}