[tracker/fts4: 19/26] Use fts4 external content tables on Tracker FTS
- From: Martyn James Russell <mr src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [tracker/fts4: 19/26] Use fts4 external content tables on Tracker FTS
- Date: Tue, 5 Feb 2013 16:55:40 +0000 (UTC)
commit 6460779dc792878807cb7921cfc9d946868429e0
Author: Carlos Garnacho <carlos lanedo com>
Date: Mon Jan 28 17:20:05 2013 +0100
Use fts4 external content tables on Tracker FTS
FTS support on Tracker is now implemented using external content
support available in sqlite >= 3.7.9 FTS4.
FTS tables created this way fetch data from a single table/view,
for this purpose an intermediate view has been created to
interface with the FTS table. As this view is mainly queried by
ID (both when populating the FTS contents, and when querying
throught the FTS table), queries are fast enough on it.
As FTS indirectly points to the data on the real tables where tracker
data is stored, strings themselves are stored only once in the database,
so there is no impact in database size when compared to the previous
custom FTS code. Performance had little changes too from testing.
src/libtracker-data/tracker-data-manager.c | 109 +++++++------
src/libtracker-data/tracker-data-manager.h | 3 +
src/libtracker-data/tracker-db-interface-sqlite.c | 128 ++++++++++------
src/libtracker-data/tracker-db-interface-sqlite.h | 7 +-
src/libtracker-data/tracker-db-manager.c | 3 +-
src/libtracker-fts/tracker-fts.c | 174 ++++++++------------
src/libtracker-fts/tracker-fts.h | 13 +-
7 files changed, 229 insertions(+), 208 deletions(-)
---
diff --git a/src/libtracker-data/tracker-data-manager.c b/src/libtracker-data/tracker-data-manager.c
index c8b2f9e..4ff890e 100644
--- a/src/libtracker-data/tracker-data-manager.c
+++ b/src/libtracker-data/tracker-data-manager.c
@@ -3583,54 +3583,76 @@ load_ontologies_gvdb (GError **error)
}
#if HAVE_TRACKER_FTS
-static gint
-compare_fts_property_ids (gconstpointer a,
- gconstpointer b)
-{
- TrackerProperty *pa, *pb;
-
- pa = (TrackerProperty *) a;
- pb = (TrackerProperty *) b;
-
- return tracker_property_get_id (pa) - tracker_property_get_id (pb);
-}
-
-static const gchar **
-ontology_get_fts_properties (gboolean only_new)
+static gboolean
+ontology_get_fts_properties (gboolean only_new,
+ GHashTable **fts_properties,
+ GHashTable **multivalued)
{
TrackerProperty **properties;
- GList *fts_props = NULL, *l;
- const gchar **prop_names;
+ gboolean has_new = FALSE;
+ GHashTable *hashtable;
guint i, len;
properties = tracker_ontologies_get_properties (&len);
+ hashtable = g_hash_table_new_full (g_str_hash, g_str_equal, NULL,
+ (GDestroyNotify) g_list_free);
+
+ if (multivalued) {
+ *multivalued = g_hash_table_new (g_str_hash, g_str_equal);
+ }
for (i = 0; i < len; i++) {
- if (only_new && !tracker_property_get_is_new (properties[i])) {
+ const gchar *name, *table_name;
+ GList *list;
+
+ if (!tracker_property_get_fulltext_indexed (properties[i])) {
continue;
}
- if (tracker_property_get_fulltext_indexed (properties[i])) {
- /* Sort them by ID */
- fts_props =
- g_list_insert_sorted (fts_props, properties[i],
- (GCompareFunc) compare_fts_property_ids);
+ has_new |= tracker_property_get_is_new (properties[i]);
+
+ if (multivalued &&
+ tracker_property_get_multiple_values (properties[i])) {
+ g_hash_table_insert (*multivalued, (gpointer) table_name,
+ GUINT_TO_POINTER (TRUE));
}
- }
- prop_names = g_new0 (const gchar *, g_list_length (fts_props) + 1);
+ table_name = tracker_property_get_table_name (properties[i]);
+ name = tracker_property_get_name (properties[i]);
+ list = g_hash_table_lookup (hashtable, table_name);
- for (l = fts_props, i = 0; l; l = l->next, i++) {
- prop_names[i] = tracker_property_get_name (l->data);
+ if (!list) {
+ list = g_list_prepend (NULL, (gpointer) name);
+ g_hash_table_insert (hashtable, (gpointer) table_name, list);
+ } else {
+ list = g_list_append (list, (gpointer) name);
+ }
}
- g_list_free (fts_props);
+ if (fts_properties) {
+ *fts_properties = hashtable;
+ }
- return prop_names;
+ return has_new;
}
#endif
gboolean
+tracker_data_manager_init_fts (TrackerDBInterface *iface,
+ gboolean create)
+{
+#if HAVE_TRACKER_FTS
+ GHashTable *fts_props, *multivalued;
+
+ ontology_get_fts_properties (FALSE, &fts_props, &multivalued);
+ tracker_db_interface_sqlite_fts_init (iface, fts_props,
+ multivalued, create);
+ g_hash_table_destroy (fts_props);
+ g_hash_table_destroy (multivalued);
+#endif
+}
+
+gboolean
tracker_data_manager_init (TrackerDBManagerFlags flags,
const gchar **test_schemas,
gboolean *first_time,
@@ -3807,10 +3829,6 @@ tracker_data_manager_init (TrackerDBManagerFlags flags,
#endif /* DISABLE_JOURNAL */
if (is_first_time_index && !read_only) {
-#if HAVE_TRACKER_FTS
- const gchar **fts_props;
-#endif
-
sorted = get_ontologies (test_schemas != NULL, ontologies_dir);
#ifndef DISABLE_JOURNAL
@@ -3899,15 +3917,13 @@ tracker_data_manager_init (TrackerDBManagerFlags flags,
return FALSE;
}
-#if HAVE_TRACKER_FTS
- fts_props = ontology_get_fts_properties (FALSE);
- tracker_db_interface_sqlite_fts_init (iface, fts_props, TRUE);
- g_free (fts_props);
-#endif
-
tracker_data_ontology_import_into_db (FALSE,
&internal_error);
+#if HAVE_TRACKER_FTS
+ tracker_data_manager_init_fts (iface, TRUE);
+#endif
+
if (internal_error) {
g_propagate_error (error, internal_error);
@@ -4050,10 +4066,7 @@ tracker_data_manager_init (TrackerDBManagerFlags flags,
}
}
-#if HAVE_TRACKER_FTS
- /* This is a no-op when FTS is disabled */
- tracker_db_interface_sqlite_fts_init (iface, NULL, FALSE);
-#endif
+ tracker_data_manager_init_fts (iface, FALSE);
}
if (check_ontology) {
@@ -4345,14 +4358,14 @@ tracker_data_manager_init (TrackerDBManagerFlags flags,
if (update_nao) {
#if HAVE_TRACKER_FTS
- const gchar **new_fts_properties;
+ GHashTable *fts_properties, *multivalued;
- new_fts_properties = ontology_get_fts_properties (TRUE);
-
- if (new_fts_properties) {
- tracker_db_interface_sqlite_fts_alter_table (iface, new_fts_properties, NULL);
- g_free (new_fts_properties);
+ if (ontology_get_fts_properties (TRUE, &fts_properties, &multivalued)) {
+ tracker_db_interface_sqlite_fts_alter_table (iface, fts_properties, multivalued);
}
+
+ g_hash_table_destroy (fts_properties);
+ g_hash_table_destroy (multivalued);
#endif
/* Update the nao:lastModified in the database */
diff --git a/src/libtracker-data/tracker-data-manager.h b/src/libtracker-data/tracker-data-manager.h
index 6aa21ea..bd82feb 100644
--- a/src/libtracker-data/tracker-data-manager.h
+++ b/src/libtracker-data/tracker-data-manager.h
@@ -63,6 +63,9 @@ gboolean tracker_data_manager_reload (TrackerBusyCallback bu
const gchar *busy_operation,
GError **error);
+gboolean tracker_data_manager_init_fts (TrackerDBInterface *interface,
+ gboolean create);
+
G_END_DECLS
#endif /* __LIBTRACKER_DATA_MANAGER_H__ */
diff --git a/src/libtracker-data/tracker-db-interface-sqlite.c b/src/libtracker-data/tracker-db-interface-sqlite.c
index ed27b55..a094669 100644
--- a/src/libtracker-data/tracker-db-interface-sqlite.c
+++ b/src/libtracker-data/tracker-db-interface-sqlite.c
@@ -94,6 +94,8 @@ struct TrackerDBInterface {
TrackerBusyCallback busy_callback;
gpointer busy_user_data;
gchar *busy_status;
+
+ gchar *fts_insert_str;
};
struct TrackerDBInterfaceClass {
@@ -991,28 +993,89 @@ close_database (TrackerDBInterface *db_interface)
}
}
+static gchar **
+_fts_create_properties (GHashTable *properties)
+{
+ GHashTableIter iter;
+ GPtrArray *cols;
+ GList *columns;
+ gchar *table;
+
+ if (g_hash_table_size (properties) == 0) {
+ return NULL;
+ }
+
+ g_hash_table_iter_init (&iter, properties);
+ cols = g_ptr_array_new ();
+
+ while (g_hash_table_iter_next (&iter, (gpointer *) &table,
+ (gpointer *) &columns)) {
+ while (columns) {
+ g_ptr_array_add (cols, g_strdup (columns->data));
+ columns = columns->next;
+ }
+ }
+
+ g_ptr_array_add (cols, NULL);
+
+ return (gchar **) g_ptr_array_free (cols, FALSE);
+}
+
void
tracker_db_interface_sqlite_fts_init (TrackerDBInterface *db_interface,
- const gchar **columns,
+ GHashTable *properties,
+ GHashTable *multivalued,
gboolean create)
{
#if HAVE_TRACKER_FTS
+ GStrv fts_columns = NULL;
+
tracker_fts_init_db (db_interface->db);
+ if (properties) {
+ fts_columns = _fts_create_properties (properties);
+ }
+
if (create &&
- !tracker_fts_create_table (db_interface->db, "fts", columns)) {
+ !tracker_fts_create_table (db_interface->db, "fts",
+ properties, multivalued)) {
g_warning ("FTS tables creation failed");
}
+
+ if (fts_columns) {
+ GString *insert, *select;
+ gint i = 0;
+
+ insert = g_string_new ("INSERT INTO fts (docid");
+ select = g_string_new ("SELECT rowid");
+
+ while (fts_columns[i]) {
+ g_string_append_printf (insert, ", \"%s\"",
+ fts_columns[i]);
+ g_string_append_printf (select, ", \"%s\"",
+ fts_columns[i]);
+ i++;
+ }
+
+ g_string_append (select, " FROM fts_view WHERE rowid=?");
+ g_string_append (insert, ") ");
+ g_string_append (insert, select->str);
+
+ g_string_free (select, TRUE);
+ db_interface->fts_insert_str = g_string_free (insert, FALSE);
+
+ g_strfreev (fts_columns);
+ }
#endif
}
#if HAVE_TRACKER_FTS
void
tracker_db_interface_sqlite_fts_alter_table (TrackerDBInterface *db_interface,
- const gchar **added_columns,
- const gchar **removed_columns)
+ GHashTable *properties,
+ GHashTable *multivalued)
{
- if (!tracker_fts_alter_table (db_interface->db, "fts", added_columns, removed_columns)) {
+ if (!tracker_fts_alter_table (db_interface->db, "fts", properties, multivalued)) {
g_critical ("Failed to update FTS columns");
}
}
@@ -1026,40 +1089,27 @@ tracker_db_interface_sqlite_fts_update_text (TrackerDBInterface *db_interface,
{
TrackerDBStatement *stmt;
GError *error = NULL;
- GString *query;
- gint i;
-
- if (create) {
- query = g_string_new ("INSERT INTO fts (docid");
-
- for (i = 0; properties[i] != NULL; i++) {
- g_string_append_printf (query, ", \"%s\"", properties[i]);
- }
-
- g_string_append (query, ") VALUES (?");
- for (i = 0; properties[i] != NULL; i++) {
- g_string_append (query, ", ?");
- }
+ if (!create) {
+ stmt = tracker_db_interface_create_statement (db_interface,
+ TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE,
+ &error,
+ "DELETE FROM fts WHERE docid=?");
+ tracker_db_statement_bind_int (stmt, 0, id);
- g_string_append_c (query, ')');
- } else {
- query = g_string_new ("UPDATE fts SET ");
+ tracker_db_statement_execute (stmt, &error);
+ g_object_unref (stmt);
- for (i = 0; properties[i] != NULL; i++) {
- if (i != 0) {
- g_string_append_c (query, ',');
- }
- g_string_append_printf (query, "\"%s\" = ?", properties[i]);
+ if (error) {
+ g_warning ("Could not update FTS text: %s", error->message);
+ g_error_free (error);
+ return FALSE;
}
-
- g_string_append (query, " WHERE docid = ?");
}
stmt = tracker_db_interface_create_statement (db_interface,
TRACKER_DB_STATEMENT_CACHE_TYPE_UPDATE,
- &error, query->str);
- g_string_free (query, TRUE);
+ &error, db_interface->fts_insert_str);
if (!stmt || error) {
if (error) {
@@ -1070,20 +1120,7 @@ tracker_db_interface_sqlite_fts_update_text (TrackerDBInterface *db_interface,
return FALSE;
}
- if (create) {
- tracker_db_statement_bind_int (stmt, 0, id);
-
- for (i = 0; properties[i] != NULL; i++) {
- tracker_db_statement_bind_text (stmt, i + 1, text[i]);
- }
- } else {
- for (i = 0; properties[i] != NULL; i++) {
- tracker_db_statement_bind_text (stmt, i, text[i]);
- }
-
- tracker_db_statement_bind_int (stmt, i, id);
- }
-
+ tracker_db_statement_bind_int (stmt, 0, id);
tracker_db_statement_execute (stmt, &error);
g_object_unref (stmt);
@@ -1184,6 +1221,7 @@ tracker_db_interface_sqlite_finalize (GObject *object)
db_interface = TRACKER_DB_INTERFACE (object);
close_database (db_interface);
+ g_free (db_interface->fts_insert_str);
g_message ("Closed sqlite3 database:'%s'", db_interface->filename);
diff --git a/src/libtracker-data/tracker-db-interface-sqlite.h b/src/libtracker-data/tracker-db-interface-sqlite.h
index f0e2d90..548814a 100644
--- a/src/libtracker-data/tracker-db-interface-sqlite.h
+++ b/src/libtracker-data/tracker-db-interface-sqlite.h
@@ -41,7 +41,8 @@ TrackerDBInterface *tracker_db_interface_sqlite_new_ro (const gc
gint64 tracker_db_interface_sqlite_get_last_insert_id (TrackerDBInterface *interface);
void tracker_db_interface_sqlite_enable_shared_cache (void);
void tracker_db_interface_sqlite_fts_init (TrackerDBInterface *interface,
- const gchar **columns,
+ GHashTable *properties,
+ GHashTable *multivalued,
gboolean create);
void tracker_db_interface_sqlite_reset_collator (TrackerDBInterface *interface);
void tracker_db_interface_sqlite_wal_hook (TrackerDBInterface *interface,
@@ -49,8 +50,8 @@ void tracker_db_interface_sqlite_wal_hook (TrackerD
#if HAVE_TRACKER_FTS
void tracker_db_interface_sqlite_fts_alter_table (TrackerDBInterface *interface,
- const gchar **added_columns,
- const gchar **removed_columns);
+ GHashTable *properties,
+ GHashTable *multivalued);
int tracker_db_interface_sqlite_fts_update_text (TrackerDBInterface *interface,
int id,
const gchar **properties,
diff --git a/src/libtracker-data/tracker-db-manager.c b/src/libtracker-data/tracker-db-manager.c
index ab59148..67cabc4 100644
--- a/src/libtracker-data/tracker-db-manager.c
+++ b/src/libtracker-data/tracker-db-manager.c
@@ -1522,8 +1522,7 @@ tracker_db_manager_get_db_interface (void)
return NULL;
}
- tracker_db_interface_sqlite_fts_init (interface, NULL, FALSE);
-
+ tracker_data_manager_init_fts (interface, FALSE);
tracker_db_interface_set_max_stmt_cache_size (interface,
TRACKER_DB_STATEMENT_CACHE_TYPE_SELECT,
diff --git a/src/libtracker-fts/tracker-fts.c b/src/libtracker-fts/tracker-fts.c
index 6c8afa7..18c4876 100644
--- a/src/libtracker-fts/tracker-fts.c
+++ b/src/libtracker-fts/tracker-fts.c
@@ -236,154 +236,120 @@ tracker_fts_init_db (sqlite3 *db) {
}
gboolean
-tracker_fts_create_table (sqlite3 *db,
- gchar *table_name,
- gchar **column_names)
+tracker_fts_create_table (sqlite3 *db,
+ gchar *table_name,
+ GHashTable *tables,
+ GHashTable *grouped_columns)
{
- GString *str;
- gint i, rc;
+ GString *str, *from, *fts;
+ GHashTableIter iter;
+ gchar *index_table;
+ GList *columns;
+ gint rc;
+
+ /* Create view on tables/columns marked as FTS-indexed */
+ g_hash_table_iter_init (&iter, tables);
+ str = g_string_new ("CREATE VIEW fts_view AS SELECT Resource.ID as rowid ");
+ from = g_string_new ("FROM Resource ");
+
+ fts = g_string_new ("CREATE VIRTUAL TABLE ");
+ g_string_append_printf (fts, "%s USING fts4(content=\"fts_view\", ",
+ table_name);
+
+ while (g_hash_table_iter_next (&iter, (gpointer *) &index_table,
+ (gpointer *) &columns)) {
+ while (columns) {
+ if (grouped_columns &&
+ g_hash_table_lookup (grouped_columns, columns->data)) {
+ g_string_append_printf (str, ", group_concat(\"%s\".\"%s\")",
+ index_table,
+ (gchar *) columns->data);
+ } else {
+ g_string_append_printf (str, ", \"%s\".\"%s\"",
+ index_table,
+ (gchar *) columns->data);
+ }
+
+ g_string_append_printf (str, " AS \"%s\" ",
+ (gchar *) columns->data);
+ g_string_append_printf (fts, "\"%s\", ",
+ (gchar *) columns->data);
- str = g_string_new ("CREATE VIRTUAL TABLE ");
- g_string_append_printf (str, "%s USING fts4(", table_name);
+ columns = columns->next;
+ }
- for (i = 0; column_names[i]; i++) {
- g_string_append_printf (str, "\"%s\", ", column_names[i]);
+ g_string_append_printf (from, "LEFT OUTER JOIN \"%s\" ON "
+ " Resource.ID = \"%s\".ID ",
+ index_table, index_table);
}
- g_string_append (str, " tokenize=TrackerTokenizer)");
+ g_string_append (str, from->str);
+ g_string_free (from, TRUE);
rc = sqlite3_exec(db, str->str, NULL, 0, NULL);
g_string_free (str, TRUE);
- return (rc == SQLITE_OK);
-}
-
-gboolean
-tracker_fts_alter_table (sqlite3 *db,
- gchar *table_name,
- gchar **added_columns,
- gchar **removed_columns)
-{
- GString *columns_str = NULL;
- GPtrArray *columns;
- sqlite3_stmt *stmt;
- gchar *query, *tmp_name;
- int rc, i;
-
- if (!added_columns && !removed_columns) {
- return TRUE;
- }
-
- query = g_strdup_printf ("PRAGMA table_info(%s)", table_name);
- rc = sqlite3_prepare_v2 (db, query, -1, &stmt, NULL);
- g_free (query);
-
if (rc != SQLITE_OK) {
return FALSE;
}
- columns = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free);
-
- /* Fetch the old columns, don't add stuff in removed_columns */
- while ((rc = sqlite3_step (stmt)) != SQLITE_DONE) {
- if (rc == SQLITE_ROW) {
- const gchar *name;
-
- name = sqlite3_column_text (stmt, 1);
+ g_string_append (fts, "tokenize=TrackerTokenizer)");
+ rc = sqlite3_exec(db, fts->str, NULL, 0, NULL);
+ g_string_free (fts, TRUE);
- for (i = 0; removed_columns && removed_columns[i]; i++) {
- if (g_strcmp0 (name, removed_columns[i]) == 0) {
- continue;
- }
- }
-
- g_ptr_array_add (columns, g_strdup (name));
- }
- }
-
- if (rc == SQLITE_DONE) {
- rc = sqlite3_finalize (stmt);
- }
-
- if (rc != SQLITE_OK) {
- g_ptr_array_free (columns, TRUE);
- return FALSE;
- }
-
- /* In columns we have the current columns, minus the removed columns,
- * create the update we'll execute later on to dump data from one
- * table to another.
- */
- for (i = 0; i < columns->len; i++) {
- if (!columns_str) {
- columns_str = g_string_new ("");
- } else {
- g_string_append_c (columns_str, ',');
- }
-
- g_string_append_printf (columns_str, "\"%s\"",
- (gchar *) g_ptr_array_index (columns, i));
- }
+ return (rc == SQLITE_OK);
+}
- if (!columns_str) {
- g_ptr_array_free (columns, TRUE);
- return FALSE;
- }
+gboolean
+tracker_fts_alter_table (sqlite3 *db,
+ gchar *table_name,
+ GHashTable *tables,
+ GHashTable *grouped_columns)
+{
+ gchar *query, *tmp_name;
+ sqlite3_stmt *stmt;
+ int rc;
tmp_name = g_strdup_printf ("%s_TMP", table_name);
- query = g_strdup_printf ("INSERT INTO %s (%s) SELECT %s FROM %s",
- tmp_name, columns_str->str,
- columns_str->str, table_name);
- g_string_free (columns_str, TRUE);
-
- /* Now append stuff in added_columns and create the temporary table */
- for (i = 0; added_columns && added_columns[i]; i++) {
- g_ptr_array_add (columns, g_strdup (added_columns[i]));
- }
+ query = g_strdup_printf ("DROP VIEW fts_view", table_name);
+ rc = sqlite3_prepare_v2 (db, query, -1, NULL, NULL);
- /* Add trailing NULL */
- g_ptr_array_add (columns, NULL);
-
- if (!tracker_fts_create_table (db, tmp_name, (gchar **) columns->pdata)) {
- g_ptr_array_free (columns, TRUE);
+ if (!tracker_fts_create_table (db, tmp_name, tables, grouped_columns)) {
g_free (tmp_name);
g_free (query);
return FALSE;
}
- /* Dump all content from one table to another */
- g_ptr_array_free (columns, TRUE);
- rc = sqlite3_exec(db, query, NULL, 0, NULL);
+ query = g_strdup_printf ("INSERT INTO %s (docid) SELECT docid FROM %s",
+ tmp_name, table_name);
+ rc = sqlite3_prepare_v2 (db, query, -1, NULL, NULL);
g_free (query);
if (rc != SQLITE_OK) {
- query = g_strdup_printf ("DROP TABLE %s", tmp_name);
- rc = sqlite3_exec(db, query, NULL, 0, NULL);
- g_free (query);
g_free (tmp_name);
return FALSE;
}
- /* Drop the old table */
- query = g_strdup_printf ("DROP TABLE %s", table_name);
- rc = sqlite3_exec(db, query, NULL, 0, NULL);
+ query = g_strdup_printf ("INSERT INTO %s(%s) VALUES('rebuild')",
+ tmp_name, tmp_name);
+ rc = sqlite3_prepare_v2 (db, query, -1, NULL, NULL);
g_free (query);
if (rc != SQLITE_OK) {
- /* FIXME: How can we leave such state? this is rather fatal */
g_free (tmp_name);
return FALSE;
}
- /* And rename the previous one */
query = g_strdup_printf ("ALTER TABLE %s RENAME TO %s",
tmp_name, table_name);
- rc = sqlite3_exec(db, query, NULL, 0, NULL);
+ rc = sqlite3_prepare_v2 (db, query, -1, NULL, NULL);
g_free (query);
g_free (tmp_name);
+
if (rc != SQLITE_OK) {
+ g_free (tmp_name);
return FALSE;
}
diff --git a/src/libtracker-fts/tracker-fts.h b/src/libtracker-fts/tracker-fts.h
index 487a6ff..c020af2 100644
--- a/src/libtracker-fts/tracker-fts.h
+++ b/src/libtracker-fts/tracker-fts.h
@@ -23,19 +23,20 @@
#define __TRACKER_FTS_H__
#include <sqlite3.h>
-#include <glib.h>
+#include <glib-object.h>
G_BEGIN_DECLS
gboolean tracker_fts_init (void);
gboolean tracker_fts_init_db (sqlite3 *db);
gboolean tracker_fts_create_table (sqlite3 *db,
- gchar *table_name,
- gchar **column_names);
+ gchar *table_name,
+ GHashTable *tables,
+ GHashTable *grouped_columns);
gboolean tracker_fts_alter_table (sqlite3 *db,
- gchar *table_name,
- gchar **added_columns,
- gchar **removed_columns);
+ gchar *table_name,
+ GHashTable *tables,
+ GHashTable *grouped_columns);
G_END_DECLS
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]