1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * Copyright (C) 2017 Red Hat, Inc. (www.redhat.com)
4 *
5 * This library is free software: you can redistribute it and/or modify it
6 * under the terms of the GNU Lesser General Public License as published by
7 * the Free Software Foundation.
8 *
9 * This library is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public License
15 * along with this library. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18 /**
19 * SECTION: e-cache
20 * @include: libebackend/libebackend.h
21 * @short_description: An SQLite data cache
22 *
23 * The #ECache is an abstract class which consists of the common
24 * parts which can be used by its descendants. It also allows
25 * storing offline state for the stored objects.
26 *
27 * The API is thread safe, with special considerations to be made
28 * around e_cache_lock() and e_cache_unlock() for
29 * the sake of isolating transactions across threads.
30 **/
31
32 #include "evolution-data-server-config.h"
33
34 #include <errno.h>
35 #include <sqlite3.h>
36
37 #include <glib.h>
38 #include <glib/gi18n-lib.h>
39 #include <glib/gstdio.h>
40
41 #include <camel/camel.h>
42 #include <libedataserver/libedataserver.h>
43
44 #include "e-sqlite3-vfs.h"
45
46 #include "e-cache.h"
47
48 #define E_CACHE_KEY_VERSION "version"
49 #define E_CACHE_KEY_REVISION "revision"
50
51 /* The number of SQLite virtual machine instructions that are
52 * evaluated at a time, the user passed GCancellable is
53 * checked between each batch of evaluated instructions.
54 */
55 #define E_CACHE_CANCEL_BATCH_SIZE 200
56
57 /* How many rows to read when e_cache_foreach_update() */
58 #define E_CACHE_UPDATE_BATCH_SIZE 100
59
60 struct _ECachePrivate {
61 gchar *filename;
62 sqlite3 *db;
63
64 GRecMutex lock; /* Main API lock */
65 guint32 in_transaction; /* Nested transaction counter */
66 ECacheLockType lock_type; /* The lock type acquired for the current transaction */
67 GCancellable *cancellable; /* User passed GCancellable, we abort an operation if cancelled */
68
69 guint32 revision_change_frozen;
70 gint revision_counter;
71 gint64 last_revision_time;
72 gboolean needs_revision_change;
73 };
74
75 enum {
76 BEFORE_PUT,
77 BEFORE_REMOVE,
78 REVISION_CHANGED,
79 LAST_SIGNAL
80 };
81
82 static guint signals[LAST_SIGNAL];
83
84 G_DEFINE_QUARK (e-cache-error-quark, e_cache_error)
85
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE(ECache,e_cache,G_TYPE_OBJECT)86 G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (ECache, e_cache, G_TYPE_OBJECT)
87
88 G_DEFINE_BOXED_TYPE (ECacheColumnValues, e_cache_column_values, e_cache_column_values_copy, e_cache_column_values_free)
89 G_DEFINE_BOXED_TYPE (ECacheOfflineChange, e_cache_offline_change, e_cache_offline_change_copy, e_cache_offline_change_free)
90 G_DEFINE_BOXED_TYPE (ECacheColumnInfo, e_cache_column_info, e_cache_column_info_copy, e_cache_column_info_free)
91
92 /**
93 * e_cache_column_values_new:
94 *
95 * Creates a new #ECacheColumnValues to store values for additional columns.
96 * The column names are compared case insensitively.
97 *
98 * Returns: (transfer full): a new #ECacheColumnValues. Free with e_cache_column_values_free(),
99 * when no longer needed.
100 *
101 * Since: 3.26
102 **/
103 ECacheColumnValues *
104 e_cache_column_values_new (void)
105 {
106 return (ECacheColumnValues *) g_hash_table_new_full (camel_strcase_hash, camel_strcase_equal, g_free, g_free);
107 }
108
109 /**
110 * e_cache_column_values_copy:
111 * @other_columns: (nullable): an #ECacheColumnValues
112 *
113 * Returns: (transfer full): Copy of the @other_columns. Free with
114 * e_cache_column_values_free(), when no longer needed.
115 *
116 * Since: 3.26
117 **/
118 ECacheColumnValues *
e_cache_column_values_copy(ECacheColumnValues * other_columns)119 e_cache_column_values_copy (ECacheColumnValues *other_columns)
120 {
121 GHashTableIter iter;
122 gpointer name, value;
123 ECacheColumnValues *copy;
124
125 if (!other_columns)
126 return NULL;
127
128 copy = e_cache_column_values_new ();
129
130 e_cache_column_values_init_iter (other_columns, &iter);
131 while (g_hash_table_iter_next (&iter, &name, &value)) {
132 e_cache_column_values_put (copy, name, value);
133 }
134
135 return copy;
136 }
137
138 /**
139 * e_cache_column_values_free:
140 * @other_columns: (nullable): an #ECacheColumnValues
141 *
142 * Frees previously allocated @other_columns with
143 * e_cache_column_values_new() or e_cache_column_values_copy().
144 *
145 * Since: 3.26
146 **/
147 void
e_cache_column_values_free(ECacheColumnValues * other_columns)148 e_cache_column_values_free (ECacheColumnValues *other_columns)
149 {
150 if (other_columns)
151 g_hash_table_destroy ((GHashTable *) other_columns);
152 }
153
154 /**
155 * e_cache_column_values_put:
156 * @other_columns: an #ECacheColumnValues
157 * @name: a column name
158 * @value: (nullable): a column value
159 *
160 * Puts the @value for column @name. If contains a value for the same
161 * column, then it is replaced. This creates a copy of both @name
162 * and @value.
163 *
164 * Since: 3.26
165 **/
166 void
e_cache_column_values_put(ECacheColumnValues * other_columns,const gchar * name,const gchar * value)167 e_cache_column_values_put (ECacheColumnValues *other_columns,
168 const gchar *name,
169 const gchar *value)
170 {
171 GHashTable *hash_table = (GHashTable *) other_columns;
172
173 g_return_if_fail (other_columns != NULL);
174 g_return_if_fail (name != NULL);
175
176 g_hash_table_insert (hash_table, g_strdup (name), g_strdup (value));
177 }
178
179 /**
180 * e_cache_column_values_take_value:
181 * @other_columns: an #ECacheColumnValues
182 * @name: a column name
183 * @value: (nullable) (in) (transfer full): a column value
184 *
185 * Puts the @value for column @name. If contains a value for the same
186 * column, then it is replaced. This creates a copy of the @name, but
187 * takes owner ship of the @value.
188 *
189 * Since: 3.26
190 **/
191 void
e_cache_column_values_take_value(ECacheColumnValues * other_columns,const gchar * name,gchar * value)192 e_cache_column_values_take_value (ECacheColumnValues *other_columns,
193 const gchar *name,
194 gchar *value)
195 {
196 GHashTable *hash_table = (GHashTable *) other_columns;
197
198 g_return_if_fail (other_columns != NULL);
199 g_return_if_fail (name != NULL);
200
201 g_hash_table_insert (hash_table, g_strdup (name), value);
202 }
203
204 /**
205 * e_cache_column_values_take:
206 * @other_columns: an #ECacheColumnValues
207 * @name: (in) (transfer full): a column name
208 * @value: (nullable) (in) (transfer full): a column value
209 *
210 * Puts the @value for column @name. If contains a value for the same
211 * column, then it is replaced. This creates takes ownership of both
212 * the @name and the @value.
213 *
214 * Since: 3.26
215 **/
216 void
e_cache_column_values_take(ECacheColumnValues * other_columns,gchar * name,gchar * value)217 e_cache_column_values_take (ECacheColumnValues *other_columns,
218 gchar *name,
219 gchar *value)
220 {
221 GHashTable *hash_table = (GHashTable *) other_columns;
222
223 g_return_if_fail (other_columns != NULL);
224 g_return_if_fail (name != NULL);
225
226 g_hash_table_insert (hash_table, name, value);
227 }
228
229 /**
230 * e_cache_column_values_contains:
231 * @other_columns: an #ECacheColumnValues
232 * @name: a column name
233 *
234 * Returns: Whether @other_columns contains column named @name.
235 *
236 * Since: 3.26
237 **/
238 gboolean
e_cache_column_values_contains(ECacheColumnValues * other_columns,const gchar * name)239 e_cache_column_values_contains (ECacheColumnValues *other_columns,
240 const gchar *name)
241 {
242 GHashTable *hash_table = (GHashTable *) other_columns;
243
244 g_return_val_if_fail (other_columns != NULL, FALSE);
245 g_return_val_if_fail (name != NULL, FALSE);
246
247 return g_hash_table_contains (hash_table, name);
248 }
249
250 /**
251 * e_cache_column_values_remove:
252 * @other_columns: an #ECacheColumnValues
253 * @name: a column name
254 *
255 * Removes value for the column named @name from @other_columns.
256 *
257 * Returns: Whether such column existed and had been removed.
258 *
259 * Since: 3.26
260 **/
261 gboolean
e_cache_column_values_remove(ECacheColumnValues * other_columns,const gchar * name)262 e_cache_column_values_remove (ECacheColumnValues *other_columns,
263 const gchar *name)
264 {
265 GHashTable *hash_table = (GHashTable *) other_columns;
266
267 g_return_val_if_fail (other_columns != NULL, FALSE);
268 g_return_val_if_fail (name != NULL, FALSE);
269
270 return g_hash_table_remove (hash_table, name);
271 }
272
273 /**
274 * e_cache_column_values_remove_all:
275 * @other_columns: an #ECacheColumnValues
276 *
277 * Removes all values from the @other_columns, leaving it empty.
278 *
279 * Since: 3.26
280 **/
281 void
e_cache_column_values_remove_all(ECacheColumnValues * other_columns)282 e_cache_column_values_remove_all (ECacheColumnValues *other_columns)
283 {
284 GHashTable *hash_table = (GHashTable *) other_columns;
285
286 g_return_if_fail (other_columns != NULL);
287
288 g_hash_table_remove_all (hash_table);
289 }
290
291 /**
292 * e_cache_column_values_lookup:
293 * @other_columns: an #ECacheColumnValues
294 * @name: a column name
295 *
296 * Looks up currently stored value for the column named @name.
297 * As the values can be %NULL one cannot distinguish between
298 * a column which doesn't have stored any value and a column
299 * which has stored %NULL value. Use e_cache_column_values_contains()
300 * to check whether such column exitst in the @other_columns.
301 * The returned pointer is owned by @other_columns and is valid until
302 * the value is overwritten of the @other_columns freed.
303 *
304 * Returns: Stored value for the column named @name, or %NULL, if
305 * no such column values is stored.
306 *
307 * Since: 3.26
308 **/
309 const gchar *
e_cache_column_values_lookup(ECacheColumnValues * other_columns,const gchar * name)310 e_cache_column_values_lookup (ECacheColumnValues *other_columns,
311 const gchar *name)
312 {
313 GHashTable *hash_table = (GHashTable *) other_columns;
314
315 g_return_val_if_fail (other_columns != NULL, NULL);
316 g_return_val_if_fail (name != NULL, NULL);
317
318 return g_hash_table_lookup (hash_table, name);
319 }
320
321 /**
322 * e_cache_column_values_get_size:
323 * @other_columns: an #ECacheColumnValues
324 *
325 * Returns: How many columns are stored in the @other_columns.
326 *
327 * Since: 3.26
328 **/
329 guint
e_cache_column_values_get_size(ECacheColumnValues * other_columns)330 e_cache_column_values_get_size (ECacheColumnValues *other_columns)
331 {
332 GHashTable *hash_table = (GHashTable *) other_columns;
333
334 g_return_val_if_fail (other_columns != NULL, 0);
335
336 return g_hash_table_size (hash_table);
337 }
338
339 /**
340 * e_cache_column_values_init_iter:
341 * @other_columns: an #ECacheColumnValues
342 * @iter: a #GHashTableIter
343 *
344 * Initialized the @iter, thus the @other_columns can be traversed
345 * with g_hash_table_iter_next(). The key is a column name and
346 * the value is the corresponding column value.
347 *
348 * Since: 3.26
349 **/
350 void
e_cache_column_values_init_iter(ECacheColumnValues * other_columns,GHashTableIter * iter)351 e_cache_column_values_init_iter (ECacheColumnValues *other_columns,
352 GHashTableIter *iter)
353 {
354 GHashTable *hash_table = (GHashTable *) other_columns;
355
356 g_return_if_fail (other_columns != NULL);
357 g_return_if_fail (iter != NULL);
358
359 g_hash_table_iter_init (iter, hash_table);
360 }
361
362 /**
363 * e_cache_offline_change_new:
364 * @uid: a unique object identifier
365 * @revision: (nullable): a revision of the object
366 * @object: (nullable): object itself
367 * @state: an #EOfflineState
368 *
369 * Creates a new #ECacheOfflineChange with the offline @state
370 * information for the given @uid.
371 *
372 * Returns: (transfer full): A new #ECacheOfflineChange. Free it with
373 * e_cache_offline_change_free() when no longer needed.
374 *
375 * Since: 3.26
376 **/
377 ECacheOfflineChange *
e_cache_offline_change_new(const gchar * uid,const gchar * revision,const gchar * object,EOfflineState state)378 e_cache_offline_change_new (const gchar *uid,
379 const gchar *revision,
380 const gchar *object,
381 EOfflineState state)
382 {
383 ECacheOfflineChange *change;
384
385 g_return_val_if_fail (uid != NULL, NULL);
386
387 change = g_slice_new0 (ECacheOfflineChange);
388 change->uid = g_strdup (uid);
389 change->revision = g_strdup (revision);
390 change->object = g_strdup (object);
391 change->state = state;
392
393 return change;
394 }
395
396 /**
397 * e_cache_offline_change_copy:
398 * @change: (nullable): a source #ECacheOfflineChange to copy, or %NULL
399 *
400 * Returns: (transfer full): Copy of the given @change. Free it with
401 * e_cache_offline_change_free() when no longer needed.
402 * If the @change is %NULL, then returns %NULL as well.
403 *
404 * Since: 3.26
405 **/
406 ECacheOfflineChange *
e_cache_offline_change_copy(const ECacheOfflineChange * change)407 e_cache_offline_change_copy (const ECacheOfflineChange *change)
408 {
409 if (!change)
410 return NULL;
411
412 return e_cache_offline_change_new (change->uid, change->revision, change->object, change->state);
413 }
414
415 /**
416 * e_cache_offline_change_free:
417 * @change: (nullable): an #ECacheOfflineChange
418 *
419 * Frees the @change structure, previously allocated with e_cache_offline_change_new()
420 * or e_cache_offline_change_copy().
421 *
422 * Since: 3.26
423 **/
424 void
e_cache_offline_change_free(gpointer change)425 e_cache_offline_change_free (gpointer change)
426 {
427 ECacheOfflineChange *chng = change;
428
429 if (chng) {
430 g_free (chng->uid);
431 g_free (chng->revision);
432 g_free (chng->object);
433 g_slice_free (ECacheOfflineChange, chng);
434 }
435 }
436
437 /**
438 * e_cache_column_info_new:
439 * @name: a column name
440 * @type: a column type
441 * @index_name: (nullable): an index name for this column, or %NULL
442 *
443 * Returns: (transfer full): A new #ECacheColumnInfo. Free it with
444 * e_cache_column_info_free() when no longer needed.
445 *
446 * Since: 3.26
447 **/
448 ECacheColumnInfo *
e_cache_column_info_new(const gchar * name,const gchar * type,const gchar * index_name)449 e_cache_column_info_new (const gchar *name,
450 const gchar *type,
451 const gchar *index_name)
452 {
453 ECacheColumnInfo *info;
454
455 g_return_val_if_fail (name != NULL, NULL);
456 g_return_val_if_fail (type != NULL, NULL);
457
458 info = g_slice_new0 (ECacheColumnInfo);
459 info->name = g_strdup (name);
460 info->type = g_strdup (type);
461 info->index_name = g_strdup (index_name);
462
463 return info;
464 }
465
466 /**
467 * e_cache_column_info_copy:
468 * @info: (nullable): a source #ECacheColumnInfo to copy, or %NULL
469 *
470 * Returns: (transfer full): Copy of the given @info. Free it with
471 * e_cache_column_info_free() when no longer needed.
472 * If the @info is %NULL, then returns %NULL as well.
473 *
474 * Since: 3.26
475 **/
476 ECacheColumnInfo *
e_cache_column_info_copy(const ECacheColumnInfo * info)477 e_cache_column_info_copy (const ECacheColumnInfo *info)
478 {
479 if (!info)
480 return NULL;
481
482 return e_cache_column_info_new (info->name, info->type, info->index_name);
483 }
484
485 /**
486 * e_cache_column_info_free:
487 * @info: (nullable): an #ECacheColumnInfo
488 *
489 * Frees the @info structure, previously allocated with e_cache_column_info_new()
490 * or e_cache_column_info_copy().
491 *
492 * Since: 3.26
493 **/
494 void
e_cache_column_info_free(gpointer info)495 e_cache_column_info_free (gpointer info)
496 {
497 ECacheColumnInfo *nfo = info;
498
499 if (nfo) {
500 g_free (nfo->name);
501 g_free (nfo->type);
502 g_free (nfo->index_name);
503 g_slice_free (ECacheColumnInfo, nfo);
504 }
505 }
506
507 #define E_CACHE_SET_ERROR_FROM_SQLITE(error, code, message, stmt) \
508 G_STMT_START { \
509 if (code == SQLITE_CONSTRAINT) { \
510 g_set_error_literal (error, E_CACHE_ERROR, E_CACHE_ERROR_CONSTRAINT, message); \
511 } else if (code == SQLITE_ABORT || code == SQLITE_INTERRUPT) { \
512 g_set_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Operation cancelled: %s", message); \
513 } else { \
514 gchar *valid_utf8 = e_util_utf8_make_valid (stmt); \
515 g_set_error (error, E_CACHE_ERROR, E_CACHE_ERROR_ENGINE, \
516 "SQLite error code '%d': %s (statement:%s)", code, message, valid_utf8 ? valid_utf8 : stmt); \
517 g_free (valid_utf8); \
518 } \
519 } G_STMT_END
520
521 struct CacheSQLiteExecData {
522 ECache *cache;
523 ECacheSelectFunc callback;
524 gpointer user_data;
525 };
526
527 static gint
e_cache_sqlite_exec_cb(gpointer user_data,gint ncols,gchar ** column_values,gchar ** column_names)528 e_cache_sqlite_exec_cb (gpointer user_data,
529 gint ncols,
530 gchar **column_values,
531 gchar **column_names)
532 {
533 struct CacheSQLiteExecData *cse = user_data;
534
535 g_return_val_if_fail (cse != NULL, SQLITE_MISUSE);
536 g_return_val_if_fail (cse->callback != NULL, SQLITE_MISUSE);
537
538 if (!cse->callback (cse->cache, ncols, (const gchar **) column_names, (const gchar **) column_values, cse->user_data))
539 return SQLITE_ABORT;
540
541 return SQLITE_OK;
542 }
543
544 static gboolean
e_cache_sqlite_exec_internal(ECache * cache,const gchar * stmt,ECacheSelectFunc callback,gpointer user_data,GCancellable * cancellable,GError ** error)545 e_cache_sqlite_exec_internal (ECache *cache,
546 const gchar *stmt,
547 ECacheSelectFunc callback,
548 gpointer user_data,
549 GCancellable *cancellable,
550 GError **error)
551 {
552 struct CacheSQLiteExecData cse;
553 GCancellable *previous_cancellable;
554 gchar *errmsg = NULL;
555 gint ret = -1, retries = 0;
556
557 g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
558 g_return_val_if_fail (stmt != NULL, FALSE);
559
560 g_rec_mutex_lock (&cache->priv->lock);
561
562 previous_cancellable = cache->priv->cancellable;
563 if (cancellable)
564 cache->priv->cancellable = cancellable;
565
566 cse.cache = cache;
567 cse.callback = callback;
568 cse.user_data = user_data;
569
570 ret = sqlite3_exec (cache->priv->db, stmt, callback ? e_cache_sqlite_exec_cb : NULL, &cse, &errmsg);
571
572 while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED || ret == -1) {
573 /* try for ~15 seconds, then give up */
574 if (retries > 150)
575 break;
576 retries++;
577
578 g_clear_pointer (&errmsg, sqlite3_free);
579 g_thread_yield ();
580 g_usleep (100 * 1000); /* Sleep for 100 ms */
581
582 ret = sqlite3_exec (cache->priv->db, stmt, callback ? e_cache_sqlite_exec_cb : NULL, &cse, &errmsg);
583 }
584
585 cache->priv->cancellable = previous_cancellable;
586
587 g_rec_mutex_unlock (&cache->priv->lock);
588
589 if (ret != SQLITE_OK) {
590 E_CACHE_SET_ERROR_FROM_SQLITE (error, ret, errmsg, stmt);
591 sqlite3_free (errmsg);
592 return FALSE;
593 }
594
595 if (errmsg)
596 sqlite3_free (errmsg);
597
598 return TRUE;
599 }
600
601 static gboolean
e_cache_sqlite_exec_printf(ECache * cache,const gchar * format,ECacheSelectFunc callback,gpointer user_data,GCancellable * cancellable,GError ** error,...)602 e_cache_sqlite_exec_printf (ECache *cache,
603 const gchar *format,
604 ECacheSelectFunc callback,
605 gpointer user_data,
606 GCancellable *cancellable,
607 GError **error,
608 ...)
609 {
610 gboolean success;
611 va_list args;
612 gchar *stmt;
613
614 g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
615 g_return_val_if_fail (format != NULL, FALSE);
616
617 va_start (args, error);
618 stmt = sqlite3_vmprintf (format, args);
619
620 success = e_cache_sqlite_exec_internal (cache, stmt, callback, user_data, cancellable, error);
621
622 sqlite3_free (stmt);
623 va_end (args);
624
625 return success;
626 }
627
628 static gboolean
e_cache_read_key_value(ECache * cache,gint ncols,const gchar ** column_names,const gchar ** column_values,gpointer user_data)629 e_cache_read_key_value (ECache *cache,
630 gint ncols,
631 const gchar **column_names,
632 const gchar **column_values,
633 gpointer user_data)
634 {
635 gchar **pvalue = user_data;
636
637 g_return_val_if_fail (ncols == 1, FALSE);
638 g_return_val_if_fail (column_names != NULL, FALSE);
639 g_return_val_if_fail (column_values != NULL, FALSE);
640 g_return_val_if_fail (pvalue != NULL, FALSE);
641
642 if (!*pvalue)
643 *pvalue = g_strdup (column_values[0]);
644
645 return TRUE;
646 }
647
648 static gchar *
e_cache_build_user_key(const gchar * key)649 e_cache_build_user_key (const gchar *key)
650 {
651 return g_strconcat ("user::", key, NULL);
652 }
653
654 static gboolean
e_cache_set_key_internal(ECache * cache,gboolean is_user_key,const gchar * key,const gchar * value,GError ** error)655 e_cache_set_key_internal (ECache *cache,
656 gboolean is_user_key,
657 const gchar *key,
658 const gchar *value,
659 GError **error)
660 {
661 gchar *tmp = NULL;
662 const gchar *usekey;
663 gboolean success;
664
665 if (is_user_key) {
666 tmp = e_cache_build_user_key (key);
667 usekey = tmp;
668 } else {
669 usekey = key;
670 }
671
672 if (value) {
673 success = e_cache_sqlite_exec_printf (cache,
674 "INSERT or REPLACE INTO " E_CACHE_TABLE_KEYS " (key, value) VALUES (%Q, %Q)",
675 NULL, NULL, NULL, error,
676 usekey, value);
677 } else {
678 success = e_cache_sqlite_exec_printf (cache,
679 "DELETE FROM " E_CACHE_TABLE_KEYS " WHERE key = %Q",
680 NULL, NULL, NULL, error,
681 usekey);
682 }
683
684 g_free (tmp);
685
686 return success;
687 }
688
689 static gchar *
e_cache_dup_key_internal(ECache * cache,gboolean is_user_key,const gchar * key,GError ** error)690 e_cache_dup_key_internal (ECache *cache,
691 gboolean is_user_key,
692 const gchar *key,
693 GError **error)
694 {
695 gchar *tmp = NULL;
696 const gchar *usekey;
697 gchar *value = NULL;
698
699 if (is_user_key) {
700 tmp = e_cache_build_user_key (key);
701 usekey = tmp;
702 } else {
703 usekey = key;
704 }
705
706 if (!e_cache_sqlite_exec_printf (cache,
707 "SELECT value FROM " E_CACHE_TABLE_KEYS " WHERE key = %Q",
708 e_cache_read_key_value, &value, NULL, error,
709 usekey)) {
710 g_warn_if_fail (value == NULL);
711 }
712
713 g_free (tmp);
714
715 return value;
716 }
717
718 static gint
e_cache_check_cancelled_cb(gpointer user_data)719 e_cache_check_cancelled_cb (gpointer user_data)
720 {
721 ECache *cache = user_data;
722
723 /* Do not use E_IS_CACHE() here, for performance reasons */
724 g_return_val_if_fail (cache != NULL, SQLITE_ABORT);
725
726 if (cache->priv->cancellable &&
727 g_cancellable_is_cancelled (cache->priv->cancellable)) {
728 return SQLITE_ABORT;
729 }
730
731 return SQLITE_OK;
732 }
733
734 static gboolean
e_cache_init_sqlite(ECache * cache,const gchar * filename,GCancellable * cancellable,GError ** error)735 e_cache_init_sqlite (ECache *cache,
736 const gchar *filename,
737 GCancellable *cancellable,
738 GError **error)
739 {
740 gint ret;
741
742 g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
743 g_return_val_if_fail (filename != NULL, FALSE);
744 g_return_val_if_fail (cache->priv->filename == NULL, FALSE);
745
746 cache->priv->filename = g_strdup (filename);
747
748 ret = sqlite3_open (filename, &cache->priv->db);
749 if (ret != SQLITE_OK) {
750 if (!cache->priv->db) {
751 g_set_error_literal (error, E_CACHE_ERROR, E_CACHE_ERROR_LOAD, _("Out of memory"));
752 } else {
753 const gchar *errmsg = sqlite3_errmsg (cache->priv->db);
754
755 g_set_error (error, E_CACHE_ERROR, E_CACHE_ERROR_ENGINE,
756 _("Can’t open database %s: %s"), filename, errmsg);
757
758 sqlite3_close (cache->priv->db);
759 cache->priv->db = NULL;
760 }
761
762 return FALSE;
763 }
764
765 /* Handle GCancellable */
766 sqlite3_progress_handler (
767 cache->priv->db,
768 E_CACHE_CANCEL_BATCH_SIZE,
769 e_cache_check_cancelled_cb,
770 cache);
771
772 return e_cache_sqlite_exec_internal (cache, "ATTACH DATABASE ':memory:' AS mem", NULL, NULL, cancellable, error) &&
773 e_cache_sqlite_exec_internal (cache, "PRAGMA foreign_keys = ON", NULL, NULL, cancellable, error) &&
774 e_cache_sqlite_exec_internal (cache, "PRAGMA case_sensitive_like = ON", NULL, NULL, cancellable, error);
775 }
776
777 static gboolean
e_cache_garther_column_names_cb(ECache * cache,gint ncols,const gchar * column_names[],const gchar * column_values[],gpointer user_data)778 e_cache_garther_column_names_cb (ECache *cache,
779 gint ncols,
780 const gchar *column_names[],
781 const gchar *column_values[],
782 gpointer user_data)
783 {
784 GHashTable *known_columns = user_data;
785 gint ii;
786
787 g_return_val_if_fail (known_columns != NULL, FALSE);
788 g_return_val_if_fail (column_names != NULL, FALSE);
789 g_return_val_if_fail (column_values != NULL, FALSE);
790
791 for (ii = 0; ii < ncols; ii++) {
792 if (column_names[ii] && camel_strcase_equal (column_names[ii], "name")) {
793 if (column_values[ii])
794 g_hash_table_insert (known_columns, g_strdup (column_values[ii]), NULL);
795 break;
796 }
797 }
798
799 return TRUE;
800 }
801
802 static gboolean
e_cache_init_tables(ECache * cache,const GSList * other_columns,GCancellable * cancellable,GError ** error)803 e_cache_init_tables (ECache *cache,
804 const GSList *other_columns,
805 GCancellable *cancellable,
806 GError **error)
807 {
808 GHashTable *known_columns;
809 GString *objects_stmt;
810 const GSList *link;
811
812 g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
813 g_return_val_if_fail (cache->priv->db != NULL, FALSE);
814
815 if (!e_cache_sqlite_exec_internal (cache,
816 "CREATE TABLE IF NOT EXISTS " E_CACHE_TABLE_KEYS " ("
817 "key TEXT PRIMARY KEY,"
818 "value TEXT)",
819 NULL, NULL, cancellable, error)) {
820 return FALSE;
821 }
822
823 objects_stmt = g_string_new ("");
824
825 g_string_append (objects_stmt, "CREATE TABLE IF NOT EXISTS " E_CACHE_TABLE_OBJECTS " ("
826 E_CACHE_COLUMN_UID " TEXT PRIMARY KEY,"
827 E_CACHE_COLUMN_REVISION " TEXT,"
828 E_CACHE_COLUMN_OBJECT " TEXT,"
829 E_CACHE_COLUMN_STATE " INTEGER");
830
831 for (link = other_columns; link; link = g_slist_next (link)) {
832 const ECacheColumnInfo *info = link->data;
833
834 if (!info)
835 continue;
836
837 g_string_append_c (objects_stmt, ',');
838 g_string_append (objects_stmt, info->name);
839 g_string_append_c (objects_stmt, ' ');
840 g_string_append (objects_stmt, info->type);
841 }
842
843 g_string_append_c (objects_stmt, ')');
844
845 if (!e_cache_sqlite_exec_internal (cache, objects_stmt->str, NULL, NULL, cancellable, error)) {
846 g_string_free (objects_stmt, TRUE);
847
848 return FALSE;
849 }
850
851 g_string_free (objects_stmt, TRUE);
852
853 /* Verify that all other columns are there and remove those unused */
854 known_columns = g_hash_table_new_full (camel_strcase_hash, camel_strcase_equal, g_free, NULL);
855
856 if (!e_cache_sqlite_exec_internal (cache, "PRAGMA table_info (" E_CACHE_TABLE_OBJECTS ")",
857 e_cache_garther_column_names_cb, known_columns, cancellable, error)) {
858 g_string_free (objects_stmt, TRUE);
859
860 return FALSE;
861 }
862
863 g_hash_table_remove (known_columns, E_CACHE_COLUMN_UID);
864 g_hash_table_remove (known_columns, E_CACHE_COLUMN_REVISION);
865 g_hash_table_remove (known_columns, E_CACHE_COLUMN_OBJECT);
866 g_hash_table_remove (known_columns, E_CACHE_COLUMN_STATE);
867
868 for (link = other_columns; link; link = g_slist_next (link)) {
869 const ECacheColumnInfo *info = link->data;
870
871 if (!info)
872 continue;
873
874 if (g_hash_table_remove (known_columns, info->name))
875 continue;
876
877 if (!e_cache_sqlite_exec_printf (cache,
878 "ALTER TABLE " E_CACHE_TABLE_OBJECTS " ADD COLUMN %Q %s",
879 NULL, NULL, cancellable, error,
880 info->name, info->type)) {
881 g_hash_table_destroy (known_columns);
882
883 return FALSE;
884 }
885 }
886
887 g_hash_table_destroy (known_columns);
888
889 for (link = other_columns; link; link = g_slist_next (link)) {
890 const ECacheColumnInfo *info = link->data;
891
892 if (!info || !info->index_name)
893 continue;
894
895 if (!e_cache_sqlite_exec_printf (cache,
896 "CREATE INDEX IF NOT EXISTS %Q ON " E_CACHE_TABLE_OBJECTS " (%s)",
897 NULL, NULL, cancellable, error,
898 info->index_name, info->name)) {
899 return FALSE;
900 }
901 }
902
903 return TRUE;
904 }
905
906 /**
907 * e_cache_initialize_sync:
908 * @cache: an #ECache
909 * @filename: a filename of an SQLite database to use
910 * @other_columns: (element-type ECacheColumnInfo) (nullable): an optional
911 * #GSList with additional columns to add to the objects table
912 * @cancellable: optional #GCancellable object, or %NULL
913 * @error: return location for a #GError, or %NULL
914 *
915 * Initializes the @cache and opens the @filename database.
916 * This should be called by the descendant.
917 *
918 * The @other_columns are added to the objects table (@E_CACHE_TABLE_OBJECTS).
919 * Values for these columns are returned by e_cache_get()
920 * and can be stored with e_cache_put().
921 *
922 * Returns: Whether succeeded.
923 *
924 * Since: 3.26
925 **/
926 gboolean
e_cache_initialize_sync(ECache * cache,const gchar * filename,const GSList * other_columns,GCancellable * cancellable,GError ** error)927 e_cache_initialize_sync (ECache *cache,
928 const gchar *filename,
929 const GSList *other_columns,
930 GCancellable *cancellable,
931 GError **error)
932 {
933 gchar *dirname;
934 gboolean success;
935
936 g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
937 g_return_val_if_fail (cache->priv->filename == NULL, FALSE);
938
939 /* Ensure existance of the directories leading up to 'filename' */
940 dirname = g_path_get_dirname (filename);
941 if (g_mkdir_with_parents (dirname, 0777) < 0) {
942 g_set_error (error, E_CACHE_ERROR, E_CACHE_ERROR_LOAD,
943 _("Can not make parent directory: %s"),
944 g_strerror (errno));
945 g_free (dirname);
946
947 return FALSE;
948 }
949
950 g_free (dirname);
951
952 g_rec_mutex_lock (&cache->priv->lock);
953
954 success = e_cache_init_sqlite (cache, filename, cancellable, error) &&
955 e_cache_init_tables (cache, other_columns, cancellable, error);
956
957 g_rec_mutex_unlock (&cache->priv->lock);
958
959 return success;
960 }
961
962 /**
963 * e_cache_get_filename:
964 * @cache: an #ECache
965 *
966 * Returns: a filename of the @cache, with which it had been initialized.
967 *
968 * Since: 3.26
969 **/
970 const gchar *
e_cache_get_filename(ECache * cache)971 e_cache_get_filename (ECache *cache)
972 {
973 g_return_val_if_fail (E_IS_CACHE (cache), NULL);
974
975 return cache->priv->filename;
976 }
977
978 /**
979 * e_cache_get_version:
980 * @cache: an #ECache
981 *
982 * Returns: A cache data version. This is meant to be used by the descendants.
983 *
984 * Since: 3.26
985 **/
986 gint
e_cache_get_version(ECache * cache)987 e_cache_get_version (ECache *cache)
988 {
989 gchar *value;
990 gint version = -1;
991
992 g_return_val_if_fail (E_IS_CACHE (cache), -1);
993
994 value = e_cache_dup_key_internal (cache, FALSE, E_CACHE_KEY_VERSION, NULL);
995
996 if (value) {
997 version = g_ascii_strtoll (value, NULL, 10);
998 g_free (value);
999 }
1000
1001 return version;
1002 }
1003
1004 /**
1005 * e_cache_set_version:
1006 * @cache: an #ECache
1007 * @version: a cache data version to set
1008 *
1009 * Sets a cache data version. This is meant to be used by the descendants.
1010 * The @version should be greater than zero.
1011 *
1012 * Since: 3.26
1013 **/
1014 void
e_cache_set_version(ECache * cache,gint version)1015 e_cache_set_version (ECache *cache,
1016 gint version)
1017 {
1018 gchar *value;
1019
1020 g_return_if_fail (E_IS_CACHE (cache));
1021 g_return_if_fail (version > 0);
1022
1023 value = g_strdup_printf ("%d", version);
1024 e_cache_set_key_internal (cache, FALSE, E_CACHE_KEY_VERSION, value, NULL);
1025 g_free (value);
1026 }
1027
1028 /**
1029 * e_cache_dup_revision:
1030 * @cache: an #ECache
1031 *
1032 * Returns: (transfer full): A revision of the whole @cache. This is meant to be
1033 * used by the descendants. Free the returned pointer with g_free(), when no
1034 * longer needed.
1035 *
1036 * Since: 3.26
1037 **/
1038 gchar *
e_cache_dup_revision(ECache * cache)1039 e_cache_dup_revision (ECache *cache)
1040 {
1041 g_return_val_if_fail (E_IS_CACHE (cache), NULL);
1042
1043 return e_cache_dup_key_internal (cache, FALSE, E_CACHE_KEY_REVISION, NULL);
1044 }
1045
1046 /**
1047 * e_cache_set_revision:
1048 * @cache: an #ECache
1049 * @revision: (nullable): a revision to set; use %NULL to unset it
1050 *
1051 * Sets the @revision of the whole @cache. This is not meant to be
1052 * used by the descendants, because the revision is updated automatically
1053 * when needed. The descendants can listen to "revision-changed" signal.
1054 *
1055 * Since: 3.26
1056 **/
1057 void
e_cache_set_revision(ECache * cache,const gchar * revision)1058 e_cache_set_revision (ECache *cache,
1059 const gchar *revision)
1060 {
1061 g_return_if_fail (E_IS_CACHE (cache));
1062
1063 e_cache_set_key_internal (cache, FALSE, E_CACHE_KEY_REVISION, revision, NULL);
1064
1065 g_signal_emit (cache, signals[REVISION_CHANGED], 0, NULL);
1066 }
1067
1068 /**
1069 * e_cache_change_revision:
1070 * @cache: an #ECache
1071 *
1072 * Instructs the @cache to change its revision. In case the revision
1073 * change is frozen with e_cache_freeze_revision_change() it notes to
1074 * change the revision once the revision change is fully thaw.
1075 *
1076 * Since: 3.26
1077 **/
1078 void
e_cache_change_revision(ECache * cache)1079 e_cache_change_revision (ECache *cache)
1080 {
1081 g_return_if_fail (E_IS_CACHE (cache));
1082
1083 g_rec_mutex_lock (&cache->priv->lock);
1084
1085 if (e_cache_is_revision_change_frozen (cache)) {
1086 cache->priv->needs_revision_change = TRUE;
1087 } else {
1088 gchar time_string[100] = { 0 };
1089 const struct tm *tm = NULL;
1090 time_t t;
1091 gint64 revision_time;
1092 gchar *revision;
1093
1094 revision_time = g_get_real_time () / (1000 * 1000);
1095 t = (time_t) revision_time;
1096
1097 if (revision_time != cache->priv->last_revision_time) {
1098 cache->priv->revision_counter = 0;
1099 cache->priv->last_revision_time = revision_time;
1100 }
1101
1102 tm = gmtime (&t);
1103 if (tm)
1104 strftime (time_string, 100, "%Y-%m-%dT%H:%M:%SZ", tm);
1105
1106 revision = g_strdup_printf ("%s(%d)", time_string, cache->priv->revision_counter++);
1107
1108 e_cache_set_revision (cache, revision);
1109
1110 g_free (revision);
1111 }
1112
1113 g_rec_mutex_unlock (&cache->priv->lock);
1114 }
1115
1116 /**
1117 * e_cache_freeze_revision_change:
1118 * @cache: an #ECache
1119 *
1120 * Freezes automatic revision change for the @cache. The function
1121 * can be called multiple times, but each such call requires its
1122 * pair function e_cache_thaw_revision_change() call. See also
1123 * e_cache_change_revision().
1124 *
1125 * Since: 3.26
1126 **/
1127 void
e_cache_freeze_revision_change(ECache * cache)1128 e_cache_freeze_revision_change (ECache *cache)
1129 {
1130 g_return_if_fail (E_IS_CACHE (cache));
1131
1132 g_rec_mutex_lock (&cache->priv->lock);
1133
1134 cache->priv->revision_change_frozen++;
1135 g_warn_if_fail (cache->priv->revision_change_frozen != 0);
1136
1137 g_rec_mutex_unlock (&cache->priv->lock);
1138 }
1139
1140 /**
1141 * e_cache_thaw_revision_change:
1142 * @cache: an #ECache
1143 *
1144 * Thaws automatic revision change for the @cache. It's the pair
1145 * function of e_cache_freeze_revision_change().
1146 *
1147 * Since: 3.26
1148 **/
1149 void
e_cache_thaw_revision_change(ECache * cache)1150 e_cache_thaw_revision_change (ECache *cache)
1151 {
1152 g_return_if_fail (E_IS_CACHE (cache));
1153
1154 g_rec_mutex_lock (&cache->priv->lock);
1155
1156 if (!cache->priv->revision_change_frozen) {
1157 g_warn_if_fail (cache->priv->revision_change_frozen > 0);
1158 } else {
1159 cache->priv->revision_change_frozen--;
1160 if (!cache->priv->revision_change_frozen &&
1161 cache->priv->needs_revision_change) {
1162 cache->priv->needs_revision_change = FALSE;
1163 e_cache_change_revision (cache);
1164 }
1165 }
1166
1167 g_rec_mutex_unlock (&cache->priv->lock);
1168 }
1169
1170 /**
1171 * e_cache_is_revision_change_frozen:
1172 * @cache: an #ECache
1173 *
1174 * Returns: Whether automatic revision change for the @cache
1175 * is currently frozen.
1176 *
1177 * Since: 3.26
1178 **/
1179 gboolean
e_cache_is_revision_change_frozen(ECache * cache)1180 e_cache_is_revision_change_frozen (ECache *cache)
1181 {
1182 gboolean frozen;
1183
1184 g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
1185
1186 g_rec_mutex_lock (&cache->priv->lock);
1187 frozen = cache->priv->revision_change_frozen > 0;
1188 g_rec_mutex_unlock (&cache->priv->lock);
1189
1190 return frozen;
1191 }
1192
1193 /**
1194 * e_cache_erase:
1195 * @cache: an #ECache
1196 *
1197 * Erases the cache and all of its content from the disk.
1198 * The only valid operation after this is to free the @cache.
1199 *
1200 * Since: 3.26
1201 **/
1202 void
e_cache_erase(ECache * cache)1203 e_cache_erase (ECache *cache)
1204 {
1205 ECacheClass *klass;
1206
1207 g_return_if_fail (E_IS_CACHE (cache));
1208
1209 if (!cache->priv->db)
1210 return;
1211
1212 klass = E_CACHE_GET_CLASS (cache);
1213 g_return_if_fail (klass != NULL);
1214
1215 if (klass->erase)
1216 klass->erase (cache);
1217
1218 sqlite3_close (cache->priv->db);
1219 cache->priv->db = NULL;
1220
1221 g_unlink (cache->priv->filename);
1222
1223 g_free (cache->priv->filename);
1224 cache->priv->filename = NULL;
1225 }
1226
1227 static gboolean
e_cache_count_rows_cb(ECache * cache,gint ncols,const gchar ** column_names,const gchar ** column_values,gpointer user_data)1228 e_cache_count_rows_cb (ECache *cache,
1229 gint ncols,
1230 const gchar **column_names,
1231 const gchar **column_values,
1232 gpointer user_data)
1233 {
1234 guint *pnrows = user_data;
1235
1236 g_return_val_if_fail (pnrows != NULL, FALSE);
1237
1238 *pnrows = (*pnrows) + 1;
1239
1240 return TRUE;
1241 }
1242
1243 /**
1244 * e_cache_contains:
1245 * @cache: an #ECache
1246 * @uid: a unique identifier of an object
1247 * @deleted_flag: one of #ECacheDeletedFlag enum
1248 *
1249 * Checkes whether the @cache contains an object with
1250 * the given @uid.
1251 *
1252 * Returns: Whether the object had been found.
1253 *
1254 * Since: 3.26
1255 **/
1256 gboolean
e_cache_contains(ECache * cache,const gchar * uid,ECacheDeletedFlag deleted_flag)1257 e_cache_contains (ECache *cache,
1258 const gchar *uid,
1259 ECacheDeletedFlag deleted_flag)
1260 {
1261 guint nrows = 0;
1262
1263 g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
1264 g_return_val_if_fail (uid != NULL, FALSE);
1265
1266 if (deleted_flag == E_CACHE_INCLUDE_DELETED) {
1267 e_cache_sqlite_exec_printf (cache,
1268 "SELECT " E_CACHE_COLUMN_UID " FROM " E_CACHE_TABLE_OBJECTS
1269 " WHERE " E_CACHE_COLUMN_UID " = %Q"
1270 " LIMIT 2",
1271 e_cache_count_rows_cb, &nrows, NULL, NULL,
1272 uid);
1273 } else {
1274 e_cache_sqlite_exec_printf (cache,
1275 "SELECT " E_CACHE_COLUMN_UID " FROM " E_CACHE_TABLE_OBJECTS
1276 " WHERE " E_CACHE_COLUMN_UID " = %Q AND " E_CACHE_COLUMN_STATE " != %d"
1277 " LIMIT 2",
1278 e_cache_count_rows_cb, &nrows, NULL, NULL,
1279 uid, E_OFFLINE_STATE_LOCALLY_DELETED);
1280 }
1281
1282 g_warn_if_fail (nrows <= 1);
1283
1284 return nrows > 0;
1285 }
1286
1287 struct GetObjectData {
1288 gchar *object;
1289 gchar **out_revision;
1290 ECacheColumnValues **out_other_columns;
1291 };
1292
1293 static gboolean
e_cache_get_object_cb(ECache * cache,gint ncols,const gchar ** column_names,const gchar ** column_values,gpointer user_data)1294 e_cache_get_object_cb (ECache *cache,
1295 gint ncols,
1296 const gchar **column_names,
1297 const gchar **column_values,
1298 gpointer user_data)
1299 {
1300 struct GetObjectData *gd = user_data;
1301 gint ii;
1302
1303 g_return_val_if_fail (gd != NULL, FALSE);
1304 g_return_val_if_fail (column_names != NULL, FALSE);
1305 g_return_val_if_fail (column_values != NULL, FALSE);
1306
1307 for (ii = 0; ii < ncols; ii++) {
1308 if (g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_UID) == 0 ||
1309 g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_STATE) == 0) {
1310 /* Skip these two */
1311 } else if (g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_REVISION) == 0) {
1312 if (gd->out_revision)
1313 *gd->out_revision = g_strdup (column_values[ii]);
1314 } else if (g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_OBJECT) == 0) {
1315 gd->object = g_strdup (column_values[ii]);
1316 } else if (gd->out_other_columns) {
1317 if (!*gd->out_other_columns)
1318 *gd->out_other_columns = e_cache_column_values_new ();
1319
1320 e_cache_column_values_put (*gd->out_other_columns, column_names[ii], column_values[ii]);
1321 } else if (gd->object && (!gd->out_revision || *gd->out_revision)) {
1322 /* Short-break the cycle when the other columns are not requested and
1323 the object/revision values were already read. */
1324 break;
1325 }
1326 }
1327
1328 return TRUE;
1329 }
1330
1331 static gchar *
e_cache_get_object_internal(ECache * cache,gboolean include_deleted,const gchar * uid,gchar ** out_revision,ECacheColumnValues ** out_other_columns,GCancellable * cancellable,GError ** error)1332 e_cache_get_object_internal (ECache *cache,
1333 gboolean include_deleted,
1334 const gchar *uid,
1335 gchar **out_revision,
1336 ECacheColumnValues **out_other_columns,
1337 GCancellable *cancellable,
1338 GError **error)
1339 {
1340 struct GetObjectData gd;
1341 gboolean success;
1342
1343 g_return_val_if_fail (E_IS_CACHE (cache), NULL);
1344 g_return_val_if_fail (uid != NULL, NULL);
1345
1346 if (out_revision)
1347 *out_revision = NULL;
1348
1349 if (out_other_columns)
1350 *out_other_columns = NULL;
1351
1352 gd.object = NULL;
1353 gd.out_revision = out_revision;
1354 gd.out_other_columns = out_other_columns;
1355
1356 if (include_deleted) {
1357 success = e_cache_sqlite_exec_printf (cache,
1358 "SELECT * FROM " E_CACHE_TABLE_OBJECTS
1359 " WHERE " E_CACHE_COLUMN_UID " = %Q",
1360 e_cache_get_object_cb, &gd, cancellable, error,
1361 uid);
1362 } else {
1363 success = e_cache_sqlite_exec_printf (cache,
1364 "SELECT * FROM " E_CACHE_TABLE_OBJECTS
1365 " WHERE " E_CACHE_COLUMN_UID " = %Q AND " E_CACHE_COLUMN_STATE " != %d",
1366 e_cache_get_object_cb, &gd, cancellable, error,
1367 uid, E_OFFLINE_STATE_LOCALLY_DELETED);
1368 }
1369
1370 if (success && !gd.object)
1371 g_set_error (error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND, _("Object “%s” not found"), uid);
1372
1373 return gd.object;
1374 }
1375
1376 /**
1377 * e_cache_get:
1378 * @cache: an #ECache
1379 * @uid: a unique identifier of an object
1380 * @out_revision: (out) (nullable) (transfer full): an out variable for a revision
1381 * of the object, or %NULL to ignore
1382 * @out_other_columns: (out) (nullable) (transfer full): an out
1383 * variable for #ECacheColumnValues other columns, as defined when creating the @cache, or %NULL to ignore
1384 * @cancellable: optional #GCancellable object, or %NULL
1385 * @error: return location for a #GError, or %NULL
1386 *
1387 * Returns an object with the given @uid. This function does not consider locally
1388 * deleted objects. The @out_revision is set to the object revision, if not %NULL.
1389 * Free it with g_free() when no longer needed. Similarly the @out_other_columns
1390 * contains a column name to column value strings for additional columns which had
1391 * been requested when calling e_cache_initialize_sync(), if not %NULL.
1392 * Free the returned #ECacheColumnValues with e_cache_column_values_free(), when
1393 * no longer needed.
1394 *
1395 * Returns: (nullable) (transfer full): An object with the given @uid. Free it
1396 * with g_free(), when no longer needed. Returns %NULL on error, like when
1397 * the object could not be found.
1398 *
1399 * Since: 3.26
1400 **/
1401 gchar *
e_cache_get(ECache * cache,const gchar * uid,gchar ** out_revision,ECacheColumnValues ** out_other_columns,GCancellable * cancellable,GError ** error)1402 e_cache_get (ECache *cache,
1403 const gchar *uid,
1404 gchar **out_revision,
1405 ECacheColumnValues **out_other_columns,
1406 GCancellable *cancellable,
1407 GError **error)
1408 {
1409 g_return_val_if_fail (E_IS_CACHE (cache), NULL);
1410 g_return_val_if_fail (uid != NULL, NULL);
1411
1412 return e_cache_get_object_internal (cache, FALSE, uid, out_revision, out_other_columns, cancellable, error);
1413 }
1414
1415 /**
1416 * e_cache_get_object_include_deleted:
1417 * @cache: an #ECache
1418 * @uid: a unique identifier of an object
1419 * @out_revision: (out) (nullable) (transfer full): an out variable for a revision
1420 * of the object, or %NULL to ignore
1421 * @out_other_columns: (out) (nullable) (transfer full): an out
1422 * variable for #ECacheColumnValues other columns, as defined when creating the @cache, or %NULL to ignore
1423 * @cancellable: optional #GCancellable object, or %NULL
1424 * @error: return location for a #GError, or %NULL
1425 *
1426 * The same as e_cache_get(), only considers also locally deleted objects.
1427 *
1428 * Returns: (nullable) (transfer full): An object with the given @uid. Free it
1429 * with g_free(), when no longer needed. Returns %NULL on error, like when
1430 * the object could not be found.
1431 *
1432 * Since: 3.30
1433 **/
1434 gchar *
e_cache_get_object_include_deleted(ECache * cache,const gchar * uid,gchar ** out_revision,ECacheColumnValues ** out_other_columns,GCancellable * cancellable,GError ** error)1435 e_cache_get_object_include_deleted (ECache *cache,
1436 const gchar *uid,
1437 gchar **out_revision,
1438 ECacheColumnValues **out_other_columns,
1439 GCancellable *cancellable,
1440 GError **error)
1441 {
1442 g_return_val_if_fail (E_IS_CACHE (cache), NULL);
1443 g_return_val_if_fail (uid != NULL, NULL);
1444
1445 return e_cache_get_object_internal (cache, TRUE, uid, out_revision, out_other_columns, cancellable, error);
1446 }
1447
1448 static gboolean
e_cache_put_locked(ECache * cache,const gchar * uid,const gchar * revision,const gchar * object,ECacheColumnValues * other_columns,EOfflineState offline_state,gboolean is_replace,GCancellable * cancellable,GError ** error)1449 e_cache_put_locked (ECache *cache,
1450 const gchar *uid,
1451 const gchar *revision,
1452 const gchar *object,
1453 ECacheColumnValues *other_columns,
1454 EOfflineState offline_state,
1455 gboolean is_replace,
1456 GCancellable *cancellable,
1457 GError **error)
1458 {
1459 ECacheColumnValues *my_other_columns = NULL;
1460 gboolean success = TRUE;
1461
1462 g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
1463 g_return_val_if_fail (uid != NULL, FALSE);
1464 g_return_val_if_fail (object != NULL, FALSE);
1465
1466 if (!other_columns) {
1467 my_other_columns = e_cache_column_values_new ();
1468 other_columns = my_other_columns;
1469 }
1470
1471 g_signal_emit (cache,
1472 signals[BEFORE_PUT],
1473 0,
1474 uid, revision, object, other_columns,
1475 is_replace, cancellable, error,
1476 &success);
1477
1478 if (success) {
1479 ECacheClass *klass;
1480
1481 klass = E_CACHE_GET_CLASS (cache);
1482 g_return_val_if_fail (klass != NULL, FALSE);
1483 g_return_val_if_fail (klass->put_locked != NULL, FALSE);
1484
1485 success = klass->put_locked (cache, uid, revision, object, other_columns, offline_state, is_replace, cancellable, error);
1486
1487 if (success)
1488 e_cache_change_revision (cache);
1489 }
1490
1491 e_cache_column_values_free (my_other_columns);
1492
1493 return success;
1494 }
1495
1496 /**
1497 * e_cache_put:
1498 * @cache: an #ECache
1499 * @uid: a unique identifier of an object
1500 * @revision: (nullable): a revision of the object
1501 * @object: the object itself
1502 * @other_columns: (nullable): an #ECacheColumnValues with other columns to set; can be %NULL
1503 * @offline_flag: one of #ECacheOfflineFlag, whether putting this object in offline
1504 * @cancellable: optional #GCancellable object, or %NULL
1505 * @error: return location for a #GError, or %NULL
1506 *
1507 * Stores an object into the cache. Depending on @offline_flag, this update
1508 * the object's offline state accordingly. When the @offline_flag is set
1509 * to %E_CACHE_IS_ONLINE, then it's set to #E_OFFLINE_STATE_SYNCED, like
1510 * to be fully synchronized with the server, regardless of its previous
1511 * offline state. Overwriting locally deleted object behaves like an addition
1512 * of a completely new object.
1513 *
1514 * Returns: Whether succeeded.
1515 *
1516 * Since: 3.26
1517 **/
1518 gboolean
e_cache_put(ECache * cache,const gchar * uid,const gchar * revision,const gchar * object,ECacheColumnValues * other_columns,ECacheOfflineFlag offline_flag,GCancellable * cancellable,GError ** error)1519 e_cache_put (ECache *cache,
1520 const gchar *uid,
1521 const gchar *revision,
1522 const gchar *object,
1523 ECacheColumnValues *other_columns,
1524 ECacheOfflineFlag offline_flag,
1525 GCancellable *cancellable,
1526 GError **error)
1527 {
1528 EOfflineState offline_state;
1529 gboolean success = TRUE, is_replace;
1530
1531 g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
1532 g_return_val_if_fail (uid != NULL, FALSE);
1533 g_return_val_if_fail (object != NULL, FALSE);
1534
1535 e_cache_lock (cache, E_CACHE_LOCK_WRITE);
1536
1537 if (offline_flag == E_CACHE_IS_ONLINE) {
1538 is_replace = e_cache_contains (cache, uid, E_CACHE_EXCLUDE_DELETED);
1539 offline_state = E_OFFLINE_STATE_SYNCED;
1540 } else {
1541 is_replace = e_cache_contains (cache, uid, E_CACHE_INCLUDE_DELETED);
1542 if (is_replace) {
1543 GError *local_error = NULL;
1544
1545 offline_state = e_cache_get_offline_state (cache, uid, cancellable, &local_error);
1546
1547 if (local_error) {
1548 success = FALSE;
1549 g_propagate_error (error, local_error);
1550 } else if (offline_state != E_OFFLINE_STATE_LOCALLY_CREATED) {
1551 offline_state = E_OFFLINE_STATE_LOCALLY_MODIFIED;
1552 }
1553 } else {
1554 offline_state = E_OFFLINE_STATE_LOCALLY_CREATED;
1555 }
1556 }
1557
1558 success = success && e_cache_put_locked (cache, uid, revision, object, other_columns,
1559 offline_state, is_replace, cancellable, error);
1560
1561 e_cache_unlock (cache, success ? E_CACHE_UNLOCK_COMMIT : E_CACHE_UNLOCK_ROLLBACK);
1562
1563 return success;
1564 }
1565
1566 /**
1567 * e_cache_remove:
1568 * @cache: an #ECache
1569 * @uid: a unique identifier of an object
1570 * @offline_flag: one of #ECacheOfflineFlag, whether removing the object in offline
1571 * @cancellable: optional #GCancellable object, or %NULL
1572 * @error: return location for a #GError, or %NULL
1573 *
1574 * Removes the object with the given @uid from the @cache. Based on the @offline_flag,
1575 * it can remove also any information about locally made offline changes. Removing
1576 * the object with %E_CACHE_IS_OFFLINE will still remember it for later use
1577 * with e_cache_get_offline_changes().
1578 *
1579 * Returns: Whether succeeded.
1580 *
1581 * Since: 3.26
1582 **/
1583 gboolean
e_cache_remove(ECache * cache,const gchar * uid,ECacheOfflineFlag offline_flag,GCancellable * cancellable,GError ** error)1584 e_cache_remove (ECache *cache,
1585 const gchar *uid,
1586 ECacheOfflineFlag offline_flag,
1587 GCancellable *cancellable,
1588 GError **error)
1589 {
1590 ECacheClass *klass;
1591 gboolean success = TRUE;
1592
1593 g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
1594 g_return_val_if_fail (uid != NULL, FALSE);
1595
1596 klass = E_CACHE_GET_CLASS (cache);
1597 g_return_val_if_fail (klass != NULL, FALSE);
1598 g_return_val_if_fail (klass->remove_locked != NULL, FALSE);
1599
1600 e_cache_lock (cache, E_CACHE_LOCK_WRITE);
1601
1602 if (offline_flag == E_CACHE_IS_ONLINE) {
1603 success = klass->remove_locked (cache, uid, cancellable, error);
1604 } else {
1605 EOfflineState offline_state;
1606
1607 offline_state = e_cache_get_offline_state (cache, uid, cancellable, error);
1608 if (offline_state == E_OFFLINE_STATE_UNKNOWN) {
1609 success = FALSE;
1610 } else if (offline_state == E_OFFLINE_STATE_LOCALLY_CREATED) {
1611 success = klass->remove_locked (cache, uid, cancellable, error);
1612 } else {
1613 g_signal_emit (cache,
1614 signals[BEFORE_REMOVE],
1615 0,
1616 uid, cancellable, error,
1617 &success);
1618
1619 if (success) {
1620 success = e_cache_set_offline_state (cache, uid,
1621 E_OFFLINE_STATE_LOCALLY_DELETED, cancellable, error);
1622 }
1623 }
1624 }
1625
1626 if (success)
1627 e_cache_change_revision (cache);
1628
1629 e_cache_unlock (cache, success ? E_CACHE_UNLOCK_COMMIT : E_CACHE_UNLOCK_ROLLBACK);
1630
1631 return success;
1632 }
1633
1634 /**
1635 * e_cache_remove_all:
1636 * @cache: an #ECache
1637 * @cancellable: optional #GCancellable object, or %NULL
1638 * @error: return location for a #GError, or %NULL
1639 *
1640 * Removes all objects from the @cache in one call.
1641 *
1642 * Returns: Whether succeeded.
1643 *
1644 * Since: 3.26
1645 **/
1646 gboolean
e_cache_remove_all(ECache * cache,GCancellable * cancellable,GError ** error)1647 e_cache_remove_all (ECache *cache,
1648 GCancellable *cancellable,
1649 GError **error)
1650 {
1651 ECacheClass *klass;
1652 GSList *uids = NULL;
1653 gboolean success;
1654
1655 g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
1656
1657 klass = E_CACHE_GET_CLASS (cache);
1658 g_return_val_if_fail (klass != NULL, FALSE);
1659 g_return_val_if_fail (klass->remove_all_locked != NULL, FALSE);
1660
1661 e_cache_lock (cache, E_CACHE_LOCK_WRITE);
1662
1663 success = e_cache_get_uids (cache, E_CACHE_INCLUDE_DELETED, &uids, NULL, cancellable, error);
1664
1665 if (success && uids)
1666 success = klass->remove_all_locked (cache, uids, cancellable, error);
1667
1668 if (success) {
1669 e_cache_sqlite_maybe_vacuum (cache, cancellable, NULL);
1670 e_cache_change_revision (cache);
1671 }
1672
1673 e_cache_unlock (cache, success ? E_CACHE_UNLOCK_COMMIT : E_CACHE_UNLOCK_ROLLBACK);
1674
1675 g_slist_free_full (uids, g_free);
1676
1677 return success;
1678 }
1679
1680 static gboolean
e_cache_get_uint64_cb(ECache * cache,gint ncols,const gchar ** column_names,const gchar ** column_values,gpointer user_data)1681 e_cache_get_uint64_cb (ECache *cache,
1682 gint ncols,
1683 const gchar **column_names,
1684 const gchar **column_values,
1685 gpointer user_data)
1686 {
1687 guint64 *pui64 = user_data;
1688
1689 g_return_val_if_fail (pui64 != NULL, FALSE);
1690
1691 if (ncols == 1) {
1692 *pui64 = column_values[0] ? g_ascii_strtoull (column_values[0], NULL, 10) : 0;
1693 } else {
1694 *pui64 = 0;
1695 }
1696
1697 return TRUE;
1698 }
1699
1700 static gboolean
e_cache_get_int64_cb(ECache * cache,gint ncols,const gchar ** column_names,const gchar ** column_values,gpointer user_data)1701 e_cache_get_int64_cb (ECache *cache,
1702 gint ncols,
1703 const gchar **column_names,
1704 const gchar **column_values,
1705 gpointer user_data)
1706 {
1707 gint64 *pi64 = user_data;
1708
1709 g_return_val_if_fail (pi64 != NULL, FALSE);
1710
1711 if (ncols == 1) {
1712 *pi64 = column_values[0] ? g_ascii_strtoll (column_values[0], NULL, 10) : 0;
1713 } else {
1714 *pi64 = 0;
1715 }
1716
1717 return TRUE;
1718 }
1719
1720 /**
1721 * e_cache_get_count:
1722 * @cache: an #ECache
1723 * @deleted_flag: one of #ECacheDeletedFlag enum
1724 * @cancellable: optional #GCancellable object, or %NULL
1725 * @error: return location for a #GError, or %NULL
1726 *
1727 * Returns: Count of objects stored in the @cache.
1728 *
1729 * Since: 3.26
1730 **/
1731 guint
e_cache_get_count(ECache * cache,ECacheDeletedFlag deleted_flag,GCancellable * cancellable,GError ** error)1732 e_cache_get_count (ECache *cache,
1733 ECacheDeletedFlag deleted_flag,
1734 GCancellable *cancellable,
1735 GError **error)
1736 {
1737 guint64 nobjects = 0;
1738
1739 g_return_val_if_fail (E_IS_CACHE (cache), 0);
1740
1741 if (deleted_flag == E_CACHE_INCLUDE_DELETED) {
1742 e_cache_sqlite_exec_printf (cache,
1743 "SELECT COUNT(*) FROM " E_CACHE_TABLE_OBJECTS,
1744 e_cache_get_uint64_cb, &nobjects, cancellable, error);
1745 } else {
1746 e_cache_sqlite_exec_printf (cache,
1747 "SELECT COUNT(*) FROM " E_CACHE_TABLE_OBJECTS
1748 " WHERE " E_CACHE_COLUMN_STATE " != %d",
1749 e_cache_get_uint64_cb, &nobjects, NULL, NULL,
1750 E_OFFLINE_STATE_LOCALLY_DELETED);
1751 }
1752
1753 return nobjects;
1754 }
1755
1756 struct GatherRowsData {
1757 GSList **out_uids;
1758 GSList **out_revisions;
1759 GSList **out_objects;
1760 };
1761
1762 static gboolean
e_cache_gather_rows_data_cb(ECache * cache,const gchar * uid,const gchar * revision,const gchar * object,EOfflineState offline_state,gint ncols,const gchar * column_names[],const gchar * column_values[],gpointer user_data)1763 e_cache_gather_rows_data_cb (ECache *cache,
1764 const gchar *uid,
1765 const gchar *revision,
1766 const gchar *object,
1767 EOfflineState offline_state,
1768 gint ncols,
1769 const gchar *column_names[],
1770 const gchar *column_values[],
1771 gpointer user_data)
1772 {
1773 struct GatherRowsData *gd = user_data;
1774
1775 g_return_val_if_fail (gd != NULL, FALSE);
1776
1777 if (gd->out_uids)
1778 *gd->out_uids = g_slist_prepend (*gd->out_uids, g_strdup (uid));
1779
1780 if (gd->out_revisions)
1781 *gd->out_revisions = g_slist_prepend (*gd->out_revisions, g_strdup (revision));
1782
1783 if (gd->out_objects)
1784 *gd->out_objects = g_slist_prepend (*gd->out_objects, g_strdup (object));
1785
1786 return TRUE;
1787 }
1788
1789 /**
1790 * e_cache_get_uids:
1791 * @cache: an #ECache
1792 * @deleted_flag: one of #ECacheDeletedFlag enum
1793 * @out_uids: (out) (transfer full) (element-type utf8): a pointer to #GSList to store the found uid to
1794 * @out_revisions: (out) (transfer full) (element-type utf8) (nullable): a pointer to #GSList to store
1795 * the found revisions to, or %NULL
1796 * @cancellable: optional #GCancellable object, or %NULL
1797 * @error: return location for a #GError, or %NULL
1798 *
1799 * Gets a list of unique object identifiers stored in the @cache, optionally
1800 * together with their revisions. The uids are not returned in any particular
1801 * order, but the position between @out_uids and @out_revisions matches
1802 * the same object.
1803 *
1804 * Both @out_uids and @out_revisions contain newly allocated #GSList, which
1805 * should be freed with g_slist_free_full (slist, g_free); when no longer needed.
1806 *
1807 * Returns: Whether succeeded. It doesn't necessarily mean that there was
1808 * any object stored in the @cache.
1809 *
1810 * Since: 3.26
1811 **/
1812 gboolean
e_cache_get_uids(ECache * cache,ECacheDeletedFlag deleted_flag,GSList ** out_uids,GSList ** out_revisions,GCancellable * cancellable,GError ** error)1813 e_cache_get_uids (ECache *cache,
1814 ECacheDeletedFlag deleted_flag,
1815 GSList **out_uids,
1816 GSList **out_revisions,
1817 GCancellable *cancellable,
1818 GError **error)
1819 {
1820 struct GatherRowsData gr;
1821
1822 g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
1823 g_return_val_if_fail (out_uids, FALSE);
1824
1825 gr.out_uids = out_uids;
1826 gr.out_revisions = out_revisions;
1827 gr.out_objects = NULL;
1828
1829 return e_cache_foreach (cache, deleted_flag, NULL,
1830 e_cache_gather_rows_data_cb, &gr, cancellable, error);
1831 }
1832
1833 /**
1834 * e_cache_get_objects:
1835 * @cache: an #ECache
1836 * @deleted_flag: one of #ECacheDeletedFlag enum
1837 * @out_objects: (out) (transfer full) (element-type utf8): a pointer to #GSList to store the found objects to
1838 * @out_revisions: (out) (transfer full) (element-type utf8) (nullable): a pointer to #GSList to store
1839 * the found revisions to, or %NULL
1840 * @cancellable: optional #GCancellable object, or %NULL
1841 * @error: return location for a #GError, or %NULL
1842 *
1843 * Gets a list of objects stored in the @cache, optionally together with
1844 * their revisions. The uids are not returned in any particular order,
1845 * but the position between @out_objects and @out_revisions matches
1846 * the same object.
1847 *
1848 * Both @out_objects and @out_revisions contain newly allocated #GSList, which
1849 * should be freed with g_slist_free_full (slist, g_free); when no longer needed.
1850 *
1851 * Returns: Whether succeeded. It doesn't necessarily mean that there was
1852 * any object stored in the @cache.
1853 *
1854 * Since: 3.26
1855 **/
1856 gboolean
e_cache_get_objects(ECache * cache,ECacheDeletedFlag deleted_flag,GSList ** out_objects,GSList ** out_revisions,GCancellable * cancellable,GError ** error)1857 e_cache_get_objects (ECache *cache,
1858 ECacheDeletedFlag deleted_flag,
1859 GSList **out_objects,
1860 GSList **out_revisions,
1861 GCancellable *cancellable,
1862 GError **error)
1863 {
1864 struct GatherRowsData gr;
1865
1866 g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
1867 g_return_val_if_fail (out_objects, FALSE);
1868
1869 gr.out_uids = NULL;
1870 gr.out_revisions = out_revisions;
1871 gr.out_objects = out_objects;
1872
1873 return e_cache_foreach (cache, deleted_flag, NULL,
1874 e_cache_gather_rows_data_cb, &gr, cancellable, error);
1875 }
1876
1877 struct ForeachData {
1878 gint uid_index;
1879 gint revision_index;
1880 gint object_index;
1881 gint state_index;
1882 ECacheForeachFunc func;
1883 gpointer user_data;
1884 };
1885
1886 static gboolean
e_cache_foreach_cb(ECache * cache,gint ncols,const gchar * column_names[],const gchar * column_values[],gpointer user_data)1887 e_cache_foreach_cb (ECache *cache,
1888 gint ncols,
1889 const gchar *column_names[],
1890 const gchar *column_values[],
1891 gpointer user_data)
1892 {
1893 struct ForeachData *fe = user_data;
1894 EOfflineState offline_state;
1895
1896 g_return_val_if_fail (fe != NULL, FALSE);
1897 g_return_val_if_fail (fe->func != NULL, FALSE);
1898 g_return_val_if_fail (column_names != NULL, FALSE);
1899 g_return_val_if_fail (column_values != NULL, FALSE);
1900
1901 if (fe->uid_index == -1 ||
1902 fe->revision_index == -1 ||
1903 fe->object_index == -1 ||
1904 fe->state_index == -1) {
1905 gint ii;
1906
1907 for (ii = 0; ii < ncols && (fe->uid_index == -1 ||
1908 fe->revision_index == -1 ||
1909 fe->object_index == -1 ||
1910 fe->state_index == -1); ii++) {
1911 if (!column_names[ii])
1912 continue;
1913
1914 if (fe->uid_index == -1 && g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_UID) == 0) {
1915 fe->uid_index = ii;
1916 } else if (fe->revision_index == -1 && g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_REVISION) == 0) {
1917 fe->revision_index = ii;
1918 } else if (fe->object_index == -1 && g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_OBJECT) == 0) {
1919 fe->object_index = ii;
1920 } else if (fe->state_index == -1 && g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_STATE) == 0) {
1921 fe->state_index = ii;
1922 }
1923 }
1924 }
1925
1926 g_return_val_if_fail (fe->uid_index >= 0 && fe->uid_index < ncols, FALSE);
1927 g_return_val_if_fail (fe->revision_index >= 0 && fe->revision_index < ncols, FALSE);
1928 g_return_val_if_fail (fe->object_index >= 0 && fe->object_index < ncols, FALSE);
1929 g_return_val_if_fail (fe->state_index >= 0 && fe->state_index < ncols, FALSE);
1930
1931 if (!column_values[fe->state_index])
1932 offline_state = E_OFFLINE_STATE_UNKNOWN;
1933 else
1934 offline_state = g_ascii_strtoull (column_values[fe->state_index], NULL, 10);
1935
1936 return fe->func (cache, column_values[fe->uid_index], column_values[fe->revision_index], column_values[fe->object_index],
1937 offline_state, ncols, column_names, column_values, fe->user_data);
1938 }
1939
1940 /**
1941 * e_cache_foreach:
1942 * @cache: an #ECache
1943 * @deleted_flag: one of #ECacheDeletedFlag enum
1944 * @where_clause: (nullable): an optional SQLite WHERE clause part, or %NULL
1945 * @func: (scope call): an #ECacheForeachFunc function to call for each object
1946 * @user_data: user data for the @func
1947 * @cancellable: optional #GCancellable object, or %NULL
1948 * @error: return location for a #GError, or %NULL
1949 *
1950 * Calls @func for each found object, which satisfies the criteria
1951 * for both @deleted_flag and @where_clause.
1952 *
1953 * Note the @func should not call any SQLite commands, because it's invoked
1954 * within a SELECT statement execution.
1955 *
1956 * Returns: Whether succeeded.
1957 *
1958 * Since: 3.26
1959 **/
1960 gboolean
e_cache_foreach(ECache * cache,ECacheDeletedFlag deleted_flag,const gchar * where_clause,ECacheForeachFunc func,gpointer user_data,GCancellable * cancellable,GError ** error)1961 e_cache_foreach (ECache *cache,
1962 ECacheDeletedFlag deleted_flag,
1963 const gchar *where_clause,
1964 ECacheForeachFunc func,
1965 gpointer user_data,
1966 GCancellable *cancellable,
1967 GError **error)
1968 {
1969 struct ForeachData fe;
1970 GString *stmt;
1971 gboolean success;
1972
1973 g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
1974 g_return_val_if_fail (func, FALSE);
1975
1976 stmt = g_string_new ("SELECT * FROM " E_CACHE_TABLE_OBJECTS);
1977
1978 if (where_clause) {
1979 g_string_append (stmt, " WHERE ");
1980
1981 if (deleted_flag == E_CACHE_INCLUDE_DELETED) {
1982 g_string_append (stmt, where_clause);
1983 } else {
1984 g_string_append_printf (stmt, E_CACHE_COLUMN_STATE "!=%d AND (%s)",
1985 E_OFFLINE_STATE_LOCALLY_DELETED, where_clause);
1986 }
1987 } else if (deleted_flag != E_CACHE_INCLUDE_DELETED) {
1988 g_string_append_printf (stmt, " WHERE " E_CACHE_COLUMN_STATE "!=%d", E_OFFLINE_STATE_LOCALLY_DELETED);
1989 }
1990
1991 fe.func = func;
1992 fe.user_data = user_data;
1993 fe.uid_index = -1;
1994 fe.revision_index = -1;
1995 fe.object_index = -1;
1996 fe.state_index = -1;
1997
1998 success = e_cache_sqlite_exec_internal (cache, stmt->str, e_cache_foreach_cb, &fe, cancellable, error);
1999
2000 g_string_free (stmt, TRUE);
2001
2002 return success;
2003 }
2004
2005 struct ForeachUpdateRowData {
2006 gchar *uid;
2007 gchar *revision;
2008 gchar *object;
2009 EOfflineState offline_state;
2010 gint ncols;
2011 GPtrArray *column_values;
2012 };
2013
2014 static void
foreach_update_row_data_free(gpointer ptr)2015 foreach_update_row_data_free (gpointer ptr)
2016 {
2017 struct ForeachUpdateRowData *fr = ptr;
2018
2019 if (fr) {
2020 g_free (fr->uid);
2021 g_free (fr->revision);
2022 g_free (fr->object);
2023 g_ptr_array_free (fr->column_values, TRUE);
2024 g_slice_free (struct ForeachUpdateRowData, fr);
2025 }
2026 }
2027
2028 struct ForeachUpdateData {
2029 gint uid_index;
2030 gint revision_index;
2031 gint object_index;
2032 gint state_index;
2033 GSList *rows; /* struct ForeachUpdateRowData * */
2034 GPtrArray *column_names;
2035 };
2036
2037 static gboolean
e_cache_foreach_update_cb(ECache * cache,gint ncols,const gchar * column_names[],const gchar * column_values[],gpointer user_data)2038 e_cache_foreach_update_cb (ECache *cache,
2039 gint ncols,
2040 const gchar *column_names[],
2041 const gchar *column_values[],
2042 gpointer user_data)
2043 {
2044 struct ForeachUpdateData *fu = user_data;
2045 struct ForeachUpdateRowData *rd;
2046 EOfflineState offline_state;
2047 GPtrArray *cnames, *cvalues;
2048 gint ii;
2049
2050 g_return_val_if_fail (fu != NULL, FALSE);
2051 g_return_val_if_fail (column_names != NULL, FALSE);
2052 g_return_val_if_fail (column_values != NULL, FALSE);
2053
2054 if (fu->uid_index == -1 ||
2055 fu->revision_index == -1 ||
2056 fu->object_index == -1 ||
2057 fu->state_index == -1) {
2058 gint ii;
2059
2060 for (ii = 0; ii < ncols && (fu->uid_index == -1 ||
2061 fu->revision_index == -1 ||
2062 fu->object_index == -1 ||
2063 fu->state_index == -1); ii++) {
2064 if (!column_names[ii])
2065 continue;
2066
2067 if (fu->uid_index == -1 && g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_UID) == 0) {
2068 fu->uid_index = ii;
2069 } else if (fu->revision_index == -1 && g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_REVISION) == 0) {
2070 fu->revision_index = ii;
2071 } else if (fu->object_index == -1 && g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_OBJECT) == 0) {
2072 fu->object_index = ii;
2073 } else if (fu->state_index == -1 && g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_STATE) == 0) {
2074 fu->state_index = ii;
2075 }
2076 }
2077 }
2078
2079 g_return_val_if_fail (fu->uid_index >= 0 && fu->uid_index < ncols, FALSE);
2080 g_return_val_if_fail (fu->revision_index >= 0 && fu->revision_index < ncols, FALSE);
2081 g_return_val_if_fail (fu->object_index >= 0 && fu->object_index < ncols, FALSE);
2082 g_return_val_if_fail (fu->state_index >= 0 && fu->state_index < ncols, FALSE);
2083
2084 if (!column_values[fu->state_index])
2085 offline_state = E_OFFLINE_STATE_UNKNOWN;
2086 else
2087 offline_state = g_ascii_strtoull (column_values[fu->state_index], NULL, 10);
2088
2089 cnames = fu->column_names ? NULL : g_ptr_array_new_full (ncols, g_free);
2090 cvalues = g_ptr_array_new_full (ncols, g_free);
2091
2092 for (ii = 0; ii < ncols; ii++) {
2093 if (fu->uid_index == ii ||
2094 fu->revision_index == ii ||
2095 fu->object_index == ii ||
2096 fu->state_index == ii) {
2097 continue;
2098 }
2099
2100 if (cnames)
2101 g_ptr_array_add (cnames, g_strdup (column_names[ii]));
2102
2103 g_ptr_array_add (cvalues, g_strdup (column_values[ii]));
2104 }
2105
2106 rd = g_slice_new0 (struct ForeachUpdateRowData);
2107 rd->uid = g_strdup (column_values[fu->uid_index]);
2108 rd->revision = g_strdup (column_values[fu->revision_index]);
2109 rd->object = g_strdup (column_values[fu->object_index]);
2110 rd->offline_state = offline_state;
2111 rd->ncols = cvalues->len;
2112 rd->column_values = cvalues;
2113
2114 if (cnames)
2115 fu->column_names = cnames;
2116
2117 fu->rows = g_slist_prepend (fu->rows, rd);
2118
2119 g_return_val_if_fail (fu->column_names && (gint) fu->column_names->len == rd->ncols, FALSE);
2120
2121 return TRUE;
2122 }
2123
2124 /**
2125 * e_cache_foreach_update:
2126 * @cache: an #ECache
2127 * @deleted_flag: one of #ECacheDeletedFlag enum
2128 * @where_clause: (nullable): an optional SQLite WHERE clause part, or %NULL
2129 * @func: (scope call): an #ECacheUpdateFunc function to call for each object
2130 * @user_data: user data for the @func
2131 * @cancellable: optional #GCancellable object, or %NULL
2132 * @error: return location for a #GError, or %NULL
2133 *
2134 * Calls @func for each found object, which satisfies the criteria for both
2135 * @deleted_flag and @where_clause, letting the caller update values where
2136 * necessary. The return value of @func is used to determine whether the call
2137 * was successful, not whether there are any changes to be saved. If anything
2138 * fails during the call then the all changes are reverted.
2139 *
2140 * When there are requested any changes by the @func, this function also
2141 * calls e_cache_copy_missing_to_column_values() to ensure no descendant
2142 * column data is lost.
2143 *
2144 * Returns: Whether succeeded.
2145 *
2146 * Since: 3.26
2147 **/
2148 gboolean
e_cache_foreach_update(ECache * cache,ECacheDeletedFlag deleted_flag,const gchar * where_clause,ECacheUpdateFunc func,gpointer user_data,GCancellable * cancellable,GError ** error)2149 e_cache_foreach_update (ECache *cache,
2150 ECacheDeletedFlag deleted_flag,
2151 const gchar *where_clause,
2152 ECacheUpdateFunc func,
2153 gpointer user_data,
2154 GCancellable *cancellable,
2155 GError **error)
2156 {
2157 GString *stmt_begin;
2158 gchar *uid = NULL;
2159 gint n_results;
2160 gboolean has_where = TRUE;
2161 gboolean success;
2162
2163 g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
2164 g_return_val_if_fail (func, FALSE);
2165
2166 e_cache_lock (cache, E_CACHE_LOCK_WRITE);
2167
2168 stmt_begin = g_string_new ("SELECT * FROM " E_CACHE_TABLE_OBJECTS);
2169
2170 if (where_clause) {
2171 g_string_append (stmt_begin, " WHERE ");
2172
2173 if (deleted_flag == E_CACHE_INCLUDE_DELETED) {
2174 g_string_append (stmt_begin, where_clause);
2175 } else {
2176 g_string_append_printf (stmt_begin, E_CACHE_COLUMN_STATE "!=%d AND (%s)",
2177 E_OFFLINE_STATE_LOCALLY_DELETED, where_clause);
2178 }
2179 } else if (deleted_flag != E_CACHE_INCLUDE_DELETED) {
2180 g_string_append_printf (stmt_begin, " WHERE " E_CACHE_COLUMN_STATE "!=%d", E_OFFLINE_STATE_LOCALLY_DELETED);
2181 } else {
2182 has_where = FALSE;
2183 }
2184
2185 do {
2186 GString *stmt;
2187 GSList *link;
2188 struct ForeachUpdateData fu;
2189
2190 fu.uid_index = -1;
2191 fu.revision_index = -1;
2192 fu.object_index = -1;
2193 fu.state_index = -1;
2194 fu.rows = NULL;
2195 fu.column_names = NULL;
2196
2197 stmt = g_string_new (stmt_begin->str);
2198
2199 if (uid) {
2200 if (has_where)
2201 g_string_append (stmt, " AND ");
2202 else
2203 g_string_append (stmt, " WHERE ");
2204
2205 e_cache_sqlite_stmt_append_printf (stmt, E_CACHE_COLUMN_UID ">%Q", uid);
2206 }
2207
2208 g_string_append_printf (stmt, " ORDER BY " E_CACHE_COLUMN_UID " ASC LIMIT %d", E_CACHE_UPDATE_BATCH_SIZE);
2209
2210 success = e_cache_sqlite_exec_internal (cache, stmt->str, e_cache_foreach_update_cb, &fu, cancellable, error);
2211
2212 g_string_free (stmt, TRUE);
2213
2214 if (success) {
2215 n_results = 0;
2216 fu.rows = g_slist_reverse (fu.rows);
2217
2218 for (link = fu.rows; success && link; link = g_slist_next (link), n_results++) {
2219 struct ForeachUpdateRowData *fr = link->data;
2220
2221 success = fr && fr->column_values && fu.column_names;
2222 if (success) {
2223 gchar *new_revision = NULL;
2224 gchar *new_object = NULL;
2225 EOfflineState new_offline_state = fr->offline_state;
2226 ECacheColumnValues *new_other_columns = NULL;
2227
2228 success = func (cache, fr->uid, fr->revision, fr->object, fr->offline_state,
2229 fr->ncols, (const gchar **) fu.column_names->pdata,
2230 (const gchar **) fr->column_values->pdata,
2231 &new_revision, &new_object, &new_offline_state, &new_other_columns,
2232 user_data);
2233
2234 if (success && (
2235 (new_revision && g_strcmp0 (new_revision, fr->revision) != 0) ||
2236 (new_object && g_strcmp0 (new_object, fr->object) != 0) ||
2237 (new_offline_state != fr->offline_state) ||
2238 (new_other_columns && e_cache_column_values_get_size (new_other_columns) > 0))) {
2239 if (!new_other_columns)
2240 new_other_columns = e_cache_column_values_new ();
2241
2242 e_cache_copy_missing_to_column_values (cache, fr->ncols,
2243 (const gchar **) fu.column_names->pdata,
2244 (const gchar **) fr->column_values->pdata,
2245 new_other_columns);
2246
2247 success = e_cache_put_locked (cache,
2248 fr->uid,
2249 new_revision ? new_revision : fr->revision,
2250 new_object ? new_object : fr->object,
2251 new_other_columns,
2252 new_offline_state,
2253 TRUE, cancellable, error);
2254 }
2255
2256 g_free (new_revision);
2257 g_free (new_object);
2258 e_cache_column_values_free (new_other_columns);
2259
2260 if (!g_slist_next (link)) {
2261 g_free (uid);
2262 uid = g_strdup (fr->uid);
2263 }
2264 }
2265 }
2266 }
2267
2268 g_slist_free_full (fu.rows, foreach_update_row_data_free);
2269 if (fu.column_names)
2270 g_ptr_array_free (fu.column_names, TRUE);
2271 } while (success && n_results == E_CACHE_UPDATE_BATCH_SIZE);
2272
2273 g_string_free (stmt_begin, TRUE);
2274 g_free (uid);
2275
2276 e_cache_unlock (cache, success ? E_CACHE_UNLOCK_COMMIT : E_CACHE_UNLOCK_ROLLBACK);
2277
2278 return success;
2279 }
2280
2281 /**
2282 * e_cache_copy_missing_to_column_values:
2283 * @cache: an #ECache
2284 * @ncols: count of columns, items in column_names and column_values
2285 * @column_names: (array length=ncols) (element-type utf8): column names
2286 * @column_values: (array length=ncols) (element-type utf8): column values
2287 * @other_columns: (inout): an #ECacheColumnValues to fill
2288 *
2289 * Adds every column value which is not part of the @other_columns to it,
2290 * except of E_CACHE_COLUMN_UID, E_CACHE_COLUMN_REVISION, E_CACHE_COLUMN_OBJECT
2291 * and E_CACHE_COLUMN_STATE columns.
2292 *
2293 * This can be used within the callback of e_cache_foreach_update().
2294 *
2295 * Since: 3.32
2296 **/
2297 void
e_cache_copy_missing_to_column_values(ECache * cache,gint ncols,const gchar * column_names[],const gchar * column_values[],ECacheColumnValues * other_columns)2298 e_cache_copy_missing_to_column_values (ECache *cache,
2299 gint ncols,
2300 const gchar *column_names[],
2301 const gchar *column_values[],
2302 ECacheColumnValues *other_columns)
2303 {
2304 gint ii;
2305
2306 g_return_if_fail (E_IS_CACHE (cache));
2307 g_return_if_fail (column_names != NULL);
2308 g_return_if_fail (column_values != NULL);
2309 g_return_if_fail (other_columns != NULL);
2310
2311 for (ii = 0; ii < ncols; ii++) {
2312 if (column_names[ii] && column_values[ii] &&
2313 !e_cache_column_values_contains (other_columns, column_names[ii]) &&
2314 g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_UID) != 0 &&
2315 g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_REVISION) != 0 &&
2316 g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_OBJECT) != 0 &&
2317 g_ascii_strcasecmp (column_names[ii], E_CACHE_COLUMN_STATE) != 0) {
2318 e_cache_column_values_put (other_columns, column_names[ii], column_values[ii]);
2319 }
2320 }
2321 }
2322
2323 /**
2324 * e_cache_get_offline_state:
2325 * @cache: an #ECache
2326 * @uid: a unique identifier of an object
2327 * @cancellable: optional #GCancellable object, or %NULL
2328 * @error: return location for a #GError, or %NULL
2329 *
2330 * Returns: Current offline state #EOfflineState for the given object.
2331 * It returns %E_OFFLINE_STATE_UNKNOWN when the object could not be
2332 * found or other error happened.
2333 *
2334 * Since: 3.26
2335 **/
2336 EOfflineState
e_cache_get_offline_state(ECache * cache,const gchar * uid,GCancellable * cancellable,GError ** error)2337 e_cache_get_offline_state (ECache *cache,
2338 const gchar *uid,
2339 GCancellable *cancellable,
2340 GError **error)
2341 {
2342 EOfflineState offline_state = E_OFFLINE_STATE_UNKNOWN;
2343 gint64 value = offline_state;
2344
2345 g_return_val_if_fail (E_IS_CACHE (cache), E_OFFLINE_STATE_UNKNOWN);
2346 g_return_val_if_fail (uid != NULL, E_OFFLINE_STATE_UNKNOWN);
2347
2348 if (!e_cache_contains (cache, uid, E_CACHE_INCLUDE_DELETED)) {
2349 g_set_error (error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND, _("Object “%s” not found"), uid);
2350 return offline_state;
2351 }
2352
2353 if (e_cache_sqlite_exec_printf (cache,
2354 "SELECT " E_CACHE_COLUMN_STATE " FROM " E_CACHE_TABLE_OBJECTS
2355 " WHERE " E_CACHE_COLUMN_UID " = %Q",
2356 e_cache_get_int64_cb, &value, cancellable, error,
2357 uid)) {
2358 offline_state = value;
2359 }
2360
2361 return offline_state;
2362 }
2363
2364 /**
2365 * e_cache_set_offline_state:
2366 * @cache: an #ECache
2367 * @uid: a unique identifier of an object
2368 * @state: an #EOfflineState to set
2369 * @cancellable: optional #GCancellable object, or %NULL
2370 * @error: return location for a #GError, or %NULL
2371 *
2372 * Sets an offline @state for the object identified by @uid.
2373 *
2374 * Returns: Whether succeeded.
2375 *
2376 * Since: 3.26
2377 **/
2378 gboolean
e_cache_set_offline_state(ECache * cache,const gchar * uid,EOfflineState state,GCancellable * cancellable,GError ** error)2379 e_cache_set_offline_state (ECache *cache,
2380 const gchar *uid,
2381 EOfflineState state,
2382 GCancellable *cancellable,
2383 GError **error)
2384 {
2385 g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
2386 g_return_val_if_fail (uid != NULL, FALSE);
2387
2388 if (!e_cache_contains (cache, uid, E_CACHE_INCLUDE_DELETED)) {
2389 g_set_error (error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND, _("Object “%s” not found"), uid);
2390 return FALSE;
2391 }
2392
2393 return e_cache_sqlite_exec_printf (cache,
2394 "UPDATE " E_CACHE_TABLE_OBJECTS " SET " E_CACHE_COLUMN_STATE "=%d"
2395 " WHERE " E_CACHE_COLUMN_UID " = %Q",
2396 NULL, NULL, cancellable, error,
2397 state, uid);
2398 }
2399
2400 static gboolean
e_cache_get_offline_changes_cb(ECache * cache,const gchar * uid,const gchar * revision,const gchar * object,EOfflineState offline_state,gint ncols,const gchar * column_names[],const gchar * column_values[],gpointer user_data)2401 e_cache_get_offline_changes_cb (ECache *cache,
2402 const gchar *uid,
2403 const gchar *revision,
2404 const gchar *object,
2405 EOfflineState offline_state,
2406 gint ncols,
2407 const gchar *column_names[],
2408 const gchar *column_values[],
2409 gpointer user_data)
2410 {
2411 GSList **pchanges = user_data;
2412
2413 g_return_val_if_fail (pchanges != NULL, FALSE);
2414
2415 if (offline_state == E_OFFLINE_STATE_LOCALLY_CREATED ||
2416 offline_state == E_OFFLINE_STATE_LOCALLY_MODIFIED ||
2417 offline_state == E_OFFLINE_STATE_LOCALLY_DELETED) {
2418 *pchanges = g_slist_prepend (*pchanges, e_cache_offline_change_new (uid, revision, object, offline_state));
2419 }
2420
2421 return TRUE;
2422 }
2423
2424 /**
2425 * e_cache_get_offline_changes:
2426 * @cache: an #ECache
2427 * @cancellable: optional #GCancellable object, or %NULL
2428 * @error: return location for a #GError, or %NULL
2429 *
2430 * Gathers the list of all offline changes being done so far.
2431 * The returned #GSList contains #ECacheOfflineChange structure.
2432 * Use e_cache_clear_offline_changes() to clear all offline
2433 * changes at once.
2434 *
2435 * Returns: (transfer full) (element-type ECacheOfflineChange): A newly allocated list of all
2436 * offline changes. Free it with g_slist_free_full (slist, e_cache_offline_change_free);
2437 * when no longer needed.
2438 *
2439 * Since: 3.26
2440 **/
2441 GSList *
e_cache_get_offline_changes(ECache * cache,GCancellable * cancellable,GError ** error)2442 e_cache_get_offline_changes (ECache *cache,
2443 GCancellable *cancellable,
2444 GError **error)
2445 {
2446 GSList *changes = NULL;
2447 gchar *stmt;
2448
2449 g_return_val_if_fail (E_IS_CACHE (cache), NULL);
2450
2451 stmt = e_cache_sqlite_stmt_printf (E_CACHE_COLUMN_STATE "!=%d", E_OFFLINE_STATE_SYNCED);
2452
2453 if (!e_cache_foreach (cache, E_CACHE_INCLUDE_DELETED, stmt, e_cache_get_offline_changes_cb, &changes, cancellable, error)) {
2454 g_slist_free_full (changes, e_cache_offline_change_free);
2455 changes = NULL;
2456 }
2457
2458 e_cache_sqlite_stmt_free (stmt);
2459
2460 return changes;
2461 }
2462
2463 /**
2464 * e_cache_clear_offline_changes:
2465 * @cache: an #ECache
2466 * @cancellable: optional #GCancellable object, or %NULL
2467 * @error: return location for a #GError, or %NULL
2468 *
2469 * Marks all objects as being fully synchronized with the server and
2470 * removes those which are marked as locally deleted.
2471 *
2472 * Returns: Whether succeeded.
2473 *
2474 * Since: 3.26
2475 **/
2476 gboolean
e_cache_clear_offline_changes(ECache * cache,GCancellable * cancellable,GError ** error)2477 e_cache_clear_offline_changes (ECache *cache,
2478 GCancellable *cancellable,
2479 GError **error)
2480 {
2481 ECacheClass *klass;
2482 gboolean success;
2483
2484 g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
2485
2486 klass = E_CACHE_GET_CLASS (cache);
2487 g_return_val_if_fail (klass != NULL, FALSE);
2488 g_return_val_if_fail (klass->clear_offline_changes_locked != NULL, FALSE);
2489
2490 e_cache_lock (cache, E_CACHE_LOCK_WRITE);
2491
2492 success = klass->clear_offline_changes_locked (cache, cancellable, error);
2493
2494 e_cache_unlock (cache, success ? E_CACHE_UNLOCK_COMMIT : E_CACHE_UNLOCK_ROLLBACK);
2495
2496 return success;
2497 }
2498
2499 /**
2500 * e_cache_set_key:
2501 * @cache: an #ECache
2502 * @key: a key name
2503 * @value: (nullable): a value to set, or %NULL to delete the key
2504 * @error: return location for a #GError, or %NULL
2505 *
2506 * Sets a @value of the user @key, or deletes it, if the @value is %NULL.
2507 *
2508 * Returns: Whether succeeded.
2509 *
2510 * Since: 3.26
2511 **/
2512 gboolean
e_cache_set_key(ECache * cache,const gchar * key,const gchar * value,GError ** error)2513 e_cache_set_key (ECache *cache,
2514 const gchar *key,
2515 const gchar *value,
2516 GError **error)
2517 {
2518 g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
2519 g_return_val_if_fail (key != NULL, FALSE);
2520
2521 return e_cache_set_key_internal (cache, TRUE, key, value, error);
2522 }
2523
2524 /**
2525 * e_cache_dup_key:
2526 * @cache: an #ECache
2527 * @key: a key name
2528 * @error: return location for a #GError, or %NULL
2529 *
2530 * Returns: (transfer full): a value of the @key. Free the returned string
2531 * with g_free(), when no longer needed.
2532 *
2533 * Since: 3.26
2534 **/
2535 gchar *
e_cache_dup_key(ECache * cache,const gchar * key,GError ** error)2536 e_cache_dup_key (ECache *cache,
2537 const gchar *key,
2538 GError **error)
2539 {
2540 g_return_val_if_fail (E_IS_CACHE (cache), NULL);
2541 g_return_val_if_fail (key != NULL, NULL);
2542
2543 return e_cache_dup_key_internal (cache, TRUE, key, error);
2544 }
2545
2546 /**
2547 * e_cache_set_key_int:
2548 * @cache: an #ECache
2549 * @key: a key name
2550 * @value: an integer value to set
2551 * @error: return location for a #GError, or %NULL
2552 *
2553 * Sets an integer @value for the user @key.
2554 *
2555 * Returns: Whether succeeded.
2556 *
2557 * Since: 3.26
2558 **/
2559 gboolean
e_cache_set_key_int(ECache * cache,const gchar * key,gint value,GError ** error)2560 e_cache_set_key_int (ECache *cache,
2561 const gchar *key,
2562 gint value,
2563 GError **error)
2564 {
2565 gchar *str_value;
2566 gboolean success;
2567
2568 g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
2569 g_return_val_if_fail (key != NULL, FALSE);
2570
2571 str_value = g_strdup_printf ("%d", value);
2572 success = e_cache_set_key (cache, key, str_value, error);
2573 g_free (str_value);
2574
2575 return success;
2576 }
2577
2578 /**
2579 * e_cache_get_key_int:
2580 * @cache: an #ECache
2581 * @key: a key name
2582 * @error: return location for a #GError, or %NULL
2583 *
2584 * Reads the user @key value as an integer.
2585 *
2586 * Returns: The user @key value or -1 on error.
2587 *
2588 * Since: 3.26
2589 **/
2590 gint
e_cache_get_key_int(ECache * cache,const gchar * key,GError ** error)2591 e_cache_get_key_int (ECache *cache,
2592 const gchar *key,
2593 GError **error)
2594 {
2595 gchar *str_value;
2596 gint value;
2597
2598 g_return_val_if_fail (E_IS_CACHE (cache), -1);
2599
2600 str_value = e_cache_dup_key (cache, key, error);
2601 if (!str_value)
2602 return -1;
2603
2604 value = g_ascii_strtoll (str_value, NULL, 10);
2605 g_free (str_value);
2606
2607 return value;
2608 }
2609
2610 /**
2611 * e_cache_lock:
2612 * @cache: an #ECache
2613 * @lock_type: an #ECacheLockType
2614 *
2615 * Locks the @cache thus other threads cannot use it.
2616 * This can be called recursively within one thread.
2617 * Each call should have its pair e_cache_unlock().
2618 *
2619 * Since: 3.26
2620 **/
2621 void
e_cache_lock(ECache * cache,ECacheLockType lock_type)2622 e_cache_lock (ECache *cache,
2623 ECacheLockType lock_type)
2624 {
2625 g_return_if_fail (E_IS_CACHE (cache));
2626
2627 g_rec_mutex_lock (&cache->priv->lock);
2628
2629 cache->priv->in_transaction++;
2630 g_return_if_fail (cache->priv->in_transaction > 0);
2631
2632 if (cache->priv->in_transaction == 1) {
2633 /* It's important to make the distinction between a
2634 * transaction which will read or one which will write.
2635 *
2636 * While it's not well documented, when receiving the SQLITE_BUSY
2637 * error status, one can only safely retry at the beginning of
2638 * the transaction.
2639 *
2640 * If a transaction is 'upgraded' to require a writer lock
2641 * half way through the transaction and SQLITE_BUSY is returned,
2642 * the whole transaction would need to be retried from the beginning.
2643 */
2644 cache->priv->lock_type = lock_type;
2645
2646 switch (lock_type) {
2647 case E_CACHE_LOCK_READ:
2648 e_cache_sqlite_exec_internal (cache, "BEGIN", NULL, NULL, NULL, NULL);
2649 break;
2650 case E_CACHE_LOCK_WRITE:
2651 e_cache_sqlite_exec_internal (cache, "BEGIN IMMEDIATE", NULL, NULL, NULL, NULL);
2652 break;
2653 }
2654 } else {
2655 /* Warn about cases where a read transaction might be upgraded */
2656 if (lock_type == E_CACHE_LOCK_WRITE && cache->priv->lock_type == E_CACHE_LOCK_READ)
2657 g_warning (
2658 "A nested transaction wants to write, "
2659 "but the outermost transaction was started "
2660 "without a writer lock.");
2661 }
2662 }
2663
2664 /**
2665 * e_cache_unlock:
2666 * @cache: an #ECache
2667 * @action: an #ECacheUnlockAction
2668 *
2669 * Unlocks the cache which was previouly locked with e_cache_lock().
2670 * The cache locked with #E_CACHE_LOCK_WRITE should use either
2671 * @action #E_CACHE_UNLOCK_COMMIT or #E_CACHE_UNLOCK_ROLLBACK,
2672 * while the #E_CACHE_LOCK_READ should use #E_CACHE_UNLOCK_NONE @action.
2673 *
2674 * Since: 3.26
2675 **/
2676 void
e_cache_unlock(ECache * cache,ECacheUnlockAction action)2677 e_cache_unlock (ECache *cache,
2678 ECacheUnlockAction action)
2679 {
2680 g_return_if_fail (E_IS_CACHE (cache));
2681 g_return_if_fail (cache->priv->in_transaction > 0);
2682
2683 cache->priv->in_transaction--;
2684
2685 if (cache->priv->in_transaction == 0) {
2686 switch (action) {
2687 case E_CACHE_UNLOCK_NONE:
2688 case E_CACHE_UNLOCK_COMMIT:
2689 e_cache_sqlite_exec_internal (cache, "COMMIT", NULL, NULL, NULL, NULL);
2690 break;
2691 case E_CACHE_UNLOCK_ROLLBACK:
2692 e_cache_sqlite_exec_internal (cache, "ROLLBACK", NULL, NULL, NULL, NULL);
2693 break;
2694 }
2695 }
2696
2697 g_rec_mutex_unlock (&cache->priv->lock);
2698 }
2699
2700 /**
2701 * e_cache_get_sqlitedb:
2702 * @cache: an #ECache
2703 *
2704 * Returns: (transfer none): An SQLite3 database pointer. It is owned by the @cache.
2705 *
2706 * Since: 3.26
2707 **/
2708 gpointer
e_cache_get_sqlitedb(ECache * cache)2709 e_cache_get_sqlitedb (ECache *cache)
2710 {
2711 g_return_val_if_fail (E_IS_CACHE (cache), NULL);
2712
2713 return cache->priv->db;
2714 }
2715
2716 /**
2717 * e_cache_sqlite_exec:
2718 * @cache: an #ECache
2719 * @sql_stmt: an SQLite statement to execute
2720 * @cancellable: optional #GCancellable object, or %NULL
2721 * @error: return location for a #GError, or %NULL
2722 *
2723 * Executes an SQLite statement. Use e_cache_sqlite_select() for
2724 * SELECT statements.
2725 *
2726 * Returns: Whether succeeded.
2727 *
2728 * Since: 3.26
2729 **/
2730 gboolean
e_cache_sqlite_exec(ECache * cache,const gchar * sql_stmt,GCancellable * cancellable,GError ** error)2731 e_cache_sqlite_exec (ECache *cache,
2732 const gchar *sql_stmt,
2733 GCancellable *cancellable,
2734 GError **error)
2735 {
2736 g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
2737
2738 return e_cache_sqlite_exec_internal (cache, sql_stmt, NULL, NULL, cancellable, error);
2739 }
2740
2741 /**
2742 * e_cache_sqlite_select:
2743 * @cache: an #ECache
2744 * @sql_stmt: an SQLite SELECT statement to execute
2745 * @func: (scope call): an #ECacheSelectFunc function to call for each row
2746 * @user_data: user data for @func
2747 * @cancellable: optional #GCancellable object, or %NULL
2748 * @error: return location for a #GError, or %NULL
2749 *
2750 * Executes a SELECT statement @sql_stmt and calls @func for each row of the result.
2751 * Use e_cache_sqlite_exec() for statements which do not return row sets.
2752 *
2753 * Returns: Whether succeeded.
2754 *
2755 * Since: 3.26
2756 **/
2757 gboolean
e_cache_sqlite_select(ECache * cache,const gchar * sql_stmt,ECacheSelectFunc func,gpointer user_data,GCancellable * cancellable,GError ** error)2758 e_cache_sqlite_select (ECache *cache,
2759 const gchar *sql_stmt,
2760 ECacheSelectFunc func,
2761 gpointer user_data,
2762 GCancellable *cancellable,
2763 GError **error)
2764 {
2765 g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
2766 g_return_val_if_fail (sql_stmt, FALSE);
2767 g_return_val_if_fail (func, FALSE);
2768
2769 return e_cache_sqlite_exec_internal (cache, sql_stmt, func, user_data, cancellable, error);
2770 }
2771
2772 /**
2773 * e_cache_sqlite_stmt_append_printf:
2774 * @stmt: a #GString statement to append to
2775 * @format: a printf-like format
2776 * @...: arguments for the @format
2777 *
2778 * Appends an SQLite statement fragment based on the @format and
2779 * its arguments to the @stmt.
2780 * The @format can contain any values recognized by sqlite3_mprintf().
2781 *
2782 * Since: 3.26
2783 **/
2784 void
e_cache_sqlite_stmt_append_printf(GString * stmt,const gchar * format,...)2785 e_cache_sqlite_stmt_append_printf (GString *stmt,
2786 const gchar *format,
2787 ...)
2788 {
2789 va_list args;
2790 gchar *tmp_stmt;
2791
2792 g_return_if_fail (stmt != NULL);
2793 g_return_if_fail (format != NULL);
2794
2795 va_start (args, format);
2796 tmp_stmt = sqlite3_vmprintf (format, args);
2797 va_end (args);
2798
2799 g_string_append (stmt, tmp_stmt);
2800
2801 sqlite3_free (tmp_stmt);
2802 }
2803
2804 /**
2805 * e_cache_sqlite_stmt_printf:
2806 * @format: a printf-like format
2807 * @...: arguments for the @format
2808 *
2809 * Creates an SQLite statement based on the @format and its arguments.
2810 * The @format can contain any values recognized by sqlite3_mprintf().
2811 *
2812 * Returns: (transfer full): A new SQLite statement. Free the returned
2813 * string with e_cache_sqlite_stmt_free() when no longer needed.
2814 *
2815 * Since: 3.26
2816 **/
2817 gchar *
e_cache_sqlite_stmt_printf(const gchar * format,...)2818 e_cache_sqlite_stmt_printf (const gchar *format,
2819 ...)
2820 {
2821 va_list args;
2822 gchar *stmt;
2823
2824 g_return_val_if_fail (format != NULL, NULL);
2825
2826 va_start (args, format);
2827 stmt = sqlite3_vmprintf (format, args);
2828 va_end (args);
2829
2830 return stmt;
2831 }
2832
2833 /**
2834 * e_cache_sqlite_stmt_free:
2835 * @stmt: a statement to free
2836 *
2837 * Frees a statement previously constructed with e_cache_sqlite_stmt_printf().
2838 *
2839 * Since: 3.26
2840 **/
2841 void
e_cache_sqlite_stmt_free(gchar * stmt)2842 e_cache_sqlite_stmt_free (gchar *stmt)
2843 {
2844 if (stmt)
2845 sqlite3_free (stmt);
2846 }
2847
2848 /**
2849 * e_cache_sqlite_maybe_vacuum:
2850 * @cache: an #ECache
2851 * @cancellable: optional #GCancellable object, or %NULL
2852 * @error: return location for a #GError, or %NULL
2853 *
2854 * Runs vacuum (compacts the database file), if needed.
2855 *
2856 * Returns: Whether succeeded. It doesn't mean that the vacuum had been run,
2857 * only that no error happened during the call.
2858 *
2859 * Since: 3.26
2860 **/
2861 gboolean
e_cache_sqlite_maybe_vacuum(ECache * cache,GCancellable * cancellable,GError ** error)2862 e_cache_sqlite_maybe_vacuum (ECache *cache,
2863 GCancellable *cancellable,
2864 GError **error)
2865 {
2866 guint64 page_count = 0, page_size = 0, freelist_count = 0;
2867 gboolean success = FALSE;
2868 GError *local_error = NULL;
2869
2870 g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
2871
2872 g_rec_mutex_lock (&cache->priv->lock);
2873
2874 if (e_cache_sqlite_exec_internal (cache, "PRAGMA page_count;", e_cache_get_uint64_cb, &page_count, cancellable, &local_error) &&
2875 e_cache_sqlite_exec_internal (cache, "PRAGMA page_size;", e_cache_get_uint64_cb, &page_size, cancellable, &local_error) &&
2876 e_cache_sqlite_exec_internal (cache, "PRAGMA freelist_count;", e_cache_get_uint64_cb, &freelist_count, cancellable, &local_error)) {
2877 /* Vacuum, if there's more than 5% of the free pages, or when free pages use more than 10MB */
2878 success = !page_count || !freelist_count ||
2879 (freelist_count * page_size < 1024 * 1024 * 10 && freelist_count * 1000 / page_count <= 50) ||
2880 e_cache_sqlite_exec_internal (cache, "vacuum;", NULL, NULL, cancellable, &local_error);
2881 }
2882
2883 g_rec_mutex_unlock (&cache->priv->lock);
2884
2885 if (local_error) {
2886 g_propagate_error (error, local_error);
2887 success = FALSE;
2888 }
2889
2890 return success;
2891 }
2892
2893 static gboolean
e_cache_put_locked_default(ECache * cache,const gchar * uid,const gchar * revision,const gchar * object,ECacheColumnValues * other_columns,EOfflineState offline_state,gboolean is_replace,GCancellable * cancellable,GError ** error)2894 e_cache_put_locked_default (ECache *cache,
2895 const gchar *uid,
2896 const gchar *revision,
2897 const gchar *object,
2898 ECacheColumnValues *other_columns,
2899 EOfflineState offline_state,
2900 gboolean is_replace,
2901 GCancellable *cancellable,
2902 GError **error)
2903 {
2904 GString *statement, *other_names = NULL, *other_values = NULL;
2905 gboolean success;
2906
2907 g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
2908 g_return_val_if_fail (uid != NULL, FALSE);
2909 g_return_val_if_fail (object != NULL, FALSE);
2910
2911 statement = g_string_sized_new (255);
2912
2913 e_cache_sqlite_stmt_append_printf (statement, "INSERT OR REPLACE INTO %Q ("
2914 E_CACHE_COLUMN_UID ","
2915 E_CACHE_COLUMN_REVISION ","
2916 E_CACHE_COLUMN_OBJECT ","
2917 E_CACHE_COLUMN_STATE,
2918 E_CACHE_TABLE_OBJECTS);
2919
2920 if (other_columns) {
2921 GHashTableIter iter;
2922 gpointer key, value;
2923
2924 e_cache_column_values_init_iter (other_columns, &iter);
2925 while (g_hash_table_iter_next (&iter, &key, &value)) {
2926 if (!other_names)
2927 other_names = g_string_new ("");
2928 g_string_append_c (other_names, ',');
2929
2930 e_cache_sqlite_stmt_append_printf (other_names, "%Q", key);
2931
2932 if (!other_values)
2933 other_values = g_string_new ("");
2934
2935 g_string_append_c (other_values, ',');
2936 if (value) {
2937 e_cache_sqlite_stmt_append_printf (other_values, "%Q", value);
2938 } else {
2939 g_string_append (other_values, "NULL");
2940 }
2941 }
2942 }
2943
2944 if (other_names)
2945 g_string_append (statement, other_names->str);
2946
2947 g_string_append (statement, ") VALUES (");
2948
2949 e_cache_sqlite_stmt_append_printf (statement, "%Q,%Q,%Q,%d", uid, revision ? revision : "", object, offline_state);
2950
2951 if (other_values)
2952 g_string_append (statement, other_values->str);
2953
2954 g_string_append_c (statement, ')');
2955
2956 success = e_cache_sqlite_exec_internal (cache, statement->str, NULL, NULL, cancellable, error);
2957
2958 if (other_names)
2959 g_string_free (other_names, TRUE);
2960 if (other_values)
2961 g_string_free (other_values, TRUE);
2962 g_string_free (statement, TRUE);
2963
2964 return success;
2965 }
2966
2967 static gboolean
e_cache_remove_locked_default(ECache * cache,const gchar * uid,GCancellable * cancellable,GError ** error)2968 e_cache_remove_locked_default (ECache *cache,
2969 const gchar *uid,
2970 GCancellable *cancellable,
2971 GError **error)
2972 {
2973 gboolean success = TRUE;
2974
2975 g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
2976 g_return_val_if_fail (uid != NULL, FALSE);
2977
2978 g_signal_emit (cache,
2979 signals[BEFORE_REMOVE],
2980 0,
2981 uid, cancellable, error,
2982 &success);
2983
2984 success = success && e_cache_sqlite_exec_printf (cache,
2985 "DELETE FROM " E_CACHE_TABLE_OBJECTS " WHERE " E_CACHE_COLUMN_UID " = %Q",
2986 NULL, NULL, cancellable, error,
2987 uid);
2988
2989 return success;
2990 }
2991
2992 static gboolean
e_cache_remove_all_locked_default(ECache * cache,const GSList * uids,GCancellable * cancellable,GError ** error)2993 e_cache_remove_all_locked_default (ECache *cache,
2994 const GSList *uids,
2995 GCancellable *cancellable,
2996 GError **error)
2997 {
2998 const GSList *link;
2999 gboolean success = TRUE;
3000
3001 g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
3002
3003 for (link = uids; link && success; link = g_slist_next (link)) {
3004 const gchar *uid = link->data;
3005
3006 g_signal_emit (cache,
3007 signals[BEFORE_REMOVE],
3008 0,
3009 uid, cancellable, error,
3010 &success);
3011 }
3012
3013 if (success) {
3014 success = e_cache_sqlite_exec_printf (cache,
3015 "DELETE FROM " E_CACHE_TABLE_OBJECTS,
3016 NULL, NULL, cancellable, error);
3017 }
3018
3019 return success;
3020 }
3021
3022 static gboolean
e_cache_clear_offline_changes_locked_default(ECache * cache,GCancellable * cancellable,GError ** error)3023 e_cache_clear_offline_changes_locked_default (ECache *cache,
3024 GCancellable *cancellable,
3025 GError **error)
3026 {
3027 gboolean success;
3028
3029 g_return_val_if_fail (E_IS_CACHE (cache), FALSE);
3030
3031 success = e_cache_sqlite_exec_printf (cache,
3032 "DELETE FROM " E_CACHE_TABLE_OBJECTS " WHERE " E_CACHE_COLUMN_STATE "=%d",
3033 NULL, NULL, cancellable, error,
3034 E_OFFLINE_STATE_LOCALLY_DELETED);
3035
3036 success = success && e_cache_sqlite_exec_printf (cache,
3037 "UPDATE " E_CACHE_TABLE_OBJECTS " SET " E_CACHE_COLUMN_STATE "=%d"
3038 " WHERE " E_CACHE_COLUMN_STATE "!=%d",
3039 NULL, NULL, cancellable, error,
3040 E_OFFLINE_STATE_SYNCED, E_OFFLINE_STATE_SYNCED);
3041
3042 return success;
3043 }
3044
3045 static gboolean
e_cache_signals_accumulator(GSignalInvocationHint * ihint,GValue * return_accu,const GValue * handler_return,gpointer data)3046 e_cache_signals_accumulator (GSignalInvocationHint *ihint,
3047 GValue *return_accu,
3048 const GValue *handler_return,
3049 gpointer data)
3050 {
3051 gboolean handler_result;
3052
3053 handler_result = g_value_get_boolean (handler_return);
3054 g_value_set_boolean (return_accu, handler_result);
3055
3056 return handler_result;
3057 }
3058
3059 static gboolean
e_cache_before_put_default(ECache * cache,const gchar * uid,const gchar * revision,const gchar * object,ECacheColumnValues * other_columns,gboolean is_replace,GCancellable * cancellable,GError ** error)3060 e_cache_before_put_default (ECache *cache,
3061 const gchar *uid,
3062 const gchar *revision,
3063 const gchar *object,
3064 ECacheColumnValues *other_columns,
3065 gboolean is_replace,
3066 GCancellable *cancellable,
3067 GError **error)
3068 {
3069 return TRUE;
3070 }
3071
3072 static gboolean
e_cache_before_remove_default(ECache * cache,const gchar * uid,GCancellable * cancellable,GError ** error)3073 e_cache_before_remove_default (ECache *cache,
3074 const gchar *uid,
3075 GCancellable *cancellable,
3076 GError **error)
3077 {
3078 return TRUE;
3079 }
3080
3081 static void
e_cache_finalize(GObject * object)3082 e_cache_finalize (GObject *object)
3083 {
3084 ECache *cache = E_CACHE (object);
3085
3086 g_free (cache->priv->filename);
3087 cache->priv->filename = NULL;
3088
3089 g_clear_pointer (&cache->priv->db, sqlite3_close);
3090
3091 g_rec_mutex_clear (&cache->priv->lock);
3092
3093 g_warn_if_fail (cache->priv->cancellable == NULL);
3094 g_clear_object (&cache->priv->cancellable);
3095
3096 /* Chain up to parent's method. */
3097 G_OBJECT_CLASS (e_cache_parent_class)->finalize (object);
3098 }
3099
3100 static void
e_cache_class_init(ECacheClass * klass)3101 e_cache_class_init (ECacheClass *klass)
3102 {
3103 GObjectClass *object_class;
3104
3105 object_class = G_OBJECT_CLASS (klass);
3106 object_class->finalize = e_cache_finalize;
3107
3108 klass->put_locked = e_cache_put_locked_default;
3109 klass->remove_locked = e_cache_remove_locked_default;
3110 klass->remove_all_locked = e_cache_remove_all_locked_default;
3111 klass->clear_offline_changes_locked = e_cache_clear_offline_changes_locked_default;
3112 klass->before_put = e_cache_before_put_default;
3113 klass->before_remove = e_cache_before_remove_default;
3114
3115 signals[BEFORE_PUT] = g_signal_new (
3116 "before-put",
3117 G_OBJECT_CLASS_TYPE (klass),
3118 G_SIGNAL_RUN_LAST,
3119 G_STRUCT_OFFSET (ECacheClass, before_put),
3120 e_cache_signals_accumulator,
3121 NULL,
3122 g_cclosure_marshal_generic,
3123 G_TYPE_BOOLEAN, 7,
3124 G_TYPE_STRING,
3125 G_TYPE_STRING,
3126 G_TYPE_STRING,
3127 E_TYPE_CACHE_COLUMN_VALUES,
3128 G_TYPE_BOOLEAN,
3129 G_TYPE_CANCELLABLE,
3130 G_TYPE_POINTER);
3131
3132 signals[BEFORE_REMOVE] = g_signal_new (
3133 "before-remove",
3134 G_OBJECT_CLASS_TYPE (klass),
3135 G_SIGNAL_RUN_LAST,
3136 G_STRUCT_OFFSET (ECacheClass, before_remove),
3137 e_cache_signals_accumulator,
3138 NULL,
3139 g_cclosure_marshal_generic,
3140 G_TYPE_BOOLEAN, 3,
3141 G_TYPE_STRING,
3142 G_TYPE_CANCELLABLE,
3143 G_TYPE_POINTER);
3144
3145 signals[REVISION_CHANGED] = g_signal_new (
3146 "revision-changed",
3147 G_OBJECT_CLASS_TYPE (klass),
3148 G_SIGNAL_RUN_LAST,
3149 G_STRUCT_OFFSET (ECacheClass, revision_changed),
3150 NULL,
3151 NULL,
3152 g_cclosure_marshal_generic,
3153 G_TYPE_NONE, 0,
3154 G_TYPE_NONE);
3155
3156 e_sqlite3_vfs_init ();
3157 }
3158
3159 static void
e_cache_init(ECache * cache)3160 e_cache_init (ECache *cache)
3161 {
3162 cache->priv = e_cache_get_instance_private (cache);
3163
3164 cache->priv->filename = NULL;
3165 cache->priv->db = NULL;
3166 cache->priv->cancellable = NULL;
3167 cache->priv->in_transaction = 0;
3168 cache->priv->revision_change_frozen = 0;
3169 cache->priv->revision_counter = 0;
3170 cache->priv->last_revision_time = 0;
3171 cache->priv->needs_revision_change = FALSE;
3172
3173 g_rec_mutex_init (&cache->priv->lock);
3174 }
3175