diff options
author | Nicolas Dufresne <nicolas.dufresne@collabora.co.uk> | 2011-03-14 16:43:38 -0400 |
---|---|---|
committer | Nicolas Dufresne <nicolas.dufresne@collabora.co.uk> | 2011-03-14 16:43:38 -0400 |
commit | 1522b7c475399d0095d09da1d5c9ae2d462eb8e6 (patch) | |
tree | 19ceeeb0e3dcc4c38369c162617a901faae9c208 | |
parent | 0e15f37026aab2dfc6a537f4682f2ebe3aa93079 (diff) |
Useless message cache
-rw-r--r-- | telepathy-logger/log-store-sqlite-internal.h | 18 | ||||
-rw-r--r-- | telepathy-logger/log-store-sqlite.c | 749 |
2 files changed, 242 insertions, 525 deletions
diff --git a/telepathy-logger/log-store-sqlite-internal.h b/telepathy-logger/log-store-sqlite-internal.h index aa978fa2c..4181c419e 100644 --- a/telepathy-logger/log-store-sqlite-internal.h +++ b/telepathy-logger/log-store-sqlite-internal.h @@ -56,7 +56,9 @@ typedef enum TPL_LOG_STORE_SQLITE_ERROR_FAILED = TPL_LOG_STORE_ERROR_LAST, /* generic _tpl_log_store_sqlite_get_pending_messages() error, to be used when * any other code cannot be use, including TPL_LOG_STORE_ERROR ones */ - TPL_LOG_STORE_SQLITE_ERROR_GET_PENDING_MESSAGES + TPL_LOG_STORE_SQLITE_ERROR_GET_PENDING_MESSAGES, + TPL_LOG_STORE_SQLITE_ERROR_REMOVE_PENDING_MESSAGES, + TPL_LOG_STORE_SQLITE_ERROR_ADD_PENDING_MESSAGE } TplLogStoreSqliteError; @@ -76,17 +78,13 @@ struct _TplLogStoreSqliteClass GType _tpl_log_store_sqlite_get_type (void); TplLogStore * _tpl_log_store_sqlite_dup (void); + GList * _tpl_log_store_sqlite_get_pending_messages (TplLogStore *self, TpChannel *channel, GError **error); -GList * _tpl_log_store_sqlite_get_log_ids (TplLogStore *self, - TpChannel *channel, gint64 timestamp, GError **error); -gboolean _tpl_log_store_sqlite_log_id_is_present (TplLogStore *self, - const gchar* log_id); - -void _tpl_log_store_sqlite_set_acknowledgment (TplLogStore *self, - const gchar* log_id, GError **error); -void _tpl_log_store_sqlite_set_acknowledgment_by_msg_id (TplLogStore *self, - TpChannel *channel, guint msg_id, GError **error); +gboolean _tpl_log_store_sqlite_remove_pending_messages (TplLogStore *self, + GList *log_ids, GError **error); +gboolean _tpl_log_store_sqlite_add_pending_message (TplLogStore *self, + TpChannel *channel, const gchar *log_id, gint64 timestamp, GError **error); gint64 _tpl_log_store_sqlite_get_most_recent (TplLogStoreSqlite *self, TpAccount *account, const char *identifier); diff --git a/telepathy-logger/log-store-sqlite.c b/telepathy-logger/log-store-sqlite.c index 7c17effcc..0f89a12e8 100644 --- a/telepathy-logger/log-store-sqlite.c +++ b/telepathy-logger/log-store-sqlite.c @@ -17,6 +17,7 @@ * * Authors: Danielle Madeley <danielle.madeley@collabora.co.uk> * Cosimo Alfarano <cosimo.alfarano@collabora.co.uk> + * Nicolas Dufresne <nicolas.dufresne@collabora.co.uk> */ #include <config.h> @@ -44,13 +45,8 @@ static void log_store_iface_init (TplLogStoreInterface *iface); -static gboolean _insert_to_cache_table (TplLogStore *self, - TplEvent *message, GError **error); -static void tpl_log_store_sqlite_purge (TplLogStoreSqlite *self, GTimeSpan delta, - GError **error); static gboolean purge_event_timeout (gpointer logstore); - G_DEFINE_TYPE_WITH_CODE (TplLogStoreSqlite, _tpl_log_store_sqlite, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (TPL_TYPE_LOG_STORE, log_store_iface_init)); @@ -73,6 +69,7 @@ struct _TplLogStoreSqlitePrivate static GObject *singleton = NULL; + static GObject * tpl_log_store_sqlite_constructor (GType type, guint n_props, @@ -95,6 +92,7 @@ tpl_log_store_sqlite_constructor (GType type, return singleton; } + static char * get_db_filename (void) { @@ -105,6 +103,7 @@ get_db_filename (void) NULL); } + static void tpl_log_store_sqlite_get_property (GObject *self, guint id, @@ -132,6 +131,7 @@ tpl_log_store_sqlite_get_property (GObject *self, } } + static void tpl_log_store_sqlite_set_property (GObject *self, guint id, @@ -151,6 +151,7 @@ tpl_log_store_sqlite_set_property (GObject *self, } } + static void tpl_log_store_sqlite_dispose (GObject *self) { @@ -171,6 +172,7 @@ tpl_log_store_sqlite_dispose (GObject *self) G_OBJECT_CLASS (_tpl_log_store_sqlite_parent_class)->dispose (self); } + static void _tpl_log_store_sqlite_class_init (TplLogStoreSqliteClass *klass) { @@ -188,6 +190,7 @@ _tpl_log_store_sqlite_class_init (TplLogStoreSqliteClass *klass) g_type_class_add_private (gobject_class, sizeof (TplLogStoreSqlitePrivate)); } + static void _tpl_log_store_sqlite_init (TplLogStoreSqlite *self) { @@ -222,18 +225,25 @@ _tpl_log_store_sqlite_init (TplLogStoreSqlite *self) /* end of common part */ /* start of cache table init */ - sqlite3_exec (priv->db, "CREATE TABLE IF NOT EXISTS message_cache ( " + + /* drop deprecated table (since 0.2.6) */ + sqlite3_exec (priv->db, "DROP TABLE IF EXISTS message_cache", + NULL, NULL, &errmsg); + if (errmsg != NULL) + { + CRITICAL ("Failed to drop deprecated message_cache table: %s\n", errmsg); + sqlite3_free (errmsg); + goto out; + } + + sqlite3_exec (priv->db, "CREATE TABLE IF NOT EXISTS pending_messages ( " "channel TEXT NOT NULL, " - "account TEXT NOT NULL, " - "pending_msg_id INTEGER DEFAULT NULL, " - "log_identifier TEXT PRIMARY KEY, " - "chat_identifier TEXT NOT NULL, " - "chatroom BOOLEAN NOT NULL, " + "log_id TEXT PRIMARY KEY, " "date DATETIME NOT NULL)", NULL, NULL, &errmsg); if (errmsg != NULL) { - CRITICAL ("Failed to create table message_cache: %s\n", errmsg); + CRITICAL ("Failed to create table pending_messages: %s\n", errmsg); sqlite3_free (errmsg); goto out; } @@ -266,6 +276,7 @@ out: g_free (filename); } + static const char * get_account_name (TpAccount *account) { @@ -273,6 +284,7 @@ get_account_name (TpAccount *account) strlen (TP_ACCOUNT_OBJECT_PATH_BASE); } + static const char * get_account_name_from_event (TplEvent *event) { @@ -280,6 +292,7 @@ get_account_name_from_event (TplEvent *event) strlen (TP_ACCOUNT_OBJECT_PATH_BASE); } + static const char * get_channel_name (TpChannel *chan) { @@ -287,12 +300,6 @@ get_channel_name (TpChannel *chan) strlen (TP_CONN_OBJECT_PATH_BASE); } -static const char * -get_channel_name_from_event (TplEvent *event) -{ - return _tpl_event_get_channel_path (event) + - strlen (TP_CONN_OBJECT_PATH_BASE); -} static char * get_date (TplEvent *event) @@ -305,17 +312,17 @@ get_date (TplEvent *event) g_date_time_unref (ts); - return date; } + static char * -get_datetime (TplEvent *event) +get_datetime (gint64 timestamp) { GDateTime *ts; gchar *date; - ts = g_date_time_new_from_unix_utc (tpl_event_get_timestamp (event)); + ts = g_date_time_new_from_unix_utc (timestamp); date = g_date_time_format (ts, TPL_LOG_STORE_SQLITE_TIMESTAMP_FORMAT); g_date_time_unref (ts); @@ -323,119 +330,13 @@ get_datetime (TplEvent *event) return date; } + static const char * tpl_log_store_sqlite_get_name (TplLogStore *self) { return TPL_LOG_STORE_SQLITE_NAME; } -/* returns log-id if present, NULL if not present */ -static gchar * -_cache_msg_id_is_present (TplLogStore *self, - TpChannel *channel, - guint msg_id) -{ - TplLogStoreSqlitePrivate *priv = GET_PRIV (self); - sqlite3_stmt *sql = NULL; - gchar *retval = NULL; - int e; - - g_return_val_if_fail (TPL_IS_LOG_STORE_SQLITE (self), NULL); - g_return_val_if_fail (TP_IS_CHANNEL (channel), NULL); - - /* get all the (chan,msg_id) couples, the most recent first */ - e = sqlite3_prepare_v2 (priv->db, - "SELECT log_identifier " - "FROM message_cache " - "WHERE channel=? AND pending_msg_id=? " - "GROUP BY date", - -1, &sql, NULL); - - if (e != SQLITE_OK) - { - CRITICAL ("Error preparing SQL to check msg_id %d for channel %s" - " presence: %s", msg_id, get_channel_name (channel), - sqlite3_errmsg (priv->db)); - goto out; - } - - sqlite3_bind_text (sql, 1, get_channel_name (channel), -1, SQLITE_TRANSIENT); - sqlite3_bind_int (sql, 2, msg_id); - - e = sqlite3_step (sql); - /* return the first (most recent) event if a raw is found */ - if (e == SQLITE_ROW) - retval = g_strdup ((const gchar *) sqlite3_column_text (sql, 0)); - else if (e == SQLITE_ERROR) - CRITICAL ("SQL Error: %s", sqlite3_errmsg (priv->db)); - -out: - if (sql != NULL) - sqlite3_finalize (sql); - - return retval; -} - - -/** - * _tpl_log_store_sqlite_log_id_is_present: - * @self: A TplLogStoreSqlite - * @log_id: the log identifier token - * - * Checks if @log_id is present in DB or not. - * - * Note that absence of @log_id in the current Sqlite doesn't mean - * that the message has never been logged. Sqlite currently maintains a record - * of recent log identifier (currently fresher than 5 days). - * - * This method can be safely used for a just arrived or just acknowledged - * message. - * - * Returns: %TRUE if @log_id is found, %FALSE otherwise - */ -gboolean -_tpl_log_store_sqlite_log_id_is_present (TplLogStore *self, - const gchar* log_id) -{ - TplLogStoreSqlitePrivate *priv = GET_PRIV (self); - sqlite3_stmt *sql = NULL; - gboolean retval = TRUE; /* TRUE = present, which usually is a failure */ - int e; - - g_return_val_if_fail (TPL_IS_LOG_STORE_SQLITE (self), FALSE); - g_return_val_if_fail (!TPL_STR_EMPTY (log_id), FALSE); - - e = sqlite3_prepare_v2 (priv->db, "SELECT log_identifier " - "FROM message_cache " - "WHERE log_identifier=?", - -1, &sql, NULL); - if (e != SQLITE_OK) - { - CRITICAL ("Error preparing SQL to check log_id %s presence: %s", - log_id, sqlite3_errmsg (priv->db)); - goto out; - } - - sqlite3_bind_text (sql, 1, log_id, -1, SQLITE_TRANSIENT); - - e = sqlite3_step (sql); - if (e == SQLITE_DONE) - { - DEBUG ("msg id %s not found, returning FALSE", log_id); - retval = FALSE; - } - else if (e == SQLITE_ROW) - DEBUG ("msg id %s found, returning TRUE", log_id); - else if (e != SQLITE_ROW) - CRITICAL ("SQL Error: %s", sqlite3_errmsg (priv->db)); - -out: - if (sql != NULL) - sqlite3_finalize (sql); - - return retval; -} - static gboolean tpl_log_store_sqlite_add_message_counter (TplLogStore *self, @@ -585,38 +486,6 @@ out: return retval; } -static gboolean -tpl_log_store_sqlite_add_message_cache (TplLogStore *self, - TplEvent *message, - GError **error) -{ - const char *log_id; - gboolean retval = FALSE; - - g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - - log_id = _tpl_event_get_log_id (message); - DEBUG ("received %s, considering if can be cached", log_id); - if (_tpl_log_store_sqlite_log_id_is_present (self, log_id)) - { - g_set_error (error, TPL_LOG_STORE_ERROR, - TPL_LOG_STORE_ERROR_PRESENT, - "in %s: log-id already logged: %s", G_STRFUNC, log_id); - - goto out; - } - - DEBUG ("caching %s", log_id); - retval = _insert_to_cache_table (self, message, error); - -out: - /* check that we set an error if appropriate */ - g_assert ((retval == TRUE && *error == NULL) || - (retval == FALSE && *error != NULL)); - - return retval; -} - /** * tpl_log_store_sqlite_add_event: @@ -657,6 +526,7 @@ tpl_log_store_sqlite_add_event (TplLogStore *self, gboolean retval = FALSE; g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + if (!TPL_IS_LOG_STORE_SQLITE (self)) { g_set_error (error, TPL_LOG_STORE_ERROR, @@ -664,6 +534,7 @@ tpl_log_store_sqlite_add_event (TplLogStore *self, "TplLogStoreSqlite intance needed"); goto out; } + if (!TPL_IS_EVENT (message)) { g_set_error (error, TPL_LOG_STORE_ERROR, @@ -671,12 +542,6 @@ tpl_log_store_sqlite_add_event (TplLogStore *self, goto out; } - retval = tpl_log_store_sqlite_add_message_cache (self, message, error); - if (retval == FALSE) - /* either the message has already been log, or a SQLite fatal error - * occurred, I won't update the counter table */ - goto out; - retval = tpl_log_store_sqlite_add_message_counter (self, message, error); out: @@ -688,215 +553,156 @@ out: return retval; } -static gboolean -_insert_to_cache_table (TplLogStore *self, - TplEvent *message, + +static void +tpl_log_store_sqlite_purge_pending_messages (TplLogStoreSqlite *self, + GTimeSpan delta, GError **error) { TplLogStoreSqlitePrivate *priv = GET_PRIV (self); - const char *account, *channel, *identifier, *log_id; - gboolean chatroom; - char *date = NULL; - gint msg_id; sqlite3_stmt *sql = NULL; - gboolean retval = FALSE; + GDateTime *now; + GDateTime *timestamp; + gchar *date; int e; - if (!TPL_IS_TEXT_EVENT (message)) - { - g_set_error (error, TPL_LOG_STORE_ERROR, - TPL_LOG_STORE_ERROR_ADD_EVENT, - "Message not handled by this log store"); - - goto out; - } + g_return_if_fail (error == NULL || *error == NULL); + g_return_if_fail (TPL_IS_LOG_STORE_SQLITE (self)); - account = get_account_name_from_event (message); - channel = get_channel_name_from_event (message); - identifier = _tpl_event_get_target_id (message); - log_id = _tpl_event_get_log_id (message); - msg_id = _tpl_text_event_get_pending_msg_id (TPL_TEXT_EVENT (message)); - chatroom = _tpl_event_target_is_room (message); - date = get_datetime (message); + now = g_date_time_new_now_utc (); + timestamp = g_date_time_add (now, -delta); - DEBUG ("channel = %s", channel); - DEBUG ("account = %s", account); - DEBUG ("chat_identifier = %s", identifier); - DEBUG ("log_identifier = %s", log_id); - DEBUG ("pending_msg_id = %d (%s)", msg_id, - (TPL_TEXT_EVENT_MSG_ID_IS_VALID (msg_id) ? - "pending" : "acknowledged or sent")); - DEBUG ("chatroom = %i", chatroom); - DEBUG ("date = %s", date); + date = g_date_time_format (timestamp, + TPL_LOG_STORE_SQLITE_TIMESTAMP_FORMAT); - if (TPL_STR_EMPTY (account) || TPL_STR_EMPTY (channel) || - TPL_STR_EMPTY (log_id) || TPL_STR_EMPTY (date)) - { - g_set_error_literal (error, TPL_LOG_STORE_ERROR, - TPL_LOG_STORE_ERROR_ADD_EVENT, - "passed LogStore has at least one of the needed properties unset: " - "account-path, channel-path, log-id, timestamp"); + g_date_time_unref (now); + g_date_time_unref (timestamp); - goto out; - } + DEBUG ("Purging entries older than %s (%u seconds ago)", date, (guint) delta); e = sqlite3_prepare_v2 (priv->db, - "INSERT INTO message_cache " - "(channel, account, pending_msg_id, log_identifier, " - "chat_identifier, chatroom, date) " - "VALUES (?, ?, ?, ?, ?, ?, datetime(?))", + "DELETE FROM pendign_messages WHERE date<datetime(?)", -1, &sql, NULL); + if (e != SQLITE_OK) { g_set_error (error, TPL_LOG_STORE_ERROR, TPL_LOG_STORE_ERROR_ADD_EVENT, - "SQL Error in %s: %s", G_STRFUNC, sqlite3_errmsg (priv->db)); + "SQL Error preparing statement in %s: %s", G_STRFUNC, + sqlite3_errmsg (priv->db)); goto out; } - sqlite3_bind_text (sql, 1, channel, -1, SQLITE_TRANSIENT); - sqlite3_bind_text (sql, 2, account, -1, SQLITE_TRANSIENT); - /* insert NULL if ACKNOWLEDGED (ie sent message's entries, which are created - * ACK'd */ - if (!TPL_TEXT_EVENT_MSG_ID_IS_VALID (msg_id)) - sqlite3_bind_null (sql, 3); - else - sqlite3_bind_int (sql, 3, msg_id); - sqlite3_bind_text (sql, 4, log_id, -1, SQLITE_TRANSIENT); - sqlite3_bind_text (sql, 5, identifier, -1, SQLITE_TRANSIENT); - sqlite3_bind_int (sql, 6, chatroom); - sqlite3_bind_text (sql, 7, date, -1, SQLITE_TRANSIENT); + sqlite3_bind_text (sql, 1, date, -1, SQLITE_TRANSIENT); e = sqlite3_step (sql); if (e != SQLITE_DONE) { g_set_error (error, TPL_LOG_STORE_ERROR, TPL_LOG_STORE_ERROR_ADD_EVENT, - "SQL Error bind in %s: %s", G_STRFUNC, sqlite3_errmsg (priv->db)); - - goto out; + "SQL Error in %s: %s", G_STRFUNC, sqlite3_errmsg (priv->db)); } - retval = TRUE; - out: - g_free (date); - if (sql != NULL) sqlite3_finalize (sql); - /* check that we set an error if appropriate */ - g_assert ((retval == TRUE && *error == NULL) || - (retval == FALSE && *error != NULL)); + g_free (date); +} - return retval; + +static gboolean +purge_event_timeout (gpointer logstore) +{ + GError *error = NULL; + TplLogStoreSqlite *self = logstore; + + tpl_log_store_sqlite_purge_pending_messages (self, + TPL_LOG_STORE_SQLITE_CLEANUP_DELTA_LIMIT, + &error); + + if (error != NULL) + { + CRITICAL ("Unable to purge entries: %s", error->message); + g_error_free (error); + } + + /* return TRUE to avoid g_timeout_add_seconds cancel the operation */ + return TRUE; } -/** - * _tpl_log_store_sqlite_get_log_ids: - * @self: a TplLogStoreSqlite instance - * @channel: a pointer to a TpChannel or NULL - * @timestamp: selects entries which timestamp is older than @timestamp. - * use %G_MAXUINT to obtain all the entries. - * @error: set if an error occurs - * - * It gets all the log-ids for messages matching the object-path of - * @channel and older than @timestamp. - * - * If @channel is %NULL, it will get all the existing log-ids. - * - * All the entries will be filtered against @timestamp, returning only log-ids - * older than this value (gint64). Set it to %G_MAXINT64 or any other value in - * the future to obtain all the entries. - * For example, to obtain entries older than one day ago, use - * @timestamp = (now - 86400) - * - * Note that (in case @channel is not %NULL) this method might return log-ids - * which are not currently related to @channel but just share the object-path, - * in fact it is possible that an channel-path is reused over time but referring - * to two completely different channels. - * There is no way to understand if a channel-path is actually related to a - * specific TpChannel instance with the same path or not, just knowking its - * path. - * This is not a problem, though, since log-ids are unique within TPL. If two - * log-ids match, they relates to the same TplEvent instance. - * - * Returns: a list of log-id - */ -GList * -_tpl_log_store_sqlite_get_log_ids (TplLogStore *self, - TpChannel *channel, - gint64 unix_timestamp, - GError **error) +static GList * +tpl_log_store_sqlite_get_entities (TplLogStore *self, + TpAccount *account) { TplLogStoreSqlitePrivate *priv = GET_PRIV (self); sqlite3_stmt *sql = NULL; - GList *retval = NULL; - GDateTime *timestamp; - gchar *date; int e; + GList *list = NULL; + const char *account_name = get_account_name (account); - g_return_val_if_fail (TPL_IS_LOG_STORE_SQLITE (self), NULL); + DEBUG ("account = %s", account_name); - if (channel == NULL) - /* get the the log-id older than date */ - e = sqlite3_prepare_v2 (priv->db, "SELECT log_identifier " - "FROM message_cache " - "WHERE date<datetime(?)", - -1, &sql, NULL); - else - /* get the log-ids related to channel and older than date */ - e = sqlite3_prepare_v2 (priv->db, "SELECT log_identifier " - "FROM message_cache " - "WHERE date<datetime(?) AND channel=?", - -1, &sql, NULL); + /* list all the identifiers known to the database */ + e = sqlite3_prepare_v2 (priv->db, + "SELECT DISTINCT identifier, chatroom FROM messagecounts WHERE " + "account=?", + -1, &sql, NULL); if (e != SQLITE_OK) { - CRITICAL ("Error preparing SQL for log-id list: %s", + DEBUG ("Failed to prepare SQL: %s", sqlite3_errmsg (priv->db)); + goto out; } - timestamp = g_date_time_new_from_unix_utc (unix_timestamp); - date = g_date_time_format (timestamp, - TPL_LOG_STORE_SQLITE_TIMESTAMP_FORMAT); - sqlite3_bind_text (sql, 1, date, -1, SQLITE_TRANSIENT); + sqlite3_bind_text (sql, 1, account_name, -1, SQLITE_TRANSIENT); - g_date_time_unref (timestamp); - g_free (date); + while ((e = sqlite3_step (sql)) == SQLITE_ROW) + { + TplEntity *entity; + const char *identifier; + gboolean chatroom; + TplEntityType type; - if (channel != NULL) - sqlite3_bind_text (sql, 2, get_channel_name (channel), -1, - SQLITE_TRANSIENT); + /* for some reason this returns unsigned char */ + identifier = (const char *) sqlite3_column_text (sql, 0); + chatroom = sqlite3_column_int (sql, 1); + type = chatroom ? TPL_ENTITY_ROOM : TPL_ENTITY_CONTACT; - /* create the log-id list */ - while (SQLITE_ROW == (e = sqlite3_step (sql))) - { - gchar *log_id = g_strdup ((const gchar *) sqlite3_column_text (sql, 0)); - retval = g_list_prepend (retval, log_id); - } + DEBUG ("identifier = %s, chatroom = %i", identifier, chatroom); + entity = tpl_entity_new (identifier, type, NULL, NULL); + + list = g_list_prepend (list, entity); + } if (e != SQLITE_DONE) { - g_set_error (error, TPL_LOG_STORE_SQLITE_ERROR, - TPL_LOG_STORE_SQLITE_ERROR_GET_PENDING_MESSAGES, - "SQL Error in %s: %s", G_STRFUNC, sqlite3_errmsg (priv->db)); - g_list_foreach (retval, (GFunc) g_free, NULL); - g_list_free (retval); - retval = NULL; + DEBUG ("Failed to execute SQL: %s", + sqlite3_errmsg (priv->db)); + goto out; } out: if (sql != NULL) sqlite3_finalize (sql); - /* check that we set an error if appropriate - * NOTE: retval == NULL && *error != - * NULL doesn't apply to this method, since NULL is also for an empty list */ - g_assert ((retval != NULL && *error == NULL) || retval == NULL); + return list; +} - return retval; +static void +log_store_iface_init (TplLogStoreInterface *iface) +{ + iface->get_name = tpl_log_store_sqlite_get_name; + iface->add_event = tpl_log_store_sqlite_add_event; + iface->get_entities = tpl_log_store_sqlite_get_entities; +} + +TplLogStore * +_tpl_log_store_sqlite_dup (void) +{ + return g_object_new (TPL_TYPE_LOG_STORE_SQLITE, NULL); } @@ -906,23 +712,12 @@ out: * @channel: a pointer to a TpChannel or NULL * @error: set if an error occurs * - * It gets all the log-ids for messages matching the object-path of - * @channel and which are still set as not acknowledged in the persisten - * layer. - * If @channel is %NULL, it will get all the pending messages in the - * persistence layer, not filtering against any channel. - * - * Note that (in case @channel is not %NULL) this method might return log-ids - * which are not currently related to @channel but just share the object-path, - * in fact it is possible that an channel-path is reused over time but referring - * to two completely different channels. - * There is no way to understand if a channel-path is actually related to a - * specific TpChannel instance with the same path or not, just knowking its - * path. - * This is not a problem, though, since log-ids are unique within TPL. If two - * log-ids match, they relates to the same TplEvent instance. + * Gets a #GList of pending message log-id associated with the @channel + * object-path. Note that those pending messaged might only share the + * object-path name, for this reason, messages must be validated + * against pending list provided by the connection manager. * - * Returns: a list of log-id + * Returns: (transfer full): a #GList of log-id */ GList * _tpl_log_store_sqlite_get_pending_messages (TplLogStore *self, @@ -935,37 +730,34 @@ _tpl_log_store_sqlite_get_pending_messages (TplLogStore *self, int e; g_return_val_if_fail (TPL_IS_LOG_STORE_SQLITE (self), NULL); - g_return_val_if_fail (TPL_IS_CHANNEL (channel) || channel == NULL, NULL); + g_return_val_if_fail (TPL_IS_CHANNEL (channel), NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); - if (channel == NULL) - /* get all the pending log-ids */ - e = sqlite3_prepare_v2 (priv->db, "SELECT log_identifier " - "FROM message_cache " - "WHERE pending_msg_id is NOT NULL", - -1, &sql, NULL); - else - /* get the pending log-ids related to channel */ - e = sqlite3_prepare_v2 (priv->db, "SELECT log_identifier " - "FROM message_cache " - "WHERE pending_msg_id is NOT NULL AND channel=?", - -1, &sql, NULL); + e = sqlite3_prepare_v2 (priv->db, "SELECT log_id" + "FROM pending_messages " + "WHERE channel=?", + -1, &sql, NULL); + if (e != SQLITE_OK) { CRITICAL ("Error preparing SQL for pending messages list: %s", sqlite3_errmsg (priv->db)); + g_set_error (error, TPL_LOG_STORE_SQLITE_ERROR, + TPL_LOG_STORE_SQLITE_ERROR_GET_PENDING_MESSAGES, + "SQL Error in %s: %s", G_STRFUNC, sqlite3_errmsg (priv->db)); goto out; } - if (channel != NULL) - sqlite3_bind_text (sql, 1, get_channel_name (channel), -1, - SQLITE_TRANSIENT); + sqlite3_bind_text (sql, 1, get_channel_name (channel), -1, + SQLITE_TRANSIENT); while (SQLITE_ROW == (e = sqlite3_step (sql))) { /* create the pending messages list */ gchar *log_id = g_strdup ((const gchar *) sqlite3_column_text (sql, 0)); - retval = g_list_prepend (retval, log_id); + retval = g_list_insert_sorted (retval, + log_id, + (GCompareFunc) g_strcmp0); } if (e != SQLITE_DONE) @@ -992,230 +784,157 @@ out: return retval; } -void -_tpl_log_store_sqlite_set_acknowledgment_by_msg_id (TplLogStore *self, - TpChannel *channel, - guint msg_id, - GError **error) -{ - gchar *log_id = NULL; - - g_return_if_fail (error == NULL || *error == NULL); - g_return_if_fail (TPL_IS_LOG_STORE_SQLITE (self)); - g_return_if_fail (TP_IS_CHANNEL (channel)); - - log_id = _cache_msg_id_is_present (self, channel, msg_id); - - if (log_id != NULL) - { - DEBUG ("%s: found %s for pending id %d", get_channel_name (channel), - log_id, msg_id); - _tpl_log_store_sqlite_set_acknowledgment (self, log_id, error); - } - else - g_set_error (error, TPL_LOG_STORE_ERROR, - TPL_LOG_STORE_ERROR_NOT_PRESENT, - "Unable to acknowledge pending message %d for channel %s: not found", - msg_id, get_channel_name (channel)); - - g_free (log_id); -} -void -_tpl_log_store_sqlite_set_acknowledgment (TplLogStore *self, - const gchar* log_id, +/** + *_tpl_log_store_sqlite_remove_pending_messages: + * @self: a #TplLogStore + * @log_ids: a #GList of string + * @error: a #GError to be set on error, or NULL + * + * Removes listed pending IDs. + * + * Returns: #TRUE on success, #FALSE on error with @error set + */ +gboolean +_tpl_log_store_sqlite_remove_pending_messages (TplLogStore *self, + GList *log_ids, GError **error) { TplLogStoreSqlitePrivate *priv = GET_PRIV (self); + gboolean retval = TRUE; + GString *query = NULL; + GList *it; sqlite3_stmt *sql = NULL; - int e; - g_return_if_fail (error == NULL || *error == NULL); - g_return_if_fail (TPL_IS_LOG_STORE_SQLITE (self)); - g_return_if_fail (!TPL_STR_EMPTY (log_id)); + g_return_val_if_fail (TPL_IS_LOG_STORE_SQLITE (self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + g_return_val_if_fail (log_ids != NULL, FALSE); - if (!_tpl_log_store_sqlite_log_id_is_present (TPL_LOG_STORE (self), log_id)) - { - g_set_error (error, TPL_LOG_STORE_ERROR, - TPL_LOG_STORE_ERROR_NOT_PRESENT, - "log_id %s not found", log_id); - goto out; - } + query = g_string_new ("DELETE FROM pending_messages WHERE log_id IN ("); - e = sqlite3_prepare_v2 (priv->db, "UPDATE message_cache " - "SET pending_msg_id=NULL " - "WHERE log_identifier=?", -1, &sql, NULL); - if (e != SQLITE_OK) + g_string_append_printf (query, "'%s'", (const gchar *) log_ids->data); + + for (it = g_list_next (log_ids); it != NULL; it = g_list_next (it)) + g_string_append_printf (query, ",'%s'", (const gchar *) it->data); + + g_string_append (query, ")"); + + if (sqlite3_prepare_v2 (priv->db, query->str, -1, &sql, NULL) != SQLITE_OK) { - g_set_error (error, TPL_LOG_STORE_ERROR, - TPL_LOG_STORE_ERROR_ADD_EVENT, + g_set_error (error, TPL_LOG_STORE_SQLITE_ERROR, + TPL_LOG_STORE_SQLITE_ERROR_REMOVE_PENDING_MESSAGES, "SQL Error in %s: %s", G_STRFUNC, sqlite3_errmsg (priv->db)); - + retval = FALSE; goto out; } - sqlite3_bind_text (sql, 1, log_id, -1, SQLITE_TRANSIENT); - - e = sqlite3_step (sql); - if (e != SQLITE_DONE) + if (sqlite3_step (sql) != SQLITE_DONE) { - g_set_error (error, TPL_LOG_STORE_ERROR, - TPL_LOG_STORE_ERROR_ADD_EVENT, + g_set_error (error, TPL_LOG_STORE_SQLITE_ERROR, + TPL_LOG_STORE_SQLITE_ERROR_REMOVE_PENDING_MESSAGES, "SQL Error in %s: %s", G_STRFUNC, sqlite3_errmsg (priv->db)); + retval = FALSE; + goto out; } out: + if (query != NULL) + g_string_free (query, TRUE); + if (sql != NULL) sqlite3_finalize (sql); + + return retval; } -static void -tpl_log_store_sqlite_purge (TplLogStoreSqlite *self, - GTimeSpan delta, +/** + *_tpl_log_store_sqlite_add_pending_message: + * @self: a #TplLogStore + * @channel: a #TpChannel + * @log_id: a string unique identifier for the log + * @timestamp: a unix utc timestamp + * @error: a #GError to be set on error, or NULL + * + * Removes listed pending IDs. + * + * Returns: #TRUE on success, #FALSE on error with @error set + */ +gboolean +_tpl_log_store_sqlite_add_pending_message (TplLogStore *self, + TpChannel *channel, + const gchar *log_id, + gint64 timestamp, GError **error) { TplLogStoreSqlitePrivate *priv = GET_PRIV (self); + gboolean retval = FALSE; + const gchar *channel_path; + gchar *date = NULL; sqlite3_stmt *sql = NULL; - GDateTime *now; - GDateTime *timestamp; - gchar *date; int e; - g_return_if_fail (error == NULL || *error == NULL); - g_return_if_fail (TPL_IS_LOG_STORE_SQLITE (self)); - - now = g_date_time_new_now_utc (); - timestamp = g_date_time_add (now, -delta); - - date = g_date_time_format (timestamp, - TPL_LOG_STORE_SQLITE_TIMESTAMP_FORMAT); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); - g_date_time_unref (now); - g_date_time_unref (timestamp); + DEBUG ("caching pending message %s", log_id); - DEBUG ("Purging entries older than %s (%u seconds ago)", date, (guint) delta); + channel_path = get_channel_name (channel); + date = get_datetime (timestamp); - e = sqlite3_prepare_v2 (priv->db, "DELETE FROM message_cache " - "WHERE date<datetime(?)", - -1, &sql, NULL); + DEBUG ("channel = %s", channel_path); + DEBUG ("date = %s", date); - if (e != SQLITE_OK) + if (TPL_STR_EMPTY (channel_path) + || TPL_STR_EMPTY (log_id) + || TPL_STR_EMPTY (date)) { - g_set_error (error, TPL_LOG_STORE_ERROR, - TPL_LOG_STORE_ERROR_ADD_EVENT, - "SQL Error preparing statement in %s: %s", G_STRFUNC, - sqlite3_errmsg (priv->db)); - + g_set_error_literal (error, TPL_LOG_STORE_ERROR, + TPL_LOG_STORE_SQLITE_ERROR_ADD_PENDING_MESSAGE, + "passed LogStore has at least one of the needed properties unset: " + "channel-path, log-id, timestamp"); goto out; } - sqlite3_bind_text (sql, 1, date, -1, SQLITE_TRANSIENT); - - e = sqlite3_step (sql); - if (e != SQLITE_DONE) - { - g_set_error (error, TPL_LOG_STORE_ERROR, - TPL_LOG_STORE_ERROR_ADD_EVENT, - "SQL Error in %s: %s", G_STRFUNC, sqlite3_errmsg (priv->db)); - } - -out: - if (sql != NULL) - sqlite3_finalize (sql); - - g_free (date); -} - -static gboolean -purge_event_timeout (gpointer logstore) -{ - GError *error = NULL; - TplLogStoreSqlite *self = logstore; - - tpl_log_store_sqlite_purge (self, TPL_LOG_STORE_SQLITE_CLEANUP_DELTA_LIMIT, - &error); - if (error != NULL) - { - CRITICAL ("Unable to purge entries: %s", error->message); - g_error_free (error); - } - - /* return TRUE to avoid g_timeout_add_seconds cancel the operation */ - return TRUE; -} - -static GList * -tpl_log_store_sqlite_get_entities (TplLogStore *self, - TpAccount *account) -{ - TplLogStoreSqlitePrivate *priv = GET_PRIV (self); - sqlite3_stmt *sql = NULL; - int e; - GList *list = NULL; - const char *account_name = get_account_name (account); - - DEBUG ("account = %s", account_name); - - /* list all the identifiers known to the database */ e = sqlite3_prepare_v2 (priv->db, - "SELECT DISTINCT identifier, chatroom FROM messagecounts WHERE " - "account=?", + "INSERT INTO pending_messages " + "(channel, log_id, data) " + "VALUES (?, ?, datetime(?))", -1, &sql, NULL); if (e != SQLITE_OK) { - DEBUG ("Failed to prepare SQL: %s", - sqlite3_errmsg (priv->db)); - + g_set_error (error, TPL_LOG_STORE_ERROR, + TPL_LOG_STORE_SQLITE_ERROR_ADD_PENDING_MESSAGE, + "SQL Error in %s: %s", G_STRFUNC, sqlite3_errmsg (priv->db)); goto out; } - sqlite3_bind_text (sql, 1, account_name, -1, SQLITE_TRANSIENT); - - while ((e = sqlite3_step (sql)) == SQLITE_ROW) - { - TplEntity *entity; - const char *identifier; - gboolean chatroom; - TplEntityType type; - - /* for some reason this returns unsigned char */ - identifier = (const char *) sqlite3_column_text (sql, 0); - chatroom = sqlite3_column_int (sql, 1); - type = chatroom ? TPL_ENTITY_ROOM : TPL_ENTITY_CONTACT; - - DEBUG ("identifier = %s, chatroom = %i", identifier, chatroom); - - entity = tpl_entity_new (identifier, type, NULL, NULL); + sqlite3_bind_text (sql, 1, channel_path, -1, SQLITE_TRANSIENT); + sqlite3_bind_text (sql, 2, log_id, -1, SQLITE_TRANSIENT); + sqlite3_bind_text (sql, 3, date, -1, SQLITE_TRANSIENT); - list = g_list_prepend (list, entity); - } + e = sqlite3_step (sql); if (e != SQLITE_DONE) { - DEBUG ("Failed to execute SQL: %s", - sqlite3_errmsg (priv->db)); + g_set_error (error, TPL_LOG_STORE_ERROR, + TPL_LOG_STORE_SQLITE_ERROR_ADD_PENDING_MESSAGE, + "SQL Error bind in %s: %s", G_STRFUNC, sqlite3_errmsg (priv->db)); goto out; } + retval = TRUE; + out: + g_free (date); + if (sql != NULL) sqlite3_finalize (sql); - return list; -} + /* check that we set an error if appropriate */ + g_assert ((retval == TRUE && *error == NULL) || + (retval == FALSE && *error != NULL)); -static void -log_store_iface_init (TplLogStoreInterface *iface) -{ - iface->get_name = tpl_log_store_sqlite_get_name; - iface->add_event = tpl_log_store_sqlite_add_event; - iface->get_entities = tpl_log_store_sqlite_get_entities; + return retval; } -TplLogStore * -_tpl_log_store_sqlite_dup (void) -{ - return g_object_new (TPL_TYPE_LOG_STORE_SQLITE, NULL); -} gint64 _tpl_log_store_sqlite_get_most_recent (TplLogStoreSqlite *self, |