2 * Copyright (c) 2018 Samsung Electronics Co., Ltd. All rights reserved.
4 * Licensed under the Apache License, Version 2.0 (the "License")
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
25 #include "ua-manager-common.h"
26 #include "ua-manager-database.h"
28 #define UAM_DB_USERDATA_TABLE "userdata" /**< User data DB table name */
29 #define UAM_DB_DEVICES_TABLE "devices" /**< Device DB table name */
30 #define UAM_DB_SERVICES_TABLE "services" /**< Service DB table name */
31 #define UAM_DB_DEVICE_SERVICES_TABLE "device_services" /**< Device services DB table name */
32 #define UAM_DB_IBEACON_ADV_TABLE "ibeacon_adv" /**< iBeacon adv DB table name */
33 #define UAM_DB_PAYLOADS_TABLE "payloads" /**< payload DB table name */
35 #define CREATE_USERDATA_TABLE "CREATE TABLE IF NOT EXISTS userdata ( " \
37 "user_id INTEGER PRIMARY KEY AUTOINCREMENT, " \
42 #define CREATE_DEVICES_TABLE "CREATE TABLE IF NOT EXISTS devices ( " \
43 "device_number INTEGER PRIMARY KEY AUTOINCREMENT, " \
46 "tech_type INTEGER, " \
50 "presence_state INTEGER, " \
52 "discriminant INTEGER, " \
53 "FOREIGN KEY(user_id) REFERENCES userdata(user_id), " \
54 "UNIQUE (device_id, tech_type, address) " \
57 #define CREATE_PAYLOADS_TABLE "CREATE TABLE IF NOT EXISTS payloads ( " \
58 "payload_number INTEGER PRIMARY KEY AUTOINCREMENT, " \
59 "primary_key TEXT, " \
60 "secondary_key TEXT, " \
62 "device_icon TEXT, " \
63 "device_number INTEGER, " \
64 "FOREIGN KEY(device_number) REFERENCES devices(device_number), " \
65 "UNIQUE (primary_key, secondary_key, device_uid) " \
68 #define CREATE_SERVICES_TABLE "CREATE TABLE IF NOT EXISTS services ( " \
69 "service_number INTEGER PRIMARY KEY AUTOINCREMENT, " \
70 "service_name TEXT, " \
72 "presence_threshold INTEGER, " \
73 "absence_threshold INTEGER, " \
74 "UNIQUE (service_name) " \
77 #define CREATE_DEVICE_SERVICES_TABLE "CREATE TABLE IF NOT EXISTS device_services ( " \
78 "device_number INTEGER, " \
79 "service_number INTEGER, " \
80 "payload_number INTEGER, " \
81 "discriminant INTEGER, " \
83 "FOREIGN KEY(device_number) REFERENCES devices(device_number), " \
84 "FOREIGN KEY(service_number) REFERENCES services(service_number), " \
85 "FOREIGN KEY(payload_number) REFERENCES payloads(payload_number), " \
86 "PRIMARY KEY(device_number, service_number) " \
89 #define CREATE_IBEACON_ADV_TABLE "CREATE TABLE IF NOT EXISTS ibeacon_adv ( " \
90 "ibeacon_id INTEGER PRIMARY KEY AUTOINCREMENT, " \
91 "ibeacon_adv TEXT, " \
93 "UNIQUE (ibeacon_adv) " \
98 sqlite3 *database_handle;
99 static sqlite3_stmt *select_version;
100 static int transaction_cnt;
102 static int __uam_db_exec_sql(char *sql, void *cb)
108 if (NULL == database_handle) {
109 UAM_ERR("database_handle is NULL");
110 return UAM_ERROR_DB_FAILED;
114 UAM_ERR("sql is NULL");
115 return UAM_ERROR_DB_FAILED;
118 sql_ret = sqlite3_exec(database_handle, sql, cb, NULL, &error);
119 if (SQLITE_OK != sql_ret) {
120 UAM_ERR("Failed to execute sql: (%d) %s", sql_ret, error);
122 sqlite3_close(database_handle);
123 database_handle = NULL;
124 return UAM_ERROR_DB_FAILED;
127 return UAM_ERROR_NONE;
130 static int __uam_db_open(void)
135 res = sqlite3_open_v2(DATABASE_FULL_PATH, &(database_handle),
136 SQLITE_OPEN_FULLMUTEX | SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, NULL);
137 if (SQLITE_OK != res) {
138 UAM_ERR("sqlite3_open_v2 failed, (%d)", res);
140 return UAM_ERROR_DB_FAILED;
142 UAM_DBG("Successfully opened database");
148 static int __uam_db_busy(void *data, int attempt)
151 int max_attempts = SQLITE_MAX_RETRY;
153 if (attempt < max_attempts) {
154 UAM_WARN("DB locked, attempt number %d, Attempt Remaining %d",
155 attempt, max_attempts - attempt);
156 usleep(SQLITE_BUSY_TIMEOUT); /* wait for a half second*/
160 UAM_ERR("DB locked by another process, attempts tried %d, Exit",
166 static int __uam_db_get_version(unsigned int *ver)
169 int error_code = UAM_ERROR_NONE;
172 sql = sqlite3_mprintf("PRAGMA user_version");
173 sqlite3_stmt *stmt = select_version;
175 sql_ret = sqlite3_prepare_v2(database_handle, sql, -1, &stmt, NULL);
176 if (sql_ret != SQLITE_OK) {
178 FINALIZE(select_version);
179 UAM_ERR("Failed to prepare \"%s\" query", sql);
185 sql_ret = sqlite3_step(stmt);
191 *ver = sqlite3_column_int(stmt, 0);
192 UAM_INFO("database version %d", *ver);
196 UAM_ERR("Failed to select database version info [%d:%s]",
197 sql_ret, sqlite3_errmsg(database_handle));
199 } while (sql_ret == SQLITE_ROW);
201 FINALIZE(select_version);
203 sqlite3_finalize(stmt);
209 static int __uam_db_update_version(unsigned int version)
213 int ret = UAM_ERROR_NONE;
215 sql = sqlite3_mprintf("PRAGMA user_version = '%d';", version);
217 ret = __uam_db_exec_sql(sql, NULL);
218 if (UAM_ERROR_NONE != ret)
219 UAM_DBG("failed to updated database version to %d", version);
221 UAM_DBG("Successfully updated database version to %d", version);
229 static int __uam_db_upgrade(unsigned int version)
233 This code should handle all the database upgrade after initial version
236 /* update database version */
237 retv_if(UAM_ERROR_NONE != __uam_db_update_version(version), UAM_ERROR_DB_FAILED);
238 UAM_DBG("Successfully upgraded database version to %d", version);
241 return UAM_ERROR_NONE;
244 static int __uam_db_upgrade_version(unsigned int old_ver, unsigned int new_ver)
247 int ret = UAM_ERROR_NONE;
250 for (i = old_ver + 1; i <= new_ver; i++) {
251 ret = __uam_db_upgrade(i);
252 if (UAM_ERROR_NONE != ret) {
253 UAM_ERR("Faild to __uam_db_upgrade(%d)", i);
254 ret = UAM_ERROR_DB_FAILED;
263 int _uam_db_check_version(void)
266 unsigned int curr_ver = VERSION;
267 unsigned int old_ver = 0;
269 /* check database version */
270 retv_if(UAM_ERROR_NONE != __uam_db_get_version(&old_ver), UAM_ERROR_DB_FAILED);
273 retv_if(UAM_ERROR_NONE != __uam_db_update_version(curr_ver), UAM_ERROR_DB_FAILED);
274 } else if (old_ver != curr_ver) {
275 UAM_DBG("Database version changed, needs upgrade");
276 /* upgrade database to new version */
277 retv_if(UAM_ERROR_NONE != __uam_db_upgrade_version(old_ver, curr_ver), UAM_ERROR_DB_FAILED);
281 return UAM_ERROR_NONE;
284 static bool __uam_db_is_table_existing(const char *table)
289 int sql_ret = SQLITE_OK;
291 sql = sqlite3_mprintf(
292 "SELECT count(*) FROM sqlite_master WHERE type='table' AND name ='%s';", table);
294 UAM_ERR("sqlite3_mprintf failed");
297 sqlite3_stmt *stmt = NULL;
299 sql_ret = sqlite3_prepare_v2(database_handle, sql, strlen(sql), &stmt, NULL);
300 if (SQLITE_OK != sql_ret) {
301 UAM_ERR("sqlite3_prepare_v2 failed, [%d:%s]", sql_ret, sqlite3_errmsg(database_handle));
303 goto is_table_existing_done;
306 sql_ret = sqlite3_step(stmt);
307 if (sql_ret != SQLITE_ROW) {
308 UAM_ERR("sqlite3_step failed, [%d:%s]", sql_ret, sqlite3_errmsg(database_handle));
310 goto is_table_existing_done;
313 count = sqlite3_column_int(stmt, 0);
319 is_table_existing_done:
320 sqlite3_finalize(stmt);
326 static int __uam_db_check_integrity_cb(
327 void *err, int count, char **data, char **columns)
331 UAM_DBG("%s = %s\n", columns[0], data[0] ? data[0] : "NULL");
332 if (!g_strcmp0(columns[0], "integrity_check") && !g_strcmp0(data[0], "ok"))
338 static int __uam_db_check_integrity(void)
341 int ret = UAM_ERROR_NONE;
344 sql = sqlite3_mprintf("PRAGMA integrity_check");
346 ret = __uam_db_exec_sql(sql, __uam_db_check_integrity_cb);
347 if (UAM_ERROR_NONE != ret)
348 UAM_ERR("Faild to __uam_db_exec_sql()");
350 UAM_DBG("Successfully verified database integrity");
358 static int __uam_db_set_locking_mode(void)
361 int ret = UAM_ERROR_NONE;
363 sql = sqlite3_mprintf("PRAGMA locking_mode = NORMAL");
364 ret = __uam_db_exec_sql(sql, NULL);
365 if (UAM_ERROR_NONE != ret)
366 ret = UAM_ERROR_DB_FAILED;
373 static int __uam_db_create_table(const char *table_name)
376 int ret = UAM_ERROR_NONE;
378 sql = sqlite3_mprintf(table_name);
379 ret = __uam_db_exec_sql(sql, NULL);
381 if (UAM_ERROR_NONE != ret)
382 UAM_ERR("Faild to __uam_db_exec_sql()");
384 UAM_DBG("Successfully created table %s", table_name);
391 static int __uam_db_check_table_creation(void)
394 int error_code = UAM_ERROR_DB_FAILED;
396 if (!__uam_db_is_table_existing(UAM_DB_USERDATA_TABLE))
397 retv_if(UAM_ERROR_NONE != __uam_db_create_table(CREATE_USERDATA_TABLE), error_code);
399 if (!__uam_db_is_table_existing(UAM_DB_DEVICES_TABLE))
400 retv_if(UAM_ERROR_NONE != __uam_db_create_table(CREATE_DEVICES_TABLE), error_code);
402 if (!__uam_db_is_table_existing(UAM_DB_SERVICES_TABLE))
403 retv_if(UAM_ERROR_NONE != __uam_db_create_table(CREATE_SERVICES_TABLE), error_code);
405 if (!__uam_db_is_table_existing(UAM_DB_DEVICE_SERVICES_TABLE))
406 retv_if(UAM_ERROR_NONE != __uam_db_create_table(CREATE_DEVICE_SERVICES_TABLE), error_code);
408 if (!__uam_db_is_table_existing(UAM_DB_IBEACON_ADV_TABLE))
409 retv_if(UAM_ERROR_NONE != __uam_db_create_table(CREATE_IBEACON_ADV_TABLE), error_code);
411 if (!__uam_db_is_table_existing(UAM_DB_PAYLOADS_TABLE))
412 retv_if(UAM_ERROR_NONE != __uam_db_create_table(CREATE_PAYLOADS_TABLE), error_code);
414 UAM_DBG("Successfully verified table creation");
416 return UAM_ERROR_NONE;
419 static int __uam_db_verify()
423 /* check table existance*/
424 retv_if(UAM_ERROR_NONE != __uam_db_check_table_creation(), UAM_ERROR_DB_FAILED);
426 _uam_get_file_size(DATABASE_FULL_PATH);
427 /* check db integrity */
428 retv_if(UAM_ERROR_NONE != __uam_db_check_integrity(), UAM_ERROR_DB_FAILED);
429 /* set locking mode */
430 retv_if(UAM_ERROR_NONE != __uam_db_set_locking_mode(), UAM_ERROR_DB_FAILED);
432 UAM_DBG("Successfully verified database");
434 return UAM_ERROR_NONE;
437 int _uam_db_initialize_once(void)
441 int ret = UAM_ERROR_NONE;
446 retv_if(SQLITE_OK != __uam_db_open(), UAM_ERROR_DB_FAILED);
448 if (UAM_ERROR_NONE != __uam_db_verify()) {
449 UAM_ERR("Failed to verify database");
450 sqlite3_close(database_handle);
451 unlink(DATABASE_FULL_PATH);
452 database_handle = NULL;
453 retv_if(0 == max_retries, UAM_ERROR_DB_FAILED);
457 } while (max_retries--);
459 /* Enable persist journal mode */
460 sql = sqlite3_mprintf("PRAGMA journal_mode = PERSIST");
461 ret = __uam_db_exec_sql(sql, NULL);
463 if (UAM_ERROR_NONE != ret) {
464 UAM_ERR("Faile to __uam_db_exec_sql()");
465 return UAM_ERROR_DB_FAILED;
468 if (NULL == database_handle) {
469 unlink(DATABASE_FULL_PATH);
470 return UAM_ERROR_DB_FAILED;
473 /* Set how many times we'll repeat our attempts for sqlite_step */
474 if (SQLITE_OK != sqlite3_busy_handler(database_handle, __uam_db_busy, NULL)) {
475 UAM_ERR("Couldn't set busy handler!");
476 return UAM_ERROR_DB_FAILED;
483 int _uam_db_initialize(void)
486 database_handle = NULL;
488 /* initialize database */
489 EXEC(UAM_ERROR_NONE, _uam_db_initialize_once(), handle_error);
491 /* check database version */
492 EXEC(UAM_ERROR_NONE, _uam_db_check_version(), handle_error);
494 /* initialize tables */
495 EXEC(UAM_ERROR_NONE, _uam_user_db_initialize(), handle_error);
496 EXEC(UAM_ERROR_NONE, _uam_device_db_initialize(), handle_error);
497 EXEC(UAM_ERROR_NONE, _uam_service_db_initialize(), handle_error);
498 EXEC(UAM_ERROR_NONE, _uam_device_service_db_initialize(), handle_error);
499 EXEC(UAM_ERROR_NONE, _uam_adv_db_initialize(), handle_error);
500 EXEC(UAM_ERROR_NONE, _uam_payload_db_initialize(), handle_error);
504 return UAM_ERROR_NONE;
507 _uam_db_deinitialize();
509 return UAM_ERROR_DB_FAILED;
512 int _uam_db_deinitialize(void)
516 retv_if(NULL == database_handle, UAM_ERROR_NONE);
518 _uam_user_db_deinitialize();
519 _uam_device_db_deinitialize();
520 _uam_service_db_deinitialize();
521 _uam_device_service_db_deinitialize();
522 _uam_adv_db_deinitialize();
523 _uam_payload_db_deinitialize();
526 return UAM_ERROR_NONE;
529 int _uam_db_clear(void)
531 int error_code = UAM_ERROR_DB_FAILED;
532 retv_if(NULL == database_handle, UAM_ERROR_DB_FAILED);
534 EXEC(UAM_ERROR_NONE, _uam_user_db_clear(), handle_error);
535 EXEC(UAM_ERROR_NONE, _uam_device_db_clear(), handle_error);
536 EXEC(UAM_ERROR_NONE, _uam_service_db_clear(), handle_error);
537 EXEC(UAM_ERROR_NONE, _uam_device_service_db_clear(), handle_error);
538 EXEC(UAM_ERROR_NONE, _uam_adv_db_clear(), handle_error);
539 EXEC(UAM_ERROR_NONE, _uam_payload_db_clear(), handle_error);
540 UAM_DBG("Table data deleted ");
542 error_code = UAM_ERROR_NONE;
548 int __uam_db_begin_transaction(void)
552 int ret = UAM_ERROR_NONE;
554 if (transaction_cnt <= 0) {
556 sql = sqlite3_mprintf("BEGIN IMMEDIATE TRANSACTION");
558 ret = __uam_db_exec_sql(sql, NULL);
559 if (UAM_ERROR_NONE != ret)
560 UAM_DBG("failed to begin transaction");
562 UAM_DBG("Successful to begin transaction");
567 UAM_DBG("transaction_cnt: %d", transaction_cnt);
573 static int __uam_db_rollback_transaction(void)
577 int ret = UAM_ERROR_NONE;
579 sql = sqlite3_mprintf("ROLLBACK TRANSACTION");
581 ret = __uam_db_exec_sql(sql, NULL);
582 if (UAM_ERROR_NONE != ret)
583 UAM_DBG("failed to rollback transaction");
585 UAM_DBG("Successful to rollback transaction");
593 int __uam_db_end_transaction(gboolean is_success)
597 int ret = UAM_ERROR_NONE;
601 if (0 != transaction_cnt) {
602 UAM_DBG("transaction_cnt: %d", transaction_cnt);
606 if (false == is_success) {
607 ret = __uam_db_rollback_transaction();
611 sql = sqlite3_mprintf("COMMIT TRANSACTION");
613 ret = __uam_db_exec_sql(sql, NULL);
614 if (UAM_ERROR_NONE != ret) {
615 UAM_DBG("failed to commit transaction");
616 ret = __uam_db_rollback_transaction();
618 UAM_DBG("Successful to commit transaction");