|
diff --git a/stash.c b/stash.c
|
| ... |
| 2 |
#include <sqlite3.h> |
2 |
#include <sqlite3.h> |
| 3 |
#include <time.h> |
3 |
#include <time.h> |
| 4 |
|
4 |
|
| 5 |
#define MAX_MEM_DBS 16 |
|
|
| 6 |
|
|
|
| 7 |
typedef struct { |
5 |
typedef struct { |
| 8 |
sqlite3 *db; |
6 |
sqlite3 *db; |
| 9 |
ev_timer cleanup_watcher; |
7 |
ev_timer cleanup_watcher; |
| 10 |
int interval; |
8 |
int interval; |
| 11 |
int id; |
|
|
| 12 |
} MemDB; |
9 |
} MemDB; |
| 13 |
|
10 |
|
| 14 |
static MemDB mem_dbs[MAX_MEM_DBS]; |
11 |
static MemDB mdb = {0}; |
| 15 |
static int current_db_idx = 0; |
|
|
| 16 |
|
12 |
|
| 17 |
static void cleanup_cb(struct ev_loop *loop, ev_timer *w, int revents) { |
13 |
static void cleanup_cb(struct ev_loop *loop, ev_timer *w, int revents) { |
| 18 |
MemDB *mdb = (MemDB *)w->data; |
14 |
if (!mdb.db) { |
| 19 |
if (!mdb->db) { |
|
|
| 20 |
return; |
15 |
return; |
| 21 |
} |
16 |
} |
| 22 |
|
17 |
|
| 23 |
sqlite3_stmt *stmt; |
18 |
sqlite3_stmt *stmt; |
| 24 |
const char *sql = "DELETE FROM kv WHERE e > 0 AND e < ?;"; |
19 |
const char *sql = "DELETE FROM kv WHERE e > 0 AND e < ?;"; |
| 25 |
if (sqlite3_prepare_v2(mdb->db, sql, -1, &stmt, NULL) == SQLITE_OK) { |
20 |
if (sqlite3_prepare_v2(mdb.db, sql, -1, &stmt, NULL) == SQLITE_OK) { |
| 26 |
sqlite3_bind_int64(stmt, 1, (sqlite3_int64)time(NULL)); |
21 |
sqlite3_bind_int64(stmt, 1, (sqlite3_int64)time(NULL)); |
| 27 |
sqlite3_step(stmt); |
22 |
sqlite3_step(stmt); |
| 28 |
sqlite3_finalize(stmt); |
23 |
sqlite3_finalize(stmt); |
| 29 |
} |
24 |
} |
| 30 |
} |
25 |
} |
| 31 |
|
26 |
|
| 32 |
static int l_stash_use(lua_State *L) { |
27 |
static int check_init(lua_State *L) { |
| 33 |
int id = (int)luaL_checkinteger(L, 1); |
28 |
if (mdb.db) { |
| 34 |
int interval = (int)luaL_optinteger(L, 2, 60); |
29 |
return 0; |
|
|
30 |
} |
| 35 |
|
31 |
|
| 36 |
if (id < 0 || id >= MAX_MEM_DBS) { |
32 |
if (sqlite3_open(":memory:", &mdb.db) != SQLITE_OK) { |
| 37 |
return luaL_error(L, "invalid database id (0-%d)", MAX_MEM_DBS - 1); |
33 |
return luaL_error(L, "failed to open in-memory database: %s", sqlite3_errmsg(mdb.db)); |
| 38 |
} |
34 |
} |
| 39 |
|
35 |
|
| 40 |
current_db_idx = id; |
36 |
const char *schema = "CREATE TABLE IF NOT EXISTS kv (k TEXT PRIMARY KEY, v TEXT, e INTEGER);" |
| 41 |
MemDB *mdb = &mem_dbs[id]; |
37 |
"CREATE INDEX IF NOT EXISTS idx_expiry ON kv(e);"; |
|
|
38 |
if (sqlite3_exec(mdb.db, schema, NULL, NULL, NULL) != SQLITE_OK) { |
|
|
39 |
return luaL_error(L, "failed to create schema: %s", sqlite3_errmsg(mdb.db)); |
|
|
40 |
} |
| 42 |
|
41 |
|
| 43 |
if (!mdb->db) { |
42 |
if (mdb.interval <= 0) { |
| 44 |
if (sqlite3_open(":memory:", &mdb->db) != SQLITE_OK) { |
43 |
mdb.interval = 60; |
| 45 |
return luaL_error(L, "failed to open in-memory database: %s", sqlite3_errmsg(mdb->db)); |
44 |
} |
| 46 |
} |
|
|
| 47 |
|
45 |
|
| 48 |
const char *schema = "CREATE TABLE IF NOT EXISTS kv (k TEXT PRIMARY KEY, v TEXT, e INTEGER);" |
46 |
mdb.cleanup_watcher.data = &mdb; |
| 49 |
"CREATE INDEX IF NOT EXISTS idx_expiry ON kv(e);"; |
47 |
ev_timer_init(&mdb.cleanup_watcher, cleanup_cb, mdb.interval, mdb.interval); |
| 50 |
if (sqlite3_exec(mdb->db, schema, NULL, NULL, NULL) != SQLITE_OK) { |
48 |
ev_timer_start(EV_DEFAULT, &mdb.cleanup_watcher); |
| 51 |
return luaL_error(L, "failed to create schema: %s", sqlite3_errmsg(mdb->db)); |
|
|
| 52 |
} |
|
|
| 53 |
|
49 |
|
| 54 |
mdb->id = id; |
50 |
return 0; |
| 55 |
mdb->cleanup_watcher.data = mdb; |
51 |
} |
| 56 |
ev_timer_init(&mdb->cleanup_watcher, cleanup_cb, interval, interval); |
52 |
|
| 57 |
ev_timer_start(EV_DEFAULT, &mdb->cleanup_watcher); |
53 |
static int l_stash_cleanup(lua_State *L) { |
| 58 |
} else if (interval != mdb->interval) { |
54 |
int interval = (int)luaL_checkinteger(L, 1); |
| 59 |
ev_timer_stop(EV_DEFAULT, &mdb->cleanup_watcher); |
55 |
if (interval <= 0) { |
| 60 |
ev_timer_set(&mdb->cleanup_watcher, interval, interval); |
56 |
return luaL_error(L, "interval must be greater than 0"); |
| 61 |
ev_timer_start(EV_DEFAULT, &mdb->cleanup_watcher); |
|
|
| 62 |
} |
57 |
} |
| 63 |
|
58 |
|
| 64 |
mdb->interval = interval; |
59 |
mdb.interval = interval; |
|
|
60 |
|
|
|
61 |
if (mdb.db) { |
|
|
62 |
ev_timer_stop(EV_DEFAULT, &mdb.cleanup_watcher); |
|
|
63 |
ev_timer_set(&mdb.cleanup_watcher, interval, interval); |
|
|
64 |
ev_timer_start(EV_DEFAULT, &mdb.cleanup_watcher); |
|
|
65 |
} else { |
|
|
66 |
check_init(L); |
|
|
67 |
} |
|
|
68 |
|
| 65 |
return 0; |
69 |
return 0; |
| 66 |
} |
70 |
} |
| 67 |
|
71 |
|
| 68 |
static int l_stash_set(lua_State *L) { |
72 |
static int l_stash_set(lua_State *L) { |
|
|
73 |
check_init(L); |
| 69 |
const char *key = luaL_checkstring(L, 1); |
74 |
const char *key = luaL_checkstring(L, 1); |
| 70 |
int ttl = (int)luaL_optinteger(L, 3, 0); |
75 |
int ttl = (int)luaL_optinteger(L, 3, 0); |
| 71 |
|
76 |
|
| 72 |
MemDB *mdb = &mem_dbs[current_db_idx]; |
|
|
| 73 |
if (!mdb->db) { |
|
|
| 74 |
return luaL_error(L, "database not initialized, call stash.use(id) first"); |
|
|
| 75 |
} |
|
|
| 76 |
|
|
|
| 77 |
cJSON *json = lua_to_cjson(L, 2); |
77 |
cJSON *json = lua_to_cjson(L, 2); |
| 78 |
char *value_str = cJSON_PrintUnformatted(json); |
78 |
char *value_str = cJSON_PrintUnformatted(json); |
| 79 |
cJSON_Delete(json); |
79 |
cJSON_Delete(json); |
| 80 |
|
80 |
|
| 81 |
sqlite3_stmt *stmt; |
81 |
sqlite3_stmt *stmt; |
| 82 |
const char *sql = "INSERT OR REPLACE INTO kv (k, v, e) VALUES (?, ?, ?);"; |
82 |
const char *sql = "INSERT OR REPLACE INTO kv (k, v, e) VALUES (?, ?, ?);"; |
| 83 |
if (sqlite3_prepare_v2(mdb->db, sql, -1, &stmt, NULL) != SQLITE_OK) { |
83 |
if (sqlite3_prepare_v2(mdb.db, sql, -1, &stmt, NULL) != SQLITE_OK) { |
| 84 |
free(value_str); |
84 |
free(value_str); |
| 85 |
return luaL_error(L, "failed to prepare statement: %s", sqlite3_errmsg(mdb->db)); |
85 |
return luaL_error(L, "failed to prepare statement: %s", sqlite3_errmsg(mdb.db)); |
| 86 |
} |
86 |
} |
| 87 |
|
87 |
|
| 88 |
sqlite3_bind_text(stmt, 1, key, -1, SQLITE_TRANSIENT); |
88 |
sqlite3_bind_text(stmt, 1, key, -1, SQLITE_TRANSIENT); |
| ... |
| 97 |
if (sqlite3_step(stmt) != SQLITE_DONE) { |
97 |
if (sqlite3_step(stmt) != SQLITE_DONE) { |
| 98 |
sqlite3_finalize(stmt); |
98 |
sqlite3_finalize(stmt); |
| 99 |
free(value_str); |
99 |
free(value_str); |
| 100 |
return luaL_error(L, "failed to execute statement: %s", sqlite3_errmsg(mdb->db)); |
100 |
return luaL_error(L, "failed to execute statement: %s", sqlite3_errmsg(mdb.db)); |
| 101 |
} |
101 |
} |
| 102 |
|
102 |
|
| 103 |
sqlite3_finalize(stmt); |
103 |
sqlite3_finalize(stmt); |
| ... |
| 106 |
} |
106 |
} |
| 107 |
|
107 |
|
| 108 |
static int l_stash_get(lua_State *L) { |
108 |
static int l_stash_get(lua_State *L) { |
|
|
109 |
check_init(L); |
| 109 |
const char *key = luaL_checkstring(L, 1); |
110 |
const char *key = luaL_checkstring(L, 1); |
| 110 |
|
|
|
| 111 |
MemDB *mdb = &mem_dbs[current_db_idx]; |
|
|
| 112 |
if (!mdb->db) { |
|
|
| 113 |
return luaL_error(L, "database not initialized, call stash.use(id) first"); |
|
|
| 114 |
} |
|
|
| 115 |
|
111 |
|
| 116 |
sqlite3_stmt *stmt; |
112 |
sqlite3_stmt *stmt; |
| 117 |
const char *sql = "SELECT v FROM kv WHERE k = ? AND (e > ? OR e = 0);"; |
113 |
const char *sql = "SELECT v FROM kv WHERE k = ? AND (e > ? OR e = 0);"; |
| 118 |
if (sqlite3_prepare_v2(mdb->db, sql, -1, &stmt, NULL) != SQLITE_OK) { |
114 |
if (sqlite3_prepare_v2(mdb.db, sql, -1, &stmt, NULL) != SQLITE_OK) { |
| 119 |
return luaL_error(L, "failed to prepare statement: %s", sqlite3_errmsg(mdb->db)); |
115 |
return luaL_error(L, "failed to prepare statement: %s", sqlite3_errmsg(mdb.db)); |
| 120 |
} |
116 |
} |
| 121 |
|
117 |
|
| 122 |
sqlite3_bind_text(stmt, 1, key, -1, SQLITE_TRANSIENT); |
118 |
sqlite3_bind_text(stmt, 1, key, -1, SQLITE_TRANSIENT); |
| ... |
| 140 |
} |
136 |
} |
| 141 |
|
137 |
|
| 142 |
static int l_stash_del(lua_State *L) { |
138 |
static int l_stash_del(lua_State *L) { |
|
|
139 |
check_init(L); |
| 143 |
const char *key = luaL_checkstring(L, 1); |
140 |
const char *key = luaL_checkstring(L, 1); |
| 144 |
|
141 |
|
| 145 |
MemDB *mdb = &mem_dbs[current_db_idx]; |
|
|
| 146 |
if (!mdb->db) { |
|
|
| 147 |
return luaL_error(L, "database not initialized, call stash.use(id) first"); |
|
|
| 148 |
} |
|
|
| 149 |
|
|
|
| 150 |
sqlite3_stmt *stmt; |
142 |
sqlite3_stmt *stmt; |
| 151 |
const char *sql = "DELETE FROM kv WHERE k = ?;"; |
143 |
const char *sql = "DELETE FROM kv WHERE k = ?;"; |
| 152 |
if (sqlite3_prepare_v2(mdb->db, sql, -1, &stmt, NULL) != SQLITE_OK) { |
144 |
if (sqlite3_prepare_v2(mdb.db, sql, -1, &stmt, NULL) != SQLITE_OK) { |
| 153 |
return luaL_error(L, "failed to prepare statement: %s", sqlite3_errmsg(mdb->db)); |
145 |
return luaL_error(L, "failed to prepare statement: %s", sqlite3_errmsg(mdb.db)); |
| 154 |
} |
146 |
} |
| 155 |
|
147 |
|
| 156 |
sqlite3_bind_text(stmt, 1, key, -1, SQLITE_TRANSIENT); |
148 |
sqlite3_bind_text(stmt, 1, key, -1, SQLITE_TRANSIENT); |
| 157 |
|
149 |
|
| 158 |
if (sqlite3_step(stmt) != SQLITE_DONE) { |
150 |
if (sqlite3_step(stmt) != SQLITE_DONE) { |
| 159 |
sqlite3_finalize(stmt); |
151 |
sqlite3_finalize(stmt); |
| 160 |
return luaL_error(L, "failed to execute statement: %s", sqlite3_errmsg(mdb->db)); |
152 |
return luaL_error(L, "failed to execute statement: %s", sqlite3_errmsg(mdb.db)); |
| 161 |
} |
153 |
} |
| 162 |
|
154 |
|
| 163 |
sqlite3_finalize(stmt); |
155 |
sqlite3_finalize(stmt); |
| ... |
| 165 |
} |
157 |
} |
| 166 |
|
158 |
|
| 167 |
static int l_stash_exists(lua_State *L) { |
159 |
static int l_stash_exists(lua_State *L) { |
|
|
160 |
check_init(L); |
| 168 |
const char *key = luaL_checkstring(L, 1); |
161 |
const char *key = luaL_checkstring(L, 1); |
| 169 |
MemDB *mdb = &mem_dbs[current_db_idx]; |
|
|
| 170 |
if (!mdb->db) { |
|
|
| 171 |
return luaL_error(L, "database not initialized"); |
|
|
| 172 |
} |
|
|
| 173 |
|
162 |
|
| 174 |
sqlite3_stmt *stmt; |
163 |
sqlite3_stmt *stmt; |
| 175 |
const char *sql = "SELECT 1 FROM kv WHERE k = ? AND (e > ? OR e = 0);"; |
164 |
const char *sql = "SELECT 1 FROM kv WHERE k = ? AND (e > ? OR e = 0);"; |
| 176 |
if (sqlite3_prepare_v2(mdb->db, sql, -1, &stmt, NULL) != SQLITE_OK) { |
165 |
if (sqlite3_prepare_v2(mdb.db, sql, -1, &stmt, NULL) != SQLITE_OK) { |
| 177 |
return luaL_error(L, "failed to prepare statement: %s", sqlite3_errmsg(mdb->db)); |
166 |
return luaL_error(L, "failed to prepare statement: %s", sqlite3_errmsg(mdb.db)); |
| 178 |
} |
167 |
} |
| 179 |
sqlite3_bind_text(stmt, 1, key, -1, SQLITE_TRANSIENT); |
168 |
sqlite3_bind_text(stmt, 1, key, -1, SQLITE_TRANSIENT); |
| 180 |
sqlite3_bind_int64(stmt, 2, (sqlite3_int64)time(NULL)); |
169 |
sqlite3_bind_int64(stmt, 2, (sqlite3_int64)time(NULL)); |
| ... |
| 186 |
} |
175 |
} |
| 187 |
|
176 |
|
| 188 |
static int l_stash_incr(lua_State *L) { |
177 |
static int l_stash_incr(lua_State *L) { |
|
|
178 |
check_init(L); |
| 189 |
const char *key = luaL_checkstring(L, 1); |
179 |
const char *key = luaL_checkstring(L, 1); |
| 190 |
double amount = luaL_optnumber(L, 2, 1); |
180 |
double amount = luaL_optnumber(L, 2, 1); |
| 191 |
MemDB *mdb = &mem_dbs[current_db_idx]; |
|
|
| 192 |
if (!mdb->db) { |
|
|
| 193 |
return luaL_error(L, "database not initialized"); |
|
|
| 194 |
} |
|
|
| 195 |
|
181 |
|
| 196 |
sqlite3_stmt *stmt; |
182 |
sqlite3_stmt *stmt; |
| 197 |
const char *get_sql = "SELECT v, e FROM kv WHERE k = ?;"; |
183 |
const char *get_sql = "SELECT v, e FROM kv WHERE k = ?;"; |
| 198 |
if (sqlite3_prepare_v2(mdb->db, get_sql, -1, &stmt, NULL) != SQLITE_OK) { |
184 |
if (sqlite3_prepare_v2(mdb.db, get_sql, -1, &stmt, NULL) != SQLITE_OK) { |
| 199 |
return luaL_error(L, "failed to prepare statement: %s", sqlite3_errmsg(mdb->db)); |
185 |
return luaL_error(L, "failed to prepare statement: %s", sqlite3_errmsg(mdb.db)); |
| 200 |
} |
186 |
} |
| 201 |
sqlite3_bind_text(stmt, 1, key, -1, SQLITE_TRANSIENT); |
187 |
sqlite3_bind_text(stmt, 1, key, -1, SQLITE_TRANSIENT); |
| 202 |
|
188 |
|
| ... |
| 216 |
snprintf(val_buf, sizeof(val_buf), "%g", new_val); |
202 |
snprintf(val_buf, sizeof(val_buf), "%g", new_val); |
| 217 |
|
203 |
|
| 218 |
const char *set_sql = "INSERT OR REPLACE INTO kv (k, v, e) VALUES (?, ?, ?);"; |
204 |
const char *set_sql = "INSERT OR REPLACE INTO kv (k, v, e) VALUES (?, ?, ?);"; |
| 219 |
if (sqlite3_prepare_v2(mdb->db, set_sql, -1, &stmt, NULL) != SQLITE_OK) { |
205 |
if (sqlite3_prepare_v2(mdb.db, set_sql, -1, &stmt, NULL) != SQLITE_OK) { |
| 220 |
return luaL_error(L, "failed to prepare statement: %s", sqlite3_errmsg(mdb->db)); |
206 |
return luaL_error(L, "failed to prepare statement: %s", sqlite3_errmsg(mdb.db)); |
| 221 |
} |
207 |
} |
| 222 |
sqlite3_bind_text(stmt, 1, key, -1, SQLITE_TRANSIENT); |
208 |
sqlite3_bind_text(stmt, 1, key, -1, SQLITE_TRANSIENT); |
| 223 |
sqlite3_bind_text(stmt, 2, val_buf, -1, SQLITE_TRANSIENT); |
209 |
sqlite3_bind_text(stmt, 2, val_buf, -1, SQLITE_TRANSIENT); |
| ... |
| 237 |
} |
223 |
} |
| 238 |
|
224 |
|
| 239 |
static int l_stash_keys(lua_State *L) { |
225 |
static int l_stash_keys(lua_State *L) { |
|
|
226 |
check_init(L); |
| 240 |
const char *pattern = luaL_optstring(L, 1, "%"); |
227 |
const char *pattern = luaL_optstring(L, 1, "%"); |
| 241 |
MemDB *mdb = &mem_dbs[current_db_idx]; |
|
|
| 242 |
if (!mdb->db) { |
|
|
| 243 |
return luaL_error(L, "database not initialized"); |
|
|
| 244 |
} |
|
|
| 245 |
|
228 |
|
| 246 |
sqlite3_stmt *stmt; |
229 |
sqlite3_stmt *stmt; |
| 247 |
const char *sql = "SELECT k FROM kv WHERE k LIKE ? AND (e > ? OR e = 0);"; |
230 |
const char *sql = "SELECT k FROM kv WHERE k LIKE ? AND (e > ? OR e = 0);"; |
| 248 |
if (sqlite3_prepare_v2(mdb->db, sql, -1, &stmt, NULL) != SQLITE_OK) { |
231 |
if (sqlite3_prepare_v2(mdb.db, sql, -1, &stmt, NULL) != SQLITE_OK) { |
| 249 |
return luaL_error(L, "failed to prepare statement: %s", sqlite3_errmsg(mdb->db)); |
232 |
return luaL_error(L, "failed to prepare statement: %s", sqlite3_errmsg(mdb.db)); |
| 250 |
} |
233 |
} |
| 251 |
sqlite3_bind_text(stmt, 1, pattern, -1, SQLITE_TRANSIENT); |
234 |
sqlite3_bind_text(stmt, 1, pattern, -1, SQLITE_TRANSIENT); |
| 252 |
sqlite3_bind_int64(stmt, 2, (sqlite3_int64)time(NULL)); |
235 |
sqlite3_bind_int64(stmt, 2, (sqlite3_int64)time(NULL)); |
| ... |
| 262 |
} |
245 |
} |
| 263 |
|
246 |
|
| 264 |
static int l_stash_ttl(lua_State *L) { |
247 |
static int l_stash_ttl(lua_State *L) { |
|
|
248 |
check_init(L); |
| 265 |
const char *key = luaL_checkstring(L, 1); |
249 |
const char *key = luaL_checkstring(L, 1); |
| 266 |
MemDB *mdb = &mem_dbs[current_db_idx]; |
|
|
| 267 |
if (!mdb->db) { |
|
|
| 268 |
return luaL_error(L, "database not initialized"); |
|
|
| 269 |
} |
|
|
| 270 |
|
250 |
|
| 271 |
sqlite3_stmt *stmt; |
251 |
sqlite3_stmt *stmt; |
| 272 |
const char *sql = "SELECT e FROM kv WHERE k = ? AND (e > ? OR e = 0);"; |
252 |
const char *sql = "SELECT e FROM kv WHERE k = ? AND (e > ? OR e = 0);"; |
| 273 |
if (sqlite3_prepare_v2(mdb->db, sql, -1, &stmt, NULL) != SQLITE_OK) { |
253 |
if (sqlite3_prepare_v2(mdb.db, sql, -1, &stmt, NULL) != SQLITE_OK) { |
| 274 |
return luaL_error(L, "failed to prepare statement: %s", sqlite3_errmsg(mdb->db)); |
254 |
return luaL_error(L, "failed to prepare statement: %s", sqlite3_errmsg(mdb.db)); |
| 275 |
} |
255 |
} |
| 276 |
sqlite3_bind_text(stmt, 1, key, -1, SQLITE_TRANSIENT); |
256 |
sqlite3_bind_text(stmt, 1, key, -1, SQLITE_TRANSIENT); |
| 277 |
sqlite3_bind_int64(stmt, 2, (sqlite3_int64)time(NULL)); |
257 |
sqlite3_bind_int64(stmt, 2, (sqlite3_int64)time(NULL)); |
| ... |
| 291 |
} |
271 |
} |
| 292 |
|
272 |
|
| 293 |
static int l_stash_expire(lua_State *L) { |
273 |
static int l_stash_expire(lua_State *L) { |
|
|
274 |
check_init(L); |
| 294 |
const char *key = luaL_checkstring(L, 1); |
275 |
const char *key = luaL_checkstring(L, 1); |
| 295 |
int ttl = (int)luaL_checkinteger(L, 2); |
276 |
int ttl = (int)luaL_checkinteger(L, 2); |
| 296 |
MemDB *mdb = &mem_dbs[current_db_idx]; |
|
|
| 297 |
if (!mdb->db) { |
|
|
| 298 |
return luaL_error(L, "database not initialized"); |
|
|
| 299 |
} |
|
|
| 300 |
|
277 |
|
| 301 |
sqlite3_stmt *stmt; |
278 |
sqlite3_stmt *stmt; |
| 302 |
const char *sql = "UPDATE kv SET e = ? WHERE k = ? AND (e > ? OR e = 0);"; |
279 |
const char *sql = "UPDATE kv SET e = ? WHERE k = ? AND (e > ? OR e = 0);"; |
| 303 |
if (sqlite3_prepare_v2(mdb->db, sql, -1, &stmt, NULL) != SQLITE_OK) { |
280 |
if (sqlite3_prepare_v2(mdb.db, sql, -1, &stmt, NULL) != SQLITE_OK) { |
| 304 |
return luaL_error(L, "failed to prepare statement: %s", sqlite3_errmsg(mdb->db)); |
281 |
return luaL_error(L, "failed to prepare statement: %s", sqlite3_errmsg(mdb.db)); |
| 305 |
} |
282 |
} |
| 306 |
|
283 |
|
| 307 |
sqlite3_int64 expiry = (ttl > 0) ? (time(NULL) + ttl) : 0; |
284 |
sqlite3_int64 expiry = (ttl > 0) ? (time(NULL) + ttl) : 0; |
| ... |
| 310 |
sqlite3_bind_int64(stmt, 3, (sqlite3_int64)time(NULL)); |
287 |
sqlite3_bind_int64(stmt, 3, (sqlite3_int64)time(NULL)); |
| 311 |
|
288 |
|
| 312 |
sqlite3_step(stmt); |
289 |
sqlite3_step(stmt); |
| 313 |
int changes = sqlite3_changes(mdb->db); |
290 |
int changes = sqlite3_changes(mdb.db); |
| 314 |
sqlite3_finalize(stmt); |
291 |
sqlite3_finalize(stmt); |
| 315 |
|
292 |
|
| 316 |
lua_pushboolean(L, changes > 0); |
293 |
lua_pushboolean(L, changes > 0); |
| ... |
| 318 |
} |
295 |
} |
| 319 |
|
296 |
|
| 320 |
static int l_stash_clear(lua_State *L) { |
297 |
static int l_stash_clear(lua_State *L) { |
| 321 |
MemDB *mdb = &mem_dbs[current_db_idx]; |
298 |
check_init(L); |
| 322 |
if (!mdb->db) { |
299 |
sqlite3_exec(mdb.db, "DELETE FROM kv;", NULL, NULL, NULL); |
| 323 |
return luaL_error(L, "database not initialized"); |
|
|
| 324 |
} |
|
|
| 325 |
|
|
|
| 326 |
sqlite3_exec(mdb->db, "DELETE FROM kv;", NULL, NULL, NULL); |
|
|
| 327 |
return 0; |
300 |
return 0; |
| 328 |
} |
301 |
} |
| 329 |
|
302 |
|
| 330 |
static int l_stash_count(lua_State *L) { |
303 |
static int l_stash_count(lua_State *L) { |
| 331 |
MemDB *mdb = &mem_dbs[current_db_idx]; |
304 |
check_init(L); |
| 332 |
if (!mdb->db) { |
|
|
| 333 |
return luaL_error(L, "database not initialized"); |
|
|
| 334 |
} |
|
|
| 335 |
|
|
|
| 336 |
sqlite3_stmt *stmt; |
305 |
sqlite3_stmt *stmt; |
| 337 |
const char *sql = "SELECT COUNT(*) FROM kv WHERE (e > ? OR e = 0);"; |
306 |
const char *sql = "SELECT COUNT(*) FROM kv WHERE (e > ? OR e = 0);"; |
| 338 |
if (sqlite3_prepare_v2(mdb->db, sql, -1, &stmt, NULL) != SQLITE_OK) { |
307 |
if (sqlite3_prepare_v2(mdb.db, sql, -1, &stmt, NULL) != SQLITE_OK) { |
| 339 |
return luaL_error(L, "failed to prepare statement: %s", sqlite3_errmsg(mdb->db)); |
308 |
return luaL_error(L, "failed to prepare statement: %s", sqlite3_errmsg(mdb.db)); |
| 340 |
} |
309 |
} |
| 341 |
sqlite3_bind_int64(stmt, 1, (sqlite3_int64)time(NULL)); |
310 |
sqlite3_bind_int64(stmt, 1, (sqlite3_int64)time(NULL)); |
| 342 |
|
311 |
|
| ... |
| 350 |
} |
319 |
} |
| 351 |
|
320 |
|
| 352 |
static const struct luaL_Reg stash_lib[] = { |
321 |
static const struct luaL_Reg stash_lib[] = { |
| 353 |
{"use", l_stash_use}, |
322 |
{"cleanup", l_stash_cleanup}, |
| 354 |
{"set", l_stash_set}, |
323 |
{"set", l_stash_set}, |
| 355 |
{"get", l_stash_get}, |
324 |
{"get", l_stash_get}, |
| 356 |
{"del", l_stash_del}, |
325 |
{"del", l_stash_del}, |
| ... |