1c3b7b56fc07526c84050a6d23ec35620b7798bb
[framework/pim/calendar-service.git] / native / cal_db_util.c
1 /*
2  * Calendar Service
3  *
4  * Copyright (c) 2012 - 2013 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
24 #include "cal_internal.h"
25 #include "cal_typedef.h"
26 #include "cal_db.h"
27
28 #include "cal_db_util.h"
29
30 static TLS sqlite3 *calendar_db_handle;
31 static TLS int transaction_cnt = 0;
32 static TLS int transaction_ver = 0;
33 static TLS bool version_up = false;
34
35 static TLS bool event_change=false;
36 static TLS bool todo_change=false;
37 static TLS bool calendar_change=false;
38
39 static inline void __cal_db_util_notify_event_change(void)
40 {
41         int fd = open(CAL_NOTI_EVENT_CHANGED, O_TRUNC | O_RDWR);
42         if (0 <= fd) {
43                 close(fd);
44                 event_change = false;
45         }
46 }
47
48 static inline void __cal_db_util_notify_todo_change(void)
49 {
50         int fd = open(CAL_NOTI_TODO_CHANGED, O_TRUNC | O_RDWR);
51         if (0 <= fd) {
52                 close(fd);
53                 todo_change = false;
54         }
55 }
56
57 static inline void __cal_db_util_notify_calendar_change(void)
58 {
59         int fd = open(CAL_NOTI_CALENDAR_CHANGED, O_TRUNC | O_RDWR);
60         if (0 <= fd) {
61                 close(fd);
62                 calendar_change = false;
63         }
64 }
65
66 static inline void __cal_db_util_cancel_changes(void)
67 {
68     event_change = false;
69     calendar_change = false;
70     todo_change = false;
71 }
72
73 int _cal_db_util_notify(cal_noti_type_e type)
74 {
75         if (0 < transaction_cnt) {
76                 switch (type) {
77                 case CAL_NOTI_TYPE_EVENT:
78                         event_change = true;
79                         break;
80                 case CAL_NOTI_TYPE_TODO:
81                         todo_change = true;
82                         break;
83                 case CAL_NOTI_TYPE_CALENDAR:
84                         calendar_change = true;
85                         break;
86                 default:
87                         ERR("The type(%d) is not supported", type);
88                         return CALENDAR_ERROR_INVALID_PARAMETER;
89                 }
90                 return CALENDAR_ERROR_NONE;
91         }
92
93         switch(type) {
94         case CAL_NOTI_TYPE_EVENT:
95                 __cal_db_util_notify_event_change();
96                 break;
97         case CAL_NOTI_TYPE_TODO:
98                 __cal_db_util_notify_todo_change();
99                 break;
100         case CAL_NOTI_TYPE_CALENDAR:
101                 __cal_db_util_notify_calendar_change();
102                 break;
103         default:
104                 ERR("The type(%d) is not supported", type);
105                 return CALENDAR_ERROR_INVALID_PARAMETER;
106         }
107
108         return CALENDAR_ERROR_NONE;
109 }
110
111 int _cal_db_util_open(void)
112 {
113         int ret = 0;
114
115         if (!calendar_db_handle) {
116                 ret = db_util_open(CAL_DB_PATH, &calendar_db_handle, 0);
117                 retvm_if(SQLITE_OK != ret, CALENDAR_ERROR_DB_FAILED,
118                                 "db_util_open() Failed(%d).", ret);
119         }
120         return CALENDAR_ERROR_NONE;
121 }
122
123 int _cal_db_util_close(void)
124 {
125         int ret = 0;
126
127         if (calendar_db_handle) {
128                 ret = db_util_close(calendar_db_handle);
129                 warn_if(SQLITE_OK != ret, "db_util_close() Failed(%d)", ret);
130                 calendar_db_handle = NULL;
131                 CAL_DBG("The database disconnected really.");
132         }
133
134         return CALENDAR_ERROR_NONE;
135 }
136
137 int _cal_db_util_last_insert_id(void)
138 {
139         return sqlite3_last_insert_rowid(calendar_db_handle);
140 }
141
142 int _cal_db_util_query_get_first_int_result(const char *query, GSList *bind_text, int *result)
143 {
144         int ret;
145         int index;
146         char *text = NULL;
147         sqlite3_stmt *stmt = NULL;
148         retvm_if(NULL == calendar_db_handle, CALENDAR_ERROR_DB_FAILED, "Database is not opended");
149
150         ret = sqlite3_prepare_v2(calendar_db_handle, query, strlen(query), &stmt, NULL);
151         retvm_if(SQLITE_OK != ret, CALENDAR_ERROR_DB_FAILED,
152                         "sqlite3_prepare_v2(%s) failed(%s).", query, sqlite3_errmsg(calendar_db_handle));
153
154         if (bind_text)
155         {
156                 for (index = 0; g_slist_nth(bind_text, index); index++)
157                 {
158                         text = (char *)g_slist_nth_data(bind_text, index);
159                         if (text)
160                         {
161                                 _cal_db_util_stmt_bind_text(stmt, index + 1, text);
162                         }
163                 }
164         }
165
166         ret = sqlite3_step(stmt);
167         if (SQLITE_ROW != ret)
168         {
169                 ERR("sqlite3_step() failed(%d, %s).", ret, sqlite3_errmsg(calendar_db_handle));
170                 sqlite3_finalize(stmt);
171                 if (SQLITE_DONE == ret) return CALENDAR_ERROR_DB_FAILED;
172                 return CALENDAR_ERROR_DB_FAILED;
173         }
174
175         if (result) *result = sqlite3_column_int(stmt, 0);
176         sqlite3_finalize(stmt);
177
178         return CALENDAR_ERROR_NONE;
179 }
180
181 cal_db_util_error_e _cal_db_util_query_exec(char *query)
182 {
183         int ret;
184         char *err_msg = NULL;
185
186         retvm_if(NULL == calendar_db_handle, CALENDAR_ERROR_DB_FAILED, "Database is not opended");
187         //CALS_DBG("query : %s", query);
188
189         ret = sqlite3_exec(calendar_db_handle, query, NULL, NULL, &err_msg);
190         if (SQLITE_OK != ret) {
191                 ERR("sqlite3_exec(%s) failed(%d, %s).", query, ret, err_msg);
192                 sqlite3_free(err_msg);
193                 switch (ret) {
194                 case SQLITE_BUSY:
195                 case SQLITE_LOCKED:
196                         return CAL_DB_ERROR_LOCKED;
197                 case SQLITE_IOERR:
198                         return CAL_DB_ERROR_IOERR;
199                 case SQLITE_FULL:
200                         return CAL_DB_ERROR_NO_SPACE;
201                 default:
202                         return CAL_DB_ERROR_FAIL;
203                 }
204         }
205         if (err_msg)
206         {
207             sqlite3_free(err_msg);
208         }
209
210         return CAL_DB_OK;
211 }
212
213 sqlite3_stmt* _cal_db_util_query_prepare(char *query)
214 {
215         int ret = -1;
216         sqlite3_stmt *stmt = NULL;
217
218         retvm_if(NULL == query, NULL, "Invalid query");
219         retvm_if(NULL == calendar_db_handle, NULL, "Database is not opended");
220         //CALS_DBG("prepare query : %s", query);
221
222         ret = sqlite3_prepare_v2(calendar_db_handle, query, strlen(query), &stmt, NULL);
223         retvm_if(SQLITE_OK != ret, NULL,
224                         "sqlite3_prepare_v2(%s) Failed(%s).", query, sqlite3_errmsg(calendar_db_handle));
225
226         return stmt;
227 }
228
229 cal_db_util_error_e _cal_db_util_stmt_step(sqlite3_stmt *stmt)
230 {
231         int ret;
232         ret = sqlite3_step(stmt);
233     switch (ret) {
234     case SQLITE_BUSY:
235     case SQLITE_LOCKED:
236         ret = CAL_DB_ERROR_LOCKED;
237         break;
238     case SQLITE_IOERR:
239         ret = CAL_DB_ERROR_IOERR;
240         break;
241     case SQLITE_FULL:
242         ret = CAL_DB_ERROR_NO_SPACE;
243         break;
244     case SQLITE_CONSTRAINT:
245         ret = CAL_DB_ERROR_ALREADY_EXIST;
246         break;
247     case SQLITE_ROW:
248         ret = CAL_DB_ROW;
249         break;
250     case SQLITE_DONE:
251         ret = CAL_DB_DONE;
252         break;
253     default:
254         ERR("sqlite3_step() Failed(%d)", ret);
255         ret = CAL_DB_ERROR_FAIL;
256         break;
257     }
258     return ret;
259 }
260
261 #define CAL_COMMIT_TRY_MAX 500000
262 int _cal_db_util_begin_trans(void)
263 {
264         if(transaction_cnt <= 0)
265         {
266                 int ret, progress;
267
268                 progress = 100000;
269                 ret = _cal_db_util_query_exec("BEGIN IMMEDIATE TRANSACTION");
270                 // !! check error code
271                 while(CAL_DB_OK != ret && progress < CAL_COMMIT_TRY_MAX) {
272                         usleep(progress);
273                         ret = _cal_db_util_query_exec("BEGIN IMMEDIATE TRANSACTION");
274                         progress *= 2;
275                 }
276                 retvm_if(CAL_DB_OK != ret, ret, "cal_query_exec() Failed(%d)", ret);
277
278                 transaction_cnt = 0;
279                 const char *query = "SELECT ver FROM "CAL_TABLE_VERSION;
280                 ret = _cal_db_util_query_get_first_int_result(query, NULL, &transaction_ver);
281                 if (CALENDAR_ERROR_NONE != ret)
282                 {
283                         ERR("_cal_db_util_query_get_first_int_result() failed");
284                         return ret;
285                 }
286                 version_up = false;
287         }
288         transaction_cnt++;
289         CAL_DBG("transaction_cnt : %d", transaction_cnt);
290
291         return CALENDAR_ERROR_NONE;
292 }
293
294 int _cal_db_util_end_trans(bool is_success)
295 {
296         int ret;
297         int progress = 0;
298         char query[CAL_DB_SQL_MIN_LEN];
299
300         transaction_cnt--;
301
302         if (0 != transaction_cnt) {
303                 CAL_DBG("transaction_cnt : %d", transaction_cnt);
304                 return CALENDAR_ERROR_NONE;
305         }
306
307         if (false == is_success) {
308                 __cal_db_util_cancel_changes();
309                 ret = _cal_db_util_query_exec("ROLLBACK TRANSACTION");
310                 return CALENDAR_ERROR_NONE;
311         }
312
313         if (version_up) {
314                 transaction_ver++;
315                 snprintf(query, sizeof(query), "UPDATE %s SET ver = %d",
316                                 CAL_TABLE_VERSION, transaction_ver);
317                 ret = _cal_db_util_query_exec(query);
318                 warn_if(CAL_DB_OK != ret, "cal_query_exec(version up) Failed(%d).", ret);
319         }
320
321         progress = 100000;
322         ret = _cal_db_util_query_exec("COMMIT TRANSACTION");
323         // !! check error code
324         while (CAL_DB_OK != ret && progress < CAL_COMMIT_TRY_MAX) {
325                 usleep(progress);
326                 ret = _cal_db_util_query_exec("COMMIT TRANSACTION");
327                 progress *= 2;
328         }
329         if (CAL_DB_OK != ret) {
330                 int tmp_ret;
331                 ERR("cal_query_exec() Failed(%d)", ret);
332                 __cal_db_util_cancel_changes();
333                 tmp_ret = _cal_db_util_query_exec("ROLLBACK TRANSACTION");
334                 warn_if(CAL_DB_OK != tmp_ret, "cal_query_exec(ROLLBACK) Failed(%d).", tmp_ret);
335                 return CALENDAR_ERROR_DB_FAILED;
336         }
337         if (event_change) __cal_db_util_notify_event_change();
338         if (todo_change) __cal_db_util_notify_todo_change();
339         if (calendar_change) __cal_db_util_notify_calendar_change();
340
341         CAL_DBG("transaction_ver = %d",transaction_ver);
342         return CALENDAR_ERROR_NONE;
343 }
344
345 int _cal_db_util_get_next_ver(void)
346 {
347         int count = 0;
348         int ret;
349         const char *query;
350
351         if (0 < transaction_cnt) {
352                 version_up = true;
353                 return transaction_ver + 1;
354         }
355
356         query = "SELECT ver FROM "CAL_TABLE_VERSION;
357
358         ret = _cal_db_util_query_get_first_int_result(query, NULL, &count);
359         if (CALENDAR_ERROR_NONE != ret)
360         {
361                 ERR("_cal_db_util_query_get_first_int_result() failed");
362                 return ret;
363         }
364         return (1 + count);
365 }
366
367 int _cal_db_util_get_transaction_ver(void)
368 {
369     return transaction_ver;
370 }