[evolution-data-server] Bug 794108 - ETimezoneCache only grows
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server] Bug 794108 - ETimezoneCache only grows
- Date: Wed, 6 Jun 2018 16:21:04 +0000 (UTC)
commit 0ef4b50bc4bf71b26f51b1801a306b231e897ad9
Author: Milan Crha <mcrha redhat com>
Date: Wed Jun 6 18:22:37 2018 +0200
Bug 794108 - ETimezoneCache only grows
CMakeLists.txt | 2 +-
src/calendar/libedata-cal/e-cal-backend.c | 13 +
src/calendar/libedata-cal/e-cal-cache.c | 634 ++++++++++++++++++++++---
src/calendar/libedata-cal/e-cal-cache.h | 10 +-
src/calendar/libedata-cal/e-cal-meta-backend.c | 111 +++--
src/libebackend/e-cache.c | 97 +++-
src/libebackend/e-cache.h | 7 +
tests/libedata-cal/test-cal-meta-backend.c | 299 ++++++++++--
8 files changed, 1007 insertions(+), 166 deletions(-)
---
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b8cf258d9..e85ec73e1 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -77,7 +77,7 @@ set(LIBECAL_CURRENT 19)
set(LIBECAL_REVISION 0)
set(LIBECAL_AGE 0)
-set(LIBEDATACAL_CURRENT 28)
+set(LIBEDATACAL_CURRENT 29)
set(LIBEDATACAL_REVISION 0)
set(LIBEDATACAL_AGE 0)
diff --git a/src/calendar/libedata-cal/e-cal-backend.c b/src/calendar/libedata-cal/e-cal-backend.c
index e5819b68c..3029f2dc5 100644
--- a/src/calendar/libedata-cal/e-cal-backend.c
+++ b/src/calendar/libedata-cal/e-cal-backend.c
@@ -782,6 +782,19 @@ cal_backend_shutdown (ECalBackend *backend)
e_source_get_display_name (source));
}
+/* Private function, not meant to be part of the public API */
+void _e_cal_backend_remove_cached_timezones (ECalBackend *cal_backend);
+
+void
+_e_cal_backend_remove_cached_timezones (ECalBackend *cal_backend)
+{
+ g_return_if_fail (E_IS_CAL_BACKEND (cal_backend));
+
+ g_mutex_lock (&cal_backend->priv->zone_cache_lock);
+ g_hash_table_remove_all (cal_backend->priv->zone_cache);
+ g_mutex_unlock (&cal_backend->priv->zone_cache_lock);
+}
+
static void
cal_backend_add_cached_timezone (ETimezoneCache *cache,
icaltimezone *zone)
diff --git a/src/calendar/libedata-cal/e-cal-cache.c b/src/calendar/libedata-cal/e-cal-cache.c
index d6e2d55dc..72d8c1859 100644
--- a/src/calendar/libedata-cal/e-cal-cache.c
+++ b/src/calendar/libedata-cal/e-cal-cache.c
@@ -42,7 +42,7 @@
#include "e-cal-cache.h"
-#define E_CAL_CACHE_VERSION 2
+#define E_CAL_CACHE_VERSION 3
#define ECC_TABLE_TIMEZONES "timezones"
@@ -68,6 +68,8 @@
#define ECC_COLUMN_EXTRA "bdata"
struct _ECalCachePrivate {
+ gboolean initializing;
+
GHashTable *loaded_timezones; /* gchar *tzid ~> icaltimezone * */
GHashTable *modified_timezones; /* gchar *tzid ~> icaltimezone * */
GRecMutex timezones_lock;
@@ -78,11 +80,15 @@ struct _ECalCachePrivate {
enum {
DUP_COMPONENT_REVISION,
+ GET_TIMEZONE,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL];
+/* Private function, not meant to be part of the public API */
+void _e_cal_cache_remove_loaded_timezones (ECalCache *cal_cache);
+
static void ecc_timezone_cache_init (ETimezoneCacheInterface *iface);
G_DEFINE_TYPE_WITH_CODE (ECalCache, e_cal_cache, E_TYPE_CACHE,
@@ -1707,7 +1713,8 @@ ecc_init_aux_tables (ECalCache *cal_cache,
stmt = e_cache_sqlite_stmt_printf ("CREATE TABLE IF NOT EXISTS %Q ("
"tzid TEXT PRIMARY KEY, "
- "zone TEXT)",
+ "zone TEXT, "
+ "refs INTEGER)",
ECC_TABLE_TIMEZONES);
success = e_cache_sqlite_exec (E_CACHE (cal_cache), stmt, cancellable, error);
e_cache_sqlite_stmt_free (stmt);
@@ -1820,6 +1827,298 @@ cal_cache_gather_v1_affected_cb (ECalCache *cal_cache,
return TRUE;
}
+static icaltimezone *
+ecc_timezone_from_string (const gchar *icalstring)
+{
+ icalcomponent *component;
+
+ g_return_val_if_fail (icalstring != NULL, NULL);
+
+ component = icalcomponent_new_from_string (icalstring);
+ if (component) {
+ icaltimezone *zone;
+
+ zone = icaltimezone_new ();
+ if (!icaltimezone_set_component (zone, component)) {
+ icalcomponent_free (component);
+ icaltimezone_free (zone, 1);
+ } else {
+ return zone;
+ }
+ }
+
+ return NULL;
+}
+
+static gboolean
+ecc_tzid_is_libical_builtin (const gchar *tzid)
+{
+ const gchar *matched_tzid;
+
+ if (!tzid || !*tzid || icaltimezone_get_builtin_timezone (tzid))
+ return TRUE;
+
+ matched_tzid = e_cal_match_tzid (tzid);
+ return matched_tzid && icaltimezone_get_builtin_timezone_from_tzid (matched_tzid);
+}
+
+typedef struct _TimezoneMigrationData {
+ icaltimezone *zone;
+ guint refs;
+ gboolean is_deref; /* TRUE when should dereference, instead of reference, the timezone with refs
references */
+} TimezoneMigrationData;
+
+static void
+timezone_migration_data_free (gpointer ptr)
+{
+ TimezoneMigrationData *tmd = ptr;
+
+ if (tmd) {
+ if (tmd->zone)
+ icaltimezone_free (tmd->zone, 1);
+ g_free (tmd);
+ }
+}
+
+typedef struct _CountTimezonesData {
+ ECalCache *cal_cache;
+ GHashTable *timezones;
+ gboolean is_inc;
+ GCancellable *cancellable;
+} CountTimezonesData;
+
+static void
+ecc_count_timezones_in_icalcomp_cb (icalparameter *param,
+ gpointer user_data)
+{
+ CountTimezonesData *ctd = user_data;
+ TimezoneMigrationData *tmd;
+ const gchar *tzid;
+
+ g_return_if_fail (ctd != NULL);
+
+ tzid = icalparameter_get_tzid (param);
+ if (!tzid)
+ return;
+
+ tmd = g_hash_table_lookup (ctd->timezones, tzid);
+ if (tmd) {
+ if (ctd->is_inc) {
+ if (tmd->is_deref) {
+ if (!tmd->refs) {
+ tmd->refs++;
+ tmd->is_deref = FALSE;
+ } else {
+ tmd->refs--;
+ }
+ } else {
+ tmd->refs++;
+ }
+ } else {
+ if (tmd->is_deref) {
+ tmd->refs++;
+ } else {
+ if (!tmd->refs) {
+ tmd->refs++;
+ tmd->is_deref = TRUE;
+ } else {
+ tmd->refs--;
+ }
+ }
+ }
+ } else if (!ecc_tzid_is_libical_builtin (tzid)) {
+ icaltimezone *zone = NULL;
+
+ g_signal_emit (ctd->cal_cache, signals[GET_TIMEZONE], 0, tzid, &zone);
+
+ if (!zone && !e_cal_cache_get_timezone (ctd->cal_cache, tzid, &zone, ctd->cancellable, NULL))
+ zone = NULL;
+
+ /* Make a copy of it, it's not owned by the caller, but by the originator */
+ if (zone) {
+ icalcomponent *zonecomp;
+
+ zonecomp = icaltimezone_get_component (zone);
+ if (zonecomp) {
+ icalcomponent *clone;
+
+ clone = icalcomponent_new_clone (zonecomp);
+ /* icaltimezone_copy() doesn't carry over the component, thus do it this way
*/
+ zone = icaltimezone_new ();
+ if (!icaltimezone_set_component (zone, clone)) {
+ icalcomponent_free (clone);
+ icaltimezone_free (zone, 1);
+ zone = NULL;
+ }
+ } else {
+ zone = NULL;
+ }
+ }
+
+ if (zone) {
+ tmd = g_new0 (TimezoneMigrationData, 1);
+ tmd->is_deref = !ctd->is_inc;
+ tmd->refs = 1;
+ tmd->zone = zone;
+
+ g_hash_table_insert (ctd->timezones, g_strdup (tzid), tmd);
+ }
+ }
+}
+
+static void
+ecc_count_timezones_for_component (ECalCache *cal_cache,
+ GHashTable *timezones,
+ icalcomponent *icalcomp,
+ gboolean is_inc,
+ GCancellable *cancellable)
+{
+ g_return_if_fail (E_IS_CAL_CACHE (cal_cache));
+ g_return_if_fail (timezones != NULL);
+
+ if (icalcomp) {
+ CountTimezonesData ctd;
+
+ ctd.cal_cache = cal_cache;
+ ctd.timezones = timezones;
+ ctd.is_inc = is_inc;
+ ctd.cancellable = cancellable;
+
+ icalcomponent_foreach_tzid (icalcomp, ecc_count_timezones_in_icalcomp_cb, &ctd);
+ }
+}
+
+static void
+ecc_count_timezones_for_old_component (ECalCache *cal_cache,
+ GHashTable *timezones,
+ const gchar *uid_in_table,
+ GCancellable *cancellable)
+{
+ gchar *objstr;
+
+ g_return_if_fail (E_IS_CAL_CACHE (cal_cache));
+ g_return_if_fail (timezones != NULL);
+ g_return_if_fail (uid_in_table != NULL);
+
+ objstr = e_cache_get_object_include_deleted (E_CACHE (cal_cache), uid_in_table, NULL, NULL,
cancellable, NULL);
+ if (objstr) {
+ icalcomponent *icalcomp;
+
+ icalcomp = icalcomponent_new_from_string (objstr);
+ if (icalcomp) {
+ ecc_count_timezones_for_component (cal_cache, timezones, icalcomp, FALSE,
cancellable);
+ icalcomponent_free (icalcomp);
+ }
+
+ g_free (objstr);
+ }
+}
+
+static gboolean
+ecc_update_timezones_table (ECalCache *cal_cache,
+ GHashTable *timezones,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GHashTableIter iter;
+ gpointer key, value;
+ gboolean success = TRUE;
+
+ g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), FALSE);
+ g_return_val_if_fail (timezones != NULL, FALSE);
+
+ g_hash_table_iter_init (&iter, timezones);
+
+ while (success && g_hash_table_iter_next (&iter, &key, &value)) {
+ const gchar *tzid = key;
+ TimezoneMigrationData *tmd = value;
+
+ if (!tzid || !tmd || !tmd->refs)
+ continue;
+
+ if (tmd->is_deref) {
+ success = e_cal_cache_remove_timezone (cal_cache, tzid, tmd->refs, cancellable,
error);
+ } else {
+ success = e_cal_cache_put_timezone (cal_cache, tmd->zone, tmd->refs, cancellable,
error);
+ }
+ }
+
+ return success;
+}
+
+static gboolean
+e_cal_cache_fill_tmd_cb (ECache *cache,
+ gint ncols,
+ const gchar *column_names[],
+ const gchar *column_values[],
+ gpointer user_data)
+{
+ GHashTable *timezones = user_data; /* gchar *tzid ~> TimezoneMigrationData * */
+
+ g_return_val_if_fail (timezones != NULL, FALSE);
+ g_return_val_if_fail (ncols == 2, FALSE);
+
+ /* Verify the timezone is not provided twice */
+ if (!g_hash_table_lookup (timezones, column_values[0])) {
+ icaltimezone *zone;
+
+ zone = ecc_timezone_from_string (column_values[1]);
+ if (zone) {
+ TimezoneMigrationData *tmd;
+
+ tmd = g_new0 (TimezoneMigrationData, 1);
+ tmd->zone = zone;
+ tmd->refs = 0;
+
+ g_hash_table_insert (timezones, g_strdup (column_values[0]), tmd);
+ }
+ }
+
+ return TRUE;
+}
+
+static void
+ecc_count_tmd_refs_cb (icalparameter *param,
+ gpointer user_data)
+{
+ GHashTable *timezones = user_data;
+ const gchar *tzid;
+ TimezoneMigrationData *tmd;
+
+ tzid = icalparameter_get_tzid (param);
+ if (!tzid || !timezones)
+ return;
+
+ tmd = g_hash_table_lookup (timezones, tzid);
+ if (tmd)
+ tmd->refs++;
+}
+
+static gboolean
+cal_cache_count_tmd_refs (ECalCache *cal_cache,
+ const gchar *uid,
+ const gchar *rid,
+ const gchar *revision,
+ const gchar *object,
+ const gchar *extra,
+ EOfflineState offline_state,
+ gpointer user_data)
+{
+ GHashTable *timezones = user_data;
+ icalcomponent *icalcomp;
+
+ g_return_val_if_fail (timezones != NULL, FALSE);
+ g_return_val_if_fail (object != NULL, FALSE);
+
+ icalcomp = icalcomponent_new_from_string (object);
+ if (icalcomp) {
+ icalcomponent_foreach_tzid (icalcomp, ecc_count_tmd_refs_cb, timezones);
+ icalcomponent_free (icalcomp);
+ }
+
+ return TRUE;
+}
+
static gboolean
e_cal_cache_migrate (ECache *cache,
gint from_version,
@@ -1827,10 +2126,33 @@ e_cal_cache_migrate (ECache *cache,
GError **error)
{
ECalCache *cal_cache = E_CAL_CACHE (cache);
+ GHashTable *timezones = NULL; /* gchar *tzid ~> TimezoneMigrationData * */
gboolean success = TRUE;
/* Add any version-related changes here (E_CAL_CACHE_VERSION) */
- if (from_version == 1) {
+
+ if (from_version > 0 && from_version < 3) {
+ gchar *stmt;
+
+ g_rec_mutex_lock (&cal_cache->priv->timezones_lock);
+
+ stmt = e_cache_sqlite_stmt_printf ("ALTER TABLE %Q ADD COLUMN refs INTEGER",
ECC_TABLE_TIMEZONES);
+ success = e_cache_sqlite_exec (E_CACHE (cal_cache), stmt, cancellable, error);
+ e_cache_sqlite_stmt_free (stmt);
+
+ if (success) {
+ timezones = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
timezone_migration_data_free);
+
+ stmt = e_cache_sqlite_stmt_printf ("SELECT tzid, zone FROM " ECC_TABLE_TIMEZONES);
+ success = e_cache_sqlite_select (E_CACHE (cal_cache), stmt,
+ e_cal_cache_fill_tmd_cb, timezones, cancellable, error);
+ e_cache_sqlite_stmt_free (stmt);
+ }
+
+ g_rec_mutex_unlock (&cal_cache->priv->timezones_lock);
+ }
+
+ if (success && from_version == 1) {
/* Version 1 incorrectly stored DATE-only DUE values */
ComponentInfo ci;
@@ -1849,6 +2171,36 @@ e_cal_cache_migrate (ECache *cache,
component_info_clear (&ci);
}
+ if (success && timezones) {
+ g_rec_mutex_lock (&cal_cache->priv->timezones_lock);
+
+ success = e_cal_cache_remove_timezones (cal_cache, cancellable, error);
+ if (success) {
+ _e_cal_cache_remove_loaded_timezones (cal_cache);
+
+ success = e_cal_cache_search_with_callback (cal_cache, NULL,
+ cal_cache_count_tmd_refs, timezones, cancellable, error);
+ }
+
+ if (success) {
+ GHashTableIter iter;
+ gpointer value;
+
+ g_hash_table_iter_init (&iter, timezones);
+ while (success && g_hash_table_iter_next (&iter, NULL, &value)) {
+ TimezoneMigrationData *tmd = value;
+
+ if (tmd && tmd->refs > 0)
+ success = e_cal_cache_put_timezone (cal_cache, tmd->zone, tmd->refs,
cancellable, error);
+ }
+ }
+
+ g_rec_mutex_unlock (&cal_cache->priv->timezones_lock);
+ }
+
+ if (timezones)
+ g_hash_table_destroy (timezones);
+
return success;
}
@@ -1865,6 +2217,8 @@ e_cal_cache_initialize (ECalCache *cal_cache,
g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), FALSE);
g_return_val_if_fail (filename != NULL, FALSE);
+ cal_cache->priv->initializing = TRUE;
+
cache = E_CACHE (cal_cache);
e_cal_cache_populate_other_columns (cal_cache, &other_columns);
@@ -1893,6 +2247,8 @@ e_cal_cache_initialize (ECalCache *cal_cache,
exit:
g_slist_free_full (other_columns, e_cache_column_info_free);
+ cal_cache->priv->initializing = FALSE;
+
return success;
}
@@ -3062,16 +3418,60 @@ e_cal_cache_delete_attachments (ECalCache *cal_cache,
return TRUE;
}
+static gboolean
+e_cal_cache_get_uint64_cb (ECache *cache,
+ gint ncols,
+ const gchar **column_names,
+ const gchar **column_values,
+ gpointer user_data)
+{
+ guint64 *pui64 = user_data;
+
+ g_return_val_if_fail (pui64 != NULL, FALSE);
+
+ if (ncols == 1) {
+ *pui64 = column_values[0] ? g_ascii_strtoull (column_values[0], NULL, 10) : 0;
+ } else {
+ *pui64 = 0;
+ }
+
+ return TRUE;
+}
+
+static gint
+e_cal_cache_get_current_timezone_refs (ECalCache *cal_cache,
+ const gchar *tzid,
+ GCancellable *cancellable)
+{
+ guint64 existing_refs = -1;
+ gchar *stmt;
+
+ g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), -1);
+ g_return_val_if_fail (tzid != NULL, -1);
+
+ stmt = e_cache_sqlite_stmt_printf ("SELECT refs FROM " ECC_TABLE_TIMEZONES " WHERE tzid=%Q", tzid);
+
+ if (!e_cache_sqlite_select (E_CACHE (cal_cache), stmt, e_cal_cache_get_uint64_cb, &existing_refs,
cancellable, NULL))
+ existing_refs = -1;
+
+ e_cache_sqlite_stmt_free (stmt);
+
+ return (gint) existing_refs;
+}
+
/**
* e_cal_cache_put_timezone:
* @cal_cache: an #ECalCache
* @zone: an icaltimezone to put
+ * @inc_ref_counts: how many refs to add, or 0 to have it stored forever
* @cancellable: optional #GCancellable object, or %NULL
* @error: return location for a #GError, or %NULL
*
* Puts the @zone into the @cal_cache using its timezone ID as
* an identificator. The function adds a new or replaces existing,
- * if any such already exists in the @cal_cache.
+ * if any such already exists in the @cal_cache. The function does
+ * nothing and returns %TRUE, when the passed-in @zone is libical
+ * builtin timezone.
*
* Returns: Whether succeeded.
*
@@ -3080,6 +3480,7 @@ e_cal_cache_delete_attachments (ECalCache *cal_cache,
gboolean
e_cal_cache_put_timezone (ECalCache *cal_cache,
const icaltimezone *zone,
+ guint inc_ref_counts,
GCancellable *cancellable,
GError **error)
{
@@ -3098,6 +3499,9 @@ e_cal_cache_put_timezone (ECalCache *cal_cache,
return FALSE;
}
+ if (ecc_tzid_is_libical_builtin (icaltimezone_get_tzid ((icaltimezone *) zone)))
+ return TRUE;
+
component = icaltimezone_get_component ((icaltimezone *) zone);
if (!component) {
g_set_error_literal (error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND, _("Cannot add timezone
without component"));
@@ -3110,40 +3514,33 @@ e_cal_cache_put_timezone (ECalCache *cal_cache,
return FALSE;
}
- stmt = e_cache_sqlite_stmt_printf (
- "INSERT or REPLACE INTO " ECC_TABLE_TIMEZONES " (tzid, zone) VALUES (%Q, %Q)",
- tzid, component_str);
+ g_rec_mutex_lock (&cal_cache->priv->timezones_lock);
- success = e_cache_sqlite_exec (E_CACHE (cal_cache), stmt, cancellable, error);
+ if (inc_ref_counts > 0) {
+ gint current_refs;
- e_cache_sqlite_stmt_free (stmt);
+ current_refs = e_cal_cache_get_current_timezone_refs (cal_cache, tzid, cancellable);
- g_free (component_str);
+ /* Zero means keep forever */
+ if (current_refs == 0)
+ inc_ref_counts = 0;
+ else if (current_refs > 0)
+ inc_ref_counts += current_refs;
+ }
- return success;
-}
+ stmt = e_cache_sqlite_stmt_printf (
+ "INSERT or REPLACE INTO " ECC_TABLE_TIMEZONES " (tzid, zone, refs) VALUES (%Q, %Q, %u)",
+ tzid, component_str, inc_ref_counts);
-static icaltimezone *
-ecc_timezone_from_string (const gchar *icalstring)
-{
- icalcomponent *component;
+ success = e_cache_sqlite_exec (E_CACHE (cal_cache), stmt, cancellable, error);
- g_return_val_if_fail (icalstring != NULL, NULL);
+ e_cache_sqlite_stmt_free (stmt);
- component = icalcomponent_new_from_string (icalstring);
- if (component) {
- icaltimezone *zone;
+ g_free (component_str);
- zone = icaltimezone_new ();
- if (!icaltimezone_set_component (zone, component)) {
- icalcomponent_free (component);
- icaltimezone_free (zone, 1);
- } else {
- return zone;
- }
- }
+ g_rec_mutex_unlock (&cal_cache->priv->timezones_lock);
- return NULL;
+ return success;
}
/**
@@ -3246,6 +3643,8 @@ e_cal_cache_dup_timezone_as_string (ECalCache *cal_cache,
*out_zone_string = NULL;
+ g_rec_mutex_lock (&cal_cache->priv->timezones_lock);
+
stmt = e_cache_sqlite_stmt_printf (
"SELECT zone FROM " ECC_TABLE_TIMEZONES " WHERE tzid=%Q",
tzid);
@@ -3255,27 +3654,9 @@ e_cal_cache_dup_timezone_as_string (ECalCache *cal_cache,
e_cache_sqlite_stmt_free (stmt);
- return success;
-}
-
-static gboolean
-e_cal_cache_get_uint64_cb (ECache *cache,
- gint ncols,
- const gchar **column_names,
- const gchar **column_values,
- gpointer user_data)
-{
- guint64 *pui64 = user_data;
-
- g_return_val_if_fail (pui64 != NULL, FALSE);
-
- if (ncols == 1) {
- *pui64 = column_values[0] ? g_ascii_strtoull (column_values[0], NULL, 10) : 0;
- } else {
- *pui64 = 0;
- }
+ g_rec_mutex_unlock (&cal_cache->priv->timezones_lock);
- return TRUE;
+ return success;
}
static gboolean
@@ -3373,6 +3754,76 @@ e_cal_cache_list_timezones (ECalCache *cal_cache,
return success;
}
+/**
+ * e_cal_cache_remove_timezone:
+ * @cal_cache: an #ECalCache
+ * @tzid: timezone ID to remove/dereference
+ * @dec_ref_counts: reference counts to drop, 0 to remove it regardless of the current reference count
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * Dereferences use count of the time zone with ID @tzid by @dec_ref_counts
+ * and removes the timezone from the cache when the reference count reaches
+ * zero. Special case is with @dec_ref_counts being zero, in which case
+ * the corresponding timezone is removed regardless of the current reference
+ * count.
+ *
+ * It's not an error when the timezone doesn't exist in the cache.
+ *
+ * Returns: Whether succeeded.
+ *
+ * Since: 3.30
+ **/
+gboolean
+e_cal_cache_remove_timezone (ECalCache *cal_cache,
+ const gchar *tzid,
+ guint dec_ref_counts,
+ GCancellable *cancellable,
+ GError **error)
+{
+ gchar *stmt;
+ gboolean success;
+
+ g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), FALSE);
+ g_return_val_if_fail (tzid != NULL, FALSE);
+
+ e_cache_lock (E_CACHE (cal_cache), E_CACHE_LOCK_WRITE);
+
+ g_rec_mutex_lock (&cal_cache->priv->timezones_lock);
+
+ if (dec_ref_counts) {
+ gint current_refs;
+
+ current_refs = e_cal_cache_get_current_timezone_refs (cal_cache, tzid, cancellable);
+ if (current_refs <= 0) {
+ g_rec_mutex_unlock (&cal_cache->priv->timezones_lock);
+ e_cache_unlock (E_CACHE (cal_cache), E_CACHE_UNLOCK_COMMIT);
+
+ return TRUE;
+ }
+
+ if (current_refs >= dec_ref_counts)
+ dec_ref_counts = current_refs - dec_ref_counts;
+ else
+ dec_ref_counts = 0;
+ }
+
+ if (dec_ref_counts)
+ stmt = e_cache_sqlite_stmt_printf ("UPDATE " ECC_TABLE_TIMEZONES " SET refs=%u WHERE
tzid=%Q", dec_ref_counts, tzid);
+ else
+ stmt = e_cache_sqlite_stmt_printf ("DELETE FROM " ECC_TABLE_TIMEZONES " WHERE tzid=%Q", tzid);
+
+ success = e_cache_sqlite_exec (E_CACHE (cal_cache), stmt, cancellable, error);
+
+ e_cache_sqlite_stmt_free (stmt);
+
+ g_rec_mutex_unlock (&cal_cache->priv->timezones_lock);
+
+ e_cache_unlock (E_CACHE (cal_cache), success ? E_CACHE_UNLOCK_COMMIT : E_CACHE_UNLOCK_ROLLBACK);
+
+ return success;
+}
+
/**
* e_cal_cache_remove_timezones:
* @cal_cache: an #ECalCache
@@ -3407,8 +3858,6 @@ e_cal_cache_remove_timezones (ECalCache *cal_cache,
return success;
}
-void _e_cal_cache_remove_loaded_timezones (ECalCache *cal_cache);
-
/* Private function, not meant to be part of the public API */
void
_e_cal_cache_remove_loaded_timezones (ECalCache *cal_cache)
@@ -3585,6 +4034,7 @@ e_cal_cache_put_locked (ECache *cache,
GCancellable *cancellable,
GError **error)
{
+ GHashTable *timezones = NULL; /* gchar *tzid ~> TimezoneMigrationData * */
ECalCache *cal_cache;
ECalComponent *comp;
gboolean success;
@@ -3600,14 +4050,61 @@ e_cal_cache_put_locked (ECache *cache,
ecc_fill_other_columns (cal_cache, other_columns, comp);
+ if (!cal_cache->priv->initializing) {
+ timezones = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
timezone_migration_data_free);
+
+ ecc_count_timezones_for_component (cal_cache, timezones, e_cal_component_get_icalcomponent
(comp), TRUE, cancellable);
+
+ if (is_replace)
+ ecc_count_timezones_for_old_component (cal_cache, timezones, uid, cancellable);
+ }
+
success = E_CACHE_CLASS (e_cal_cache_parent_class)->put_locked (cache, uid, revision, object,
other_columns, offline_state,
is_replace, cancellable, error);
+ if (success)
+ success = ecc_update_timezones_table (cal_cache, timezones, cancellable, error);
+
+ if (timezones)
+ g_hash_table_destroy (timezones);
+
g_clear_object (&comp);
return success;
}
+static gboolean
+e_cal_cache_remove_locked (ECache *cache,
+ const gchar *uid,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GHashTable *timezones = NULL; /* gchar *tzid ~> TimezoneMigrationData * */
+ ECalCache *cal_cache;
+ gboolean success;
+
+ g_return_val_if_fail (E_IS_CAL_CACHE (cache), FALSE);
+ g_return_val_if_fail (uid != NULL, FALSE);
+
+ cal_cache = E_CAL_CACHE (cache);
+
+ if (!cal_cache->priv->initializing) {
+ timezones = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
timezone_migration_data_free);
+
+ ecc_count_timezones_for_old_component (cal_cache, timezones, uid, cancellable);
+ }
+
+ success = E_CACHE_CLASS (e_cal_cache_parent_class)->remove_locked (cache, uid, cancellable, error);
+
+ if (success)
+ success = ecc_update_timezones_table (cal_cache, timezones, cancellable, error);
+
+ if (timezones)
+ g_hash_table_destroy (timezones);
+
+ return success;
+}
+
static gboolean
e_cal_cache_remove_all_locked (ECache *cache,
const GSList *uids,
@@ -3644,20 +4141,13 @@ ecc_add_cached_timezone (ETimezoneCache *cache,
icaltimezone *zone)
{
ECalCache *cal_cache;
- const gchar *matched_tzid;
- const gchar *tzid;
cal_cache = E_CAL_CACHE (cache);
- tzid = icaltimezone_get_tzid (zone);
- if (!tzid || !*tzid || icaltimezone_get_builtin_timezone (tzid))
+ if (!zone || ecc_tzid_is_libical_builtin (icaltimezone_get_tzid (zone)))
return;
- matched_tzid = e_cal_match_tzid (tzid);
- if (matched_tzid && icaltimezone_get_builtin_timezone_from_tzid (matched_tzid))
- return;
-
- e_cal_cache_put_timezone (cal_cache, zone, NULL, NULL);
+ e_cal_cache_put_timezone (cal_cache, zone, 0, NULL, NULL);
}
static icaltimezone *
@@ -3784,6 +4274,7 @@ e_cal_cache_class_init (ECalCacheClass *klass)
cache_class = E_CACHE_CLASS (klass);
cache_class->put_locked = e_cal_cache_put_locked;
+ cache_class->remove_locked = e_cal_cache_remove_locked;
cache_class->remove_all_locked = e_cal_cache_remove_all_locked;
klass->dup_component_revision = ecc_dup_component_revision;
@@ -3804,6 +4295,29 @@ e_cal_cache_class_init (ECalCacheClass *klass)
g_cclosure_marshal_generic,
G_TYPE_STRING, 1,
G_TYPE_POINTER);
+
+ /**
+ * ECalCache:get-timezone:
+ * @cal_cache: an #ECalCache
+ * @tzid: timezone ID
+ *
+ * A signal being called to get timezone when putting component
+ * into the cache. It's used to make sure the cache contains
+ * all timezones which are needed by the component. The returned
+ * icaltimezone will not be freed.
+ *
+ * Since: 3.30
+ **/
+ signals[GET_TIMEZONE] = g_signal_new (
+ "get-timezone",
+ G_OBJECT_CLASS_TYPE (klass),
+ G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
+ G_STRUCT_OFFSET (ECalCacheClass, get_timezone),
+ g_signal_accumulator_first_wins,
+ NULL,
+ g_cclosure_marshal_generic,
+ G_TYPE_POINTER, 1,
+ G_TYPE_STRING);
}
static void
diff --git a/src/calendar/libedata-cal/e-cal-cache.h b/src/calendar/libedata-cal/e-cal-cache.h
index 571ebfb6d..66f104532 100644
--- a/src/calendar/libedata-cal/e-cal-cache.h
+++ b/src/calendar/libedata-cal/e-cal-cache.h
@@ -179,9 +179,11 @@ struct _ECalCacheClass {
gchar * (* dup_component_revision)
(ECalCache *cal_cache,
icalcomponent *icalcomp);
+ icaltimezone * (* get_timezone) (ECalCache *cal_cache,
+ const gchar *tzid);
/* Padding for future expansion */
- gpointer reserved[10];
+ gpointer reserved[9];
};
GType e_cal_cache_get_type (void) G_GNUC_CONST;
@@ -307,6 +309,7 @@ gboolean e_cal_cache_delete_attachments (ECalCache *cal_cache,
gboolean e_cal_cache_put_timezone (ECalCache *cal_cache,
const icaltimezone *zone,
+ guint inc_ref_counts,
GCancellable *cancellable,
GError **error);
gboolean e_cal_cache_get_timezone (ECalCache *cal_cache,
@@ -324,6 +327,11 @@ gboolean e_cal_cache_list_timezones (ECalCache *cal_cache,
GList **out_timezones,
GCancellable *cancellable,
GError **error);
+gboolean e_cal_cache_remove_timezone (ECalCache *cal_cache,
+ const gchar *tzid,
+ guint dec_ref_counts,
+ GCancellable *cancellable,
+ GError **error);
gboolean e_cal_cache_remove_timezones (ECalCache *cal_cache,
GCancellable *cancellable,
GError **error);
diff --git a/src/calendar/libedata-cal/e-cal-meta-backend.c b/src/calendar/libedata-cal/e-cal-meta-backend.c
index 8920577a5..a2c82b643 100644
--- a/src/calendar/libedata-cal/e-cal-meta-backend.c
+++ b/src/calendar/libedata-cal/e-cal-meta-backend.c
@@ -73,6 +73,7 @@ struct _ECalMetaBackendPrivate {
gulong source_changed_id;
gulong notify_online_id;
gulong revision_changed_id;
+ gulong get_timezone_id;
guint refresh_timeout_id;
gboolean refresh_after_authenticate;
@@ -102,6 +103,11 @@ enum {
static guint signals[LAST_SIGNAL];
+/* To be able to call the ECalBackend implementation, which stores zones only in the memory */
+static icaltimezone * (* ecmb_timezone_cache_parent_get_timezone) (ETimezoneCache *cache,
+ const gchar *tzid);
+static GList * (* ecmb_timezone_cache_parent_list_timezones) (ETimezoneCache *cache);
+
/* Forward Declarations */
static void e_cal_meta_backend_timezone_cache_init (ETimezoneCacheInterface *iface);
@@ -1023,7 +1029,8 @@ ecmb_gather_timezones (ECalMetaBackend *meta_backend,
clone = icalcomponent_new_clone (subcomp);
if (icaltimezone_set_component (zone, clone)) {
- e_timezone_cache_add_timezone (timezone_cache, zone);
+ if (icaltimezone_get_tzid (zone))
+ e_timezone_cache_add_timezone (timezone_cache, zone);
} else {
icalcomponent_free (clone);
}
@@ -1070,7 +1077,7 @@ ecmb_load_component_wrapper_sync (ECalMetaBackend *meta_backend,
icalcomponent_kind kind;
icalcomponent *subcomp;
- ecmb_gather_timezones (meta_backend, E_TIMEZONE_CACHE (cal_cache), icalcomp);
+ ecmb_gather_timezones (meta_backend, E_TIMEZONE_CACHE (meta_backend), icalcomp);
kind = e_cal_backend_get_kind (E_CAL_BACKEND (meta_backend));
@@ -2515,7 +2522,7 @@ ecmb_receive_objects_sync (ECalBackendSync *sync_backend,
comps = g_slist_reverse (comps);
if (icalcomponent_isa (icalcomp) == ICAL_VCALENDAR_COMPONENT)
- ecmb_gather_timezones (meta_backend, E_TIMEZONE_CACHE (cal_cache), icalcomp);
+ ecmb_gather_timezones (meta_backend, E_TIMEZONE_CACHE (meta_backend), icalcomp);
if (icalcomponent_get_first_property (icalcomp, ICAL_METHOD_PROPERTY))
top_method = icalcomponent_get_method (icalcomp);
@@ -2684,7 +2691,6 @@ ecmb_get_timezone_sync (ECalBackendSync *sync_backend,
gchar **tzobject,
GError **error)
{
- ECalCache *cal_cache;
icaltimezone *zone;
gchar *timezone_str = NULL;
GError *local_error = NULL;
@@ -2696,10 +2702,7 @@ ecmb_get_timezone_sync (ECalBackendSync *sync_backend,
if (g_cancellable_set_error_if_cancelled (cancellable, error))
return;
- cal_cache = e_cal_meta_backend_ref_cache (E_CAL_META_BACKEND (sync_backend));
- g_return_if_fail (cal_cache != NULL);
-
- zone = e_timezone_cache_get_timezone (E_TIMEZONE_CACHE (cal_cache), tzid);
+ zone = e_timezone_cache_get_timezone (E_TIMEZONE_CACHE (sync_backend), tzid);
if (zone) {
icalcomponent *icalcomp;
@@ -2712,8 +2715,6 @@ ecmb_get_timezone_sync (ECalBackendSync *sync_backend,
}
}
- g_object_unref (cal_cache);
-
if (!local_error && !timezone_str)
local_error = e_data_cal_create_error (ObjectNotFound, NULL);
@@ -2747,7 +2748,6 @@ ecmb_add_timezone_sync (ECalBackendSync *sync_backend,
icalcomponent_isa (tz_comp) != ICAL_VTIMEZONE_COMPONENT) {
g_propagate_error (error, e_data_cal_create_error (InvalidObject, NULL));
} else {
- ECalCache *cal_cache;
icaltimezone *zone;
zone = icaltimezone_new ();
@@ -2755,14 +2755,9 @@ ecmb_add_timezone_sync (ECalBackendSync *sync_backend,
tz_comp = NULL;
- cal_cache = e_cal_meta_backend_ref_cache (E_CAL_META_BACKEND (sync_backend));
- if (cal_cache) {
- e_timezone_cache_add_timezone (E_TIMEZONE_CACHE (cal_cache), zone);
- icaltimezone_free (zone, 1);
- g_object_unref (cal_cache);
- } else {
- g_warn_if_reached ();
- }
+ /* Add it only to memory, do not store it persistently into the ECalCache */
+ e_timezone_cache_add_timezone (E_TIMEZONE_CACHE (sync_backend), zone);
+ icaltimezone_free (zone, 1);
}
if (tz_comp)
@@ -3108,20 +3103,6 @@ ecmb_maybe_wait_for_credentials (ECalMetaBackend *meta_backend,
return got_credentials;
}
-static void
-ecmb_add_cached_timezone (ETimezoneCache *cache,
- icaltimezone *zone)
-{
- ECalCache *cal_cache;
-
- cal_cache = e_cal_meta_backend_ref_cache (E_CAL_META_BACKEND (cache));
- g_return_if_fail (E_IS_CAL_CACHE (cal_cache));
-
- e_timezone_cache_add_timezone (E_TIMEZONE_CACHE (cal_cache), zone);
-
- g_clear_object (&cal_cache);
-}
-
static icaltimezone *
ecmb_get_cached_timezone (ETimezoneCache *cache,
const gchar *tzid)
@@ -3129,6 +3110,13 @@ ecmb_get_cached_timezone (ETimezoneCache *cache,
ECalCache *cal_cache;
icaltimezone *zone;
+ if (ecmb_timezone_cache_parent_get_timezone) {
+ zone = ecmb_timezone_cache_parent_get_timezone (cache, tzid);
+
+ if (zone)
+ return zone;
+ }
+
cal_cache = e_cal_meta_backend_ref_cache (E_CAL_META_BACKEND (cache));
g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), NULL);
@@ -3152,6 +3140,16 @@ ecmb_list_cached_timezones (ETimezoneCache *cache)
g_clear_object (&cal_cache);
+ if (ecmb_timezone_cache_parent_list_timezones) {
+ GList *backend_zones;
+
+ backend_zones = ecmb_timezone_cache_parent_list_timezones (E_TIMEZONE_CACHE (cache));
+
+ /* There can be duplicates in the 'zones' GList, but let's make it no big deal */
+ if (backend_zones)
+ zones = g_list_concat (zones, backend_zones);
+ }
+
return zones;
}
@@ -3251,6 +3249,12 @@ e_cal_meta_backend_dispose (GObject *object)
meta_backend->priv->revision_changed_id = 0;
}
+ if (meta_backend->priv->get_timezone_id) {
+ if (meta_backend->priv->cache)
+ g_signal_handler_disconnect (meta_backend->priv->cache,
meta_backend->priv->get_timezone_id);
+ meta_backend->priv->get_timezone_id = 0;
+ }
+
g_hash_table_foreach (meta_backend->priv->view_cancellables, ecmb_cancel_view_cb, NULL);
if (meta_backend->priv->refresh_cancellable) {
@@ -3415,7 +3419,10 @@ e_cal_meta_backend_init (ECalMetaBackend *meta_backend)
static void
e_cal_meta_backend_timezone_cache_init (ETimezoneCacheInterface *iface)
{
- iface->add_timezone = ecmb_add_cached_timezone;
+ ecmb_timezone_cache_parent_get_timezone = iface->get_timezone;
+ ecmb_timezone_cache_parent_list_timezones = iface->list_timezones;
+
+ /* leave the iface->add_timezone as it was, to have them in memory only */
iface->get_timezone = ecmb_get_cached_timezone;
iface->list_timezones = ecmb_list_cached_timezones;
}
@@ -3623,6 +3630,20 @@ ecmb_cache_revision_changed_cb (ECache *cache,
}
}
+static icaltimezone *
+ecmb_cache_get_timezone_cb (ECalCache *cal_cache,
+ const gchar *tzid,
+ gpointer user_data)
+{
+ ECalMetaBackend *meta_backend = user_data;
+
+ g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), NULL);
+ g_return_val_if_fail (tzid != NULL, NULL);
+ g_return_val_if_fail (E_IS_CAL_META_BACKEND (meta_backend), NULL);
+
+ return e_timezone_cache_get_timezone (E_TIMEZONE_CACHE (meta_backend), tzid);
+}
+
/**
* e_cal_meta_backend_set_cache:
* @meta_backend: an #ECalMetaBackend
@@ -3656,6 +3677,8 @@ e_cal_meta_backend_set_cache (ECalMetaBackend *meta_backend,
if (meta_backend->priv->cache) {
g_signal_handler_disconnect (meta_backend->priv->cache,
meta_backend->priv->revision_changed_id);
+ g_signal_handler_disconnect (meta_backend->priv->cache,
+ meta_backend->priv->get_timezone_id);
}
g_clear_object (&meta_backend->priv->cache);
@@ -3664,6 +3687,9 @@ e_cal_meta_backend_set_cache (ECalMetaBackend *meta_backend,
meta_backend->priv->revision_changed_id = g_signal_connect_object (meta_backend->priv->cache,
"revision-changed", G_CALLBACK (ecmb_cache_revision_changed_cb), meta_backend, 0);
+ meta_backend->priv->get_timezone_id = g_signal_connect_object (meta_backend->priv->cache,
+ "get-timezone", G_CALLBACK (ecmb_cache_get_timezone_cb), meta_backend, 0);
+
g_mutex_unlock (&meta_backend->priv->property_lock);
g_object_notify (G_OBJECT (meta_backend), "cache");
@@ -3719,7 +3745,7 @@ sort_master_first_cb (gconstpointer a,
}
typedef struct {
- ECalCache *cache;
+ ETimezoneCache *timezone_cache;
gboolean replace_tzid_with_location;
icalcomponent *vcalendar;
icalcomponent *icalcomp;
@@ -3745,8 +3771,8 @@ add_timezone_cb (icalparameter *param,
tz = icalcomponent_get_timezone (f_data->icalcomp, tzid);
if (!tz)
tz = icaltimezone_get_builtin_timezone_from_tzid (tzid);
- if (!tz && f_data->cache)
- tz = e_timezone_cache_get_timezone (E_TIMEZONE_CACHE (f_data->cache), tzid);
+ if (!tz && f_data->timezone_cache)
+ tz = e_timezone_cache_get_timezone (f_data->timezone_cache, tzid);
if (!tz)
return;
@@ -3808,7 +3834,6 @@ e_cal_meta_backend_merge_instances (ECalMetaBackend *meta_backend,
const GSList *instances,
gboolean replace_tzid_with_location)
{
- ECalCache *cal_cache;
ForeachTzidData f_data;
icalcomponent *vcalendar;
GSList *link, *sorted;
@@ -3816,14 +3841,11 @@ e_cal_meta_backend_merge_instances (ECalMetaBackend *meta_backend,
g_return_val_if_fail (E_IS_CAL_META_BACKEND (meta_backend), NULL);
g_return_val_if_fail (instances != NULL, NULL);
- cal_cache = e_cal_meta_backend_ref_cache (meta_backend);
- g_return_val_if_fail (cal_cache != NULL, NULL);
-
sorted = g_slist_sort (g_slist_copy ((GSList *) instances), sort_master_first_cb);
vcalendar = e_cal_util_new_top_level ();
- f_data.cache = cal_cache;
+ f_data.timezone_cache = E_TIMEZONE_CACHE (meta_backend);
f_data.replace_tzid_with_location = replace_tzid_with_location;
f_data.vcalendar = vcalendar;
@@ -3844,7 +3866,6 @@ e_cal_meta_backend_merge_instances (ECalMetaBackend *meta_backend,
icalcomponent_foreach_tzid (icalcomp, add_timezone_cb, &f_data);
}
- g_clear_object (&f_data.cache);
g_slist_free (sorted);
return vcalendar;
@@ -4059,7 +4080,7 @@ e_cal_meta_backend_store_inline_attachments_sync (ECalMetaBackend *meta_backend,
* @error: return location for a #GError, or %NULL
*
* Extracts all VTIMEZONE components from the @vcalendar and adds them
- * to the cache, thus they are available when needed. The function does
+ * to the memory cache, thus they are available when needed. The function does
* nothing when the @vcalendar doesn't hold a VCALENDAR component.
*
* Set the @remove_existing argument to %TRUE to remove all cached timezones
@@ -4095,7 +4116,7 @@ e_cal_meta_backend_gather_timezones_sync (ECalMetaBackend *meta_backend,
success = e_cal_cache_remove_timezones (cal_cache, cancellable, error);
if (success)
- ecmb_gather_timezones (meta_backend, E_TIMEZONE_CACHE (cal_cache), vcalendar);
+ ecmb_gather_timezones (meta_backend, E_TIMEZONE_CACHE (meta_backend), vcalendar);
e_cache_unlock (E_CACHE (cal_cache), success ? E_CACHE_UNLOCK_COMMIT : E_CACHE_UNLOCK_ROLLBACK);
diff --git a/src/libebackend/e-cache.c b/src/libebackend/e-cache.c
index e683da119..dc7831d1d 100644
--- a/src/libebackend/e-cache.c
+++ b/src/libebackend/e-cache.c
@@ -1328,6 +1328,51 @@ e_cache_get_object_cb (ECache *cache,
return TRUE;
}
+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)
+{
+ struct GetObjectData gd;
+ gboolean success;
+
+ g_return_val_if_fail (E_IS_CACHE (cache), NULL);
+ g_return_val_if_fail (uid != NULL, NULL);
+
+ if (out_revision)
+ *out_revision = NULL;
+
+ if (out_other_columns)
+ *out_other_columns = NULL;
+
+ gd.object = NULL;
+ gd.out_revision = out_revision;
+ gd.out_other_columns = out_other_columns;
+
+ if (include_deleted) {
+ success = e_cache_sqlite_exec_printf (cache,
+ "SELECT * FROM " E_CACHE_TABLE_OBJECTS
+ " WHERE " E_CACHE_COLUMN_UID " = %Q",
+ e_cache_get_object_cb, &gd, cancellable, error,
+ uid);
+ } else {
+ success = e_cache_sqlite_exec_printf (cache,
+ "SELECT * FROM " E_CACHE_TABLE_OBJECTS
+ " WHERE " E_CACHE_COLUMN_UID " = %Q AND " E_CACHE_COLUMN_STATE " != %d",
+ e_cache_get_object_cb, &gd, cancellable, error,
+ uid, E_OFFLINE_STATE_LOCALLY_DELETED);
+ }
+
+ if (success && !gd.object)
+ g_set_error (error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND, _("Object ā%sā not found"), uid);
+
+ return gd.object;
+}
+
/**
* e_cache_get:
* @cache: an #ECache
@@ -1361,31 +1406,43 @@ e_cache_get (ECache *cache,
GCancellable *cancellable,
GError **error)
{
- struct GetObjectData gd;
-
g_return_val_if_fail (E_IS_CACHE (cache), NULL);
g_return_val_if_fail (uid != NULL, NULL);
- if (out_revision)
- *out_revision = NULL;
-
- if (out_other_columns)
- *out_other_columns = NULL;
-
- gd.object = NULL;
- gd.out_revision = out_revision;
- gd.out_other_columns = out_other_columns;
+ return e_cache_get_object_internal (cache, FALSE, uid, out_revision, out_other_columns, cancellable,
error);
+}
- if (e_cache_sqlite_exec_printf (cache,
- "SELECT * FROM " E_CACHE_TABLE_OBJECTS
- " WHERE " E_CACHE_COLUMN_UID " = %Q AND " E_CACHE_COLUMN_STATE " != %d",
- e_cache_get_object_cb, &gd, cancellable, error,
- uid, E_OFFLINE_STATE_LOCALLY_DELETED) &&
- !gd.object) {
- g_set_error (error, E_CACHE_ERROR, E_CACHE_ERROR_NOT_FOUND, _("Object ā%sā not found"), uid);
- }
+/**
+ * e_cache_get_object_include_deleted:
+ * @cache: an #ECache
+ * @uid: a unique identifier of an object
+ * @out_revision: (out) (nullable) (transfer full): an out variable for a revision
+ * of the object, or %NULL to ignore
+ * @out_other_columns: (out) (nullable) (transfer full): an out
+ * variable for #ECacheColumnValues other columns, as defined when creating the @cache, or %NULL to ignore
+ * @cancellable: optional #GCancellable object, or %NULL
+ * @error: return location for a #GError, or %NULL
+ *
+ * The same as e_cache_get(), only considers also locally deleted objects.
+ *
+ * Returns: (nullable) (transfer full): An object with the given @uid. Free it
+ * with g_free(), when no longer needed. Returns %NULL on error, like when
+ * the object could not be found.
+ *
+ * Since: 3.30
+ **/
+gchar *
+e_cache_get_object_include_deleted (ECache *cache,
+ const gchar *uid,
+ gchar **out_revision,
+ ECacheColumnValues **out_other_columns,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (E_IS_CACHE (cache), NULL);
+ g_return_val_if_fail (uid != NULL, NULL);
- return gd.object;
+ return e_cache_get_object_internal (cache, TRUE, uid, out_revision, out_other_columns, cancellable,
error);
}
static gboolean
diff --git a/src/libebackend/e-cache.h b/src/libebackend/e-cache.h
index 18b3ed732..3ff428a72 100644
--- a/src/libebackend/e-cache.h
+++ b/src/libebackend/e-cache.h
@@ -409,6 +409,13 @@ gchar * e_cache_get (ECache *cache,
ECacheColumnValues **out_other_columns,
GCancellable *cancellable,
GError **error);
+gchar * e_cache_get_object_include_deleted
+ (ECache *cache,
+ const gchar *uid,
+ gchar **out_revision,
+ ECacheColumnValues **out_other_columns,
+ GCancellable *cancellable,
+ GError **error);
gboolean e_cache_put (ECache *cache,
const gchar *uid,
const gchar *revision,
diff --git a/tests/libedata-cal/test-cal-meta-backend.c b/tests/libedata-cal/test-cal-meta-backend.c
index 6d30c9071..dc953f656 100644
--- a/tests/libedata-cal/test-cal-meta-backend.c
+++ b/tests/libedata-cal/test-cal-meta-backend.c
@@ -27,6 +27,7 @@
#include "test-cal-cache-utils.h"
void _e_cal_cache_remove_loaded_timezones (ECalCache *cal_cache); /* e-cal-cache.c, private function */
+void _e_cal_backend_remove_cached_timezones (ECalBackend *cal_backend); /* e-cal-backend.c, private function
*/
#define EXPECTED_TZID "/freeassociation.sourceforge.net/America/New_York"
#define EXPECTED_LOCATION "America/New_York"
@@ -1177,7 +1178,8 @@ test_empty_cache (TCUFixture *fixture,
#define TZID "/meta/backend/test/timezone"
#define TZLOC "test/timezone"
- const gchar *in_tzobj =
+ const gchar *in_vcalobj =
+ "BEGIN:VCALENDAR\r\n"
"BEGIN:VTIMEZONE\r\n"
"TZID:" TZID "\r\n"
"X-LIC-LOCATION:" TZLOC "\r\n"
@@ -1195,7 +1197,18 @@ test_empty_cache (TCUFixture *fixture,
"TZOFFSETFROM:-0500\r\n"
"TZOFFSETTO:-0400\r\n"
"END:DAYLIGHT\r\n"
- "END:VTIMEZONE\r\n";
+ "END:VTIMEZONE\r\n"
+ "BEGIN:VEVENT\r\n"
+ "UID:test-event\r\n"
+ "DTSTAMP:20170130T000000Z\r\n"
+ "CREATED:20170216T155507Z\r\n"
+ "LAST-MODIFIED:20170216T155543Z\r\n"
+ "SEQUENCE:1\r\n"
+ "DTSTART;TZID=" TZID ":20170209T013000Z\r\n"
+ "DTEND;TZID=" TZID ":20170209T030000Z\r\n"
+ "SUMMARY:Test Event\r\n"
+ "END:VEVENT\r\n"
+ "END:VCALENDAR\r\n";
ECalBackendSyncClass *backend_class;
ECalMetaBackend *meta_backend;
GList *zones;
@@ -1207,10 +1220,10 @@ test_empty_cache (TCUFixture *fixture,
backend_class = E_CAL_BACKEND_SYNC_GET_CLASS (meta_backend);
g_return_if_fail (backend_class != NULL);
- g_return_if_fail (backend_class->add_timezone_sync != NULL);
+ g_return_if_fail (backend_class->receive_objects_sync != NULL);
- /* Add timezone to the cache */
- backend_class->add_timezone_sync (E_CAL_BACKEND_SYNC (meta_backend), NULL, NULL, in_tzobj, &error);
+ /* This adds the object and the used timezone to the cache */
+ backend_class->receive_objects_sync (E_CAL_BACKEND_SYNC (meta_backend), NULL, NULL, in_vcalobj,
&error);
g_assert_no_error (error);
zones = NULL;
@@ -1332,34 +1345,82 @@ test_discard_alarm (ECalMetaBackend *meta_backend)
g_clear_error (&error);
}
+static gboolean
+tcmb_get_uint64_cb (ECache *cache,
+ gint ncols,
+ const gchar **column_names,
+ const gchar **column_values,
+ gpointer user_data)
+{
+ guint64 *pui64 = user_data;
+
+ g_return_val_if_fail (pui64 != NULL, FALSE);
+
+ if (ncols == 1) {
+ *pui64 = column_values[0] ? g_ascii_strtoull (column_values[0], NULL, 10) : 0;
+ } else {
+ *pui64 = 0;
+ }
+
+ return TRUE;
+}
+
+static gint
+tcmb_get_tzid_ref_count (ECalCache *cal_cache,
+ const gchar *tzid)
+{
+ guint64 refs = 0;
+ gchar *stmt;
+ gboolean success;
+ GError *error = NULL;
+
+ g_return_val_if_fail (E_IS_CAL_CACHE (cal_cache), -1);
+ g_return_val_if_fail (tzid != NULL, -1);
+
+ stmt = e_cache_sqlite_stmt_printf ("SELECT refs FROM timezones WHERE tzid=%Q", tzid);
+
+ success = e_cache_sqlite_select (E_CACHE (cal_cache), stmt, tcmb_get_uint64_cb, &refs, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ e_cache_sqlite_stmt_free (stmt);
+
+ return (gint) refs;
+}
+
static void
test_timezones (ECalMetaBackend *meta_backend)
{
- #define TZID "/meta/backend/test/timezone"
- #define TZLOC "test/timezone"
+ #define TZID1 "/meta/backend/test/timezone1"
+ #define TZLOC1 "test/timezone1"
+ #define TZID2 "/meta/backend/test/timezone2"
+ #define TZLOC2 "test/timezone2"
+ #define TZSTRDEF(id, loc) \
+ "BEGIN:VTIMEZONE\r\n" \
+ "TZID:" id "\r\n" \
+ "X-LIC-LOCATION:" loc "\r\n" \
+ "BEGIN:STANDARD\r\n" \
+ "TZNAME:Test-ST\r\n" \
+ "DTSTART:19701106T020000\r\n" \
+ "RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11\r\n" \
+ "TZOFFSETFROM:-0400\r\n" \
+ "TZOFFSETTO:-0500\r\n" \
+ "END:STANDARD\r\n" \
+ "BEGIN:DAYLIGHT\r\n" \
+ "TZNAME:Test-DT\r\n" \
+ "DTSTART:19700313T020000\r\n" \
+ "RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3\r\n" \
+ "TZOFFSETFROM:-0500\r\n" \
+ "TZOFFSETTO:-0400\r\n" \
+ "END:DAYLIGHT\r\n" \
+ "END:VTIMEZONE\r\n"
- const gchar *in_tzobj =
- "BEGIN:VTIMEZONE\r\n"
- "TZID:" TZID "\r\n"
- "X-LIC-LOCATION:" TZLOC "\r\n"
- "BEGIN:STANDARD\r\n"
- "TZNAME:Test-ST\r\n"
- "DTSTART:19701106T020000\r\n"
- "RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11\r\n"
- "TZOFFSETFROM:-0400\r\n"
- "TZOFFSETTO:-0500\r\n"
- "END:STANDARD\r\n"
- "BEGIN:DAYLIGHT\r\n"
- "TZNAME:Test-DT\r\n"
- "DTSTART:19700313T020000\r\n"
- "RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3\r\n"
- "TZOFFSETFROM:-0500\r\n"
- "TZOFFSETTO:-0400\r\n"
- "END:DAYLIGHT\r\n"
- "END:VTIMEZONE\r\n";
+ const gchar *in_tz1obj = TZSTRDEF (TZID1, TZLOC1);
+ const gchar *in_tz2obj = TZSTRDEF (TZID2, TZLOC2);
ECalBackendSyncClass *backend_class;
ECalCache *cal_cache;
icalcomponent *vcalendar;
+ ECalComponent *comp;
gchar *tzobj = NULL;
GList *zones;
gboolean success;
@@ -1371,36 +1432,37 @@ test_timezones (ECalMetaBackend *meta_backend)
g_return_if_fail (backend_class != NULL);
g_return_if_fail (backend_class->add_timezone_sync != NULL);
g_return_if_fail (backend_class->get_timezone_sync != NULL);
+ g_return_if_fail (backend_class->get_timezone_sync != NULL);
/* Verify neither TZID, not LOCATION is in the timezone cache */
backend_class->get_timezone_sync (E_CAL_BACKEND_SYNC (meta_backend),
- NULL, NULL, TZID, &tzobj, &error);
+ NULL, NULL, TZID1, &tzobj, &error);
g_assert_error (error, E_DATA_CAL_ERROR, ObjectNotFound);
g_assert_null (tzobj);
g_clear_error (&error);
backend_class->get_timezone_sync (E_CAL_BACKEND_SYNC (meta_backend),
- NULL, NULL, TZLOC, &tzobj, &error);
+ NULL, NULL, TZLOC1, &tzobj, &error);
g_assert_error (error, E_DATA_CAL_ERROR, ObjectNotFound);
g_assert_null (tzobj);
g_clear_error (&error);
/* Add it to the cache */
backend_class->add_timezone_sync (E_CAL_BACKEND_SYNC (meta_backend),
- NULL, NULL, in_tzobj, &error);
+ NULL, NULL, in_tz1obj, &error);
g_assert_no_error (error);
/* Read it back */
backend_class->get_timezone_sync (E_CAL_BACKEND_SYNC (meta_backend),
- NULL, NULL, TZID, &tzobj, &error);
+ NULL, NULL, TZID1, &tzobj, &error);
g_assert_no_error (error);
- g_assert_cmpstr (tzobj, ==, in_tzobj);
+ g_assert_cmpstr (tzobj, ==, in_tz1obj);
g_free (tzobj);
tzobj = NULL;
/* As a non-built-in timezone it cannot be read with location, only with TZID */
backend_class->get_timezone_sync (E_CAL_BACKEND_SYNC (meta_backend),
- NULL, NULL, TZLOC, &tzobj, &error);
+ NULL, NULL, TZLOC1, &tzobj, &error);
g_assert_error (error, E_DATA_CAL_ERROR, ObjectNotFound);
g_assert_null (tzobj);
g_clear_error (&error);
@@ -1472,6 +1534,10 @@ test_timezones (ECalMetaBackend *meta_backend)
success = e_cal_cache_list_timezones (cal_cache, &zones, NULL, &error);
g_assert_no_error (error);
g_assert (success);
+ g_assert_cmpint (g_list_length (zones), ==, 0);
+ g_list_free (zones);
+
+ zones = e_timezone_cache_list_timezones (E_TIMEZONE_CACHE (meta_backend));
g_assert_cmpint (g_list_length (zones), ==, 2);
g_list_free (zones);
@@ -1484,6 +1550,10 @@ test_timezones (ECalMetaBackend *meta_backend)
success = e_cal_cache_list_timezones (cal_cache, &zones, NULL, &error);
g_assert_no_error (error);
g_assert (success);
+ g_assert_cmpint (g_list_length (zones), ==, 0);
+ g_list_free (zones);
+
+ zones = e_timezone_cache_list_timezones (E_TIMEZONE_CACHE (meta_backend));
g_assert_cmpint (g_list_length (zones), ==, 4);
g_list_free (zones);
@@ -1492,6 +1562,41 @@ test_timezones (ECalMetaBackend *meta_backend)
g_assert (success);
_e_cal_cache_remove_loaded_timezones (cal_cache);
+ _e_cal_backend_remove_cached_timezones (E_CAL_BACKEND (meta_backend));
+
+ zones = e_timezone_cache_list_timezones (E_TIMEZONE_CACHE (meta_backend));
+ g_assert_cmpint (g_list_length (zones), ==, 0);
+
+ backend_class->add_timezone_sync (E_CAL_BACKEND_SYNC (meta_backend),
+ NULL, NULL, in_tz1obj, &error);
+ g_assert_no_error (error);
+
+ zones = e_timezone_cache_list_timezones (E_TIMEZONE_CACHE (meta_backend));
+ g_assert_cmpint (g_list_length (zones), ==, 1);
+ g_list_free (zones);
+
+ /* Remove existing and add the new */
+ success = e_cal_meta_backend_gather_timezones_sync (meta_backend, vcalendar, TRUE, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ _e_cal_cache_remove_loaded_timezones (cal_cache);
+ _e_cal_backend_remove_cached_timezones (E_CAL_BACKEND (meta_backend));
+
+ zones = e_timezone_cache_list_timezones (E_TIMEZONE_CACHE (meta_backend));
+ g_assert_cmpint (g_list_length (zones), ==, 0);
+
+ icalcomponent_free (vcalendar);
+
+ /* And now when the timezones are actually referenced, thus should be part of the persistent cache */
+
+ backend_class->add_timezone_sync (E_CAL_BACKEND_SYNC (meta_backend),
+ NULL, NULL, in_tz1obj, &error);
+ g_assert_no_error (error);
+
+ backend_class->add_timezone_sync (E_CAL_BACKEND_SYNC (meta_backend),
+ NULL, NULL, in_tz2obj, &error);
+ g_assert_no_error (error);
zones = NULL;
success = e_cal_cache_list_timezones (cal_cache, &zones, NULL, &error);
@@ -1499,9 +1604,27 @@ test_timezones (ECalMetaBackend *meta_backend)
g_assert (success);
g_assert_cmpint (g_list_length (zones), ==, 0);
- backend_class->add_timezone_sync (E_CAL_BACKEND_SYNC (meta_backend),
- NULL, NULL, in_tzobj, &error);
+ /* Uses TZID1 twice */
+ comp = e_cal_component_new_from_string (
+ "BEGIN:VEVENT\r\n"
+ "UID:tz1\r\n"
+ "DTSTAMP:20170130T000000Z\r\n"
+ "CREATED:20170216T155507Z\r\n"
+ "LAST-MODIFIED:20170216T155543Z\r\n"
+ "SEQUENCE:1\r\n"
+ "DTSTART;TZID=" TZID1 ":20170209T013000\r\n"
+ "DTEND;TZID=" TZID1 ":20170209T030000\r\n"
+ "SUMMARY:tz1\r\n"
+ "END:VEVENT\r\n");
+ g_assert_nonnull (comp);
+
+ /* Add a component which uses TZID1, thus it's in the cache */
+ success = e_cal_cache_put_component (cal_cache, comp, NULL, E_CACHE_IS_ONLINE, NULL, &error);
g_assert_no_error (error);
+ g_assert (success);
+
+ g_object_unref (comp);
+ _e_cal_cache_remove_loaded_timezones (cal_cache);
zones = NULL;
success = e_cal_cache_list_timezones (cal_cache, &zones, NULL, &error);
@@ -1510,11 +1633,32 @@ test_timezones (ECalMetaBackend *meta_backend)
g_assert_cmpint (g_list_length (zones), ==, 1);
g_list_free (zones);
- /* Remove existing and add the new */
- success = e_cal_meta_backend_gather_timezones_sync (meta_backend, vcalendar, TRUE, NULL, &error);
+ g_assert_cmpint (tcmb_get_tzid_ref_count (cal_cache, TZID1), ==, 2);
+
+ /* Uses TZID1 and TZID2 */
+ comp = e_cal_component_new_from_string (
+ "BEGIN:VEVENT\r\n"
+ "UID:tz2\r\n"
+ "DTSTAMP:20170130T000000Z\r\n"
+ "CREATED:20170216T155507Z\r\n"
+ "LAST-MODIFIED:20170216T155543Z\r\n"
+ "SEQUENCE:1\r\n"
+ "DTSTART;TZID=" TZID2 ":20170209T013000\r\n"
+ "DTEND;TZID=" TZID1 ":20170209T030000\r\n"
+ "SUMMARY:tz2\r\n"
+ "END:VEVENT\r\n");
+ g_assert_nonnull (comp);
+
+ backend_class->add_timezone_sync (E_CAL_BACKEND_SYNC (meta_backend),
+ NULL, NULL, in_tz2obj, &error);
+ g_assert_no_error (error);
+
+ /* Add a component which uses TZID1 and TZID2, thus it's in the cache */
+ success = e_cal_cache_put_component (cal_cache, comp, NULL, E_CACHE_IS_ONLINE, NULL, &error);
g_assert_no_error (error);
g_assert (success);
+ g_object_unref (comp);
_e_cal_cache_remove_loaded_timezones (cal_cache);
zones = NULL;
@@ -1524,11 +1668,88 @@ test_timezones (ECalMetaBackend *meta_backend)
g_assert_cmpint (g_list_length (zones), ==, 2);
g_list_free (zones);
- icalcomponent_free (vcalendar);
+ g_assert_cmpint (tcmb_get_tzid_ref_count (cal_cache, TZID1), ==, 3);
+ g_assert_cmpint (tcmb_get_tzid_ref_count (cal_cache, TZID2), ==, 1);
+
+ /* Remove in offline doesn't modify timezone cache, because the component is still there */
+ success = e_cal_cache_remove_component (cal_cache, "tz1", NULL, E_CACHE_IS_OFFLINE, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ g_assert_cmpint (tcmb_get_tzid_ref_count (cal_cache, TZID1), ==, 3);
+ g_assert_cmpint (tcmb_get_tzid_ref_count (cal_cache, TZID2), ==, 1);
+
+ /* Remove in online modifies timezone cache */
+ success = e_cal_cache_remove_component (cal_cache, "tz1", NULL, E_CACHE_IS_ONLINE, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ _e_cal_cache_remove_loaded_timezones (cal_cache);
+
+ zones = NULL;
+ success = e_cal_cache_list_timezones (cal_cache, &zones, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ g_assert_cmpint (g_list_length (zones), ==, 2);
+ g_list_free (zones);
+
+ g_assert_cmpint (tcmb_get_tzid_ref_count (cal_cache, TZID1), ==, 1);
+ g_assert_cmpint (tcmb_get_tzid_ref_count (cal_cache, TZID2), ==, 1);
+
+ /* Modify tz2 to use only TZID2, TZID1 is removed */
+ comp = e_cal_component_new_from_string (
+ "BEGIN:VEVENT\r\n"
+ "UID:tz2\r\n"
+ "DTSTAMP:20170130T000000Z\r\n"
+ "CREATED:20170216T155507Z\r\n"
+ "LAST-MODIFIED:20170216T155544Z\r\n"
+ "SEQUENCE:2\r\n"
+ "DTSTART;TZID=" TZID2 ":20170209T013000\r\n"
+ "DTEND:20170209T030000Z\r\n"
+ "SUMMARY:tz2\r\n"
+ "END:VEVENT\r\n");
+ g_assert_nonnull (comp);
+
+ success = e_cal_cache_put_component (cal_cache, comp, NULL, E_CACHE_IS_ONLINE, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ g_object_unref (comp);
+ _e_cal_cache_remove_loaded_timezones (cal_cache);
+
+ zones = NULL;
+ success = e_cal_cache_list_timezones (cal_cache, &zones, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ g_assert_cmpint (g_list_length (zones), ==, 1);
+ g_list_free (zones);
+
+ g_assert_cmpint (tcmb_get_tzid_ref_count (cal_cache, TZID1), ==, 0);
+ g_assert_cmpint (tcmb_get_tzid_ref_count (cal_cache, TZID2), ==, 1);
+
+ /* Finally remove component straight in online, which removed the only one timezone too */
+ success = e_cal_cache_remove_component (cal_cache, "tz2", NULL, E_CACHE_IS_ONLINE, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+
+ _e_cal_cache_remove_loaded_timezones (cal_cache);
+
+ zones = NULL;
+ success = e_cal_cache_list_timezones (cal_cache, &zones, NULL, &error);
+ g_assert_no_error (error);
+ g_assert (success);
+ g_assert_cmpint (g_list_length (zones), ==, 0);
+
+ g_assert_cmpint (tcmb_get_tzid_ref_count (cal_cache, TZID1), ==, 0);
+ g_assert_cmpint (tcmb_get_tzid_ref_count (cal_cache, TZID2), ==, 0);
+
g_object_unref (cal_cache);
- #undef TZLOC
- #undef TZID
+ #undef TZSTRDEF
+ #undef TZLOC2
+ #undef TZID2
+ #undef TZLOC1
+ #undef TZID1
}
static void
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]