4 * Copyright (c) 2010 - 2012 Samsung Electronics Co., Ltd. All rights reserved.
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
21 #include "ctsvc_internal.h"
22 #include "ctsvc_schema.h"
23 #include "ctsvc_sqlite.h"
24 #include "ctsvc_db_plugin_person_helper.h"
25 #include "ctsvc_localize.h"
26 #include "ctsvc_normalize.h"
27 #include "ctsvc_db_init.h"
28 #include "ctsvc_utils.h"
29 #include "ctsvc_db_query.h"
30 #include "ctsvc_record.h"
31 #include "ctsvc_notification.h"
33 #ifdef _CONTACTS_IPC_SERVER
34 #include "ctsvc_server_change_subject.h"
37 int ctsvc_db_person_create_record_from_stmt_with_projection(cts_stmt stmt, unsigned int *projection, int projection_count, contacts_record_h *record)
39 ctsvc_person_s *person;
40 char full_path[CTSVC_IMG_FULL_PATH_SIZE_MAX] = {0};
42 contacts_record_create(_contacts_person._uri, record);
43 person = (ctsvc_person_s*)*record;
46 for(i=0;i<projection_count;i++) {
48 int property_id = projection[i];
51 case CTSVC_PROPERTY_PERSON_ID:
52 person->person_id = ctsvc_stmt_get_int(stmt, i);
54 case CTSVC_PROPERTY_PERSON_DISPLAY_NAME:
55 temp = ctsvc_stmt_get_text(stmt, i);
56 person->display_name = SAFE_STRDUP(temp);
58 case CTSVC_PROPERTY_PERSON_DISPLAY_NAME_INDEX:
59 temp = ctsvc_stmt_get_text(stmt, i);
60 person->display_name_index = SAFE_STRDUP(temp);
62 case CTSVC_PROPERTY_PERSON_DISPLAY_CONTACT_ID:
63 person->name_contact_id = ctsvc_stmt_get_int(stmt, i);
65 case CTSVC_PROPERTY_PERSON_RINGTONE:
66 temp = ctsvc_stmt_get_text(stmt, i);
67 person->ringtone_path = SAFE_STRDUP(temp);
69 case CTSVC_PROPERTY_PERSON_IMAGE_THUMBNAIL:
70 temp = ctsvc_stmt_get_text(stmt, i);
72 snprintf(full_path, sizeof(full_path), "%s/%s", CTS_IMG_FULL_LOCATION, temp);
73 person->image_thumbnail_path = strdup(full_path);
76 case CTSVC_PROPERTY_PERSON_VIBRATION:
77 temp = ctsvc_stmt_get_text(stmt, i);
78 person->vibration = SAFE_STRDUP(temp);
80 case CTSVC_PROPERTY_PERSON_MESSAGE_ALERT:
81 temp = ctsvc_stmt_get_text(stmt, i);
82 person->message_alert = SAFE_STRDUP(temp);
84 case CTSVC_PROPERTY_PERSON_STATUS:
85 temp = ctsvc_stmt_get_text(stmt, i);
86 person->status = SAFE_STRDUP(temp);
88 case CTSVC_PROPERTY_PERSON_IS_FAVORITE:
89 person->is_favorite = ctsvc_stmt_get_int(stmt, i);
91 case CTSVC_PROPERTY_PERSON_HAS_PHONENUMBER:
92 person->has_phonenumber = ctsvc_stmt_get_int(stmt, i);
94 case CTSVC_PROPERTY_PERSON_HAS_EMAIL:
95 person->has_email = ctsvc_stmt_get_int(stmt, i);
97 case CTSVC_PROPERTY_PERSON_LINK_COUNT:
98 person->link_count = ctsvc_stmt_get_int(stmt, i);
100 case CTSVC_PROPERTY_PERSON_ADDRESSBOOK_IDS:
101 temp = ctsvc_stmt_get_text(stmt, i);
102 person->addressbook_ids = SAFE_STRDUP(temp);
104 case CTSVC_PROPERTY_PERSON_FAVORITE_PRIORITY:
107 int value = ctsvc_stmt_get_int(stmt, i);
108 value++; // fix warning
111 // ASSERT_NOT_REACHED("Invalid parameter : property_id(0x%0x) is not supported in projection value(person)", property_id);
112 // return CONTACTS_ERROR_INVALID_PARAMETER;
114 ASSERT_NOT_REACHED("Invalid parameter : property_id(0x%0x) is not supported in value(person)", property_id);
115 return CONTACTS_ERROR_INVALID_PARAMETER;
118 return CONTACTS_ERROR_NONE;
122 int ctsvc_db_person_create_record_from_stmt_with_query(cts_stmt stmt, contacts_query_h query, contacts_record_h *record)
124 RETV_IF(NULL == query, CONTACTS_ERROR_INVALID_PARAMETER);
125 ctsvc_query_s *s_query = (ctsvc_query_s *)query;
127 if (0 == s_query->projection_count)
129 unsigned int *projection = malloc(sizeof(unsigned int)*s_query->property_count);
131 for(i=0;i<s_query->property_count;i++)
133 projection[i] = s_query->properties[i].property_id;
136 int ret = ctsvc_db_person_create_record_from_stmt_with_projection(stmt, projection, s_query->property_count, record);
143 return ctsvc_db_person_create_record_from_stmt_with_projection(stmt, s_query->projection, s_query->projection_count, record);
147 int ctsvc_db_person_create_record_from_stmt(cts_stmt stmt, contacts_record_h *record)
152 ctsvc_person_s *person;
155 ret = contacts_record_create(_contacts_person._uri, record);
156 RETVM_IF(CONTACTS_ERROR_NONE != ret, ret, "contacts_record_create is failed(%d)", ret);
157 person = (ctsvc_person_s *)*record;
158 person->person_id = ctsvc_stmt_get_int(stmt, i++);
160 temp = ctsvc_stmt_get_text(stmt, i++);
161 person->display_name = SAFE_STRDUP(temp);
162 temp = ctsvc_stmt_get_text(stmt, i++);
163 person->display_name_index = SAFE_STRDUP(temp);
164 person->name_contact_id = ctsvc_stmt_get_int(stmt, i++);
166 temp = ctsvc_stmt_get_text(stmt, i++);
168 char full_path[CTSVC_IMG_FULL_PATH_SIZE_MAX] = {0};
169 snprintf(full_path, sizeof(full_path), "%s/%s", CTS_IMG_FULL_LOCATION, temp);
170 person->image_thumbnail_path = strdup(full_path);
173 temp = ctsvc_stmt_get_text(stmt, i++);
174 person->ringtone_path = SAFE_STRDUP(temp);
175 temp = ctsvc_stmt_get_text(stmt, i++);
176 person->vibration = SAFE_STRDUP(temp);
177 temp = ctsvc_stmt_get_text(stmt, i++);
178 person->message_alert = SAFE_STRDUP(temp);
179 temp = ctsvc_stmt_get_text(stmt, i++);
180 person->status = SAFE_STRDUP(temp);
182 person->link_count = ctsvc_stmt_get_int(stmt, i++);
183 temp = ctsvc_stmt_get_text(stmt, i++);
184 person->addressbook_ids = SAFE_STRDUP(temp);
186 person->has_phonenumber = ctsvc_stmt_get_int(stmt, i++);
187 person->has_email = ctsvc_stmt_get_int(stmt, i++);
188 person->is_favorite = ctsvc_stmt_get_int(stmt, i++);
190 return CONTACTS_ERROR_NONE;
193 inline static const char* __cts_get_image_filename(const char* src)
195 const char* dir = CTS_IMG_FULL_LOCATION;
197 while (dir[pos]==src[pos]) {
202 return src + pos + 1;
207 int ctsvc_db_person_set_favorite(int person_id, bool set, bool propagate)
211 cts_stmt stmt = NULL;
212 char query[CTS_SQL_MIN_LEN] = {0};
215 snprintf(query, sizeof(query),
216 "SELECT MAX(favorite_prio) FROM "CTS_TABLE_FAVORITES);
218 stmt = cts_query_prepare(query);
219 RETVM_IF(NULL == stmt, CONTACTS_ERROR_DB, "cts_query_prepare() Failed");
221 ret = cts_stmt_step(stmt);
222 if (1 /*CTS_TRUE*/ == ret) {
223 prio = ctsvc_stmt_get_dbl(stmt, 0);
225 else if (CONTACTS_ERROR_NONE != ret) {
226 CTS_ERR("cts_stmt_step() Failed(%d)", ret);
227 cts_stmt_finalize(stmt);
230 cts_stmt_finalize(stmt);
233 snprintf(query, sizeof(query),
234 "INSERT OR REPLACE INTO "CTS_TABLE_FAVORITES" values(%d, %f)", person_id, prio);
237 snprintf(query, sizeof(query),
238 "DELETE FROM "CTS_TABLE_FAVORITES" WHERE person_id = %d", person_id);
241 ret = ctsvc_query_exec(query);
242 if (CONTACTS_ERROR_NONE != ret) {
243 CTS_ERR("cts_query_exec() Failed(%d)", ret);
248 snprintf(query, sizeof(query),
249 "UPDATE "CTS_TABLE_CONTACTS" SET is_favorite = %d "
250 "WHERE person_id=%d AND deleted = 0", set?1:0, person_id);
251 ret = ctsvc_query_exec(query);
252 if (CONTACTS_ERROR_NONE != ret) {
253 CTS_ERR("cts_query_exec() Failed(%d)", ret);
258 return CONTACTS_ERROR_NONE;
261 int ctsvc_db_insert_person(contacts_record_h record)
264 cts_stmt stmt = NULL;
265 char query[CTS_SQL_MIN_LEN] = {0};
267 ctsvc_contact_s *contact = (ctsvc_contact_s*)record;
270 snprintf(query, sizeof(query),
271 "SELECT status FROM %s "
272 "WHERE contact_id=%d "
273 "ORDER BY timestamp DESC LIMIT 1",
274 CTS_TABLE_ACTIVITIES, contact->id);
275 stmt = cts_query_prepare(query);
277 CTS_ERR("DB error : cts_query_prepare() Failed()");
278 return CONTACTS_ERROR_DB;
281 if (1 == cts_stmt_step(stmt))
282 status = SAFE_STRDUP(ctsvc_stmt_get_text(stmt, 0));
283 cts_stmt_finalize(stmt);
285 version = ctsvc_get_next_ver();
286 snprintf(query, sizeof(query),
287 "INSERT INTO "CTS_TABLE_PERSONS"(name_contact_id, created_ver, changed_ver, "
288 "has_phonenumber, has_email, ringtone_path, vibration, message_alert, status, "
289 "image_thumbnail_path, link_count, addressbook_ids) "
290 "VALUES(%d, %d, %d, %d, %d, ?, ?, ?, ?, ?, 1, '%d') ",
291 contact->id, version, version,
292 contact->has_phonenumber, contact->has_email, contact->addressbook_id);
294 stmt = cts_query_prepare(query);
296 CTS_ERR("DB error : cts_query_prepare() Failed()");
298 return CONTACTS_ERROR_DB;
300 if(contact->ringtone_path)
301 cts_stmt_bind_text(stmt, 1, contact->ringtone_path);
302 if(contact->vibration)
303 cts_stmt_bind_text(stmt, 2, contact->vibration);
304 if(contact->message_alert)
305 cts_stmt_bind_text(stmt, 3, contact->message_alert);
307 cts_stmt_bind_text(stmt, 4, status);
308 if(contact->image_thumbnail_path)
309 cts_stmt_bind_text(stmt, 5, __cts_get_image_filename(contact->image_thumbnail_path));
311 ret = cts_stmt_step(stmt);
312 if (CONTACTS_ERROR_NONE != ret) {
313 CTS_ERR("cts_stmt_step() Failed(%d)", ret);
314 cts_stmt_finalize(stmt);
318 index = cts_db_get_last_insert_id();
320 cts_stmt_finalize(stmt);
322 snprintf(query, sizeof(query),
323 "UPDATE "CTS_TABLE_DATA" SET is_primary_default = 1 "
324 "WHERE is_default = 1 AND contact_id = %d AND is_my_profile = 0", contact->id);
326 ret = ctsvc_query_exec(query);
327 if (CONTACTS_ERROR_NONE != ret) {
328 CTS_ERR("cts_query_exec Failed(%d)", ret);
334 if (contact->is_favorite) {
335 ret = ctsvc_db_person_set_favorite(index, contact->is_favorite, false);
336 if (CONTACTS_ERROR_NONE != ret) {
337 CTS_ERR("cts_stmt_step() Failed(%d)", ret);
343 ctsvc_set_person_noti();
344 #ifdef _CONTACTS_IPC_SERVER
345 ctsvc_change_subject_add_changed_person_id(CONTACTS_CHANGE_INSERTED, index);
351 static inline int __ctsvc_db_update_person_default(int person_id, int datatype)
354 cts_stmt stmt = NULL;
355 char query[CTS_SQL_MIN_LEN] = {0};
357 char *image_thumbnail_path = NULL;
359 snprintf(query, sizeof(query),
360 "SELECT D.id FROM "CTS_TABLE_CONTACTS" C, "CTS_TABLE_DATA" D "
361 "ON C.contact_id = D.contact_id AND C.deleted = 0 "
362 "WHERE C.person_id=%d AND D.datatype=%d AND is_primary_default=1 AND D.is_my_profile = 0",
363 person_id, datatype);
365 ret = ctsvc_query_get_first_int_result(query, &data_id);
366 if (CONTACTS_ERROR_NO_DATA == ret ) {
367 snprintf(query, sizeof(query),
368 "SELECT D.id, D.data3 FROM "CTS_TABLE_CONTACTS" C, "CTS_TABLE_DATA" D "
369 "ON C.contact_id = D.contact_id AND C.deleted = 0 "
370 "WHERE C.person_id=%d AND D.datatype=%d AND D.is_default=1 AND D.is_my_profile = 0 ORDER BY D.id",
371 person_id, datatype);
373 stmt = cts_query_prepare(query);
375 CTS_ERR("cts_query_prepare() Failed");
376 return CONTACTS_ERROR_DB;
379 if ((ret = cts_stmt_step(stmt))) {
380 data_id = ctsvc_stmt_get_int(stmt, 0);
382 snprintf(query, sizeof(query),
383 "UPDATE "CTS_TABLE_DATA" SET is_primary_default=1 WHERE id=%d"
386 ret = ctsvc_query_exec(query);
387 if (CONTACTS_ERROR_NONE != ret) {
388 CTS_ERR("cts_query_exec Failed(%d)", ret);
389 cts_stmt_finalize(stmt);
392 temp = ctsvc_stmt_get_text(stmt, 1);
393 image_thumbnail_path = SAFE_STRDUP(temp);
395 cts_stmt_finalize(stmt);
397 if (CTSVC_DATA_IMAGE == datatype) {
398 if (image_thumbnail_path) {
399 snprintf(query, sizeof(query),
400 "UPDATE "CTS_TABLE_PERSONS" SET image_thumbnail_path=? WHERE person_id=%d", person_id);
401 stmt = cts_query_prepare(query);
402 cts_stmt_bind_text(stmt, 1, image_thumbnail_path);
403 ret = cts_stmt_step(stmt);
404 cts_stmt_finalize(stmt);
405 free(image_thumbnail_path);
406 if (CONTACTS_ERROR_NONE != ret) {
407 CTS_ERR("cts_query_exec Failed(%d)", ret);
413 free(image_thumbnail_path);
417 return CONTACTS_ERROR_NONE;
420 static bool __ctsvc_get_has_column(int person_id, const char *culumn)
423 int contact_count = 0;
424 char query[CTS_SQL_MIN_LEN] = {0};
426 snprintf(query, sizeof(query),
427 "SELECT count(contact_id) FROM "CTS_TABLE_CONTACTS" "
428 "WHERE person_id=%d AND %s=1 AND deleted = 0",
431 ret = ctsvc_query_get_first_int_result(query, &contact_count);
432 RETV_IF(CONTACTS_ERROR_NONE != ret, false);
439 static int __ctsvc_get_thumbnail_contact_id(int person_id)
443 char query[CTS_SQL_MIN_LEN] = {0};
445 snprintf(query, sizeof(query),
446 "SELECT D.contact_id FROM "CTS_TABLE_CONTACTS" C, "CTS_TABLE_DATA" D "
447 "ON C.contact_id = D.contact_id AND C.deleted = 0 "
448 "WHERE C.person_id=%d AND D.datatype=%d AND is_primary_default=1 AND D.is_my_profile = 0",
449 person_id, CTSVC_DATA_IMAGE);
450 ret = ctsvc_query_get_first_int_result(query, &contact_id);
451 RETV_IF(CONTACTS_ERROR_NONE != ret, -1);
455 int ctsvc_db_update_person(contacts_record_h record)
458 cts_stmt stmt = NULL;
459 char query[CTS_SQL_MIN_LEN] = {0};
460 ctsvc_contact_s *contact = (ctsvc_contact_s*)record;
461 bool has_phonenumber=false, has_email=false;
462 int thumbnail_contact_id = 0;
466 ret = ctsvc_begin_trans();
467 RETVM_IF(ret, ret, "ctsvc_begin_trans() Failed(%d)", ret);
469 __ctsvc_db_update_person_default(contact->person_id, CTSVC_DATA_NUMBER);
470 __ctsvc_db_update_person_default(contact->person_id, CTSVC_DATA_EMAIL);
471 __ctsvc_db_update_person_default(contact->person_id, CTSVC_DATA_IMAGE);
473 has_phonenumber = __ctsvc_get_has_column(contact->person_id, "has_phonenumber");
474 has_email = __ctsvc_get_has_column(contact->person_id, "has_email");
475 thumbnail_contact_id = __ctsvc_get_thumbnail_contact_id(contact->person_id);
477 len = snprintf(query, sizeof(query),
478 "UPDATE "CTS_TABLE_PERSONS" SET changed_ver=%d, has_phonenumber=%d, has_email=%d ",
479 ctsvc_get_next_ver(), has_phonenumber, has_email);
481 if (ctsvc_record_check_property_flag((ctsvc_record_s *)record, _contacts_contact.ringtone_path, CTSVC_PROPERTY_FLAG_DIRTY))
482 len += snprintf(query+len, sizeof(query)-len, ", ringtone_path=?");
483 if (ctsvc_record_check_property_flag((ctsvc_record_s *)record, _contacts_contact.vibration, CTSVC_PROPERTY_FLAG_DIRTY))
484 len += snprintf(query+len, sizeof(query)-len, ", vibration=?");
485 if (ctsvc_record_check_property_flag((ctsvc_record_s *)record, _contacts_contact.message_alert, CTSVC_PROPERTY_FLAG_DIRTY))
486 len += snprintf(query+len, sizeof(query)-len, ", message_alert=?");
487 if (ctsvc_record_check_property_flag((ctsvc_record_s *)record, _contacts_contact.image_thumbnail_path, CTSVC_PROPERTY_FLAG_DIRTY) &&
488 (contact->id == thumbnail_contact_id || thumbnail_contact_id == -1))
489 len += snprintf(query+len, sizeof(query)-len, ", image_thumbnail_path=?");
491 snprintf(query+len, sizeof(query)-len,
492 " WHERE person_id=%d", contact->person_id);
494 stmt = cts_query_prepare(query);
496 CTS_ERR("cts_query_prepare() Failed");
497 ctsvc_end_trans(false);
498 return CONTACTS_ERROR_DB;
501 if (ctsvc_record_check_property_flag((ctsvc_record_s *)record, _contacts_contact.ringtone_path, CTSVC_PROPERTY_FLAG_DIRTY)) {
502 if (contact->ringtone_path)
503 cts_stmt_bind_text(stmt, i, contact->ringtone_path);
506 if (ctsvc_record_check_property_flag((ctsvc_record_s *)record, _contacts_contact.vibration, CTSVC_PROPERTY_FLAG_DIRTY)) {
507 if (contact->vibration)
508 cts_stmt_bind_text(stmt, i, contact->vibration);
511 if (ctsvc_record_check_property_flag((ctsvc_record_s *)record, _contacts_contact.message_alert, CTSVC_PROPERTY_FLAG_DIRTY)) {
512 if (contact->message_alert)
513 cts_stmt_bind_text(stmt, i, contact->message_alert);
516 if (ctsvc_record_check_property_flag((ctsvc_record_s *)record, _contacts_contact.image_thumbnail_path, CTSVC_PROPERTY_FLAG_DIRTY) &&
517 (contact->id == thumbnail_contact_id || thumbnail_contact_id == -1)) {
518 if (contact->image_thumbnail_path)
519 cts_stmt_bind_text(stmt, i, contact->image_thumbnail_path);
523 ret = cts_stmt_step(stmt);
524 if (CONTACTS_ERROR_NONE != ret) {
525 CTS_ERR("cts_stmt_step() Failed(%d)", ret);
526 cts_stmt_finalize(stmt);
527 ctsvc_end_trans(false);
530 cts_stmt_finalize(stmt);
533 snprintf(query, sizeof(query),
534 "SELECT is_favorite FROM "CTS_TABLE_CONTACTS" WHERE contact_id =%d ", contact->id);
535 ret = ctsvc_query_get_first_int_result(query, &is_favorite);
536 if (ret < CONTACTS_ERROR_NONE) {
537 CTS_ERR("ctsvc_query_get_first_int_result() Failed(%d)", ret);
538 ctsvc_end_trans(false);
542 snprintf(query, sizeof(query),
543 "SELECT person_id FROM "CTS_TABLE_FAVORITES" WHERE person_id =%d ", contact->person_id);
544 ret = ctsvc_query_get_first_int_result(query, &person_id);
545 if (CONTACTS_ERROR_NO_DATA == ret && is_favorite) {
546 ret = ctsvc_db_person_set_favorite(contact->person_id, true, false);
547 if (CONTACTS_ERROR_NONE != ret) {
548 CTS_ERR("ctsvc_db_person_set_favorite() Failed(%d)", ret);
549 ctsvc_end_trans(false);
553 else if (CONTACTS_ERROR_NONE == ret && !is_favorite) {
554 snprintf(query, sizeof(query),
555 "SELECT person_id FROM "CTS_TABLE_CONTACTS" WHERE person_id =%d AND is_favorite = 1", contact->person_id);
556 ret = ctsvc_query_get_first_int_result(query, &person_id);
557 if (CONTACTS_ERROR_NO_DATA == ret) {
558 ret = ctsvc_db_person_set_favorite(contact->person_id, false, false);
559 if (CONTACTS_ERROR_NONE != ret) {
560 CTS_ERR("ctsvc_db_person_set_favorite() Failed(%d)", ret);
561 ctsvc_end_trans(false);
565 else if (CONTACTS_ERROR_NONE != ret) {
566 CTS_ERR("ctsvc_query_get_first_int_result() Failed(%d)", ret);
567 ctsvc_end_trans(false);
571 else if (ret < CONTACTS_ERROR_NONE && CONTACTS_ERROR_NO_DATA != ret) {
572 CTS_ERR("ctsvc_query_get_first_int_result() Failed(%d)", ret);
573 ctsvc_end_trans(false);
577 ctsvc_set_person_noti();
579 #ifdef _CONTACTS_IPC_SERVER
580 ctsvc_change_subject_add_changed_person_id(CONTACTS_CHANGE_UPDATED, contact->person_id);
583 ret = ctsvc_end_trans(true);
584 if (ret < CONTACTS_ERROR_NONE)
586 CTS_ERR("DB error : ctsvc_end_trans() Failed(%d)", ret);
590 return CONTACTS_ERROR_NONE;
593 void ctsvc_db_normalize_str_callback(sqlite3_context * context,
594 int argc, sqlite3_value ** argv)
596 const char *display_name;
597 int display_name_language = CTSVC_LANG_OTHERS;
600 sqlite3_result_null(context);
604 display_name_language = sqlite3_value_int(argv[1]);
605 if (display_name_language == CTSVC_SORT_OTHERS || display_name_language == CTSVC_SORT_NUMBER) {
606 sqlite3_result_text(context, "#", 1, SQLITE_TRANSIENT);
610 display_name = (const char *)sqlite3_value_text(argv[0]);
614 ret = ctsvc_normalize_index(display_name, &dest);
615 if (ret < CONTACTS_ERROR_NONE) {
616 CTS_ERR("ctsvc_normalize_index() Failed(%d)", ret);
617 sqlite3_result_null(context);
620 CTS_VERBOSE("normalize index : %s, %s", display_name, dest);
621 sqlite3_result_text(context, dest, strlen(dest), SQLITE_TRANSIENT);
627 sqlite3_result_null(context);