[evolution-data-server/sqlite-refactor] Experimental work in progress
- From: Tristan Van Berkom <tvb src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-data-server/sqlite-refactor] Experimental work in progress
- Date: Sat, 30 Nov 2013 14:45:15 +0000 (UTC)
commit c113da749d092108436e0f86526d026e838cc898
Author: Tristan Van Berkom <tristanvb openismus com>
Date: Sat Nov 30 23:44:33 2013 +0900
Experimental work in progress
addressbook/libedata-book/e-book-sqlite.c | 619 +++++------------------------
1 files changed, 91 insertions(+), 528 deletions(-)
---
diff --git a/addressbook/libedata-book/e-book-sqlite.c b/addressbook/libedata-book/e-book-sqlite.c
index 2da05e4..56f33cc 100644
--- a/addressbook/libedata-book/e-book-sqlite.c
+++ b/addressbook/libedata-book/e-book-sqlite.c
@@ -36,9 +36,6 @@
#include "e-book-backend-sexp.h"
-
-
-
/******************************************************
* Debugging Macros *
******************************************************
@@ -220,10 +217,17 @@ ebsql_init_debug (void)
#define FOLDER_VERSION 8
#define INSERT_MULTI_STMT_BYTES 128
#define COLUMN_DEFINITION_BYTES 32
-#define GENERATED_QUERY_BYTES 2048
+#define GENERATED_QUERY_BYTES 1024
#define DEFAULT_FOLDER_ID "folder_id"
+/* We use a 64 bitmask to track which auxiliary tables
+ * are needed to satisfy a query, it's doubtful that
+ * anyone will need an addressbook with 64 fields configured
+ * in the summary.
+ */
+#define EBSQL_MAX_SUMMARY_FIELDS 64
+
/* The number of SQLite virtual machine instructions that are
* evaluated at a time, the user passed GCancellable is
* checked between each batch of evaluated instructions.
@@ -569,17 +573,30 @@ summary_fields_add_indexes (GArray *array,
}
}
-static SummaryField *
-summary_field_get (EBookSqlite *ebsql,
- EContactField field_id)
+static inline gint
+summary_field_get_index (EBookSqlite *ebsql,
+ EContactField field_id)
{
gint i;
for (i = 0; i < ebsql->priv->n_summary_fields; i++) {
if (ebsql->priv->summary_fields[i].field_id == field_id)
- return &(ebsql->priv->summary_fields[i]);
+ return i;
}
+ return -1;
+}
+
+static inline SummaryField *
+summary_field_get (EBookSqlite *ebsql,
+ EContactField field_id)
+{
+ gint index;
+
+ index = summary_field_get_index (ebsql, field_id);
+ if (index >= 0)
+ return &(ebsql->priv->summary_fields[index]);
+
return NULL;
}
@@ -2365,6 +2382,22 @@ ebsql_init_aux_tables (EBookSqlite *ebsql,
field->aux_table, ebsql->priv->folderid);
g_string_free (string, TRUE);
+
+ if (success) {
+ /* Create an index on the 'uid' column, this speeds up inserts on large
+ * addressbooks, because rows need to be deleted by UID before reinserting
+ */
+ tmp = g_strconcat ("UIDINDEX",
+ "_", field->dbname,
+ "_", ebsql->priv->folderid,
+ NULL);
+ ebsql_exec_printf (ebsql,
+ "CREATE INDEX IF NOT EXISTS %Q ON %Q (%s)",
+ NULL, NULL, NULL, error,
+ tmp, field->aux_table, "uid");
+ g_free (tmp);
+ }
+
/* Add indexes to columns in this auxiliary table
*/
for (l = aux_columns; success && l; l = l->next) {
@@ -3605,9 +3638,6 @@ typedef struct {
SummaryField *field; /* The summary field for 'field' */
gchar *value; /* The value to compare with */
- /* For preflighting without collecting strings */
- guint has_value : 1;
- guint has_extra : 1;
} QueryFieldTest;
typedef struct {
@@ -3617,8 +3647,6 @@ typedef struct {
EContactField field_id; /* The EContactField to compare */
SummaryField *field; /* The summary field for 'field' */
gchar *value; /* The value to compare with */
- guint has_value : 1;
- guint has_extra : 1;
/* Extension */
gchar *region; /* Region code from the query input */
@@ -3636,20 +3664,14 @@ typedef enum {
PREFLIGHT_UNSUPPORTED,
} PreflightStatus;
-typedef struct {
- EContactField field_id; /* multi string field id */
- GPtrArray *constraints; /* segmented query, if applicable */
-} PreflightAuxData;
-
/* Stack initializer for the PreflightContext struct below */
-#define PREFLIGHT_CONTEXT_INIT { PREFLIGHT_OK, NULL, FALSE, NULL }
+#define PREFLIGHT_CONTEXT_INIT { PREFLIGHT_OK, NULL, FALSE, 0 }
typedef struct {
- PreflightStatus status; /* result status */
- GPtrArray *constraints; /* main query */
- gboolean list_all; /* TRUE if all results should be returned */
-
- GSList *aux_fields; /* List of PreflightAuxData */
+ PreflightStatus status; /* result status */
+ GPtrArray *constraints; /* main query */
+ gboolean list_all; /* TRUE if all results should be returned */
+ guint64 aux_mask; /* Bitmask of which auxiliary tables are needed in the query */
} PreflightContext;
static QueryElement *
@@ -3681,8 +3703,6 @@ query_field_test_new (guint query,
/* Instead of g_slice_new0, NULL them out manually */
test->field = NULL;
test->value = NULL;
- test->has_value = FALSE;
- test->has_extra = FALSE;
return test;
}
@@ -3702,8 +3722,6 @@ query_phone_test_new (guint query,
/* Instead of g_slice_new0, NULL them out manually */
test->field = NULL;
test->value = NULL;
- test->has_value = FALSE;
- test->has_extra = FALSE;
/* Extra QueryPhoneTest fields */
test->region = NULL;
@@ -3801,67 +3819,10 @@ constraints_insert_field_test (GPtrArray *array,
test = query_field_test_new (query, field->field_id);
test->field = field;
test->value = g_strdup (value);
- test->has_value = (value && value[0]);
constraints_insert (array, idx, test);
}
-static PreflightAuxData *
-preflight_aux_data_new (EContactField field_id)
-{
- PreflightAuxData *aux_data = g_slice_new (PreflightAuxData);
-
- aux_data->field_id = field_id;
- aux_data->constraints = NULL;
-
- return aux_data;
-}
-
-static void
-preflight_aux_data_free (PreflightAuxData *aux_data)
-{
- if (aux_data) {
- if (aux_data->constraints)
- g_ptr_array_free (aux_data->constraints, TRUE);
-
- g_slice_free (PreflightAuxData, aux_data);
- }
-}
-
-static gint
-preflight_aux_data_find (PreflightAuxData *aux_data,
- gpointer data)
-{
- EContactField field_id = GPOINTER_TO_UINT (data);
-
- /* Unsigned comparison, just to be safe,
- * let's not return something like:
- * 'aux_data->field_id - field_id'
- */
- if (aux_data->field_id > field_id)
- return 1;
- else if (aux_data->field_id < field_id)
- return -1;
-
- return 0;
-}
-
-static PreflightAuxData *
-preflight_context_search_aux (PreflightContext *context,
- EContactField field_id)
-{
- PreflightAuxData *aux_data = NULL;
- GSList *link;
-
- link = g_slist_find_custom (context->aux_fields,
- GUINT_TO_POINTER (field_id),
- (GCompareFunc)preflight_aux_data_find);
- if (link)
- aux_data = link->data;
-
- return aux_data;
-}
-
static void
preflight_context_clear (PreflightContext *context)
{
@@ -3869,11 +3830,6 @@ preflight_context_clear (PreflightContext *context)
/* Free any allocated data, but leave the context values in place */
if (context->constraints)
g_ptr_array_free (context->constraints, TRUE);
-
- g_slist_free_full (context->aux_fields,
- (GDestroyNotify)preflight_aux_data_free);
-
- context->aux_fields = NULL;
context->constraints = NULL;
}
}
@@ -4121,21 +4077,17 @@ func_check (struct _ESExp *f,
QueryPhoneTest *test;
/* Collect data from this field test */
- test = query_phone_test_new (query_type, field);
- test->has_value = (query_value && query_value[0]);
- test->has_extra = (query_extra && query_extra[0]);
- test->value = g_strdup (query_value);
- test->region = g_strdup (query_extra);
+ test = query_phone_test_new (query_type, field);
+ test->value = g_strdup (query_value);
+ test->region = g_strdup (query_extra);
element = (QueryElement *)test;
} else {
QueryFieldTest *test;
/* Collect data from this field test */
- test = query_field_test_new (query_type, field);
- test->has_value = (query_value && query_value[0]);
- test->has_extra = (query_extra && query_extra[0]);
- test->value = g_strdup (query_value);
+ test = query_field_test_new (query_type, field);
+ test->value = g_strdup (query_value);
element = (QueryElement *)test;
}
@@ -4150,8 +4102,10 @@ func_check (struct _ESExp *f,
return result;
}
-/* Initial stage of preflighting, parse the search
- * expression and generate our array of QueryElements
+/* Initial stage of preflighting:
+ *
+ * o Parse the search expression and generate our array of QueryElements
+ * o Collect lengths of query terms
*/
static void
query_preflight_initialize (PreflightContext *context,
@@ -4234,9 +4188,10 @@ check_has_attr_list_cb (QueryElement *element,
return (data->has_attr_list == FALSE);
}
-/* This pass resolves values on the QueryElements useful
- * for actually performing the query, furthermore it resolves
- * whether the query can be performed on the SQLite tables or not.
+/* What is done in this pass:
+ * o Viability of the query is analyzed, i.e. can it be done with the summary columns.
+ * o Phone numbers are parsed and loaded onto QueryPhoneTests
+ * o Bitmask of auxiliary tables is collected
*/
static void
query_preflight_check (PreflightContext *context,
@@ -4254,9 +4209,7 @@ query_preflight_check (PreflightContext *context,
QueryFieldTest *test;
guint field_test;
- /* We don't care about the subquery delimiters at this point */
if (elements[i]->query >= BOOK_QUERY_SUB_FIRST) {
-
/* It's too complicated to properly perform
* the unary NOT operator on a constraint which
* accesses attribute lists.
@@ -4312,7 +4265,7 @@ query_preflight_check (PreflightContext *context,
* This is only true if the 'any field contains' query is
* the only test in the constraints, however.
*/
- if (!test->has_value && n_elements == 1) {
+ if (!test->value && n_elements == 1) {
context->list_all = TRUE;
@@ -4343,7 +4296,6 @@ query_preflight_check (PreflightContext *context,
* or fields which hold multiple strings
*/
if (test->field) {
-
if (test->field->type != G_TYPE_STRING &&
test->field->type != E_TYPE_CONTACT_ATTR_LIST)
context->status = MAX (context->status, PREFLIGHT_INVALID);
@@ -4403,25 +4355,15 @@ query_preflight_check (PreflightContext *context,
if (test->field &&
test->field->type == E_TYPE_CONTACT_ATTR_LIST) {
+ gint aux_index = summary_field_get_index (ebsql, test->field_id);
- PreflightAuxData *aux_data;
-
- aux_data = preflight_context_search_aux (context, test->field_id);
- if (!aux_data) {
- aux_data = preflight_aux_data_new (test->field_id);
- context->aux_fields =
- g_slist_prepend (context->aux_fields, aux_data);
- }
+ /* It's really improbable that we ever get 64 fields in the summary
+ * In any case we warn about this in e_book_sqlite_new_full().
+ */
+ g_warn_if_fail (aux_index < EBSQL_MAX_SUMMARY_FIELDS);
+ context->aux_mask |= (1 << aux_index);
}
}
-
- /* If we cannot satisfy this query with the summary, there is no point
- * to return the allocated list */
- if (context->status > PREFLIGHT_OK) {
- g_slist_free_full (context->aux_fields,
- (GDestroyNotify)preflight_aux_data_free);
- context->aux_fields = NULL;
- }
}
/* Handle special case of E_CONTACT_FULL_NAME
@@ -4485,382 +4427,6 @@ query_preflight_substitute_full_name (PreflightContext *context,
}
}
-/* Migrates the chunk of the constraints at 'offset' into one of the
- * PreflightAuxData indicated by aux_field.
- *
- * Returns the number of QueryElements which have been removed from
- * the main constraints
- */
-static gint
-query_preflight_migrate_offset (PreflightContext *context,
- EContactField aux_field,
- gint offset)
-{
- PreflightAuxData *aux_data;
- QueryElement **elements;
- gint sub_counter = 0;
- gint dest_offset = 0;
- gint n_migrated = 0;
-
- aux_data = preflight_context_search_aux (context, aux_field);
- g_return_val_if_fail (aux_data != NULL, 0);
-
- if (!aux_data->constraints) {
-
- /* We created a new batch for 'aux_field',
- * we'll be adding this batch directly to the beginning
- */
- aux_data->constraints = g_ptr_array_new_with_free_func ((GDestroyNotify)query_element_free);
-
- } else {
- elements = (QueryElement **)aux_data->constraints->pdata;
-
- /* If we're migrating a second or third constraint, we must ensure that
- * it's encapsulated with an AND
- */
- if (elements[0]->query != BOOK_QUERY_SUB_AND) {
- constraints_insert_delimiter (aux_data->constraints, 0, BOOK_QUERY_SUB_AND);
- constraints_insert_delimiter (aux_data->constraints, -1, BOOK_QUERY_SUB_END);
- }
-
- /* We're going to insert this starting at position 1 (directly after opening the AND)
- * The order of AND statements in the query is inconsequential.
- */
- dest_offset = 1;
- }
-
- elements = (QueryElement **)context->constraints->pdata;
- do {
- QueryElement *element;
-
- /* Migrate one element */
- element = constraints_take (context->constraints, offset);
- constraints_insert (aux_data->constraints, dest_offset++, element);
-
- n_migrated++;
-
- /* If we migrated a group... migrate the whole group */
- if (element->query == BOOK_QUERY_SUB_END)
- sub_counter--;
- else if (element->query >= BOOK_QUERY_SUB_FIRST)
- sub_counter++;
-
- } while (context->constraints->len > offset && sub_counter > 0);
-
-
- /* Return the number of elements removed from the main constraints */
- return n_migrated;
-}
-
-/* Will set the EContactField to 0 if it's completely isolated
- * to the summary table, E_CONTACT_FIELD_LAST if it's not isolated,
- * or another attribute list type EContactField if it's isolated
- * to that field.
- *
- * Expects the initial value to be 'E_CONTACT_FIELD_LAST + 1'
- */
-static gboolean
-check_isolated_cb (QueryElement *element,
- gint sub_level,
- gint offset,
- gpointer user_data)
-{
- EContactField *field_id = (EContactField *)user_data;
- QueryFieldTest *test = (QueryFieldTest *)element;
-
- if (*field_id > E_CONTACT_FIELD_LAST) {
-
- /* First field encountered, let's see what it is... */
- if (test->field->type == E_TYPE_CONTACT_ATTR_LIST)
- *field_id = test->field_id;
- else
- *field_id = 0;
-
- return TRUE;
-
- } else if (*field_id == 0) {
-
- if (test->field->type == E_TYPE_CONTACT_ATTR_LIST) {
-
- /* Oops, summary and auxiliary encountered */
- *field_id = E_CONTACT_FIELD_LAST;
- return FALSE;
- }
-
- } else if (test->field_id != *field_id) {
- /* Auxiliary and something else encountered */
- *field_id = E_CONTACT_FIELD_LAST;
- return FALSE;
- }
-
- return TRUE;
-}
-
-/* fetch_sub_groups_cb will list the index of each component of a sub,
- * unless not every subgroup was isolated, in which case the
- * PreflightAndData->isolated will be set to FALSE.
- */
-typedef struct {
- QueryElement **elements;
- gint n_elements;
-
- gboolean isolated;
- gboolean checked;
-
- GSList *offsets;
- GSList *fields;
-} PreflightAndData;
-
-static gboolean
-fetch_sub_groups_cb (QueryElement *element,
- gint sub_level,
- gint offset,
- gpointer user_data)
-{
- PreflightAndData *data = (PreflightAndData *)user_data;
-
- data->checked = TRUE;
-
- if (sub_level == 1 && element->query < BOOK_QUERY_SUB_FIRST) {
-
- QueryFieldTest *test = (QueryFieldTest *)element;
-
- data->offsets =
- g_slist_prepend (data->offsets,
- GINT_TO_POINTER (offset));
- data->fields =
- g_slist_prepend (data->fields,
- GUINT_TO_POINTER (test->field_id));
-
- } else if (sub_level == 2 &&
- element->query >= BOOK_QUERY_SUB_FIRST &&
- element->query != BOOK_QUERY_SUB_END) {
-
- EContactField field_id = E_CONTACT_FIELD_LAST + 1;
-
- query_preflight_foreach_sub (data->elements,
- data->n_elements,
- offset, FALSE,
- check_isolated_cb,
- &field_id);
-
- if (field_id == E_CONTACT_FIELD_LAST) {
- data->isolated = FALSE;
- } else {
- data->offsets =
- g_slist_prepend (data->offsets,
- GINT_TO_POINTER (offset));
- data->fields =
- g_slist_prepend (data->fields,
- GUINT_TO_POINTER (field_id));
- }
- }
-
- return (data->isolated != FALSE);
-}
-
-static void
-query_preflight_optimize_and (PreflightContext *context,
- EBookSqlite *ebsql,
- QueryElement **elements,
- gint n_elements)
-{
- PreflightAndData data = { elements, n_elements, TRUE, FALSE, NULL, NULL };
-
- /* First, find the indexes to the various toplevel elements */
- query_preflight_foreach_sub (elements,
- n_elements,
- 0, TRUE,
- fetch_sub_groups_cb,
- &data);
-
- if (data.checked && data.isolated) {
- GSList *l, *ll;
- gint n_migrated = 0;
- gint remaining;
-
- /* Lists are created in reverse, with higher offsets
- * comming first, let's keep it this way.
- *
- * We can now migrate them one by one and the later
- * offsets (i.e. lower offsets) in the list will not
- * be invalid. This order should also reduce calls
- * to memmove().
- */
- for (l = data.offsets, ll = data.fields;
- l && ll;
- l = l->next, ll = ll->next) {
- gint offset = GPOINTER_TO_INT (l->data);
- EContactField field_id = GPOINTER_TO_UINT (ll->data);
- SummaryField *field;
-
- field = summary_field_get (ebsql, field_id);
-
- if (field && field->type == E_TYPE_CONTACT_ATTR_LIST) {
- n_migrated++;
- query_preflight_migrate_offset (context, field_id, offset);
- }
- }
-
- /* If there is only one statement left inside the AND clause
- * in context->constraints, we need to remove the encapsulating
- * AND statement.
- */
- remaining = g_slist_length (data.offsets) - n_migrated;
- if (remaining < 2) {
- g_ptr_array_remove_index (context->constraints, 0);
- g_ptr_array_remove_index (context->constraints,
- context->constraints->len - 1);
- }
- }
-
- g_slist_free (data.offsets);
- g_slist_free (data.fields);
-}
-
-static void
-query_preflight_optimize_toplevel (PreflightContext *context,
- EBookSqlite *ebsql,
- QueryElement **elements,
- gint n_elements)
-{
- EContactField field_id;
-
- if (elements[0]->query >= BOOK_QUERY_SUB_FIRST) {
-
- switch (elements[0]->query) {
- case BOOK_QUERY_SUB_AND:
-
- /* AND components at the toplevel can be migrated, so long
- * as each component is isolated
- */
- query_preflight_optimize_and (context, ebsql, elements, n_elements);
- break;
-
- case BOOK_QUERY_SUB_OR:
-
- /* OR at the toplevel can be migrated if limited to one table */
- field_id = E_CONTACT_FIELD_LAST + 1;
- query_preflight_foreach_sub (elements,
- n_elements,
- 0, FALSE,
- check_isolated_cb,
- &field_id);
-
- if (field_id != 0 &&
- field_id != E_CONTACT_FIELD_LAST) {
-
- /* Isolated to an auxiliary table, let's migrate it */
- query_preflight_migrate_offset (context, field_id, 0);
- }
- break;
-
- case BOOK_QUERY_SUB_NOT:
-
- /* We dont support NOT operations on attribute lists as
- * summarized queries, so there can not be any optimization
- * made here.
- */
- break;
-
- case BOOK_QUERY_SUB_END:
- default:
- g_warn_if_reached ();
- break;
- }
-
- } else {
-
- QueryFieldTest *test = (QueryFieldTest *)elements[0];
-
- /* Toplevel field test should stand alone at the first position */
-
- /* Special case of 'x-evolution-any-field' will have no SummaryField
- * resolved in test->field
- */
- if (test->field && test->field->type == E_TYPE_CONTACT_ATTR_LIST)
- query_preflight_migrate_offset (context, test->field_id, 0);
- }
-}
-
-/* In this phase, we attempt to pull out field tests from the main constraints array
- * which touch auxiliary tables and place them instead into their PreflightAuxData
- * constraint arrays respectively.
- *
- * This will result in queries being generated using nested select statements before joining,
- * allowing us to leverage the indexes we created in those.
- *
- * A query which would normally generate like this:
- * ================================================
- * SELECT DISTINCT summary.uid, summary.vcard FROM 'folder_id' AS summary
- * LEFT OUTER JOIN 'folder_id_phone_list' AS phone_list ON phone_list.uid = summary.uid
- * LEFT OUTER JOIN 'folder_id_email_list' AS email_list ON email_list.uid = summary.uid
- * WHERE (phone_list.value_reverse IS NOT NULL AND phone_list.value_reverse LIKE '0505%')
- * AND (email_list.value IS NOT NULL AND email_list.value LIKE 'eddie%')
- *
- * After optimization, will be generated instead like so:
- * ================================================
- * SELECT DISTINCT summary.uid, summary.vcard FROM (
- * SELECT DISTINCT phone_list.uid FROM 'folder_id_phone_list' AS phone_list
- * WHERE (phone_list.value_reverse IS NOT NULL AND phone_list.value_reverse LIKE '0505%')
- * ) AS phone_list_results
- * LEFT OUTER JOIN (
- * SELECT DISTINCT email_list.uid FROM 'folder_id_email_list' AS email_list
- * WHERE (email_list.value IS NOT NULL AND email_list.value LIKE 'eddie%')
- * ) AS email_list_results ON email_list_results.uid = phone_list_results.uid
- * LEFT OUTER JOIN 'folder_id' AS summary ON summary.uid = email_list_results.uid
- * WHERE summary.uid IS NOT NULL
- *
- * Currently we make the following assumptions when optimizing the query:
- *
- * o Any shallow query with only one auxiliary table constraint can have
- * the auxiliary constraint migrated into the nested select
- *
- * o Any grouped query which contains constraints which access the same
- * table can be considered an atomic constraint and is a suitable target
- * for migration.
- *
- * o Any toplevel AND query which contains one or more summary table constraints
- * and one or more auxiliary table constraints, can have the auxiliary
- * table constraints migrated into the nested select.
- *
- */
-static void
-query_preflight_optimize (PreflightContext *context,
- EBookSqlite *ebsql)
-{
- QueryElement **elements;
- gint n_elements;
-
- if (context->constraints &&
- context->constraints->len > 0) {
-
- elements = (QueryElement **)context->constraints->pdata;
- n_elements = context->constraints->len;
-
- query_preflight_optimize_toplevel (context, ebsql, elements, n_elements);
- }
-
-
- /* In any case that we did have constraints, add an (exists "uid") constraint
- * to the end, this is because it's possible for the optimization above to return
- * some NULL rows
- */
- if (context->constraints &&
- context->constraints->len == 0) {
- constraints_insert_field_test (context->constraints, 0,
- summary_field_get (ebsql, E_CONTACT_UID),
- BOOK_QUERY_EXISTS, NULL);
- } else {
- /* AND it with the remaining constraints */
- constraints_insert_delimiter (context->constraints, 0, BOOK_QUERY_SUB_AND);
- constraints_insert_field_test (context->constraints, -1,
- summary_field_get (ebsql, E_CONTACT_UID),
- BOOK_QUERY_EXISTS, NULL);
- constraints_insert_delimiter (context->constraints, -1, BOOK_QUERY_SUB_END);
- }
-}
-
static void
query_preflight (PreflightContext *context,
EBookSqlite *ebsql,
@@ -4881,8 +4447,6 @@ query_preflight (PreflightContext *context,
/* Handle E_CONTACT_FULL_NAME substitutions */
query_preflight_substitute_full_name (context, ebsql);
- /* Optimize queries which touch auxiliary columns */
- /* query_preflight_optimize (context, ebsql); */
} else {
/* We might use this context to perform a fallback query,
@@ -5354,18 +4918,9 @@ ebsql_generate_constraints (EBookSqlite *ebsql,
sub_query_context_free (ctx);
}
-
-/* SELECT DISTINCT summary.uid, summary.vcard FROM folder_id AS summary */
-/* JOIN folder_id_phone_list AS phone_list */
-/* ON phone_list.uid = summary.uid */
-/* JOIN folder_id_email_list AS email_list */
-/* ON email_list.uid = summary.uid */
-/* WHERE phone_list.value LIKE '%0505' AND email_list.value LIKE 'eddie%'; */
-
-
-/* Generates the SELECT portion of the query, this will possibly add some
- * of the constraints into nested selects. Constraints that could not be
- * nested will have their symbolic table names in context.
+/* Generates the SELECT portion of the query, this will take care of
+ * preparing the context of the query, and add the needed JOIN statements
+ * based on which fields are referenced in the query expression.
*
* This also handles getting the correct callback and asking for the
* right data depending on the 'search_type'
@@ -5377,11 +4932,12 @@ ebsql_generate_select (EBookSqlite *ebsql,
PreflightContext *context,
GError **error)
{
- GSList *l;
EbSqlRowFunc callback = NULL;
+ gint i;
g_string_append (string, "SELECT ");
- if (context->aux_fields)
+ if (context->status == PREFLIGHT_OK &&
+ context->aux_mask != 0)
g_string_append (string, "DISTINCT ");
switch (search_type) {
@@ -5401,7 +4957,7 @@ ebsql_generate_select (EBookSqlite *ebsql,
break;
case SEARCH_COUNT:
callback = get_count_cb;
- if (context->aux_fields)
+ if (context->aux_mask != 0)
g_string_append (string, "count (DISTINCT summary.uid) ");
else
g_string_append (string, "count (*) ");
@@ -5410,16 +4966,20 @@ ebsql_generate_select (EBookSqlite *ebsql,
ebsql_string_append_printf (string, "FROM %Q AS summary", ebsql->priv->folderid);
- for (l = context->aux_fields; l; l = l->next) {
- PreflightAuxData *aux_data = (PreflightAuxData *)l->data;
- SummaryField *field = summary_field_get (ebsql, aux_data->field_id);
+ /* Add any required auxiliary tables into the query context */
+ if (context->status == PREFLIGHT_OK) {
+ for (i = 0; i < ebsql->priv->n_summary_fields; i++) {
+ /* We cap this at EBSQL_MAX_SUMMARY_FIELDS (64 bits) at creation time */
+ if ((context->aux_mask & (1 << i)) != 0) {
+ SummaryField *field = &(ebsql->priv->summary_fields[i]);
- /* Join the table in the normal way and leave the constraints for later */
- ebsql_string_append_printf (string, " JOIN %Q AS %s ON %s.uid = summary.uid",
- field->aux_table,
- field->aux_table_symbolic,
- field->aux_table_symbolic);
+ ebsql_string_append_printf (string, " JOIN %Q AS %s ON %s.uid = summary.uid",
+ field->aux_table,
+ field->aux_table_symbolic,
+ field->aux_table_symbolic);
+ }
+ }
}
return callback;
@@ -5713,8 +5273,6 @@ ebsql_cursor_setup_query (EBookSqlite *ebsql,
cursor->sexp = NULL;
} else {
/* Generate the constraints for our queries
- *
- * It can be that they are optimized into the select segment
*/
string = g_string_new (NULL);
ebsql_generate_constraints (ebsql,
@@ -6285,7 +5843,12 @@ e_book_sqlite_new_full (const gchar *path,
indexed_fields = e_source_backend_summary_setup_get_indexed_fields (setup, &index_types,
&n_indexed_fields);
/* No specified summary fields indicates the default summary configuration should be used */
- if (n_fields <= 0) {
+ if (n_fields <= 0 || n_fields >= EBSQL_MAX_SUMMARY_FIELDS) {
+
+ if (n_fields)
+ g_warning ("EBookSqlite refused to create addressbook with over %d summary fields",
+ EBSQL_MAX_SUMMARY_FIELDS);
+
ebsql = ebsql_new_default (path,
vcard_callback,
change_callback,
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]