modify terminology:calendar->book,allday->localtime,normal->utime,svc->service
[platform/core/pim/calendar-service.git] / server / db / cal_db_util.c
1 /*
2  * Calendar Service
3  *
4  * Copyright (c) 2012 - 2015 Samsung Electronics Co., Ltd. All rights reserved.
5  *
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
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
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.
17  *
18  */
19 #include <unistd.h>
20 #include <fcntl.h>
21 #include <stdbool.h>
22 #include <db-util.h>
23 #include <sys/time.h>
24 #include <sys/stat.h>
25 #include <sys/types.h>
26
27 #include "cal_internal.h"
28 #include "cal_typedef.h"
29 #include "cal_db.h"
30 #include "cal_db_util.h"
31 #include "cal_utils.h"
32
33 #define CAL_QUERY_RETRY_TIME 4
34 #define CAL_COMMIT_TRY_MAX 500000
35 #define CAL_QUERY_RETRY_INTERVAL (50*1000)
36 #define CAL_CHANGES_EVENT 0x01
37 #define CAL_CHANGES_TODO 0x02
38 #define CAL_CHANGES_CALENDAR 0x04
39
40 static TLS sqlite3 *cal_db = NULL;
41 static TLS int transaction_cnt = 0;
42 static TLS int transaction_ver = 0;
43 static TLS bool version_up = false;
44
45 static TLS bool event_change = false;
46 static TLS bool todo_change = false;
47 static TLS bool calendar_change = false;
48
49 static void _cal_db_util_notify_event_change(void)
50 {
51         int fd = open(CAL_NOTI_FILE_EVENT, O_TRUNC | O_RDWR);
52         if (0 <= fd) {
53                 close(fd);
54                 event_change = false;
55         } else {
56                 /* LCOV_EXCL_START */
57                 ERR("open() Fail");
58                 /* LCOV_EXCL_STOP */
59         }
60 }
61
62 static inline void _cal_db_util_notify_todo_change(void)
63 {
64         int fd = open(CAL_NOTI_FILE_TODO, O_TRUNC | O_RDWR);
65         if (0 <= fd) {
66                 close(fd);
67                 todo_change = false;
68         } else {
69                 /* LCOV_EXCL_START */
70                 ERR("open() Fail");
71                 /* LCOV_EXCL_STOP */
72         }
73 }
74
75 static inline void _cal_db_util_notify_calendar_change(void)
76 {
77         int fd = open(CAL_NOTI_FILE_BOOK, O_TRUNC | O_RDWR);
78         if (0 <= fd) {
79                 close(fd);
80                 calendar_change = false;
81         } else {
82                 /* LCOV_EXCL_START */
83                 ERR("open() Fail");
84                 /* LCOV_EXCL_STOP */
85         }
86 }
87
88 static inline void _cal_db_util_cancel_changes(void)
89 {
90         event_change = false;
91         calendar_change = false;
92         todo_change = false;
93 }
94
95 int cal_db_util_notify(cal_noti_type_e type)
96 {
97         if (0 < transaction_cnt) {
98                 switch (type) {
99                 case CAL_NOTI_TYPE_EVENT:
100                         event_change = true;
101                         break;
102                 case CAL_NOTI_TYPE_TODO:
103                         todo_change = true;
104                         break;
105                 case CAL_NOTI_TYPE_BOOK:
106                         calendar_change = true;
107                         break;
108                 default:
109                         /* LCOV_EXCL_START */
110                         ERR("The type(%d) is not supported", type);
111                         return CALENDAR_ERROR_INVALID_PARAMETER;
112                         /* LCOV_EXCL_STOP */
113                 }
114                 return CALENDAR_ERROR_NONE;
115         }
116
117         switch (type) {
118         case CAL_NOTI_TYPE_EVENT:
119                 _cal_db_util_notify_event_change();
120                 break;
121         case CAL_NOTI_TYPE_TODO:
122                 _cal_db_util_notify_todo_change();
123                 break;
124         case CAL_NOTI_TYPE_BOOK:
125                 _cal_db_util_notify_calendar_change();
126                 break;
127         default:
128                 /* LCOV_EXCL_START */
129                 ERR("The type(%d) is not supported", type);
130                 return CALENDAR_ERROR_INVALID_PARAMETER;
131                 /* LCOV_EXCL_STOP */
132         }
133
134         return CALENDAR_ERROR_NONE;
135 }
136
137 int cal_db_util_open(void)
138 {
139         if (cal_db)
140                 return CALENDAR_ERROR_NONE;
141
142         if (-1 == access(DB_PATH, F_OK))
143                 mkdir(DB_PATH, 755);
144
145         if (-1 == access(CAL_DB_FILE, F_OK))
146                 mkdir(DB_PATH, 755);
147
148         int ret = 0;
149         ret = db_util_open(CAL_DB_FILE, &cal_db, 0);
150         if (SQLITE_OK != ret) {
151                 /* LCOV_EXCL_START */
152                 ERR("db_util_open() Fail(%d).", ret);
153                 return CALENDAR_ERROR_DB_FAILED;
154                 /* LCOV_EXCL_STOP */
155         }
156
157         return CALENDAR_ERROR_NONE;
158 }
159
160 int cal_db_util_close(void)
161 {
162         if (NULL == cal_db)
163                 return CALENDAR_ERROR_NONE;
164
165         int ret = 0;
166         ret = db_util_close(cal_db);
167         if (SQLITE_OK != ret)
168                 WARN("db_util_close() Fail(%d)", ret);
169
170         cal_db = NULL;
171         DBG("The database disconnected really.");
172
173         return CALENDAR_ERROR_NONE;
174 }
175
176 int cal_db_util_last_insert_id(void)
177 {
178         return sqlite3_last_insert_rowid(cal_db);
179 }
180
181 int cal_db_util_query_prepare(char *query, sqlite3_stmt **stmt)
182 {
183         int ret = 0;
184
185         RETV_IF(NULL == cal_db, CALENDAR_ERROR_INVALID_PARAMETER);
186         RETV_IF(NULL == query, CALENDAR_ERROR_INVALID_PARAMETER);
187         RETV_IF(NULL == stmt, CALENDAR_ERROR_INVALID_PARAMETER);
188
189         struct timeval from, now, diff;
190         gettimeofday(&from, NULL);
191
192         bool retry = false;
193         do {
194                 ret = sqlite3_prepare_v2(cal_db, query, strlen(query), stmt, NULL);
195                 if (SQLITE_BUSY == ret || SQLITE_LOCKED == ret) {
196                         gettimeofday(&now, NULL);
197                         timersub(&now, &from, &diff);
198                         retry = (diff.tv_sec < CAL_QUERY_RETRY_TIME) ? true : false;
199                         if (retry)
200                                 usleep(CAL_QUERY_RETRY_INTERVAL); /* 50ms */
201                 } else {
202                         retry = false;
203                 }
204         } while (retry);
205
206         switch (ret) {
207         case SQLITE_OK:
208                 ret = CALENDAR_ERROR_NONE;
209                 break;
210         case SQLITE_BUSY:
211                 /* LCOV_EXCL_START */
212                 ERR("SQLITE_BUSY");
213                 ret = CALENDAR_ERROR_DB_FAILED;
214                 break;
215                 /* LCOV_EXCL_STOP */
216         case SQLITE_LOCKED:
217                 /* LCOV_EXCL_START */
218                 ERR("SQLITE_LOCKED");
219                 ret = CALENDAR_ERROR_DB_FAILED;
220                 break;
221                 /* LCOV_EXCL_STOP */
222         default:
223                 /* LCOV_EXCL_START */
224                 ERR("ERROR(%d)", ret);
225                 ret = CALENDAR_ERROR_DB_FAILED;
226                 break;
227                 /* LCOV_EXCL_STOP */
228         }
229         return ret;
230 }
231
232 int cal_db_util_stmt_step(sqlite3_stmt *stmt)
233 {
234         int ret = 0;
235         RETV_IF(NULL == stmt, CALENDAR_ERROR_INVALID_PARAMETER);
236
237         struct timeval from, now, diff;
238         bool retry = false;
239
240         gettimeofday(&from, NULL);
241         do {
242                 ret = sqlite3_step(stmt);
243                 if (SQLITE_BUSY == ret || SQLITE_LOCKED == ret) {
244                         gettimeofday(&now, NULL);
245                         timersub(&now, &from, &diff);
246                         retry = (diff.tv_sec < CAL_QUERY_RETRY_TIME) ? true : false;
247                         if (retry)
248                                 usleep(CAL_QUERY_RETRY_INTERVAL); /* 50ms */
249                 } else {
250                         retry = false;
251                 }
252         } while (retry);
253
254         switch (ret) {
255         case SQLITE_ROW:
256                 ret = CAL_SQLITE_ROW; /* TRUE */
257                 break;
258         case SQLITE_DONE:
259                 ret = CALENDAR_ERROR_NONE;
260                 break;
261         case SQLITE_BUSY:
262                 /* LCOV_EXCL_START */
263                 ERR("SQLITE_BUSY");
264                 ret = CALENDAR_ERROR_DB_FAILED;
265                 break;
266                 /* LCOV_EXCL_STOP */
267         case SQLITE_LOCKED:
268                 /* LCOV_EXCL_START */
269                 ERR("SQLITE_LOCKED");
270                 ret = CALENDAR_ERROR_DB_FAILED;
271                 break;
272                 /* LCOV_EXCL_STOP */
273         default:
274                 /* LCOV_EXCL_START */
275                 ERR("ERROR(%d)", ret);
276                 ret = CALENDAR_ERROR_DB_FAILED;
277                 break;
278                 /* LCOV_EXCL_STOP */
279         }
280         return ret;
281 }
282
283 int cal_db_util_query_exec(char *query)
284 {
285         int ret = 0;
286         RETV_IF(NULL == cal_db, CALENDAR_ERROR_INVALID_PARAMETER);
287         RETV_IF(NULL == query, CALENDAR_ERROR_INVALID_PARAMETER);
288
289         sqlite3_stmt *stmt = NULL;
290         ret = cal_db_util_query_prepare(query, &stmt);
291         if (CALENDAR_ERROR_NONE != ret) {
292                 /* LCOV_EXCL_START */
293                 ERR("cal_db_util_query_prepare() Fail(%d)", ret);
294                 return ret;
295                 /* LCOV_EXCL_STOP */
296         }
297
298         ret = cal_db_util_stmt_step(stmt);
299         if (CALENDAR_ERROR_NONE != ret) {
300                 /* LCOV_EXCL_START */
301                 ERR("cal_db_util_stmt_step() Fail(%d)", ret);
302                 SECURE("query[%s]", query);
303                 sqlite3_finalize(stmt);
304                 return ret;
305                 /* LCOV_EXCL_STOP */
306         }
307         sqlite3_finalize(stmt);
308
309         return CALENDAR_ERROR_NONE;
310 }
311
312 int cal_db_util_query_get_first_int_result(const char *query, GSList *bind_text, int *result)
313 {
314         int ret = 0;
315         RETV_IF(NULL == cal_db, CALENDAR_ERROR_INVALID_PARAMETER);
316         RETV_IF(NULL == query, CALENDAR_ERROR_INVALID_PARAMETER);
317         RETV_IF(NULL == result, CALENDAR_ERROR_INVALID_PARAMETER);
318
319         struct timeval from, now, diff;
320         gettimeofday(&from, NULL);
321
322         bool retry = false;
323         sqlite3_stmt *stmt = NULL;
324         do {
325                 ret = sqlite3_prepare_v2(cal_db, query, strlen(query), &stmt, NULL);
326                 if (SQLITE_BUSY == ret || SQLITE_LOCKED == ret) {
327                         /* LCOV_EXCL_START */
328                         ERR("sqlite3_prepare_v2() Fail:[%s]", sqlite3_errmsg(cal_db));
329                         SECURE("[%s]", query);
330                         gettimeofday(&now, NULL);
331                         timersub(&now, &from, &diff);
332                         retry = (diff.tv_sec < CAL_QUERY_RETRY_TIME) ? true : false;
333                         if (retry)
334                                 usleep(CAL_QUERY_RETRY_INTERVAL); /* 50ms */
335                         /* LCOV_EXCL_STOP */
336                 } else {
337                         retry = false;
338                 }
339         } while (retry);
340
341         if (SQLITE_OK != ret) {
342                 /* LCOV_EXCL_START */
343                 ERR("sqlite3_prepare_v2(%s) Fail(%s).", query, sqlite3_errmsg(cal_db));
344                 SECURE("query[%s]", query);
345                 return CALENDAR_ERROR_DB_FAILED;
346                 /* LCOV_EXCL_STOP */
347         }
348
349         if (bind_text) {
350                 int i;
351                 for (i = 0; g_slist_nth(bind_text, i); i++) {
352                         char *text = (char *)g_slist_nth_data(bind_text, i);
353                         if (text)
354                                 cal_db_util_stmt_bind_text(stmt, i +1, text);
355                 }
356         }
357
358         retry = false;
359         gettimeofday(&from, NULL);
360         do {
361                 ret = sqlite3_step(stmt);
362                 if (SQLITE_BUSY == ret || SQLITE_LOCKED == ret) {
363                         /* LCOV_EXCL_START */
364                         ERR("sqlite3_step fail(%d, %s)", ret, sqlite3_errmsg(cal_db));
365                         gettimeofday(&now, NULL);
366                         timersub(&now, &from, &diff);
367                         retry = (diff.tv_sec < CAL_QUERY_RETRY_TIME) ? true :  false;
368                         if (retry)
369                                 usleep(CAL_QUERY_RETRY_INTERVAL); /* 50ms */
370                         /* LCOV_EXCL_STOP */
371                 } else {
372                         retry = false;
373                 }
374         } while (retry);
375
376         if (SQLITE_ROW == ret) {
377                 *result = sqlite3_column_int(stmt, 0);
378                 sqlite3_finalize(stmt);
379                 return CALENDAR_ERROR_NONE;
380         }
381         sqlite3_finalize(stmt);
382
383         switch (ret) {
384         case SQLITE_DONE:
385                 DBG("SQLITE_DONE");
386                 ret = CALENDAR_ERROR_NO_DATA;
387                 break;
388         case SQLITE_BUSY:
389                 /* LCOV_EXCL_START */
390                 ERR("SQLITE_BUSY");
391                 ret = CALENDAR_ERROR_DB_FAILED;
392                 break;
393                 /* LCOV_EXCL_STOP */
394         case SQLITE_LOCKED:
395                 /* LCOV_EXCL_START */
396                 ERR("SQLITE_LOCKED");
397                 ret = CALENDAR_ERROR_DB_FAILED;
398                 break;
399                 /* LCOV_EXCL_STOP */
400         default:
401                 /* LCOV_EXCL_START */
402                 ERR("ERROR(%d)", ret);
403                 ret = CALENDAR_ERROR_DB_FAILED;
404                 break;
405                 /* LCOV_EXCL_STOP */
406         }
407         return CALENDAR_ERROR_NONE;
408 }
409
410 int cal_db_util_begin_trans(void)
411 {
412         if (transaction_cnt <= 0) {
413                 int ret, progress;
414
415                 progress = 100000;
416                 ret = cal_db_util_query_exec("BEGIN IMMEDIATE TRANSACTION");
417                 /*  !! check error code */
418                 while (CALENDAR_ERROR_NONE != ret && progress < CAL_COMMIT_TRY_MAX) {
419                         usleep(progress);
420                         ret = cal_db_util_query_exec("BEGIN IMMEDIATE TRANSACTION");
421                         progress *= 2;
422                 }
423                 RETVM_IF(CALENDAR_ERROR_NONE != ret, ret, "cal_query_exec() Fail(%d)", ret);
424
425                 transaction_cnt = 0;
426                 const char *query = "SELECT ver FROM "CAL_TABLE_VERSION;
427                 ret = cal_db_util_query_get_first_int_result(query, NULL, &transaction_ver);
428                 if (CALENDAR_ERROR_NONE != ret) {
429                         /* LCOV_EXCL_START */
430                         ERR("cal_db_util_query_get_first_int_result() Fail");
431                         return ret;
432                         /* LCOV_EXCL_STOP */
433                 }
434                 version_up = false;
435         }
436         transaction_cnt++;
437         DBG("transaction_cnt : %d", transaction_cnt);
438
439         return CALENDAR_ERROR_NONE;
440 }
441
442 int cal_db_util_end_trans(bool is_success)
443 {
444         int ret;
445         int progress = 0;
446         char query[CAL_DB_SQL_MIN_LEN];
447
448         transaction_cnt--;
449
450         if (0 != transaction_cnt) {
451                 DBG("transaction_cnt : %d", transaction_cnt);
452                 return CALENDAR_ERROR_NONE;
453         }
454
455         if (false == is_success) {
456                 _cal_db_util_cancel_changes();
457                 ret = cal_db_util_query_exec("ROLLBACK TRANSACTION");
458                 return CALENDAR_ERROR_NONE;
459         }
460
461         if (version_up) {
462                 transaction_ver++;
463                 snprintf(query, sizeof(query), "UPDATE %s SET ver = %d",
464                                 CAL_TABLE_VERSION, transaction_ver);
465                 ret = cal_db_util_query_exec(query);
466                 WARN_IF(CALENDAR_ERROR_NONE != ret, "cal_query_exec(version up) Fail(%d).", ret);
467         }
468
469         INFO("start commit");
470         progress = 100000;
471         ret = cal_db_util_query_exec("COMMIT TRANSACTION");
472         /* !! check error code */
473         while (CALENDAR_ERROR_NONE != ret && progress < CAL_COMMIT_TRY_MAX) {
474                 usleep(progress);
475                 ret = cal_db_util_query_exec("COMMIT TRANSACTION");
476                 progress *= 2;
477         }
478         INFO("%s", (CALENDAR_ERROR_NONE == ret) ? "commit" : "rollback");
479
480         if (CALENDAR_ERROR_NONE != ret) {
481                 int tmp_ret;
482                 /* LCOV_EXCL_START */
483                 ERR("cal_query_exec() Fail(%d)", ret);
484                 _cal_db_util_cancel_changes();
485                 tmp_ret = cal_db_util_query_exec("ROLLBACK TRANSACTION");
486                 WARN_IF(CALENDAR_ERROR_NONE != tmp_ret, "cal_query_exec(ROLLBACK) Fail(%d).", tmp_ret);
487                 return CALENDAR_ERROR_DB_FAILED;
488                 /* LCOV_EXCL_STOP */
489         }
490         if (event_change) _cal_db_util_notify_event_change();
491         if (todo_change) _cal_db_util_notify_todo_change();
492         if (calendar_change) _cal_db_util_notify_calendar_change();
493
494         DBG("transaction_ver = %d", transaction_ver);
495         return CALENDAR_ERROR_NONE;
496 }
497
498 int cal_db_util_get_next_ver(void)
499 {
500         int ret = 0;
501         if (0 < transaction_cnt) {
502                 version_up = true;
503                 return transaction_ver + 1;
504         }
505
506         int count = 0;
507         const char *query = "SELECT ver FROM "CAL_TABLE_VERSION;
508         ret = cal_db_util_query_get_first_int_result(query, NULL, &count);
509         if (CALENDAR_ERROR_NONE != ret)
510                 WARN("cal_db_util_query_get_first_int_result() Fail(%d)", ret);
511
512         return (1 + count);
513 }
514
515 int cal_db_util_get_transaction_ver(void)
516 {
517         return transaction_ver;
518 }
519
520 int cal_db_util_stmt_bind_text(sqlite3_stmt *stmt, int pos, const char *str)
521 {
522         return sqlite3_bind_text(stmt, pos, str, str ? strlen(str) : 0, SQLITE_STATIC);
523 }