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,&params.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,&params);
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