1 /******************************************************************************
2 * $Id$
3 *
4 * Project: MapServer
5 * Purpose: MapCache tile caching support file: sqlite cache backend
6 * Author: Thomas Bonfort and the MapServer team.
7 *
8 ******************************************************************************
9 * Copyright (c) 1996-2011 Regents of the University of Minnesota.
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining a
12 * copy of this software and associated documentation files (the "Software"),
13 * to deal in the Software without restriction, including without limitation
14 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 * and/or sell copies of the Software, and to permit persons to whom the
16 * Software is furnished to do so, subject to the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be included in
19 * all copies of this Software or works derived from this Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 * DEALINGS IN THE SOFTWARE.
28 *****************************************************************************/
29
30 #include "mapcache.h"
31 #ifdef USE_SQLITE
32
33 #include <apr_strings.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <time.h>
37 #include <apr_reslist.h>
38 #include <apr_hash.h>
39 #ifdef APR_HAS_THREADS
40 #include <apr_thread_mutex.h>
41 #endif
42
43 #ifndef _WIN32
44 #include <unistd.h>
45 #endif
46
47 #include <sqlite3.h>
48
49 /**\class mapcache_cache_sqlite
50 * \brief a mapcache_cache on a filesytem
51 * \implements mapcache_cache
52 */
53 typedef struct mapcache_cache_sqlite mapcache_cache_sqlite;
54 typedef struct mapcache_cache_sqlite_stmt mapcache_cache_sqlite_stmt;
55
56 struct mapcache_cache_sqlite_stmt {
57 char *sql;
58 };
59
60 struct sqlite_conn;
61
62 struct mapcache_cache_sqlite {
63 mapcache_cache cache;
64 char *dbfile;
65 mapcache_cache_sqlite_stmt create_stmt;
66 mapcache_cache_sqlite_stmt exists_stmt;
67 mapcache_cache_sqlite_stmt get_stmt;
68 mapcache_cache_sqlite_stmt set_stmt;
69 mapcache_cache_sqlite_stmt delete_stmt;
70 apr_table_t *pragmas;
71 void (*bind_stmt)(mapcache_context *ctx, void *stmt, mapcache_cache_sqlite *cache, mapcache_tile *tile);
72 int n_prepared_statements;
73 int detect_blank;
74 char *x_fmt,*y_fmt,*z_fmt,*inv_x_fmt,*inv_y_fmt,*div_x_fmt,*div_y_fmt,*inv_div_x_fmt,*inv_div_y_fmt,
75 *top_fmt,*top_x_fmt,*top_y_fmt,*inv_top_x_fmt,*inv_top_y_fmt;
76 int count_x, count_y;
77 int top;
78 };
79
80
81 struct sqlite_conn_params {
82 mapcache_cache_sqlite *cache;
83 char *dbfile;
84 int readonly;
85 };
86
87 struct sqlite_conn {
88 sqlite3 *handle;
89 int nstatements;
90 sqlite3_stmt **prepared_statements;
91 };
92
93 #define SQLITE_CONN(pooled_connection) ((struct sqlite_conn*)((pooled_connection)->connection))
94
95 #define HAS_TILE_STMT_IDX 0
96 #define GET_TILE_STMT_IDX 1
97 #define SQLITE_SET_TILE_STMT_IDX 2
98 #define SQLITE_DEL_TILE_STMT_IDX 3
99 #define MBTILES_SET_EMPTY_TILE_STMT1_IDX 2
100 #define MBTILES_SET_EMPTY_TILE_STMT2_IDX 3
101 #define MBTILES_SET_TILE_STMT1_IDX 4
102 #define MBTILES_SET_TILE_STMT2_IDX 5
103 #define MBTILES_DEL_TILE_SELECT_STMT_IDX 6
104 #define MBTILES_DEL_TILE_STMT1_IDX 7
105 #define MBTILES_DEL_TILE_STMT2_IDX 8
106
107 static void _mapcache_cache_sqlite_filename_for_tile(mapcache_context *ctx, mapcache_cache_sqlite *dcache, mapcache_tile *tile, char **path);
108
_sqlite_set_pragmas(mapcache_context * ctx,mapcache_cache_sqlite * cache,struct sqlite_conn * conn)109 static void _sqlite_set_pragmas(mapcache_context *ctx, mapcache_cache_sqlite* cache, struct sqlite_conn *conn)
110 {
111 if (cache->pragmas && !apr_is_empty_table(cache->pragmas)) {
112 const apr_array_header_t *elts = apr_table_elts(cache->pragmas);
113 /* FIXME dynamically allocate this string */
114 int i,ret;
115 char *pragma_stmt;
116 for (i = 0; i < elts->nelts; i++) {
117 apr_table_entry_t entry = APR_ARRAY_IDX(elts, i, apr_table_entry_t);
118 pragma_stmt = apr_psprintf(ctx->pool,"PRAGMA %s=%s",entry.key,entry.val);
119 do {
120 ret = sqlite3_exec(conn->handle, pragma_stmt, 0, 0, NULL);
121 if (ret != SQLITE_OK && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) {
122 break;
123 }
124 } while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED);
125 if (ret != SQLITE_OK) {
126 ctx->set_error(ctx, 500, "failed to execute pragma statement %s",pragma_stmt);
127 return;
128 }
129 }
130 }
131 return;
132 }
133
134
135
mapcache_sqlite_release_conn(mapcache_context * ctx,mapcache_pooled_connection * conn)136 static void mapcache_sqlite_release_conn(mapcache_context *ctx, mapcache_pooled_connection *conn) {
137 mapcache_connection_pool_release_connection(ctx,conn);
138 }
139
mapcache_sqlite_connection_constructor(mapcache_context * ctx,void ** conn_,void * params)140 void mapcache_sqlite_connection_constructor(mapcache_context *ctx, void **conn_, void *params)
141 {
142 int ret;
143 int flags;
144 struct sqlite_conn_params *sq_params = (struct sqlite_conn_params*)params;
145 struct sqlite_conn *conn = calloc(1, sizeof (struct sqlite_conn));
146 *conn_ = conn;
147 if(sq_params->readonly) {
148 flags = SQLITE_OPEN_READONLY | SQLITE_OPEN_NOMUTEX;
149 } else {
150 flags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_NOMUTEX | SQLITE_OPEN_CREATE;
151 mapcache_make_parent_dirs(ctx,sq_params->dbfile);
152 GC_CHECK_ERROR(ctx);
153 }
154 ret = sqlite3_open_v2(sq_params->dbfile, &conn->handle, flags, NULL);
155 if (ret != SQLITE_OK) {
156 ctx->set_error(ctx,500,"sqlite backend failed to open db %s: %s", sq_params->dbfile, sqlite3_errmsg(conn->handle));
157 return;
158 }
159 sqlite3_busy_timeout(conn->handle, 300000);
160 do {
161 ret = sqlite3_exec(conn->handle, sq_params->cache->create_stmt.sql, 0, 0, NULL);
162 if (ret != SQLITE_OK && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) {
163 break;
164 }
165 } while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED);
166 if (ret != SQLITE_OK) {
167 ctx->set_error(ctx,500, "sqlite backend failed to create db schema on %s: %s", sq_params->dbfile, sqlite3_errmsg(conn->handle));
168 sqlite3_close(conn->handle);
169 return;
170 }
171 _sqlite_set_pragmas(ctx, sq_params->cache, conn);
172 if(GC_HAS_ERROR(ctx)) {
173 sqlite3_close(conn->handle);
174 return;
175 }
176 conn->prepared_statements = calloc(sq_params->cache->n_prepared_statements,sizeof(sqlite3_stmt*));
177 conn->nstatements = sq_params->cache->n_prepared_statements;
178 }
179
mapcache_sqlite_connection_destructor(void * conn_)180 void mapcache_sqlite_connection_destructor(void *conn_)
181 {
182 struct sqlite_conn *conn = (struct sqlite_conn*) conn_;
183 int i;
184 for(i=0; i<conn->nstatements; i++) {
185 if(conn->prepared_statements[i]) {
186 sqlite3_finalize(conn->prepared_statements[i]);
187 }
188 }
189 free(conn->prepared_statements);
190 sqlite3_close(conn->handle);
191 }
192
mapcache_sqlite_get_conn(mapcache_context * ctx,mapcache_cache_sqlite * cache,mapcache_tile * tile,int readonly)193 static mapcache_pooled_connection* mapcache_sqlite_get_conn(mapcache_context *ctx, mapcache_cache_sqlite *cache, mapcache_tile *tile, int readonly) {
194 struct sqlite_conn_params params;
195 mapcache_pooled_connection *pc;
196 char *key;
197 _mapcache_cache_sqlite_filename_for_tile(ctx,cache,tile,¶ms.dbfile);
198 params.cache = cache;
199 params.readonly = readonly;
200 if(!strstr(cache->dbfile,"{")) {
201 key = apr_pstrcat(ctx->pool,readonly?"ro_":"rw_",cache->cache.name,NULL);
202 } else {
203 key = apr_pstrcat(ctx->pool,readonly?"ro_":"rw_",params.dbfile,NULL);
204 }
205 pc = mapcache_connection_pool_get_connection(ctx,key,mapcache_sqlite_connection_constructor,mapcache_sqlite_connection_destructor,¶ms);
206 return pc;
207 }
208
209
210 /**
211 * \brief return sqlite db filename for given tile
212 *
213 * \param tile the tile to get the key from
214 * \param path pointer to a char* that will contain the filename
215 * \param r
216 * \private \memberof mapcache_cache_sqlite
217 */
_mapcache_cache_sqlite_filename_for_tile(mapcache_context * ctx,mapcache_cache_sqlite * dcache,mapcache_tile * tile,char ** path)218 static void _mapcache_cache_sqlite_filename_for_tile(mapcache_context *ctx, mapcache_cache_sqlite *dcache, mapcache_tile *tile, char **path)
219 {
220 *path = dcache->dbfile;
221
222 if(strstr(*path,"{")) {
223 /*
224 * generic template substitutions
225 */
226 if(strstr(*path,"{tileset}"))
227 *path = mapcache_util_str_replace(ctx->pool,*path, "{tileset}",
228 tile->tileset->name);
229 if(strstr(*path,"{grid}"))
230 *path = mapcache_util_str_replace(ctx->pool,*path, "{grid}",
231 tile->grid_link->grid->name);
232 if(tile->dimensions && strstr(*path,"{dim")) {
233 char *dimstring="";
234 int i = tile->dimensions->nelts;
235 while(i--) {
236 mapcache_requested_dimension *entry = APR_ARRAY_IDX(tile->dimensions,i,mapcache_requested_dimension*);
237 const char *dimval = mapcache_util_str_sanitize(ctx->pool,entry->cached_value,"/.",'#');
238 char *single_dim = apr_pstrcat(ctx->pool,"{dim:",entry->dimension->name,"}",NULL);
239 dimstring = apr_pstrcat(ctx->pool,dimstring,"#",dimval,NULL);
240 if(strstr(*path,single_dim)) {
241 *path = mapcache_util_str_replace(ctx->pool,*path, single_dim, dimval);
242 }
243 }
244 *path = mapcache_util_str_replace(ctx->pool,*path, "{dim}", dimstring);
245 }
246
247
248 while(strstr(*path,"{z}"))
249 *path = mapcache_util_str_replace(ctx->pool,*path, "{z}",
250 apr_psprintf(ctx->pool,dcache->z_fmt,tile->z));
251
252 if(dcache->count_x > 0) {
253 while(strstr(*path,"{div_x}"))
254 *path = mapcache_util_str_replace(ctx->pool,*path, "{div_x}",
255 apr_psprintf(ctx->pool,dcache->div_x_fmt,tile->x/dcache->count_x));
256 while(strstr(*path,"{inv_div_x}"))
257 *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_div_x}",
258 apr_psprintf(ctx->pool,dcache->inv_div_x_fmt,(tile->grid_link->grid->levels[tile->z]->maxx - tile->x - 1)/dcache->count_x));
259 while(strstr(*path,"{x}"))
260 *path = mapcache_util_str_replace(ctx->pool,*path, "{x}",
261 apr_psprintf(ctx->pool,dcache->x_fmt,tile->x/dcache->count_x*dcache->count_x));
262 while(strstr(*path,"{inv_x}"))
263 *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_x}",
264 apr_psprintf(ctx->pool,dcache->inv_x_fmt,(tile->grid_link->grid->levels[tile->z]->maxx - tile->x - 1)/dcache->count_x*dcache->count_x));
265 }
266
267 if(dcache->count_y > 0) {
268 while(strstr(*path,"{div_y}"))
269 *path = mapcache_util_str_replace(ctx->pool,*path, "{div_y}",
270 apr_psprintf(ctx->pool,dcache->div_y_fmt,tile->y/dcache->count_y));
271 while(strstr(*path,"{inv_div_y}"))
272 *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_div_y}",
273 apr_psprintf(ctx->pool,dcache->inv_div_y_fmt,(tile->grid_link->grid->levels[tile->z]->maxy - tile->y - 1)/dcache->count_y));
274 while(strstr(*path,"{y}"))
275 *path = mapcache_util_str_replace(ctx->pool,*path, "{y}",
276 apr_psprintf(ctx->pool,dcache->y_fmt,tile->y/dcache->count_y*dcache->count_y));
277 while(strstr(*path,"{inv_y}"))
278 *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_y}",
279 apr_psprintf(ctx->pool,dcache->inv_y_fmt,(tile->grid_link->grid->levels[tile->z]->maxy - tile->y - 1)/dcache->count_y*dcache->count_y));
280
281 }
282
283 if (dcache->top > 0) {
284 while(strstr(*path,"{top}"))
285 *path = mapcache_util_str_replace(ctx->pool,*path, "{top}",
286 apr_psprintf(ctx->pool,dcache->top_fmt,dcache->top));
287 while(strstr(*path,"{top_x}"))
288 *path = mapcache_util_str_replace(ctx->pool,*path, "{top_x}",
289 apr_psprintf(ctx->pool,dcache->top_x_fmt,
290 tile->x * tile->grid_link->grid->levels[dcache->top]->maxx
291 / tile->grid_link->grid->levels[tile->z]->maxx));
292 while(strstr(*path,"{top_y}"))
293 *path = mapcache_util_str_replace(ctx->pool,*path, "{top_y}",
294 apr_psprintf(ctx->pool,dcache->top_y_fmt,
295 tile->y * tile->grid_link->grid->levels[dcache->top]->maxy
296 / tile->grid_link->grid->levels[tile->z]->maxy));
297 while(strstr(*path,"{inv_top_x}"))
298 *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_top_x}",
299 apr_psprintf(ctx->pool,dcache->inv_top_x_fmt,
300 tile->grid_link->grid->levels[dcache->top]->maxx - 1
301 - tile->x * tile->grid_link->grid->levels[dcache->top]->maxx
302 / tile->grid_link->grid->levels[tile->z]->maxx));
303 while(strstr(*path,"{inv_top_y}"))
304 *path = mapcache_util_str_replace(ctx->pool,*path, "{inv_top_y}",
305 apr_psprintf(ctx->pool,dcache->inv_top_y_fmt,
306 tile->grid_link->grid->levels[dcache->top]->maxy - 1
307 - tile->y * tile->grid_link->grid->levels[dcache->top]->maxy
308 / tile->grid_link->grid->levels[tile->z]->maxy));
309 }
310 }
311
312 if(!*path) {
313 ctx->set_error(ctx,500, "failed to allocate tile key");
314 }
315 }
316
317
318
319 /**
320 * \brief apply appropriate tile properties to the sqlite statement */
_bind_sqlite_params(mapcache_context * ctx,void * vstmt,mapcache_cache_sqlite * cache,mapcache_tile * tile)321 static void _bind_sqlite_params(mapcache_context *ctx, void *vstmt, mapcache_cache_sqlite *cache, mapcache_tile *tile)
322 {
323 sqlite3_stmt *stmt = vstmt;
324 int paramidx;
325 /* tile->x */
326 paramidx = sqlite3_bind_parameter_index(stmt, ":x");
327 if (paramidx) sqlite3_bind_int(stmt, paramidx, tile->x);
328
329 paramidx = sqlite3_bind_parameter_index(stmt, ":inv_x");
330 if (paramidx)
331 sqlite3_bind_int(stmt, paramidx, tile->grid_link->grid->levels[tile->z]->maxx - tile->x - 1);
332
333 /* tile->y */
334 paramidx = sqlite3_bind_parameter_index(stmt, ":y");
335 if (paramidx) sqlite3_bind_int(stmt, paramidx, tile->y);
336
337 paramidx = sqlite3_bind_parameter_index(stmt, ":inv_y");
338 if (paramidx)
339 sqlite3_bind_int(stmt, paramidx, tile->grid_link->grid->levels[tile->z]->maxy - tile->y - 1);
340
341 /* tile->z */
342 paramidx = sqlite3_bind_parameter_index(stmt, ":z");
343 if (paramidx) sqlite3_bind_int(stmt, paramidx, tile->z);
344
345 /* eventual dimensions */
346 paramidx = sqlite3_bind_parameter_index(stmt, ":dim");
347 if (paramidx) {
348 if (tile->dimensions) {
349 char *dim = mapcache_util_get_tile_dimkey(ctx, tile, NULL, NULL);
350 sqlite3_bind_text(stmt, paramidx, dim, -1, SQLITE_STATIC);
351 } else {
352 sqlite3_bind_text(stmt, paramidx, "", -1, SQLITE_STATIC);
353 }
354 }
355
356 /* grid */
357 paramidx = sqlite3_bind_parameter_index(stmt, ":grid");
358 if (paramidx) sqlite3_bind_text(stmt, paramidx, tile->grid_link->grid->name, -1, SQLITE_STATIC);
359
360 /* tileset */
361 paramidx = sqlite3_bind_parameter_index(stmt, ":tileset");
362 if (paramidx) sqlite3_bind_text(stmt, paramidx, tile->tileset->name, -1, SQLITE_STATIC);
363
364 /* tile blob data */
365 paramidx = sqlite3_bind_parameter_index(stmt, ":data");
366 if (paramidx) {
367 int written = 0;
368 if(cache->detect_blank) {
369 if(!tile->raw_image) {
370 tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data);
371 GC_CHECK_ERROR(ctx);
372 }
373 if(mapcache_image_blank_color(tile->raw_image) != MAPCACHE_FALSE) {
374 char *buf = apr_palloc(ctx->pool, 5* sizeof(char));
375 buf[0] = '#';
376 memcpy(buf+1,tile->raw_image->data,4);
377 written = 1;
378 sqlite3_bind_blob(stmt, paramidx, buf, 5, SQLITE_STATIC);
379 }
380 }
381 if(!written) {
382 if (!tile->encoded_data) {
383 tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format);
384 GC_CHECK_ERROR(ctx);
385 }
386 if (tile->encoded_data && tile->encoded_data->size) {
387 sqlite3_bind_blob(stmt, paramidx, tile->encoded_data->buf, tile->encoded_data->size, SQLITE_STATIC);
388 } else {
389 sqlite3_bind_text(stmt, paramidx, "", -1, SQLITE_STATIC);
390 }
391 }
392 }
393 }
394
_bind_mbtiles_params(mapcache_context * ctx,void * vstmt,mapcache_cache_sqlite * cache,mapcache_tile * tile)395 static void _bind_mbtiles_params(mapcache_context *ctx, void *vstmt, mapcache_cache_sqlite *cache, mapcache_tile *tile)
396 {
397 sqlite3_stmt *stmt = vstmt;
398 int paramidx;
399 paramidx = sqlite3_bind_parameter_index(stmt, ":x");
400 if (paramidx) sqlite3_bind_int(stmt, paramidx, tile->x);
401
402 /* tile->y */
403 paramidx = sqlite3_bind_parameter_index(stmt, ":y");
404 if (paramidx) sqlite3_bind_int(stmt, paramidx, tile->y);
405
406 /* tile->y */
407 paramidx = sqlite3_bind_parameter_index(stmt, ":z");
408 if (paramidx) sqlite3_bind_int(stmt, paramidx, tile->z);
409
410 /* mbtiles foreign key */
411 paramidx = sqlite3_bind_parameter_index(stmt, ":key");
412 if (paramidx) {
413 char *key = apr_psprintf(ctx->pool,"%d-%d-%d",tile->x,tile->y,tile->z);
414 sqlite3_bind_text(stmt, paramidx, key, -1, SQLITE_STATIC);
415 }
416
417 paramidx = sqlite3_bind_parameter_index(stmt, ":color");
418 if (paramidx) {
419 char *key;
420 assert(tile->raw_image);
421 key = apr_psprintf(ctx->pool,"#%02x%02x%02x%02x",
422 tile->raw_image->data[0],
423 tile->raw_image->data[1],
424 tile->raw_image->data[2],
425 tile->raw_image->data[3]);
426 sqlite3_bind_text(stmt, paramidx, key, -1, SQLITE_STATIC);
427 }
428
429 /* tile blob data */
430 paramidx = sqlite3_bind_parameter_index(stmt, ":data");
431 if (paramidx) {
432 if (!tile->encoded_data) {
433 tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format);
434 GC_CHECK_ERROR(ctx);
435 }
436 if (tile->encoded_data && tile->encoded_data->size) {
437 sqlite3_bind_blob(stmt, paramidx, tile->encoded_data->buf, tile->encoded_data->size, SQLITE_STATIC);
438 } else {
439 sqlite3_bind_text(stmt, paramidx, "", -1, SQLITE_STATIC);
440 }
441 }
442
443 }
444
_mapcache_cache_sqlite_has_tile(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tile)445 static int _mapcache_cache_sqlite_has_tile(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
446 {
447 mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) pcache;
448 mapcache_pooled_connection *pc;
449 struct sqlite_conn *conn;
450 sqlite3_stmt *stmt;
451 int ret;
452 pc = mapcache_sqlite_get_conn(ctx,cache,tile,1);
453 if (GC_HAS_ERROR(ctx)) {
454 if(pc) mapcache_sqlite_release_conn(ctx, pc);
455 if(!tile->tileset->read_only && tile->tileset->source) {
456 /* not an error in this case, as the db file may not have been created yet */
457 ctx->clear_errors(ctx);
458 }
459 return MAPCACHE_FALSE;
460 }
461 conn = SQLITE_CONN(pc);
462 stmt = conn->prepared_statements[HAS_TILE_STMT_IDX];
463 if(!stmt) {
464 sqlite3_prepare(conn->handle, cache->exists_stmt.sql, -1, &conn->prepared_statements[HAS_TILE_STMT_IDX], NULL);
465 stmt = conn->prepared_statements[HAS_TILE_STMT_IDX];
466 }
467 cache->bind_stmt(ctx, stmt, cache, tile);
468 ret = sqlite3_step(stmt);
469 if (ret != SQLITE_DONE && ret != SQLITE_ROW) {
470 ctx->set_error(ctx, 500, "sqlite backend failed on has_tile: %s", sqlite3_errmsg(conn->handle));
471 }
472 if (ret == SQLITE_DONE) {
473 ret = MAPCACHE_FALSE;
474 } else if (ret == SQLITE_ROW) {
475 ret = MAPCACHE_TRUE;
476 }
477 sqlite3_reset(stmt);
478 mapcache_sqlite_release_conn(ctx, pc);
479 return ret;
480 }
481
_mapcache_cache_sqlite_delete(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tile)482 static void _mapcache_cache_sqlite_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
483 {
484 mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) pcache;
485 mapcache_pooled_connection *pc;
486 struct sqlite_conn *conn;
487 sqlite3_stmt *stmt;
488 int ret;
489 pc = mapcache_sqlite_get_conn(ctx,cache,tile,0);
490 if (GC_HAS_ERROR(ctx)) {
491 mapcache_sqlite_release_conn(ctx, pc);
492 return;
493 }
494 conn = SQLITE_CONN(pc);
495 stmt = conn->prepared_statements[SQLITE_DEL_TILE_STMT_IDX];
496 if(!stmt) {
497 sqlite3_prepare(conn->handle, cache->delete_stmt.sql, -1, &conn->prepared_statements[SQLITE_DEL_TILE_STMT_IDX], NULL);
498 stmt = conn->prepared_statements[SQLITE_DEL_TILE_STMT_IDX];
499 }
500 cache->bind_stmt(ctx, stmt, cache, tile);
501 ret = sqlite3_step(stmt);
502 if (ret != SQLITE_DONE && ret != SQLITE_ROW) {
503 ctx->set_error(ctx, 500, "sqlite backend failed on delete: %s", sqlite3_errmsg(conn->handle));
504 }
505 sqlite3_reset(stmt);
506 mapcache_sqlite_release_conn(ctx, pc);
507 }
508
509
_mapcache_cache_mbtiles_delete(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tile)510 static void _mapcache_cache_mbtiles_delete(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
511 {
512 mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) pcache;
513 mapcache_pooled_connection *pc;
514 struct sqlite_conn *conn;
515 sqlite3_stmt *stmt1,*stmt2,*stmt3;
516 int ret;
517 const char *tile_id;
518 size_t tile_id_size;
519 pc = mapcache_sqlite_get_conn(ctx,cache,tile,0);
520 if (GC_HAS_ERROR(ctx)) {
521 mapcache_sqlite_release_conn(ctx, pc);
522 return;
523 }
524 conn = SQLITE_CONN(pc);
525 stmt1 = conn->prepared_statements[MBTILES_DEL_TILE_SELECT_STMT_IDX];
526 stmt2 = conn->prepared_statements[MBTILES_DEL_TILE_STMT1_IDX];
527 stmt3 = conn->prepared_statements[MBTILES_DEL_TILE_STMT2_IDX];
528 if(!stmt1) {
529 sqlite3_prepare(conn->handle, "select tile_id from map where tile_col=:x and tile_row=:y and zoom_level=:z",-1,&conn->prepared_statements[MBTILES_DEL_TILE_SELECT_STMT_IDX], NULL);
530 sqlite3_prepare(conn->handle, "delete from map where tile_col=:x and tile_row=:y and zoom_level=:z", -1, &conn->prepared_statements[MBTILES_DEL_TILE_STMT1_IDX], NULL);
531 sqlite3_prepare(conn->handle, "delete from images where tile_id=:foobar", -1, &conn->prepared_statements[MBTILES_DEL_TILE_STMT2_IDX], NULL);
532 stmt1 = conn->prepared_statements[MBTILES_DEL_TILE_SELECT_STMT_IDX];
533 stmt2 = conn->prepared_statements[MBTILES_DEL_TILE_STMT1_IDX];
534 stmt3 = conn->prepared_statements[MBTILES_DEL_TILE_STMT2_IDX];
535 }
536
537 /* first extract tile_id from the tile we will delete. We need this because we do not know
538 * if the tile is empty or not.
539 * If it is empty, we will not delete the image blob data from the images table */
540 cache->bind_stmt(ctx, stmt1, cache, tile);
541 do {
542 ret = sqlite3_step(stmt1);
543 if (ret != SQLITE_DONE && ret != SQLITE_ROW && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) {
544 ctx->set_error(ctx, 500, "sqlite backend failed on mbtile del 1: %s", sqlite3_errmsg(conn->handle));
545 sqlite3_reset(stmt1);
546 mapcache_sqlite_release_conn(ctx, pc);
547 return;
548 }
549 } while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED);
550 if (ret == SQLITE_DONE) { /* tile does not exist, ignore */
551 sqlite3_reset(stmt1);
552 mapcache_sqlite_release_conn(ctx, pc);
553 return;
554 } else {
555 tile_id = (const char*) sqlite3_column_text(stmt1, 0);
556 tile_id_size = sqlite3_column_bytes(stmt1, 0);
557 }
558
559
560 /* delete the tile from the "map" table */
561 cache->bind_stmt(ctx,stmt2, cache, tile);
562 ret = sqlite3_step(stmt2);
563 if (ret != SQLITE_DONE && ret != SQLITE_ROW) {
564 ctx->set_error(ctx, 500, "sqlite backend failed on mbtile del 2: %s", sqlite3_errmsg(conn->handle));
565 sqlite3_reset(stmt1);
566 sqlite3_reset(stmt2);
567 mapcache_sqlite_release_conn(ctx, pc);
568 return;
569 }
570
571 if(tile_id[0] != '#') {
572 /* the tile isn't empty, we must also delete from the images table */
573 int paramidx = sqlite3_bind_parameter_index(stmt3, ":foobar");
574 if (paramidx) {
575 sqlite3_bind_text(stmt3, paramidx, tile_id, tile_id_size, SQLITE_STATIC);
576 }
577 ret = sqlite3_step(stmt3);
578 if (ret != SQLITE_DONE && ret != SQLITE_ROW) {
579 ctx->set_error(ctx, 500, "sqlite backend failed on mbtile del 3: %s", sqlite3_errmsg(conn->handle));
580 sqlite3_reset(stmt1);
581 sqlite3_reset(stmt2);
582 sqlite3_reset(stmt3);
583 mapcache_sqlite_release_conn(ctx, pc);
584 return;
585 }
586 }
587
588 sqlite3_reset(stmt1);
589 sqlite3_reset(stmt2);
590 sqlite3_reset(stmt3);
591 mapcache_sqlite_release_conn(ctx, pc);
592 }
593
594
595
_single_mbtile_set(mapcache_context * ctx,mapcache_cache_sqlite * cache,mapcache_tile * tile,struct sqlite_conn * conn)596 static void _single_mbtile_set(mapcache_context *ctx, mapcache_cache_sqlite *cache, mapcache_tile *tile, struct sqlite_conn *conn)
597 {
598 sqlite3_stmt *stmt1,*stmt2;
599 int ret;
600 if(!tile->raw_image) {
601 tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data);
602 GC_CHECK_ERROR(ctx);
603 }
604 if(mapcache_image_blank_color(tile->raw_image) != MAPCACHE_FALSE) {
605 stmt1 = conn->prepared_statements[MBTILES_SET_EMPTY_TILE_STMT1_IDX];
606 stmt2 = conn->prepared_statements[MBTILES_SET_EMPTY_TILE_STMT2_IDX];
607 if(!stmt1) {
608 sqlite3_prepare(conn->handle,
609 "insert or ignore into images(tile_id,tile_data) values (:color,:data);",
610 -1, &conn->prepared_statements[MBTILES_SET_EMPTY_TILE_STMT1_IDX], NULL);
611 sqlite3_prepare(conn->handle,
612 "insert or replace into map(tile_column,tile_row,zoom_level,tile_id) values (:x,:y,:z,:color);",
613 -1, &conn->prepared_statements[MBTILES_SET_EMPTY_TILE_STMT2_IDX], NULL);
614 stmt1 = conn->prepared_statements[MBTILES_SET_EMPTY_TILE_STMT1_IDX];
615 stmt2 = conn->prepared_statements[MBTILES_SET_EMPTY_TILE_STMT2_IDX];
616 }
617 cache->bind_stmt(ctx, stmt1, cache, tile);
618 cache->bind_stmt(ctx, stmt2, cache, tile);
619 } else {
620 stmt1 = conn->prepared_statements[MBTILES_SET_TILE_STMT1_IDX];
621 stmt2 = conn->prepared_statements[MBTILES_SET_TILE_STMT2_IDX];
622 if(!stmt1) {
623 sqlite3_prepare(conn->handle,
624 "insert or replace into images(tile_id,tile_data) values (:key,:data);",
625 -1, &conn->prepared_statements[MBTILES_SET_TILE_STMT1_IDX], NULL);
626 sqlite3_prepare(conn->handle,
627 "insert or replace into map(tile_column,tile_row,zoom_level,tile_id) values (:x,:y,:z,:key);",
628 -1, &conn->prepared_statements[MBTILES_SET_TILE_STMT2_IDX], NULL);
629 stmt1 = conn->prepared_statements[MBTILES_SET_TILE_STMT1_IDX];
630 stmt2 = conn->prepared_statements[MBTILES_SET_TILE_STMT2_IDX];
631 }
632 cache->bind_stmt(ctx, stmt1, cache, tile);
633 cache->bind_stmt(ctx, stmt2, cache, tile);
634 }
635 do {
636 ret = sqlite3_step(stmt1);
637 if (ret != SQLITE_DONE && ret != SQLITE_ROW && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) {
638 ctx->set_error(ctx, 500, "mbtiles backend failed on image set: %s (%d)", sqlite3_errmsg(conn->handle), ret);
639 break;
640 }
641 if (ret == SQLITE_BUSY) {
642 sqlite3_reset(stmt1);
643 }
644 } while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED);
645 if(ret == SQLITE_DONE) {
646 do {
647 ret = sqlite3_step(stmt2);
648 if (ret != SQLITE_DONE && ret != SQLITE_ROW && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) {
649 ctx->set_error(ctx, 500, "mbtiles backend failed on map set: %s (%d)", sqlite3_errmsg(conn->handle), ret);
650 break;
651 }
652 if (ret == SQLITE_BUSY) {
653 sqlite3_reset(stmt2);
654 }
655 } while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED);
656 }
657 sqlite3_reset(stmt1);
658 sqlite3_reset(stmt2);
659 }
660
_mapcache_cache_sqlite_get(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tile)661 static int _mapcache_cache_sqlite_get(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
662 {
663 mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) pcache;
664 struct sqlite_conn *conn;
665 sqlite3_stmt *stmt;
666 int ret;
667 mapcache_pooled_connection *pc = mapcache_sqlite_get_conn(ctx,cache,tile,1);
668 if (GC_HAS_ERROR(ctx)) {
669 if(tile->tileset->read_only || !tile->tileset->source) {
670 mapcache_sqlite_release_conn(ctx, pc);
671 return MAPCACHE_FAILURE;
672 } else {
673 /* not an error in this case, as the db file may not have been created yet */
674 ctx->clear_errors(ctx);
675 mapcache_sqlite_release_conn(ctx, pc);
676 return MAPCACHE_CACHE_MISS;
677 }
678 }
679 conn = SQLITE_CONN(pc);
680 stmt = conn->prepared_statements[GET_TILE_STMT_IDX];
681 if(!stmt) {
682 sqlite3_prepare(conn->handle, cache->get_stmt.sql, -1, &conn->prepared_statements[GET_TILE_STMT_IDX], NULL);
683 stmt = conn->prepared_statements[GET_TILE_STMT_IDX];
684 }
685 cache->bind_stmt(ctx, stmt, cache, tile);
686 do {
687 ret = sqlite3_step(stmt);
688 if (ret != SQLITE_DONE && ret != SQLITE_ROW && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) {
689 ctx->set_error(ctx, 500, "sqlite backend failed on get: %s", sqlite3_errmsg(conn->handle));
690 sqlite3_reset(stmt);
691 mapcache_sqlite_release_conn(ctx, pc);
692 return MAPCACHE_FAILURE;
693 }
694 } while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED);
695 if (ret == SQLITE_DONE) {
696 sqlite3_reset(stmt);
697 mapcache_sqlite_release_conn(ctx, pc);
698 return MAPCACHE_CACHE_MISS;
699 } else {
700 const void *blob = sqlite3_column_blob(stmt, 0);
701 int size = sqlite3_column_bytes(stmt, 0);
702 if(size>0 && ((char*)blob)[0] == '#') {
703 tile->encoded_data = mapcache_empty_png_decode(ctx,tile->grid_link->grid->tile_sx, tile->grid_link->grid->tile_sy ,blob,&tile->nodata);
704 } else {
705 tile->encoded_data = mapcache_buffer_create(size, ctx->pool);
706 memcpy(tile->encoded_data->buf, blob, size);
707 tile->encoded_data->size = size;
708 }
709 if (sqlite3_column_count(stmt) > 1) {
710 time_t mtime = sqlite3_column_int64(stmt, 1);
711 apr_time_ansi_put(&(tile->mtime), mtime);
712 }
713 sqlite3_reset(stmt);
714 mapcache_sqlite_release_conn(ctx, pc);
715 return MAPCACHE_SUCCESS;
716 }
717 }
718
_single_sqlitetile_set(mapcache_context * ctx,mapcache_cache_sqlite * cache,mapcache_tile * tile,struct sqlite_conn * conn)719 static void _single_sqlitetile_set(mapcache_context *ctx, mapcache_cache_sqlite *cache, mapcache_tile *tile, struct sqlite_conn *conn)
720 {
721 sqlite3_stmt *stmt = conn->prepared_statements[SQLITE_SET_TILE_STMT_IDX];
722 int ret;
723
724 if(!stmt) {
725 sqlite3_prepare(conn->handle, cache->set_stmt.sql, -1, &conn->prepared_statements[SQLITE_SET_TILE_STMT_IDX], NULL);
726 stmt = conn->prepared_statements[SQLITE_SET_TILE_STMT_IDX];
727 }
728 cache->bind_stmt(ctx, stmt, cache, tile);
729 do {
730 ret = sqlite3_step(stmt);
731 if (ret != SQLITE_DONE && ret != SQLITE_ROW && ret != SQLITE_BUSY && ret != SQLITE_LOCKED) {
732 ctx->set_error(ctx, 500, "sqlite backend failed on set: %s (%d)", sqlite3_errmsg(conn->handle), ret);
733 break;
734 }
735 if (ret == SQLITE_BUSY) {
736 sqlite3_reset(stmt);
737 }
738 } while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED);
739 sqlite3_reset(stmt);
740 }
741
_mapcache_cache_sqlite_set(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tile)742 static void _mapcache_cache_sqlite_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
743 {
744 mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*)pcache;
745 struct sqlite_conn *conn;
746 mapcache_pooled_connection *pc = mapcache_sqlite_get_conn(ctx,cache,tile,0);
747 if (GC_HAS_ERROR(ctx)) {
748 mapcache_sqlite_release_conn(ctx, pc);
749 return;
750 }
751 conn = SQLITE_CONN(pc);
752 sqlite3_exec(conn->handle, "BEGIN TRANSACTION", 0, 0, 0);
753 _single_sqlitetile_set(ctx,cache, tile,conn);
754 if (GC_HAS_ERROR(ctx)) {
755 sqlite3_exec(conn->handle, "ROLLBACK TRANSACTION", 0, 0, 0);
756 } else {
757 sqlite3_exec(conn->handle, "END TRANSACTION", 0, 0, 0);
758 }
759 mapcache_sqlite_release_conn(ctx, pc);
760 }
761
_mapcache_cache_sqlite_multi_set(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tiles,int ntiles)762 static void _mapcache_cache_sqlite_multi_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tiles, int ntiles)
763 {
764 mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*)pcache;
765 int i;
766 struct sqlite_conn *conn;
767 mapcache_pooled_connection *pc = mapcache_sqlite_get_conn(ctx,cache,&tiles[0],0);
768 if (GC_HAS_ERROR(ctx)) {
769 mapcache_sqlite_release_conn(ctx, pc);
770 return;
771 }
772 conn = SQLITE_CONN(pc);
773 sqlite3_exec(conn->handle, "BEGIN TRANSACTION", 0, 0, 0);
774 for (i = 0; i < ntiles; i++) {
775 mapcache_tile *tile = &tiles[i];
776 _single_sqlitetile_set(ctx,cache, tile,conn);
777 if(GC_HAS_ERROR(ctx)) break;
778 }
779 if (GC_HAS_ERROR(ctx)) {
780 sqlite3_exec(conn->handle, "ROLLBACK TRANSACTION", 0, 0, 0);
781 } else {
782 sqlite3_exec(conn->handle, "END TRANSACTION", 0, 0, 0);
783 }
784 mapcache_sqlite_release_conn(ctx, pc);
785 }
786
_mapcache_cache_mbtiles_set(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tile)787 static void _mapcache_cache_mbtiles_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tile)
788 {
789 mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*)pcache;
790 struct sqlite_conn *conn;
791 mapcache_pooled_connection *pc = mapcache_sqlite_get_conn(ctx,cache,tile,0);
792 if (GC_HAS_ERROR(ctx)) {
793 mapcache_sqlite_release_conn(ctx, pc);
794 return;
795 }
796 conn = SQLITE_CONN(pc);
797 if(!tile->raw_image) {
798 tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data);
799 if(GC_HAS_ERROR(ctx)) {
800 mapcache_sqlite_release_conn(ctx, pc);
801 return;
802 }
803 }
804 sqlite3_exec(conn->handle, "BEGIN TRANSACTION", 0, 0, 0);
805 _single_mbtile_set(ctx, cache, tile,conn);
806 if (GC_HAS_ERROR(ctx)) {
807 sqlite3_exec(conn->handle, "ROLLBACK TRANSACTION", 0, 0, 0);
808 } else {
809 sqlite3_exec(conn->handle, "END TRANSACTION", 0, 0, 0);
810 }
811 mapcache_sqlite_release_conn(ctx, pc);
812 }
813
_mapcache_cache_mbtiles_multi_set(mapcache_context * ctx,mapcache_cache * pcache,mapcache_tile * tiles,int ntiles)814 static void _mapcache_cache_mbtiles_multi_set(mapcache_context *ctx, mapcache_cache *pcache, mapcache_tile *tiles, int ntiles)
815 {
816 int i;
817 mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*)pcache;
818 mapcache_pooled_connection *pc;
819 struct sqlite_conn *conn;
820
821 /* decode/encode image data before going into the sqlite write lock */
822 for (i = 0; i < ntiles; i++) {
823 mapcache_tile *tile = &tiles[i];
824 if(!tile->raw_image) {
825 tile->raw_image = mapcache_imageio_decode(ctx, tile->encoded_data);
826 GC_CHECK_ERROR(ctx);
827 }
828 /* only encode to image format if tile is not blank */
829 if (mapcache_image_blank_color(tile->raw_image) != MAPCACHE_TRUE && !tile->encoded_data) {
830 tile->encoded_data = tile->tileset->format->write(ctx, tile->raw_image, tile->tileset->format);
831 GC_CHECK_ERROR(ctx);
832 }
833 }
834 pc = mapcache_sqlite_get_conn(ctx,cache,&tiles[0],0);
835 if (GC_HAS_ERROR(ctx)) {
836 mapcache_sqlite_release_conn(ctx, pc);
837 return;
838 }
839 conn = SQLITE_CONN(pc);
840
841 sqlite3_exec(conn->handle, "BEGIN TRANSACTION", 0, 0, 0);
842 for (i = 0; i < ntiles; i++) {
843 mapcache_tile *tile = &tiles[i];
844 _single_mbtile_set(ctx,cache,tile,conn);
845 if(GC_HAS_ERROR(ctx)) break;
846 }
847 if (GC_HAS_ERROR(ctx)) {
848 sqlite3_exec(conn->handle, "ROLLBACK TRANSACTION", 0, 0, 0);
849 } else {
850 sqlite3_exec(conn->handle, "END TRANSACTION", 0, 0, 0);
851 }
852 mapcache_sqlite_release_conn(ctx, pc);
853 }
854
_mapcache_cache_sqlite_configuration_parse_xml(mapcache_context * ctx,ezxml_t node,mapcache_cache * pcache,mapcache_cfg * config)855 static void _mapcache_cache_sqlite_configuration_parse_xml(mapcache_context *ctx, ezxml_t node, mapcache_cache *pcache, mapcache_cfg *config)
856 {
857 ezxml_t cur_node;
858 mapcache_cache_sqlite *cache;
859 sqlite3_initialize();
860 sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
861 cache = (mapcache_cache_sqlite*) pcache;
862 if ((cur_node = ezxml_child(node, "base")) != NULL) {
863 ctx->set_error(ctx, 500, "sqlite config <base> not supported anymore, use <dbfile>");
864 return;
865 }
866 if ((cur_node = ezxml_child(node, "dbname_template")) != NULL) {
867 ctx->set_error(ctx, 500, "sqlite config <dbname_template> not supported anymore, use a \"multi-sqlite3\" cache type");
868 return;
869 }
870 if ((cur_node = ezxml_child(node, "dbfile")) != NULL) {
871 char *fmt;
872 cache->dbfile = apr_pstrdup(ctx->pool, cur_node->txt);
873 fmt = (char*)ezxml_attr(cur_node,"x_fmt");
874 if(fmt && *fmt) {
875 cache->x_fmt = apr_pstrdup(ctx->pool,fmt);
876 }
877 fmt = (char*)ezxml_attr(cur_node,"y_fmt");
878 if(fmt && *fmt) {
879 cache->y_fmt = apr_pstrdup(ctx->pool,fmt);
880 }
881 fmt = (char*)ezxml_attr(cur_node,"z_fmt");
882 if(fmt && *fmt) {
883 cache->z_fmt = apr_pstrdup(ctx->pool,fmt);
884 }
885 fmt = (char*)ezxml_attr(cur_node,"inv_x_fmt");
886 if(fmt && *fmt) {
887 cache->inv_x_fmt = apr_pstrdup(ctx->pool,fmt);
888 }
889 fmt = (char*)ezxml_attr(cur_node,"inv_y_fmt");
890 if(fmt && *fmt) {
891 cache->inv_y_fmt = apr_pstrdup(ctx->pool,fmt);
892 }
893 fmt = (char*)ezxml_attr(cur_node,"div_x_fmt");
894 if(fmt && *fmt) {
895 cache->div_x_fmt = apr_pstrdup(ctx->pool,fmt);
896 }
897 fmt = (char*)ezxml_attr(cur_node,"div_y_fmt");
898 if(fmt && *fmt) {
899 cache->div_y_fmt = apr_pstrdup(ctx->pool,fmt);
900 }
901 fmt = (char*)ezxml_attr(cur_node,"inv_div_x_fmt");
902 if(fmt && *fmt) {
903 cache->inv_div_x_fmt = apr_pstrdup(ctx->pool,fmt);
904 }
905 fmt = (char*)ezxml_attr(cur_node,"inv_div_y_fmt");
906 if(fmt && *fmt) {
907 cache->inv_div_y_fmt = apr_pstrdup(ctx->pool,fmt);
908 }
909 fmt = (char*)ezxml_attr(cur_node,"top_fmt");
910 if(fmt && *fmt) {
911 cache->top_fmt = apr_pstrdup(ctx->pool,fmt);
912 }
913 fmt = (char*)ezxml_attr(cur_node,"top_x_fmt");
914 if(fmt && *fmt) {
915 cache->top_x_fmt = apr_pstrdup(ctx->pool,fmt);
916 }
917 fmt = (char*)ezxml_attr(cur_node,"top_y_fmt");
918 if(fmt && *fmt) {
919 cache->top_y_fmt = apr_pstrdup(ctx->pool,fmt);
920 }
921 fmt = (char*)ezxml_attr(cur_node,"inv_top_x_fmt");
922 if(fmt && *fmt) {
923 cache->inv_top_x_fmt = apr_pstrdup(ctx->pool,fmt);
924 }
925 fmt = (char*)ezxml_attr(cur_node,"inv_top_y_fmt");
926 if(fmt && *fmt) {
927 cache->inv_top_y_fmt = apr_pstrdup(ctx->pool,fmt);
928 }
929 }
930
931 cache->detect_blank = 0;
932 if ((cur_node = ezxml_child(node, "detect_blank")) != NULL) {
933 if(strcasecmp(cur_node->txt,"false")) {
934 cache->detect_blank = 1;
935 }
936 }
937
938 if ((cur_node = ezxml_child(node, "hitstats")) != NULL) {
939 if (!strcasecmp(cur_node->txt, "true")) {
940 ctx->set_error(ctx, 500, "sqlite config <hitstats> not supported anymore");
941 }
942 }
943 if ((cur_node = ezxml_child(node, "pragma")) != NULL) {
944 cache->pragmas = apr_table_make(ctx->pool,1);
945 while(cur_node) {
946 char *name = (char*)ezxml_attr(cur_node,"name");
947 if(!name || !cur_node->txt || !strlen(cur_node->txt)) {
948 ctx->set_error(ctx,500,"<pragma> missing name attribute");
949 return;
950 }
951 apr_table_set(cache->pragmas,name,cur_node->txt);
952 cur_node = cur_node->next;
953 }
954 }
955 if ((cur_node = ezxml_child(node, "queries")) != NULL) {
956 ezxml_t query_node;
957 if ((query_node = ezxml_child(cur_node, "exists")) != NULL) {
958 cache->exists_stmt.sql = apr_pstrdup(ctx->pool,query_node->txt);
959 }
960 if ((query_node = ezxml_child(cur_node, "get")) != NULL) {
961 cache->get_stmt.sql = apr_pstrdup(ctx->pool,query_node->txt);
962 }
963 if ((query_node = ezxml_child(cur_node, "set")) != NULL) {
964 cache->set_stmt.sql = apr_pstrdup(ctx->pool,query_node->txt);
965 }
966 if ((query_node = ezxml_child(cur_node, "delete")) != NULL) {
967 cache->delete_stmt.sql = apr_pstrdup(ctx->pool,query_node->txt);
968 }
969 if ((query_node = ezxml_child(cur_node, "create")) != NULL) {
970 cache->create_stmt.sql = apr_pstrdup(ctx->pool,query_node->txt);
971 }
972 }
973
974 cur_node = ezxml_child(node,"xcount");
975 if(cur_node && cur_node->txt && *cur_node->txt) {
976 char *endptr;
977 cache->count_x = (int)strtol(cur_node->txt,&endptr,10);
978 if(*endptr != 0) {
979 ctx->set_error(ctx,400,"failed to parse xcount value %s for sqlite cache %s", cur_node->txt,cache->cache.name);
980 return;
981 }
982 }
983 cur_node = ezxml_child(node,"ycount");
984 if(cur_node && cur_node->txt && *cur_node->txt) {
985 char *endptr;
986 cache->count_y = (int)strtol(cur_node->txt,&endptr,10);
987 if(*endptr != 0) {
988 ctx->set_error(ctx,400,"failed to parse ycount value %s for sqlite cache %s", cur_node->txt,cache->cache.name);
989 return;
990 }
991 }
992
993 cur_node = ezxml_child(node,"top");
994 if(cur_node && cur_node->txt && *cur_node->txt) {
995 char *endptr;
996 cache->top = (int)strtol(cur_node->txt,&endptr,10);
997 if(*endptr != 0) {
998 ctx->set_error(ctx,400,"failed to parse top value %s for sqlite cache %s", cur_node->txt,cache->cache.name);
999 return;
1000 }
1001 }
1002 }
1003
1004 /**
1005 * \private \memberof mapcache_cache_sqlite
1006 */
_mapcache_cache_sqlite_configuration_post_config(mapcache_context * ctx,mapcache_cache * pcache,mapcache_cfg * cfg)1007 static void _mapcache_cache_sqlite_configuration_post_config(mapcache_context *ctx,
1008 mapcache_cache *pcache, mapcache_cfg *cfg)
1009 {
1010 mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*)pcache;
1011 if (!cache->dbfile) {
1012 ctx->set_error(ctx, 500, "sqlite cache \"%s\" is missing <dbfile> entry", pcache->name);
1013 return;
1014 }
1015 }
1016
1017 /**
1018 * \private \memberof mapcache_cache_sqlite
1019 */
_mapcache_cache_mbtiles_configuration_post_config(mapcache_context * ctx,mapcache_cache * pcache,mapcache_cfg * cfg)1020 static void _mapcache_cache_mbtiles_configuration_post_config(mapcache_context *ctx,
1021 mapcache_cache *pcache, mapcache_cfg *cfg)
1022 {
1023 /* check that only one tileset/grid references this cache, as mbtiles does
1024 not support multiple tilesets/grids per cache */
1025 #ifdef FIXME
1026 int nrefs = 0;
1027 apr_hash_index_t *tileseti = apr_hash_first(ctx->pool,cfg->tilesets);
1028 while(tileseti) {
1029 mapcache_tileset *tileset;
1030 const void *key;
1031 apr_ssize_t keylen;
1032 apr_hash_this(tileseti,&key,&keylen,(void**)&tileset);
1033 if(tileset->cache == cache) {
1034 nrefs++;
1035 if(nrefs>1) {
1036 ctx->set_error(ctx,500,"mbtiles cache %s is referenced by more than 1 tileset, which is not supported",cache->name);
1037 return;
1038 }
1039 if(tileset->grid_links->nelts > 1) {
1040 ctx->set_error(ctx,500,"mbtiles cache %s is referenced by tileset %s which has more than 1 grid, which is not supported",cache->name,tileset->name);
1041 return;
1042 }
1043 }
1044 tileseti = apr_hash_next(tileseti);
1045 }
1046 #endif
1047 }
1048
mapcache_cache_sqlite_create(mapcache_context * ctx)1049 mapcache_cache* mapcache_cache_sqlite_create(mapcache_context *ctx)
1050 {
1051 mapcache_cache_sqlite *cache = apr_pcalloc(ctx->pool, sizeof (mapcache_cache_sqlite));
1052 if (!cache) {
1053 ctx->set_error(ctx, 500, "failed to allocate sqlite cache");
1054 return NULL;
1055 }
1056 cache->cache.metadata = apr_table_make(ctx->pool, 3);
1057 cache->cache.type = MAPCACHE_CACHE_SQLITE;
1058 cache->cache._tile_delete = _mapcache_cache_sqlite_delete;
1059 cache->cache._tile_get = _mapcache_cache_sqlite_get;
1060 cache->cache._tile_exists = _mapcache_cache_sqlite_has_tile;
1061 cache->cache._tile_set = _mapcache_cache_sqlite_set;
1062 cache->cache._tile_multi_set = _mapcache_cache_sqlite_multi_set;
1063 cache->cache.configuration_post_config = _mapcache_cache_sqlite_configuration_post_config;
1064 cache->cache.configuration_parse_xml = _mapcache_cache_sqlite_configuration_parse_xml;
1065 cache->create_stmt.sql = apr_pstrdup(ctx->pool,
1066 "create table if not exists tiles(tileset text, grid text, x integer, y integer, z integer, data blob, dim text, ctime datetime, primary key(tileset,grid,x,y,z,dim))");
1067 cache->exists_stmt.sql = apr_pstrdup(ctx->pool,
1068 "select 1 from tiles where x=:x and y=:y and z=:z and dim=:dim and tileset=:tileset and grid=:grid");
1069 cache->get_stmt.sql = apr_pstrdup(ctx->pool,
1070 "select data,strftime(\"%s\",ctime) from tiles where tileset=:tileset and grid=:grid and x=:x and y=:y and z=:z and dim=:dim");
1071 cache->set_stmt.sql = apr_pstrdup(ctx->pool,
1072 "insert or replace into tiles(tileset,grid,x,y,z,data,dim,ctime) values (:tileset,:grid,:x,:y,:z,:data,:dim,datetime('now'))");
1073 cache->delete_stmt.sql = apr_pstrdup(ctx->pool,
1074 "delete from tiles where x=:x and y=:y and z=:z and dim=:dim and tileset=:tileset and grid=:grid");
1075 cache->n_prepared_statements = 4;
1076 cache->bind_stmt = _bind_sqlite_params;
1077 cache->detect_blank = 1;
1078 cache->x_fmt = cache->y_fmt = cache->z_fmt
1079 = cache->inv_x_fmt = cache->inv_y_fmt
1080 = cache->div_x_fmt = cache->div_y_fmt
1081 = cache->inv_div_x_fmt = cache->inv_div_y_fmt
1082 = cache->top_fmt = cache->top_x_fmt = cache->top_y_fmt
1083 = cache->inv_top_x_fmt = cache->inv_top_y_fmt = apr_pstrdup(ctx->pool,"%d");
1084 cache->count_x = cache->count_y = -1;
1085 cache->top = -1;
1086 return (mapcache_cache*)cache;
1087 }
1088
1089
1090 /**
1091 * \brief creates and initializes a mapcache_sqlite_cache
1092 */
mapcache_cache_mbtiles_create(mapcache_context * ctx)1093 mapcache_cache* mapcache_cache_mbtiles_create(mapcache_context *ctx)
1094 {
1095 mapcache_cache_sqlite *cache = (mapcache_cache_sqlite*) mapcache_cache_sqlite_create(ctx);
1096 if (!cache) {
1097 return NULL;
1098 }
1099 cache->cache.configuration_post_config = _mapcache_cache_mbtiles_configuration_post_config;
1100 cache->cache._tile_set = _mapcache_cache_mbtiles_set;
1101 cache->cache._tile_multi_set = _mapcache_cache_mbtiles_multi_set;
1102 cache->cache._tile_delete = _mapcache_cache_mbtiles_delete;
1103 cache->create_stmt.sql = apr_pstrdup(ctx->pool,
1104 "create table if not exists images(tile_id text, tile_data blob, primary key(tile_id));"\
1105 "CREATE TABLE IF NOT EXISTS map (zoom_level integer, tile_column integer, tile_row integer, tile_id text, foreign key(tile_id) references images(tile_id), primary key(tile_row,tile_column,zoom_level));"\
1106 "create table if not exists metadata(name text, value text);"\
1107 "create view if not exists tiles AS SELECT map.zoom_level AS zoom_level, map.tile_column AS tile_column, map.tile_row AS tile_row, images.tile_data AS tile_data FROM map JOIN images ON images.tile_id = map.tile_id;"
1108 );
1109 cache->exists_stmt.sql = apr_pstrdup(ctx->pool,
1110 "select 1 from tiles where tile_column=:x and tile_row=:y and zoom_level=:z");
1111 cache->get_stmt.sql = apr_pstrdup(ctx->pool,
1112 "select tile_data from tiles where tile_column=:x and tile_row=:y and zoom_level=:z");
1113 cache->delete_stmt.sql = apr_pstrdup(ctx->pool,
1114 "delete from tiles where tile_column=:x and tile_row=:y and zoom_level=:z");
1115 cache->n_prepared_statements = 9;
1116 cache->bind_stmt = _bind_mbtiles_params;
1117 return (mapcache_cache*) cache;
1118 }
1119 #else
1120
mapcache_cache_sqlite_create(mapcache_context * ctx)1121 mapcache_cache* mapcache_cache_sqlite_create(mapcache_context *ctx) {
1122 ctx->set_error(ctx,400,"SQLITE support not compiled in this version");
1123 return NULL;
1124 }
mapcache_cache_mbtiles_create(mapcache_context * ctx)1125 mapcache_cache* mapcache_cache_mbtiles_create(mapcache_context *ctx) {
1126 ctx->set_error(ctx,400,"SQLITE (MBtiles) support not compiled in this version");
1127 return NULL;
1128 }
1129
1130
1131 #endif
1132
1133 /* vim: ts=2 sts=2 et sw=2
1134 */
1135