Mercurial > dovecot > original-hg > dovecot-1.2
annotate src/lib-sql/driver-sqlite.c @ 4444:d0d04db8e7a6 HEAD
Escape ' with '' instead of with \'.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Tue, 27 Jun 2006 12:25:55 +0300 |
parents | 1a98cb709395 |
children | 3e196acd60b7 |
rev | line source |
---|---|
3923 | 1 /* Copyright (C) 2006 Jakob Hirsch */ |
2 | |
3 #include "lib.h" | |
4 #include "str.h" | |
5 #include "sql-api-private.h" | |
6 | |
3943
cbe5c6772e0d
Added support for dynamically building SQL drivers.
Timo Sirainen <tss@iki.fi>
parents:
3936
diff
changeset
|
7 #ifdef BUILD_SQLITE |
3923 | 8 #include <sqlite3.h> |
9 | |
10 /* retry time if db is busy (in ms) */ | |
11 const int sqlite_busy_timeout = 1000; | |
12 | |
13 struct sqlite_db { | |
14 struct sql_db api; | |
15 | |
16 pool_t pool; | |
17 const char *dbfile; | |
18 sqlite3 *sqlite; | |
19 unsigned int connected:1; | |
20 int rc; | |
21 }; | |
22 | |
23 struct sqlite_result { | |
24 struct sql_result api; | |
25 sqlite3_stmt *stmt; | |
26 unsigned int cols; | |
27 const char **row; | |
28 }; | |
29 | |
30 struct sqlite_transaction_context { | |
31 struct sql_transaction_context ctx; | |
32 unsigned int failed:1; | |
33 }; | |
34 | |
3943
cbe5c6772e0d
Added support for dynamically building SQL drivers.
Timo Sirainen <tss@iki.fi>
parents:
3936
diff
changeset
|
35 extern struct sql_db driver_sqlite_db; |
3923 | 36 extern struct sql_result driver_sqlite_result; |
37 extern struct sql_result driver_sqlite_error_result; | |
38 | |
39 static int driver_sqlite_connect(struct sql_db *_db) | |
40 { | |
41 struct sqlite_db *db = (struct sqlite_db *)_db; | |
42 | |
43 if (db->connected) | |
44 return 1; | |
45 | |
46 db->rc = sqlite3_open(db->dbfile, &db->sqlite); | |
47 | |
48 if (db->rc == SQLITE_OK) { | |
49 db->connected = TRUE; | |
50 sqlite3_busy_timeout(db->sqlite, sqlite_busy_timeout); | |
51 return 1; | |
52 } else { | |
53 i_error("sqlite: open(%s) failed: %s", db->dbfile, | |
54 sqlite3_errmsg(db->sqlite)); | |
55 sqlite3_close(db->sqlite); | |
56 return -1; | |
57 } | |
58 } | |
59 | |
3943
cbe5c6772e0d
Added support for dynamically building SQL drivers.
Timo Sirainen <tss@iki.fi>
parents:
3936
diff
changeset
|
60 static struct sql_db *_driver_sqlite_init(const char *connect_string) |
3923 | 61 { |
62 struct sqlite_db *db; | |
63 pool_t pool; | |
64 | |
65 i_assert(connect_string != NULL); | |
66 | |
67 pool = pool_alloconly_create("sqlite driver", 512); | |
68 db = p_new(pool, struct sqlite_db, 1); | |
69 db->pool = pool; | |
70 db->api = driver_sqlite_db; | |
71 db->dbfile = p_strdup(db->pool, connect_string); | |
72 db->connected = FALSE; | |
73 | |
74 return &db->api; | |
75 } | |
76 | |
3943
cbe5c6772e0d
Added support for dynamically building SQL drivers.
Timo Sirainen <tss@iki.fi>
parents:
3936
diff
changeset
|
77 static void _driver_sqlite_deinit(struct sql_db *_db) |
3923 | 78 { |
79 struct sqlite_db *db = (struct sqlite_db *)_db; | |
80 | |
81 sqlite3_close(db->sqlite); | |
82 pool_unref(db->pool); | |
83 } | |
84 | |
85 static enum sql_db_flags | |
86 driver_sqlite_get_flags(struct sql_db *db __attr_unused__) | |
87 { | |
88 return SQL_DB_FLAG_BLOCKING; | |
89 } | |
90 | |
4294 | 91 static char *driver_sqlite_escape_string(struct sql_db *_db __attr_unused__, |
92 const char *string) | |
93 { | |
4444
d0d04db8e7a6
Escape ' with '' instead of with \'.
Timo Sirainen <tss@iki.fi>
parents:
4294
diff
changeset
|
94 const char *p; |
d0d04db8e7a6
Escape ' with '' instead of with \'.
Timo Sirainen <tss@iki.fi>
parents:
4294
diff
changeset
|
95 char *dest, *destbegin; |
d0d04db8e7a6
Escape ' with '' instead of with \'.
Timo Sirainen <tss@iki.fi>
parents:
4294
diff
changeset
|
96 |
d0d04db8e7a6
Escape ' with '' instead of with \'.
Timo Sirainen <tss@iki.fi>
parents:
4294
diff
changeset
|
97 /* find the first ' */ |
d0d04db8e7a6
Escape ' with '' instead of with \'.
Timo Sirainen <tss@iki.fi>
parents:
4294
diff
changeset
|
98 for (p = string; *p != '\''; p++) { |
d0d04db8e7a6
Escape ' with '' instead of with \'.
Timo Sirainen <tss@iki.fi>
parents:
4294
diff
changeset
|
99 if (*p == '\0') |
d0d04db8e7a6
Escape ' with '' instead of with \'.
Timo Sirainen <tss@iki.fi>
parents:
4294
diff
changeset
|
100 return t_strdup_noconst(string); |
d0d04db8e7a6
Escape ' with '' instead of with \'.
Timo Sirainen <tss@iki.fi>
parents:
4294
diff
changeset
|
101 } |
d0d04db8e7a6
Escape ' with '' instead of with \'.
Timo Sirainen <tss@iki.fi>
parents:
4294
diff
changeset
|
102 |
d0d04db8e7a6
Escape ' with '' instead of with \'.
Timo Sirainen <tss@iki.fi>
parents:
4294
diff
changeset
|
103 /* @UNSAFE: escape ' with '' */ |
d0d04db8e7a6
Escape ' with '' instead of with \'.
Timo Sirainen <tss@iki.fi>
parents:
4294
diff
changeset
|
104 dest = destbegin = t_buffer_get((p - string) + strlen(string) * 2 + 1); |
d0d04db8e7a6
Escape ' with '' instead of with \'.
Timo Sirainen <tss@iki.fi>
parents:
4294
diff
changeset
|
105 |
d0d04db8e7a6
Escape ' with '' instead of with \'.
Timo Sirainen <tss@iki.fi>
parents:
4294
diff
changeset
|
106 memcpy(dest, string, p - string); |
d0d04db8e7a6
Escape ' with '' instead of with \'.
Timo Sirainen <tss@iki.fi>
parents:
4294
diff
changeset
|
107 dest += p - string; |
d0d04db8e7a6
Escape ' with '' instead of with \'.
Timo Sirainen <tss@iki.fi>
parents:
4294
diff
changeset
|
108 |
d0d04db8e7a6
Escape ' with '' instead of with \'.
Timo Sirainen <tss@iki.fi>
parents:
4294
diff
changeset
|
109 for (; *p != '\0'; p++) { |
d0d04db8e7a6
Escape ' with '' instead of with \'.
Timo Sirainen <tss@iki.fi>
parents:
4294
diff
changeset
|
110 *dest++ = *p; |
d0d04db8e7a6
Escape ' with '' instead of with \'.
Timo Sirainen <tss@iki.fi>
parents:
4294
diff
changeset
|
111 if (*p == '\'') |
d0d04db8e7a6
Escape ' with '' instead of with \'.
Timo Sirainen <tss@iki.fi>
parents:
4294
diff
changeset
|
112 *dest++ = *p; |
d0d04db8e7a6
Escape ' with '' instead of with \'.
Timo Sirainen <tss@iki.fi>
parents:
4294
diff
changeset
|
113 } |
d0d04db8e7a6
Escape ' with '' instead of with \'.
Timo Sirainen <tss@iki.fi>
parents:
4294
diff
changeset
|
114 *dest++ = '\0'; |
d0d04db8e7a6
Escape ' with '' instead of with \'.
Timo Sirainen <tss@iki.fi>
parents:
4294
diff
changeset
|
115 t_buffer_alloc(dest - destbegin); |
d0d04db8e7a6
Escape ' with '' instead of with \'.
Timo Sirainen <tss@iki.fi>
parents:
4294
diff
changeset
|
116 |
d0d04db8e7a6
Escape ' with '' instead of with \'.
Timo Sirainen <tss@iki.fi>
parents:
4294
diff
changeset
|
117 return destbegin; |
4294 | 118 } |
119 | |
3923 | 120 static void driver_sqlite_exec(struct sql_db *_db, const char *query) |
121 { | |
122 struct sqlite_db *db = (struct sqlite_db *)_db; | |
123 | |
124 db->rc = sqlite3_exec(db->sqlite, query, NULL, 0, NULL); | |
125 if (db->rc != SQLITE_OK) { | |
126 i_error("sqlite: exec(%s) failed: %s (%d)", | |
127 query, sqlite3_errmsg(db->sqlite), db->rc); | |
128 } | |
129 } | |
130 | |
131 static void driver_sqlite_query(struct sql_db *db, const char *query, | |
132 sql_query_callback_t *callback, void *context) | |
133 { | |
134 struct sql_result *result; | |
135 | |
136 result = sql_query_s(db, query); | |
137 result->callback = TRUE; | |
138 callback(result, context); | |
3946 | 139 result->callback = FALSE; |
3923 | 140 sql_result_free(result); |
141 } | |
142 | |
143 static struct sql_result * | |
144 driver_sqlite_query_s(struct sql_db *_db, const char *query) | |
145 { | |
146 struct sqlite_db *db = (struct sqlite_db *)_db; | |
147 struct sqlite_result *result; | |
148 int rc; | |
149 | |
150 result = i_new(struct sqlite_result, 1); | |
151 | |
152 rc = sqlite3_prepare(db->sqlite, query, -1, &result->stmt, NULL); | |
153 if (rc == SQLITE_OK) { | |
154 result->api = driver_sqlite_result; | |
155 result->cols = sqlite3_column_count(result->stmt); | |
156 result->row = i_new(const char *, result->cols); | |
157 } else { | |
158 result->api = driver_sqlite_error_result; | |
159 result->stmt = NULL; | |
160 result->cols = 0; | |
161 } | |
162 result->api.db = _db; | |
163 | |
164 return &result->api; | |
165 } | |
166 | |
167 static void driver_sqlite_result_free(struct sql_result *_result) | |
168 { | |
169 struct sqlite_result *result = (struct sqlite_result *)_result; | |
170 struct sqlite_db *db = (struct sqlite_db *) result->api.db; | |
171 int rc; | |
172 | |
3947 | 173 if (_result->callback) |
174 return; | |
175 | |
3923 | 176 if (result->stmt != NULL) { |
177 if ((rc = sqlite3_finalize(result->stmt)) != SQLITE_OK) { | |
178 i_warning("sqlite: finalize failed: %s (%d)", | |
179 sqlite3_errmsg(db->sqlite), rc); | |
180 } | |
181 i_free(result->row); | |
182 } | |
183 i_free(result); | |
184 } | |
185 | |
186 static int driver_sqlite_result_next_row(struct sql_result *_result) | |
187 { | |
188 struct sqlite_result *result = (struct sqlite_result *)_result; | |
189 | |
190 switch (sqlite3_step(result->stmt)) { | |
191 case SQLITE_ROW: | |
192 return 1; | |
193 case SQLITE_DONE: | |
194 return 0; | |
195 default: | |
196 return -1; | |
197 } | |
198 } | |
199 | |
200 static unsigned int | |
201 driver_sqlite_result_get_fields_count(struct sql_result *_result) | |
202 { | |
203 struct sqlite_result *result = (struct sqlite_result *)_result; | |
204 | |
205 return result->cols; | |
206 } | |
207 | |
208 static const char * | |
209 driver_sqlite_result_get_field_name(struct sql_result *_result, | |
210 unsigned int idx) | |
211 { | |
212 struct sqlite_result *result = (struct sqlite_result *)_result; | |
213 | |
214 return sqlite3_column_name(result->stmt, idx); | |
215 } | |
216 | |
217 static int driver_sqlite_result_find_field(struct sql_result *_result, | |
218 const char *field_name) | |
219 { | |
220 struct sqlite_result *result = (struct sqlite_result *)_result; | |
221 unsigned int i; | |
222 | |
223 for (i = 0; i < result->cols; ++i) { | |
224 const char *col = sqlite3_column_name(result->stmt, i); | |
225 | |
226 if (strcmp(col, field_name) == 0) | |
227 return i; | |
228 } | |
229 | |
230 return -1; | |
231 } | |
232 | |
233 static const char * | |
234 driver_sqlite_result_get_field_value(struct sql_result *_result, | |
235 unsigned int idx) | |
236 { | |
237 struct sqlite_result *result = (struct sqlite_result *)_result; | |
238 | |
3936
8e827b05047b
Compiler warning fix. Patch by Marcus Rueckert
Timo Sirainen <tss@iki.fi>
parents:
3923
diff
changeset
|
239 return (const char*)sqlite3_column_text(result->stmt, idx); |
3923 | 240 } |
241 | |
242 static const char * | |
243 driver_sqlite_result_find_field_value(struct sql_result *result, | |
244 const char *field_name) | |
245 { | |
246 int idx; | |
247 | |
248 idx = driver_sqlite_result_find_field(result, field_name); | |
249 if (idx < 0) | |
250 return NULL; | |
251 return driver_sqlite_result_get_field_value(result, idx); | |
252 } | |
253 | |
254 static const char *const * | |
255 driver_sqlite_result_get_values(struct sql_result *_result) | |
256 { | |
257 struct sqlite_result *result = (struct sqlite_result *)_result; | |
258 unsigned int i; | |
259 | |
260 for (i = 0; i < result->cols; ++i) { | |
261 result->row[i] = | |
262 driver_sqlite_result_get_field_value(_result, i); | |
263 } | |
264 | |
265 return (const char *const *)result->row; | |
266 } | |
267 | |
268 static const char *driver_sqlite_result_get_error(struct sql_result *_result) | |
269 { | |
270 struct sqlite_result *result = (struct sqlite_result *)_result; | |
271 struct sqlite_db *db = (struct sqlite_db *)result->api.db; | |
272 | |
273 return sqlite3_errmsg(db->sqlite); | |
274 } | |
275 | |
276 static struct sql_transaction_context * | |
277 driver_sqlite_transaction_begin(struct sql_db *_db) | |
278 { | |
279 struct sqlite_transaction_context *ctx; | |
280 struct sqlite_db *db = (struct sqlite_db *)_db; | |
281 | |
282 ctx = i_new(struct sqlite_transaction_context, 1); | |
283 ctx->ctx.db = _db; | |
284 | |
285 sql_exec(_db, "BEGIN TRANSACTION"); | |
286 if (db->rc != SQLITE_OK) | |
287 ctx->failed = TRUE; | |
288 | |
289 return &ctx->ctx; | |
290 } | |
291 | |
292 static void | |
293 driver_sqlite_transaction_rollback(struct sql_transaction_context *_ctx) | |
294 { | |
295 struct sqlite_transaction_context *ctx = | |
296 (struct sqlite_transaction_context *)_ctx; | |
297 | |
298 sql_exec(_ctx->db, "ROLLBACK"); | |
299 i_free(ctx); | |
300 } | |
301 | |
302 static void | |
303 driver_sqlite_transaction_commit(struct sql_transaction_context *_ctx, | |
304 sql_commit_callback_t *callback, void *context) | |
305 { | |
306 struct sqlite_transaction_context *ctx = | |
307 (struct sqlite_transaction_context *)_ctx; | |
308 struct sqlite_db *db = (struct sqlite_db *)ctx->ctx.db; | |
309 const char *errmsg; | |
310 | |
311 if (!ctx->failed) { | |
312 sql_exec(_ctx->db, "COMMIT"); | |
313 if (db->rc != SQLITE_OK) | |
314 ctx->failed = TRUE; | |
315 } | |
316 | |
317 if (ctx->failed) { | |
318 errmsg = sqlite3_errmsg(db->sqlite); | |
319 callback(errmsg, context); | |
320 /* also does i_free(ctx) */ | |
321 driver_sqlite_transaction_rollback(_ctx); | |
322 } else { | |
323 callback(NULL, context); | |
324 i_free(ctx); | |
325 } | |
326 } | |
327 | |
328 static int | |
329 driver_sqlite_transaction_commit_s(struct sql_transaction_context *_ctx, | |
330 const char **error_r) | |
331 { | |
332 struct sqlite_transaction_context *ctx = | |
333 (struct sqlite_transaction_context *)_ctx; | |
334 struct sqlite_db *db = (struct sqlite_db *) ctx->ctx.db; | |
335 | |
336 if (ctx->failed) { | |
337 /* also does i_free(ctx) */ | |
338 driver_sqlite_transaction_rollback(_ctx); | |
339 return -1; | |
340 } | |
341 | |
342 sql_exec(_ctx->db, "COMMIT"); | |
343 *error_r = sqlite3_errmsg(db->sqlite); | |
344 i_free(ctx); | |
345 return 0; | |
346 } | |
347 | |
348 static void | |
349 driver_sqlite_update(struct sql_transaction_context *_ctx, const char *query) | |
350 { | |
351 struct sqlite_transaction_context *ctx = | |
352 (struct sqlite_transaction_context *)_ctx; | |
353 struct sqlite_db *db = (struct sqlite_db *)ctx->ctx.db; | |
354 | |
355 if (ctx->failed) | |
356 return; | |
357 | |
358 sql_exec(_ctx->db, query); | |
359 if (db->rc != SQLITE_OK) | |
360 ctx->failed = TRUE; | |
361 } | |
362 | |
363 struct sql_db driver_sqlite_db = { | |
364 "sqlite", | |
365 | |
3943
cbe5c6772e0d
Added support for dynamically building SQL drivers.
Timo Sirainen <tss@iki.fi>
parents:
3936
diff
changeset
|
366 _driver_sqlite_init, |
cbe5c6772e0d
Added support for dynamically building SQL drivers.
Timo Sirainen <tss@iki.fi>
parents:
3936
diff
changeset
|
367 _driver_sqlite_deinit, |
3923 | 368 driver_sqlite_get_flags, |
369 driver_sqlite_connect, | |
4294 | 370 driver_sqlite_escape_string, |
3923 | 371 driver_sqlite_exec, |
372 driver_sqlite_query, | |
373 driver_sqlite_query_s, | |
374 | |
375 driver_sqlite_transaction_begin, | |
376 driver_sqlite_transaction_commit, | |
377 driver_sqlite_transaction_commit_s, | |
378 driver_sqlite_transaction_rollback, | |
379 driver_sqlite_update | |
380 }; | |
381 | |
382 struct sql_result driver_sqlite_result = { | |
383 NULL, | |
384 | |
385 driver_sqlite_result_free, | |
386 driver_sqlite_result_next_row, | |
387 driver_sqlite_result_get_fields_count, | |
388 driver_sqlite_result_get_field_name, | |
389 driver_sqlite_result_find_field, | |
390 driver_sqlite_result_get_field_value, | |
391 driver_sqlite_result_find_field_value, | |
392 driver_sqlite_result_get_values, | |
393 driver_sqlite_result_get_error, | |
394 | |
395 FALSE | |
396 }; | |
397 | |
398 static int | |
399 driver_sqlite_result_error_next_row(struct sql_result *result __attr_unused__) | |
400 { | |
401 return -1; | |
402 } | |
403 | |
404 struct sql_result driver_sqlite_error_result = { | |
405 NULL, | |
406 | |
407 driver_sqlite_result_free, | |
408 driver_sqlite_result_error_next_row, | |
409 NULL, NULL, NULL, NULL, NULL, NULL, | |
410 driver_sqlite_result_get_error, | |
411 | |
412 FALSE | |
413 }; | |
414 | |
3943
cbe5c6772e0d
Added support for dynamically building SQL drivers.
Timo Sirainen <tss@iki.fi>
parents:
3936
diff
changeset
|
415 void driver_sqlite_init(void); |
cbe5c6772e0d
Added support for dynamically building SQL drivers.
Timo Sirainen <tss@iki.fi>
parents:
3936
diff
changeset
|
416 void driver_sqlite_deinit(void); |
cbe5c6772e0d
Added support for dynamically building SQL drivers.
Timo Sirainen <tss@iki.fi>
parents:
3936
diff
changeset
|
417 |
cbe5c6772e0d
Added support for dynamically building SQL drivers.
Timo Sirainen <tss@iki.fi>
parents:
3936
diff
changeset
|
418 void driver_sqlite_init(void) |
cbe5c6772e0d
Added support for dynamically building SQL drivers.
Timo Sirainen <tss@iki.fi>
parents:
3936
diff
changeset
|
419 { |
cbe5c6772e0d
Added support for dynamically building SQL drivers.
Timo Sirainen <tss@iki.fi>
parents:
3936
diff
changeset
|
420 sql_driver_register(&driver_sqlite_db); |
cbe5c6772e0d
Added support for dynamically building SQL drivers.
Timo Sirainen <tss@iki.fi>
parents:
3936
diff
changeset
|
421 } |
cbe5c6772e0d
Added support for dynamically building SQL drivers.
Timo Sirainen <tss@iki.fi>
parents:
3936
diff
changeset
|
422 |
cbe5c6772e0d
Added support for dynamically building SQL drivers.
Timo Sirainen <tss@iki.fi>
parents:
3936
diff
changeset
|
423 void driver_sqlite_deinit(void) |
cbe5c6772e0d
Added support for dynamically building SQL drivers.
Timo Sirainen <tss@iki.fi>
parents:
3936
diff
changeset
|
424 { |
cbe5c6772e0d
Added support for dynamically building SQL drivers.
Timo Sirainen <tss@iki.fi>
parents:
3936
diff
changeset
|
425 sql_driver_unregister(&driver_sqlite_db); |
cbe5c6772e0d
Added support for dynamically building SQL drivers.
Timo Sirainen <tss@iki.fi>
parents:
3936
diff
changeset
|
426 } |
cbe5c6772e0d
Added support for dynamically building SQL drivers.
Timo Sirainen <tss@iki.fi>
parents:
3936
diff
changeset
|
427 |
3923 | 428 #endif |