1 /*-*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /* e-book-sqlite.c
3 *
4 * Copyright (C) 2013 Intel Corporation
5 *
6 * This library is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License as published by
8 * the Free Software Foundation.
9 *
10 * This library is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
13 * for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this library. If not, see <http://www.gnu.org/licenses/>.
17 *
18 * Authors: Tristan Van Berkom <tristanvb@openismus.com>
19 */
20
21 /**
22 * SECTION: e-book-sqlite
23 * @include: libedata-book/libedata-book.h
24 * @short_description: An SQLite storage facility for addressbooks
25 *
26 * The #EBookSqlite is an API for storing and looking up #EContact(s)
27 * in an SQLite database. It also supports a lean index mode via
28 * the #EbSqlVCardCallback, if you are in a situation where it is
29 * not convenient to store the vCards directly in the SQLite. It is
30 * however recommended to avoid storing contacts in separate storage
31 * if at all possible, as this will decrease performance of searches
32 * an also contribute to flash wear.
33 *
34 * The API is thread safe, with special considerations to be made
35 * around e_book_sqlite_lock() and e_book_sqlite_unlock() for
36 * the sake of isolating transactions across threads.
37 *
38 * Any operations which can take a lot of time to complete (depending
39 * on the size of your addressbook) can be cancelled using a #GCancellable.
40 *
41 * Depending on your summary configuration, your mileage will vary. Refer
42 * to the #ESourceBackendSummarySetup for configuring your addressbook
43 * for the type of usage you mean to make of it.
44 */
45
46 #include "e-book-sqlite.h"
47
48 #include <locale.h>
49 #include <string.h>
50 #include <errno.h>
51
52 #include <glib/gi18n.h>
53 #include <glib/gstdio.h>
54
55 #include <sqlite3.h>
56
57 /* For e_sqlite3_vfs_init() */
58 #include <libebackend/libebackend.h>
59
60 #include "e-book-backend-sexp.h"
61
62 /******************************************************
63 * Debugging Macros *
64 ******************************************************
65 * Run EDS with EBSQL_DEBUG=statements:explain to print
66 * all statements and explain query plans.
67 *
68 * Use any of the values below to select which debug
69 * to enable.
70 */
71 #define EBSQL_ENV_DEBUG "EBSQL_DEBUG"
72
73 typedef enum {
74 EBSQL_DEBUG_STATEMENTS = 1 << 0, /* Output all executed statements */
75 EBSQL_DEBUG_EXPLAIN = 1 << 1, /* Output SQLite's query plan for SELECT statements */
76 EBSQL_DEBUG_LOCKS = 1 << 2, /* Print which function locks and unlocks the mutex */
77 EBSQL_DEBUG_ERRORS = 1 << 3, /* Print all errors which are set */
78 EBSQL_DEBUG_SCHEMA = 1 << 4, /* Debugging the schema building / upgrading */
79 EBSQL_DEBUG_INSERT = 1 << 5, /* Debugging contact insertions */
80 EBSQL_DEBUG_FETCH_VCARD = 1 << 6, /* Print invocations of the EbSqlVCardCallback fallback */
81 EBSQL_DEBUG_CURSOR = 1 << 7, /* Print information about EbSqlCursor operations */
82 EBSQL_DEBUG_CONVERT_E164 = 1 << 8, /* Print information e164 phone number conversions in vcards */
83 EBSQL_DEBUG_REF_COUNTS = 1 << 9, /* Print about shared EBookSqlite instances, print when finalized */
84 EBSQL_DEBUG_CANCEL = 1 << 10, /* Print information about GCancellable cancellations */
85 EBSQL_DEBUG_PREFLIGHT = 1 << 11, /* Print information about query preflighting */
86 EBSQL_DEBUG_TIMING = 1 << 12, /* Print information about timing */
87 } EbSqlDebugFlag;
88
89 static const GDebugKey ebsql_debug_keys[] = {
90 { "statements", EBSQL_DEBUG_STATEMENTS },
91 { "explain", EBSQL_DEBUG_EXPLAIN },
92 { "locks", EBSQL_DEBUG_LOCKS },
93 { "errors", EBSQL_DEBUG_ERRORS },
94 { "schema", EBSQL_DEBUG_SCHEMA },
95 { "insert", EBSQL_DEBUG_INSERT },
96 { "fetch-vcard", EBSQL_DEBUG_FETCH_VCARD },
97 { "cursor", EBSQL_DEBUG_CURSOR },
98 { "e164", EBSQL_DEBUG_CONVERT_E164 },
99 { "ref-counts", EBSQL_DEBUG_REF_COUNTS },
100 { "cancel", EBSQL_DEBUG_CANCEL },
101 { "preflight", EBSQL_DEBUG_PREFLIGHT },
102 { "timing", EBSQL_DEBUG_TIMING },
103 };
104
105 static EbSqlDebugFlag ebsql_debug_flags = 0;
106
107 static void
ebsql_init_debug(void)108 ebsql_init_debug (void)
109 {
110 static gboolean initialized = FALSE;
111
112 if (G_UNLIKELY (!initialized)) {
113 const gchar *env_string;
114
115 env_string = g_getenv (EBSQL_ENV_DEBUG);
116
117 if (env_string != NULL)
118 ebsql_debug_flags =
119 g_parse_debug_string (
120 env_string,
121 ebsql_debug_keys,
122 G_N_ELEMENTS (ebsql_debug_keys));
123 }
124 }
125
126 static const gchar *
ebsql_error_str(EBookSqliteError code)127 ebsql_error_str (EBookSqliteError code)
128 {
129 switch (code) {
130 case E_BOOK_SQLITE_ERROR_ENGINE:
131 return "engine";
132 case E_BOOK_SQLITE_ERROR_CONSTRAINT:
133 return "constraint";
134 case E_BOOK_SQLITE_ERROR_CONTACT_NOT_FOUND:
135 return "contact not found";
136 case E_BOOK_SQLITE_ERROR_INVALID_QUERY:
137 return "invalid query";
138 case E_BOOK_SQLITE_ERROR_UNSUPPORTED_QUERY:
139 return "unsupported query";
140 case E_BOOK_SQLITE_ERROR_UNSUPPORTED_FIELD:
141 return "unsupported field";
142 case E_BOOK_SQLITE_ERROR_END_OF_LIST:
143 return "end of list";
144 case E_BOOK_SQLITE_ERROR_LOAD:
145 return "load";
146 }
147
148 return "(unknown)";
149 }
150
151 static const gchar *
ebsql_origin_str(EbSqlCursorOrigin origin)152 ebsql_origin_str (EbSqlCursorOrigin origin)
153 {
154 switch (origin) {
155 case EBSQL_CURSOR_ORIGIN_CURRENT:
156 return "current";
157 case EBSQL_CURSOR_ORIGIN_BEGIN:
158 return "begin";
159 case EBSQL_CURSOR_ORIGIN_END:
160 return "end";
161 }
162
163 return "(invalid)";
164 }
165
166 #define EBSQL_NOTE(type,action) \
167 G_STMT_START { \
168 if (ebsql_debug_flags & EBSQL_DEBUG_##type) \
169 { action; }; \
170 } G_STMT_END
171
172 #define EBSQL_LOCK_MUTEX(mutex) \
173 G_STMT_START { \
174 if (ebsql_debug_flags & EBSQL_DEBUG_LOCKS) { \
175 g_printerr ("%s: Locking %s\n", G_STRFUNC, #mutex); \
176 g_mutex_lock (mutex); \
177 g_printerr ("%s: Locked %s\n", G_STRFUNC, #mutex); \
178 } else { \
179 g_mutex_lock (mutex); \
180 } \
181 } G_STMT_END
182
183 #define EBSQL_UNLOCK_MUTEX(mutex) \
184 G_STMT_START { \
185 if (ebsql_debug_flags & EBSQL_DEBUG_LOCKS) { \
186 g_printerr ("%s: Unlocking %s\n", G_STRFUNC, #mutex); \
187 g_mutex_unlock (mutex); \
188 g_printerr ("%s: Unlocked %s\n", G_STRFUNC, #mutex); \
189 } else { \
190 g_mutex_unlock (mutex); \
191 } \
192 } G_STMT_END
193
194 /* Format strings are passed through dgettext(), need to be reformatted */
195 #define EBSQL_SET_ERROR(error, code, fmt, args...) \
196 G_STMT_START { \
197 if (ebsql_debug_flags & EBSQL_DEBUG_ERRORS) { \
198 gchar *format = g_strdup_printf ( \
199 "ERR [%%s]: Set error code '%%s': %s\n", fmt); \
200 g_printerr (format, G_STRFUNC, \
201 ebsql_error_str (code), ## args); \
202 g_free (format); \
203 } \
204 g_set_error (error, E_BOOK_SQLITE_ERROR, code, fmt, ## args); \
205 } G_STMT_END
206
207 #define EBSQL_SET_ERROR_LITERAL(error, code, detail) \
208 G_STMT_START { \
209 if (ebsql_debug_flags & EBSQL_DEBUG_ERRORS) { \
210 g_printerr ("ERR [%s]: " \
211 "Set error code %s: %s\n", \
212 G_STRFUNC, \
213 ebsql_error_str (code), detail); \
214 } \
215 g_set_error_literal (error, E_BOOK_SQLITE_ERROR, code, detail); \
216 } G_STMT_END
217
218 /* EBSQL_LOCK_OR_RETURN:
219 * @ebsql: The #EBookSqlite
220 * @cancellable: A #GCancellable passed into an API
221 * @val: Value to return if this check fails
222 *
223 * This will first lock the mutex and then check if
224 * the passed cancellable is valid or invalid, it can
225 * be invalid if it differs from a cancellable passed
226 * to a toplevel transaction via e_book_sqlite_lock().
227 *
228 * If the check fails, the lock is released and then
229 * @val is returned.
230 */
231 #define EBSQL_LOCK_OR_RETURN(ebsql, cancellable, val) \
232 G_STMT_START { \
233 EBSQL_LOCK_MUTEX (&(ebsql)->priv->lock); \
234 if (cancellable != NULL && (ebsql)->priv->cancel && \
235 (ebsql)->priv->cancel != cancellable) { \
236 g_warning ("The GCancellable passed to `%s' " \
237 "is not the same as the cancel object " \
238 "passed to e_book_sqlite_lock()", \
239 G_STRFUNC); \
240 EBSQL_UNLOCK_MUTEX (&(ebsql)->priv->lock); \
241 return val; \
242 } \
243 } G_STMT_END
244
245 /* Set an error code from an sqlite_exec() or sqlite_step() return value & error message */
246 #define EBSQL_SET_ERROR_FROM_SQLITE(error, code, message) \
247 G_STMT_START { \
248 if (code == SQLITE_CONSTRAINT) { \
249 EBSQL_SET_ERROR_LITERAL (error, \
250 E_BOOK_SQLITE_ERROR_CONSTRAINT, \
251 errmsg); \
252 } else if (code == SQLITE_ABORT) { \
253 if (ebsql_debug_flags & EBSQL_DEBUG_ERRORS) { \
254 g_printerr ("ERR [%s]: Set cancelled error\n", \
255 G_STRFUNC); \
256 } \
257 g_set_error (error, \
258 G_IO_ERROR, \
259 G_IO_ERROR_CANCELLED, \
260 "Operation cancelled: %s", errmsg); \
261 } else { \
262 EBSQL_SET_ERROR (error, \
263 E_BOOK_SQLITE_ERROR_ENGINE, \
264 "SQLite error code `%d': %s", \
265 code, errmsg); \
266 } \
267 } G_STMT_END
268
269 #define FOLDER_VERSION 12
270 #define INSERT_MULTI_STMT_BYTES 128
271 #define COLUMN_DEFINITION_BYTES 32
272 #define GENERATED_QUERY_BYTES 1024
273
274 #define DEFAULT_FOLDER_ID "folder_id"
275
276 /* We use a 64 bitmask to track which auxiliary tables
277 * are needed to satisfy a query, it's doubtful that
278 * anyone will need an addressbook with 64 fields configured
279 * in the summary.
280 */
281 #define EBSQL_MAX_SUMMARY_FIELDS 64
282
283 /* The number of SQLite virtual machine instructions that are
284 * evaluated at a time, the user passed GCancellable is
285 * checked between each batch of evaluated instructions.
286 */
287 #define EBSQL_CANCEL_BATCH_SIZE 200
288
289 /* Number of contacts to relocalize at a time
290 * while relocalizing the whole database
291 */
292 #define EBSQL_UPGRADE_BATCH_SIZE 20
293
294 #define EBSQL_ESCAPE_SEQUENCE "ESCAPE '^'"
295
296 /* Names for custom functions */
297 #define EBSQL_FUNC_COMPARE_VCARD "compare_vcard"
298 #define EBSQL_FUNC_FETCH_VCARD "fetch_vcard"
299 #define EBSQL_FUNC_EQPHONE_EXACT "eqphone_exact"
300 #define EBSQL_FUNC_EQPHONE_NATIONAL "eqphone_national"
301 #define EBSQL_FUNC_EQPHONE_SHORT "eqphone_short"
302
303 /* Fallback collations are generated as with a prefix and an EContactField name */
304 #define EBSQL_COLLATE_PREFIX "ebsql_"
305
306 /* A special vcard attribute that we use only for private vcards */
307 #define EBSQL_VCARD_SORT_KEY "X-EVOLUTION-SORT-KEY"
308
309 /* Suffixes for column names used to store specialized data */
310 #define EBSQL_SUFFIX_REVERSE "reverse"
311 #define EBSQL_SUFFIX_SORT_KEY "localized"
312 #define EBSQL_SUFFIX_PHONE "phone"
313 #define EBSQL_SUFFIX_COUNTRY "country"
314
315 /* Track EBookIndexType's in a bit mask */
316 #define INDEX_FLAG(type) (1 << E_BOOK_INDEX_##type)
317
318 /* This macro is used to reffer to vcards in statements */
319 #define EBSQL_VCARD_FRAGMENT(ebsql) \
320 ((ebsql)->priv->vcard_callback ? \
321 EBSQL_FUNC_FETCH_VCARD " (summary.uid, summary.bdata)" : \
322 "summary.vcard")
323
324 /* Signatures for some of the SQLite callbacks which we pass around */
325 typedef void (*EbSqlCustomFunc) (sqlite3_context *context,
326 gint argc,
327 sqlite3_value **argv);
328 typedef gint (*EbSqlRowFunc) (gpointer ref,
329 gint n_cols,
330 gchar **cols,
331 gchar **names);
332
333 /* Some forward declarations */
334 static gboolean ebsql_init_statements (EBookSqlite *ebsql,
335 GError **error);
336 static gboolean ebsql_insert_contact (EBookSqlite *ebsql,
337 EbSqlChangeType change_type,
338 EContact *contact,
339 const gchar *original_vcard,
340 const gchar *extra,
341 gboolean replace,
342 GError **error);
343 static gboolean ebsql_exec (EBookSqlite *ebsql,
344 const gchar *stmt,
345 EbSqlRowFunc callback,
346 gpointer data,
347 GCancellable *cancellable,
348 GError **error);
349
350 typedef struct {
351 EContactField field_id; /* The EContact field */
352 GType type; /* The GType (only support string or gboolean) */
353 const gchar *dbname; /* The key for this field in the sqlite3 table */
354 gint index; /* Types of searches this field should support (see EBookIndexType) */
355 gchar *aux_table; /* Name of auxiliary table for this field, for multivalued fields only */
356 gchar *aux_table_symbolic; /* Symolic name of auxiliary table used in queries */
357 } SummaryField;
358
359 struct _EBookSqlitePrivate {
360
361 /* Parameters and settings */
362 gchar *path; /* Full file name of the file we're operating on (used for hash table entries) */
363 gchar *locale; /* The current locale */
364 gchar *region_code; /* Region code (for phone number parsing) */
365 gchar *folderid; /* The summary table name (configurable, for support of legacy
366 * databases created by EBookSqliteDB) */
367
368 EbSqlVCardCallback vcard_callback; /* User callback to fetch vcards instead of storing them */
369 EbSqlChangeCallback change_callback; /* User callback to catch change notifications */
370 gpointer user_data; /* Data & Destroy notifier for the above callbacks */
371 GDestroyNotify user_data_destroy;
372
373 /* Summary configuration */
374 SummaryField *summary_fields;
375 gint n_summary_fields;
376
377 GMutex lock; /* Main API lock */
378 GMutex updates_lock; /* Lock used for calls to e_book_sqlite_lock_updates () */
379 guint32 in_transaction; /* Nested transaction counter */
380 EbSqlLockType lock_type; /* The lock type acquired for the current transaction */
381 GCancellable *cancel; /* User passed GCancellable, we abort an operation if cancelled */
382
383 ECollator *collator; /* The ECollator to create sort keys for any sortable fields */
384
385 /* SQLite resources */
386 sqlite3 *db;
387 sqlite3_stmt *insert_stmt; /* Insert statement for main summary table */
388 sqlite3_stmt *replace_stmt; /* Replace statement for main summary table */
389 GHashTable *multi_deletes; /* Delete statement for each auxiliary table */
390 GHashTable *multi_inserts; /* Insert statement for each auxiliary table */
391
392 ESource *source;
393 };
394
395 enum {
396 BEFORE_INSERT_CONTACT,
397 BEFORE_REMOVE_CONTACT,
398 LAST_SIGNAL
399 };
400
401 static guint signals[LAST_SIGNAL];
402
403 G_DEFINE_TYPE_WITH_CODE (EBookSqlite, e_book_sqlite, G_TYPE_OBJECT,
404 G_ADD_PRIVATE (EBookSqlite)
405 G_IMPLEMENT_INTERFACE (E_TYPE_EXTENSIBLE, NULL))
406
407 G_DEFINE_QUARK (e-book-backend-sqlite-error-quark,
408 e_book_sqlite_error)
409
410 /* The ColumnInfo struct is used to constant data
411 * and dynamically allocated data, the 'type' and
412 * 'extra' members are however always constant.
413 */
414 typedef struct {
415 gchar *name;
416 const gchar *type;
417 const gchar *extra;
418 gchar *index;
419 } ColumnInfo;
420
421 static ColumnInfo main_table_columns[] = {
422 { (gchar *) "folder_id", "TEXT", "PRIMARY KEY", NULL },
423 { (gchar *) "version", "INTEGER", NULL, NULL },
424 { (gchar *) "multivalues", "TEXT", NULL, NULL },
425 { (gchar *) "lc_collate", "TEXT", NULL, NULL },
426 { (gchar *) "countrycode", "VARCHAR(2)", NULL, NULL },
427 };
428
429 /* Default summary configuration */
430 static EContactField default_summary_fields[] = {
431 E_CONTACT_UID,
432 E_CONTACT_REV,
433 E_CONTACT_FILE_AS,
434 E_CONTACT_NICKNAME,
435 E_CONTACT_FULL_NAME,
436 E_CONTACT_GIVEN_NAME,
437 E_CONTACT_FAMILY_NAME,
438 E_CONTACT_EMAIL,
439 E_CONTACT_TEL,
440 E_CONTACT_IS_LIST,
441 E_CONTACT_LIST_SHOW_ADDRESSES,
442 E_CONTACT_WANTS_HTML,
443 E_CONTACT_X509_CERT,
444 E_CONTACT_PGP_CERT
445 };
446
447 /* Create indexes on full_name and email fields as autocompletion
448 * queries would mainly rely on this.
449 *
450 * Add sort keys for name fields as those are likely targets for
451 * cursor usage.
452 */
453 static EContactField default_indexed_fields[] = {
454 E_CONTACT_FULL_NAME,
455 E_CONTACT_NICKNAME,
456 E_CONTACT_FILE_AS,
457 E_CONTACT_GIVEN_NAME,
458 E_CONTACT_FAMILY_NAME,
459 E_CONTACT_EMAIL,
460 E_CONTACT_FILE_AS,
461 E_CONTACT_FAMILY_NAME,
462 E_CONTACT_GIVEN_NAME
463 };
464
465 static EBookIndexType default_index_types[] = {
466 E_BOOK_INDEX_PREFIX,
467 E_BOOK_INDEX_PREFIX,
468 E_BOOK_INDEX_PREFIX,
469 E_BOOK_INDEX_PREFIX,
470 E_BOOK_INDEX_PREFIX,
471 E_BOOK_INDEX_PREFIX,
472 E_BOOK_INDEX_SORT_KEY,
473 E_BOOK_INDEX_SORT_KEY,
474 E_BOOK_INDEX_SORT_KEY
475 };
476
477 /******************************************************
478 * Summary Fields *
479 ******************************************************/
480 static ColumnInfo *
column_info_new(SummaryField * field,const gchar * folderid,const gchar * column_suffix,const gchar * column_type,const gchar * column_extra,const gchar * idx_prefix)481 column_info_new (SummaryField *field,
482 const gchar *folderid,
483 const gchar *column_suffix,
484 const gchar *column_type,
485 const gchar *column_extra,
486 const gchar *idx_prefix)
487 {
488 ColumnInfo *info;
489
490 info = g_slice_new0 (ColumnInfo);
491 info->type = column_type;
492 info->extra = column_extra;
493
494 if (!info->type) {
495 if (field->type == G_TYPE_STRING)
496 info->type = "TEXT";
497 else if (field->type == G_TYPE_BOOLEAN || field->type == E_TYPE_CONTACT_CERT)
498 info->type = "INTEGER";
499 else if (field->type == E_TYPE_CONTACT_ATTR_LIST)
500 info->type = "TEXT";
501 else
502 g_warn_if_reached ();
503 }
504
505 if (field->type == E_TYPE_CONTACT_ATTR_LIST)
506 /* Attribute lists are on their own table */
507 info->name = g_strconcat (
508 "value",
509 column_suffix ? "_" : NULL,
510 column_suffix,
511 NULL);
512 else
513 /* Regular fields are named by their 'dbname' */
514 info->name = g_strconcat (
515 field->dbname,
516 column_suffix ? "_" : NULL,
517 column_suffix,
518 NULL);
519
520 if (idx_prefix)
521 info->index = g_strconcat (
522 idx_prefix,
523 "_", field->dbname,
524 "_", folderid,
525 NULL);
526
527 return info;
528 }
529
530 static void
column_info_free(ColumnInfo * info)531 column_info_free (ColumnInfo *info)
532 {
533 if (info) {
534 g_free (info->name);
535 g_free (info->index);
536 g_slice_free (ColumnInfo, info);
537 }
538 }
539
540 static gint
summary_field_array_index(GArray * array,EContactField field)541 summary_field_array_index (GArray *array,
542 EContactField field)
543 {
544 gint i;
545
546 for (i = 0; i < array->len; i++) {
547 SummaryField *iter = &g_array_index (array, SummaryField, i);
548 if (field == iter->field_id)
549 return i;
550 }
551
552 return -1;
553 }
554
555 static SummaryField *
summary_field_append(GArray * array,const gchar * folderid,EContactField field_id,GError ** error)556 summary_field_append (GArray *array,
557 const gchar *folderid,
558 EContactField field_id,
559 GError **error)
560 {
561 const gchar *dbname = NULL;
562 GType type = G_TYPE_INVALID;
563 gint idx;
564 SummaryField new_field = { 0, };
565
566 if (field_id < 1 || field_id >= E_CONTACT_FIELD_LAST) {
567 EBSQL_SET_ERROR (
568 error, E_BOOK_SQLITE_ERROR_UNSUPPORTED_FIELD,
569 _("Unsupported contact field “%d” specified in summary"),
570 field_id);
571 return NULL;
572 }
573
574 /* Avoid including the same field twice in the summary */
575 idx = summary_field_array_index (array, field_id);
576 if (idx >= 0)
577 return &g_array_index (array, SummaryField, idx);
578
579 /* Resolve some exceptions, we store these
580 * specific contact fields with different names
581 * than those found in the EContactField table
582 */
583 switch (field_id) {
584 case E_CONTACT_UID:
585 dbname = "uid";
586 break;
587 case E_CONTACT_IS_LIST:
588 dbname = "is_list";
589 break;
590 default:
591 dbname = e_contact_field_name (field_id);
592 break;
593 }
594
595 type = e_contact_field_type (field_id);
596
597 if (type != G_TYPE_STRING &&
598 type != G_TYPE_BOOLEAN &&
599 type != E_TYPE_CONTACT_CERT &&
600 type != E_TYPE_CONTACT_ATTR_LIST) {
601 EBSQL_SET_ERROR (
602 error, E_BOOK_SQLITE_ERROR_UNSUPPORTED_FIELD,
603 _("Contact field “%s” of type “%s” specified in summary, "
604 "but only boolean, string and string list field types are supported"),
605 e_contact_pretty_name (field_id), g_type_name (type));
606 return NULL;
607 }
608
609 if (type == E_TYPE_CONTACT_ATTR_LIST) {
610 new_field.aux_table = g_strconcat (folderid, "_", dbname, "_list", NULL);
611 new_field.aux_table_symbolic = g_strconcat (dbname, "_list", NULL);
612 }
613
614 new_field.field_id = field_id;
615 new_field.dbname = dbname;
616 new_field.type = type;
617 new_field.index = 0;
618 g_array_append_val (array, new_field);
619
620 return &g_array_index (array, SummaryField, array->len - 1);
621 }
622
623 static gboolean
summary_field_remove(GArray * array,EContactField field)624 summary_field_remove (GArray *array,
625 EContactField field)
626 {
627 gint idx;
628
629 idx = summary_field_array_index (array, field);
630 if (idx < 0)
631 return FALSE;
632
633 g_array_remove_index_fast (array, idx);
634 return TRUE;
635 }
636
637 static void
summary_fields_add_indexes(GArray * array,EContactField * indexes,EBookIndexType * index_types,gint n_indexes)638 summary_fields_add_indexes (GArray *array,
639 EContactField *indexes,
640 EBookIndexType *index_types,
641 gint n_indexes)
642 {
643 gint i, j;
644
645 for (i = 0; i < array->len; i++) {
646 SummaryField *sfield = &g_array_index (array, SummaryField, i);
647
648 for (j = 0; j < n_indexes; j++) {
649 if (sfield->field_id == indexes[j])
650 sfield->index |= (1 << index_types[j]);
651
652 }
653 }
654 }
655
656 static inline gint
summary_field_get_index(EBookSqlite * ebsql,EContactField field_id)657 summary_field_get_index (EBookSqlite *ebsql,
658 EContactField field_id)
659 {
660 gint i;
661
662 for (i = 0; i < ebsql->priv->n_summary_fields; i++) {
663 if (ebsql->priv->summary_fields[i].field_id == field_id)
664 return i;
665 }
666
667 return -1;
668 }
669
670 static inline SummaryField *
summary_field_get(EBookSqlite * ebsql,EContactField field_id)671 summary_field_get (EBookSqlite *ebsql,
672 EContactField field_id)
673 {
674 gint index;
675
676 index = summary_field_get_index (ebsql, field_id);
677 if (index >= 0)
678 return &(ebsql->priv->summary_fields[index]);
679
680 return NULL;
681 }
682
683 static GSList *
summary_field_list_columns(SummaryField * field,const gchar * folderid)684 summary_field_list_columns (SummaryField *field,
685 const gchar *folderid)
686 {
687 GSList *columns = NULL;
688 ColumnInfo *info;
689
690 /* Doesn't hurt to verify a bit more here, this shouldn't happen though */
691 g_return_val_if_fail (
692 field->type == G_TYPE_STRING ||
693 field->type == G_TYPE_BOOLEAN ||
694 field->type == E_TYPE_CONTACT_CERT ||
695 field->type == E_TYPE_CONTACT_ATTR_LIST,
696 NULL);
697
698 /* Normal / default column */
699 info = column_info_new (
700 field, folderid, NULL, NULL,
701 (field->field_id == E_CONTACT_UID) ? "PRIMARY KEY" : NULL,
702 (field->index & INDEX_FLAG (PREFIX)) != 0 ? "INDEX" : NULL);
703 columns = g_slist_prepend (columns, info);
704
705 /* Localized column, for storing sort keys */
706 if (field->type == G_TYPE_STRING && (field->index & INDEX_FLAG (SORT_KEY))) {
707 info = column_info_new (field, folderid, EBSQL_SUFFIX_SORT_KEY, "TEXT", NULL, "SINDEX");
708 columns = g_slist_prepend (columns, info);
709 }
710
711 /* Suffix match column */
712 if (field->type != G_TYPE_BOOLEAN && field->type != E_TYPE_CONTACT_CERT &&
713 (field->index & INDEX_FLAG (SUFFIX)) != 0) {
714 info = column_info_new (field, folderid, EBSQL_SUFFIX_REVERSE, "TEXT", NULL, "RINDEX");
715 columns = g_slist_prepend (columns, info);
716 }
717
718 /* Phone match columns */
719 if (field->type != G_TYPE_BOOLEAN && field->type != E_TYPE_CONTACT_CERT &&
720 (field->index & INDEX_FLAG (PHONE)) != 0) {
721
722 /* One indexed column for storing the national number */
723 info = column_info_new (field, folderid, EBSQL_SUFFIX_PHONE, "TEXT", NULL, "PINDEX");
724 columns = g_slist_prepend (columns, info);
725
726 /* One integer column for storing the country code */
727 info = column_info_new (field, folderid, EBSQL_SUFFIX_COUNTRY, "INTEGER", "DEFAULT 0", NULL);
728 columns = g_slist_prepend (columns, info);
729 }
730
731 return g_slist_reverse (columns);
732 }
733
734 static void
summary_fields_array_free(SummaryField * fields,gint n_fields)735 summary_fields_array_free (SummaryField *fields,
736 gint n_fields)
737 {
738 gint i;
739
740 for (i = 0; i < n_fields; i++) {
741 g_free (fields[i].aux_table);
742 g_free (fields[i].aux_table_symbolic);
743 }
744
745 g_free (fields);
746 }
747
748 /******************************************************
749 * Sharing EBookSqlite instances *
750 ******************************************************/
751 static GHashTable *db_connections = NULL;
752 static GMutex dbcon_lock;
753
754 static EBookSqlite *
ebsql_ref_from_hash(const gchar * path)755 ebsql_ref_from_hash (const gchar *path)
756 {
757 EBookSqlite *ebsql = NULL;
758
759 if (db_connections != NULL) {
760 ebsql = g_hash_table_lookup (db_connections, path);
761 }
762
763 if (ebsql) {
764 EBSQL_NOTE (REF_COUNTS, g_printerr ("EBookSqlite ref count increased from hash table reference\n"));
765 g_object_ref (ebsql);
766 }
767
768 return ebsql;
769 }
770
771 static void
ebsql_register_to_hash(EBookSqlite * ebsql,const gchar * path)772 ebsql_register_to_hash (EBookSqlite *ebsql,
773 const gchar *path)
774 {
775 if (db_connections == NULL)
776 db_connections = g_hash_table_new_full (
777 (GHashFunc) g_str_hash,
778 (GEqualFunc) g_str_equal,
779 (GDestroyNotify) g_free,
780 (GDestroyNotify) NULL);
781 g_hash_table_insert (db_connections, g_strdup (path), ebsql);
782 }
783
784 static void
ebsql_unregister_from_hash(EBookSqlite * ebsql)785 ebsql_unregister_from_hash (EBookSqlite *ebsql)
786 {
787 EBookSqlitePrivate *priv = ebsql->priv;
788
789 EBSQL_LOCK_MUTEX (&dbcon_lock);
790 if (db_connections != NULL) {
791 if (priv->path != NULL) {
792 g_hash_table_remove (db_connections, priv->path);
793
794 if (g_hash_table_size (db_connections) == 0) {
795 g_hash_table_destroy (db_connections);
796 db_connections = NULL;
797 }
798
799 }
800 }
801 EBSQL_UNLOCK_MUTEX (&dbcon_lock);
802 }
803
804 /************************************************************
805 * SQLite helper functions *
806 ************************************************************/
807
808 /* For EBSQL_DEBUG_EXPLAIN */
809 static gint
ebsql_debug_query_plan_cb(gpointer ref,gint n_cols,gchar ** cols,gchar ** name)810 ebsql_debug_query_plan_cb (gpointer ref,
811 gint n_cols,
812 gchar **cols,
813 gchar **name)
814 {
815 gint i;
816
817 for (i = 0; i < n_cols; i++) {
818 if (strcmp (name[i], "detail") == 0) {
819 g_printerr (" PLAN: %s\n", cols[i]);
820 break;
821 }
822 }
823
824 return 0;
825 }
826
827 /* Collect a GList of column names in the main summary table */
828 static gint
get_columns_cb(gpointer ref,gint col,gchar ** cols,gchar ** name)829 get_columns_cb (gpointer ref,
830 gint col,
831 gchar **cols,
832 gchar **name)
833 {
834 GSList **columns = (GSList **) ref;
835 gint i;
836
837 for (i = 0; i < col; i++) {
838 if (strcmp (name[i], "name") == 0) {
839
840 /* Keep comparing for the legacy 'bdata' column */
841 if (strcmp (cols[i], "vcard") != 0 &&
842 strcmp (cols[i], "bdata") != 0) {
843 gchar *column = g_strdup (cols[i]);
844
845 *columns = g_slist_prepend (*columns, column);
846 }
847 break;
848 }
849 }
850 return 0;
851 }
852
853 /* Collect the first string result */
854 static gint
get_string_cb(gpointer ref,gint col,gchar ** cols,gchar ** name)855 get_string_cb (gpointer ref,
856 gint col,
857 gchar **cols,
858 gchar **name)
859 {
860 gchar **ret = ref;
861
862 *ret = g_strdup (cols [0]);
863
864 return 0;
865 }
866
867 /* Collect the first integer result */
868 static gint
get_int_cb(gpointer ref,gint col,gchar ** cols,gchar ** name)869 get_int_cb (gpointer ref,
870 gint col,
871 gchar **cols,
872 gchar **name)
873 {
874 gint *ret = ref;
875
876 *ret = cols [0] ? g_ascii_strtoll (cols[0], NULL, 10) : 0;
877
878 return 0;
879 }
880
881 /* Collect the result of a SELECT count(*) statement */
882 static gint
get_count_cb(gpointer ref,gint n_cols,gchar ** cols,gchar ** name)883 get_count_cb (gpointer ref,
884 gint n_cols,
885 gchar **cols,
886 gchar **name)
887 {
888 gint64 count = 0;
889 gint *ret = ref;
890 gint i;
891
892 for (i = 0; i < n_cols; i++) {
893 if (name[i] && strncmp (name[i], "count", 5) == 0) {
894 count = g_ascii_strtoll (cols[i], NULL, 10);
895
896 break;
897 }
898 }
899
900 *ret = count;
901
902 return 0;
903 }
904
905 /* Report if there was at least one result */
906 static gint
get_exists_cb(gpointer ref,gint col,gchar ** cols,gchar ** name)907 get_exists_cb (gpointer ref,
908 gint col,
909 gchar **cols,
910 gchar **name)
911 {
912 gboolean *exists = ref;
913
914 *exists = TRUE;
915
916 return 0;
917 }
918
919 static EbSqlSearchData *
search_data_from_results(gint ncol,gchar ** cols,gchar ** names)920 search_data_from_results (gint ncol,
921 gchar **cols,
922 gchar **names)
923 {
924 EbSqlSearchData *data = g_slice_new0 (EbSqlSearchData);
925 gint i;
926 const gchar *name;
927
928 for (i = 0; i < ncol; i++) {
929
930 if (!names[i] || !cols[i])
931 continue;
932
933 name = names[i];
934 if (!strncmp (name, "summary.", 8))
935 name += 8;
936
937 /* These come through differently depending on the configuration,
938 * search within text is good enough
939 */
940 if (!g_ascii_strcasecmp (name, "uid")) {
941 data->uid = g_strdup (cols[i]);
942 } else if (!g_ascii_strcasecmp (name, "vcard") ||
943 !g_ascii_strncasecmp (name, "fetch_vcard", 11)) {
944 data->vcard = g_strdup (cols[i]);
945 } else if (!g_ascii_strcasecmp (name, "bdata")) {
946 data->extra = g_strdup (cols[i]);
947 }
948 }
949
950 return data;
951 }
952
953 static gint
collect_full_results_cb(gpointer ref,gint ncol,gchar ** cols,gchar ** names)954 collect_full_results_cb (gpointer ref,
955 gint ncol,
956 gchar **cols,
957 gchar **names)
958 {
959 EbSqlSearchData *data;
960 GSList **vcard_data = ref;
961
962 data = search_data_from_results (ncol, cols, names);
963
964 *vcard_data = g_slist_prepend (*vcard_data, data);
965
966 return 0;
967 }
968
969 static gint
collect_uid_results_cb(gpointer ref,gint ncol,gchar ** cols,gchar ** names)970 collect_uid_results_cb (gpointer ref,
971 gint ncol,
972 gchar **cols,
973 gchar **names)
974 {
975 GSList **uids = ref;
976
977 if (cols[0])
978 *uids = g_slist_prepend (*uids, g_strdup (cols [0]));
979
980 return 0;
981 }
982
983 static gint
collect_lean_results_cb(gpointer ref,gint ncol,gchar ** cols,gchar ** names)984 collect_lean_results_cb (gpointer ref,
985 gint ncol,
986 gchar **cols,
987 gchar **names)
988 {
989 GSList **vcard_data = ref;
990 EbSqlSearchData *search_data = g_slice_new0 (EbSqlSearchData);
991 EContact *contact = e_contact_new ();
992 gchar *vcard;
993 gint i;
994
995 /* parse through cols, this will be useful if the api starts supporting field restrictions */
996 for (i = 0; i < ncol; i++) {
997 if (!names[i] || !cols[i])
998 continue;
999
1000 /* Only UID & REV can be used to create contacts from the summary columns */
1001 if (!g_ascii_strcasecmp (names[i], "uid")) {
1002 e_contact_set (contact, E_CONTACT_UID, cols[i]);
1003 search_data->uid = g_strdup (cols[i]);
1004 } else if (!g_ascii_strcasecmp (names[i], "Rev")) {
1005 e_contact_set (contact, E_CONTACT_REV, cols[i]);
1006 } else if (!g_ascii_strcasecmp (names[i], "bdata")) {
1007 search_data->extra = g_strdup (cols[i]);
1008 }
1009 }
1010
1011 vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
1012 search_data->vcard = vcard;
1013 *vcard_data = g_slist_prepend (*vcard_data, search_data);
1014
1015 g_object_unref (contact);
1016 return 0;
1017 }
1018
1019 static void
ebsql_string_append_vprintf(GString * string,const gchar * fmt,va_list args)1020 ebsql_string_append_vprintf (GString *string,
1021 const gchar *fmt,
1022 va_list args)
1023 {
1024 gchar *stmt;
1025
1026 /* Unfortunately, sqlite3_vsnprintf() doesnt tell us
1027 * how many bytes it would have needed if it doesnt fit
1028 * into the target buffer, so we can't avoid this
1029 * really disgusting memory dup.
1030 */
1031 stmt = sqlite3_vmprintf (fmt, args);
1032 g_string_append (string, stmt);
1033 sqlite3_free (stmt);
1034 }
1035
1036 static void
ebsql_string_append_printf(GString * string,const gchar * fmt,...)1037 ebsql_string_append_printf (GString *string,
1038 const gchar *fmt,
1039 ...)
1040 {
1041 va_list args;
1042
1043 va_start (args, fmt);
1044 ebsql_string_append_vprintf (string, fmt, args);
1045 va_end (args);
1046 }
1047
1048 /* Appends an identifier suitable to identify the
1049 * column to test in the context of a query.
1050 *
1051 * The suffix is for special indexed columns (such as
1052 * reverse values, sort keys, phone numbers, etc).
1053 */
1054 static void
ebsql_string_append_column(GString * string,SummaryField * field,const gchar * suffix)1055 ebsql_string_append_column (GString *string,
1056 SummaryField *field,
1057 const gchar *suffix)
1058 {
1059 if (field->aux_table) {
1060 g_string_append (string, field->aux_table_symbolic);
1061 g_string_append (string, ".value");
1062 } else {
1063 g_string_append (string, "summary.");
1064 g_string_append (string, field->dbname);
1065 }
1066
1067 if (suffix) {
1068 g_string_append_c (string, '_');
1069 g_string_append (string, suffix);
1070 }
1071 }
1072
1073 static gboolean
ebsql_exec_vprintf(EBookSqlite * ebsql,const gchar * fmt,EbSqlRowFunc callback,gpointer data,GCancellable * cancellable,GError ** error,va_list args)1074 ebsql_exec_vprintf (EBookSqlite *ebsql,
1075 const gchar *fmt,
1076 EbSqlRowFunc callback,
1077 gpointer data,
1078 GCancellable *cancellable,
1079 GError **error,
1080 va_list args)
1081 {
1082 gboolean success;
1083 gchar *stmt;
1084
1085 stmt = sqlite3_vmprintf (fmt, args);
1086 success = ebsql_exec (ebsql, stmt, callback, data, cancellable, error);
1087 sqlite3_free (stmt);
1088
1089 return success;
1090 }
1091
1092 static gboolean
ebsql_exec_printf(EBookSqlite * ebsql,const gchar * fmt,EbSqlRowFunc callback,gpointer data,GCancellable * cancellable,GError ** error,...)1093 ebsql_exec_printf (EBookSqlite *ebsql,
1094 const gchar *fmt,
1095 EbSqlRowFunc callback,
1096 gpointer data,
1097 GCancellable *cancellable,
1098 GError **error,
1099 ...)
1100 {
1101 gboolean success;
1102 va_list args;
1103
1104 va_start (args, error);
1105 success = ebsql_exec_vprintf (ebsql, fmt, callback, data, cancellable, error, args);
1106 va_end (args);
1107
1108 return success;
1109 }
1110
1111 static inline void
ebsql_exec_maybe_debug(EBookSqlite * ebsql,const gchar * stmt)1112 ebsql_exec_maybe_debug (EBookSqlite *ebsql,
1113 const gchar *stmt)
1114 {
1115 if (ebsql_debug_flags & EBSQL_DEBUG_EXPLAIN &&
1116 strncmp (stmt, "SELECT", 6) == 0) {
1117 g_printerr ("EXPLAIN BEGIN\n STMT: %s\n", stmt);
1118 ebsql_exec_printf (ebsql, "EXPLAIN QUERY PLAN %s",
1119 ebsql_debug_query_plan_cb,
1120 NULL, NULL, NULL, stmt);
1121 g_printerr ("EXPLAIN END\n");
1122 } else {
1123 EBSQL_NOTE (STATEMENTS, g_printerr ("STMT: %s\n", stmt));
1124 }
1125 }
1126
1127 static gboolean
ebsql_exec(EBookSqlite * ebsql,const gchar * stmt,EbSqlRowFunc callback,gpointer data,GCancellable * cancellable,GError ** error)1128 ebsql_exec (EBookSqlite *ebsql,
1129 const gchar *stmt,
1130 EbSqlRowFunc callback,
1131 gpointer data,
1132 GCancellable *cancellable,
1133 GError **error)
1134 {
1135 gboolean had_cancel;
1136 gchar *errmsg = NULL;
1137 gint ret = -1, retries = 0;
1138 gint64 t1 = 0, t2;
1139
1140 g_return_val_if_fail (stmt != NULL, FALSE);
1141
1142 /* Debug output for statements and query plans */
1143 ebsql_exec_maybe_debug (ebsql, stmt);
1144
1145 /* Just convenience to set the cancellable on an execution
1146 * without a transaction, error checking on the cancellable
1147 * is done with EBSQL_LOCK_OR_RETURN()
1148 */
1149 if (ebsql->priv->cancel) {
1150 had_cancel = TRUE;
1151 } else {
1152 ebsql->priv->cancel = cancellable;
1153 had_cancel = FALSE;
1154 }
1155
1156 if ((ebsql_debug_flags & EBSQL_DEBUG_TIMING) != 0 &&
1157 strncmp (stmt, "EXPLAIN QUERY PLAN ", 19) != 0)
1158 t1 = g_get_monotonic_time();
1159
1160 ret = sqlite3_exec (ebsql->priv->db, stmt, callback, data, &errmsg);
1161
1162 while (ret == SQLITE_BUSY || ret == SQLITE_LOCKED || ret == -1) {
1163 /* try for ~15 seconds, then give up */
1164 if (retries > 150)
1165 break;
1166 retries++;
1167
1168 g_clear_pointer (&errmsg, sqlite3_free);
1169 g_thread_yield ();
1170 g_usleep (100 * 1000); /* Sleep for 100 ms */
1171
1172 if (t1)
1173 t1 = g_get_monotonic_time();
1174
1175 ret = sqlite3_exec (ebsql->priv->db, stmt, callback, data, &errmsg);
1176 }
1177
1178 if (!had_cancel)
1179 ebsql->priv->cancel = NULL;
1180
1181 if (t1) {
1182 t2 = g_get_monotonic_time();
1183 g_printerr ("TIME: %" G_GINT64_FORMAT " ms\n", (t2 - t1) / 1000);
1184 }
1185 if (ret != SQLITE_OK) {
1186 EBSQL_SET_ERROR_FROM_SQLITE (error, ret, errmsg);
1187 sqlite3_free (errmsg);
1188 return FALSE;
1189 }
1190
1191 if (errmsg)
1192 sqlite3_free (errmsg);
1193
1194 return TRUE;
1195 }
1196
1197 static gboolean
ebsql_start_transaction(EBookSqlite * ebsql,EbSqlLockType lock_type,GCancellable * cancel,GError ** error)1198 ebsql_start_transaction (EBookSqlite *ebsql,
1199 EbSqlLockType lock_type,
1200 GCancellable *cancel,
1201 GError **error)
1202 {
1203 gboolean success = TRUE;
1204
1205 g_return_val_if_fail (ebsql != NULL, FALSE);
1206 g_return_val_if_fail (ebsql->priv != NULL, FALSE);
1207 g_return_val_if_fail (ebsql->priv->db != NULL, FALSE);
1208
1209 ebsql->priv->in_transaction++;
1210 g_return_val_if_fail (ebsql->priv->in_transaction > 0, FALSE);
1211
1212 if (ebsql->priv->in_transaction == 1) {
1213
1214 /* No cancellable should be set at transaction start time */
1215 if (ebsql->priv->cancel) {
1216 g_warning (
1217 "Starting a transaction with a cancellable already set. "
1218 "Clearing previously set cancellable");
1219 g_clear_object (&ebsql->priv->cancel);
1220 }
1221
1222 /* Hold on to the cancel object until the end of the transaction */
1223 if (cancel)
1224 ebsql->priv->cancel = g_object_ref (cancel);
1225
1226 /* It's important to make the distinction between a
1227 * transaction which will read or one which will write.
1228 *
1229 * While it's not well documented, when receiving the SQLITE_BUSY
1230 * error status, one can only safely retry at the beginning of
1231 * the transaction.
1232 *
1233 * If a transaction is 'upgraded' to require a writer lock
1234 * half way through the transaction and SQLITE_BUSY is returned,
1235 * the whole transaction would need to be retried from the beginning.
1236 */
1237 ebsql->priv->lock_type = lock_type;
1238
1239 switch (lock_type) {
1240 case EBSQL_LOCK_READ:
1241 success = ebsql_exec (ebsql, "BEGIN", NULL, NULL, NULL, error);
1242 break;
1243 case EBSQL_LOCK_WRITE:
1244 success = ebsql_exec (ebsql, "BEGIN IMMEDIATE", NULL, NULL, NULL, error);
1245 break;
1246 }
1247
1248 } else {
1249
1250 /* Warn about cases where where a read transaction might be upgraded */
1251 if (lock_type == EBSQL_LOCK_WRITE && ebsql->priv->lock_type == EBSQL_LOCK_READ)
1252 g_warning (
1253 "A nested transaction wants to write, "
1254 "but the outermost transaction was started "
1255 "without a writer lock.");
1256 }
1257
1258 return success;
1259 }
1260
1261 static gboolean
ebsql_commit_transaction(EBookSqlite * ebsql,GError ** error)1262 ebsql_commit_transaction (EBookSqlite *ebsql,
1263 GError **error)
1264 {
1265 gboolean success = TRUE;
1266
1267 g_return_val_if_fail (ebsql != NULL, FALSE);
1268 g_return_val_if_fail (ebsql->priv != NULL, FALSE);
1269 g_return_val_if_fail (ebsql->priv->db != NULL, FALSE);
1270
1271 g_return_val_if_fail (ebsql->priv->in_transaction > 0, FALSE);
1272
1273 ebsql->priv->in_transaction--;
1274
1275 if (ebsql->priv->in_transaction == 0) {
1276 success = ebsql_exec (ebsql, "COMMIT", NULL, NULL, NULL, error);
1277
1278 /* The outermost transaction is finished, let's release
1279 * our reference to the user's cancel object here */
1280 g_clear_object (&ebsql->priv->cancel);
1281 }
1282
1283 return success;
1284 }
1285
1286 static gboolean
ebsql_rollback_transaction(EBookSqlite * ebsql,GError ** error)1287 ebsql_rollback_transaction (EBookSqlite *ebsql,
1288 GError **error)
1289 {
1290 gboolean success = TRUE;
1291
1292 g_return_val_if_fail (ebsql != NULL, FALSE);
1293 g_return_val_if_fail (ebsql->priv != NULL, FALSE);
1294 g_return_val_if_fail (ebsql->priv->db != NULL, FALSE);
1295
1296 g_return_val_if_fail (ebsql->priv->in_transaction > 0, FALSE);
1297
1298 ebsql->priv->in_transaction--;
1299
1300 if (ebsql->priv->in_transaction == 0) {
1301 success = ebsql_exec (ebsql, "ROLLBACK", NULL, NULL, NULL, error);
1302
1303 /* The outermost transaction is finished, let's release
1304 * our reference to the user's cancel object here */
1305 g_clear_object (&ebsql->priv->cancel);
1306 }
1307 return success;
1308 }
1309
1310 static sqlite3_stmt *
ebsql_prepare_statement(EBookSqlite * ebsql,const gchar * stmt_str,GError ** error)1311 ebsql_prepare_statement (EBookSqlite *ebsql,
1312 const gchar *stmt_str,
1313 GError **error)
1314 {
1315 sqlite3_stmt *stmt;
1316 const gchar *stmt_tail = NULL;
1317 gint ret;
1318
1319 g_return_val_if_fail (stmt_str != NULL, NULL);
1320
1321 ret = sqlite3_prepare_v2 (ebsql->priv->db, stmt_str, strlen (stmt_str), &stmt, &stmt_tail);
1322
1323 if (ret != SQLITE_OK) {
1324 const gchar *errmsg = sqlite3_errmsg (ebsql->priv->db);
1325 EBSQL_SET_ERROR_LITERAL (
1326 error,
1327 E_BOOK_SQLITE_ERROR_ENGINE,
1328 errmsg);
1329 } else if (stmt == NULL) {
1330 EBSQL_SET_ERROR_LITERAL (
1331 error,
1332 E_BOOK_SQLITE_ERROR_ENGINE,
1333 "Unknown error preparing SQL statement");
1334 }
1335
1336 if (stmt_tail && stmt_tail[0])
1337 g_warning ("Part of this statement was not parsed: %s", stmt_tail);
1338
1339 return stmt;
1340 }
1341
1342 /* Convenience for running statements. After successfully
1343 * binding all parameters, just return with this.
1344 */
1345 static gboolean
ebsql_complete_statement(EBookSqlite * ebsql,sqlite3_stmt * stmt,gint ret,GError ** error)1346 ebsql_complete_statement (EBookSqlite *ebsql,
1347 sqlite3_stmt *stmt,
1348 gint ret,
1349 GError **error)
1350 {
1351 if (ret == SQLITE_OK)
1352 ret = sqlite3_step (stmt);
1353
1354 if (ret != SQLITE_OK && ret != SQLITE_DONE) {
1355 const gchar *errmsg = sqlite3_errmsg (ebsql->priv->db);
1356 EBSQL_SET_ERROR_FROM_SQLITE (error, ret, errmsg);
1357 }
1358
1359 /* Reset / Clear at the end, regardless of error state */
1360 sqlite3_reset (stmt);
1361 sqlite3_clear_bindings (stmt);
1362
1363 return (ret == SQLITE_OK || ret == SQLITE_DONE);
1364 }
1365
1366 /******************************************************
1367 * Functions installed into the SQLite *
1368 ******************************************************/
1369
1370 /* Implementation for REGEXP keyword */
1371 static void
ebsql_regexp(sqlite3_context * context,gint argc,sqlite3_value ** argv)1372 ebsql_regexp (sqlite3_context *context,
1373 gint argc,
1374 sqlite3_value **argv)
1375 {
1376 GRegex *regex;
1377 const gchar *expression;
1378 const gchar *text;
1379
1380 /* Reuse the same GRegex for all REGEXP queries with the same expression */
1381 regex = sqlite3_get_auxdata (context, 0);
1382 if (!regex) {
1383 GError *error = NULL;
1384
1385 expression = (const gchar *) sqlite3_value_text (argv[0]);
1386
1387 regex = g_regex_new (expression, 0, 0, &error);
1388
1389 if (!regex) {
1390 sqlite3_result_error (
1391 context,
1392 error ? error->message :
1393 _("Error parsing regular expression"),
1394 -1);
1395 g_clear_error (&error);
1396 return;
1397 }
1398
1399 /* SQLite will take care of freeing the GRegex when we're done with the query */
1400 sqlite3_set_auxdata (context, 0, regex, (GDestroyNotify) g_regex_unref);
1401 }
1402
1403 /* Now perform the comparison */
1404 text = (const gchar *) sqlite3_value_text (argv[1]);
1405 if (text != NULL) {
1406 gboolean match;
1407
1408 match = g_regex_match (regex, text, 0, NULL);
1409 sqlite3_result_int (context, match ? 1 : 0);
1410 }
1411 }
1412
1413 /* Implementation of EBSQL_FUNC_COMPARE_VCARD (fallback for non-summary queries) */
1414 static void
ebsql_compare_vcard(sqlite3_context * context,gint argc,sqlite3_value ** argv)1415 ebsql_compare_vcard (sqlite3_context *context,
1416 gint argc,
1417 sqlite3_value **argv)
1418 {
1419 EBookBackendSExp *sexp = NULL;
1420 const gchar *text;
1421 const gchar *vcard;
1422
1423 /* Reuse the same sexp for all queries with the same search expression */
1424 sexp = sqlite3_get_auxdata (context, 0);
1425 if (!sexp) {
1426
1427 /* The first argument will be reused for many rows */
1428 text = (const gchar *) sqlite3_value_text (argv[0]);
1429 if (text) {
1430 sexp = e_book_backend_sexp_new (text);
1431 sqlite3_set_auxdata (
1432 context, 0,
1433 sexp,
1434 g_object_unref);
1435 }
1436
1437 /* This shouldn't happen, catch invalid sexp in preflight */
1438 if (!sexp) {
1439 sqlite3_result_int (context, 0);
1440 return;
1441 }
1442
1443 }
1444
1445 /* Reuse the same vcard as much as possible (it can be referred to more than
1446 * once in the query, so it can be reused for multiple comparisons on the same row)
1447 *
1448 * This may look extensive, but as the vcard might be resolved by calling an
1449 * EbSqlVCardCallback, it's important to reuse this string as much as possible.
1450 *
1451 * See ebsql_fetch_vcard() for details.
1452 */
1453 vcard = sqlite3_get_auxdata (context, 1);
1454 if (!vcard) {
1455 vcard = (const gchar *) sqlite3_value_text (argv[1]);
1456
1457 if (vcard)
1458 sqlite3_set_auxdata (context, 1, g_strdup (vcard), g_free);
1459 }
1460
1461 /* A NULL vcard can never match */
1462 if (vcard == NULL || *vcard == '\0') {
1463 sqlite3_result_int (context, 0);
1464 return;
1465 }
1466
1467 /* Compare this vcard */
1468 if (e_book_backend_sexp_match_vcard (sexp, vcard))
1469 sqlite3_result_int (context, 1);
1470 else
1471 sqlite3_result_int (context, 0);
1472 }
1473
1474 static void
ebsql_eqphone(sqlite3_context * context,gint argc,sqlite3_value ** argv,EPhoneNumberMatch requested_match)1475 ebsql_eqphone (sqlite3_context *context,
1476 gint argc,
1477 sqlite3_value **argv,
1478 EPhoneNumberMatch requested_match)
1479 {
1480 EBookSqlite *ebsql = sqlite3_user_data (context);
1481 EPhoneNumber *input_phone = NULL, *row_phone = NULL;
1482 EPhoneNumberMatch match = E_PHONE_NUMBER_MATCH_NONE;
1483 const gchar *text;
1484
1485 /* Reuse the same phone number for all queries with the same phone number argument */
1486 input_phone = sqlite3_get_auxdata (context, 0);
1487 if (!input_phone) {
1488
1489 /* The first argument will be reused for many rows */
1490 text = (const gchar *) sqlite3_value_text (argv[0]);
1491 if (text) {
1492
1493 /* Ignore errors, they are fine for phone numbers */
1494 input_phone = e_phone_number_from_string (text, ebsql->priv->region_code, NULL);
1495
1496 /* SQLite will take care of freeing the EPhoneNumber when we're done with the expression */
1497 if (input_phone)
1498 sqlite3_set_auxdata (
1499 context, 0,
1500 input_phone,
1501 (GDestroyNotify) e_phone_number_free);
1502 }
1503 }
1504
1505 /* This shouldn't happen, as we catch invalid phone number queries in preflight
1506 */
1507 if (!input_phone) {
1508 sqlite3_result_int (context, 0);
1509 return;
1510 }
1511
1512 /* Parse the phone number for this row */
1513 text = (const gchar *) sqlite3_value_text (argv[1]);
1514 if (text != NULL) {
1515 row_phone = e_phone_number_from_string (text, ebsql->priv->region_code, NULL);
1516
1517 /* And perform the comparison */
1518 if (row_phone) {
1519 match = e_phone_number_compare (input_phone, row_phone);
1520
1521 e_phone_number_free (row_phone);
1522 }
1523 }
1524
1525 /* Now report the result */
1526 if (match != E_PHONE_NUMBER_MATCH_NONE &&
1527 match <= requested_match)
1528 sqlite3_result_int (context, 1);
1529 else
1530 sqlite3_result_int (context, 0);
1531 }
1532
1533 /* Exact phone number match function: EBSQL_FUNC_EQPHONE_EXACT */
1534 static void
ebsql_eqphone_exact(sqlite3_context * context,gint argc,sqlite3_value ** argv)1535 ebsql_eqphone_exact (sqlite3_context *context,
1536 gint argc,
1537 sqlite3_value **argv)
1538 {
1539 ebsql_eqphone (context, argc, argv, E_PHONE_NUMBER_MATCH_EXACT);
1540 }
1541
1542 /* National phone number match function: EBSQL_FUNC_EQPHONE_NATIONAL */
1543 static void
ebsql_eqphone_national(sqlite3_context * context,gint argc,sqlite3_value ** argv)1544 ebsql_eqphone_national (sqlite3_context *context,
1545 gint argc,
1546 sqlite3_value **argv)
1547 {
1548 ebsql_eqphone (context, argc, argv, E_PHONE_NUMBER_MATCH_NATIONAL);
1549 }
1550
1551 /* Short phone number match function: EBSQL_FUNC_EQPHONE_SHORT */
1552 static void
ebsql_eqphone_short(sqlite3_context * context,gint argc,sqlite3_value ** argv)1553 ebsql_eqphone_short (sqlite3_context *context,
1554 gint argc,
1555 sqlite3_value **argv)
1556 {
1557 ebsql_eqphone (context, argc, argv, E_PHONE_NUMBER_MATCH_SHORT);
1558 }
1559
1560 /* Implementation of EBSQL_FUNC_FETCH_VCARD (fallback for shallow addressbooks) */
1561 static void
ebsql_fetch_vcard(sqlite3_context * context,gint argc,sqlite3_value ** argv)1562 ebsql_fetch_vcard (sqlite3_context *context,
1563 gint argc,
1564 sqlite3_value **argv)
1565 {
1566 EBookSqlite *ebsql = sqlite3_user_data (context);
1567 const gchar *uid;
1568 const gchar *extra;
1569 gchar *vcard = NULL;
1570
1571 uid = (const gchar *) sqlite3_value_text (argv[0]);
1572 extra = (const gchar *) sqlite3_value_text (argv[1]);
1573
1574 /* Call our delegate to generate the vcard */
1575 if (ebsql->priv->vcard_callback)
1576 vcard = ebsql->priv->vcard_callback (
1577 uid, extra, ebsql->priv->user_data);
1578
1579 EBSQL_NOTE (
1580 FETCH_VCARD,
1581 g_printerr (
1582 "fetch_vcard (%s, %s) %s",
1583 uid, extra, vcard ? "Got VCard" : "No VCard"));
1584
1585 sqlite3_result_text (context, vcard, -1, g_free);
1586 }
1587
1588 typedef struct {
1589 const gchar *name;
1590 EbSqlCustomFunc func;
1591 gint arguments;
1592 } EbSqlCustomFuncTab;
1593
1594 static EbSqlCustomFuncTab ebsql_custom_functions[] = {
1595 { "regexp", ebsql_regexp, 2 }, /* regexp (expression, column_data) */
1596 { EBSQL_FUNC_COMPARE_VCARD, ebsql_compare_vcard, 2 }, /* compare_vcard (sexp, vcard) */
1597 { EBSQL_FUNC_FETCH_VCARD, ebsql_fetch_vcard, 2 }, /* fetch_vcard (uid, extra) */
1598 { EBSQL_FUNC_EQPHONE_EXACT, ebsql_eqphone_exact, 2 }, /* eqphone_exact (search_input, column_data) */
1599 { EBSQL_FUNC_EQPHONE_NATIONAL, ebsql_eqphone_national, 2 }, /* eqphone_national (search_input, column_data) */
1600 { EBSQL_FUNC_EQPHONE_SHORT, ebsql_eqphone_short, 2 }, /* eqphone_national (search_input, column_data) */
1601 };
1602
1603 /******************************************************
1604 * Fallback Collation Sequences *
1605 ******************************************************
1606 *
1607 * The fallback simply compares vcards, vcards which have been
1608 * stored on the cursor will have a preencoded key (these
1609 * utilities encode & decode that key).
1610 */
1611 static gchar *
ebsql_encode_vcard_sort_key(const gchar * sort_key)1612 ebsql_encode_vcard_sort_key (const gchar *sort_key)
1613 {
1614 EVCard *vcard = e_vcard_new ();
1615 gchar *base64;
1616 gchar *encoded;
1617
1618 /* Encode this otherwise e-vcard messes it up */
1619 base64 = g_base64_encode ((const guchar *) sort_key, strlen (sort_key));
1620 e_vcard_append_attribute_with_value (
1621 vcard,
1622 e_vcard_attribute_new (NULL, EBSQL_VCARD_SORT_KEY),
1623 base64);
1624 encoded = e_vcard_to_string (vcard, EVC_FORMAT_VCARD_30);
1625
1626 g_free (base64);
1627 g_object_unref (vcard);
1628
1629 return encoded;
1630 }
1631
1632 static gchar *
ebsql_decode_vcard_sort_key_from_vcard(EVCard * vcard)1633 ebsql_decode_vcard_sort_key_from_vcard (EVCard *vcard)
1634 {
1635 EVCardAttribute *attr;
1636 GList *values = NULL;
1637 gchar *sort_key = NULL;
1638 gchar *base64 = NULL;
1639
1640 attr = e_vcard_get_attribute (vcard, EBSQL_VCARD_SORT_KEY);
1641 if (attr)
1642 values = e_vcard_attribute_get_values (attr);
1643
1644 if (values && values->data) {
1645 gsize len;
1646
1647 base64 = g_strdup (values->data);
1648
1649 sort_key = (gchar *) g_base64_decode (base64, &len);
1650 g_free (base64);
1651 }
1652
1653 return sort_key;
1654 }
1655
1656 static gchar *
ebsql_decode_vcard_sort_key(const gchar * encoded)1657 ebsql_decode_vcard_sort_key (const gchar *encoded)
1658 {
1659 EVCard *vcard;
1660 gchar *sort_key;
1661
1662 vcard = e_vcard_new_from_string (encoded);
1663 sort_key = ebsql_decode_vcard_sort_key_from_vcard (vcard);
1664 g_object_unref (vcard);
1665
1666 return sort_key;
1667 }
1668
1669 typedef struct {
1670 EBookSqlite *ebsql;
1671 EContactField field;
1672 } EbSqlCollData;
1673
1674 static gint
ebsql_fallback_collator(gpointer ref,gint len1,gconstpointer data1,gint len2,gconstpointer data2)1675 ebsql_fallback_collator (gpointer ref,
1676 gint len1,
1677 gconstpointer data1,
1678 gint len2,
1679 gconstpointer data2)
1680 {
1681 EbSqlCollData *data = (EbSqlCollData *) ref;
1682 EBookSqlitePrivate *priv;
1683 EContact *contact1, *contact2;
1684 const gchar *str1, *str2;
1685 gchar *key1, *key2;
1686 gchar *tmp;
1687 gint result = 0;
1688
1689 priv = data->ebsql->priv;
1690
1691 str1 = (const gchar *) data1;
1692 str2 = (const gchar *) data2;
1693
1694 /* Construct 2 contacts (we're comparing vcards) */
1695 contact1 = e_contact_new ();
1696 contact2 = e_contact_new ();
1697 e_vcard_construct_full (E_VCARD (contact1), str1, len1, NULL);
1698 e_vcard_construct_full (E_VCARD (contact2), str2, len2, NULL);
1699
1700 /* Extract first key */
1701 key1 = ebsql_decode_vcard_sort_key_from_vcard (E_VCARD (contact1));
1702 if (!key1) {
1703 tmp = e_contact_get (contact1, data->field);
1704 if (tmp)
1705 key1 = e_collator_generate_key (priv->collator, tmp, NULL);
1706 g_free (tmp);
1707 }
1708 if (!key1)
1709 key1 = g_strdup ("");
1710
1711 /* Extract second key */
1712 key2 = ebsql_decode_vcard_sort_key_from_vcard (E_VCARD (contact2));
1713 if (!key2) {
1714 tmp = e_contact_get (contact2, data->field);
1715 if (tmp)
1716 key2 = e_collator_generate_key (priv->collator, tmp, NULL);
1717 g_free (tmp);
1718 }
1719 if (!key2)
1720 key2 = g_strdup ("");
1721
1722 result = strcmp (key1, key2);
1723
1724 g_free (key1);
1725 g_free (key2);
1726 g_object_unref (contact1);
1727 g_object_unref (contact2);
1728
1729 return result;
1730 }
1731
1732 static EbSqlCollData *
ebsql_coll_data_new(EBookSqlite * ebsql,EContactField field)1733 ebsql_coll_data_new (EBookSqlite *ebsql,
1734 EContactField field)
1735 {
1736 EbSqlCollData *data = g_slice_new (EbSqlCollData);
1737
1738 data->ebsql = ebsql;
1739 data->field = field;
1740
1741 return data;
1742 }
1743
1744 static void
ebsql_coll_data_free(EbSqlCollData * data)1745 ebsql_coll_data_free (EbSqlCollData *data)
1746 {
1747 if (data)
1748 g_slice_free (EbSqlCollData, data);
1749 }
1750
1751 /* COLLATE functions are generated on demand only */
1752 static void
ebsql_generate_collator(gpointer ref,sqlite3 * db,gint eTextRep,const gchar * coll_name)1753 ebsql_generate_collator (gpointer ref,
1754 sqlite3 *db,
1755 gint eTextRep,
1756 const gchar *coll_name)
1757 {
1758 EBookSqlite *ebsql = (EBookSqlite *) ref;
1759 EbSqlCollData *data;
1760 EContactField field;
1761 const gchar *field_name;
1762
1763 field_name = coll_name + strlen (EBSQL_COLLATE_PREFIX);
1764 field = e_contact_field_id (field_name);
1765
1766 /* This should be caught before reaching here, just an extra check */
1767 if (field == 0 || field >= E_CONTACT_FIELD_LAST ||
1768 e_contact_field_type (field) != G_TYPE_STRING) {
1769 g_warning ("Specified collation on invalid contact field");
1770 return;
1771 }
1772
1773 data = ebsql_coll_data_new (ebsql, field);
1774 sqlite3_create_collation_v2 (
1775 db, coll_name, SQLITE_UTF8,
1776 data, ebsql_fallback_collator,
1777 (GDestroyNotify) ebsql_coll_data_free);
1778 }
1779
1780 /**********************************************************
1781 * Cancel long operations with GCancellable *
1782 **********************************************************/
1783 static gint
ebsql_check_cancel(gpointer ref)1784 ebsql_check_cancel (gpointer ref)
1785 {
1786 EBookSqlite *ebsql = (EBookSqlite *) ref;
1787
1788 if (ebsql->priv->cancel &&
1789 g_cancellable_is_cancelled (ebsql->priv->cancel)) {
1790 EBSQL_NOTE (
1791 CANCEL,
1792 g_printerr ("CANCEL: An operation was cancelled\n"));
1793 return -1;
1794 }
1795
1796 return 0;
1797 }
1798
1799 /**********************************************************
1800 * Database Initialization *
1801 **********************************************************/
1802 static inline gint
main_table_index_by_name(const gchar * name)1803 main_table_index_by_name (const gchar *name)
1804 {
1805 gint i;
1806
1807 for (i = 0; i < G_N_ELEMENTS (main_table_columns); i++) {
1808 if (g_strcmp0 (name, main_table_columns[i].name) == 0)
1809 return i;
1810 }
1811
1812 return -1;
1813 }
1814
1815 static gint
check_main_table_columns(gpointer data,gint n_cols,gchar ** cols,gchar ** name)1816 check_main_table_columns (gpointer data,
1817 gint n_cols,
1818 gchar **cols,
1819 gchar **name)
1820 {
1821 guint *columns_mask = (guint *) data;
1822 gint i;
1823
1824 for (i = 0; i < n_cols; i++) {
1825
1826 if (g_strcmp0 (name[i], "name") == 0) {
1827 gint idx = main_table_index_by_name (cols[i]);
1828
1829 if (idx >= 0)
1830 *columns_mask |= (1 << idx);
1831
1832 break;
1833 }
1834 }
1835
1836 return 0;
1837 }
1838
1839 static gboolean
ebsql_init_sqlite(EBookSqlite * ebsql,const gchar * filename,GError ** error)1840 ebsql_init_sqlite (EBookSqlite *ebsql,
1841 const gchar *filename,
1842 GError **error)
1843 {
1844 gint ret, i;
1845
1846 e_sqlite3_vfs_init ();
1847
1848 ret = sqlite3_open (filename, &ebsql->priv->db);
1849
1850 /* Handle GCancellable */
1851 sqlite3_progress_handler (
1852 ebsql->priv->db,
1853 EBSQL_CANCEL_BATCH_SIZE,
1854 ebsql_check_cancel,
1855 ebsql);
1856
1857 /* Install our custom functions */
1858 for (i = 0; ret == SQLITE_OK && i < G_N_ELEMENTS (ebsql_custom_functions); i++)
1859 ret = sqlite3_create_function (
1860 ebsql->priv->db,
1861 ebsql_custom_functions[i].name,
1862 ebsql_custom_functions[i].arguments,
1863 SQLITE_UTF8, ebsql,
1864 ebsql_custom_functions[i].func,
1865 NULL, NULL);
1866
1867 /* Fallback COLLATE implementations generated on demand */
1868 if (ret == SQLITE_OK)
1869 ret = sqlite3_collation_needed (
1870 ebsql->priv->db, ebsql, ebsql_generate_collator);
1871
1872 if (ret != SQLITE_OK) {
1873 if (!ebsql->priv->db) {
1874 EBSQL_SET_ERROR_LITERAL (
1875 error,
1876 E_BOOK_SQLITE_ERROR_LOAD,
1877 _("Insufficient memory"));
1878 } else {
1879 const gchar *errmsg = sqlite3_errmsg (ebsql->priv->db);
1880
1881 EBSQL_SET_ERROR (
1882 error,
1883 E_BOOK_SQLITE_ERROR_ENGINE,
1884 "Can't open database %s: %s\n",
1885 filename, errmsg);
1886 sqlite3_close (ebsql->priv->db);
1887 }
1888 return FALSE;
1889 }
1890
1891 ebsql_exec (ebsql, "ATTACH DATABASE ':memory:' AS mem", NULL, NULL, NULL, NULL);
1892 ebsql_exec (ebsql, "PRAGMA foreign_keys = ON", NULL, NULL, NULL, NULL);
1893 ebsql_exec (ebsql, "PRAGMA case_sensitive_like = ON", NULL, NULL, NULL, NULL);
1894
1895 return TRUE;
1896 }
1897
1898 static inline void
format_column_declaration(GString * string,ColumnInfo * info)1899 format_column_declaration (GString *string,
1900 ColumnInfo *info)
1901 {
1902 g_string_append (string, info->name);
1903 g_string_append_c (string, ' ');
1904
1905 g_string_append (string, info->type);
1906
1907 if (info->extra) {
1908 g_string_append_c (string, ' ');
1909 g_string_append (string, info->extra);
1910 }
1911 }
1912
1913 static inline gboolean
ensure_column_index(EBookSqlite * ebsql,const gchar * table,ColumnInfo * info,GError ** error)1914 ensure_column_index (EBookSqlite *ebsql,
1915 const gchar *table,
1916 ColumnInfo *info,
1917 GError **error)
1918 {
1919 if (!info->index)
1920 return TRUE;
1921
1922 return ebsql_exec_printf (
1923 ebsql,
1924 "CREATE INDEX IF NOT EXISTS %Q ON %Q (%s)",
1925 NULL, NULL, NULL, error,
1926 info->index, table, info->name);
1927 }
1928
1929 /* Called with the lock held and inside a transaction */
1930 static gboolean
ebsql_resolve_folderid(EBookSqlite * ebsql,gint * previous_schema,gint * already_exists,GError ** error)1931 ebsql_resolve_folderid (EBookSqlite *ebsql,
1932 gint *previous_schema,
1933 gint *already_exists,
1934 GError **error)
1935 {
1936 gint n_folders = 0;
1937 gint version = 0;
1938 gchar *loaded_folder_id = NULL;
1939 gboolean success;
1940
1941 success = ebsql_exec (
1942 ebsql, "SELECT count(*) FROM sqlite_master "
1943 "WHERE type='table' AND name='folders';",
1944 get_count_cb, &n_folders, NULL, error);
1945
1946 if (success && n_folders > 1) {
1947 EBSQL_SET_ERROR_LITERAL (
1948 error,
1949 E_BOOK_SQLITE_ERROR_LOAD,
1950 _("Cannot upgrade contacts database from a legacy "
1951 "database with more than one addressbook. "
1952 "Delete one of the entries in the “folders” table first."));
1953 success = FALSE;
1954 }
1955
1956 if (success && n_folders == 1)
1957 success = ebsql_exec (
1958 ebsql, "SELECT folder_id FROM folders LIMIT 1",
1959 get_string_cb, &loaded_folder_id, NULL, error);
1960
1961 if (success && n_folders == 1)
1962 success = ebsql_exec (
1963 ebsql, "SELECT version FROM folders LIMIT 1",
1964 get_int_cb, &version, NULL, error);
1965
1966 if (success && n_folders == 1) {
1967 g_free (ebsql->priv->folderid);
1968 ebsql->priv->folderid = loaded_folder_id;
1969 } else {
1970 g_free (loaded_folder_id);
1971 }
1972
1973 if (n_folders == 1)
1974 *already_exists = TRUE;
1975 else
1976 *already_exists = FALSE;
1977
1978 EBSQL_NOTE (
1979 SCHEMA,
1980 g_printerr (
1981 "SCHEMA: main folder id resolved as '%s', "
1982 "already existing tables: %d loaded version: %d (%s)\n",
1983 ebsql->priv->folderid, n_folders, version,
1984 success ? "success" : "failed"));
1985
1986 *previous_schema = version;
1987
1988 return success;
1989 }
1990
1991 /* Called with the lock held and inside a transaction */
1992 static gboolean
ebsql_init_folders(EBookSqlite * ebsql,gint previous_schema,GError ** error)1993 ebsql_init_folders (EBookSqlite *ebsql,
1994 gint previous_schema,
1995 GError **error)
1996 {
1997 GString *string;
1998 guint existing_columns_mask = 0, i;
1999 gboolean success;
2000
2001 string = g_string_sized_new (COLUMN_DEFINITION_BYTES * G_N_ELEMENTS (main_table_columns));
2002 g_string_append (string, "CREATE TABLE IF NOT EXISTS folders (");
2003 for (i = 0; i < G_N_ELEMENTS (main_table_columns); i++) {
2004
2005 if (i > 0)
2006 g_string_append (string, ", ");
2007
2008 format_column_declaration (string, &(main_table_columns[i]));
2009 }
2010 g_string_append_c (string, ')');
2011
2012 /* Create main folders table */
2013 success = ebsql_exec (ebsql, string->str, NULL, NULL, NULL, error);
2014 g_string_free (string, TRUE);
2015
2016 /* Check which columns in the main table already exist */
2017 if (success)
2018 success = ebsql_exec (
2019 ebsql, "PRAGMA table_info (folders)",
2020 check_main_table_columns, &existing_columns_mask,
2021 NULL, error);
2022
2023 /* Add columns which may be missing */
2024 for (i = 0; success && i < G_N_ELEMENTS (main_table_columns); i++) {
2025 ColumnInfo *info = &(main_table_columns[i]);
2026
2027 if ((existing_columns_mask & (1 << i)) != 0)
2028 continue;
2029
2030 success = ebsql_exec_printf (
2031 ebsql, "ALTER TABLE folders ADD COLUMN %s %s %s",
2032 NULL, NULL, NULL, error, info->name, info->type,
2033 info->extra ? info->extra : "");
2034 }
2035
2036 /* Special case upgrade for schema versions 3 & 4.
2037 *
2038 * Drops the reverse_multivalues column.
2039 */
2040 if (success && previous_schema >= 3 && previous_schema < 5) {
2041
2042 success = ebsql_exec (
2043 ebsql,
2044 "UPDATE folders SET "
2045 "multivalues = REPLACE(RTRIM(REPLACE("
2046 "multivalues || ':', ':', "
2047 "CASE reverse_multivalues "
2048 "WHEN 0 THEN ';prefix ' "
2049 "ELSE ';prefix;suffix ' "
2050 "END)), ' ', ':'), "
2051 "reverse_multivalues = NULL",
2052 NULL, NULL, NULL, error);
2053 }
2054
2055 /* Finish the eventual upgrade by storing the current schema version.
2056 */
2057 if (success && previous_schema >= 1 && previous_schema < FOLDER_VERSION)
2058 success = ebsql_exec_printf (
2059 ebsql, "UPDATE folders SET version = %d",
2060 NULL, NULL, NULL, error, FOLDER_VERSION);
2061
2062 EBSQL_NOTE (
2063 SCHEMA,
2064 g_printerr (
2065 "SCHEMA: Initialized main folders table (%s)\n",
2066 success ? "success" : "failed"));
2067
2068 return success;
2069 }
2070
2071 /* Called with the lock held and inside a transaction */
2072 static gboolean
ebsql_init_keys(EBookSqlite * ebsql,GError ** error)2073 ebsql_init_keys (EBookSqlite *ebsql,
2074 GError **error)
2075 {
2076 gboolean success;
2077
2078 /* Create a child table to store key/value pairs for a folder. */
2079 success = ebsql_exec (
2080 ebsql,
2081 "CREATE TABLE IF NOT EXISTS keys ("
2082 " key TEXT PRIMARY KEY,"
2083 " value TEXT,"
2084 " folder_id TEXT REFERENCES folders)",
2085 NULL, NULL, NULL, error);
2086
2087 /* Add an index on the keys */
2088 if (success)
2089 success = ebsql_exec (
2090 ebsql,
2091 "CREATE INDEX IF NOT EXISTS keysindex ON keys (folder_id)",
2092 NULL, NULL, NULL, error);
2093
2094 EBSQL_NOTE (
2095 SCHEMA,
2096 g_printerr (
2097 "SCHEMA: Initialized keys table (%s)\n",
2098 success ? "success" : "failed"));
2099
2100 return success;
2101 }
2102
2103 static gchar *
format_multivalues(EBookSqlite * ebsql)2104 format_multivalues (EBookSqlite *ebsql)
2105 {
2106 gint i;
2107 GString *string;
2108 gboolean first = TRUE;
2109
2110 string = g_string_new (NULL);
2111
2112 for (i = 0; i < ebsql->priv->n_summary_fields; i++) {
2113 if (ebsql->priv->summary_fields[i].type == E_TYPE_CONTACT_ATTR_LIST) {
2114 if (first)
2115 first = FALSE;
2116 else
2117 g_string_append_c (string, ':');
2118
2119 g_string_append (string, ebsql->priv->summary_fields[i].dbname);
2120
2121 /* E_BOOK_INDEX_SORT_KEY is not supported in the multivalue fields */
2122 if ((ebsql->priv->summary_fields[i].index & INDEX_FLAG (PREFIX)) != 0)
2123 g_string_append (string, ";prefix");
2124 if ((ebsql->priv->summary_fields[i].index & INDEX_FLAG (SUFFIX)) != 0)
2125 g_string_append (string, ";suffix");
2126 if ((ebsql->priv->summary_fields[i].index & INDEX_FLAG (PHONE)) != 0)
2127 g_string_append (string, ";phone");
2128 }
2129 }
2130
2131 return g_string_free (string, string->len == 0);
2132 }
2133
2134 /* Called with the lock held and inside a transaction */
2135 static gboolean
ebsql_add_folder(EBookSqlite * ebsql,GError ** error)2136 ebsql_add_folder (EBookSqlite *ebsql,
2137 GError **error)
2138 {
2139 gboolean success;
2140 gchar *multivalues;
2141 const gchar *lc_collate;
2142
2143 multivalues = format_multivalues (ebsql);
2144 lc_collate = setlocale (LC_COLLATE, NULL);
2145
2146 success = ebsql_exec_printf (
2147 ebsql,
2148 "INSERT OR IGNORE INTO folders"
2149 " ( folder_id, version, multivalues, lc_collate ) "
2150 "VALUES ( %Q, %d, %Q, %Q ) ",
2151 NULL, NULL, NULL, error,
2152 ebsql->priv->folderid, FOLDER_VERSION, multivalues, lc_collate);
2153
2154 g_free (multivalues);
2155
2156 EBSQL_NOTE (
2157 SCHEMA,
2158 g_printerr (
2159 "SCHEMA: Added '%s' entry to main folder (%s)\n",
2160 ebsql->priv->folderid, success ? "success" : "failed"));
2161
2162 return success;
2163 }
2164
2165 static gboolean
ebsql_email_list_exists(EBookSqlite * ebsql)2166 ebsql_email_list_exists (EBookSqlite *ebsql)
2167 {
2168 gint n_tables = 0;
2169 gboolean success;
2170
2171 success = ebsql_exec_printf (
2172 ebsql, "SELECT count(*) FROM sqlite_master WHERE type='table' AND name='%q_email_list';",
2173 get_count_cb, &n_tables, NULL, NULL,
2174 ebsql->priv->folderid);
2175
2176 if (!success)
2177 return FALSE;
2178
2179 return n_tables == 1;
2180 }
2181
2182 /* Called with the lock held and inside a transaction */
2183 static gboolean
ebsql_introspect_summary(EBookSqlite * ebsql,gint previous_schema,GSList ** introspected_columns,GError ** error)2184 ebsql_introspect_summary (EBookSqlite *ebsql,
2185 gint previous_schema,
2186 GSList **introspected_columns,
2187 GError **error)
2188 {
2189 gboolean success;
2190 GSList *summary_columns = NULL, *l;
2191 GArray *summary_fields = NULL;
2192 gchar *multivalues = NULL;
2193 gint i, j;
2194
2195 success = ebsql_exec_printf (
2196 ebsql, "PRAGMA table_info (%Q);",
2197 get_columns_cb, &summary_columns, NULL, error,
2198 ebsql->priv->folderid);
2199
2200 if (!success)
2201 goto introspect_summary_finish;
2202
2203 summary_columns = g_slist_reverse (summary_columns);
2204 summary_fields = g_array_new (FALSE, FALSE, sizeof (SummaryField));
2205
2206 /* Introspect the normal summary fields */
2207 for (l = summary_columns; l; l = l->next) {
2208 EContactField field_id;
2209 const gchar *col = l->data;
2210 gchar *p;
2211 gint computed = 0;
2212 gchar *freeme = NULL;
2213
2214 /* Note that we don't have any way to introspect
2215 * E_BOOK_INDEX_PREFIX, this is not important because if
2216 * the prefix index is specified, it will be created
2217 * the first time the SQLite tables are created, so
2218 * it's not important to ensure prefix indexes after
2219 * introspecting the summary.
2220 */
2221
2222 /* Check if we're parsing a reverse field */
2223 if ((p = strstr (col, "_" EBSQL_SUFFIX_REVERSE)) != NULL) {
2224 computed = INDEX_FLAG (SUFFIX);
2225 freeme = g_strndup (col, p - col);
2226 col = freeme;
2227 } else if ((p = strstr (col, "_" EBSQL_SUFFIX_PHONE)) != NULL) {
2228 computed = INDEX_FLAG (PHONE);
2229 freeme = g_strndup (col, p - col);
2230 col = freeme;
2231 } else if ((p = strstr (col, "_" EBSQL_SUFFIX_COUNTRY)) != NULL) {
2232 computed = INDEX_FLAG (PHONE);
2233 freeme = g_strndup (col, p - col);
2234 col = freeme;
2235 } else if ((p = strstr (col, "_" EBSQL_SUFFIX_SORT_KEY)) != NULL) {
2236 computed = INDEX_FLAG (SORT_KEY);
2237 freeme = g_strndup (col, p - col);
2238 col = freeme;
2239 }
2240
2241 /* First check exception fields */
2242 if (g_ascii_strcasecmp (col, "uid") == 0)
2243 field_id = E_CONTACT_UID;
2244 else if (g_ascii_strcasecmp (col, "is_list") == 0)
2245 field_id = E_CONTACT_IS_LIST;
2246 else
2247 field_id = e_contact_field_id (col);
2248
2249 /* Check for parse error */
2250 if (field_id == 0) {
2251 EBSQL_SET_ERROR (
2252 error,
2253 E_BOOK_SQLITE_ERROR_UNSUPPORTED_FIELD,
2254 _("Error introspecting unknown summary field “%s”"),
2255 col);
2256 success = FALSE;
2257 g_free (freeme);
2258 break;
2259 }
2260
2261 /* Computed columns are always declared after the normal columns,
2262 * if a reverse field is encountered we need to set the suffix
2263 * index on the coresponding summary field
2264 */
2265 if (computed) {
2266 gint field_idx;
2267 SummaryField *iter;
2268
2269 field_idx = summary_field_array_index (summary_fields, field_id);
2270 if (field_idx >= 0) {
2271 iter = &g_array_index (summary_fields, SummaryField, field_idx);
2272 iter->index |= computed;
2273 }
2274
2275 } else {
2276 summary_field_append (
2277 summary_fields, ebsql->priv->folderid,
2278 field_id, NULL);
2279 }
2280
2281 g_free (freeme);
2282 }
2283
2284 if (!success)
2285 goto introspect_summary_finish;
2286
2287 /* Introspect the multivalied summary fields */
2288 success = ebsql_exec_printf (
2289 ebsql,
2290 "SELECT multivalues FROM folders "
2291 "WHERE folder_id = %Q",
2292 get_string_cb, &multivalues, NULL, error,
2293 ebsql->priv->folderid);
2294
2295 if (!success)
2296 goto introspect_summary_finish;
2297
2298 if (!multivalues || !*multivalues) {
2299 g_free (multivalues);
2300 multivalues = NULL;
2301
2302 /* The migration from a previous version didn't store this default multivalue
2303 reference, thus the next backend open (not the immediate one after migration),
2304 didn't know about this table, which has a FOREIGN KEY constraint, thus an item
2305 delete caused a 'FOREIGN KEY constraint failed' error.
2306 */
2307 if (ebsql_email_list_exists (ebsql))
2308 multivalues = g_strdup ("email;prefix");
2309 }
2310
2311 if (multivalues) {
2312 gchar **fields = g_strsplit (multivalues, ":", 0);
2313
2314 for (i = 0; fields[i] != NULL; i++) {
2315 EContactField field_id;
2316 SummaryField *iter;
2317 gchar **params;
2318
2319 params = g_strsplit (fields[i], ";", 0);
2320 field_id = e_contact_field_id (params[0]);
2321 iter = summary_field_append (
2322 summary_fields,
2323 ebsql->priv->folderid,
2324 field_id, NULL);
2325
2326 if (iter) {
2327 for (j = 1; params[j]; ++j) {
2328 /* Sort keys not supported for multivalued fields */
2329 if (strcmp (params[j], "prefix") == 0) {
2330 iter->index |= INDEX_FLAG (PREFIX);
2331 } else if (strcmp (params[j], "suffix") == 0) {
2332 iter->index |= INDEX_FLAG (SUFFIX);
2333 } else if (strcmp (params[j], "phone") == 0) {
2334 iter->index |= INDEX_FLAG (PHONE);
2335 }
2336 }
2337 }
2338
2339 g_strfreev (params);
2340 }
2341
2342 g_strfreev (fields);
2343 }
2344
2345 /* HARD CODE UP AHEAD
2346 *
2347 * Now we're finished introspecting, if the summary is from a previous version,
2348 * we need to add any summary fields which we're added to the default summary
2349 * since the schema version which was introduced here
2350 */
2351 if (previous_schema >= 1) {
2352 SummaryField *summary_field;
2353
2354 if (previous_schema < 8) {
2355
2356 /* We used to keep 4 email fields in the summary, before we supported
2357 * the multivaliued E_CONTACT_EMAIL... convert the old summary to use
2358 * the multivaliued field instead.
2359 */
2360 if (summary_field_array_index (summary_fields, E_CONTACT_EMAIL_1) >= 0 &&
2361 summary_field_array_index (summary_fields, E_CONTACT_EMAIL_2) >= 0 &&
2362 summary_field_array_index (summary_fields, E_CONTACT_EMAIL_3) >= 0 &&
2363 summary_field_array_index (summary_fields, E_CONTACT_EMAIL_4) >= 0) {
2364
2365 summary_field_remove (summary_fields, E_CONTACT_EMAIL_1);
2366 summary_field_remove (summary_fields, E_CONTACT_EMAIL_2);
2367 summary_field_remove (summary_fields, E_CONTACT_EMAIL_3);
2368 summary_field_remove (summary_fields, E_CONTACT_EMAIL_4);
2369
2370 summary_field = summary_field_append (
2371 summary_fields,
2372 ebsql->priv->folderid,
2373 E_CONTACT_EMAIL, NULL);
2374 summary_field->index |= INDEX_FLAG (PREFIX);
2375 }
2376
2377 /* Regardless of whether it was a default summary or not, add the sort
2378 * keys to anything less than Schema 8 (as long as those fields are at least
2379 * in the summary)
2380 */
2381 if ((i = summary_field_array_index (summary_fields, E_CONTACT_FILE_AS)) >= 0) {
2382 summary_field = &g_array_index (summary_fields, SummaryField, i);
2383 summary_field->index |= INDEX_FLAG (SORT_KEY);
2384 }
2385
2386 if ((i = summary_field_array_index (summary_fields, E_CONTACT_GIVEN_NAME)) >= 0) {
2387 summary_field = &g_array_index (summary_fields, SummaryField, i);
2388 summary_field->index |= INDEX_FLAG (SORT_KEY);
2389 }
2390
2391 if ((i = summary_field_array_index (summary_fields, E_CONTACT_FAMILY_NAME)) >= 0) {
2392 summary_field = &g_array_index (summary_fields, SummaryField, i);
2393 summary_field->index |= INDEX_FLAG (SORT_KEY);
2394 }
2395 }
2396
2397 if (previous_schema < 9) {
2398 if (summary_field_array_index (summary_fields, E_CONTACT_X509_CERT) < 0) {
2399 summary_field_append (summary_fields, ebsql->priv->folderid,
2400 E_CONTACT_X509_CERT, NULL);
2401 }
2402 }
2403
2404 if (previous_schema < 10) {
2405 if ((i = summary_field_array_index (summary_fields, E_CONTACT_NICKNAME)) >= 0) {
2406 summary_field = &g_array_index (summary_fields, SummaryField, i);
2407 summary_field->index |= INDEX_FLAG (PREFIX);
2408 }
2409
2410 if ((i = summary_field_array_index (summary_fields, E_CONTACT_FILE_AS)) >= 0) {
2411 summary_field = &g_array_index (summary_fields, SummaryField, i);
2412 summary_field->index |= INDEX_FLAG (PREFIX);
2413 }
2414
2415 if ((i = summary_field_array_index (summary_fields, E_CONTACT_GIVEN_NAME)) >= 0) {
2416 summary_field = &g_array_index (summary_fields, SummaryField, i);
2417 summary_field->index |= INDEX_FLAG (PREFIX);
2418 }
2419
2420 if ((i = summary_field_array_index (summary_fields, E_CONTACT_FAMILY_NAME)) >= 0) {
2421 summary_field = &g_array_index (summary_fields, SummaryField, i);
2422 summary_field->index |= INDEX_FLAG (PREFIX);
2423 }
2424
2425 }
2426
2427 if (previous_schema < 12) {
2428 if (summary_field_array_index (summary_fields, E_CONTACT_PGP_CERT) < 0) {
2429 summary_field_append (summary_fields, ebsql->priv->folderid,
2430 E_CONTACT_PGP_CERT, NULL);
2431 }
2432 }
2433 }
2434
2435 introspect_summary_finish:
2436
2437 /* Apply the introspected summary fields */
2438 if (success) {
2439 summary_fields_array_free (
2440 ebsql->priv->summary_fields,
2441 ebsql->priv->n_summary_fields);
2442
2443 ebsql->priv->n_summary_fields = summary_fields->len;
2444 ebsql->priv->summary_fields = (SummaryField *) g_array_free (summary_fields, FALSE);
2445
2446 *introspected_columns = summary_columns;
2447 } else if (summary_fields) {
2448 gint n_fields;
2449 SummaryField *fields;
2450
2451 /* Properly free the array */
2452 n_fields = summary_fields->len;
2453 fields = (SummaryField *) g_array_free (summary_fields, FALSE);
2454 summary_fields_array_free (fields, n_fields);
2455
2456 g_slist_free_full (summary_columns, (GDestroyNotify) g_free);
2457 }
2458
2459 g_free (multivalues);
2460
2461 EBSQL_NOTE (
2462 SCHEMA,
2463 g_printerr (
2464 "SCHEMA: Introspected summary (%s)\n",
2465 success ? "success" : "failed"));
2466
2467 return success;
2468 }
2469
2470 /* Called with the lock held and inside a transaction */
2471 static gboolean
ebsql_init_contacts(EBookSqlite * ebsql,GSList * introspected_columns,GError ** error)2472 ebsql_init_contacts (EBookSqlite *ebsql,
2473 GSList *introspected_columns,
2474 GError **error)
2475 {
2476 gint i;
2477 gboolean success = TRUE;
2478 GString *string;
2479 GSList *summary_columns = NULL, *l;
2480
2481 /* Get a list of all columns and indexes which should be present
2482 * in the main summary table */
2483 for (i = 0; i < ebsql->priv->n_summary_fields; i++) {
2484 SummaryField *field = &(ebsql->priv->summary_fields[i]);
2485
2486 if (field->type != E_TYPE_CONTACT_ATTR_LIST) {
2487 l = summary_field_list_columns (field, ebsql->priv->folderid);
2488 summary_columns = g_slist_concat (summary_columns, l);
2489 }
2490 }
2491
2492 /* Create the main contacts table for this folder
2493 */
2494 string = g_string_sized_new (32 * g_slist_length (summary_columns));
2495 g_string_append (string, "CREATE TABLE IF NOT EXISTS %Q (");
2496
2497 for (l = summary_columns; l; l = l->next) {
2498 ColumnInfo *info = l->data;
2499
2500 if (l != summary_columns)
2501 g_string_append (string, ", ");
2502
2503 format_column_declaration (string, info);
2504 }
2505 g_string_append (string, ", vcard TEXT, bdata TEXT)");
2506
2507 success = ebsql_exec_printf (
2508 ebsql, string->str,
2509 NULL, NULL, NULL, error,
2510 ebsql->priv->folderid);
2511
2512 g_string_free (string, TRUE);
2513
2514 /* If we introspected something, let's first adjust the contacts table
2515 * so that it includes the right columns */
2516 if (introspected_columns) {
2517
2518 /* Add any missing columns which are in the summary fields but
2519 * not found in the contacts table
2520 */
2521 for (l = summary_columns; success && l; l = l->next) {
2522 ColumnInfo *info = l->data;
2523
2524 if (g_slist_find_custom (introspected_columns,
2525 info->name, (GCompareFunc) g_ascii_strcasecmp))
2526 continue;
2527
2528 success = ebsql_exec_printf (
2529 ebsql,
2530 "ALTER TABLE %Q ADD COLUMN %s %s %s",
2531 NULL, NULL, NULL, error,
2532 ebsql->priv->folderid,
2533 info->name, info->type,
2534 info->extra ? info->extra : "");
2535 }
2536 }
2537
2538 /* Add indexes to columns in the main contacts table
2539 */
2540 for (l = summary_columns; success && l; l = l->next) {
2541 ColumnInfo *info = l->data;
2542
2543 success = ensure_column_index (ebsql, ebsql->priv->folderid, info, error);
2544 }
2545
2546 g_slist_free_full (summary_columns, (GDestroyNotify) column_info_free);
2547
2548 EBSQL_NOTE (
2549 SCHEMA,
2550 g_printerr (
2551 "SCHEMA: Initialized summary table '%s' (%s)\n",
2552 ebsql->priv->folderid, success ? "success" : "failed"));
2553
2554 return success;
2555 }
2556
2557 /* Called with the lock held and inside a transaction */
2558 static gboolean
ebsql_init_aux_tables(EBookSqlite * ebsql,gint previous_schema,GError ** error)2559 ebsql_init_aux_tables (EBookSqlite *ebsql,
2560 gint previous_schema,
2561 GError **error)
2562 {
2563 GString *string;
2564 gboolean success = TRUE;
2565 GSList *aux_columns = NULL, *l;
2566 gchar *tmp;
2567 gint i;
2568
2569 /* Drop the general 'folder_id_lists' table which was used prior to
2570 * version 8 of the schema
2571 */
2572 if (previous_schema >= 1 && previous_schema < 8) {
2573 tmp = g_strconcat (ebsql->priv->folderid, "_lists", NULL);
2574 success = ebsql_exec_printf (
2575 ebsql, "DROP TABLE IF EXISTS %Q",
2576 NULL, NULL, NULL, error, tmp);
2577 g_free (tmp);
2578 }
2579
2580 for (i = 0; success && i < ebsql->priv->n_summary_fields; i++) {
2581 SummaryField *field = &(ebsql->priv->summary_fields[i]);
2582
2583 if (field->type != E_TYPE_CONTACT_ATTR_LIST)
2584 continue;
2585
2586 aux_columns = summary_field_list_columns (field, ebsql->priv->folderid);
2587
2588 /* Create the auxiliary table for this multi valued field */
2589 string = g_string_sized_new (
2590 COLUMN_DEFINITION_BYTES * 3 +
2591 COLUMN_DEFINITION_BYTES * g_slist_length (aux_columns));
2592
2593 g_string_append (string, "CREATE TABLE IF NOT EXISTS %Q (uid TEXT NOT NULL REFERENCES %Q (uid)");
2594 for (l = aux_columns; l; l = l->next) {
2595 ColumnInfo *info = l->data;
2596
2597 g_string_append (string, ", ");
2598 format_column_declaration (string, info);
2599 }
2600 g_string_append_c (string, ')');
2601
2602 success = ebsql_exec_printf (
2603 ebsql, string->str, NULL, NULL, NULL, error,
2604 field->aux_table, ebsql->priv->folderid);
2605 g_string_free (string, TRUE);
2606
2607 if (success) {
2608
2609 /* Create an index on the implied 'uid' column, this is important
2610 * when replacing (modifying) contacts, since we need to remove
2611 * all rows in an auxiliary table which matches a given UID.
2612 *
2613 * This index speeds up the constraint in a statement such as:
2614 *
2615 * DELETE from email_list WHERE email_list.uid = 'contact uid'
2616 */
2617 tmp = g_strconcat (
2618 "UID_INDEX",
2619 "_", field->dbname,
2620 "_", ebsql->priv->folderid,
2621 NULL);
2622 ebsql_exec_printf (
2623 ebsql,
2624 "CREATE INDEX IF NOT EXISTS %Q ON %Q (%s)",
2625 NULL, NULL, NULL, error,
2626 tmp, field->aux_table, "uid");
2627 g_free (tmp);
2628 }
2629
2630 /* Add indexes to columns in this auxiliary table
2631 */
2632 for (l = aux_columns; success && l; l = l->next) {
2633 ColumnInfo *info = l->data;
2634
2635 success = ensure_column_index (ebsql, field->aux_table, info, error);
2636 }
2637
2638 g_slist_free_full (aux_columns, (GDestroyNotify) column_info_free);
2639
2640 EBSQL_NOTE (
2641 SCHEMA,
2642 g_printerr (
2643 "SCHEMA: Initialized auxiliary table '%s'\n",
2644 field->aux_table));
2645 }
2646
2647 if (success) {
2648 gchar *multivalues;
2649
2650 multivalues = format_multivalues (ebsql);
2651
2652 success = ebsql_exec_printf (
2653 ebsql,
2654 "UPDATE folders SET multivalues=%Q WHERE folder_id=%Q",
2655 NULL, NULL, NULL, error,
2656 multivalues, ebsql->priv->folderid);
2657
2658 g_free (multivalues);
2659 }
2660
2661 EBSQL_NOTE (
2662 SCHEMA,
2663 g_printerr (
2664 "SCHEMA: Initialized auxiliary tables (%s)\n",
2665 success ? "success" : "failed"));
2666
2667 return success;
2668 }
2669
2670 static gboolean
ebsql_upgrade_one(EBookSqlite * ebsql,EbSqlChangeType change_type,EbSqlSearchData * result,GError ** error)2671 ebsql_upgrade_one (EBookSqlite *ebsql,
2672 EbSqlChangeType change_type,
2673 EbSqlSearchData *result,
2674 GError **error)
2675 {
2676 EContact *contact = NULL;
2677 gboolean success;
2678
2679 /* It can be we're opening a light summary which was created without
2680 * storing the vcards, such as was used in EDS versions 3.2 to 3.6.
2681 *
2682 * In this case we just want to skip the contacts we can't load
2683 * and leave them as is in the SQLite, they will be added from
2684 * the old BDB in the case of a migration anyway.
2685 */
2686 if (result->vcard)
2687 contact = e_contact_new_from_vcard_with_uid (result->vcard, result->uid);
2688
2689 if (contact == NULL)
2690 return TRUE;
2691
2692 success = ebsql_insert_contact (
2693 ebsql, change_type, contact,
2694 result->vcard, result->extra,
2695 TRUE, error);
2696
2697 g_object_unref (contact);
2698
2699 return success;
2700 }
2701
2702 /* Called with the lock held and inside a transaction */
2703 static gboolean
ebsql_upgrade(EBookSqlite * ebsql,EbSqlChangeType change_type,GError ** error)2704 ebsql_upgrade (EBookSqlite *ebsql,
2705 EbSqlChangeType change_type,
2706 GError **error)
2707 {
2708 gchar *uid = NULL;
2709 gint n_results;
2710 gboolean success = TRUE;
2711
2712 do {
2713 GSList *batch = NULL, *l;
2714 EbSqlSearchData *result = NULL;
2715
2716 if (uid == NULL) {
2717 success = ebsql_exec_printf (
2718 ebsql,
2719 "SELECT summary.uid, %s, summary.bdata FROM %Q AS summary "
2720 "ORDER BY summary.uid ASC LIMIT %d",
2721 collect_full_results_cb, &batch, NULL, error,
2722 EBSQL_VCARD_FRAGMENT (ebsql),
2723 ebsql->priv->folderid, EBSQL_UPGRADE_BATCH_SIZE);
2724 } else {
2725 success = ebsql_exec_printf (
2726 ebsql,
2727 "SELECT summary.uid, %s, summary.bdata FROM %Q AS summary "
2728 "WHERE summary.uid > %Q "
2729 "ORDER BY summary.uid ASC LIMIT %d",
2730 collect_full_results_cb, &batch, NULL, error,
2731 EBSQL_VCARD_FRAGMENT (ebsql),
2732 ebsql->priv->folderid, uid, EBSQL_UPGRADE_BATCH_SIZE);
2733 }
2734
2735 /* Reverse the list, we want to walk through it forwards */
2736 batch = g_slist_reverse (batch);
2737 for (l = batch; success && l; l = l->next) {
2738 result = l->data;
2739 success = ebsql_upgrade_one (
2740 ebsql,
2741 change_type,
2742 result,
2743 error);
2744 }
2745
2746 /* result is now the last one in the list */
2747 if (result) {
2748 g_free (uid);
2749 uid = result->uid;
2750 result->uid = NULL;
2751 }
2752
2753 n_results = g_slist_length (batch);
2754 g_slist_free_full (batch, (GDestroyNotify) e_book_sqlite_search_data_free);
2755
2756 } while (success && n_results == EBSQL_UPGRADE_BATCH_SIZE);
2757
2758 g_free (uid);
2759
2760 /* Store the new locale & country code */
2761 if (success)
2762 success = ebsql_exec_printf (
2763 ebsql, "UPDATE folders SET countrycode = %Q WHERE folder_id = %Q",
2764 NULL, NULL, NULL, error,
2765 ebsql->priv->region_code, ebsql->priv->folderid);
2766
2767 if (success)
2768 success = ebsql_exec_printf (
2769 ebsql, "UPDATE folders SET lc_collate = %Q WHERE folder_id = %Q",
2770 NULL, NULL, NULL, error,
2771 ebsql->priv->locale, ebsql->priv->folderid);
2772
2773 return success;
2774 }
2775
2776 static gboolean
ebsql_set_locale_internal(EBookSqlite * ebsql,const gchar * locale,GError ** error)2777 ebsql_set_locale_internal (EBookSqlite *ebsql,
2778 const gchar *locale,
2779 GError **error)
2780 {
2781 EBookSqlitePrivate *priv = ebsql->priv;
2782 ECollator *collator;
2783
2784 g_return_val_if_fail (locale && locale[0], FALSE);
2785
2786 if (g_strcmp0 (priv->locale, locale) != 0) {
2787 gchar *country_code = NULL;
2788
2789 collator = e_collator_new_interpret_country (
2790 locale, &country_code, error);
2791 if (collator == NULL)
2792 return FALSE;
2793
2794 /* Assign region code parsed from the locale by ICU */
2795 g_free (priv->region_code);
2796 priv->region_code = country_code;
2797
2798 /* Assign locale */
2799 g_free (priv->locale);
2800 priv->locale = g_strdup (locale);
2801
2802 /* Assign collator */
2803 if (ebsql->priv->collator)
2804 e_collator_unref (ebsql->priv->collator);
2805 ebsql->priv->collator = collator;
2806 }
2807
2808 return TRUE;
2809 }
2810
2811 /* Called with the lock held and inside a transaction */
2812 static gboolean
ebsql_init_legacy_keys(EBookSqlite * ebsql,gint previous_schema,GError ** error)2813 ebsql_init_legacy_keys (EBookSqlite *ebsql,
2814 gint previous_schema,
2815 GError **error)
2816 {
2817 gboolean success = TRUE;
2818
2819 /* Schema 8 is when we moved from EBookSqlite */
2820 if (previous_schema >= 1 && previous_schema < 8) {
2821 gint is_populated = 0;
2822 gchar *sync_data = NULL;
2823
2824 /* We need to hold on to the value of any previously set 'is_populated' flag */
2825 success = ebsql_exec_printf (
2826 ebsql, "SELECT is_populated FROM folders WHERE folder_id = %Q",
2827 get_int_cb, &is_populated, NULL, error, ebsql->priv->folderid);
2828
2829 if (success) {
2830 /* We can't use e_book_sqlite_set_key_value_int() at this
2831 * point as that would hold the access locks
2832 */
2833 success = ebsql_exec_printf (
2834 ebsql, "INSERT or REPLACE INTO keys (key, value, folder_id) values (%Q, %Q, %Q)",
2835 NULL, NULL, NULL, error,
2836 E_BOOK_SQL_IS_POPULATED_KEY,
2837 is_populated ? "1" : "0",
2838 ebsql->priv->folderid);
2839 }
2840
2841 /* Repeat for 'sync_data' */
2842 success = success && ebsql_exec_printf (
2843 ebsql, "SELECT sync_data FROM folders WHERE folder_id = %Q",
2844 get_string_cb, &sync_data, NULL, error, ebsql->priv->folderid);
2845
2846 if (success) {
2847 success = ebsql_exec_printf (
2848 ebsql, "INSERT or REPLACE INTO keys (key, value, folder_id) values (%Q, %Q, %Q)",
2849 NULL, NULL, NULL, error,
2850 E_BOOK_SQL_SYNC_DATA_KEY,
2851 sync_data, ebsql->priv->folderid);
2852
2853 g_free (sync_data);
2854 }
2855 }
2856
2857 return success;
2858 }
2859
2860 /* Called with the lock held and inside a transaction */
2861 static gboolean
ebsql_init_locale(EBookSqlite * ebsql,gint previous_schema,gboolean already_exists,GError ** error)2862 ebsql_init_locale (EBookSqlite *ebsql,
2863 gint previous_schema,
2864 gboolean already_exists,
2865 GError **error)
2866 {
2867 gchar *stored_lc_collate = NULL;
2868 gchar *stored_region_code = NULL;
2869 const gchar *lc_collate = NULL;
2870 gboolean success = TRUE;
2871 gboolean relocalize_needed = FALSE;
2872
2873 /* Get the locale setting for this addressbook */
2874 if (already_exists) {
2875 success = ebsql_exec_printf (
2876 ebsql, "SELECT lc_collate FROM folders WHERE folder_id = %Q",
2877 get_string_cb, &stored_lc_collate, NULL, error, ebsql->priv->folderid);
2878
2879 if (success)
2880 success = ebsql_exec_printf (
2881 ebsql, "SELECT countrycode FROM folders WHERE folder_id = %Q",
2882 get_string_cb, &stored_region_code, NULL, error, ebsql->priv->folderid);
2883
2884 lc_collate = stored_lc_collate;
2885 }
2886
2887 /* When creating a new addressbook, or upgrading from a version
2888 * where we did not have any locale setting; default to system locale,
2889 * we must absolutely always have a locale set.
2890 */
2891 if (!lc_collate || !lc_collate[0])
2892 lc_collate = setlocale (LC_COLLATE, NULL);
2893 if (!lc_collate || !lc_collate[0])
2894 lc_collate = setlocale (LC_ALL, NULL);
2895 if (!lc_collate || !lc_collate[0])
2896 lc_collate = "en_US.utf8";
2897
2898 /* Before touching any data, make sure we have a valid ECollator,
2899 * this will also resolve our region code
2900 */
2901 if (success)
2902 success = ebsql_set_locale_internal (ebsql, lc_collate, error);
2903
2904 /* Check if we need to relocalize */
2905 if (success) {
2906 /* Need to relocalize the whole thing if the schema has been upgraded to version 7 */
2907 if (previous_schema >= 1 && previous_schema < 11)
2908 relocalize_needed = TRUE;
2909
2910 /* We may need to relocalize for a country code change */
2911 else if (g_strcmp0 (ebsql->priv->region_code, stored_region_code) != 0)
2912 relocalize_needed = TRUE;
2913 }
2914
2915 /* Reinsert all contacts with new locale & country code */
2916 if (success && relocalize_needed)
2917 success = ebsql_upgrade (ebsql, EBSQL_CHANGE_LAST, error);
2918
2919 EBSQL_NOTE (
2920 SCHEMA,
2921 g_printerr (
2922 "SCHEMA: Initialized locale as '%s' (%s)\n",
2923 ebsql->priv->locale, success ? "success" : "failed"));
2924
2925 g_free (stored_region_code);
2926 g_free (stored_lc_collate);
2927
2928 return success;
2929 }
2930
2931 static EBookSqlite *
ebsql_new_internal(const gchar * path,ESource * source,EbSqlVCardCallback vcard_callback,EbSqlChangeCallback change_callback,gpointer user_data,GDestroyNotify user_data_destroy,SummaryField * fields,gint n_fields,GCancellable * cancellable,GError ** error)2932 ebsql_new_internal (const gchar *path,
2933 ESource *source,
2934 EbSqlVCardCallback vcard_callback,
2935 EbSqlChangeCallback change_callback,
2936 gpointer user_data,
2937 GDestroyNotify user_data_destroy,
2938 SummaryField *fields,
2939 gint n_fields,
2940 GCancellable *cancellable,
2941 GError **error)
2942 {
2943 EBookSqlite *ebsql;
2944 gchar *dirname = NULL;
2945 gint previous_schema = 0;
2946 gboolean already_exists = FALSE;
2947 gboolean success = TRUE;
2948 GSList *introspected_columns = NULL;
2949
2950 g_return_val_if_fail (path != NULL, NULL);
2951
2952 EBSQL_LOCK_MUTEX (&dbcon_lock);
2953
2954 EBSQL_NOTE (
2955 SCHEMA,
2956 g_printerr ("SCHEMA: Creating new EBookSqlite at path '%s'\n", path));
2957
2958 ebsql = ebsql_ref_from_hash (path);
2959 if (ebsql) {
2960 EBSQL_NOTE (SCHEMA, g_printerr ("SCHEMA: An EBookSqlite already existed\n"));
2961 goto exit;
2962 }
2963
2964 ebsql = g_object_new (E_TYPE_BOOK_SQLITE, NULL);
2965 ebsql->priv->path = g_strdup (path);
2966 ebsql->priv->folderid = g_strdup (DEFAULT_FOLDER_ID);
2967 ebsql->priv->summary_fields = fields;
2968 ebsql->priv->n_summary_fields = n_fields;
2969 ebsql->priv->vcard_callback = vcard_callback;
2970 ebsql->priv->change_callback = change_callback;
2971 ebsql->priv->user_data = user_data;
2972 ebsql->priv->user_data_destroy = user_data_destroy;
2973 if (source != NULL)
2974 ebsql->priv->source = g_object_ref (source);
2975 else
2976 ebsql->priv->source = NULL;
2977
2978 EBSQL_NOTE (REF_COUNTS, g_printerr ("EBookSqlite initially created\n"));
2979
2980 /* Ensure existance of the directories leading up to 'path' */
2981 dirname = g_path_get_dirname (path);
2982 if (g_mkdir_with_parents (dirname, 0777) < 0) {
2983 EBSQL_SET_ERROR (
2984 error,
2985 E_BOOK_SQLITE_ERROR_LOAD,
2986 "Can not make parent directory: %s",
2987 g_strerror (errno));
2988 success = FALSE;
2989 goto exit;
2990 }
2991
2992 /* The additional instance lock is unneccesarry because of the global
2993 * lock held here, but let's keep it locked because we hold it while
2994 * executing any SQLite code throughout this code
2995 */
2996 EBSQL_LOCK_MUTEX (&ebsql->priv->lock);
2997
2998 /* Initialize the SQLite (set some parameters and add some custom hooks) */
2999 if (!ebsql_init_sqlite (ebsql, path, error)) {
3000 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
3001 success = FALSE;
3002 goto exit;
3003 }
3004
3005 /* Lets do it all atomically inside a single transaction */
3006 if (!ebsql_start_transaction (ebsql, EBSQL_LOCK_WRITE, cancellable, error)) {
3007 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
3008 success = FALSE;
3009 goto exit;
3010 }
3011
3012 /* When loading addressbooks created by EBookBackendSqlite, we
3013 * need to fetch the 'folderid' which was in use for that existing
3014 * addressbook before introspecting its summary and upgrading
3015 * the schema.
3016 */
3017 if (success)
3018 success = ebsql_resolve_folderid (
3019 ebsql,
3020 &previous_schema,
3021 &already_exists,
3022 error);
3023
3024 /* Initialize main folders table, also retrieve the current
3025 * schema version if the table already exists
3026 */
3027 if (success)
3028 success = ebsql_init_folders (ebsql, previous_schema, error);
3029
3030 /* Initialize the key/value table */
3031 if (success)
3032 success = ebsql_init_keys (ebsql, error);
3033
3034 /* Determine if the addressbook already existed, and fill out
3035 * some information in the main folder table
3036 */
3037 if (success && !already_exists)
3038 success = ebsql_add_folder (ebsql, error);
3039
3040 /* If the addressbook did exist, then check how it's configured.
3041 *
3042 * Let the existing summary information override the current
3043 * one asked for by our callers.
3044 *
3045 * Some summary fields are also adjusted for schema upgrades
3046 */
3047 if (success && already_exists)
3048 success = ebsql_introspect_summary (
3049 ebsql,
3050 previous_schema,
3051 &introspected_columns,
3052 error);
3053
3054 /* Add the contacts table, ensure the right columns are defined
3055 * to handle our summary configuration
3056 */
3057 if (success)
3058 success = ebsql_init_contacts (
3059 ebsql,
3060 introspected_columns,
3061 error);
3062
3063 /* Add any auxiliary tables which we might need to support our
3064 * summary configuration.
3065 *
3066 * Any fields which represent a 'list-of-strings' require an
3067 * auxiliary table to store them in.
3068 */
3069 if (success)
3070 success = ebsql_init_aux_tables (ebsql, previous_schema, error);
3071
3072 /* At this point we have resolved our schema, let's build our
3073 * precompiled statements, we might use them to re-insert contacts
3074 * in the next step
3075 */
3076 if (success)
3077 success = ebsql_init_statements (ebsql, error);
3078
3079 /* When porting from older schemas, we need to port the old 'is-populated' flag */
3080 if (success)
3081 success = ebsql_init_legacy_keys (ebsql, previous_schema, error);
3082
3083 /* Load / resolve the current locale setting
3084 *
3085 * Also perform the overall upgrade in this step
3086 * in the case that an upgrade happened, or a locale
3087 * change is detected... all rows need to be renormalized
3088 * for this.
3089 */
3090 if (success)
3091 success = ebsql_init_locale (
3092 ebsql, previous_schema,
3093 already_exists, error);
3094
3095
3096 /* Schema 12 added E_CONTACT_PGP_CERT column into the summary;
3097 the ebsql_init_locale() also calls ebsql_upgrade() for schema 10-,
3098 thus call it here only for schema 11, to populate the PGP column */
3099 if (success && previous_schema == 11)
3100 success = ebsql_upgrade (ebsql, EBSQL_CHANGE_LAST, error);
3101
3102 if (success)
3103 success = ebsql_commit_transaction (ebsql, error);
3104 else
3105 /* The GError is already set. */
3106 ebsql_rollback_transaction (ebsql, NULL);
3107
3108 /* Release the instance lock and register to the global hash */
3109 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
3110
3111 if (success)
3112 ebsql_register_to_hash (ebsql, path);
3113
3114 exit:
3115
3116 /* Cleanup and exit */
3117 EBSQL_UNLOCK_MUTEX (&dbcon_lock);
3118
3119 /* If we failed somewhere, give up on creating the 'ebsql',
3120 * otherwise add it to the hash table
3121 */
3122 if (!success)
3123 g_clear_object (&ebsql);
3124
3125 EBSQL_NOTE (
3126 SCHEMA,
3127 g_printerr (
3128 "SCHEMA: %s the new EBookSqlite\n",
3129 success ? "Successfully created" : "Failed to create"));
3130
3131 g_slist_free_full (introspected_columns, (GDestroyNotify) g_free);
3132 g_free (dirname);
3133
3134 return ebsql;
3135 }
3136
3137 /**********************************************************
3138 * Inserting Contacts *
3139 **********************************************************/
3140 static gchar *
convert_phone(const gchar * normal,const gchar * region_code,gint * out_country_code)3141 convert_phone (const gchar *normal,
3142 const gchar *region_code,
3143 gint *out_country_code)
3144 {
3145 EPhoneNumber *number = NULL;
3146 gchar *national_number = NULL;
3147 gint country_code = 0;
3148
3149 /* Don't warn about erronous phone number strings, it's a perfectly normal
3150 * use case for users to enter notes instead of phone numbers in the phone
3151 * number contact fields, such as "Ask Jenny for Lisa's phone number"
3152 */
3153 if (normal && e_phone_number_is_supported ())
3154 number = e_phone_number_from_string (normal, region_code, NULL);
3155
3156 if (number) {
3157 EPhoneNumberCountrySource source;
3158
3159 national_number = e_phone_number_get_national_number (number);
3160 country_code = e_phone_number_get_country_code (number, &source);
3161 e_phone_number_free (number);
3162
3163 if (source == E_PHONE_NUMBER_COUNTRY_FROM_DEFAULT)
3164 country_code = 0;
3165 }
3166
3167 if (out_country_code)
3168 *out_country_code = country_code;
3169
3170 return national_number;
3171 }
3172
3173 static gchar *
remove_leading_zeros(gchar * number)3174 remove_leading_zeros (gchar *number)
3175 {
3176 gchar *trimmed = NULL;
3177 gchar *tmp = number;
3178
3179 if (!number)
3180 return NULL;
3181
3182 while ('0' == *tmp)
3183 tmp++;
3184
3185 if (tmp == number)
3186 return number;
3187
3188 trimmed = g_strdup (tmp);
3189 g_free (number);
3190
3191 return trimmed;
3192 }
3193
3194 typedef struct {
3195 gint country_code;
3196 gchar *national;
3197 } E164Number;
3198
3199 static E164Number *
ebsql_e164_number_new(gint country_code,gchar * national)3200 ebsql_e164_number_new (gint country_code,
3201 gchar *national)
3202 {
3203 E164Number *number = g_slice_new (E164Number);
3204
3205 number->country_code = country_code;
3206 number->national = g_strdup (national);
3207
3208 return number;
3209 }
3210
3211 static void
ebsql_e164_number_free(E164Number * number)3212 ebsql_e164_number_free (E164Number *number)
3213 {
3214 if (number) {
3215 g_free (number->national);
3216 g_slice_free (E164Number, number);
3217 }
3218 }
3219
3220 static gint
ebsql_e164_number_find(E164Number * number_a,E164Number * number_b)3221 ebsql_e164_number_find (E164Number *number_a,
3222 E164Number *number_b)
3223 {
3224 gint ret;
3225
3226 ret = number_a->country_code - number_b->country_code;
3227
3228 if (ret == 0)
3229 ret = g_strcmp0 (
3230 number_a->national,
3231 number_b->national);
3232
3233 return ret;
3234 }
3235
3236 static GList *
extract_e164_attribute_params(EContact * contact)3237 extract_e164_attribute_params (EContact *contact)
3238 {
3239 EVCard *vcard = E_VCARD (contact);
3240 GList *extracted = NULL;
3241 GList *attr_list;
3242
3243 for (attr_list = e_vcard_get_attributes (vcard); attr_list; attr_list = attr_list->next) {
3244 EVCardAttribute *const attr = attr_list->data;
3245 EVCardAttributeParam *param = NULL;
3246 GList *param_list, *values, *l;
3247 gchar *this_national = NULL;
3248 gint this_country = 0;
3249
3250 /* We only attach E164 parameters to TEL attributes. */
3251 if (strcmp (e_vcard_attribute_get_name (attr), EVC_TEL) != 0)
3252 continue;
3253
3254 /* Find already exisiting parameter, so that we can reuse it. */
3255 for (param_list = e_vcard_attribute_get_params (attr); param_list; param_list = param_list->next) {
3256 if (strcmp (e_vcard_attribute_param_get_name (param_list->data), EVC_X_E164) == 0) {
3257 param = param_list->data;
3258 break;
3259 }
3260 }
3261
3262 if (!param)
3263 continue;
3264
3265 values = e_vcard_attribute_param_get_values (param);
3266 for (l = values; l; l = l->next) {
3267 const gchar *value = l->data;
3268
3269 if (value[0] == '+')
3270 this_country = g_ascii_strtoll (&value[1], NULL, 10);
3271 else if (this_national == NULL)
3272 this_national = g_strdup (value);
3273 }
3274
3275 if (this_national) {
3276 E164Number *number;
3277
3278 EBSQL_NOTE (
3279 CONVERT_E164,
3280 g_printerr (
3281 "Extracted e164 number from '%s' with "
3282 "country = %d national = %s\n",
3283 (gchar *) e_contact_get_const (contact, E_CONTACT_UID),
3284 this_country, this_national));
3285
3286 number = ebsql_e164_number_new (
3287 this_country, this_national);
3288 extracted = g_list_prepend (extracted, number);
3289 }
3290
3291 g_free (this_national);
3292
3293 /* Clear the values, we'll insert new ones */
3294 e_vcard_attribute_param_remove_values (param);
3295 e_vcard_attribute_remove_param (attr, EVC_X_E164);
3296 }
3297
3298 EBSQL_NOTE (
3299 CONVERT_E164,
3300 g_printerr (
3301 "Extracted %d numbers from '%s'\n",
3302 g_list_length (extracted),
3303 (gchar *) e_contact_get_const (contact, E_CONTACT_UID)));
3304
3305 return extracted;
3306 }
3307
3308 static gboolean
update_e164_attribute_params(EBookSqlite * ebsql,EContact * contact,const gchar * default_region)3309 update_e164_attribute_params (EBookSqlite *ebsql,
3310 EContact *contact,
3311 const gchar *default_region)
3312 {
3313 GList *original_numbers = NULL;
3314 GList *attr_list;
3315 gboolean changed = FALSE;
3316 gint n_numbers = 0;
3317 EVCard *vcard = E_VCARD (contact);
3318
3319 original_numbers = extract_e164_attribute_params (contact);
3320
3321 for (attr_list = e_vcard_get_attributes (vcard); attr_list; attr_list = attr_list->next) {
3322 EVCardAttribute *const attr = attr_list->data;
3323 EVCardAttributeParam *param = NULL;
3324 const gchar *original_number = NULL;
3325 gchar *country_string;
3326 GList *values;
3327 E164Number number = { 0, NULL };
3328
3329 /* We only attach E164 parameters to TEL attributes. */
3330 if (strcmp (e_vcard_attribute_get_name (attr), EVC_TEL) != 0)
3331 continue;
3332
3333 /* Fetch the TEL value */
3334 values = e_vcard_attribute_get_values (attr);
3335
3336 /* Compute E164 number based on the TEL value */
3337 if (values && values->data) {
3338 original_number = (const gchar *) values->data;
3339 number.national = convert_phone (
3340 original_number,
3341 ebsql->priv->region_code,
3342 &(number.country_code));
3343 }
3344
3345 if (number.national == NULL)
3346 continue;
3347
3348 /* Count how many we successfully parsed in this region code */
3349 n_numbers++;
3350
3351 /* Check if we have a differing e164 number, if there is no match
3352 * in the old existing values then the vcard changed
3353 */
3354 if (!g_list_find_custom (original_numbers, &number,
3355 (GCompareFunc) ebsql_e164_number_find))
3356 changed = TRUE;
3357
3358 if (number.country_code != 0)
3359 country_string = g_strdup_printf ("+%d", number.country_code);
3360 else
3361 country_string = g_strdup ("");
3362
3363 param = e_vcard_attribute_param_new (EVC_X_E164);
3364 e_vcard_attribute_add_param (attr, param);
3365
3366 /* Assign the parameter values. It seems odd that we revert
3367 * the order of NN and CC, but at least EVCard's parser doesn't
3368 * permit an empty first param value. Which of course could be
3369 * fixed - in order to create a nice potential IOP problem with
3370 ** other vCard parsers. */
3371 e_vcard_attribute_param_add_values (param, number.national, country_string, NULL);
3372
3373 EBSQL_NOTE (
3374 CONVERT_E164,
3375 g_printerr (
3376 "Converted '%s' to e164 number with country = %d "
3377 "national = %s for '%s' (changed %s)\n",
3378 original_number, number.country_code, number.national,
3379 (gchar *) e_contact_get_const (contact, E_CONTACT_UID),
3380 changed ? "yes" : "no"));
3381
3382 g_free (number.national);
3383 g_free (country_string);
3384 }
3385
3386 if (!changed &&
3387 n_numbers != g_list_length (original_numbers))
3388 changed = TRUE;
3389
3390 EBSQL_NOTE (
3391 CONVERT_E164,
3392 g_printerr (
3393 "Converted %d e164 numbers for '%s' which previously had %d e164 numbers\n",
3394 n_numbers,
3395 (gchar *) e_contact_get_const (contact, E_CONTACT_UID),
3396 g_list_length (original_numbers)));
3397
3398 g_list_free_full (original_numbers, (GDestroyNotify) ebsql_e164_number_free);
3399
3400 return changed;
3401 }
3402
3403 static sqlite3_stmt *
ebsql_prepare_multi_delete(EBookSqlite * ebsql,SummaryField * field,GError ** error)3404 ebsql_prepare_multi_delete (EBookSqlite *ebsql,
3405 SummaryField *field,
3406 GError **error)
3407 {
3408 sqlite3_stmt *stmt = NULL;
3409 gchar *stmt_str;
3410
3411 stmt_str = sqlite3_mprintf ("DELETE FROM %Q WHERE uid = :uid", field->aux_table);
3412 stmt = ebsql_prepare_statement (ebsql, stmt_str, error);
3413 sqlite3_free (stmt_str);
3414
3415 return stmt;
3416 }
3417
3418 static gboolean
ebsql_run_multi_delete(EBookSqlite * ebsql,SummaryField * field,const gchar * uid,GError ** error)3419 ebsql_run_multi_delete (EBookSqlite *ebsql,
3420 SummaryField *field,
3421 const gchar *uid,
3422 GError **error)
3423 {
3424 sqlite3_stmt *stmt;
3425 gint ret;
3426
3427 stmt = g_hash_table_lookup (ebsql->priv->multi_deletes, GUINT_TO_POINTER (field->field_id));
3428
3429 /* This can return an error if a previous call to sqlite3_step() had errors,
3430 * so let's just ignore any error in this case
3431 */
3432 sqlite3_reset (stmt);
3433
3434 /* Clear all previously set values */
3435 ret = sqlite3_clear_bindings (stmt);
3436
3437 /* Set the UID host parameter statically */
3438 if (ret == SQLITE_OK)
3439 ret = sqlite3_bind_text (stmt, 1, uid, -1, SQLITE_STATIC);
3440
3441 /* Run the statement */
3442 return ebsql_complete_statement (ebsql, stmt, ret, error);
3443 }
3444
3445 static sqlite3_stmt *
ebsql_prepare_multi_insert(EBookSqlite * ebsql,SummaryField * field,GError ** error)3446 ebsql_prepare_multi_insert (EBookSqlite *ebsql,
3447 SummaryField *field,
3448 GError **error)
3449 {
3450 sqlite3_stmt *stmt = NULL;
3451 GString *string;
3452
3453 string = g_string_sized_new (INSERT_MULTI_STMT_BYTES);
3454 ebsql_string_append_printf (string, "INSERT INTO %Q (uid, value", field->aux_table);
3455
3456 if ((field->index & INDEX_FLAG (SUFFIX)) != 0)
3457 g_string_append (string, ", value_" EBSQL_SUFFIX_REVERSE);
3458
3459 if ((field->index & INDEX_FLAG (PHONE)) != 0) {
3460 g_string_append (string, ", value_" EBSQL_SUFFIX_PHONE);
3461 g_string_append (string, ", value_" EBSQL_SUFFIX_COUNTRY);
3462 }
3463
3464 g_string_append (string, ") VALUES (:uid, :value");
3465
3466 if ((field->index & INDEX_FLAG (SUFFIX)) != 0)
3467 g_string_append (string, ", :value_" EBSQL_SUFFIX_REVERSE);
3468
3469 if ((field->index & INDEX_FLAG (PHONE)) != 0) {
3470 g_string_append (string, ", :value_" EBSQL_SUFFIX_PHONE);
3471 g_string_append (string, ", :value_" EBSQL_SUFFIX_COUNTRY);
3472 }
3473
3474 g_string_append_c (string, ')');
3475
3476 stmt = ebsql_prepare_statement (ebsql, string->str, error);
3477 g_string_free (string, TRUE);
3478
3479 return stmt;
3480 }
3481
3482 static gboolean
ebsql_run_multi_insert_one(EBookSqlite * ebsql,sqlite3_stmt * stmt,SummaryField * field,const gchar * uid,const gchar * value,GError ** error)3483 ebsql_run_multi_insert_one (EBookSqlite *ebsql,
3484 sqlite3_stmt *stmt,
3485 SummaryField *field,
3486 const gchar *uid,
3487 const gchar *value,
3488 GError **error)
3489 {
3490 gchar *normal = e_util_utf8_normalize (value);
3491 gchar *str;
3492 gint ret, param_idx = 1;
3493
3494 /* :uid */
3495 ret = sqlite3_bind_text (stmt, param_idx++, uid, -1, SQLITE_STATIC);
3496
3497 if (ret == SQLITE_OK) /* :value */
3498 ret = sqlite3_bind_text (stmt, param_idx++, normal, -1, g_free);
3499
3500 if (ret == SQLITE_OK && (field->index & INDEX_FLAG (SUFFIX)) != 0) {
3501 if (normal)
3502 str = g_utf8_strreverse (normal, -1);
3503 else
3504 str = NULL;
3505
3506 /* :value_reverse */
3507 ret = sqlite3_bind_text (stmt, param_idx++, str, -1, g_free);
3508 }
3509
3510 if (ret == SQLITE_OK && (field->index & INDEX_FLAG (PHONE)) != 0) {
3511 gint country_code;
3512
3513 str = convert_phone (
3514 normal, ebsql->priv->region_code,
3515 &country_code);
3516 str = remove_leading_zeros (str);
3517
3518 /* :value_phone */
3519 ret = sqlite3_bind_text (stmt, param_idx++, str, -1, g_free);
3520
3521 /* :value_country */
3522 if (ret == SQLITE_OK)
3523 sqlite3_bind_int (stmt, param_idx++, country_code);
3524
3525 }
3526
3527 /* Run the statement */
3528 return ebsql_complete_statement (ebsql, stmt, ret, error);
3529 }
3530
3531 static gboolean
ebsql_run_multi_insert(EBookSqlite * ebsql,SummaryField * field,const gchar * uid,EContact * contact,GError ** error)3532 ebsql_run_multi_insert (EBookSqlite *ebsql,
3533 SummaryField *field,
3534 const gchar *uid,
3535 EContact *contact,
3536 GError **error)
3537 {
3538 sqlite3_stmt *stmt;
3539 GList *values, *l;
3540 gboolean success = TRUE;
3541
3542 stmt = g_hash_table_lookup (ebsql->priv->multi_inserts, GUINT_TO_POINTER (field->field_id));
3543 values = e_contact_get (contact, field->field_id);
3544
3545 for (l = values; success && l != NULL; l = l->next) {
3546 gchar *value = (gchar *) l->data;
3547
3548 success = ebsql_run_multi_insert_one (
3549 ebsql, stmt, field, uid, value, error);
3550 }
3551
3552 /* Free the list of allocated strings */
3553 e_contact_attr_list_free (values);
3554
3555 return success;
3556 }
3557
3558 static sqlite3_stmt *
ebsql_prepare_insert(EBookSqlite * ebsql,gboolean replace_existing,GError ** error)3559 ebsql_prepare_insert (EBookSqlite *ebsql,
3560 gboolean replace_existing,
3561 GError **error)
3562 {
3563 sqlite3_stmt *stmt;
3564 GString *string;
3565 gint i;
3566
3567 string = g_string_new ("");
3568 if (replace_existing)
3569 ebsql_string_append_printf (
3570 string, "INSERT or REPLACE INTO %Q (",
3571 ebsql->priv->folderid);
3572 else
3573 ebsql_string_append_printf (
3574 string, "INSERT or FAIL INTO %Q (",
3575 ebsql->priv->folderid);
3576
3577 /*
3578 * First specify the column names for the insert, since it's possible we
3579 * upgraded the DB and cannot be sure the order of the columns are ordered
3580 * just how we like them to be.
3581 */
3582 for (i = 0; i < ebsql->priv->n_summary_fields; i++) {
3583 SummaryField *field = &(ebsql->priv->summary_fields[i]);
3584
3585 /* Multi values go into a separate table/statement */
3586 if (field->type != E_TYPE_CONTACT_ATTR_LIST) {
3587
3588 /* Only add a ", " before every field except the first,
3589 * this will not break because the first 2 fields (UID & REV)
3590 * are string fields.
3591 */
3592 if (i > 0)
3593 g_string_append (string, ", ");
3594
3595 g_string_append (string, field->dbname);
3596 }
3597
3598 if (field->type == G_TYPE_STRING) {
3599
3600 if ((field->index & INDEX_FLAG (SORT_KEY)) != 0) {
3601 g_string_append (string, ", ");
3602 g_string_append (string, field->dbname);
3603 g_string_append (string, "_" EBSQL_SUFFIX_SORT_KEY);
3604 }
3605
3606 if ((field->index & INDEX_FLAG (SUFFIX)) != 0) {
3607 g_string_append (string, ", ");
3608 g_string_append (string, field->dbname);
3609 g_string_append (string, "_" EBSQL_SUFFIX_REVERSE);
3610 }
3611
3612 if ((field->index & INDEX_FLAG (PHONE)) != 0) {
3613
3614 g_string_append (string, ", ");
3615 g_string_append (string, field->dbname);
3616 g_string_append (string, "_" EBSQL_SUFFIX_PHONE);
3617
3618 g_string_append (string, ", ");
3619 g_string_append (string, field->dbname);
3620 g_string_append (string, "_" EBSQL_SUFFIX_COUNTRY);
3621 }
3622 }
3623 }
3624 g_string_append (string, ", vcard, bdata)");
3625
3626 /*
3627 * Now specify values for all of the column names we specified.
3628 */
3629 g_string_append (string, " VALUES (");
3630 for (i = 0; i < ebsql->priv->n_summary_fields; i++) {
3631 SummaryField *field = &(ebsql->priv->summary_fields[i]);
3632
3633 if (field->type != E_TYPE_CONTACT_ATTR_LIST) {
3634 /* Only add a ", " before every field except the first,
3635 * this will not break because the first 2 fields (UID & REV)
3636 * are string fields.
3637 */
3638 if (i > 0)
3639 g_string_append (string, ", ");
3640 }
3641
3642 if (field->type == G_TYPE_STRING || field->type == G_TYPE_BOOLEAN ||
3643 field->type == E_TYPE_CONTACT_CERT) {
3644
3645 g_string_append_c (string, ':');
3646 g_string_append (string, field->dbname);
3647
3648 if ((field->index & INDEX_FLAG (SORT_KEY)) != 0)
3649 g_string_append_printf (string, ", :%s_" EBSQL_SUFFIX_SORT_KEY, field->dbname);
3650
3651 if ((field->index & INDEX_FLAG (SUFFIX)) != 0)
3652 g_string_append_printf (string, ", :%s_" EBSQL_SUFFIX_REVERSE, field->dbname);
3653
3654 if ((field->index & INDEX_FLAG (PHONE)) != 0) {
3655 g_string_append_printf (string, ", :%s_" EBSQL_SUFFIX_PHONE, field->dbname);
3656 g_string_append_printf (string, ", :%s_" EBSQL_SUFFIX_COUNTRY, field->dbname);
3657 }
3658
3659 } else if (field->type != E_TYPE_CONTACT_ATTR_LIST)
3660 g_warn_if_reached ();
3661 }
3662
3663 g_string_append (string, ", :vcard, :bdata)");
3664
3665 stmt = ebsql_prepare_statement (ebsql, string->str, error);
3666 g_string_free (string, TRUE);
3667
3668 return stmt;
3669 }
3670
3671 static gboolean
ebsql_init_statements(EBookSqlite * ebsql,GError ** error)3672 ebsql_init_statements (EBookSqlite *ebsql,
3673 GError **error)
3674 {
3675 sqlite3_stmt *stmt;
3676 gint i;
3677
3678 ebsql->priv->insert_stmt = ebsql_prepare_insert (ebsql, FALSE, error);
3679 if (!ebsql->priv->insert_stmt)
3680 goto preparation_failed;
3681
3682 ebsql->priv->replace_stmt = ebsql_prepare_insert (ebsql, TRUE, error);
3683 if (!ebsql->priv->replace_stmt)
3684 goto preparation_failed;
3685
3686 ebsql->priv->multi_deletes =
3687 g_hash_table_new_full (
3688 g_direct_hash, g_direct_equal,
3689 NULL,
3690 (GDestroyNotify) sqlite3_finalize);
3691 ebsql->priv->multi_inserts =
3692 g_hash_table_new_full (
3693 g_direct_hash, g_direct_equal,
3694 NULL,
3695 (GDestroyNotify) sqlite3_finalize);
3696
3697 for (i = 0; i < ebsql->priv->n_summary_fields; i++) {
3698 SummaryField *field = &(ebsql->priv->summary_fields[i]);
3699
3700 if (field->type != E_TYPE_CONTACT_ATTR_LIST)
3701 continue;
3702
3703 stmt = ebsql_prepare_multi_insert (ebsql, field, error);
3704 if (!stmt)
3705 goto preparation_failed;
3706
3707 g_hash_table_insert (
3708 ebsql->priv->multi_inserts,
3709 GUINT_TO_POINTER (field->field_id),
3710 stmt);
3711
3712 stmt = ebsql_prepare_multi_delete (ebsql, field, error);
3713 if (!stmt)
3714 goto preparation_failed;
3715
3716 g_hash_table_insert (
3717 ebsql->priv->multi_deletes,
3718 GUINT_TO_POINTER (field->field_id),
3719 stmt);
3720 }
3721
3722 return TRUE;
3723
3724 preparation_failed:
3725
3726 return FALSE;
3727 }
3728
3729 static gboolean
ebsql_run_insert(EBookSqlite * ebsql,gboolean replace,EContact * contact,gchar * vcard,const gchar * extra,GError ** error)3730 ebsql_run_insert (EBookSqlite *ebsql,
3731 gboolean replace,
3732 EContact *contact,
3733 gchar *vcard,
3734 const gchar *extra,
3735 GError **error)
3736 {
3737 EBookSqlitePrivate *priv;
3738 sqlite3_stmt *stmt;
3739 gint i, param_idx;
3740 gint ret;
3741 gboolean success;
3742 GError *local_error = NULL;
3743
3744 priv = ebsql->priv;
3745
3746 if (replace)
3747 stmt = ebsql->priv->replace_stmt;
3748 else
3749 stmt = ebsql->priv->insert_stmt;
3750
3751 /* This can return an error if a previous call to sqlite3_step() had errors,
3752 * so let's just ignore any error in this case
3753 */
3754 sqlite3_reset (stmt);
3755
3756 /* Clear all previously set values */
3757 ret = sqlite3_clear_bindings (stmt);
3758
3759 for (i = 0, param_idx = 1; ret == SQLITE_OK && i < ebsql->priv->n_summary_fields; i++) {
3760 SummaryField *field = &(ebsql->priv->summary_fields[i]);
3761
3762 if (field->type == G_TYPE_STRING) {
3763 gchar *val;
3764 gchar *normal;
3765 gchar *str;
3766
3767 val = e_contact_get (contact, field->field_id);
3768
3769 /* Special exception, never normalize/localize the UID or REV string */
3770 if (field->field_id != E_CONTACT_UID &&
3771 field->field_id != E_CONTACT_REV) {
3772 normal = e_util_utf8_normalize (val);
3773 } else
3774 normal = g_strdup (val);
3775
3776 /* Takes ownership of 'normal' */
3777 ret = sqlite3_bind_text (stmt, param_idx++, normal, -1, g_free);
3778
3779 if (ret == SQLITE_OK &&
3780 (field->index & INDEX_FLAG (SORT_KEY)) != 0) {
3781 if (val)
3782 str = e_collator_generate_key (ebsql->priv->collator, val, NULL);
3783 else
3784 str = g_strdup ("");
3785
3786 ret = sqlite3_bind_text (stmt, param_idx++, str, -1, g_free);
3787 }
3788
3789 if (ret == SQLITE_OK &&
3790 (field->index & INDEX_FLAG (SUFFIX)) != 0) {
3791 if (normal)
3792 str = g_utf8_strreverse (normal, -1);
3793 else
3794 str = NULL;
3795
3796 ret = sqlite3_bind_text (stmt, param_idx++, str, -1, g_free);
3797 }
3798
3799 if (ret == SQLITE_OK &&
3800 (field->index & INDEX_FLAG (PHONE)) != 0) {
3801 gint country_code;
3802
3803 str = convert_phone (
3804 normal, ebsql->priv->region_code,
3805 &country_code);
3806 str = remove_leading_zeros (str);
3807
3808 ret = sqlite3_bind_text (stmt, param_idx++, str, -1, g_free);
3809 if (ret == SQLITE_OK)
3810 sqlite3_bind_int (stmt, param_idx++, country_code);
3811 }
3812
3813 g_free (val);
3814 } else if (field->type == G_TYPE_BOOLEAN) {
3815 gboolean val;
3816
3817 val = e_contact_get (contact, field->field_id) ? TRUE : FALSE;
3818
3819 ret = sqlite3_bind_int (stmt, param_idx++, val ? 1 : 0);
3820 } else if (field->type == E_TYPE_CONTACT_CERT) {
3821 EContactCert *cert = NULL;
3822
3823 cert = e_contact_get (contact, field->field_id);
3824
3825 /* We don't actually store the cert; only a boolean to indicate
3826 * that is *has* a cert. */
3827 ret = sqlite3_bind_int (stmt, param_idx++, cert ? 1 : 0);
3828 e_contact_cert_free (cert);
3829 } else if (field->type != E_TYPE_CONTACT_ATTR_LIST)
3830 g_warn_if_reached ();
3831 }
3832
3833 if (ret == SQLITE_OK) {
3834
3835 EBSQL_NOTE (
3836 INSERT,
3837 g_printerr (
3838 "Inserting vcard for contact with UID '%s'\n%s\n",
3839 (gchar *) e_contact_get_const (contact, E_CONTACT_UID),
3840 vcard ? vcard : "(no vcard)"));
3841
3842 /* If we have a priv->vcard_callback, then it's a shallow addressbook
3843 * and we don't populate the vcard column, need to free it anyway
3844 */
3845 if (priv->vcard_callback != NULL) {
3846 g_free (vcard);
3847 vcard = NULL;
3848 }
3849
3850 ret = sqlite3_bind_text (stmt, param_idx++, vcard, -1, g_free);
3851 }
3852
3853 /* The extra data */
3854 if (ret == SQLITE_OK)
3855 ret = sqlite3_bind_text (stmt, param_idx++, g_strdup (extra), -1, g_free);
3856
3857 /* Run the statement */
3858 success = ebsql_complete_statement (ebsql, stmt, ret, &local_error);
3859
3860 EBSQL_NOTE (
3861 INSERT,
3862 g_printerr (
3863 "%s contact with UID '%s' and extra data '%s' vcard: %s (error: %s)\n",
3864 success ? "Succesfully inserted" : "Failed to insert",
3865 (gchar *) e_contact_get_const (contact, E_CONTACT_UID), extra,
3866 vcard ? "yes" : "no",
3867 local_error ? local_error->message : "(none)"));
3868
3869 if (!success)
3870 g_propagate_error (error, local_error);
3871
3872 return success;
3873 }
3874
3875 static gboolean
ebsql_insert_contact(EBookSqlite * ebsql,EbSqlChangeType change_type,EContact * contact,const gchar * original_vcard,const gchar * extra,gboolean replace,GError ** error)3876 ebsql_insert_contact (EBookSqlite *ebsql,
3877 EbSqlChangeType change_type,
3878 EContact *contact,
3879 const gchar *original_vcard,
3880 const gchar *extra,
3881 gboolean replace,
3882 GError **error)
3883 {
3884 EBookSqlitePrivate *priv;
3885 gboolean e164_changed = FALSE;
3886 gboolean success;
3887 gchar *uid, *vcard = NULL;
3888
3889 priv = ebsql->priv;
3890 uid = e_contact_get (contact, E_CONTACT_UID);
3891
3892 /* Update E.164 parameters in vcard if needed */
3893 e164_changed = update_e164_attribute_params (
3894 ebsql, contact, priv->region_code);
3895
3896 if (e164_changed || original_vcard == NULL) {
3897
3898 /* Generate a new one if it changed (or if we don't have one) */
3899 vcard = e_vcard_to_string (E_VCARD (contact), EVC_FORMAT_VCARD_30);
3900
3901 if (e164_changed &&
3902 change_type != EBSQL_CHANGE_LAST &&
3903 ebsql->priv->change_callback)
3904 ebsql->priv->change_callback (change_type,
3905 uid, extra, vcard,
3906 ebsql->priv->user_data);
3907 } else {
3908
3909 vcard = g_strdup (original_vcard);
3910 }
3911
3912 /* This actually consumes 'vcard' */
3913 success = ebsql_run_insert (ebsql, replace, contact, vcard, extra, error);
3914
3915 /* Update attribute list table */
3916 if (success) {
3917 gint i;
3918
3919 for (i = 0; success && i < priv->n_summary_fields; i++) {
3920 SummaryField *field = &(ebsql->priv->summary_fields[i]);
3921
3922 if (field->type != E_TYPE_CONTACT_ATTR_LIST)
3923 continue;
3924
3925 success = ebsql_run_multi_delete (
3926 ebsql, field, uid, error);
3927
3928 if (success)
3929 success = ebsql_run_multi_insert (
3930 ebsql, field, uid, contact, error);
3931 }
3932 }
3933
3934 g_free (uid);
3935
3936 return success;
3937 }
3938
3939 /***************************************************************
3940 * Structures and utilities for preflight and query generation *
3941 ***************************************************************/
3942
3943 /* This enumeration is ordered by severity, higher values
3944 * of PreflightStatus take precedence in error reporting.
3945 */
3946 typedef enum {
3947 PREFLIGHT_OK = 0,
3948 PREFLIGHT_LIST_ALL,
3949 PREFLIGHT_NOT_SUMMARIZED,
3950 PREFLIGHT_INVALID,
3951 PREFLIGHT_UNSUPPORTED,
3952 } PreflightStatus;
3953
3954 #define EBSQL_STATUS_STR(status) \
3955 ((status) == PREFLIGHT_OK ? "Ok" : \
3956 (status) == PREFLIGHT_LIST_ALL ? "List all" : \
3957 (status) == PREFLIGHT_NOT_SUMMARIZED ? "Not Summarized" : \
3958 (status) == PREFLIGHT_INVALID ? "Invalid" : \
3959 (status) == PREFLIGHT_UNSUPPORTED ? "Unsupported" : "(unknown status)")
3960
3961 /* Whether we can satisfy the constraints or whether we
3962 * need to do a fallback, we still need to call
3963 * ebsql_generate_constraints()
3964 */
3965 #define EBSQL_STATUS_GEN_CONSTRAINTS(status) \
3966 ((status) == PREFLIGHT_OK || \
3967 (status) == PREFLIGHT_NOT_SUMMARIZED)
3968
3969 /* Internal extension of the EBookQueryTest enumeration */
3970 enum {
3971 /* 'exists' is a supported query on a field, but not part of EBookQueryTest */
3972 BOOK_QUERY_EXISTS = E_BOOK_QUERY_LAST,
3973 BOOK_QUERY_EXISTS_VCARD,
3974
3975 /* From here the compound types start */
3976 BOOK_QUERY_SUB_AND,
3977 BOOK_QUERY_SUB_OR,
3978 BOOK_QUERY_SUB_NOT,
3979 BOOK_QUERY_SUB_END,
3980
3981 BOOK_QUERY_SUB_FIRST = BOOK_QUERY_SUB_AND,
3982 };
3983
3984 #define EBSQL_QUERY_TYPE_STR(query) \
3985 ((query) == BOOK_QUERY_EXISTS ? "exists" : \
3986 (query) == BOOK_QUERY_EXISTS_VCARD ? "exists_vcard" : \
3987 (query) == BOOK_QUERY_SUB_AND ? "AND" : \
3988 (query) == BOOK_QUERY_SUB_OR ? "OR" : \
3989 (query) == BOOK_QUERY_SUB_NOT ? "NOT" : \
3990 (query) == BOOK_QUERY_SUB_END ? "END" : \
3991 (query) == E_BOOK_QUERY_IS ? "is" : \
3992 (query) == E_BOOK_QUERY_CONTAINS ? "contains" : \
3993 (query) == E_BOOK_QUERY_BEGINS_WITH ? "begins-with" : \
3994 (query) == E_BOOK_QUERY_ENDS_WITH ? "ends-with" : \
3995 (query) == E_BOOK_QUERY_EQUALS_PHONE_NUMBER ? "eqphone" : \
3996 (query) == E_BOOK_QUERY_EQUALS_NATIONAL_PHONE_NUMBER ? "eqphone-national" : \
3997 (query) == E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER ? "eqphone-short" : \
3998 (query) == E_BOOK_QUERY_REGEX_NORMAL ? "regex-normal" : \
3999 (query) == E_BOOK_QUERY_REGEX_NORMAL ? "regex-raw" : "(unknown)")
4000
4001 #define EBSQL_FIELD_ID_STR(field_id) \
4002 ((field_id) == E_CONTACT_FIELD_LAST ? "x-evolution-any-field" : \
4003 (field_id) == 0 ? "(not an EContactField)" : \
4004 e_contact_field_name (field_id))
4005
4006 #define IS_QUERY_PHONE(query) \
4007 ((query) == E_BOOK_QUERY_EQUALS_PHONE_NUMBER || \
4008 (query) == E_BOOK_QUERY_EQUALS_NATIONAL_PHONE_NUMBER || \
4009 (query) == E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER)
4010
4011 typedef struct {
4012 guint query; /* EBookQueryTest (extended) */
4013 } QueryElement;
4014
4015 typedef struct {
4016 guint query; /* EBookQueryTest (extended) */
4017 } QueryDelimiter;
4018
4019 typedef struct {
4020 guint query; /* EBookQueryTest (extended) */
4021
4022 EContactField field_id; /* The EContactField to compare */
4023 SummaryField *field; /* The summary field for 'field' */
4024 gchar *value; /* The value to compare with */
4025
4026 } QueryFieldTest;
4027
4028 typedef struct {
4029 guint query; /* EBookQueryTest (extended) */
4030
4031 /* Common fields from QueryFieldTest */
4032 EContactField field_id; /* The EContactField to compare */
4033 SummaryField *field; /* The summary field for 'field' */
4034 gchar *value; /* The value to compare with */
4035
4036 /* Extension */
4037 gchar *region; /* Region code from the query input */
4038 gchar *national; /* Parsed national number */
4039 gint country; /* Parsed country code */
4040 } QueryPhoneTest;
4041
4042 /* Stack initializer for the PreflightContext struct below */
4043 #define PREFLIGHT_CONTEXT_INIT { PREFLIGHT_OK, NULL, 0, FALSE }
4044
4045 typedef struct {
4046 PreflightStatus status; /* result status */
4047 GPtrArray *constraints; /* main query; may be NULL */
4048 guint64 aux_mask; /* Bitmask of which auxiliary tables are needed in the query */
4049 guint64 left_join_mask; /* Do we need to use a LEFT JOIN */
4050 } PreflightContext;
4051
4052 static QueryElement *
query_delimiter_new(guint query)4053 query_delimiter_new (guint query)
4054 {
4055 QueryDelimiter *delim;
4056
4057 g_return_val_if_fail (query >= BOOK_QUERY_SUB_FIRST, NULL);
4058
4059 delim = g_slice_new (QueryDelimiter);
4060 delim->query = query;
4061
4062 return (QueryElement *) delim;
4063 }
4064
4065 static QueryFieldTest *
query_field_test_new(guint query,EContactField field)4066 query_field_test_new (guint query,
4067 EContactField field)
4068 {
4069 QueryFieldTest *test;
4070
4071 g_return_val_if_fail (query < BOOK_QUERY_SUB_FIRST, NULL);
4072 g_return_val_if_fail (IS_QUERY_PHONE (query) == FALSE, NULL);
4073
4074 test = g_slice_new (QueryFieldTest);
4075 test->query = query;
4076 test->field_id = field;
4077
4078 /* Instead of g_slice_new0, NULL them out manually */
4079 test->field = NULL;
4080 test->value = NULL;
4081
4082 return test;
4083 }
4084
4085 static QueryPhoneTest *
query_phone_test_new(guint query,EContactField field)4086 query_phone_test_new (guint query,
4087 EContactField field)
4088 {
4089 QueryPhoneTest *test;
4090
4091 g_return_val_if_fail (IS_QUERY_PHONE (query), NULL);
4092
4093 test = g_slice_new (QueryPhoneTest);
4094 test->query = query;
4095 test->field_id = field;
4096
4097 /* Instead of g_slice_new0, NULL them out manually */
4098 test->field = NULL;
4099 test->value = NULL;
4100
4101 /* Extra QueryPhoneTest fields */
4102 test->region = NULL;
4103 test->national = NULL;
4104 test->country = 0;
4105
4106 return test;
4107 }
4108
4109 static void
query_element_free(QueryElement * element)4110 query_element_free (QueryElement *element)
4111 {
4112 if (element) {
4113
4114 if (element->query >= BOOK_QUERY_SUB_FIRST) {
4115 QueryDelimiter *delim = (QueryDelimiter *) element;
4116
4117 g_slice_free (QueryDelimiter, delim);
4118 } else if (IS_QUERY_PHONE (element->query)) {
4119 QueryPhoneTest *test = (QueryPhoneTest *) element;
4120
4121 g_free (test->value);
4122 g_free (test->region);
4123 g_free (test->national);
4124 g_slice_free (QueryPhoneTest, test);
4125 } else {
4126 QueryFieldTest *test = (QueryFieldTest *) element;
4127
4128 g_free (test->value);
4129 g_slice_free (QueryFieldTest, test);
4130 }
4131 }
4132 }
4133
4134 /* We use ptr arrays for the QueryElement vectors */
4135 static inline void
constraints_insert(GPtrArray * array,gint idx,gpointer data)4136 constraints_insert (GPtrArray *array,
4137 gint idx,
4138 gpointer data)
4139 {
4140 #if 0
4141 g_ptr_array_insert (array, idx, data);
4142 #else
4143 g_return_if_fail ((idx >= -1) && (idx < (gint) array->len + 1));
4144
4145 if (idx < 0)
4146 idx = array->len;
4147
4148 g_ptr_array_add (array, NULL);
4149
4150 if (idx != (array->len - 1))
4151 memmove (
4152 &(array->pdata[idx + 1]),
4153 &(array->pdata[idx]),
4154 ((array->len - 1) - idx) * sizeof (gpointer));
4155
4156 array->pdata[idx] = data;
4157 #endif
4158 }
4159
4160 static inline void
constraints_insert_delimiter(GPtrArray * array,gint idx,guint query)4161 constraints_insert_delimiter (GPtrArray *array,
4162 gint idx,
4163 guint query)
4164 {
4165 QueryElement *delim;
4166
4167 delim = query_delimiter_new (query);
4168 constraints_insert (array, idx, delim);
4169 }
4170
4171 static inline void
constraints_insert_field_test(GPtrArray * array,gint idx,SummaryField * field,guint query,const gchar * value)4172 constraints_insert_field_test (GPtrArray *array,
4173 gint idx,
4174 SummaryField *field,
4175 guint query,
4176 const gchar *value)
4177 {
4178 QueryFieldTest *test;
4179
4180 test = query_field_test_new (query, field->field_id);
4181 test->field = field;
4182 test->value = g_strdup (value);
4183
4184 constraints_insert (array, idx, test);
4185 }
4186
4187 static void
preflight_context_clear(PreflightContext * context)4188 preflight_context_clear (PreflightContext *context)
4189 {
4190 if (context) {
4191 /* Free any allocated data, but leave the context values in place */
4192 if (context->constraints)
4193 g_ptr_array_free (context->constraints, TRUE);
4194 context->constraints = NULL;
4195 }
4196 }
4197
4198 /* A small API to track the current sub-query context.
4199 *
4200 * I.e. sub contexts can be OR, AND, or NOT, in which
4201 * field tests or other sub contexts are nested.
4202 *
4203 * The 'count' field is a simple counter of how deep the contexts are nested.
4204 *
4205 * The 'cond_count' field is to be used by the caller for its own purposes;
4206 * it is incremented in sub_query_context_push() only if the inc_cond_count
4207 * parameter is TRUE. This is used by query_preflight_check() in a complex
4208 * fashion which is described there.
4209 */
4210 typedef GQueue SubQueryContext;
4211
4212 typedef struct {
4213 guint sub_type; /* The type of this sub context */
4214 guint count; /* The number of field tests so far in this context */
4215 guint cond_count; /* User-specific conditional counter */
4216 } SubQueryData;
4217
4218 #define sub_query_context_new g_queue_new
4219 #define sub_query_context_free(ctx) g_queue_free (ctx)
4220
4221 static inline void
sub_query_context_push(SubQueryContext * ctx,guint sub_type,gboolean inc_cond_count)4222 sub_query_context_push (SubQueryContext *ctx,
4223 guint sub_type, gboolean inc_cond_count)
4224 {
4225 SubQueryData *data, *prev;
4226
4227 prev = g_queue_peek_tail (ctx);
4228
4229 data = g_slice_new (SubQueryData);
4230 data->sub_type = sub_type;
4231 data->count = 0;
4232 data->cond_count = prev ? prev->cond_count : 0;
4233 if (inc_cond_count)
4234 data->cond_count++;
4235
4236 g_queue_push_tail (ctx, data);
4237 }
4238
4239 static inline void
sub_query_context_pop(SubQueryContext * ctx)4240 sub_query_context_pop (SubQueryContext *ctx)
4241 {
4242 SubQueryData *data;
4243
4244 data = g_queue_pop_tail (ctx);
4245 g_slice_free (SubQueryData, data);
4246 }
4247
4248 static inline guint
sub_query_context_peek_type(SubQueryContext * ctx)4249 sub_query_context_peek_type (SubQueryContext *ctx)
4250 {
4251 SubQueryData *data;
4252
4253 data = g_queue_peek_tail (ctx);
4254
4255 return data->sub_type;
4256 }
4257
4258 static inline guint
sub_query_context_peek_cond_counter(SubQueryContext * ctx)4259 sub_query_context_peek_cond_counter (SubQueryContext *ctx)
4260 {
4261 SubQueryData *data;
4262
4263 data = g_queue_peek_tail (ctx);
4264
4265 if (data)
4266 return data->cond_count;
4267 else
4268 return 0;
4269 }
4270
4271 /* Returns the context field test count before incrementing */
4272 static inline guint
sub_query_context_increment(SubQueryContext * ctx)4273 sub_query_context_increment (SubQueryContext *ctx)
4274 {
4275 SubQueryData *data;
4276
4277 data = g_queue_peek_tail (ctx);
4278
4279 if (data) {
4280 data->count++;
4281
4282 return (data->count - 1);
4283 }
4284
4285 /* If we're not in a sub context, just return 0 */
4286 return 0;
4287 }
4288
4289 /**********************************************************
4290 * Querying preflighting *
4291 **********************************************************
4292 *
4293 * The preflight checks are performed before a query might
4294 * take place in order to evaluate whether the given query
4295 * can be performed with the current summary configuration.
4296 *
4297 * After preflighting, all relevant data has been extracted
4298 * from the search expression and the search expression need
4299 * not be parsed again.
4300 */
4301
4302 /* The PreflightSubCallback is expected to return TRUE
4303 * to keep iterating and FALSE to abort iteration.
4304 *
4305 * The sub_level is the counter of how deep the 'element'
4306 * is nested in sub elements, the offset is the real offset
4307 * of 'element' in the array passed to query_preflight_foreach_sub().
4308 */
4309 typedef gboolean (* PreflightSubCallback) (QueryElement *element,
4310 gint sub_level,
4311 gint offset,
4312 gpointer user_data);
4313
4314 static void
query_preflight_foreach_sub(QueryElement ** elements,gint n_elements,gint offset,gboolean include_delim,PreflightSubCallback callback,gpointer user_data)4315 query_preflight_foreach_sub (QueryElement **elements,
4316 gint n_elements,
4317 gint offset,
4318 gboolean include_delim,
4319 PreflightSubCallback callback,
4320 gpointer user_data)
4321 {
4322 gint sub_counter = 1, i;
4323
4324 g_return_if_fail (offset >= 0 && offset < n_elements);
4325 g_return_if_fail (elements[offset]->query >= BOOK_QUERY_SUB_FIRST);
4326 g_return_if_fail (callback != NULL);
4327
4328 if (include_delim && !callback (elements[offset], 0, offset, user_data))
4329 return;
4330
4331 for (i = (offset + 1); sub_counter > 0 && i < n_elements; i++) {
4332
4333 if (elements[i]->query >= BOOK_QUERY_SUB_FIRST) {
4334
4335 if (elements[i]->query == BOOK_QUERY_SUB_END)
4336 sub_counter--;
4337 else
4338 sub_counter++;
4339
4340 if (include_delim &&
4341 !callback (elements[i], sub_counter, i, user_data))
4342 break;
4343 } else {
4344
4345 if (!callback (elements[i], sub_counter, i, user_data))
4346 break;
4347 }
4348 }
4349 }
4350
4351 /* Table used in ESExp parsing below */
4352 static const struct {
4353 const gchar *name; /* Name of the symbol to match for this parse phase */
4354 gboolean subset; /* TRUE for the subset ESExpIFunc, otherwise the field check ESExpFunc */
4355 guint test; /* Extended EBookQueryTest value */
4356 } check_symbols[] = {
4357 { "and", TRUE, BOOK_QUERY_SUB_AND },
4358 { "or", TRUE, BOOK_QUERY_SUB_OR },
4359 { "not", TRUE, BOOK_QUERY_SUB_NOT },
4360
4361 { "contains", FALSE, E_BOOK_QUERY_CONTAINS },
4362 { "is", FALSE, E_BOOK_QUERY_IS },
4363 { "beginswith", FALSE, E_BOOK_QUERY_BEGINS_WITH },
4364 { "endswith", FALSE, E_BOOK_QUERY_ENDS_WITH },
4365 { "eqphone", FALSE, E_BOOK_QUERY_EQUALS_PHONE_NUMBER },
4366 { "eqphone_national", FALSE, E_BOOK_QUERY_EQUALS_NATIONAL_PHONE_NUMBER },
4367 { "eqphone_short", FALSE, E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER },
4368 { "regex_normal", FALSE, E_BOOK_QUERY_REGEX_NORMAL },
4369 { "regex_raw", FALSE, E_BOOK_QUERY_REGEX_RAW },
4370 { "exists", FALSE, BOOK_QUERY_EXISTS },
4371 { "exists_vcard", FALSE, BOOK_QUERY_EXISTS_VCARD }
4372 };
4373
4374 /* Cheat our way into passing mode data to these funcs */
4375 static ESExpResult *
func_check_subset(ESExp * f,gint argc,struct _ESExpTerm ** argv,gpointer data)4376 func_check_subset (ESExp *f,
4377 gint argc,
4378 struct _ESExpTerm **argv,
4379 gpointer data)
4380 {
4381 ESExpResult *result, *sub_result;
4382 GPtrArray *result_array;
4383 QueryElement *element, **sub_elements;
4384 gint i, j, len;
4385 guint query_type;
4386
4387 query_type = GPOINTER_TO_UINT (data);
4388
4389 /* The compound query delimiter is the first element in this return array */
4390 result_array = g_ptr_array_new_with_free_func ((GDestroyNotify) query_element_free);
4391 element = query_delimiter_new (query_type);
4392 g_ptr_array_add (result_array, element);
4393
4394 EBSQL_NOTE (
4395 PREFLIGHT,
4396 g_printerr (
4397 "PREFLIGHT INIT: Open sub: %s\n",
4398 EBSQL_QUERY_TYPE_STR (query_type)));
4399
4400 for (i = 0; i < argc; i++) {
4401 sub_result = e_sexp_term_eval (f, argv[i]);
4402
4403 if (sub_result->type == ESEXP_RES_ARRAY_PTR) {
4404 /* Steal the elements directly from the sub result */
4405 sub_elements = (QueryElement **) sub_result->value.ptrarray->pdata;
4406 len = sub_result->value.ptrarray->len;
4407
4408 for (j = 0; j < len; j++) {
4409 element = sub_elements[j];
4410 sub_elements[j] = NULL;
4411
4412 g_ptr_array_add (result_array, element);
4413 }
4414 }
4415 e_sexp_result_free (f, sub_result);
4416 }
4417
4418 EBSQL_NOTE (
4419 PREFLIGHT,
4420 g_printerr (
4421 "PREFLIGHT INIT: Close sub: %s\n",
4422 EBSQL_QUERY_TYPE_STR (query_type)));
4423
4424 /* The last element in this return array is the sub end delimiter */
4425 element = query_delimiter_new (BOOK_QUERY_SUB_END);
4426 g_ptr_array_add (result_array, element);
4427
4428 result = e_sexp_result_new (f, ESEXP_RES_ARRAY_PTR);
4429 result->value.ptrarray = result_array;
4430
4431 return result;
4432 }
4433
4434 static ESExpResult *
func_check(struct _ESExp * f,gint argc,struct _ESExpResult ** argv,gpointer data)4435 func_check (struct _ESExp *f,
4436 gint argc,
4437 struct _ESExpResult **argv,
4438 gpointer data)
4439 {
4440 ESExpResult *result;
4441 GPtrArray *result_array;
4442 QueryElement *element = NULL;
4443 EContactField field_id = 0;
4444 const gchar *query_name = NULL;
4445 const gchar *query_value = NULL;
4446 const gchar *query_extra = NULL;
4447 guint query_type;
4448
4449 query_type = GPOINTER_TO_UINT (data);
4450
4451 if (argc == 1 && query_type == BOOK_QUERY_EXISTS &&
4452 argv[0]->type == ESEXP_RES_STRING) {
4453 query_name = argv[0]->value.string;
4454
4455 field_id = e_contact_field_id (query_name);
4456 } else if (argc == 2 &&
4457 argv[0]->type == ESEXP_RES_STRING &&
4458 argv[1]->type == ESEXP_RES_STRING) {
4459 query_name = argv[0]->value.string;
4460 query_value = argv[1]->value.string;
4461
4462 /* We use E_CONTACT_FIELD_LAST to hold the special case of "x-evolution-any-field" */
4463 if (g_strcmp0 (query_name, "x-evolution-any-field") == 0)
4464 field_id = E_CONTACT_FIELD_LAST;
4465 else
4466 field_id = e_contact_field_id (query_name);
4467
4468 } else if (argc == 3 &&
4469 argv[0]->type == ESEXP_RES_STRING &&
4470 argv[1]->type == ESEXP_RES_STRING &&
4471 argv[2]->type == ESEXP_RES_STRING) {
4472 query_name = argv[0]->value.string;
4473 query_value = argv[1]->value.string;
4474 query_extra = argv[2]->value.string;
4475
4476 field_id = e_contact_field_id (query_name);
4477 }
4478
4479 if (IS_QUERY_PHONE (query_type)) {
4480 QueryPhoneTest *test;
4481
4482 /* Collect data from this field test */
4483 test = query_phone_test_new (query_type, field_id);
4484 test->value = g_strdup (query_value);
4485 test->region = g_strdup (query_extra);
4486
4487 element = (QueryElement *) test;
4488 } else {
4489 QueryFieldTest *test;
4490
4491 /* Collect data from this field test */
4492 test = query_field_test_new (query_type, field_id);
4493 test->value = g_strdup (query_value);
4494
4495 element = (QueryElement *) test;
4496 }
4497
4498 EBSQL_NOTE (
4499 PREFLIGHT,
4500 g_printerr (
4501 "PREFLIGHT INIT: Adding field test: `%s' on field `%s' "
4502 "(field name: %s query value: %s query extra: %s)\n",
4503 EBSQL_QUERY_TYPE_STR (query_type),
4504 EBSQL_FIELD_ID_STR (field_id),
4505 query_name, query_value, query_extra));
4506
4507 /* Return an array with only one element, for lack of a pointer type ESExpResult */
4508 result_array = g_ptr_array_new_with_free_func ((GDestroyNotify) query_element_free);
4509 g_ptr_array_add (result_array, element);
4510
4511 result = e_sexp_result_new (f, ESEXP_RES_ARRAY_PTR);
4512 result->value.ptrarray = result_array;
4513
4514 return result;
4515 }
4516
4517 /* Initial stage of preflighting:
4518 *
4519 * o Parse the search expression and generate our array of QueryElements
4520 * o Collect lengths of query terms
4521 */
4522 static void
query_preflight_initialize(PreflightContext * context,const gchar * sexp)4523 query_preflight_initialize (PreflightContext *context,
4524 const gchar *sexp)
4525 {
4526 ESExp *sexp_parser;
4527 ESExpResult *result;
4528 gint esexp_error, i;
4529
4530 if (sexp == NULL || *sexp == '\0') {
4531 context->status = PREFLIGHT_LIST_ALL;
4532 return;
4533 }
4534
4535 sexp_parser = e_sexp_new ();
4536
4537 for (i = 0; i < G_N_ELEMENTS (check_symbols); i++) {
4538 if (check_symbols[i].subset) {
4539 e_sexp_add_ifunction (
4540 sexp_parser, 0, check_symbols[i].name,
4541 func_check_subset,
4542 GUINT_TO_POINTER (check_symbols[i].test));
4543 } else {
4544 e_sexp_add_function (
4545 sexp_parser, 0, check_symbols[i].name,
4546 func_check,
4547 GUINT_TO_POINTER (check_symbols[i].test));
4548 }
4549 }
4550
4551 e_sexp_input_text (sexp_parser, sexp, strlen (sexp));
4552 esexp_error = e_sexp_parse (sexp_parser);
4553
4554 if (esexp_error == -1) {
4555 context->status = PREFLIGHT_INVALID;
4556
4557 EBSQL_NOTE (
4558 PREFLIGHT,
4559 g_printerr ("PREFLIGHT INIT: Sexp parse error\n"));
4560 } else {
4561
4562 result = e_sexp_eval (sexp_parser);
4563 if (result) {
4564
4565 if (result->type == ESEXP_RES_ARRAY_PTR) {
4566
4567 /* Just steal the array away from the ESexpResult */
4568 context->constraints = result->value.ptrarray;
4569 result->value.ptrarray = NULL;
4570
4571 } else {
4572 context->status = PREFLIGHT_INVALID;
4573
4574 EBSQL_NOTE (
4575 PREFLIGHT,
4576 g_printerr ("PREFLIGHT INIT: ERROR, Did not get GPtrArray\n"));
4577 }
4578 }
4579
4580 e_sexp_result_free (sexp_parser, result);
4581 }
4582
4583 g_object_unref (sexp_parser);
4584
4585 EBSQL_NOTE (
4586 PREFLIGHT,
4587 g_printerr (
4588 "PREFLIGHT INIT: Completed with status %s\n",
4589 EBSQL_STATUS_STR (context->status)));
4590 }
4591
4592 typedef struct {
4593 EBookSqlite *ebsql;
4594 SummaryField *field;
4595 gboolean condition;
4596 } AttrListCheckData;
4597
4598 static gboolean
check_has_attr_list_cb(QueryElement * element,gint sub_level,gint offset,gpointer user_data)4599 check_has_attr_list_cb (QueryElement *element,
4600 gint sub_level,
4601 gint offset,
4602 gpointer user_data)
4603 {
4604 QueryFieldTest *test = (QueryFieldTest *) element;
4605 AttrListCheckData *data = (AttrListCheckData *) user_data;
4606
4607 /* We havent resolved all the fields at this stage yet */
4608 if (!test->field)
4609 test->field = summary_field_get (data->ebsql, test->field_id);
4610
4611 if (test->field && test->field->type == E_TYPE_CONTACT_ATTR_LIST)
4612 data->condition = TRUE;
4613
4614 /* Keep looping until we find one */
4615 return (data->condition == FALSE);
4616 }
4617
4618 static gboolean
check_different_fields_cb(QueryElement * element,gint sub_level,gint offset,gpointer user_data)4619 check_different_fields_cb (QueryElement *element,
4620 gint sub_level,
4621 gint offset,
4622 gpointer user_data)
4623 {
4624 QueryFieldTest *test = (QueryFieldTest *) element;
4625 AttrListCheckData *data = (AttrListCheckData *) user_data;
4626
4627 /* We havent resolved all the fields at this stage yet */
4628 if (!test->field)
4629 test->field = summary_field_get (data->ebsql, test->field_id);
4630
4631 if (test->field && data->field && test->field != data->field)
4632 data->condition = TRUE;
4633 else
4634 data->field = test->field;
4635
4636 /* Keep looping until we find one */
4637 return (data->condition == FALSE);
4638 }
4639
4640 /* What is done in this pass:
4641 * o Viability of the query is analyzed, i.e. can it be done with the summary columns.
4642 * o Phone numbers are parsed and loaded onto QueryPhoneTests
4643 * o Bitmask of auxiliary tables is collected
4644 */
4645 static void
query_preflight_check(PreflightContext * context,EBookSqlite * ebsql)4646 query_preflight_check (PreflightContext *context,
4647 EBookSqlite *ebsql)
4648 {
4649 gint i, n_elements;
4650 QueryElement **elements;
4651 SubQueryContext *ctx;
4652
4653 context->status = PREFLIGHT_OK;
4654
4655 if (context->constraints != NULL) {
4656 elements = (QueryElement **) context->constraints->pdata;
4657 n_elements = context->constraints->len;
4658 } else {
4659 elements = NULL;
4660 n_elements = 0;
4661 }
4662
4663 ctx = sub_query_context_new ();
4664
4665 for (i = 0; i < n_elements; i++) {
4666 QueryFieldTest *test;
4667 guint field_test;
4668
4669 EBSQL_NOTE (
4670 PREFLIGHT,
4671 g_printerr (
4672 "PREFLIGHT CHECK: Encountered: %s\n",
4673 EBSQL_QUERY_TYPE_STR (elements[i]->query)));
4674
4675 if (elements[i]->query >= BOOK_QUERY_SUB_FIRST) {
4676 AttrListCheckData data = { ebsql, NULL, FALSE };
4677
4678 switch (elements[i]->query) {
4679 case BOOK_QUERY_SUB_OR:
4680 /* An OR doesn't have to force us to use a LEFT JOIN, as long
4681 as all its sub-conditions are on the same field. */
4682 query_preflight_foreach_sub (elements,
4683 n_elements,
4684 i, FALSE,
4685 check_different_fields_cb,
4686 &data);
4687 /* falls through */
4688 case BOOK_QUERY_SUB_AND:
4689 sub_query_context_push (ctx, elements[i]->query, data.condition);
4690 break;
4691 case BOOK_QUERY_SUB_END:
4692 sub_query_context_pop (ctx);
4693 break;
4694
4695 /* It's too complicated to properly perform
4696 * the unary NOT operator on a constraint which
4697 * accesses attribute lists.
4698 *
4699 * Hint, if the contact has a "%.com" email address
4700 * and a "%.org" email address, what do we return
4701 * for (not (endswith "email" ".com") ?
4702 *
4703 * Currently we rely on DISTINCT to sort out
4704 * muliple results from the attribute list tables,
4705 * this breaks down with NOT.
4706 */
4707 case BOOK_QUERY_SUB_NOT:
4708 query_preflight_foreach_sub (elements,
4709 n_elements,
4710 i, FALSE,
4711 check_has_attr_list_cb,
4712 &data);
4713
4714 if (data.condition) {
4715 context->status = MAX (
4716 context->status,
4717 PREFLIGHT_NOT_SUMMARIZED);
4718 EBSQL_NOTE (
4719 PREFLIGHT,
4720 g_printerr (
4721 "PREFLIGHT CHECK: "
4722 "Setting invalid for NOT (mutli-attribute), "
4723 "new status: %s\n",
4724 EBSQL_STATUS_STR (context->status)));
4725 }
4726 break;
4727
4728 default:
4729 g_warn_if_reached ();
4730 }
4731
4732 continue;
4733 }
4734
4735 test = (QueryFieldTest *) elements[i];
4736 field_test = (EBookQueryTest) test->query;
4737
4738 if (!test->field)
4739 test->field = summary_field_get (ebsql, test->field_id);
4740
4741 /* Even if the field is not in the summary, we need to
4742 * retport unsupported errors if phone number queries are
4743 * issued while libphonenumber is unavailable
4744 */
4745 if (!test->field) {
4746
4747 /* Special case for e_book_query_any_field_contains().
4748 *
4749 * We interpret 'x-evolution-any-field' as E_CONTACT_FIELD_LAST
4750 */
4751 if (test->field_id == E_CONTACT_FIELD_LAST) {
4752
4753 /* If we search for a NULL or zero length string, it
4754 * means 'get all contacts', that is considered a summary
4755 * query but is handled differently (i.e. we just drop the
4756 * field tests and run a regular query).
4757 *
4758 * This is only true if the 'any field contains' query is
4759 * the only test in the constraints, however.
4760 */
4761 if (n_elements == 1 && (!test->value || !test->value[0])) {
4762
4763 context->status = MAX (context->status, PREFLIGHT_LIST_ALL);
4764 EBSQL_NOTE (
4765 PREFLIGHT,
4766 g_printerr (
4767 "PREFLIGHT CHECK: "
4768 "Encountered lonesome 'x-evolution-any-field' with empty value, "
4769 "new status: %s\n",
4770 EBSQL_STATUS_STR (context->status)));
4771 } else {
4772
4773 /* Searching for a value with 'x-evolution-any-field' is
4774 * not a summary query.
4775 */
4776 context->status = MAX (context->status, PREFLIGHT_NOT_SUMMARIZED);
4777 EBSQL_NOTE (
4778 PREFLIGHT,
4779 g_printerr (
4780 "PREFLIGHT CHECK: "
4781 "Encountered 'x-evolution-any-field', "
4782 "new status: %s\n",
4783 EBSQL_STATUS_STR (context->status)));
4784 }
4785
4786 } else {
4787
4788 /* Couldnt resolve the field, it's not a summary query */
4789 context->status = MAX (context->status, PREFLIGHT_NOT_SUMMARIZED);
4790 EBSQL_NOTE (
4791 PREFLIGHT,
4792 g_printerr (
4793 "PREFLIGHT CHECK: "
4794 "Field `%s' not in the summary, new status: %s\n",
4795 EBSQL_FIELD_ID_STR (test->field_id),
4796 EBSQL_STATUS_STR (context->status)));
4797 }
4798 }
4799
4800 if (test->field && test->field->type == E_TYPE_CONTACT_CERT) {
4801 /* For certificates, and later potentially other fields,
4802 * the only information in the summary is the fact that
4803 * they exist, or not. So the only check we can do from
4804 * the summary is BOOK_QUERY_EXISTS. */
4805 if (field_test != BOOK_QUERY_EXISTS) {
4806 context->status = MAX (context->status, PREFLIGHT_NOT_SUMMARIZED);
4807 EBSQL_NOTE (
4808 PREFLIGHT,
4809 g_printerr (
4810 "PREFLIGHT CHECK: "
4811 "Cannot perform '%s' check on existence summary field '%s', new status: %s\n",
4812 EBSQL_QUERY_TYPE_STR (field_test),
4813 EBSQL_FIELD_ID_STR (test->field_id),
4814 EBSQL_STATUS_STR (context->status)));
4815 }
4816 /* Bypass the other checks below which are not appropriate. */
4817 continue;
4818 }
4819
4820 switch (field_test) {
4821 case E_BOOK_QUERY_IS:
4822 break;
4823
4824 case BOOK_QUERY_EXISTS:
4825 case E_BOOK_QUERY_CONTAINS:
4826 case E_BOOK_QUERY_BEGINS_WITH:
4827 case E_BOOK_QUERY_ENDS_WITH:
4828 case E_BOOK_QUERY_REGEX_NORMAL:
4829
4830 /* All of these queries can only apply to string fields,
4831 * or fields which hold multiple strings
4832 */
4833 if (test->field) {
4834 if (test->field->type == G_TYPE_BOOLEAN &&
4835 field_test == BOOK_QUERY_EXISTS) {
4836 context->status = MAX (context->status, PREFLIGHT_NOT_SUMMARIZED);
4837 } else if (test->field->type != G_TYPE_STRING &&
4838 test->field->type != E_TYPE_CONTACT_ATTR_LIST) {
4839 context->status = MAX (context->status, PREFLIGHT_INVALID);
4840 EBSQL_NOTE (
4841 PREFLIGHT,
4842 g_printerr (
4843 "PREFLIGHT CHECK: "
4844 "Refusing pattern match on boolean field `%s', new status: %s\n",
4845 EBSQL_FIELD_ID_STR (test->field_id),
4846 EBSQL_STATUS_STR (context->status)));
4847 }
4848 }
4849
4850 break;
4851
4852 case BOOK_QUERY_EXISTS_VCARD:
4853 /* Exists vCard queries only supported in the fallback */
4854 context->status = MAX (context->status, PREFLIGHT_NOT_SUMMARIZED);
4855 EBSQL_NOTE (
4856 PREFLIGHT,
4857 g_printerr (
4858 "PREFLIGHT CHECK: "
4859 "Exists vCard requires full data, new status: %s\n",
4860 EBSQL_STATUS_STR (context->status)));
4861 break;
4862
4863 case E_BOOK_QUERY_REGEX_RAW:
4864 /* Raw regex queries only supported in the fallback */
4865 context->status = MAX (context->status, PREFLIGHT_NOT_SUMMARIZED);
4866 EBSQL_NOTE (
4867 PREFLIGHT,
4868 g_printerr (
4869 "PREFLIGHT CHECK: "
4870 "Raw regexp requires full data, new status: %s\n",
4871 EBSQL_STATUS_STR (context->status)));
4872 break;
4873
4874 case E_BOOK_QUERY_EQUALS_PHONE_NUMBER:
4875 case E_BOOK_QUERY_EQUALS_NATIONAL_PHONE_NUMBER:
4876 case E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER:
4877
4878 /* Phone number queries are supported so long as they are in the summary,
4879 * libphonenumber is available, and the phone number string is a valid one
4880 */
4881 if (!e_phone_number_is_supported ()) {
4882
4883 context->status = MAX (context->status, PREFLIGHT_UNSUPPORTED);
4884 EBSQL_NOTE (
4885 PREFLIGHT,
4886 g_printerr (
4887 "PREFLIGHT CHECK: "
4888 "Usupported phone number query, new status: %s\n",
4889 EBSQL_STATUS_STR (context->status)));
4890 } else {
4891 QueryPhoneTest *phone_test = (QueryPhoneTest *) test;
4892 EPhoneNumberCountrySource source;
4893 EPhoneNumber *number;
4894 const gchar *region_code;
4895
4896 if (phone_test->region)
4897 region_code = phone_test->region;
4898 else
4899 region_code = ebsql->priv->region_code;
4900
4901 number = e_phone_number_from_string (
4902 phone_test->value,
4903 region_code, NULL);
4904
4905 if (number == NULL) {
4906
4907 context->status = MAX (context->status, PREFLIGHT_INVALID);
4908 EBSQL_NOTE (
4909 PREFLIGHT,
4910 g_printerr (
4911 "PREFLIGHT CHECK: "
4912 "Invalid phone number `%s', new status: %s\n",
4913 phone_test->value,
4914 EBSQL_STATUS_STR (context->status)));
4915 } else {
4916 /* Collect values we'll need later while generating field
4917 * tests, no need to parse the phone number more than once
4918 */
4919 phone_test->national = e_phone_number_get_national_number (number);
4920 phone_test->country = e_phone_number_get_country_code (number, &source);
4921 phone_test->national = remove_leading_zeros (phone_test->national);
4922
4923 if (source == E_PHONE_NUMBER_COUNTRY_FROM_DEFAULT)
4924 phone_test->country = 0;
4925
4926 e_phone_number_free (number);
4927 }
4928 }
4929 break;
4930 }
4931
4932 if (test->field &&
4933 test->field->type == E_TYPE_CONTACT_ATTR_LIST) {
4934 gint aux_index = summary_field_get_index (ebsql, test->field_id);
4935
4936 /* It's really improbable that we ever get 64 fields in the summary
4937 * In any case we warn about this in e_book_sqlite_new_full().
4938 */
4939 g_warn_if_fail (aux_index >= 0 && aux_index < EBSQL_MAX_SUMMARY_FIELDS);
4940
4941 /* Just to mute a compiler warning when aux_index == -1 */
4942 aux_index = ABS (aux_index);
4943
4944 context->aux_mask |= (1 << aux_index);
4945 EBSQL_NOTE (
4946 PREFLIGHT,
4947 g_printerr (
4948 "PREFLIGHT CHECK: "
4949 "Adding auxiliary field `%s' to the mask\n",
4950 EBSQL_FIELD_ID_STR (test->field_id)));
4951
4952 /* If this condition is a *requirement* for the overall query to
4953 match a given record (i.e. there's no surrounding 'OR' but
4954 only 'AND'), then we can use an inner join for the query and
4955 it will be a lot more efficient. If records without this
4956 condition can also match the overall condition, then we must
4957 use LEFT JOIN. */
4958 if (sub_query_context_peek_cond_counter (ctx)) {
4959 context->left_join_mask |= (1 << aux_index);
4960 EBSQL_NOTE (
4961 PREFLIGHT,
4962 g_printerr (
4963 "PREFLIGHT CHECK: "
4964 "Using LEFT JOIN because auxiliary field is not absolute requirement\n"));
4965 }
4966 }
4967 }
4968
4969 sub_query_context_free (ctx);
4970 }
4971
4972 /* Handle special case of E_CONTACT_FULL_NAME
4973 *
4974 * For any query which accesses the full name field,
4975 * we need to also OR it with any of the related name
4976 * fields, IF those are found in the summary as well.
4977 */
4978 static void
query_preflight_substitute_full_name(PreflightContext * context,EBookSqlite * ebsql)4979 query_preflight_substitute_full_name (PreflightContext *context,
4980 EBookSqlite *ebsql)
4981 {
4982 gint i, j;
4983
4984 for (i = 0; context->constraints != NULL && i < context->constraints->len; i++) {
4985 SummaryField *family_name, *given_name, *nickname;
4986 QueryElement *element;
4987 QueryFieldTest *test;
4988
4989 element = g_ptr_array_index (context->constraints, i);
4990
4991 if (element->query >= BOOK_QUERY_SUB_FIRST)
4992 continue;
4993
4994 test = (QueryFieldTest *) element;
4995 if (test->field_id != E_CONTACT_FULL_NAME)
4996 continue;
4997
4998 family_name = summary_field_get (ebsql, E_CONTACT_FAMILY_NAME);
4999 given_name = summary_field_get (ebsql, E_CONTACT_GIVEN_NAME);
5000 nickname = summary_field_get (ebsql, E_CONTACT_NICKNAME);
5001
5002 /* If any of these are in the summary, then we'll construct
5003 * a grouped OR statment for this E_CONTACT_FULL_NAME test */
5004 if (family_name || given_name || nickname) {
5005 /* Add the OR directly before the E_CONTACT_FULL_NAME test */
5006 constraints_insert_delimiter (context->constraints, i, BOOK_QUERY_SUB_OR);
5007
5008 j = i + 2;
5009
5010 if (family_name)
5011 constraints_insert_field_test (
5012 context->constraints, j++,
5013 family_name, test->query,
5014 test->value);
5015
5016 if (given_name)
5017 constraints_insert_field_test (
5018 context->constraints, j++,
5019 given_name, test->query,
5020 test->value);
5021
5022 if (nickname)
5023 constraints_insert_field_test (
5024 context->constraints, j++,
5025 nickname, test->query,
5026 test->value);
5027
5028 constraints_insert_delimiter (context->constraints, j, BOOK_QUERY_SUB_END);
5029
5030 i = j;
5031 }
5032 }
5033 }
5034
5035 static void
query_preflight(PreflightContext * context,EBookSqlite * ebsql,const gchar * sexp)5036 query_preflight (PreflightContext *context,
5037 EBookSqlite *ebsql,
5038 const gchar *sexp)
5039 {
5040 EBSQL_NOTE (PREFLIGHT, g_printerr ("PREFLIGHT BEGIN\n"));
5041 query_preflight_initialize (context, sexp);
5042
5043 if (context->status == PREFLIGHT_OK) {
5044
5045 query_preflight_check (context, ebsql);
5046
5047 /* No need to change the constraints if we're not
5048 * going to generate statements with it
5049 */
5050 if (context->status == PREFLIGHT_OK) {
5051 EBSQL_NOTE (
5052 PREFLIGHT,
5053 g_printerr ("PREFLIGHT: Substituting full name\n"));
5054
5055 /* Handle E_CONTACT_FULL_NAME substitutions */
5056 query_preflight_substitute_full_name (context, ebsql);
5057
5058 } else {
5059 EBSQL_NOTE (PREFLIGHT, g_printerr ("PREFLIGHT: Clearing context\n"));
5060
5061 /* We might use this context to perform a fallback query,
5062 * so let's clear out all the constraints now
5063 */
5064 preflight_context_clear (context);
5065 }
5066 }
5067
5068 EBSQL_NOTE (
5069 PREFLIGHT,
5070 g_printerr (
5071 "PREFLIGHT END (status: %s)\n",
5072 EBSQL_STATUS_STR (context->status)));
5073 }
5074
5075 /**********************************************************
5076 * Field Test Generators *
5077 **********************************************************
5078 *
5079 * This section contains the field test generators for
5080 * various EBookQueryTest types. When implementing new
5081 * query types, a new GenerateFieldTest needs to be created
5082 * and added to the table below.
5083 */
5084
5085 typedef void (* GenerateFieldTest) (EBookSqlite *ebsql,
5086 GString *string,
5087 QueryFieldTest *test);
5088
5089 /* This function escapes characters which need escaping
5090 * for LIKE statements as well as the single quotes.
5091 *
5092 * The return value is not suitable to be formatted
5093 * with %Q or %q
5094 */
5095 static gchar *
ebsql_normalize_for_like(QueryFieldTest * test,gboolean reverse_string,gboolean * escape_needed)5096 ebsql_normalize_for_like (QueryFieldTest *test,
5097 gboolean reverse_string,
5098 gboolean *escape_needed)
5099 {
5100 GString *str;
5101 size_t len;
5102 gchar c;
5103 gboolean escape_modifier_needed = FALSE;
5104 const gchar *normal = NULL;
5105 const gchar *ptr;
5106 const gchar *str_to_escape;
5107 gchar *reverse = NULL;
5108 gchar *freeme = NULL;
5109
5110 if (test->field_id == E_CONTACT_UID ||
5111 test->field_id == E_CONTACT_REV) {
5112 normal = test->value;
5113 } else {
5114 freeme = e_util_utf8_normalize (test->value);
5115 normal = freeme;
5116 }
5117
5118 if (reverse_string) {
5119 reverse = g_utf8_strreverse (normal, -1);
5120 str_to_escape = reverse;
5121 } else
5122 str_to_escape = normal;
5123
5124 /* Just assume each character must be escaped. The result of this function
5125 * is discarded shortly after calling this function. Therefore it's
5126 * acceptable to possibly allocate twice the memory needed.
5127 */
5128 len = strlen (str_to_escape);
5129 str = g_string_sized_new (2 * len + 4 + strlen (EBSQL_ESCAPE_SEQUENCE) - 1);
5130
5131 ptr = str_to_escape;
5132 while ((c = *ptr++)) {
5133 if (c == '\'') {
5134 g_string_append_c (str, '\'');
5135 } else if (c == '%' || c == '_' || c == '^') {
5136 g_string_append_c (str, '^');
5137 escape_modifier_needed = TRUE;
5138 }
5139
5140 g_string_append_c (str, c);
5141 }
5142
5143 if (escape_needed)
5144 *escape_needed = escape_modifier_needed;
5145
5146 g_free (freeme);
5147 g_free (reverse);
5148
5149 return g_string_free (str, FALSE);
5150 }
5151
5152 static void
field_test_query_is(EBookSqlite * ebsql,GString * string,QueryFieldTest * test)5153 field_test_query_is (EBookSqlite *ebsql,
5154 GString *string,
5155 QueryFieldTest *test)
5156 {
5157 SummaryField *field = test->field;
5158 gchar *normal;
5159
5160 ebsql_string_append_column (string, field, NULL);
5161
5162 if (test->field_id == E_CONTACT_UID ||
5163 test->field_id == E_CONTACT_REV) {
5164 /* UID & REV fields are not normalized in the summary */
5165 ebsql_string_append_printf (string, " = %Q", test->value);
5166 } else {
5167 normal = e_util_utf8_normalize (test->value);
5168 ebsql_string_append_printf (string, " = %Q", normal);
5169 g_free (normal);
5170 }
5171 }
5172
5173 static void
field_test_query_contains(EBookSqlite * ebsql,GString * string,QueryFieldTest * test)5174 field_test_query_contains (EBookSqlite *ebsql,
5175 GString *string,
5176 QueryFieldTest *test)
5177 {
5178 SummaryField *field = test->field;
5179 gboolean need_escape;
5180 gchar *escaped;
5181
5182 escaped = ebsql_normalize_for_like (test, FALSE, &need_escape);
5183
5184 g_string_append_c (string, '(');
5185
5186 ebsql_string_append_column (string, field, NULL);
5187 g_string_append (string, " IS NOT NULL AND ");
5188 ebsql_string_append_column (string, field, NULL);
5189 g_string_append (string, " LIKE '%");
5190 g_string_append (string, escaped);
5191 g_string_append (string, "%'");
5192
5193 if (need_escape)
5194 g_string_append (string, EBSQL_ESCAPE_SEQUENCE);
5195
5196 g_string_append_c (string, ')');
5197
5198 g_free (escaped);
5199 }
5200
5201 static void
field_test_query_begins_with(EBookSqlite * ebsql,GString * string,QueryFieldTest * test)5202 field_test_query_begins_with (EBookSqlite *ebsql,
5203 GString *string,
5204 QueryFieldTest *test)
5205 {
5206 SummaryField *field = test->field;
5207 gboolean need_escape;
5208 gchar *escaped;
5209
5210 escaped = ebsql_normalize_for_like (test, FALSE, &need_escape);
5211
5212 g_string_append_c (string, '(');
5213 ebsql_string_append_column (string, field, NULL);
5214 g_string_append (string, " IS NOT NULL AND ");
5215
5216 ebsql_string_append_column (string, field, NULL);
5217 g_string_append (string, " LIKE \'");
5218 g_string_append (string, escaped);
5219 g_string_append (string, "%\'");
5220
5221 if (need_escape)
5222 g_string_append (string, EBSQL_ESCAPE_SEQUENCE);
5223 g_string_append_c (string, ')');
5224
5225 g_free (escaped);
5226 }
5227
5228 static void
field_test_query_ends_with(EBookSqlite * ebsql,GString * string,QueryFieldTest * test)5229 field_test_query_ends_with (EBookSqlite *ebsql,
5230 GString *string,
5231 QueryFieldTest *test)
5232 {
5233 SummaryField *field = test->field;
5234 gboolean need_escape;
5235 gchar *escaped;
5236
5237 if ((field->index & INDEX_FLAG (SUFFIX)) != 0) {
5238
5239 escaped = ebsql_normalize_for_like (test, TRUE, &need_escape);
5240
5241 g_string_append_c (string, '(');
5242 ebsql_string_append_column (string, field, EBSQL_SUFFIX_REVERSE);
5243 g_string_append (string, " IS NOT NULL AND ");
5244
5245 ebsql_string_append_column (string, field, EBSQL_SUFFIX_REVERSE);
5246 g_string_append (string, " LIKE \'");
5247 g_string_append (string, escaped);
5248 g_string_append (string, "%\'");
5249
5250 } else {
5251
5252 escaped = ebsql_normalize_for_like (test, FALSE, &need_escape);
5253 g_string_append_c (string, '(');
5254
5255 ebsql_string_append_column (string, field, NULL);
5256 g_string_append (string, " IS NOT NULL AND ");
5257
5258 ebsql_string_append_column (string, field, NULL);
5259 g_string_append (string, " LIKE \'%");
5260 g_string_append (string, escaped);
5261 g_string_append_c (string, '\'');
5262 }
5263
5264 if (need_escape)
5265 g_string_append (string, EBSQL_ESCAPE_SEQUENCE);
5266
5267 g_string_append_c (string, ')');
5268 g_free (escaped);
5269 }
5270
5271 static void
field_test_query_eqphone(EBookSqlite * ebsql,GString * string,QueryFieldTest * test)5272 field_test_query_eqphone (EBookSqlite *ebsql,
5273 GString *string,
5274 QueryFieldTest *test)
5275 {
5276 SummaryField *field = test->field;
5277 QueryPhoneTest *phone_test = (QueryPhoneTest *) test;
5278
5279 if ((field->index & INDEX_FLAG (PHONE)) != 0) {
5280
5281 g_string_append_c (string, '(');
5282 ebsql_string_append_column (string, field, EBSQL_SUFFIX_PHONE);
5283 ebsql_string_append_printf (string, " = %Q AND ", phone_test->national);
5284
5285 /* For exact matches, a country code qualifier is required by both
5286 * query input and row input
5287 */
5288 ebsql_string_append_column (string, field, EBSQL_SUFFIX_COUNTRY);
5289 g_string_append (string, " != 0 AND ");
5290
5291 ebsql_string_append_column (string, field, EBSQL_SUFFIX_COUNTRY);
5292 ebsql_string_append_printf (string, " = %d", phone_test->country);
5293 g_string_append_c (string, ')');
5294
5295 } else {
5296
5297 /* No indexed columns available, perform the fallback */
5298 g_string_append (string, EBSQL_FUNC_EQPHONE_EXACT " (");
5299 ebsql_string_append_column (string, field, NULL);
5300 ebsql_string_append_printf (string, ", %Q)", test->value);
5301 }
5302 }
5303
5304 static void
field_test_query_eqphone_national(EBookSqlite * ebsql,GString * string,QueryFieldTest * test)5305 field_test_query_eqphone_national (EBookSqlite *ebsql,
5306 GString *string,
5307 QueryFieldTest *test)
5308 {
5309
5310 SummaryField *field = test->field;
5311 QueryPhoneTest *phone_test = (QueryPhoneTest *) test;
5312
5313 if ((field->index & INDEX_FLAG (PHONE)) != 0) {
5314
5315 /* Only a compound expression if there is a country code */
5316 if (phone_test->country)
5317 g_string_append_c (string, '(');
5318
5319 /* Generate: phone = %Q */
5320 ebsql_string_append_column (string, field, EBSQL_SUFFIX_PHONE);
5321 ebsql_string_append_printf (string, " = %Q", phone_test->national);
5322
5323 /* When doing a national search, no need to check country
5324 * code unless the query number also has a country code
5325 */
5326 if (phone_test->country) {
5327 /* Generate: (phone = %Q AND (country = 0 OR country = %d)) */
5328 g_string_append (string, " AND (");
5329 ebsql_string_append_column (string, field, EBSQL_SUFFIX_COUNTRY);
5330 g_string_append (string, " = 0 OR ");
5331 ebsql_string_append_column (string, field, EBSQL_SUFFIX_COUNTRY);
5332 ebsql_string_append_printf (string, " = %d))", phone_test->country);
5333
5334 }
5335
5336 } else {
5337
5338 /* No indexed columns available, perform the fallback */
5339 g_string_append (string, EBSQL_FUNC_EQPHONE_NATIONAL " (");
5340 ebsql_string_append_column (string, field, NULL);
5341 ebsql_string_append_printf (string, ", %Q)", test->value);
5342 }
5343 }
5344
5345 static void
field_test_query_eqphone_short(EBookSqlite * ebsql,GString * string,QueryFieldTest * test)5346 field_test_query_eqphone_short (EBookSqlite *ebsql,
5347 GString *string,
5348 QueryFieldTest *test)
5349 {
5350 SummaryField *field = test->field;
5351
5352 /* No quick way to do the short match */
5353 g_string_append (string, EBSQL_FUNC_EQPHONE_SHORT " (");
5354 ebsql_string_append_column (string, field, NULL);
5355 ebsql_string_append_printf (string, ", %Q)", test->value);
5356 }
5357
5358 static void
field_test_query_regex_normal(EBookSqlite * ebsql,GString * string,QueryFieldTest * test)5359 field_test_query_regex_normal (EBookSqlite *ebsql,
5360 GString *string,
5361 QueryFieldTest *test)
5362 {
5363 SummaryField *field = test->field;
5364 gchar *normal;
5365
5366 normal = e_util_utf8_normalize (test->value);
5367
5368 if (field->aux_table)
5369 ebsql_string_append_printf (
5370 string, "%s.value REGEXP %Q",
5371 field->aux_table_symbolic,
5372 normal);
5373 else
5374 ebsql_string_append_printf (
5375 string, "summary.%s REGEXP %Q",
5376 field->dbname,
5377 normal);
5378
5379 g_free (normal);
5380 }
5381
5382 static void
field_test_query_exists(EBookSqlite * ebsql,GString * string,QueryFieldTest * test)5383 field_test_query_exists (EBookSqlite *ebsql,
5384 GString *string,
5385 QueryFieldTest *test)
5386 {
5387 SummaryField *field = test->field;
5388
5389 ebsql_string_append_column (string, field, NULL);
5390
5391 if (test->field->type == E_TYPE_CONTACT_CERT)
5392 ebsql_string_append_printf (string, " IS NOT '0'");
5393 else
5394 ebsql_string_append_printf (string, " IS NOT NULL");
5395 }
5396
5397 /* Lookup table for field test generators per EBookQueryTest,
5398 *
5399 * WARNING: This must stay in line with the EBookQueryTest definition.
5400 */
5401 static const GenerateFieldTest field_test_func_table[] = {
5402 field_test_query_is, /* E_BOOK_QUERY_IS */
5403 field_test_query_contains, /* E_BOOK_QUERY_CONTAINS */
5404 field_test_query_begins_with, /* E_BOOK_QUERY_BEGINS_WITH */
5405 field_test_query_ends_with, /* E_BOOK_QUERY_ENDS_WITH */
5406 field_test_query_eqphone, /* E_BOOK_QUERY_EQUALS_PHONE_NUMBER */
5407 field_test_query_eqphone_national, /* E_BOOK_QUERY_EQUALS_NATIONAL_PHONE_NUMBER */
5408 field_test_query_eqphone_short, /* E_BOOK_QUERY_EQUALS_SHORT_PHONE_NUMBER */
5409 field_test_query_regex_normal, /* E_BOOK_QUERY_REGEX_NORMAL */
5410 NULL /* Requires fallback */, /* E_BOOK_QUERY_REGEX_RAW */
5411 field_test_query_exists, /* BOOK_QUERY_EXISTS */
5412 NULL /* Requires fallback */ /* BOOK_QUERY_EXISTS_VCARD */
5413 };
5414
5415 /**********************************************************
5416 * Querying Contacts *
5417 **********************************************************/
5418
5419 /* The various search types indicate what should be fetched
5420 */
5421 typedef enum {
5422 SEARCH_FULL, /* Get a list of EbSqlSearchData */
5423 SEARCH_UID_AND_REV, /* Get a list of EbSqlSearchData, with shallow vcards only containing UID & REV */
5424 SEARCH_UID, /* Get a list of UID strings */
5425 SEARCH_COUNT, /* Get the number of matching rows */
5426 } SearchType;
5427
5428 static void
ebsql_generate_constraints(EBookSqlite * ebsql,GString * string,GPtrArray * constraints,const gchar * sexp)5429 ebsql_generate_constraints (EBookSqlite *ebsql,
5430 GString *string,
5431 GPtrArray *constraints,
5432 const gchar *sexp)
5433 {
5434 SubQueryContext *ctx;
5435 QueryDelimiter *delim;
5436 QueryFieldTest *test;
5437 QueryElement **elements;
5438 gint n_elements, i;
5439
5440 /* If there are no constraints, we generate the fallback constraint for 'sexp' */
5441 if (constraints == NULL) {
5442 ebsql_string_append_printf (
5443 string,
5444 EBSQL_FUNC_COMPARE_VCARD " (%Q, %s)",
5445 sexp, EBSQL_VCARD_FRAGMENT (ebsql));
5446 return;
5447 }
5448
5449 elements = (QueryElement **) constraints->pdata;
5450 n_elements = constraints->len;
5451
5452 ctx = sub_query_context_new ();
5453
5454 for (i = 0; i < n_elements; i++) {
5455 GenerateFieldTest generate_test_func = NULL;
5456
5457 /* Seperate field tests with the appropriate grouping */
5458 if (elements[i]->query != BOOK_QUERY_SUB_END &&
5459 sub_query_context_increment (ctx) > 0) {
5460 guint delim_type = sub_query_context_peek_type (ctx);
5461
5462 switch (delim_type) {
5463 case BOOK_QUERY_SUB_AND:
5464
5465 g_string_append (string, " AND ");
5466 break;
5467
5468 case BOOK_QUERY_SUB_OR:
5469
5470 g_string_append (string, " OR ");
5471 break;
5472
5473 case BOOK_QUERY_SUB_NOT:
5474
5475 /* Nothing to do between children of NOT,
5476 * there should only ever be one child of NOT anyway
5477 */
5478 break;
5479
5480 case BOOK_QUERY_SUB_END:
5481 default:
5482 g_warn_if_reached ();
5483 }
5484 }
5485
5486 if (elements[i]->query >= BOOK_QUERY_SUB_FIRST) {
5487 delim = (QueryDelimiter *) elements[i];
5488
5489 switch (delim->query) {
5490
5491 case BOOK_QUERY_SUB_NOT:
5492
5493 /* NOT is a unary operator and as such
5494 * comes before the opening parenthesis
5495 */
5496 g_string_append (string, "NOT ");
5497
5498 /* Fall through */
5499
5500 case BOOK_QUERY_SUB_AND:
5501 case BOOK_QUERY_SUB_OR:
5502
5503 /* Open a grouped statement and push the context */
5504 sub_query_context_push (ctx, delim->query, FALSE);
5505 g_string_append_c (string, '(');
5506 break;
5507
5508 case BOOK_QUERY_SUB_END:
5509 /* Close a grouped statement and pop the context */
5510 g_string_append_c (string, ')');
5511 sub_query_context_pop (ctx);
5512 break;
5513 default:
5514 g_warn_if_reached ();
5515 }
5516
5517 continue;
5518 }
5519
5520 /* Find the appropriate field test generator */
5521 test = (QueryFieldTest *) elements[i];
5522 if (test->query < G_N_ELEMENTS (field_test_func_table))
5523 generate_test_func = field_test_func_table[test->query];
5524
5525 /* These should never happen, if it does it should be
5526 * fixed in the preflight checks
5527 */
5528 g_warn_if_fail (generate_test_func != NULL);
5529 g_warn_if_fail (test->field != NULL);
5530
5531 /* Generate the field test */
5532 /* coverity[var_deref_op] */
5533 generate_test_func (ebsql, string, test);
5534 }
5535
5536 sub_query_context_free (ctx);
5537 }
5538
5539 /* Generates the SELECT portion of the query, this will take care of
5540 * preparing the context of the query, and add the needed JOIN statements
5541 * based on which fields are referenced in the query expression.
5542 *
5543 * This also handles getting the correct callback and asking for the
5544 * right data depending on the 'search_type'
5545 */
5546 static EbSqlRowFunc
ebsql_generate_select(EBookSqlite * ebsql,GString * string,SearchType search_type,PreflightContext * context,GError ** error)5547 ebsql_generate_select (EBookSqlite *ebsql,
5548 GString *string,
5549 SearchType search_type,
5550 PreflightContext *context,
5551 GError **error)
5552 {
5553 EbSqlRowFunc callback = NULL;
5554 gboolean add_auxiliary_tables = FALSE;
5555 gint i;
5556
5557 if (context->status == PREFLIGHT_OK &&
5558 context->aux_mask != 0)
5559 add_auxiliary_tables = TRUE;
5560
5561 g_string_append (string, "SELECT ");
5562 if (add_auxiliary_tables)
5563 g_string_append (string, "DISTINCT ");
5564
5565 switch (search_type) {
5566 case SEARCH_FULL:
5567 callback = collect_full_results_cb;
5568 g_string_append (string, "summary.uid, ");
5569 g_string_append (string, EBSQL_VCARD_FRAGMENT (ebsql));
5570 g_string_append (string, ", summary.bdata ");
5571 break;
5572 case SEARCH_UID_AND_REV:
5573 callback = collect_lean_results_cb;
5574 g_string_append (string, "summary.uid, summary.Rev, summary.bdata ");
5575 break;
5576 case SEARCH_UID:
5577 callback = collect_uid_results_cb;
5578 g_string_append (string, "summary.uid ");
5579 break;
5580 case SEARCH_COUNT:
5581 callback = get_count_cb;
5582 if (context->aux_mask != 0)
5583 g_string_append (string, "count (DISTINCT summary.uid) ");
5584 else
5585 g_string_append (string, "count (*) ");
5586 break;
5587 }
5588
5589 ebsql_string_append_printf (string, "FROM %Q AS summary", ebsql->priv->folderid);
5590
5591 /* Add any required auxiliary tables into the query context */
5592 if (add_auxiliary_tables) {
5593 for (i = 0; i < ebsql->priv->n_summary_fields; i++) {
5594
5595 /* We cap this at EBSQL_MAX_SUMMARY_FIELDS (64 bits) at creation time */
5596 if ((context->aux_mask & (1 << i)) != 0) {
5597 SummaryField *field = &(ebsql->priv->summary_fields[i]);
5598 gboolean left_join = (context->left_join_mask >> i) & 1;
5599
5600 /* Note the '+' in the JOIN statement.
5601 *
5602 * This plus makes the uid's index ineligable to participate
5603 * in any indexing.
5604 *
5605 * Without this, the indexes which we prefer for prefix or
5606 * suffix matching in the auxiliary tables are ignored and
5607 * only considered on exact matches.
5608 *
5609 * This is crucial to ensure that the uid index does not
5610 * compete with the value index in constraints such as:
5611 *
5612 * WHERE email_list.value LIKE "boogieman%"
5613 */
5614 ebsql_string_append_printf (
5615 string, " %sJOIN %Q AS %s ON %s%s.uid = summary.uid",
5616 left_join ? "LEFT " : "",
5617 field->aux_table,
5618 field->aux_table_symbolic,
5619 left_join ? "" : "+",
5620 field->aux_table_symbolic);
5621 }
5622 }
5623 }
5624
5625 return callback;
5626 }
5627
5628 static gboolean
ebsql_is_autocomplete_query(PreflightContext * context)5629 ebsql_is_autocomplete_query (PreflightContext *context)
5630 {
5631 QueryFieldTest *test;
5632 QueryElement **elements;
5633 gint n_elements, i;
5634 int non_aux_fields = 0;
5635
5636 if (context->status != PREFLIGHT_OK || context->aux_mask == 0)
5637 return FALSE;
5638
5639 elements = (QueryElement **) context->constraints->pdata;
5640 n_elements = context->constraints->len;
5641
5642 for (i = 0; i < n_elements; i++) {
5643 test = (QueryFieldTest *) elements[i];
5644
5645 /* For these, check if the field being operated on is
5646 an auxiliary field or not. */
5647 if (elements[i]->query == E_BOOK_QUERY_BEGINS_WITH ||
5648 elements[i]->query == E_BOOK_QUERY_ENDS_WITH ||
5649 elements[i]->query == E_BOOK_QUERY_IS ||
5650 elements[i]->query == BOOK_QUERY_EXISTS ||
5651 elements[i]->query == E_BOOK_QUERY_CONTAINS) {
5652 if (test->field->type != E_TYPE_CONTACT_ATTR_LIST)
5653 non_aux_fields++;
5654 continue;
5655 }
5656
5657 /* Nothing else is allowed other than "(or" ... ")" */
5658 if (elements[i]->query != BOOK_QUERY_SUB_OR &&
5659 elements[i]->query != BOOK_QUERY_SUB_END)
5660 return FALSE;
5661 }
5662
5663 /* If there were no non-aux fields being queried, don't bother */
5664 return non_aux_fields != 0;
5665 }
5666
5667 static EbSqlRowFunc
ebsql_generate_autocomplete_query(EBookSqlite * ebsql,GString * string,SearchType search_type,PreflightContext * context,GError ** error)5668 ebsql_generate_autocomplete_query (EBookSqlite *ebsql,
5669 GString *string,
5670 SearchType search_type,
5671 PreflightContext *context,
5672 GError **error)
5673 {
5674 QueryElement **elements;
5675 gint n_elements, i;
5676 guint64 aux_mask = context->aux_mask;
5677 guint64 left_join_mask = context->left_join_mask;
5678 EbSqlRowFunc callback;
5679 gboolean first = TRUE;
5680
5681 elements = (QueryElement **) context->constraints->pdata;
5682 n_elements = context->constraints->len;
5683
5684 /* First the queries which use aux tables. */
5685 for (i = 0; i < n_elements; i++) {
5686 GenerateFieldTest generate_test_func = NULL;
5687 QueryFieldTest *test;
5688 gint aux_index;
5689
5690 if (elements[i]->query == BOOK_QUERY_SUB_OR ||
5691 elements[i]->query == BOOK_QUERY_SUB_END)
5692 continue;
5693
5694 test = (QueryFieldTest *) elements[i];
5695 if (test->field->type != E_TYPE_CONTACT_ATTR_LIST)
5696 continue;
5697
5698 aux_index = summary_field_get_index (ebsql, test->field_id);
5699 g_warn_if_fail (aux_index >= 0 && aux_index < EBSQL_MAX_SUMMARY_FIELDS);
5700
5701 /* Just to mute a compiler warning when aux_index == -1 */
5702 aux_index = ABS (aux_index);
5703
5704 context->aux_mask = (1 << aux_index);
5705 context->left_join_mask = 0;
5706
5707 callback = ebsql_generate_select (ebsql, string, search_type, context, error);
5708 g_string_append (string, " WHERE ");
5709 context->aux_mask = aux_mask;
5710 context->left_join_mask = left_join_mask;
5711 if (!callback)
5712 return NULL;
5713
5714 generate_test_func = field_test_func_table[test->query];
5715 generate_test_func (ebsql, string, test);
5716
5717 g_string_append (string, " UNION ");
5718 }
5719 /* Finally, generate the SELECT for the primary fields. */
5720 context->aux_mask = 0;
5721 callback = ebsql_generate_select (ebsql, string, search_type, context, error);
5722 context->aux_mask = aux_mask;
5723 if (!callback)
5724 return NULL;
5725
5726 g_string_append (string, " WHERE ");
5727
5728 for (i = 0; i < n_elements; i++) {
5729 GenerateFieldTest generate_test_func = NULL;
5730 QueryFieldTest *test;
5731
5732 if (elements[i]->query == BOOK_QUERY_SUB_OR ||
5733 elements[i]->query == BOOK_QUERY_SUB_END)
5734 continue;
5735
5736 test = (QueryFieldTest *) elements[i];
5737 if (test->field->type == E_TYPE_CONTACT_ATTR_LIST)
5738 continue;
5739
5740 if (!first)
5741 g_string_append (string, " OR ");
5742 else
5743 first = FALSE;
5744
5745 generate_test_func = field_test_func_table[test->query];
5746 generate_test_func (ebsql, string, test);
5747 }
5748
5749 return callback;
5750 }
5751 static gboolean
ebsql_do_search_query(EBookSqlite * ebsql,PreflightContext * context,const gchar * sexp,SearchType search_type,GSList ** return_data,GCancellable * cancellable,GError ** error)5752 ebsql_do_search_query (EBookSqlite *ebsql,
5753 PreflightContext *context,
5754 const gchar *sexp,
5755 SearchType search_type,
5756 GSList **return_data,
5757 GCancellable *cancellable,
5758 GError **error)
5759 {
5760 GString *string;
5761 EbSqlRowFunc callback = NULL;
5762 gboolean success = FALSE;
5763
5764 /* We might calculate a reasonable estimation of bytes
5765 * during the preflight checks */
5766 string = g_string_sized_new (GENERATED_QUERY_BYTES);
5767
5768 /* Extra special case. For the common case of the email composer's
5769 addressbook autocompletion, we really want the most optimal query.
5770 So check for it and use a basically hand-crafted one. */
5771 if (ebsql_is_autocomplete_query(context)) {
5772 callback = ebsql_generate_autocomplete_query (ebsql, string, search_type, context, error);
5773 } else {
5774 /* Generate the leading SELECT statement */
5775 callback = ebsql_generate_select (
5776 ebsql, string, search_type, context, error);
5777
5778 if (callback &&
5779 EBSQL_STATUS_GEN_CONSTRAINTS (context->status)) {
5780 /*
5781 * Now generate the search expression on the main contacts table
5782 */
5783 g_string_append (string, " WHERE ");
5784 ebsql_generate_constraints (
5785 ebsql, string, context->constraints, sexp);
5786 }
5787 }
5788
5789 if (callback)
5790 success = ebsql_exec (
5791 ebsql, string->str,
5792 callback, return_data,
5793 cancellable, error);
5794
5795 g_string_free (string, TRUE);
5796
5797 return success;
5798 }
5799
5800 /* ebsql_search_query:
5801 * @ebsql: An EBookSqlite
5802 * @sexp: The search expression, or NULL for all contacts
5803 * @search_type: Indicates what kind of data should be returned
5804 * @return_data: A list of data fetched from the DB, as specified by 'search_type'
5805 * @error: Location to store any error which may have occurred
5806 *
5807 * This is the main common entry point for querying contacts.
5808 *
5809 * If the query cannot be satisfied with the summary, then
5810 * a fallback will automatically be used.
5811 */
5812 static gboolean
ebsql_search_query(EBookSqlite * ebsql,const gchar * sexp,SearchType search_type,GSList ** return_data,GCancellable * cancellable,GError ** error)5813 ebsql_search_query (EBookSqlite *ebsql,
5814 const gchar *sexp,
5815 SearchType search_type,
5816 GSList **return_data,
5817 GCancellable *cancellable,
5818 GError **error)
5819 {
5820 PreflightContext context = PREFLIGHT_CONTEXT_INIT;
5821 gboolean success = FALSE;
5822
5823 /* Now start with the query preflighting */
5824 query_preflight (&context, ebsql, sexp);
5825
5826 switch (context.status) {
5827 case PREFLIGHT_OK:
5828 case PREFLIGHT_LIST_ALL:
5829 case PREFLIGHT_NOT_SUMMARIZED:
5830 /* No errors, let's really search */
5831 success = ebsql_do_search_query (
5832 ebsql, &context, sexp,
5833 search_type, return_data,
5834 cancellable, error);
5835 break;
5836
5837 case PREFLIGHT_INVALID:
5838 EBSQL_SET_ERROR (
5839 error,
5840 E_BOOK_SQLITE_ERROR_INVALID_QUERY,
5841 _("Invalid query: %s"), sexp);
5842 break;
5843
5844 case PREFLIGHT_UNSUPPORTED:
5845 EBSQL_SET_ERROR_LITERAL (
5846 error,
5847 E_BOOK_SQLITE_ERROR_UNSUPPORTED_QUERY,
5848 _("Query contained unsupported elements"));
5849 break;
5850 }
5851
5852 preflight_context_clear (&context);
5853
5854 return success;
5855 }
5856
5857 /******************************************************************
5858 * EbSqlCursor Implementation *
5859 ******************************************************************/
5860 typedef struct _CursorState CursorState;
5861
5862 struct _CursorState {
5863 gchar **values; /* The current cursor position, results will be returned after this position */
5864 gchar *last_uid; /* The current cursor contact UID position, used as a tie breaker */
5865 EbSqlCursorOrigin position; /* The position is updated with the cursor state and is used to distinguish
5866 * between the beginning and the ending of the cursor's contact list.
5867 * While the cursor is in a non-null state, the position will be
5868 * EBSQL_CURSOR_ORIGIN_CURRENT.
5869 */
5870 };
5871
5872 struct _EbSqlCursor {
5873 EBookBackendSExp *sexp; /* An EBookBackendSExp based on the query, used by e_book_sqlite_cursor_compare () */
5874 gchar *select_vcards; /* The first fragment when querying results */
5875 gchar *select_count; /* The first fragment when querying contact counts */
5876 gchar *query; /* The SQL query expression derived from the passed search expression */
5877 gchar *order; /* The normal order SQL query fragment to append at the end, containing ORDER BY etc */
5878 gchar *reverse_order; /* The reverse order SQL query fragment to append at the end, containing ORDER BY etc */
5879
5880 EContactField *sort_fields; /* The fields to sort in a query in the order or sort priority */
5881 EBookCursorSortType *sort_types; /* The sort method to use for each field */
5882 gint n_sort_fields; /* The amound of sort fields */
5883
5884 CursorState state;
5885 };
5886
5887 static CursorState *cursor_state_copy (EbSqlCursor *cursor,
5888 CursorState *state);
5889 static void cursor_state_free (EbSqlCursor *cursor,
5890 CursorState *state);
5891 static void cursor_state_clear (EbSqlCursor *cursor,
5892 CursorState *state,
5893 EbSqlCursorOrigin position);
5894 static void cursor_state_set_from_contact (EBookSqlite *ebsql,
5895 EbSqlCursor *cursor,
5896 CursorState *state,
5897 EContact *contact);
5898 static void cursor_state_set_from_vcard (EBookSqlite *ebsql,
5899 EbSqlCursor *cursor,
5900 CursorState *state,
5901 const gchar *vcard);
5902
5903 static CursorState *
cursor_state_copy(EbSqlCursor * cursor,CursorState * state)5904 cursor_state_copy (EbSqlCursor *cursor,
5905 CursorState *state)
5906 {
5907 CursorState *copy;
5908 gint i;
5909
5910 copy = g_slice_new0 (CursorState);
5911 copy->values = g_new0 (gchar *, cursor->n_sort_fields);
5912
5913 for (i = 0; i < cursor->n_sort_fields; i++)
5914 copy->values[i] = g_strdup (state->values[i]);
5915
5916 copy->last_uid = g_strdup (state->last_uid);
5917 copy->position = state->position;
5918
5919 return copy;
5920 }
5921
5922 static void
cursor_state_free(EbSqlCursor * cursor,CursorState * state)5923 cursor_state_free (EbSqlCursor *cursor,
5924 CursorState *state)
5925 {
5926 if (state) {
5927 cursor_state_clear (cursor, state, EBSQL_CURSOR_ORIGIN_BEGIN);
5928 g_free (state->values);
5929 g_slice_free (CursorState, state);
5930 }
5931 }
5932
5933 static void
cursor_state_clear(EbSqlCursor * cursor,CursorState * state,EbSqlCursorOrigin position)5934 cursor_state_clear (EbSqlCursor *cursor,
5935 CursorState *state,
5936 EbSqlCursorOrigin position)
5937 {
5938 gint i;
5939
5940 for (i = 0; i < cursor->n_sort_fields; i++) {
5941 g_free (state->values[i]);
5942 state->values[i] = NULL;
5943 }
5944
5945 g_free (state->last_uid);
5946 state->last_uid = NULL;
5947 state->position = position;
5948 }
5949
5950 static void
cursor_state_set_from_contact(EBookSqlite * ebsql,EbSqlCursor * cursor,CursorState * state,EContact * contact)5951 cursor_state_set_from_contact (EBookSqlite *ebsql,
5952 EbSqlCursor *cursor,
5953 CursorState *state,
5954 EContact *contact)
5955 {
5956 gint i;
5957
5958 cursor_state_clear (cursor, state, EBSQL_CURSOR_ORIGIN_BEGIN);
5959
5960 for (i = 0; i < cursor->n_sort_fields; i++) {
5961 const gchar *string = e_contact_get_const (contact, cursor->sort_fields[i]);
5962 SummaryField *field;
5963 gchar *sort_key;
5964
5965 if (string)
5966 sort_key = e_collator_generate_key (
5967 ebsql->priv->collator,
5968 string, NULL);
5969 else
5970 sort_key = g_strdup ("");
5971
5972 field = summary_field_get (ebsql, cursor->sort_fields[i]);
5973
5974 if (field && (field->index & INDEX_FLAG (SORT_KEY)) != 0) {
5975 state->values[i] = sort_key;
5976 } else {
5977 state->values[i] = ebsql_encode_vcard_sort_key (sort_key);
5978 g_free (sort_key);
5979 }
5980 }
5981
5982 state->last_uid = e_contact_get (contact, E_CONTACT_UID);
5983 state->position = EBSQL_CURSOR_ORIGIN_CURRENT;
5984 }
5985
5986 static void
cursor_state_set_from_vcard(EBookSqlite * ebsql,EbSqlCursor * cursor,CursorState * state,const gchar * vcard)5987 cursor_state_set_from_vcard (EBookSqlite *ebsql,
5988 EbSqlCursor *cursor,
5989 CursorState *state,
5990 const gchar *vcard)
5991 {
5992 EContact *contact;
5993
5994 contact = e_contact_new_from_vcard (vcard);
5995 cursor_state_set_from_contact (ebsql, cursor, state, contact);
5996 g_object_unref (contact);
5997 }
5998
5999 static gboolean
ebsql_cursor_setup_query(EBookSqlite * ebsql,EbSqlCursor * cursor,const gchar * sexp,GError ** error)6000 ebsql_cursor_setup_query (EBookSqlite *ebsql,
6001 EbSqlCursor *cursor,
6002 const gchar *sexp,
6003 GError **error)
6004 {
6005 PreflightContext context = PREFLIGHT_CONTEXT_INIT;
6006 GString *string;
6007
6008 /* Preflighting and error checking */
6009 if (sexp) {
6010 query_preflight (&context, ebsql, sexp);
6011
6012 if (context.status > PREFLIGHT_NOT_SUMMARIZED) {
6013 EBSQL_SET_ERROR_LITERAL (
6014 error,
6015 E_BOOK_SQLITE_ERROR_INVALID_QUERY,
6016 _("Invalid query for EbSqlCursor"));
6017
6018 preflight_context_clear (&context);
6019 return FALSE;
6020
6021 }
6022 }
6023
6024 /* Now we caught the errors, let's generate our queries and get out of here ... */
6025 g_free (cursor->select_vcards);
6026 g_free (cursor->select_count);
6027 g_free (cursor->query);
6028 g_clear_object (&(cursor->sexp));
6029
6030 /* Generate the leading SELECT portions that we need */
6031 string = g_string_new ("");
6032 ebsql_generate_select (ebsql, string, SEARCH_FULL, &context, NULL);
6033 cursor->select_vcards = g_string_free (string, FALSE);
6034
6035 string = g_string_new ("");
6036 ebsql_generate_select (ebsql, string, SEARCH_COUNT, &context, NULL);
6037 cursor->select_count = g_string_free (string, FALSE);
6038
6039 if (sexp == NULL || context.status == PREFLIGHT_LIST_ALL) {
6040 cursor->query = NULL;
6041 cursor->sexp = NULL;
6042 } else {
6043 /* Generate the constraints for our queries
6044 */
6045 string = g_string_new (NULL);
6046 ebsql_generate_constraints (
6047 ebsql, string, context.constraints, sexp);
6048 cursor->query = g_string_free (string, FALSE);
6049 cursor->sexp = e_book_backend_sexp_new (sexp);
6050 }
6051
6052 preflight_context_clear (&context);
6053
6054 return TRUE;
6055 }
6056
6057 static gchar *
ebsql_cursor_order_by_fragment(EBookSqlite * ebsql,const EContactField * sort_fields,const EBookCursorSortType * sort_types,guint n_sort_fields,gboolean reverse)6058 ebsql_cursor_order_by_fragment (EBookSqlite *ebsql,
6059 const EContactField *sort_fields,
6060 const EBookCursorSortType *sort_types,
6061 guint n_sort_fields,
6062 gboolean reverse)
6063 {
6064 GString *string;
6065 gint i;
6066
6067 string = g_string_new ("ORDER BY ");
6068
6069 for (i = 0; i < n_sort_fields; i++) {
6070 SummaryField *field = summary_field_get (ebsql, sort_fields[i]);
6071
6072 if (i > 0)
6073 g_string_append (string, ", ");
6074
6075 if (field &&
6076 (field->index & INDEX_FLAG (SORT_KEY)) != 0) {
6077 g_string_append (string, "summary.");
6078 g_string_append (string, field->dbname);
6079 g_string_append (string, "_" EBSQL_SUFFIX_SORT_KEY " ");
6080 } else {
6081 g_string_append (string, EBSQL_VCARD_FRAGMENT (ebsql));
6082 g_string_append (string, " COLLATE ");
6083 g_string_append (string, EBSQL_COLLATE_PREFIX);
6084 g_string_append (string, e_contact_field_name (sort_fields[i]));
6085 g_string_append_c (string, ' ');
6086 }
6087
6088 if (reverse)
6089 g_string_append (string, (sort_types[i] == E_BOOK_CURSOR_SORT_ASCENDING ? "DESC" : "ASC"));
6090 else
6091 g_string_append (string, (sort_types[i] == E_BOOK_CURSOR_SORT_ASCENDING ? "ASC" : "DESC"));
6092 }
6093
6094 /* Also order the UID, since it's our tie breaker */
6095 if (n_sort_fields > 0)
6096 g_string_append (string, ", ");
6097
6098 g_string_append (string, "summary.uid ");
6099 g_string_append (string, reverse ? "DESC" : "ASC");
6100
6101 return g_string_free (string, FALSE);
6102 }
6103
6104 static EbSqlCursor *
ebsql_cursor_new(EBookSqlite * ebsql,const gchar * sexp,const EContactField * sort_fields,const EBookCursorSortType * sort_types,guint n_sort_fields)6105 ebsql_cursor_new (EBookSqlite *ebsql,
6106 const gchar *sexp,
6107 const EContactField *sort_fields,
6108 const EBookCursorSortType *sort_types,
6109 guint n_sort_fields)
6110 {
6111 EbSqlCursor *cursor = g_slice_new0 (EbSqlCursor);
6112
6113 cursor->order = ebsql_cursor_order_by_fragment (
6114 ebsql, sort_fields, sort_types, n_sort_fields, FALSE);
6115 cursor->reverse_order = ebsql_cursor_order_by_fragment (
6116 ebsql, sort_fields, sort_types, n_sort_fields, TRUE);
6117
6118 /* Sort parameters */
6119 cursor->n_sort_fields = n_sort_fields;
6120 cursor->sort_fields = g_memdup (sort_fields, sizeof (EContactField) * n_sort_fields);
6121 cursor->sort_types = g_memdup (sort_types, sizeof (EBookCursorSortType) * n_sort_fields);
6122
6123 /* Cursor state */
6124 cursor->state.values = g_new0 (gchar *, n_sort_fields);
6125 cursor->state.last_uid = NULL;
6126 cursor->state.position = EBSQL_CURSOR_ORIGIN_BEGIN;
6127
6128 return cursor;
6129 }
6130
6131 static void
ebsql_cursor_free(EbSqlCursor * cursor)6132 ebsql_cursor_free (EbSqlCursor *cursor)
6133 {
6134 if (cursor) {
6135 cursor_state_clear (cursor, &(cursor->state), EBSQL_CURSOR_ORIGIN_BEGIN);
6136 g_free (cursor->state.values);
6137
6138 g_clear_object (&(cursor->sexp));
6139 g_free (cursor->select_vcards);
6140 g_free (cursor->select_count);
6141 g_free (cursor->query);
6142 g_free (cursor->order);
6143 g_free (cursor->reverse_order);
6144 g_free (cursor->sort_fields);
6145 g_free (cursor->sort_types);
6146
6147 g_slice_free (EbSqlCursor, cursor);
6148 }
6149 }
6150
6151 #define GREATER_OR_LESS(cursor, idx, reverse) \
6152 (reverse ? \
6153 (((EbSqlCursor *) cursor)->sort_types[idx] == E_BOOK_CURSOR_SORT_ASCENDING ? '<' : '>') : \
6154 (((EbSqlCursor *) cursor)->sort_types[idx] == E_BOOK_CURSOR_SORT_ASCENDING ? '>' : '<'))
6155
6156 static inline void
ebsql_cursor_format_equality(EBookSqlite * ebsql,GString * string,EContactField field_id,const gchar * value,gchar equality)6157 ebsql_cursor_format_equality (EBookSqlite *ebsql,
6158 GString *string,
6159 EContactField field_id,
6160 const gchar *value,
6161 gchar equality)
6162 {
6163 SummaryField *field = summary_field_get (ebsql, field_id);
6164
6165 if (field &&
6166 (field->index & INDEX_FLAG (SORT_KEY)) != 0) {
6167
6168 g_string_append (string, "summary.");
6169 g_string_append (string, field->dbname);
6170 g_string_append (string, "_" EBSQL_SUFFIX_SORT_KEY " ");
6171
6172 ebsql_string_append_printf (string, "%c %Q", equality, value);
6173
6174 } else {
6175 ebsql_string_append_printf (
6176 string, "(%s %c %Q ",
6177 EBSQL_VCARD_FRAGMENT (ebsql),
6178 equality, value);
6179
6180 g_string_append (string, "COLLATE " EBSQL_COLLATE_PREFIX);
6181 g_string_append (string, e_contact_field_name (field_id));
6182 g_string_append_c (string, ')');
6183 }
6184 }
6185
6186 static gchar *
ebsql_cursor_constraints(EBookSqlite * ebsql,EbSqlCursor * cursor,CursorState * state,gboolean reverse,gboolean include_current_uid)6187 ebsql_cursor_constraints (EBookSqlite *ebsql,
6188 EbSqlCursor *cursor,
6189 CursorState *state,
6190 gboolean reverse,
6191 gboolean include_current_uid)
6192 {
6193 GString *string;
6194 gint i, j;
6195
6196 /* Example for:
6197 * ORDER BY family_name ASC, given_name DESC
6198 *
6199 * Where current cursor values are:
6200 * family_name = Jackson
6201 * given_name = Micheal
6202 *
6203 * With reverse = FALSE
6204 *
6205 * (summary.family_name > 'Jackson') OR
6206 * (summary.family_name = 'Jackson' AND summary.given_name < 'Micheal') OR
6207 * (summary.family_name = 'Jackson' AND summary.given_name = 'Micheal' AND summary.uid > 'last-uid')
6208 *
6209 * With reverse = TRUE (needed for moving the cursor backwards through results)
6210 *
6211 * (summary.family_name < 'Jackson') OR
6212 * (summary.family_name = 'Jackson' AND summary.given_name > 'Micheal') OR
6213 * (summary.family_name = 'Jackson' AND summary.given_name = 'Micheal' AND summary.uid < 'last-uid')
6214 *
6215 */
6216 string = g_string_new (NULL);
6217
6218 for (i = 0; i <= cursor->n_sort_fields; i++) {
6219
6220 /* Break once we hit a NULL value */
6221 if ((i < cursor->n_sort_fields && state->values[i] == NULL) ||
6222 (i == cursor->n_sort_fields && state->last_uid == NULL))
6223 break;
6224
6225 /* Between each qualifier, add an 'OR' */
6226 if (i > 0)
6227 g_string_append (string, " OR ");
6228
6229 /* Begin qualifier */
6230 g_string_append_c (string, '(');
6231
6232 /* Create the '=' statements leading up to the current tie breaker */
6233 for (j = 0; j < i; j++) {
6234 ebsql_cursor_format_equality (ebsql, string,
6235 cursor->sort_fields[j],
6236 state->values[j], '=');
6237 g_string_append (string, " AND ");
6238 }
6239
6240 if (i == cursor->n_sort_fields) {
6241
6242 /* The 'include_current_uid' clause is used for calculating
6243 * the current position of the cursor, inclusive of the
6244 * current position.
6245 */
6246 if (include_current_uid)
6247 g_string_append_c (string, '(');
6248
6249 /* Append the UID tie breaker */
6250 ebsql_string_append_printf (
6251 string,
6252 "summary.uid %c %Q",
6253 reverse ? '<' : '>',
6254 state->last_uid);
6255
6256 if (include_current_uid)
6257 ebsql_string_append_printf (
6258 string,
6259 " OR summary.uid = %Q)",
6260 state->last_uid);
6261
6262 } else {
6263
6264 /* SPECIAL CASE: If we have a parially set cursor state, then we must
6265 * report next results that are inclusive of the final qualifier.
6266 *
6267 * This allows one to set the cursor with the family name set to 'J'
6268 * and include the results for contact's Mr & Miss 'J'.
6269 */
6270 gboolean include_exact_match =
6271 (reverse == FALSE &&
6272 ((i + 1 < cursor->n_sort_fields && state->values[i + 1] == NULL) ||
6273 (i + 1 == cursor->n_sort_fields && state->last_uid == NULL)));
6274
6275 if (include_exact_match)
6276 g_string_append_c (string, '(');
6277
6278 /* Append the final qualifier for this field */
6279 ebsql_cursor_format_equality (ebsql, string,
6280 cursor->sort_fields[i],
6281 state->values[i],
6282 GREATER_OR_LESS (cursor, i, reverse));
6283
6284 if (include_exact_match) {
6285 g_string_append (string, " OR ");
6286 ebsql_cursor_format_equality (ebsql, string,
6287 cursor->sort_fields[i],
6288 state->values[i], '=');
6289 g_string_append_c (string, ')');
6290 }
6291 }
6292
6293 /* End qualifier */
6294 g_string_append_c (string, ')');
6295 }
6296
6297 return g_string_free (string, FALSE);
6298 }
6299
6300 static gboolean
cursor_count_total_locked(EBookSqlite * ebsql,EbSqlCursor * cursor,gint * total,GError ** error)6301 cursor_count_total_locked (EBookSqlite *ebsql,
6302 EbSqlCursor *cursor,
6303 gint *total,
6304 GError **error)
6305 {
6306 GString *query;
6307 gboolean success;
6308
6309 query = g_string_new (cursor->select_count);
6310
6311 /* Add the filter constraints (if any) */
6312 if (cursor->query) {
6313 g_string_append (query, " WHERE ");
6314
6315 g_string_append_c (query, '(');
6316 g_string_append (query, cursor->query);
6317 g_string_append_c (query, ')');
6318 }
6319
6320 /* Execute the query */
6321 success = ebsql_exec (ebsql, query->str, get_count_cb, total, NULL, error);
6322
6323 g_string_free (query, TRUE);
6324
6325 return success;
6326 }
6327
6328 static gboolean
cursor_count_position_locked(EBookSqlite * ebsql,EbSqlCursor * cursor,gint * position,GError ** error)6329 cursor_count_position_locked (EBookSqlite *ebsql,
6330 EbSqlCursor *cursor,
6331 gint *position,
6332 GError **error)
6333 {
6334 GString *query;
6335 gboolean success;
6336
6337 query = g_string_new (cursor->select_count);
6338
6339 /* Add the filter constraints (if any) */
6340 if (cursor->query) {
6341 g_string_append (query, " WHERE ");
6342
6343 g_string_append_c (query, '(');
6344 g_string_append (query, cursor->query);
6345 g_string_append_c (query, ')');
6346 }
6347
6348 /* Add the cursor constraints (if any) */
6349 if (cursor->state.values[0] != NULL) {
6350 gchar *constraints = NULL;
6351
6352 if (!cursor->query)
6353 g_string_append (query, " WHERE ");
6354 else
6355 g_string_append (query, " AND ");
6356
6357 /* Here we do a reverse query, we're looking for all the
6358 * results leading up to the current cursor value, including
6359 * the cursor value
6360 */
6361 constraints = ebsql_cursor_constraints (
6362 ebsql, cursor, &(cursor->state), TRUE, TRUE);
6363
6364 g_string_append_c (query, '(');
6365 g_string_append (query, constraints);
6366 g_string_append_c (query, ')');
6367
6368 g_free (constraints);
6369 }
6370
6371 /* Execute the query */
6372 success = ebsql_exec (ebsql, query->str, get_count_cb, position, NULL, error);
6373
6374 g_string_free (query, TRUE);
6375
6376 return success;
6377 }
6378
6379 /**********************************************************
6380 * GObjectClass *
6381 **********************************************************/
6382 static void
e_book_sqlite_dispose(GObject * object)6383 e_book_sqlite_dispose (GObject *object)
6384 {
6385 EBookSqlite *ebsql = E_BOOK_SQLITE (object);
6386
6387 ebsql_unregister_from_hash (ebsql);
6388
6389 /* Chain up to parent's dispose() method. */
6390 G_OBJECT_CLASS (e_book_sqlite_parent_class)->dispose (object);
6391 }
6392
6393 static void
e_book_sqlite_finalize(GObject * object)6394 e_book_sqlite_finalize (GObject *object)
6395 {
6396 EBookSqlite *ebsql = E_BOOK_SQLITE (object);
6397 EBookSqlitePrivate *priv = ebsql->priv;
6398
6399 summary_fields_array_free (
6400 priv->summary_fields,
6401 priv->n_summary_fields);
6402
6403 g_free (priv->folderid);
6404 g_free (priv->path);
6405 g_free (priv->locale);
6406 g_free (priv->region_code);
6407
6408 if (priv->collator)
6409 e_collator_unref (priv->collator);
6410
6411 g_clear_object (&priv->source);
6412
6413 g_mutex_clear (&priv->lock);
6414 g_mutex_clear (&priv->updates_lock);
6415
6416 if (priv->multi_deletes)
6417 g_hash_table_destroy (priv->multi_deletes);
6418
6419 if (priv->multi_inserts)
6420 g_hash_table_destroy (priv->multi_inserts);
6421
6422 if (priv->user_data && priv->user_data_destroy)
6423 priv->user_data_destroy (priv->user_data);
6424
6425 sqlite3_finalize (priv->insert_stmt);
6426 sqlite3_finalize (priv->replace_stmt);
6427 sqlite3_close (priv->db);
6428
6429 EBSQL_NOTE (REF_COUNTS, g_printerr ("EBookSqlite finalized\n"));
6430
6431 /* Chain up to parent's finalize() method. */
6432 G_OBJECT_CLASS (e_book_sqlite_parent_class)->finalize (object);
6433 }
6434
6435 static void
e_book_sqlite_constructed(GObject * object)6436 e_book_sqlite_constructed (GObject *object)
6437 {
6438 /* Chain up to parent's constructed() method. */
6439 G_OBJECT_CLASS (e_book_sqlite_parent_class)->constructed (object);
6440
6441 e_extensible_load_extensions (E_EXTENSIBLE (object));
6442 }
6443
6444 static gboolean
ebsql_signals_accumulator(GSignalInvocationHint * ihint,GValue * return_accu,const GValue * handler_return,gpointer data)6445 ebsql_signals_accumulator (GSignalInvocationHint *ihint,
6446 GValue *return_accu,
6447 const GValue *handler_return,
6448 gpointer data)
6449 {
6450 gboolean handler_result;
6451
6452 handler_result = g_value_get_boolean (handler_return);
6453 g_value_set_boolean (return_accu, handler_result);
6454
6455 return handler_result;
6456 }
6457
6458 static gboolean
ebsql_before_insert_contact_default(EBookSqlite * ebsql,gpointer db,EContact * contact,const gchar * extra,gboolean replace,GCancellable * cancellable,GError ** error)6459 ebsql_before_insert_contact_default (EBookSqlite *ebsql,
6460 gpointer db,
6461 EContact *contact,
6462 const gchar *extra,
6463 gboolean replace,
6464 GCancellable *cancellable,
6465 GError **error)
6466 {
6467 return TRUE;
6468 }
6469
6470 static gboolean
ebsql_before_remove_contact_default(EBookSqlite * ebsql,gpointer db,const gchar * contact_uid,GCancellable * cancellable,GError ** error)6471 ebsql_before_remove_contact_default (EBookSqlite *ebsql,
6472 gpointer db,
6473 const gchar *contact_uid,
6474 GCancellable *cancellable,
6475 GError **error)
6476 {
6477 return TRUE;
6478 }
6479
6480 static void
e_book_sqlite_class_init(EBookSqliteClass * class)6481 e_book_sqlite_class_init (EBookSqliteClass *class)
6482 {
6483 GObjectClass *object_class;
6484
6485 object_class = G_OBJECT_CLASS (class);
6486 object_class->dispose = e_book_sqlite_dispose;
6487 object_class->finalize = e_book_sqlite_finalize;
6488 object_class->constructed = e_book_sqlite_constructed;
6489
6490 class->before_insert_contact = ebsql_before_insert_contact_default;
6491 class->before_remove_contact = ebsql_before_remove_contact_default;
6492
6493 /* Parse the EBSQL_DEBUG environment variable */
6494 ebsql_init_debug ();
6495
6496 signals[BEFORE_INSERT_CONTACT] = g_signal_new (
6497 "before-insert-contact",
6498 G_OBJECT_CLASS_TYPE (class),
6499 G_SIGNAL_RUN_LAST,
6500 G_STRUCT_OFFSET (EBookSqliteClass, before_insert_contact),
6501 ebsql_signals_accumulator,
6502 NULL,
6503 g_cclosure_marshal_generic,
6504 G_TYPE_BOOLEAN, 6,
6505 G_TYPE_POINTER,
6506 E_TYPE_CONTACT,
6507 G_TYPE_STRING,
6508 G_TYPE_BOOLEAN,
6509 G_TYPE_OBJECT,
6510 G_TYPE_POINTER);
6511
6512 signals[BEFORE_REMOVE_CONTACT] = g_signal_new (
6513 "before-remove-contact",
6514 G_OBJECT_CLASS_TYPE (class),
6515 G_SIGNAL_RUN_LAST,
6516 G_STRUCT_OFFSET (EBookSqliteClass, before_remove_contact),
6517 ebsql_signals_accumulator,
6518 NULL,
6519 g_cclosure_marshal_generic,
6520 G_TYPE_BOOLEAN, 4,
6521 G_TYPE_POINTER,
6522 G_TYPE_STRING,
6523 G_TYPE_CANCELLABLE,
6524 G_TYPE_POINTER);
6525 }
6526
6527 static void
e_book_sqlite_init(EBookSqlite * ebsql)6528 e_book_sqlite_init (EBookSqlite *ebsql)
6529 {
6530 ebsql->priv = e_book_sqlite_get_instance_private (ebsql);
6531
6532 g_mutex_init (&ebsql->priv->lock);
6533 g_mutex_init (&ebsql->priv->updates_lock);
6534 }
6535
6536 /**********************************************************
6537 * API *
6538 **********************************************************/
6539 static EBookSqlite *
ebsql_new_default(const gchar * path,ESource * source,EbSqlVCardCallback vcard_callback,EbSqlChangeCallback change_callback,gpointer user_data,GDestroyNotify user_data_destroy,GCancellable * cancellable,GError ** error)6540 ebsql_new_default (const gchar *path,
6541 ESource *source,
6542 EbSqlVCardCallback vcard_callback,
6543 EbSqlChangeCallback change_callback,
6544 gpointer user_data,
6545 GDestroyNotify user_data_destroy,
6546 GCancellable *cancellable,
6547 GError **error)
6548 {
6549 EBookSqlite *ebsql;
6550 GArray *summary_fields;
6551 gint i;
6552
6553 /* Create the default summary structs */
6554 summary_fields = g_array_new (FALSE, FALSE, sizeof (SummaryField));
6555 for (i = 0; i < G_N_ELEMENTS (default_summary_fields); i++)
6556 summary_field_append (summary_fields, DEFAULT_FOLDER_ID, default_summary_fields[i], NULL);
6557
6558 /* Add the default index flags */
6559 summary_fields_add_indexes (
6560 summary_fields,
6561 default_indexed_fields,
6562 default_index_types,
6563 G_N_ELEMENTS (default_indexed_fields));
6564
6565 ebsql = ebsql_new_internal (
6566 path, source,
6567 vcard_callback, change_callback,
6568 user_data, user_data_destroy,
6569 (SummaryField *) summary_fields->data,
6570 summary_fields->len,
6571 cancellable, error);
6572
6573 g_array_free (summary_fields, FALSE);
6574
6575 return ebsql;
6576 }
6577
6578 /**
6579 * e_book_sqlite_new:
6580 * @path: location to load or create the new database
6581 * @source: (nullable): an optional #ESource, associated with the #EBookSqlite, or %NULL
6582 * @cancellable: A #GCancellable
6583 * @error: A location to store any error that may have occurred.
6584 *
6585 * Creates a new #EBookSqlite with the default summary configuration.
6586 *
6587 * Aside from the manditory fields %E_CONTACT_UID, %E_CONTACT_REV,
6588 * the default configuration stores the following fields for quick
6589 * performance of searches: %E_CONTACT_FILE_AS, %E_CONTACT_NICKNAME,
6590 * %E_CONTACT_FULL_NAME, %E_CONTACT_GIVEN_NAME, %E_CONTACT_FAMILY_NAME,
6591 * %E_CONTACT_EMAIL, %E_CONTACT_TEL, %E_CONTACT_IS_LIST, %E_CONTACT_LIST_SHOW_ADDRESSES,
6592 * and %E_CONTACT_WANTS_HTML.
6593 *
6594 * The fields %E_CONTACT_FULL_NAME and %E_CONTACT_EMAIL are configured
6595 * to respond extra quickly with the %E_BOOK_INDEX_PREFIX index flag.
6596 *
6597 * The fields %E_CONTACT_FILE_AS, %E_CONTACT_FAMILY_NAME and
6598 * %E_CONTACT_GIVEN_NAME are configured to perform well with
6599 * the #EbSqlCursor interface, using the %E_BOOK_INDEX_SORT_KEY
6600 * index flag.
6601 *
6602 * Returns: (transfer full): A reference to an #EBookSqlite
6603 *
6604 * Since: 3.12
6605 **/
6606 EBookSqlite *
e_book_sqlite_new(const gchar * path,ESource * source,GCancellable * cancellable,GError ** error)6607 e_book_sqlite_new (const gchar *path,
6608 ESource *source,
6609 GCancellable *cancellable,
6610 GError **error)
6611 {
6612 g_return_val_if_fail (path && path[0], NULL);
6613
6614 return ebsql_new_default (path, source, NULL, NULL, NULL, NULL, cancellable, error);
6615 }
6616
6617 /**
6618 * e_book_sqlite_new_full:
6619 * @path: location to load or create the new database
6620 * @source: an optional #ESource, associated with the #EBookSqlite, or %NULL
6621 * @setup: (nullable): an #ESourceBackendSummarySetup describing how the summary should be setup, or %NULL to use the default
6622 * @vcard_callback: (nullable) (scope async) (closure user_data): A function to resolve vcards
6623 * @change_callback: (nullable) (scope async) (closure user_data): A function to catch notifications of vcard changes
6624 * @user_data: (nullable): callback user data
6625 * @user_data_destroy: (nullable): A function to free @user_data automatically when the created #EBookSqlite is destroyed.
6626 * @cancellable: A #GCancellable
6627 * @error: A location to store any error that may have occurred.
6628 *
6629 * Opens or creates a new addressbook at @path.
6630 *
6631 * Like e_book_sqlite_new(), but allows configuration of which contact fields
6632 * will be stored for quick reference in the summary. The configuration indicated by
6633 * @setup will only be taken into account when initially creating the underlying table,
6634 * further configurations will be ignored.
6635 *
6636 * The fields %E_CONTACT_UID and %E_CONTACT_REV are not optional,
6637 * they will be stored in the summary regardless of this function's parameters.
6638 * Only #EContactFields with the type #G_TYPE_STRING, #G_TYPE_BOOLEAN or
6639 * #E_TYPE_CONTACT_ATTR_LIST are currently supported.
6640 *
6641 * If @vcard_callback is specified, then vcards will not be stored by functions
6642 * such as e_book_sqlitedb_add_contact(). Instead @vcard_callback will be invoked
6643 * at any time the created #EBookSqlite requires a vcard, either as a fallback
6644 * for querying search expressions which cannot be satisfied with the summary
6645 * fields, or when reporting results from searches.
6646 *
6647 * If any error occurs and %NULL is returned, then the passed @user_data will
6648 * be automatically freed using the @user_data_destroy function, if specified.
6649 *
6650 * It is recommended to store all contact vcards in the #EBookSqlite addressbook
6651 * if at all possible, however in some cases the vcards must be stored in some
6652 * other storage.
6653 *
6654 * Returns: (transfer full): The newly created #EBookSqlite, or %NULL if opening or creating the addressbook failed.
6655 *
6656 * Since: 3.12
6657 **/
6658 EBookSqlite *
e_book_sqlite_new_full(const gchar * path,ESource * source,ESourceBackendSummarySetup * setup,EbSqlVCardCallback vcard_callback,EbSqlChangeCallback change_callback,gpointer user_data,GDestroyNotify user_data_destroy,GCancellable * cancellable,GError ** error)6659 e_book_sqlite_new_full (const gchar *path,
6660 ESource *source,
6661 ESourceBackendSummarySetup *setup,
6662 EbSqlVCardCallback vcard_callback,
6663 EbSqlChangeCallback change_callback,
6664 gpointer user_data,
6665 GDestroyNotify user_data_destroy,
6666 GCancellable *cancellable,
6667 GError **error)
6668 {
6669 EBookSqlite *ebsql = NULL;
6670 EContactField *fields;
6671 EContactField *indexed_fields;
6672 EBookIndexType *index_types = NULL;
6673 gboolean had_error = FALSE;
6674 GArray *summary_fields;
6675 gint n_fields = 0, n_indexed_fields = 0, i;
6676
6677 g_return_val_if_fail (path && path[0], NULL);
6678 g_return_val_if_fail (setup == NULL || E_IS_SOURCE_BACKEND_SUMMARY_SETUP (setup), NULL);
6679
6680 if (!setup)
6681 return ebsql_new_default (
6682 path,
6683 source,
6684 vcard_callback,
6685 change_callback,
6686 user_data,
6687 user_data_destroy,
6688 cancellable, error);
6689
6690 fields = e_source_backend_summary_setup_get_summary_fields (setup, &n_fields);
6691 indexed_fields = e_source_backend_summary_setup_get_indexed_fields (setup, &index_types, &n_indexed_fields);
6692
6693 /* No specified summary fields indicates the default summary configuration should be used */
6694 if (n_fields <= 0 || n_fields >= EBSQL_MAX_SUMMARY_FIELDS) {
6695
6696 if (n_fields)
6697 g_warning (
6698 "EBookSqlite refused to create addressbook with over %d summary fields",
6699 EBSQL_MAX_SUMMARY_FIELDS);
6700
6701 ebsql = ebsql_new_default (
6702 path,
6703 source,
6704 vcard_callback,
6705 change_callback,
6706 user_data,
6707 user_data_destroy,
6708 cancellable, error);
6709 g_free (fields);
6710 g_free (index_types);
6711 g_free (indexed_fields);
6712
6713 return ebsql;
6714 }
6715
6716 summary_fields = g_array_new (FALSE, FALSE, sizeof (SummaryField));
6717
6718 /* Ensure the non-optional fields first */
6719 summary_field_append (summary_fields, DEFAULT_FOLDER_ID, E_CONTACT_UID, error);
6720 summary_field_append (summary_fields, DEFAULT_FOLDER_ID, E_CONTACT_REV, error);
6721
6722 for (i = 0; i < n_fields; i++) {
6723 if (!summary_field_append (summary_fields, DEFAULT_FOLDER_ID, fields[i], error)) {
6724 had_error = TRUE;
6725 break;
6726 }
6727 }
6728
6729 if (had_error) {
6730 gint n_sfields;
6731 SummaryField *sfields;
6732
6733 /* Properly free the array */
6734 n_sfields = summary_fields->len;
6735 sfields = (SummaryField *) g_array_free (summary_fields, FALSE);
6736 summary_fields_array_free (sfields, n_sfields);
6737
6738 g_free (fields);
6739 g_free (index_types);
6740 g_free (indexed_fields);
6741
6742 if (user_data && user_data_destroy)
6743 user_data_destroy (user_data);
6744
6745 return NULL;
6746 }
6747
6748 /* Add the 'indexed' flag to the SummaryField structs */
6749 summary_fields_add_indexes (
6750 summary_fields, indexed_fields, index_types, n_indexed_fields);
6751
6752 ebsql = ebsql_new_internal (
6753 path, source,
6754 vcard_callback, change_callback,
6755 user_data, user_data_destroy,
6756 (SummaryField *) summary_fields->data,
6757 summary_fields->len,
6758 cancellable, error);
6759
6760 g_free (fields);
6761 g_free (index_types);
6762 g_free (indexed_fields);
6763 g_array_free (summary_fields, FALSE);
6764
6765 return ebsql;
6766 }
6767
6768 /**
6769 * e_book_sqlite_lock:
6770 * @ebsql: An #EBookSqlite
6771 * @lock_type: The #EbSqlLockType to acquire
6772 * @cancellable: A #GCancellable
6773 * @error: A location to store any error that may have occurred.
6774 *
6775 * Obtains an exclusive lock on @ebsql and starts a transaction.
6776 *
6777 * This should be called if you need to access @ebsql multiple times while
6778 * ensuring an atomic transaction. End this transaction with e_book_sqlite_unlock().
6779 *
6780 * If @cancellable is specified, then @ebsql will retain a reference to it until
6781 * e_book_sqlite_unlock() is called. Any accesses to @ebsql with the lock held
6782 * are expected to have the same @cancellable specified, or %NULL.
6783 *
6784 * <note><para>Aside from ensuring atomicity of transactions, this function will hold a mutex
6785 * which will cause further calls to e_book_sqlite_lock() to block. If you are accessing
6786 * @ebsql from multiple threads, then any interactions with @ebsql should be nested in calls
6787 * to e_book_sqlite_lock() and e_book_sqlite_unlock().</para></note>
6788 *
6789 * Returns: %TRUE on success, otherwise %FALSE is returned and @error is set appropriately.
6790 *
6791 * Since: 3.12
6792 **/
6793 gboolean
e_book_sqlite_lock(EBookSqlite * ebsql,EbSqlLockType lock_type,GCancellable * cancellable,GError ** error)6794 e_book_sqlite_lock (EBookSqlite *ebsql,
6795 EbSqlLockType lock_type,
6796 GCancellable *cancellable,
6797 GError **error)
6798 {
6799 gboolean success;
6800
6801 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), FALSE);
6802
6803 EBSQL_LOCK_MUTEX (&ebsql->priv->updates_lock);
6804
6805 /* Here, after obtaining the outer facing transaction lock, we need
6806 * to assert that there is no cancellable already set */
6807 if (ebsql->priv->cancel != NULL) {
6808 /* This should never happen, if it does it's a bug
6809 * in this code, not the calling code
6810 */
6811 g_warn_if_reached ();
6812 EBSQL_UNLOCK_MUTEX (&ebsql->priv->updates_lock);
6813 return FALSE;
6814 }
6815
6816 EBSQL_LOCK_MUTEX (&ebsql->priv->lock);
6817
6818 /* Here, after obtaining the regular lock, we need to assert that we are
6819 * the toplevel transaction */
6820 if (ebsql->priv->in_transaction != 0) {
6821 g_warn_if_reached ();
6822 EBSQL_LOCK_MUTEX (&ebsql->priv->lock);
6823 EBSQL_UNLOCK_MUTEX (&ebsql->priv->updates_lock);
6824 return FALSE;
6825 }
6826
6827 success = ebsql_start_transaction (ebsql, lock_type, cancellable, error);
6828 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
6829
6830 /* If we failed to start the transaction, we don't hold the lock */
6831 if (!success)
6832 EBSQL_UNLOCK_MUTEX (&ebsql->priv->updates_lock);
6833
6834 return success;
6835 }
6836
6837 /**
6838 * e_book_sqlite_unlock:
6839 * @ebsql: An #EBookSqlite
6840 * @action: Which #EbSqlUnlockAction to take while unlocking
6841 * @error: A location to store any error that may have occurred.
6842 *
6843 * Releases an exclusive on @ebsql and finishes a transaction previously
6844 * started with e_book_sqlite_lock_updates().
6845 *
6846 * <note><para>If this fails, the lock on @ebsql is still released and @error will
6847 * be set to indicate why the transaction or rollback failed.</para></note>
6848 *
6849 * Returns: %TRUE on success, otherwise %FALSE is returned and @error is set appropriately.
6850 *
6851 * Since: 3.12
6852 **/
6853 gboolean
e_book_sqlite_unlock(EBookSqlite * ebsql,EbSqlUnlockAction action,GError ** error)6854 e_book_sqlite_unlock (EBookSqlite *ebsql,
6855 EbSqlUnlockAction action,
6856 GError **error)
6857 {
6858 gboolean success = FALSE;
6859
6860 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), FALSE);
6861
6862 EBSQL_LOCK_MUTEX (&ebsql->priv->lock);
6863
6864 switch (action) {
6865 case EBSQL_UNLOCK_NONE:
6866 case EBSQL_UNLOCK_COMMIT:
6867 success = ebsql_commit_transaction (ebsql, error);
6868 break;
6869 case EBSQL_UNLOCK_ROLLBACK:
6870 success = ebsql_rollback_transaction (ebsql, error);
6871 break;
6872 }
6873
6874 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
6875
6876 EBSQL_UNLOCK_MUTEX (&ebsql->priv->updates_lock);
6877
6878 return success;
6879 }
6880
6881 /**
6882 * e_book_sqlite_ref_collator:
6883 * @ebsql: An #EBookSqlite
6884 *
6885 * References the currently active #ECollator for @ebsql,
6886 * use e_collator_unref() when finished using the returned collator.
6887 *
6888 * Note that the active collator will change with the active locale setting.
6889 *
6890 * Returns: (transfer full): A reference to the active collator.
6891 *
6892 * Since: 3.12
6893 */
6894 ECollator *
e_book_sqlite_ref_collator(EBookSqlite * ebsql)6895 e_book_sqlite_ref_collator (EBookSqlite *ebsql)
6896 {
6897 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), NULL);
6898
6899 return e_collator_ref (ebsql->priv->collator);
6900 }
6901
6902 /**
6903 * e_book_sqlite_ref_source:
6904 * @ebsql: An #EBookSqlite
6905 *
6906 * References the #ESource to which @ebsql is paired,
6907 * use g_object_unref() when finished using the source.
6908 * It can be %NULL in some cases, like when running tests.
6909 *
6910 * Returns: (transfer full): A reference to the #ESource to which @ebsql
6911 * is paired, or %NULL.
6912 *
6913 * Since: 3.16
6914 */
6915 ESource *
e_book_sqlite_ref_source(EBookSqlite * ebsql)6916 e_book_sqlite_ref_source (EBookSqlite *ebsql)
6917 {
6918 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), NULL);
6919
6920 if (!ebsql->priv->source)
6921 return NULL;
6922
6923 return g_object_ref (ebsql->priv->source);
6924 }
6925
6926 /**
6927 * e_book_sqlitedb_add_contact:
6928 * @ebsql: An #EBookSqlite
6929 * @contact: EContact to be added
6930 * @extra: Extra data to store in association with this contact
6931 * @replace: Whether this contact should replace another contact with the same UID.
6932 * @cancellable: A #GCancellable
6933 * @error: A location to store any error that may have occurred.
6934 *
6935 * This is a convenience wrapper for e_book_sqlite_add_contacts(),
6936 * which is the preferred means to add or modify multiple contacts when possible.
6937 *
6938 * Returns: %TRUE on success, otherwise %FALSE is returned and @error is set appropriately.
6939 *
6940 * Since: 3.12
6941 **/
6942 gboolean
e_book_sqlite_add_contact(EBookSqlite * ebsql,EContact * contact,const gchar * extra,gboolean replace,GCancellable * cancellable,GError ** error)6943 e_book_sqlite_add_contact (EBookSqlite *ebsql,
6944 EContact *contact,
6945 const gchar *extra,
6946 gboolean replace,
6947 GCancellable *cancellable,
6948 GError **error)
6949 {
6950 GSList l;
6951 GSList el;
6952
6953 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), FALSE);
6954 g_return_val_if_fail (E_IS_CONTACT (contact), FALSE);
6955
6956 l.data = contact;
6957 l.next = NULL;
6958
6959 el.data = (gpointer) extra;
6960 el.next = NULL;
6961
6962 return e_book_sqlite_add_contacts (ebsql, &l, &el, replace, cancellable, error);
6963 }
6964
6965 /**
6966 * e_book_sqlite_add_contacts:
6967 * @ebsql: An #EBookSqlite
6968 * @contacts: (element-type EContact): A list of contacts to add to @ebsql
6969 * @extra: (nullable) (element-type utf8): A list of extra data to store in association with this contact
6970 * @replace: Whether this contact should replace another contact with the same UID.
6971 * @cancellable: A #GCancellable
6972 * @error: A location to store any error that may have occurred.
6973 *
6974 * Adds or replaces contacts in @ebsql. If @replace_existing is specified then existing
6975 * contacts with the same UID will be replaced, otherwise adding an existing contact
6976 * will return an error.
6977 *
6978 * If @extra is specified, it must have an equal length as the @contacts list. Each element
6979 * from the @extra list will be stored in association with it's corresponding contact
6980 * in the @contacts list.
6981 *
6982 * Returns: %TRUE on success, otherwise %FALSE is returned and @error is set appropriately.
6983 *
6984 * Since: 3.12
6985 **/
6986 gboolean
e_book_sqlite_add_contacts(EBookSqlite * ebsql,GSList * contacts,GSList * extra,gboolean replace,GCancellable * cancellable,GError ** error)6987 e_book_sqlite_add_contacts (EBookSqlite *ebsql,
6988 GSList *contacts,
6989 GSList *extra,
6990 gboolean replace,
6991 GCancellable *cancellable,
6992 GError **error)
6993 {
6994 GSList *l, *ll;
6995 gboolean success = TRUE;
6996
6997 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), FALSE);
6998 g_return_val_if_fail (contacts != NULL, FALSE);
6999 g_return_val_if_fail (extra == NULL ||
7000 g_slist_length (extra) == g_slist_length (contacts), FALSE);
7001
7002 EBSQL_LOCK_OR_RETURN (ebsql, cancellable, FALSE);
7003
7004 if (!ebsql_start_transaction (ebsql, EBSQL_LOCK_WRITE, cancellable, error)) {
7005 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
7006 return FALSE;
7007 }
7008
7009 for (l = contacts, ll = extra;
7010 success && l != NULL;
7011 l = l->next, ll = ll ? ll->next : NULL) {
7012 EContact *contact = (EContact *) l->data;
7013 const gchar *extra_data = NULL;
7014
7015 if (ll)
7016 extra_data = (const gchar *) ll->data;
7017
7018 g_signal_emit (ebsql,
7019 signals[BEFORE_INSERT_CONTACT],
7020 0,
7021 ebsql->priv->db,
7022 contact, extra_data,
7023 replace,
7024 cancellable, error,
7025 &success);
7026 if (!success)
7027 break;
7028
7029 success = ebsql_insert_contact (
7030 ebsql,
7031 EBSQL_CHANGE_CONTACT_ADDED,
7032 contact, NULL, extra_data,
7033 replace, error);
7034 }
7035
7036 if (success)
7037 success = ebsql_commit_transaction (ebsql, error);
7038 else
7039 /* The GError is already set. */
7040 ebsql_rollback_transaction (ebsql, NULL);
7041
7042 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
7043
7044 return success;
7045 }
7046
7047 /**
7048 * e_book_sqlite_remove_contact:
7049 * @ebsql: An #EBookSqlite
7050 * @uid: the uid of the contact to remove
7051 * @cancellable: A #GCancellable
7052 * @error: A location to store any error that may have occurred.
7053 *
7054 * Removes the contact indicated by @uid from @ebsql.
7055 *
7056 * Returns: %TRUE on success, otherwise %FALSE is returned and @error is set appropriately.
7057 *
7058 * Since: 3.12
7059 **/
7060 gboolean
e_book_sqlite_remove_contact(EBookSqlite * ebsql,const gchar * uid,GCancellable * cancellable,GError ** error)7061 e_book_sqlite_remove_contact (EBookSqlite *ebsql,
7062 const gchar *uid,
7063 GCancellable *cancellable,
7064 GError **error)
7065 {
7066 GSList l;
7067
7068 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), FALSE);
7069 g_return_val_if_fail (uid != NULL, FALSE);
7070
7071 l.data = (gchar *) uid; /* Won't modify it, I promise :) */
7072 l.next = NULL;
7073
7074 return e_book_sqlite_remove_contacts (
7075 ebsql, &l, cancellable, error);
7076 }
7077
7078 static gchar *
generate_delete_stmt(const gchar * table,GSList * uids)7079 generate_delete_stmt (const gchar *table,
7080 GSList *uids)
7081 {
7082 GString *str = g_string_new (NULL);
7083 GSList *l;
7084
7085 ebsql_string_append_printf (str, "DELETE FROM %Q WHERE uid IN (", table);
7086
7087 for (l = uids; l; l = l->next) {
7088 const gchar *uid = (const gchar *) l->data;
7089
7090 /* First uid with no comma */
7091 if (l != uids)
7092 g_string_append_printf (str, ", ");
7093
7094 ebsql_string_append_printf (str, "%Q", uid);
7095 }
7096
7097 g_string_append_c (str, ')');
7098
7099 return g_string_free (str, FALSE);
7100 }
7101
7102 /**
7103 * e_book_sqlite_remove_contacts:
7104 * @ebsql: An #EBookSqlite
7105 * @uids: (element-type utf8): a #GSList of uids indicating which contacts to remove
7106 * @cancellable: A #GCancellable
7107 * @error: A location to store any error that may have occurred.
7108 *
7109 * Removes the contacts indicated by @uids from @ebsql.
7110 *
7111 * Returns: %TRUE on success, otherwise %FALSE is returned and @error is set appropriately.
7112 *
7113 * Since: 3.12
7114 **/
7115 gboolean
e_book_sqlite_remove_contacts(EBookSqlite * ebsql,GSList * uids,GCancellable * cancellable,GError ** error)7116 e_book_sqlite_remove_contacts (EBookSqlite *ebsql,
7117 GSList *uids,
7118 GCancellable *cancellable,
7119 GError **error)
7120 {
7121 gboolean success = TRUE;
7122 gint i;
7123 gchar *stmt;
7124 const gchar *contact_uid;
7125 GSList *l = NULL;
7126
7127 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), FALSE);
7128 g_return_val_if_fail (uids != NULL, FALSE);
7129
7130 EBSQL_LOCK_OR_RETURN (ebsql, cancellable, FALSE);
7131
7132 if (!ebsql_start_transaction (ebsql, EBSQL_LOCK_WRITE, cancellable, error)) {
7133 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
7134 return FALSE;
7135 }
7136
7137 for (l = uids; success && l; l = l->next) {
7138 contact_uid = (const gchar *) l->data;
7139 g_signal_emit (ebsql,
7140 signals[BEFORE_REMOVE_CONTACT],
7141 0,
7142 ebsql->priv->db,
7143 contact_uid,
7144 cancellable, error,
7145 &success);
7146 }
7147
7148 /* Delete data from the auxiliary tables first */
7149 for (i = 0; success && i < ebsql->priv->n_summary_fields; i++) {
7150 SummaryField *field = &(ebsql->priv->summary_fields[i]);
7151
7152 if (field->type != E_TYPE_CONTACT_ATTR_LIST)
7153 continue;
7154
7155 stmt = generate_delete_stmt (field->aux_table, uids);
7156 success = ebsql_exec (ebsql, stmt, NULL, NULL, NULL, error);
7157 g_free (stmt);
7158 }
7159
7160 /* Now delete the entry from the main contacts */
7161 if (success) {
7162 stmt = generate_delete_stmt (ebsql->priv->folderid, uids);
7163 success = ebsql_exec (ebsql, stmt, NULL, NULL, NULL, error);
7164 g_free (stmt);
7165 }
7166
7167 if (success)
7168 success = ebsql_commit_transaction (ebsql, error);
7169 else
7170 /* The GError is already set. */
7171 ebsql_rollback_transaction (ebsql, NULL);
7172
7173 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
7174
7175 return success;
7176 }
7177
7178 /**
7179 * e_book_sqlite_has_contact:
7180 * @ebsql: An #EBookSqlite
7181 * @uid: The uid of the contact to check for
7182 * @exists: (out): Return location to store whether the contact exists.
7183 * @error: A location to store any error that may have occurred.
7184 *
7185 * Checks if a contact bearing the UID indicated by @uid is stored in @ebsql.
7186 *
7187 * Returns: %TRUE on success, otherwise %FALSE is returned and @error is set appropriately.
7188 *
7189 * Since: 3.12
7190 **/
7191 gboolean
e_book_sqlite_has_contact(EBookSqlite * ebsql,const gchar * uid,gboolean * exists,GError ** error)7192 e_book_sqlite_has_contact (EBookSqlite *ebsql,
7193 const gchar *uid,
7194 gboolean *exists,
7195 GError **error)
7196 {
7197 gboolean local_exists = FALSE;
7198 gboolean success;
7199
7200 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), FALSE);
7201 g_return_val_if_fail (uid != NULL, FALSE);
7202 g_return_val_if_fail (exists != NULL, FALSE);
7203
7204 EBSQL_LOCK_MUTEX (&ebsql->priv->lock);
7205 success = ebsql_exec_printf (
7206 ebsql,
7207 "SELECT uid FROM %Q WHERE uid = %Q",
7208 get_exists_cb, &local_exists, NULL, error,
7209 ebsql->priv->folderid, uid);
7210 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
7211
7212 *exists = local_exists;
7213
7214 return success;
7215 }
7216
7217 /**
7218 * e_book_sqlite_get_contact:
7219 * @ebsql: An #EBookSqlite
7220 * @uid: The uid of the contact to fetch
7221 * @meta_contact: Whether an entire contact is desired, or only the metadata
7222 * @ret_contact: (out): Return location to store the fetched contact
7223 * @error: A location to store any error that may have occurred.
7224 *
7225 * Fetch the #EContact specified by @uid in @ebsql.
7226 *
7227 * If @meta_contact is specified, then a shallow #EContact will be created
7228 * holding only the %E_CONTACT_UID and %E_CONTACT_REV fields.
7229 *
7230 * Returns: %TRUE on success, otherwise %FALSE is returned and @error is set appropriately.
7231 *
7232 * Since: 3.12
7233 **/
7234 gboolean
e_book_sqlite_get_contact(EBookSqlite * ebsql,const gchar * uid,gboolean meta_contact,EContact ** ret_contact,GError ** error)7235 e_book_sqlite_get_contact (EBookSqlite *ebsql,
7236 const gchar *uid,
7237 gboolean meta_contact,
7238 EContact **ret_contact,
7239 GError **error)
7240 {
7241 gboolean success = FALSE;
7242 gchar *vcard = NULL;
7243
7244 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), FALSE);
7245 g_return_val_if_fail (uid != NULL, FALSE);
7246 g_return_val_if_fail (ret_contact != NULL && *ret_contact == NULL, FALSE);
7247
7248 success = e_book_sqlite_get_vcard (
7249 ebsql, uid, meta_contact, &vcard, error);
7250
7251 if (success && vcard) {
7252 *ret_contact = e_contact_new_from_vcard_with_uid (vcard, uid);
7253 g_free (vcard);
7254 }
7255
7256 return success;
7257 }
7258
7259 /**
7260 * ebsql_get_contact_unlocked:
7261 * @ebsql: An #EBookSqlite
7262 * @uid: The uid of the contact to fetch
7263 * @meta_contact: Whether an entire contact is desired, or only the metadata
7264 * @contact: (out): Return location to store the fetched contact
7265 * @error: A location to store any error that may have occurred.
7266 *
7267 * Fetch the #EContact specified by @uid in @ebsql without locking internal mutex.
7268 *
7269 * If @meta_contact is specified, then a shallow #EContact will be created
7270 * holding only the %E_CONTACT_UID and %E_CONTACT_REV fields.
7271 *
7272 * Returns: %TRUE on success, otherwise %FALSE is returned and @error is set appropriately.
7273 *
7274 * Since: 3.16
7275 **/
7276 gboolean
ebsql_get_contact_unlocked(EBookSqlite * ebsql,const gchar * uid,gboolean meta_contact,EContact ** contact,GError ** error)7277 ebsql_get_contact_unlocked (EBookSqlite *ebsql,
7278 const gchar *uid,
7279 gboolean meta_contact,
7280 EContact **contact,
7281 GError **error)
7282 {
7283 gboolean success = FALSE;
7284 gchar *vcard = NULL;
7285
7286 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), FALSE);
7287 g_return_val_if_fail (uid != NULL, FALSE);
7288 g_return_val_if_fail (contact != NULL && *contact == NULL, FALSE);
7289
7290 success = ebsql_get_vcard_unlocked (ebsql,
7291 uid,
7292 meta_contact,
7293 &vcard,
7294 error);
7295
7296 if (success && vcard) {
7297 *contact = e_contact_new_from_vcard_with_uid (vcard, uid);
7298 g_free (vcard);
7299 }
7300
7301 return success;
7302 }
7303
7304 /**
7305 * e_book_sqlite_get_vcard:
7306 * @ebsql: An #EBookSqlite
7307 * @uid: The uid of the contact to fetch
7308 * @meta_contact: Whether an entire contact is desired, or only the metadata
7309 * @ret_vcard: (out): Return location to store the fetched vcard string
7310 * @error: A location to store any error that may have occurred.
7311 *
7312 * Fetch a vcard string for @uid in @ebsql.
7313 *
7314 * If @meta_contact is specified, then a shallow vcard representation will be
7315 * created holding only the %E_CONTACT_UID and %E_CONTACT_REV fields.
7316 *
7317 * Returns: %TRUE on success, otherwise %FALSE is returned and @error is set appropriately.
7318 *
7319 * Since: 3.12
7320 **/
7321 gboolean
e_book_sqlite_get_vcard(EBookSqlite * ebsql,const gchar * uid,gboolean meta_contact,gchar ** ret_vcard,GError ** error)7322 e_book_sqlite_get_vcard (EBookSqlite *ebsql,
7323 const gchar *uid,
7324 gboolean meta_contact,
7325 gchar **ret_vcard,
7326 GError **error)
7327 {
7328 gboolean success = FALSE;
7329 gchar *vcard = NULL;
7330
7331 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), FALSE);
7332 g_return_val_if_fail (uid != NULL, FALSE);
7333 g_return_val_if_fail (ret_vcard != NULL && *ret_vcard == NULL, FALSE);
7334
7335 EBSQL_LOCK_MUTEX (&ebsql->priv->lock);
7336
7337 /* Try constructing contacts from only UID/REV first if that's requested */
7338 if (meta_contact) {
7339 GSList *vcards = NULL;
7340
7341 success = ebsql_exec_printf (
7342 ebsql, "SELECT summary.uid, summary.Rev FROM %Q AS summary WHERE uid = %Q",
7343 collect_lean_results_cb, &vcards, NULL, error,
7344 ebsql->priv->folderid, uid);
7345
7346 if (vcards) {
7347 EbSqlSearchData *search_data = (EbSqlSearchData *) vcards->data;
7348
7349 vcard = search_data->vcard;
7350 search_data->vcard = NULL;
7351
7352 g_slist_free_full (vcards, (GDestroyNotify) e_book_sqlite_search_data_free);
7353 vcards = NULL;
7354 }
7355
7356 } else {
7357 success = ebsql_exec_printf (
7358 ebsql, "SELECT %s FROM %Q AS summary WHERE summary.uid = %Q",
7359 get_string_cb, &vcard, NULL, error,
7360 EBSQL_VCARD_FRAGMENT (ebsql), ebsql->priv->folderid, uid);
7361 }
7362
7363 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
7364
7365 *ret_vcard = vcard;
7366
7367 if (success && !vcard) {
7368 EBSQL_SET_ERROR (
7369 error,
7370 E_BOOK_SQLITE_ERROR_CONTACT_NOT_FOUND,
7371 _("Contact “%s” not found"), uid);
7372 success = FALSE;
7373 }
7374
7375 return success;
7376 }
7377
7378 /**
7379 * ebsql_get_vcard_unlocked:
7380 * @ebsql: An #EBookSqlite
7381 * @uid: The uid of the contact to fetch
7382 * @meta_contact: Whether an entire contact is desired, or only the metadata
7383 * @ret_vcard: (out): Return location to store the fetched vcard string
7384 * @error: A location to store any error that may have occurred.
7385 *
7386 * Fetch a vcard string for @uid in @ebsql without locking internal mutex.
7387 *
7388 * If @meta_contact is specified, then a shallow vcard representation will be
7389 * created holding only the %E_CONTACT_UID and %E_CONTACT_REV fields.
7390 *
7391 * Returns: %TRUE on success, otherwise %FALSE is returned and @error is set appropriately.
7392 *
7393 * Since: 3.16
7394 **/
7395 gboolean
ebsql_get_vcard_unlocked(EBookSqlite * ebsql,const gchar * uid,gboolean meta_contact,gchar ** ret_vcard,GError ** error)7396 ebsql_get_vcard_unlocked (EBookSqlite *ebsql,
7397 const gchar *uid,
7398 gboolean meta_contact,
7399 gchar **ret_vcard,
7400 GError **error)
7401 {
7402 gboolean success = FALSE;
7403 gchar *vcard = NULL;
7404
7405 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), FALSE);
7406 g_return_val_if_fail (uid != NULL, FALSE);
7407 g_return_val_if_fail (ret_vcard != NULL && *ret_vcard == NULL, FALSE);
7408
7409 /* Try constructing contacts from only UID/REV first if that's requested */
7410 if (meta_contact) {
7411 GSList *vcards = NULL;
7412
7413 success = ebsql_exec_printf (
7414 ebsql, "SELECT summary.uid, summary.Rev FROM %Q AS summary WHERE uid = %Q",
7415 collect_lean_results_cb, &vcards, NULL, error,
7416 ebsql->priv->folderid, uid);
7417
7418 if (vcards) {
7419 EbSqlSearchData *search_data = (EbSqlSearchData *) vcards->data;
7420
7421 vcard = search_data->vcard;
7422 search_data->vcard = NULL;
7423
7424 g_slist_free_full (vcards, (GDestroyNotify) e_book_sqlite_search_data_free);
7425 vcards = NULL;
7426 }
7427
7428 } else {
7429 success = ebsql_exec_printf (
7430 ebsql, "SELECT %s FROM %Q AS summary WHERE summary.uid = %Q",
7431 get_string_cb, &vcard, NULL, error,
7432 EBSQL_VCARD_FRAGMENT (ebsql), ebsql->priv->folderid, uid);
7433 }
7434
7435 *ret_vcard = vcard;
7436
7437 if (success && !vcard) {
7438 EBSQL_SET_ERROR (error,
7439 E_BOOK_SQLITE_ERROR_CONTACT_NOT_FOUND,
7440 _("Contact “%s” not found"), uid);
7441 success = FALSE;
7442 }
7443
7444 return success;
7445 }
7446
7447 /**
7448 * e_book_sqlite_set_contact_extra:
7449 * @ebsql: An #EBookSqlite
7450 * @uid: The uid of the contact to set the extra data for
7451 * @extra: (nullable): The extra data to set
7452 * @error: A location to store any error that may have occurred.
7453 *
7454 * Sets or replaces the extra data associated with @uid.
7455 *
7456 * Returns: %TRUE on success, otherwise %FALSE is returned and @error is set appropriately.
7457 *
7458 * Since: 3.12
7459 **/
7460 gboolean
e_book_sqlite_set_contact_extra(EBookSqlite * ebsql,const gchar * uid,const gchar * extra,GError ** error)7461 e_book_sqlite_set_contact_extra (EBookSqlite *ebsql,
7462 const gchar *uid,
7463 const gchar *extra,
7464 GError **error)
7465 {
7466 gboolean success;
7467
7468 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), FALSE);
7469 g_return_val_if_fail (uid != NULL, FALSE);
7470
7471 EBSQL_LOCK_MUTEX (&ebsql->priv->lock);
7472 success = ebsql_exec_printf (
7473 ebsql, "UPDATE %Q SET bdata = %Q WHERE uid = %Q",
7474 NULL, NULL, NULL, error,
7475 ebsql->priv->folderid, uid);
7476 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
7477
7478 return success;
7479 }
7480
7481 /**
7482 * e_book_sqlite_get_contact_extra:
7483 * @ebsql: An #EBookSqlite
7484 * @uid: The uid of the contact to fetch the extra data for
7485 * @ret_extra: (out): Return location to store the extra data
7486 * @error: A location to store any error that may have occurred.
7487 *
7488 * Fetches the extra data previously set for @uid, either with
7489 * e_book_sqlite_set_contact_extra() or when adding contacts.
7490 *
7491 * Returns: %TRUE on success, otherwise %FALSE is returned and @error is set appropriately.
7492 *
7493 * Since: 3.12
7494 **/
7495 gboolean
e_book_sqlite_get_contact_extra(EBookSqlite * ebsql,const gchar * uid,gchar ** ret_extra,GError ** error)7496 e_book_sqlite_get_contact_extra (EBookSqlite *ebsql,
7497 const gchar *uid,
7498 gchar **ret_extra,
7499 GError **error)
7500 {
7501 gboolean success;
7502
7503 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), FALSE);
7504 g_return_val_if_fail (uid != NULL, FALSE);
7505 g_return_val_if_fail (ret_extra != NULL && *ret_extra == NULL, FALSE);
7506
7507 EBSQL_LOCK_MUTEX (&ebsql->priv->lock);
7508 success = ebsql_exec_printf (
7509 ebsql, "SELECT bdata FROM %Q WHERE uid = %Q",
7510 get_string_cb, ret_extra, NULL, error,
7511 ebsql->priv->folderid, uid);
7512 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
7513
7514 return success;
7515 }
7516
7517 /**
7518 * ebsql_get_contact_extra_unlocked:
7519 * @ebsql: An #EBookSqlite
7520 * @uid: The uid of the contact to fetch the extra data for
7521 * @ret_extra: (out): Return location to store the extra data
7522 * @error: A location to store any error that may have occurred.
7523 *
7524 * Fetches the extra data previously set for @uid, either with
7525 * e_book_sqlite_set_contact_extra() or when adding contacts,
7526 * without locking internal mutex.
7527 *
7528 * Returns: %TRUE on success, otherwise %FALSE is returned and @error is set appropriately.
7529 *
7530 * Since: 3.16
7531 **/
7532 gboolean
ebsql_get_contact_extra_unlocked(EBookSqlite * ebsql,const gchar * uid,gchar ** ret_extra,GError ** error)7533 ebsql_get_contact_extra_unlocked (EBookSqlite *ebsql,
7534 const gchar *uid,
7535 gchar **ret_extra,
7536 GError **error)
7537 {
7538 gboolean success;
7539
7540 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), FALSE);
7541 g_return_val_if_fail (uid != NULL, FALSE);
7542 g_return_val_if_fail (ret_extra != NULL && *ret_extra == NULL, FALSE);
7543
7544 success = ebsql_exec_printf (
7545 ebsql, "SELECT bdata FROM %Q WHERE uid = %Q",
7546 get_string_cb, ret_extra, NULL, error,
7547 ebsql->priv->folderid, uid);
7548
7549 return success;
7550 }
7551
7552 /**
7553 * e_book_sqlite_search:
7554 * @ebsql: An #EBookSqlite
7555 * @sexp: (nullable): search expression; use %NULL or an empty string to list all stored contacts.
7556 * @meta_contacts: Whether entire contacts are desired, or only the metadata
7557 * @ret_list: (out) (transfer full) (element-type EbSqlSearchData): Return location
7558 * to store a #GSList of #EbSqlSearchData structures
7559 * @cancellable: A #GCancellable
7560 * @error: A location to store any error that may have occurred.
7561 *
7562 * Searches @ebsql for contacts matching the search expression indicated by @sexp.
7563 *
7564 * When @sexp refers only to #EContactFields configured in the summary of @ebsql,
7565 * the search should always be quick, when searching for other #EContactFields
7566 * a fallback will be used, possibly invoking any #EbSqlVCardCallback which
7567 * may have been passed to e_book_sqlite_new_full().
7568 *
7569 * The returned @ret_list list should be freed with g_slist_free()
7570 * and all elements freed with e_book_sqlite_search_data_free().
7571 *
7572 * If @meta_contact is specified, then shallow vcard representations will be
7573 * created holding only the %E_CONTACT_UID and %E_CONTACT_REV fields.
7574 *
7575 * Returns: %TRUE on success, otherwise %FALSE is returned and @error is set appropriately.
7576 *
7577 * Since: 3.12
7578 **/
7579 gboolean
e_book_sqlite_search(EBookSqlite * ebsql,const gchar * sexp,gboolean meta_contacts,GSList ** ret_list,GCancellable * cancellable,GError ** error)7580 e_book_sqlite_search (EBookSqlite *ebsql,
7581 const gchar *sexp,
7582 gboolean meta_contacts,
7583 GSList **ret_list,
7584 GCancellable *cancellable,
7585 GError **error)
7586 {
7587 gboolean success;
7588
7589 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), FALSE);
7590 g_return_val_if_fail (ret_list != NULL && *ret_list == NULL, FALSE);
7591
7592 EBSQL_LOCK_OR_RETURN (ebsql, cancellable, FALSE);
7593 success = ebsql_search_query (
7594 ebsql, sexp,
7595 meta_contacts ?
7596 SEARCH_UID_AND_REV : SEARCH_FULL,
7597 ret_list,
7598 cancellable,
7599 error);
7600 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
7601
7602 return success;
7603 }
7604
7605 /**
7606 * e_book_sqlite_search_uids:
7607 * @ebsql: An #EBookSqlite
7608 * @sexp: (nullable): search expression; use %NULL or an empty string to get all stored contacts.
7609 * @ret_list: (out) (transfer full) (element-type utf8): Return location to store a #GSList of contact uids
7610 * @cancellable: A #GCancellable
7611 * @error: A location to store any error that may have occurred.
7612 *
7613 * Similar to e_book_sqlitedb_search(), but fetches only a list of contact UIDs.
7614 *
7615 * The returned @ret_list list should be freed with g_slist_free() and all
7616 * elements freed with g_free().
7617 *
7618 * Returns: %TRUE on success, otherwise %FALSE is returned and @error is set appropriately.
7619 *
7620 * Since: 3.12
7621 **/
7622 gboolean
e_book_sqlite_search_uids(EBookSqlite * ebsql,const gchar * sexp,GSList ** ret_list,GCancellable * cancellable,GError ** error)7623 e_book_sqlite_search_uids (EBookSqlite *ebsql,
7624 const gchar *sexp,
7625 GSList **ret_list,
7626 GCancellable *cancellable,
7627 GError **error)
7628 {
7629 gboolean success;
7630
7631 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), FALSE);
7632 g_return_val_if_fail (ret_list != NULL && *ret_list == NULL, FALSE);
7633
7634 EBSQL_LOCK_OR_RETURN (ebsql, cancellable, FALSE);
7635 success = ebsql_search_query (ebsql, sexp, SEARCH_UID, ret_list, cancellable, error);
7636 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
7637
7638 return success;
7639 }
7640
7641 /**
7642 * e_book_sqlite_get_key_value:
7643 * @ebsql: An #EBookSqlite
7644 * @key: The key to fetch a value for
7645 * @value: (out): A return location to store the value for @key
7646 * @error: A location to store any error that may have occurred.
7647 *
7648 * Fetches the value for @key and stores it in @value
7649 *
7650 * Returns: %TRUE on success, otherwise %FALSE is returned and @error is set appropriately.
7651 *
7652 * Since: 3.12
7653 **/
7654 gboolean
e_book_sqlite_get_key_value(EBookSqlite * ebsql,const gchar * key,gchar ** value,GError ** error)7655 e_book_sqlite_get_key_value (EBookSqlite *ebsql,
7656 const gchar *key,
7657 gchar **value,
7658 GError **error)
7659 {
7660 gboolean success;
7661
7662 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), FALSE);
7663 g_return_val_if_fail (key != NULL, FALSE);
7664 g_return_val_if_fail (value != NULL && *value == NULL, FALSE);
7665
7666 EBSQL_LOCK_MUTEX (&ebsql->priv->lock);
7667 success = ebsql_exec_printf (
7668 ebsql,
7669 "SELECT value FROM keys WHERE folder_id = %Q AND key = %Q",
7670 get_string_cb, value, NULL, error,
7671 ebsql->priv->folderid, key);
7672 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
7673
7674 return success;
7675 }
7676
7677 /**
7678 * e_book_sqlite_set_key_value:
7679 * @ebsql: An #EBookSqlite
7680 * @key: The key to fetch a value for
7681 * @value: The new value for @key
7682 * @error: A location to store any error that may have occurred.
7683 *
7684 * Sets the value for @key to be @value
7685 *
7686 * Returns: %TRUE on success, otherwise %FALSE is returned and @error is set appropriately.
7687 *
7688 * Since: 3.12
7689 **/
7690 gboolean
e_book_sqlite_set_key_value(EBookSqlite * ebsql,const gchar * key,const gchar * value,GError ** error)7691 e_book_sqlite_set_key_value (EBookSqlite *ebsql,
7692 const gchar *key,
7693 const gchar *value,
7694 GError **error)
7695 {
7696 gboolean success;
7697
7698 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), FALSE);
7699 g_return_val_if_fail (key != NULL, FALSE);
7700 g_return_val_if_fail (value != NULL, FALSE);
7701
7702 EBSQL_LOCK_MUTEX (&ebsql->priv->lock);
7703 success = ebsql_exec_printf (
7704 ebsql, "INSERT or REPLACE INTO keys (key, value, folder_id) values (%Q, %Q, %Q)",
7705 NULL, NULL, NULL, error,
7706 key, value, ebsql->priv->folderid);
7707 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
7708
7709 return success;
7710 }
7711
7712 /**
7713 * e_book_sqlite_get_key_value_int:
7714 * @ebsql: An #EBookSqlite
7715 * @key: The key to fetch a value for
7716 * @value: (out): A return location to store the value for @key
7717 * @error: A location to store any error that may have occurred.
7718 *
7719 * A convenience function to fetch the value of @key as an integer.
7720 *
7721 * Returns: %TRUE on success, otherwise %FALSE is returned and @error is set appropriately.
7722 *
7723 * Since: 3.12
7724 **/
7725 gboolean
e_book_sqlite_get_key_value_int(EBookSqlite * ebsql,const gchar * key,gint * value,GError ** error)7726 e_book_sqlite_get_key_value_int (EBookSqlite *ebsql,
7727 const gchar *key,
7728 gint *value,
7729 GError **error)
7730 {
7731 gboolean success;
7732 gchar *str_value = NULL;
7733
7734 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), FALSE);
7735 g_return_val_if_fail (key != NULL, FALSE);
7736 g_return_val_if_fail (value != NULL, FALSE);
7737
7738 success = e_book_sqlite_get_key_value (ebsql, key, &str_value, error);
7739
7740 if (success) {
7741
7742 if (str_value)
7743 *value = g_ascii_strtoll (str_value, NULL, 10);
7744 else
7745 *value = 0;
7746
7747 g_free (str_value);
7748 }
7749
7750 return success;
7751 }
7752
7753 /**
7754 * e_book_sqlite_set_key_value_int:
7755 * @ebsql: An #EBookSqlite
7756 * @key: The key to fetch a value for
7757 * @value: The new value for @key
7758 * @error: A location to store any error that may have occurred.
7759 *
7760 * A convenience function to set the value of @key as an integer.
7761 *
7762 * Returns: %TRUE on success, otherwise %FALSE is returned and @error is set appropriately.
7763 *
7764 * Since: 3.12
7765 **/
7766 gboolean
e_book_sqlite_set_key_value_int(EBookSqlite * ebsql,const gchar * key,gint value,GError ** error)7767 e_book_sqlite_set_key_value_int (EBookSqlite *ebsql,
7768 const gchar *key,
7769 gint value,
7770 GError **error)
7771 {
7772 gboolean success;
7773 gchar *str_value = NULL;
7774
7775 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), FALSE);
7776 g_return_val_if_fail (key != NULL, FALSE);
7777
7778 str_value = g_strdup_printf ("%d", value);
7779 success = e_book_sqlite_set_key_value (
7780 ebsql, key, str_value, error);
7781 g_free (str_value);
7782
7783 return success;
7784 }
7785
7786 /**
7787 * e_book_sqlite_search_data_free:
7788 * @data: An #EbSqlSearchData
7789 *
7790 * Frees an #EbSqlSearchData
7791 *
7792 * Since: 3.12
7793 **/
7794 void
e_book_sqlite_search_data_free(EbSqlSearchData * data)7795 e_book_sqlite_search_data_free (EbSqlSearchData *data)
7796 {
7797 if (data) {
7798 g_free (data->uid);
7799 g_free (data->vcard);
7800 g_free (data->extra);
7801 g_slice_free (EbSqlSearchData, data);
7802 }
7803 }
7804
7805 /**
7806 * e_book_sqlite_set_locale:
7807 * @ebsql: An #EBookSqlite
7808 * @lc_collate: The new locale for the addressbook
7809 * @cancellable: A #GCancellable
7810 * @error: A location to store any error that may have occurred
7811 *
7812 * Relocalizes any locale specific data in the specified
7813 * new @lc_collate locale.
7814 *
7815 * The @lc_collate locale setting is stored and remembered on
7816 * subsequent accesses of the addressbook, changing the locale
7817 * will store the new locale and will modify sort keys and any
7818 * locale specific data in the addressbook.
7819 *
7820 * As a side effect, it's possible that changing the locale
7821 * will cause stored vcards to change. Notifications for
7822 * these changes can be caught with the #EbSqlVCardCallback
7823 * provided to e_book_sqlite_new_full().
7824 *
7825 * Returns: Whether the new locale was successfully set.
7826 *
7827 * Since: 3.12
7828 */
7829 gboolean
e_book_sqlite_set_locale(EBookSqlite * ebsql,const gchar * lc_collate,GCancellable * cancellable,GError ** error)7830 e_book_sqlite_set_locale (EBookSqlite *ebsql,
7831 const gchar *lc_collate,
7832 GCancellable *cancellable,
7833 GError **error)
7834 {
7835 gboolean success;
7836 gchar *stored_lc_collate = NULL;
7837
7838 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), FALSE);
7839
7840 EBSQL_LOCK_OR_RETURN (ebsql, cancellable, FALSE);
7841
7842 if (!ebsql_start_transaction (ebsql, EBSQL_LOCK_WRITE, cancellable, error)) {
7843 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
7844 return FALSE;
7845 }
7846
7847 success = ebsql_set_locale_internal (ebsql, lc_collate, error);
7848
7849 if (success)
7850 success = ebsql_exec_printf (
7851 ebsql, "SELECT lc_collate FROM folders WHERE folder_id = %Q",
7852 get_string_cb, &stored_lc_collate, NULL, error,
7853 ebsql->priv->folderid);
7854
7855 if (success && g_strcmp0 (stored_lc_collate, lc_collate) != 0)
7856 success = ebsql_upgrade (ebsql, EBSQL_CHANGE_LOCALE_CHANGED, error);
7857
7858 /* If for some reason we failed, then reset the collator to use the old locale */
7859 if (!success && stored_lc_collate && stored_lc_collate[0])
7860 ebsql_set_locale_internal (ebsql, stored_lc_collate, NULL);
7861
7862 if (success)
7863 success = ebsql_commit_transaction (ebsql, error);
7864 else
7865 /* The GError is already set. */
7866 ebsql_rollback_transaction (ebsql, NULL);
7867
7868 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
7869
7870 g_free (stored_lc_collate);
7871
7872 return success;
7873 }
7874
7875 /**
7876 * e_book_sqlite_get_locale:
7877 * @ebsql: An #EBookSqlite
7878 * @locale_out: (out) (transfer full): The location to return the current locale
7879 * @error: A location to store any error that may have occurred
7880 *
7881 * Fetches the current locale setting for the address-book.
7882 *
7883 * Upon success, @lc_collate_out will hold the returned locale setting,
7884 * otherwise %FALSE will be returned and @error will be updated accordingly.
7885 *
7886 * Returns: Whether the locale was successfully fetched.
7887 *
7888 * Since: 3.12
7889 */
7890 gboolean
e_book_sqlite_get_locale(EBookSqlite * ebsql,gchar ** locale_out,GError ** error)7891 e_book_sqlite_get_locale (EBookSqlite *ebsql,
7892 gchar **locale_out,
7893 GError **error)
7894 {
7895 gboolean success;
7896 GError *local_error = NULL;
7897
7898 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), FALSE);
7899 g_return_val_if_fail (locale_out != NULL && *locale_out == NULL, FALSE);
7900
7901 EBSQL_LOCK_MUTEX (&ebsql->priv->lock);
7902
7903 success = ebsql_exec_printf (
7904 ebsql, "SELECT lc_collate FROM folders WHERE folder_id = %Q",
7905 get_string_cb, locale_out, NULL, error,
7906 ebsql->priv->folderid);
7907
7908 if (*locale_out == NULL) {
7909
7910 /* This can't realistically happen, if it does we
7911 * should warn about it in stdout */
7912 g_warning ("EBookSqlite has no active locale in the database");
7913
7914 *locale_out = g_strdup (ebsql->priv->locale);
7915 }
7916
7917 if (success && !ebsql_set_locale_internal (ebsql, *locale_out, &local_error)) {
7918 g_warning ("Error loading new locale: %s", local_error->message);
7919 g_clear_error (&local_error);
7920 }
7921
7922 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
7923
7924 return success;
7925 }
7926
7927 /**
7928 * e_book_sqlite_cursor_new: (skip)
7929 * @ebsql: An #EBookSqlite
7930 * @sexp: search expression; use NULL or an empty string to get all stored contacts.
7931 * @sort_fields: (array length=n_sort_fields): An array of #EContactFields as sort keys in order of priority
7932 * @sort_types: (array length=n_sort_fields): An array of #EBookCursorSortTypes, one for each field in @sort_fields
7933 * @n_sort_fields: The number of fields to sort results by.
7934 * @error: A return location to store any error that might be reported.
7935 *
7936 * Creates a new #EbSqlCursor.
7937 *
7938 * The cursor should be freed with e_book_sqlite_cursor_free().
7939 *
7940 * Returns: (transfer full): A newly created #EbSqlCursor
7941 *
7942 * Since: 3.12
7943 */
7944 EbSqlCursor *
e_book_sqlite_cursor_new(EBookSqlite * ebsql,const gchar * sexp,const EContactField * sort_fields,const EBookCursorSortType * sort_types,guint n_sort_fields,GError ** error)7945 e_book_sqlite_cursor_new (EBookSqlite *ebsql,
7946 const gchar *sexp,
7947 const EContactField *sort_fields,
7948 const EBookCursorSortType *sort_types,
7949 guint n_sort_fields,
7950 GError **error)
7951 {
7952 EbSqlCursor *cursor;
7953 gint i;
7954
7955 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), NULL);
7956
7957 /* We don't like '\0' sexps, prefer NULL */
7958 if (sexp && !sexp[0])
7959 sexp = NULL;
7960
7961 EBSQL_LOCK_MUTEX (&ebsql->priv->lock);
7962
7963 /* Need one sort key ... */
7964 if (n_sort_fields == 0) {
7965 EBSQL_SET_ERROR_LITERAL (
7966 error, E_BOOK_SQLITE_ERROR_INVALID_QUERY,
7967 _("At least one sort field must be specified to use an EbSqlCursor"));
7968 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
7969 return NULL;
7970 }
7971
7972 /* We only support string fields to sort the cursor */
7973 for (i = 0; i < n_sort_fields; i++) {
7974 EBSQL_NOTE (
7975 CURSOR,
7976 g_printerr (
7977 "Building cursor to sort '%s' in '%s' order\n",
7978 e_contact_field_name (sort_fields[i]),
7979 sort_types[i] == E_BOOK_CURSOR_SORT_ASCENDING ?
7980 "ascending" : "descending"));
7981
7982 if (e_contact_field_type (sort_fields[i]) != G_TYPE_STRING) {
7983 EBSQL_SET_ERROR_LITERAL (
7984 error, E_BOOK_SQLITE_ERROR_INVALID_QUERY,
7985 _("Cannot sort by a field that is not a string type"));
7986 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
7987 return NULL;
7988 }
7989 }
7990
7991 /* Now we need to create the cursor instance before setting up the query
7992 * (not really true, but more convenient that way).
7993 */
7994 cursor = ebsql_cursor_new (ebsql, sexp, sort_fields, sort_types, n_sort_fields);
7995
7996 /* Setup the cursor's query expression which might fail */
7997 if (!ebsql_cursor_setup_query (ebsql, cursor, sexp, error)) {
7998 ebsql_cursor_free (cursor);
7999 cursor = NULL;
8000 }
8001
8002 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
8003
8004 EBSQL_NOTE (
8005 CURSOR,
8006 g_printerr (
8007 "%s cursor with search expression '%s'\n",
8008 cursor ? "Successfully created" : "Failed to create",
8009 sexp));
8010
8011 return cursor;
8012 }
8013
8014 /**
8015 * e_book_sqlite_cursor_free: (skip)
8016 * @ebsql: An #EBookSqlite
8017 * @cursor: The #EbSqlCursor to free
8018 *
8019 * Frees @cursor.
8020 *
8021 * Since: 3.12
8022 */
8023 void
e_book_sqlite_cursor_free(EBookSqlite * ebsql,EbSqlCursor * cursor)8024 e_book_sqlite_cursor_free (EBookSqlite *ebsql,
8025 EbSqlCursor *cursor)
8026 {
8027 g_return_if_fail (E_IS_BOOK_SQLITE (ebsql));
8028
8029 ebsql_cursor_free (cursor);
8030 }
8031
8032 typedef struct {
8033 GSList *results;
8034 gchar *alloc_vcard;
8035 const gchar *last_vcard;
8036
8037 gboolean collect_results;
8038 gint n_results;
8039 } CursorCollectData;
8040
8041 static gint
collect_results_for_cursor_cb(gpointer ref,gint ncol,gchar ** cols,gchar ** names)8042 collect_results_for_cursor_cb (gpointer ref,
8043 gint ncol,
8044 gchar **cols,
8045 gchar **names)
8046 {
8047 CursorCollectData *data = ref;
8048
8049 if (data->collect_results) {
8050 EbSqlSearchData *search_data;
8051
8052 search_data = search_data_from_results (ncol, cols, names);
8053
8054 data->results = g_slist_prepend (data->results, search_data);
8055
8056 data->last_vcard = search_data->vcard;
8057 } else {
8058 g_free (data->alloc_vcard);
8059 data->alloc_vcard = g_strdup (cols[1]);
8060
8061 data->last_vcard = data->alloc_vcard;
8062 }
8063
8064 data->n_results++;
8065
8066 return 0;
8067 }
8068
8069 /**
8070 * e_book_sqlite_cursor_step: (skip)
8071 * @ebsql: An #EBookSqlite
8072 * @cursor: The #EbSqlCursor to use
8073 * @flags: The #EbSqlCursorStepFlags for this step
8074 * @origin: The #EbSqlCursorOrigin from whence to step
8075 * @count: A positive or negative amount of contacts to try and fetch
8076 * @results: (out) (nullable) (element-type EbSqlSearchData) (transfer full):
8077 * A return location to store the results, or %NULL if %EBSQL_CURSOR_STEP_FETCH is not specified in @flags.
8078 * @cancellable: A #GCancellable
8079 * @error: A return location to store any error that might be reported.
8080 *
8081 * Steps @cursor through its sorted query by a maximum of @count contacts
8082 * starting from @origin.
8083 *
8084 * If @count is negative, then the cursor will move through the list in reverse.
8085 *
8086 * If @cursor reaches the beginning or end of the query results, then the
8087 * returned list might not contain the amount of desired contacts, or might
8088 * return no results if the cursor currently points to the last contact.
8089 * Reaching the end of the list is not considered an error condition. Attempts
8090 * to step beyond the end of the list after having reached the end of the list
8091 * will however trigger an %E_BOOK_SQLITE_ERROR_END_OF_LIST error.
8092 *
8093 * If %EBSQL_CURSOR_STEP_FETCH is specified in @flags, a pointer to
8094 * a %NULL #GSList pointer should be provided for the @results parameter.
8095 *
8096 * The result list will be stored to @results and should be freed with g_slist_free()
8097 * and all elements freed with e_book_sqlite_search_data_free().
8098 *
8099 * Returns: The number of contacts traversed if successful, otherwise -1 is
8100 * returned and @error is set.
8101 *
8102 * Since: 3.12
8103 */
8104 gint
e_book_sqlite_cursor_step(EBookSqlite * ebsql,EbSqlCursor * cursor,EbSqlCursorStepFlags flags,EbSqlCursorOrigin origin,gint count,GSList ** results,GCancellable * cancellable,GError ** error)8105 e_book_sqlite_cursor_step (EBookSqlite *ebsql,
8106 EbSqlCursor *cursor,
8107 EbSqlCursorStepFlags flags,
8108 EbSqlCursorOrigin origin,
8109 gint count,
8110 GSList **results,
8111 GCancellable *cancellable,
8112 GError **error)
8113 {
8114 CursorCollectData data = { NULL, NULL, NULL, FALSE, 0 };
8115 CursorState *state;
8116 GString *query;
8117 gboolean success;
8118 EbSqlCursorOrigin try_position;
8119
8120 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), -1);
8121 g_return_val_if_fail (cursor != NULL, -1);
8122 g_return_val_if_fail ((flags & EBSQL_CURSOR_STEP_FETCH) == 0 ||
8123 (results != NULL && *results == NULL), -1);
8124
8125 /* Lock and check cancellable early */
8126 EBSQL_LOCK_OR_RETURN (ebsql, cancellable, -1);
8127
8128 EBSQL_NOTE (
8129 CURSOR,
8130 g_printerr (
8131 "Cursor requested to step by %d with origin %s will move: %s will fetch: %s\n",
8132 count, ebsql_origin_str (origin),
8133 (flags & EBSQL_CURSOR_STEP_MOVE) ? "yes" : "no",
8134 (flags & EBSQL_CURSOR_STEP_FETCH) ? "yes" : "no"));
8135
8136 /* Check if this step should result in an end of list error first */
8137 try_position = cursor->state.position;
8138 if (origin != EBSQL_CURSOR_ORIGIN_CURRENT)
8139 try_position = origin;
8140
8141 /* Report errors for requests to run off the end of the list */
8142 if (try_position == EBSQL_CURSOR_ORIGIN_BEGIN && count < 0) {
8143 EBSQL_SET_ERROR_LITERAL (
8144 error, E_BOOK_SQLITE_ERROR_END_OF_LIST,
8145 _("Tried to step a cursor in reverse, "
8146 "but cursor is already at the beginning of the contact list"));
8147
8148 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
8149 return -1;
8150 } else if (try_position == EBSQL_CURSOR_ORIGIN_END && count > 0) {
8151 EBSQL_SET_ERROR_LITERAL (
8152 error, E_BOOK_SQLITE_ERROR_END_OF_LIST,
8153 _("Tried to step a cursor forwards, "
8154 "but cursor is already at the end of the contact list"));
8155
8156 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
8157 return -1;
8158 }
8159
8160 /* Nothing to do, silently return */
8161 if (count == 0 && try_position == EBSQL_CURSOR_ORIGIN_CURRENT) {
8162 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
8163 return 0;
8164 }
8165
8166 /* If we're not going to modify the position, just use
8167 * a copy of the current cursor state.
8168 */
8169 if ((flags & EBSQL_CURSOR_STEP_MOVE) != 0)
8170 state = &(cursor->state);
8171 else
8172 state = cursor_state_copy (cursor, &(cursor->state));
8173
8174 /* Every query starts with the STATE_CURRENT position, first
8175 * fix up the cursor state according to 'origin'
8176 */
8177 switch (origin) {
8178 case EBSQL_CURSOR_ORIGIN_CURRENT:
8179 /* Do nothing, normal operation */
8180 break;
8181
8182 case EBSQL_CURSOR_ORIGIN_BEGIN:
8183 case EBSQL_CURSOR_ORIGIN_END:
8184
8185 /* Prepare the state before executing the query */
8186 cursor_state_clear (cursor, state, origin);
8187 break;
8188 }
8189
8190 /* If count is 0 then there is no need to run any
8191 * query, however it can be useful if you just want
8192 * to move the cursor to the beginning or ending of
8193 * the list.
8194 */
8195 if (count == 0) {
8196
8197 /* Free the state copy if need be */
8198 if ((flags & EBSQL_CURSOR_STEP_MOVE) == 0)
8199 cursor_state_free (cursor, state);
8200
8201 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
8202 return 0;
8203 }
8204
8205 query = g_string_new (cursor->select_vcards);
8206
8207 /* Add the filter constraints (if any) */
8208 if (cursor->query) {
8209 g_string_append (query, " WHERE ");
8210
8211 g_string_append_c (query, '(');
8212 g_string_append (query, cursor->query);
8213 g_string_append_c (query, ')');
8214 }
8215
8216 /* Add the cursor constraints (if any) */
8217 if (state->values[0] != NULL) {
8218 gchar *constraints = NULL;
8219
8220 if (!cursor->query)
8221 g_string_append (query, " WHERE ");
8222 else
8223 g_string_append (query, " AND ");
8224
8225 constraints = ebsql_cursor_constraints (
8226 ebsql, cursor, state, count < 0, FALSE);
8227
8228 g_string_append_c (query, '(');
8229 g_string_append (query, constraints);
8230 g_string_append_c (query, ')');
8231
8232 g_free (constraints);
8233 }
8234
8235 /* Add the sort order */
8236 g_string_append_c (query, ' ');
8237 if (count > 0)
8238 g_string_append (query, cursor->order);
8239 else
8240 g_string_append (query, cursor->reverse_order);
8241
8242 /* Add the limit */
8243 g_string_append_printf (query, " LIMIT %d", ABS (count));
8244
8245 /* Specify whether we really want results or not */
8246 data.collect_results = (flags & EBSQL_CURSOR_STEP_FETCH) != 0;
8247
8248 /* Execute the query */
8249 success = ebsql_exec (
8250 ebsql, query->str,
8251 collect_results_for_cursor_cb, &data,
8252 cancellable, error);
8253
8254 /* Lock was obtained above */
8255 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
8256
8257 g_string_free (query, TRUE);
8258
8259 /* If there was no error, update the internal cursor state */
8260 if (success) {
8261
8262 if (data.n_results < ABS (count)) {
8263
8264 /* We've reached the end, clear the current state */
8265 if (count < 0)
8266 cursor_state_clear (cursor, state, EBSQL_CURSOR_ORIGIN_BEGIN);
8267 else
8268 cursor_state_clear (cursor, state, EBSQL_CURSOR_ORIGIN_END);
8269
8270 } else if (data.last_vcard) {
8271
8272 /* Set the cursor state to the last result */
8273 cursor_state_set_from_vcard (ebsql, cursor, state, data.last_vcard);
8274 } else
8275 /* Should never get here */
8276 g_warn_if_reached ();
8277
8278 /* Assign the results to return (if any) */
8279 if (results) {
8280 /* Correct the order of results at the last minute */
8281 *results = g_slist_reverse (data.results);
8282 data.results = NULL;
8283 }
8284 }
8285
8286 /* Cleanup what was allocated by collect_results_for_cursor_cb() */
8287 if (data.results)
8288 g_slist_free_full (
8289 data.results,
8290 (GDestroyNotify) e_book_sqlite_search_data_free);
8291 g_free (data.alloc_vcard);
8292
8293 /* Free the copy state if we were working with a copy */
8294 if ((flags & EBSQL_CURSOR_STEP_MOVE) == 0)
8295 cursor_state_free (cursor, state);
8296
8297 if (success)
8298 return data.n_results;
8299
8300 return -1;
8301 }
8302
8303 /**
8304 * e_book_sqlite_cursor_set_target_alphabetic_index: (skip)
8305 * @ebsql: An #EBookSqlite
8306 * @cursor: The #EbSqlCursor to modify
8307 * @idx: The alphabetic index
8308 *
8309 * Sets the @cursor position to an
8310 * <link linkend="cursor-alphabet">Alphabetic Index</link>
8311 * into the alphabet active in @ebsql's locale.
8312 *
8313 * After setting the target to an alphabetic index, for example the
8314 * index for letter 'E', then further calls to e_book_sqlite_cursor_step()
8315 * will return results starting with the letter 'E' (or results starting
8316 * with the last result in 'D', if moving in a negative direction).
8317 *
8318 * The passed index must be a valid index in the active locale, knowledge
8319 * on the currently active alphabet index must be obtained using #ECollator
8320 * APIs.
8321 *
8322 * Use e_book_sqlite_ref_collator() to obtain the active collator for @ebsql.
8323 *
8324 * Since: 3.12
8325 */
8326 void
e_book_sqlite_cursor_set_target_alphabetic_index(EBookSqlite * ebsql,EbSqlCursor * cursor,gint idx)8327 e_book_sqlite_cursor_set_target_alphabetic_index (EBookSqlite *ebsql,
8328 EbSqlCursor *cursor,
8329 gint idx)
8330 {
8331 gint n_labels = 0;
8332
8333 g_return_if_fail (E_IS_BOOK_SQLITE (ebsql));
8334 g_return_if_fail (cursor != NULL);
8335 g_return_if_fail (idx >= 0);
8336
8337 e_collator_get_index_labels (
8338 ebsql->priv->collator, &n_labels,
8339 NULL, NULL, NULL);
8340 g_return_if_fail (idx < n_labels);
8341
8342 cursor_state_clear (cursor, &(cursor->state), EBSQL_CURSOR_ORIGIN_CURRENT);
8343 if (cursor->n_sort_fields > 0) {
8344 SummaryField *field;
8345 gchar *index_key;
8346
8347 index_key = e_collator_generate_key_for_index (ebsql->priv->collator, idx);
8348 field = summary_field_get (ebsql, cursor->sort_fields[0]);
8349
8350 if (field && (field->index & INDEX_FLAG (SORT_KEY)) != 0) {
8351 cursor->state.values[0] = index_key;
8352 } else {
8353 cursor->state.values[0] =
8354 ebsql_encode_vcard_sort_key (index_key);
8355 g_free (index_key);
8356 }
8357 }
8358 }
8359
8360 /**
8361 * e_book_sqlite_cursor_set_sexp: (skip)
8362 * @ebsql: An #EBookSqlite
8363 * @cursor: The #EbSqlCursor
8364 * @sexp: The new query expression for @cursor
8365 * @error: A return location to store any error that might be reported.
8366 *
8367 * Modifies the current query expression for @cursor. This will not
8368 * modify @cursor's state, but will change the outcome of any further
8369 * calls to e_book_sqlite_cursor_calculate() or
8370 * e_book_sqlite_cursor_step().
8371 *
8372 * Returns: %TRUE if the expression was valid and accepted by @ebsql
8373 *
8374 * Since: 3.12
8375 */
8376 gboolean
e_book_sqlite_cursor_set_sexp(EBookSqlite * ebsql,EbSqlCursor * cursor,const gchar * sexp,GError ** error)8377 e_book_sqlite_cursor_set_sexp (EBookSqlite *ebsql,
8378 EbSqlCursor *cursor,
8379 const gchar *sexp,
8380 GError **error)
8381 {
8382 gboolean success;
8383
8384 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), FALSE);
8385 g_return_val_if_fail (cursor != NULL, FALSE);
8386
8387 /* We don't like '\0' sexps, prefer NULL */
8388 if (sexp && !sexp[0])
8389 sexp = NULL;
8390
8391 EBSQL_LOCK_MUTEX (&ebsql->priv->lock);
8392 success = ebsql_cursor_setup_query (ebsql, cursor, sexp, error);
8393 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
8394
8395 return success;
8396 }
8397
8398 /**
8399 * e_book_sqlite_cursor_calculate: (skip)
8400 * @ebsql: An #EBookSqlite
8401 * @cursor: The #EbSqlCursor
8402 * @total: (out) (optional): A return location to store the total result set for this cursor
8403 * @position: (out) (optional): A return location to store the total results before the cursor value
8404 * @cancellable: A #GCancellable
8405 * @error: A return location to store any error that might be reported.
8406 *
8407 * Calculates the @total amount of results for the @cursor's query expression,
8408 * as well as the current @position of @cursor in the results. @position is
8409 * represented as the amount of results which lead up to the current value
8410 * of @cursor, if @cursor currently points to an exact contact, the position
8411 * also includes the cursor contact.
8412 *
8413 * Returns: Whether @total and @position were successfully calculated.
8414 *
8415 * Since: 3.12
8416 */
8417 gboolean
e_book_sqlite_cursor_calculate(EBookSqlite * ebsql,EbSqlCursor * cursor,gint * total,gint * position,GCancellable * cancellable,GError ** error)8418 e_book_sqlite_cursor_calculate (EBookSqlite *ebsql,
8419 EbSqlCursor *cursor,
8420 gint *total,
8421 gint *position,
8422 GCancellable *cancellable,
8423 GError **error)
8424 {
8425 gboolean success = TRUE;
8426 gint local_total = 0;
8427
8428 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), FALSE);
8429 g_return_val_if_fail (cursor != NULL, FALSE);
8430
8431 /* If we're in a clear cursor state, then the position is 0 */
8432 if (position && cursor->state.values[0] == NULL) {
8433
8434 if (cursor->state.position == EBSQL_CURSOR_ORIGIN_BEGIN) {
8435 /* Mark the local pointer NULL, no need to calculate this anymore */
8436 *position = 0;
8437 position = NULL;
8438 } else if (cursor->state.position == EBSQL_CURSOR_ORIGIN_END) {
8439
8440 /* Make sure that we look up the total so we can
8441 * set the position to 'total + 1'
8442 */
8443 if (!total)
8444 total = &local_total;
8445 }
8446 }
8447
8448 /* Early return if there is nothing to do */
8449 if (!total && !position)
8450 return TRUE;
8451
8452 EBSQL_LOCK_OR_RETURN (ebsql, cancellable, -1);
8453
8454 /* Start a read transaction, it's important our two queries are atomic */
8455 if (!ebsql_start_transaction (ebsql, EBSQL_LOCK_READ, cancellable, error)) {
8456 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
8457 return FALSE;
8458 }
8459
8460 if (total)
8461 success = cursor_count_total_locked (ebsql, cursor, total, error);
8462
8463 if (success && position)
8464 success = cursor_count_position_locked (ebsql, cursor, position, error);
8465
8466 if (success)
8467 success = ebsql_commit_transaction (ebsql, error);
8468 else
8469 /* The GError is already set. */
8470 ebsql_rollback_transaction (ebsql, NULL);
8471
8472 EBSQL_UNLOCK_MUTEX (&ebsql->priv->lock);
8473
8474 /* In the case we're at the end, we just set the position
8475 * to be the total + 1
8476 */
8477 if (success && position && total &&
8478 cursor->state.position == EBSQL_CURSOR_ORIGIN_END)
8479 *position = *total + 1;
8480
8481 return success;
8482 }
8483
8484 /**
8485 * e_book_sqlite_cursor_compare_contact: (skip)
8486 * @ebsql: An #EBookSqlite
8487 * @cursor: The #EbSqlCursor
8488 * @contact: The #EContact to compare
8489 * @matches_sexp: (out) (optional): Whether the contact matches the cursor's search expression
8490 *
8491 * Compares @contact with @cursor and returns whether @contact is less than, equal to, or greater
8492 * than @cursor.
8493 *
8494 * Returns: A value that is less than, equal to, or greater than zero if @contact is found,
8495 * respectively, to be less than, to match, or be greater than the current value of @cursor.
8496 *
8497 * Since: 3.12
8498 */
8499 gint
e_book_sqlite_cursor_compare_contact(EBookSqlite * ebsql,EbSqlCursor * cursor,EContact * contact,gboolean * matches_sexp)8500 e_book_sqlite_cursor_compare_contact (EBookSqlite *ebsql,
8501 EbSqlCursor *cursor,
8502 EContact *contact,
8503 gboolean *matches_sexp)
8504 {
8505 EBookSqlitePrivate *priv;
8506 gint i;
8507 gint comparison = 0;
8508
8509 g_return_val_if_fail (E_IS_BOOK_SQLITE (ebsql), -1);
8510 g_return_val_if_fail (E_IS_CONTACT (contact), -1);
8511 g_return_val_if_fail (cursor != NULL, -1);
8512
8513 priv = ebsql->priv;
8514
8515 if (matches_sexp) {
8516 if (cursor->sexp == NULL)
8517 *matches_sexp = TRUE;
8518 else
8519 *matches_sexp =
8520 e_book_backend_sexp_match_contact (cursor->sexp, contact);
8521 }
8522
8523 for (i = 0; i < cursor->n_sort_fields && comparison == 0; i++) {
8524 SummaryField *field;
8525 gchar *contact_key = NULL;
8526 const gchar *cursor_key = NULL;
8527 const gchar *field_value;
8528 gchar *freeme = NULL;
8529
8530 field_value = (const gchar *) e_contact_get_const (contact, cursor->sort_fields[i]);
8531 if (field_value)
8532 contact_key = e_collator_generate_key (priv->collator, field_value, NULL);
8533
8534 field = summary_field_get (ebsql, cursor->sort_fields[i]);
8535
8536 if (field && (field->index & INDEX_FLAG (SORT_KEY)) != 0) {
8537 cursor_key = cursor->state.values[i];
8538 } else {
8539
8540 if (cursor->state.values[i])
8541 freeme = ebsql_decode_vcard_sort_key (cursor->state.values[i]);
8542
8543 cursor_key = freeme;
8544 }
8545
8546 /* Empty state sorts below any contact value, which means the contact sorts above cursor */
8547 if (cursor_key == NULL)
8548 comparison = 1;
8549 else
8550 /* Check if contact sorts below, equal to, or above the cursor */
8551 comparison = g_strcmp0 (contact_key, cursor_key);
8552
8553 g_free (contact_key);
8554 g_free (freeme);
8555 }
8556
8557 /* UID tie-breaker */
8558 if (comparison == 0) {
8559 const gchar *uid;
8560
8561 uid = (const gchar *) e_contact_get_const (contact, E_CONTACT_UID);
8562
8563 if (cursor->state.last_uid == NULL)
8564 comparison = 1;
8565 else if (uid == NULL)
8566 comparison = -1;
8567 else
8568 comparison = strcmp (uid, cursor->state.last_uid);
8569 }
8570
8571 return comparison;
8572 }
8573