Template and prepared statement caching
This commit is contained in:
+139
-26
@@ -1,42 +1,155 @@
|
||||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "sqlite3utils.h"
|
||||
|
||||
int sqlite3utils_prepare_impl(sqlite3 *db, sqlite3_stmt **pstmt, const char *fmt, VArgs args)
|
||||
typedef struct {
|
||||
char *str;
|
||||
int len;
|
||||
sqlite3_stmt *stmt;
|
||||
} Prepped;
|
||||
|
||||
struct SQLiteCache {
|
||||
sqlite3 *db;
|
||||
int count;
|
||||
int capacity_log2;
|
||||
Prepped items[];
|
||||
};
|
||||
|
||||
SQLiteCache *sqlite_cache_init(sqlite3 *db, int capacity_log2)
|
||||
{
|
||||
SQLiteCache *cache = malloc(sizeof(SQLiteCache) + (1 << capacity_log2) * sizeof(Prepped));
|
||||
if (cache == NULL)
|
||||
return NULL;
|
||||
|
||||
cache->db = db;
|
||||
cache->count = 0;
|
||||
cache->capacity_log2 = capacity_log2;
|
||||
|
||||
for (int i = 0; i < (1 << capacity_log2); i++)
|
||||
cache->items[i].stmt = NULL;
|
||||
|
||||
return cache;
|
||||
}
|
||||
|
||||
void sqlite_cache_free(SQLiteCache *cache)
|
||||
{
|
||||
for (int i = 0; i < (1 << cache->capacity_log2); i++) {
|
||||
sqlite3_stmt *stmt = cache->items[i].stmt;
|
||||
if (stmt) {
|
||||
free(cache->items[i].str);
|
||||
sqlite3_finalize(stmt);
|
||||
}
|
||||
}
|
||||
free(cache);
|
||||
}
|
||||
|
||||
sqlite3 *sqlite_cache_getdb(SQLiteCache *cache)
|
||||
{
|
||||
return cache->db;
|
||||
}
|
||||
|
||||
static unsigned long djb2(char *src, int len)
|
||||
{
|
||||
char *ptr = src;
|
||||
char *end = src + len;
|
||||
|
||||
unsigned long hash = 5381;
|
||||
int c;
|
||||
while (ptr < end && (c = *ptr++))
|
||||
hash = ((hash << 5) + hash) + c; // hash * 33 + c
|
||||
return hash;
|
||||
}
|
||||
|
||||
static int lookup(SQLiteCache *cache, char *fmt, int fmtlen)
|
||||
{
|
||||
int mask = (1 << cache->capacity_log2) - 1;
|
||||
int hash = djb2(fmt, fmtlen);
|
||||
int i = hash & mask;
|
||||
int perturb = hash;
|
||||
for (;;) {
|
||||
|
||||
if (cache->items[i].stmt == NULL)
|
||||
return i;
|
||||
|
||||
if (cache->items[i].len == fmtlen && !memcmp(cache->items[i].str, fmt, fmtlen))
|
||||
return i;
|
||||
|
||||
perturb >>= 5;
|
||||
i = (i * 5 + 1 + perturb) & mask;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sqlite3utils_prepare(SQLiteCache *cache, sqlite3_stmt **pstmt, const char *fmt, int fmtlen)
|
||||
{
|
||||
if (fmtlen < 0)
|
||||
fmtlen = strlen(fmt);
|
||||
|
||||
int i = lookup(cache, fmt, fmtlen);
|
||||
if (cache->items[i].stmt == NULL) {
|
||||
|
||||
printf("Preparing statement [%.*s]\n", fmtlen, fmt); // TODO
|
||||
|
||||
sqlite3_stmt *stmt;
|
||||
int ret = sqlite3_prepare_v2(cache->db, fmt, -1, &stmt, NULL);
|
||||
if (ret != SQLITE_OK) {
|
||||
//fprintf(stderr, "Failed to prepare statement: %s (%s:%d)\n", sqlite3_errmsg(db), __FILE__, __LINE__);
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *cpy = malloc(fmtlen);
|
||||
if (cpy == NULL) {
|
||||
sqlite3_finalize(stmt);
|
||||
return SQLITE_NOMEM;
|
||||
}
|
||||
memcpy(cpy, fmt, fmtlen);
|
||||
|
||||
cache->items[i].str = cpy;
|
||||
cache->items[i].len = fmtlen;
|
||||
cache->items[i].stmt = stmt;
|
||||
}
|
||||
sqlite3_stmt *stmt = cache->items[i].stmt;
|
||||
|
||||
*pstmt = stmt;
|
||||
return SQLITE_OK;
|
||||
}
|
||||
|
||||
int sqlite3utils_prepare_and_bind_impl(SQLiteCache *cache,
|
||||
sqlite3_stmt **pstmt, const char *fmt, VArgs args)
|
||||
{
|
||||
sqlite3_stmt *stmt;
|
||||
int ret = sqlite3_prepare_v2(db, fmt, -1, &stmt, NULL);
|
||||
if (ret != SQLITE_OK) {
|
||||
fprintf(stderr, "Failed to prepare statement: %s (%s:%d)\n", sqlite3_errmsg(db), __FILE__, __LINE__);
|
||||
int ret = sqlite3utils_prepare(cache, &stmt, fmt, strlen(fmt));
|
||||
if (ret != SQLITE_OK)
|
||||
return ret;
|
||||
}
|
||||
|
||||
for (int i = 0; i < args.len; i++) {
|
||||
VArg arg = args.ptr[i];
|
||||
switch (arg.type) {
|
||||
case VARG_TYPE_C : ret = sqlite3_bind_text(stmt, i+1, &arg.c, 1, NULL); break;
|
||||
case VARG_TYPE_S : ret = sqlite3_bind_int(stmt, i+1, arg.s); break;
|
||||
case VARG_TYPE_I : ret = sqlite3_bind_int(stmt, i+1, arg.i); break;
|
||||
case VARG_TYPE_L : ret = sqlite3_bind_int64(stmt, i+1, arg.l); break;
|
||||
case VARG_TYPE_LL : ret = sqlite3_bind_int64(stmt, i+1, arg.ll); break;
|
||||
case VARG_TYPE_SC : ret = sqlite3_bind_int(stmt, i+1, arg.sc); break;
|
||||
case VARG_TYPE_SS : ret = sqlite3_bind_int(stmt, i+1, arg.ss); break;
|
||||
case VARG_TYPE_SI : ret = sqlite3_bind_int(stmt, i+1, arg.si); break;
|
||||
case VARG_TYPE_SL : ret = sqlite3_bind_int64(stmt, i+1, arg.sl); break;
|
||||
case VARG_TYPE_SLL: ret = sqlite3_bind_int(stmt, i+1, arg.sll); break;
|
||||
case VARG_TYPE_UC : ret = sqlite3_bind_int(stmt, i+1, arg.uc); break;
|
||||
case VARG_TYPE_US : ret = sqlite3_bind_int(stmt, i+1, arg.us); break;
|
||||
case VARG_TYPE_UI : ret = sqlite3_bind_int64(stmt, i+1, arg.ui); break;
|
||||
case VARG_TYPE_UL : ret = sqlite3_bind_int64(stmt, i+1, arg.ul); break;
|
||||
case VARG_TYPE_ULL: ret = sqlite3_bind_int64(stmt, i+1, arg.ull); break;
|
||||
case VARG_TYPE_F : ret = sqlite3_bind_double(stmt, i+1, arg.f); break;
|
||||
case VARG_TYPE_D : ret = sqlite3_bind_double(stmt, i+1, arg.d); break;
|
||||
case VARG_TYPE_B : ret = sqlite3_bind_int(stmt, i+1, arg.b); break;
|
||||
case VARG_TYPE_STR: ret = sqlite3_bind_text(stmt, i+1, arg.str.ptr, arg.str.len, NULL); break;
|
||||
case VARG_TYPE_C : ret = sqlite3_bind_text (stmt, i+1, &arg.c, 1, NULL); break;
|
||||
case VARG_TYPE_S : ret = sqlite3_bind_int (stmt, i+1, arg.s); break;
|
||||
case VARG_TYPE_I : ret = sqlite3_bind_int (stmt, i+1, arg.i); break;
|
||||
case VARG_TYPE_L : ret = sqlite3_bind_int64 (stmt, i+1, arg.l); break;
|
||||
case VARG_TYPE_LL : ret = sqlite3_bind_int64 (stmt, i+1, arg.ll); break;
|
||||
case VARG_TYPE_SC : ret = sqlite3_bind_int (stmt, i+1, arg.sc); break;
|
||||
case VARG_TYPE_SS : ret = sqlite3_bind_int (stmt, i+1, arg.ss); break;
|
||||
case VARG_TYPE_SI : ret = sqlite3_bind_int (stmt, i+1, arg.si); break;
|
||||
case VARG_TYPE_SL : ret = sqlite3_bind_int64 (stmt, i+1, arg.sl); break;
|
||||
case VARG_TYPE_SLL: ret = sqlite3_bind_int (stmt, i+1, arg.sll); break;
|
||||
case VARG_TYPE_UC : ret = sqlite3_bind_int (stmt, i+1, arg.uc); break;
|
||||
case VARG_TYPE_US : ret = sqlite3_bind_int (stmt, i+1, arg.us); break;
|
||||
case VARG_TYPE_UI : ret = sqlite3_bind_int64 (stmt, i+1, arg.ui); break;
|
||||
case VARG_TYPE_UL : ret = sqlite3_bind_int64 (stmt, i+1, arg.ul); break;
|
||||
case VARG_TYPE_ULL: ret = sqlite3_bind_int64 (stmt, i+1, arg.ull); break;
|
||||
case VARG_TYPE_F : ret = sqlite3_bind_double(stmt, i+1, arg.f); break;
|
||||
case VARG_TYPE_D : ret = sqlite3_bind_double(stmt, i+1, arg.d); break;
|
||||
case VARG_TYPE_B : ret = sqlite3_bind_int (stmt, i+1, arg.b); break;
|
||||
case VARG_TYPE_STR: ret = sqlite3_bind_text (stmt, i+1, arg.str.ptr, arg.str.len, NULL); break;
|
||||
}
|
||||
if (ret != SQLITE_OK) {
|
||||
fprintf(stderr, "Failed to prepare statement: %s (%s:%d)\n", sqlite3_errmsg(db), __FILE__, __LINE__);
|
||||
sqlite3_finalize(stmt);
|
||||
sqlite3_reset(stmt);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user