QOF 0.8.4
|
00001 /**************************************************************** 00002 * qof-sqlite.c 00003 * 00004 * Sun Jan 15 12:52:46 2006 00005 * Copyright 2006-2008 Neil Williams 00006 * linux@codehelp.co.uk 00007 ****************************************************************/ 00008 /* 00009 * This program is free software; you can redistribute it and/or modify 00010 * it under the terms of the GNU General Public License as published by 00011 * the Free Software Foundation; either version 2 of the License, or 00012 * (at your option) any later version. 00013 * 00014 * This program is distributed in the hope that it will be useful, 00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00017 * GNU General Public License for more details. 00018 * 00019 * You should have received a copy of the GNU General Public License 00020 * along with this program; if not, write to the Free Software 00021 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 00022 */ 00023 00024 #include "config.h" 00025 #include <errno.h> 00026 #include <stdlib.h> 00027 #include <time.h> 00028 #include <glib/gstdio.h> 00029 #include <sqlite.h> 00030 #include <glib.h> 00031 #include <libintl.h> 00032 #include "qof.h" 00033 #include "qofsql-p.h" 00034 #include "qof-sqlite.h" 00035 #include "kvputil-p.h" 00036 00037 #define _(String) dgettext (GETTEXT_PACKAGE, String) 00038 #define ACCESS_METHOD "sqlite" 00039 00046 #define PRIORITY_HIGH 9 00047 00048 #define PRIORITY_STANDARD 5 00049 00050 #define PRIORITY_LOW 0 00051 00052 #define QSQL_ERROR -1 00053 00054 #undef QSQL_KVP_TABLE 00055 #define QSQL_KVP_TABLE "sqlite_kvp" 00056 00057 #define END_DB_VERSION " dbversion int );" 00058 00059 static QofLogModule log_module = QOF_MOD_SQLITE; 00060 static gboolean loading = FALSE; 00061 00068 typedef struct 00069 { 00070 QofBackend be; 00071 sqlite *sqliteh; 00072 QsqlStatementType stm_type; 00073 gint dbversion; 00074 gint create_handler; 00075 gint delete_handler; 00076 const gchar *fullpath; 00077 gchar *err; 00078 gboolean error; 00079 /* full hashtable of kvp records */ 00080 GHashTable *kvp_table; 00081 /* hashtable relating the GUID to the kvp_id */ 00082 GHashTable *kvp_id; 00083 /* highest kvp_id in the table */ 00084 gulong index; 00085 QofBook *book; 00086 QofErrorId err_delete, err_insert, err_update, err_create; 00087 } QSQLiteBackend; 00088 00095 struct QsqlBuilder 00096 { 00098 QSQLiteBackend *qsql_be; 00100 QofEntity *ent; 00102 QofIdType e_type; 00104 gchar *sql_str; 00106 GList *dirty_list; 00108 gboolean exists; 00110 gboolean has_slots; 00112 const QofParam *dirty; 00113 }; 00114 00116 static KvpValue * 00117 string_to_kvp_value (const gchar * content, KvpValueType type) 00118 { 00119 gchar *tail; 00120 gint64 cm_i64; 00121 gdouble cm_double; 00122 QofNumeric cm_numeric; 00123 GUID *cm_guid; 00124 00125 switch (type) 00126 { 00127 case KVP_TYPE_GINT64: 00128 { 00129 errno = 0; 00130 cm_i64 = strtoll (content, &tail, 0); 00131 if (errno == 0) 00132 { 00133 return kvp_value_new_gint64 (cm_i64); 00134 } 00135 break; 00136 } 00137 case KVP_TYPE_DOUBLE: 00138 { 00139 errno = 0; 00140 cm_double = strtod (content, &tail); 00141 if (errno == 0) 00142 return kvp_value_new_double (cm_double); 00143 break; 00144 } 00145 case KVP_TYPE_NUMERIC: 00146 { 00147 qof_numeric_from_string (content, &cm_numeric); 00148 return kvp_value_new_numeric (cm_numeric); 00149 break; 00150 } 00151 case KVP_TYPE_STRING: 00152 { 00153 return kvp_value_new_string (content); 00154 break; 00155 } 00156 case KVP_TYPE_GUID: 00157 { 00158 cm_guid = g_new0 (GUID, 1); 00159 if (TRUE == string_to_guid (content, cm_guid)) 00160 return kvp_value_new_guid (cm_guid); 00161 break; 00162 } 00163 case KVP_TYPE_TIME: 00164 { 00165 QofDate *qd; 00166 QofTime *qt; 00167 KvpValue *retval; 00168 00169 qd = qof_date_parse (content, QOF_DATE_FORMAT_UTC); 00170 if (qd) 00171 { 00172 qt = qof_date_to_qtime (qd); 00173 retval = kvp_value_new_time (qt); 00174 qof_date_free (qd); 00175 qof_time_free (qt); 00176 return retval; 00177 } 00178 else 00179 PERR (" failed to parse date"); 00180 } 00181 case KVP_TYPE_BOOLEAN: 00182 { 00183 gboolean val; 00184 val = qof_util_bool_to_int (content); 00185 return kvp_value_new_boolean (val); 00186 } 00187 default: 00188 break; 00189 } 00190 return NULL; 00191 } 00192 00194 static G_GNUC_UNUSED void 00195 kvpvalue_to_sql (const gchar * key, KvpValue * val, gpointer builder) 00196 { 00197 QSQLiteBackend *qsql_be; 00198 struct QsqlBuilder *qb; 00199 KvpValueType n; 00200 00201 ENTER (" "); 00202 qb = (struct QsqlBuilder *) builder; 00203 qsql_be = qb->qsql_be; 00204 g_return_if_fail (key && val && qsql_be); 00205 n = kvp_value_get_type (val); 00206 switch (n) 00207 { 00208 case KVP_TYPE_GINT64: 00209 case KVP_TYPE_DOUBLE: 00210 case KVP_TYPE_NUMERIC: 00211 case KVP_TYPE_STRING: 00212 case KVP_TYPE_GUID: 00213 case KVP_TYPE_TIME: 00214 case KVP_TYPE_BOOLEAN: 00215 { 00216 /* ("kvp_id int primary key not null", "guid char(32)", "path mediumtext", 00217 "type mediumtext", "value text", */ 00218 00219 qb->sql_str = 00220 g_strdup_printf (" kvp key=%s val=%s type=%s", key, 00221 kvp_value_to_bare_string (val), 00222 kvp_value_type_to_qof_id (n)); 00223 DEBUG (" %s", qb->sql_str); 00224 qb->has_slots = TRUE; 00225 break; 00226 } 00227 case KVP_TYPE_FRAME: 00228 { 00229 kvp_frame_for_each_slot (kvp_value_get_frame (val), 00230 kvpvalue_to_sql, qb); 00231 break; 00232 } 00233 default: 00234 { 00235 PERR (" unsupported value = %d", kvp_value_get_type (val)); 00236 break; 00237 } 00238 } 00239 LEAVE (" %s", qb->sql_str); 00240 } 00241 00246 static void 00247 delete_event (QofEntity * ent, QofEventId event_type, 00248 gpointer handler_data, gpointer event_data) 00249 { 00250 QofBackend *be; 00251 QSQLiteBackend *qsql_be; 00252 gchar *gstr, *sql_str; 00253 00254 qsql_be = (QSQLiteBackend *) handler_data; 00255 be = (QofBackend *) qsql_be; 00256 if (!ent) 00257 return; 00258 if (0 == safe_strcmp (ent->e_type, QOF_ID_BOOK)) 00259 return; 00260 /* do not try to delete if only a QofObject has been loaded. */ 00261 if (!qof_class_is_registered (ent->e_type)) 00262 return; 00263 switch (event_type) 00264 { 00265 case QOF_EVENT_DESTROY: 00266 { 00267 ENTER (" %s do_free=%d", ent->e_type, 00268 ((QofInstance *) ent)->do_free); 00269 gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' '); 00270 guid_to_string_buff (qof_entity_get_guid (ent), gstr); 00271 sql_str = qof_sql_entity_delete (ent); 00272 DEBUG (" sql_str=%s", sql_str); 00273 if (sqlite_exec (qsql_be->sqliteh, sql_str, 00274 NULL, qsql_be, &qsql_be->err) != SQLITE_OK) 00275 { 00276 qof_error_set_be (be, qsql_be->err_delete); 00277 qsql_be->error = TRUE; 00278 LEAVE (" error on delete:%s", qsql_be->err); 00279 break; 00280 } 00281 LEAVE (" %d", event_type); 00282 qsql_be->error = FALSE; 00283 g_free (gstr); 00284 break; 00285 } 00286 default: 00287 break; 00288 } 00289 } 00290 00292 static void 00293 create_event (QofEntity * ent, QofEventId event_type, 00294 gpointer handler_data, gpointer event_data) 00295 { 00296 QofBackend *be; 00297 struct QsqlBuilder qb; 00298 QSQLiteBackend *qsql_be; 00299 gchar *gstr; 00300 KvpFrame *slots; 00301 00302 qsql_be = (QSQLiteBackend *) handler_data; 00303 be = (QofBackend *) qsql_be; 00304 if (!ent) 00305 return; 00306 if (0 == safe_strcmp (ent->e_type, QOF_ID_BOOK)) 00307 return; 00308 if (!qof_class_is_registered (ent->e_type)) 00309 return; 00310 switch (event_type) 00311 { 00312 case QOF_EVENT_CREATE: 00313 { 00314 ENTER (" insert:%s", ent->e_type); 00315 gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' '); 00316 guid_to_string_buff (qof_instance_get_guid ((QofInstance *) 00317 ent), gstr); 00318 DEBUG (" guid=%s", gstr); 00319 qb.ent = ent; 00320 qb.sql_str = qof_sql_entity_insert (ent); 00322 DEBUG (" sql_str=%s", qb.sql_str); 00323 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str, 00324 NULL, &qb, &qsql_be->err) != SQLITE_OK) 00325 { 00326 qof_error_set_be (be, qsql_be->err_insert); 00327 qsql_be->error = TRUE; 00328 PERR (" error on create_event:%s", qsql_be->err); 00329 } 00330 else 00331 { 00332 ((QofInstance *) ent)->dirty = FALSE; 00333 qsql_be->error = FALSE; 00334 g_free (qb.sql_str); 00335 g_free (gstr); 00336 LEAVE (" "); 00337 break; 00338 } 00339 /* insert sqlite_kvp data */ 00340 slots = qof_instance_get_slots ((QofInstance *) ent); 00341 if (slots) 00342 { 00343 /* id, guid, path, type, value */ 00344 qb.sql_str = qof_sql_entity_insert (ent); 00345 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str, 00346 NULL, &qb, &qsql_be->err) != SQLITE_OK) 00347 { 00348 qof_error_set_be (be, qsql_be->err_insert); 00349 qsql_be->error = TRUE; 00350 PERR (" error on KVP create_event:%s", qsql_be->err); 00351 } 00352 else 00353 { 00354 ((QofInstance *) ent)->dirty = FALSE; 00355 qsql_be->error = FALSE; 00356 g_free (qb.sql_str); 00357 g_free (gstr); 00358 LEAVE (" "); 00359 break; 00360 } 00361 } 00362 g_free (qb.sql_str); 00363 g_free (gstr); 00364 LEAVE (" "); 00365 break; 00366 } 00367 default: 00368 break; 00369 } 00370 } 00371 00372 static void 00373 qsql_modify (QofBackend * be, QofInstance * inst) 00374 { 00375 struct QsqlBuilder qb; 00376 QSQLiteBackend *qsql_be; 00377 00378 qsql_be = (QSQLiteBackend *) be; 00379 qb.qsql_be = qsql_be; 00380 if (!inst) 00381 return; 00382 if (!inst->param) 00383 return; 00384 if (loading) 00385 return; 00386 if (!inst->param->param_setfcn) 00387 return; 00388 ENTER (" modified %s param:%s", ((QofEntity *) inst)->e_type, 00389 inst->param->param_name); 00390 qb.sql_str = qof_sql_entity_update ((QofEntity*)inst); 00391 if (!qb.sql_str) 00392 { 00393 LEAVE (" null string"); 00394 return; 00395 } 00396 DEBUG (" sql_str=%s", qb.sql_str); 00397 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str, 00398 NULL, &qb, &qsql_be->err) != SQLITE_OK) 00399 { 00400 qof_error_set_be (be, qsql_be->err_update); 00401 qsql_be->error = TRUE; 00402 PERR (" error on modify:%s", qsql_be->err); 00403 } 00404 else 00405 { 00406 inst->dirty = FALSE; 00407 g_free (qb.sql_str); 00408 qsql_be->error = FALSE; 00409 LEAVE (" "); 00410 return; 00411 } 00412 LEAVE (" "); 00413 } 00414 00416 static gint 00417 record_foreach (gpointer builder, gint col_num, gchar ** strings, 00418 gchar ** columnNames) 00419 { 00420 QSQLiteBackend *qsql_be; 00421 struct QsqlBuilder *qb; 00422 const QofParam *param; 00423 QofInstance *inst; 00424 QofEntity *ent; 00425 gint i; 00426 00427 g_return_val_if_fail (builder, QSQL_ERROR); 00428 qb = (struct QsqlBuilder *) builder; 00429 qsql_be = qb->qsql_be; 00430 qof_event_suspend (); 00431 inst = (QofInstance *) qof_object_new_instance (qb->e_type, qsql_be->book); 00432 ent = &inst->entity; 00433 for (i = 0; i < col_num; i++) 00434 { 00435 /* get param and set as string */ 00436 param = qof_class_get_parameter (qb->e_type, columnNames[i]); 00437 if (!param) 00438 continue; 00439 /* set the inst->param entry */ 00440 inst->param = param; 00441 if (0 == safe_strcmp (columnNames[i], QOF_TYPE_GUID)) 00442 { 00443 GUID *guid; 00444 guid = guid_malloc (); 00445 if (!string_to_guid (strings[i], guid)) 00446 { 00447 DEBUG (" set guid failed:%s", strings[i]); 00448 return QSQL_ERROR; 00449 } 00450 qof_entity_set_guid (ent, guid); 00451 } 00452 if (strings[i]) 00453 qof_util_param_set_string (ent, param, strings[i]); 00454 } 00455 qof_event_resume (); 00456 return SQLITE_OK; 00457 } 00458 00459 static void 00460 update_dirty (gpointer value, gpointer builder) 00461 { 00462 QofInstance *inst; 00463 QofEntity *ent; 00464 struct QsqlBuilder *qb; 00465 QSQLiteBackend *qsql_be; 00466 QofBackend *be; 00467 gchar *gstr; 00468 00469 qb = (struct QsqlBuilder *) builder; 00470 qsql_be = qb->qsql_be; 00471 be = (QofBackend *) qsql_be; 00472 ent = (QofEntity *) value; 00473 inst = (QofInstance *) ent; 00474 if (!inst->dirty) 00475 return; 00476 ENTER (" "); 00477 gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' '); 00478 guid_to_string_buff (qof_entity_get_guid (ent), gstr); 00479 /* qof_class_param_foreach */ 00480 qb->sql_str = qof_sql_entity_update (ent); 00481 if (!qb->sql_str) 00482 { 00483 LEAVE (" null string"); 00484 return; 00485 } 00486 DEBUG (" update=%s", qb->sql_str); 00487 if (sqlite_exec (qsql_be->sqliteh, qb->sql_str, 00488 NULL, qb, &qsql_be->err) != SQLITE_OK) 00489 { 00490 qof_error_set_be (be, qsql_be->err_update); 00491 qsql_be->error = TRUE; 00492 PERR (" error on update_dirty:%s", qsql_be->err); 00493 } 00494 else 00495 { 00496 qof_error_get_message_be (be); 00497 qsql_be->error = FALSE; 00498 inst->dirty = FALSE; 00499 } 00500 LEAVE (" "); 00501 g_free (gstr); 00502 return; 00503 } 00504 00505 static gint 00506 create_dirty_list (gpointer builder, gint col_num, gchar ** strings, 00507 gchar ** columnNames) 00508 { 00509 struct QsqlBuilder *qb; 00510 QofInstance *inst; 00511 const QofParam *param; 00512 gchar * G_GNUC_UNUSED value, *columnName, * G_GNUC_UNUSED tmp; 00513 00514 param = NULL; 00515 qb = (struct QsqlBuilder *) builder; 00516 /* qb->ent is the live data, strings is the sqlite data */ 00517 inst = (QofInstance *) qb->ent; 00518 qb->exists = TRUE; 00519 if (!inst->dirty) 00520 return SQLITE_OK; 00521 columnName = columnNames[col_num]; 00522 tmp = strings[col_num]; 00523 param = qof_class_get_parameter (qb->ent->e_type, columnName); 00524 if (!param) 00525 return SQLITE_OK; 00526 value = qof_util_param_to_string (qb->ent, param); 00527 qb->dirty = param; 00528 qb->dirty_list = g_list_prepend (qb->dirty_list, qb->ent); 00529 DEBUG (" dirty_list=%d", g_list_length (qb->dirty_list)); 00530 return SQLITE_OK; 00531 } 00532 00533 static gint 00534 mark_entity (gpointer builder, gint col_num, gchar ** strings, 00535 gchar ** columnNames) 00536 { 00537 struct QsqlBuilder *qb; 00538 00539 qb = (struct QsqlBuilder *) builder; 00540 qb->exists = TRUE; 00541 return SQLITE_OK; 00542 } 00543 00544 static void 00545 qsql_create (QofBackend * be, QofInstance * inst) 00546 { 00547 gchar *gstr; 00548 QSQLiteBackend *qsql_be; 00549 struct QsqlBuilder qb; 00550 QofEntity *ent; 00551 00552 qsql_be = (QSQLiteBackend *) be; 00553 if (!inst) 00554 return; 00555 if (loading) 00556 return; 00557 ent = (QofEntity *) inst; 00558 qof_event_suspend (); 00559 qb.has_slots = FALSE; 00560 ENTER (" %s", ent->e_type); 00561 gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' '); 00562 guid_to_string_buff (qof_entity_get_guid (ent), gstr); 00563 qb.sql_str = 00564 g_strdup_printf ("SELECT * FROM %s where guid = \"%s\";", 00565 ent->e_type, gstr); 00566 PINFO (" check exists: %s", qb.sql_str); 00567 qb.ent = ent; 00568 qb.dirty_list = NULL; 00569 qb.exists = FALSE; 00570 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str, 00571 mark_entity, &qb, &qsql_be->err) != SQLITE_OK) 00572 { 00573 qof_error_set_be (be, qsql_be->err_update); 00574 qsql_be->error = TRUE; 00575 PERR (" error on select :%s", qsql_be->err); 00576 } 00577 if (!qb.exists) 00578 { 00579 /* create new entity */ 00580 qb.sql_str = qof_sql_entity_insert (ent); 00581 DEBUG (" sql_str= %s", qb.sql_str); 00582 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str, 00583 NULL, qsql_be, &qsql_be->err) != SQLITE_OK) 00584 { 00585 qof_error_set_be (be, qsql_be->err_insert); 00586 qsql_be->error = TRUE; 00587 PERR (" error creating new entity:%s", qsql_be->err); 00588 } 00589 } 00590 g_free (qb.sql_str); 00591 g_free (gstr); 00592 qof_event_resume (); 00593 LEAVE (" "); 00594 } 00595 00596 static void 00597 check_state (QofEntity * ent, gpointer builder) 00598 { 00599 gchar *gstr; 00600 QSQLiteBackend *qsql_be; 00601 struct QsqlBuilder *qb; 00602 QofBackend *be; 00603 QofInstance *inst; 00604 00605 qb = (struct QsqlBuilder *) builder; 00606 qsql_be = qb->qsql_be; 00607 be = (QofBackend *) qsql_be; 00608 inst = (QofInstance *) ent; 00609 if (!inst->dirty) 00610 return; 00611 /* check if this entity already exists */ 00612 gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' '); 00613 guid_to_string_buff (qof_entity_get_guid (ent), gstr); 00614 qb->sql_str = 00615 g_strdup_printf ("SELECT * FROM %s where guid = \"%s\";", 00616 ent->e_type, gstr); 00617 qb->ent = ent; 00618 qb->dirty_list = NULL; 00619 /* assume entity does not yet exist in backend, 00620 e.g. being copied from another session. */ 00621 qb->exists = FALSE; 00622 qb->qsql_be = qsql_be; 00623 /* update each dirty instance */ 00624 /* Make a GList of dirty instances 00625 Don't update during a SELECT, 00626 UPDATE will fail with DB_LOCKED */ 00627 if (sqlite_exec (qsql_be->sqliteh, qb->sql_str, 00628 create_dirty_list, qb, &qsql_be->err) != SQLITE_OK) 00629 { 00630 qof_error_set_be (be, qsql_be->err_update); 00631 qsql_be->error = TRUE; 00632 PERR (" error on check_state:%s", qsql_be->err); 00633 } 00634 if (!qb->exists) 00635 { 00636 /* create new entity */ 00637 qb->sql_str = qof_sql_entity_insert (ent); 00638 DEBUG (" sql_str= %s", qb->sql_str); 00639 if (sqlite_exec (qsql_be->sqliteh, qb->sql_str, 00640 NULL, qb, &qsql_be->err) != SQLITE_OK) 00641 { 00642 qof_error_set_be (be, qsql_be->err_insert); 00643 qsql_be->error = TRUE; 00644 PERR (" error on check_state create_new:%s", qsql_be->err); 00645 } 00646 g_free (qb->sql_str); 00647 } 00648 /* update instead */ 00649 g_list_foreach (qb->dirty_list, update_dirty, &qb); 00650 g_free (qb->sql_str); 00651 g_free (gstr); 00652 } 00653 00662 static gint 00663 build_kvp_table (gpointer builder, gint col_num, gchar ** strings, 00664 gchar ** columnNames) 00665 { 00666 QSQLiteBackend *qsql_be; 00667 struct QsqlBuilder *qb; 00668 KvpFrame *frame; 00669 KvpValueType type; 00670 KvpValue *value; 00671 gulong max; 00672 gchar *tail; 00673 00674 g_return_val_if_fail (builder, QSQL_ERROR); 00675 qb = (struct QsqlBuilder *) builder; 00676 max = 0; 00677 qsql_be = qb->qsql_be; 00678 g_return_val_if_fail ((col_num < 4), QSQL_ERROR); 00679 g_return_val_if_fail (strings[2], QSQL_ERROR); 00680 frame = kvp_frame_new (); 00681 /* columnNames = fields strings = values 00682 [0]=kvp_id, [1]=guid, [2]=path, [3]=type, [4]=value 00683 get type from type_string */ 00684 type = qof_id_to_kvp_value_type (strings[3]); 00685 if (type == 0) 00686 { 00687 PERR (" invalid type returned from kvp table"); 00688 return QSQL_ERROR; 00689 } 00690 /* use the type to make a KvpValue from value */ 00691 value = string_to_kvp_value (strings[4], type); 00692 if (!value) 00693 { 00694 PERR (" invalid KvpValue for type: %d", type); 00695 return QSQL_ERROR; 00696 } 00697 /* add the KvpValue to the frame at path */ 00698 kvp_frame_set_value (frame, strings[2], value); 00699 /* index the frame under the entity GUID */ 00700 g_hash_table_insert (qsql_be->kvp_table, strings[1], frame); 00701 /* index the guid under the kvp_id */ 00702 g_hash_table_insert (qsql_be->kvp_id, strings[0], strings[1]); 00703 errno = 0; 00704 max = strtol (strings[0], &tail, 0); 00705 if (errno == 0) 00706 { 00707 qsql_be->index = (max > qsql_be->index) ? max : qsql_be->index; 00708 } 00709 return SQLITE_OK; 00710 } 00711 00713 static void 00714 qsql_load_kvp (QSQLiteBackend * qsql_be) 00715 { 00716 struct QsqlBuilder qb; 00717 QofBackend *be; 00718 gint sq_code; 00719 00720 g_return_if_fail (qsql_be); 00721 sq_code = SQLITE_OK; 00722 be = (QofBackend *) qsql_be; 00723 qb.sql_str = 00724 g_strdup_printf ("SELECT kvp_id from %s;", QSQL_KVP_TABLE); 00725 sq_code = sqlite_exec (qsql_be->sqliteh, qb.sql_str, build_kvp_table, 00726 &qb, &qsql_be->err); 00727 /* catch older files without a sqlite_kvp table */ 00728 if (sq_code == SQLITE_ERROR) 00729 { 00730 g_free (qb.sql_str); 00731 qb.sql_str = 00732 g_strdup_printf ("CREATE TABLE %s (%s, %s, %s, %s, %s, %s", 00733 QSQL_KVP_TABLE, "kvp_id int primary key not null", 00734 "guid char(32)", "path mediumtext", "type mediumtext", 00735 "value text", END_DB_VERSION); 00736 PINFO (" creating kvp table. sql=%s", qb.sql_str); 00737 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str, 00738 record_foreach, &qb, &qsql_be->err) != SQLITE_OK) 00739 { 00740 qsql_be->error = TRUE; 00741 PERR (" unable to create kvp table:%s", qsql_be->err); 00742 } 00743 } 00744 else if (sq_code != SQLITE_OK) 00745 { 00746 qof_error_set_be (be, qsql_be->err_create); 00747 qsql_be->error = TRUE; 00748 PERR (" error on KVP select:%s:%s:%d", qb.sql_str, qsql_be->err, sq_code); 00749 } 00750 g_free (qb.sql_str); 00751 } 00752 00754 static void 00755 qsql_class_foreach (QofObject * obj, gpointer data) 00756 { 00757 struct QsqlBuilder qb; 00758 QSQLiteBackend *qsql_be; 00759 QofBackend *be; 00760 00761 qsql_be = (QSQLiteBackend *) data; 00762 be = (QofBackend *) qsql_be; 00763 qb.qsql_be = qsql_be; 00764 qb.e_type = obj->e_type; 00765 ENTER (" obj_type=%s", qb.e_type); 00766 switch (qsql_be->stm_type) 00767 { 00768 case SQL_NONE: 00769 case SQL_INSERT: 00770 case SQL_DELETE: 00771 case SQL_UPDATE: 00772 { 00773 break; 00774 } 00775 case SQL_CREATE: 00776 { 00777 /* KVP is handled separately */ 00778 qb.sql_str = qof_sql_object_create_table (obj); 00779 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str, 00780 NULL, NULL, &qsql_be->err) != SQLITE_OK) 00781 { 00782 qof_error_set_be (be, qsql_be->err_create); 00783 qsql_be->error = TRUE; 00784 PERR (" error on SQL_CREATE:%s", qsql_be->err); 00785 } 00786 g_free (qb.sql_str); 00787 break; 00788 } 00789 case SQL_LOAD: 00790 { 00791 qb.sql_str = 00792 g_strdup_printf ("SELECT * FROM %s;", obj->e_type); 00793 PINFO (" sql=%s", qb.sql_str); 00794 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str, 00795 record_foreach, &qb, &qsql_be->err) != SQLITE_OK) 00796 { 00797 qsql_be->error = TRUE; 00798 PERR (" error on SQL_LOAD:%s", qsql_be->err); 00799 } 00800 break; 00801 } 00802 case SQL_WRITE: 00803 { 00804 if (!qof_book_not_saved (qsql_be->book)) 00805 break; 00806 qof_object_foreach (obj->e_type, qsql_be->book, check_state, 00807 &qb); 00808 break; 00809 } 00810 } 00811 LEAVE (" "); 00812 } 00813 00814 static void 00815 qsql_backend_createdb (QofBackend * be, QofSession * session) 00816 { 00817 FILE *f; 00818 QSQLiteBackend *qsql_be; 00819 struct QsqlBuilder G_GNUC_UNUSED qb; 00820 00821 g_return_if_fail (be || session); 00822 ENTER (" "); 00823 qsql_be = (QSQLiteBackend *) be; 00824 qsql_be->stm_type = SQL_CREATE; 00825 qb.qsql_be = qsql_be; 00826 qsql_be->book = qof_session_get_book (session); 00827 DEBUG (" create_file %s", qsql_be->fullpath); 00828 f = fopen (qsql_be->fullpath, "a+"); 00829 if (f) 00830 fclose (f); 00831 else 00832 { 00833 qof_error_set (session, qof_error_register 00834 (_("Unable to open the output file '%s' - do you have " 00835 "permission to create this file?"), TRUE)); 00836 qsql_be->error = TRUE; 00837 LEAVE (" unable to create new file '%s'", qsql_be->fullpath); 00838 return; 00839 } 00840 qsql_be->sqliteh = 00841 sqlite_open (qsql_be->fullpath, 0644, &qsql_be->err); 00842 if (!qsql_be->sqliteh) 00843 { 00844 qof_error_set_be (be, qsql_be->err_create); 00845 qsql_be->error = TRUE; 00846 LEAVE (" unable to open sqlite:%s", qsql_be->err); 00847 return; 00848 } 00849 qof_object_foreach_type (qsql_class_foreach, qsql_be); 00850 LEAVE (" "); 00851 } 00852 00853 static void 00854 qsql_backend_opendb (QofBackend * be, QofSession * session) 00855 { 00856 QSQLiteBackend *qsql_be; 00857 00858 g_return_if_fail (be || session); 00859 ENTER (" "); 00860 qsql_be = (QSQLiteBackend *) be; 00861 qsql_be->sqliteh = 00862 sqlite_open (qsql_be->fullpath, 0666, &qsql_be->err); 00863 if (!qsql_be->sqliteh) 00864 { 00865 qof_error_set_be (be, qof_error_register 00866 (_("Unable to open the sqlite database '%s'."), TRUE)); 00867 qsql_be->error = TRUE; 00868 PERR (" %s", qsql_be->err); 00869 } 00870 LEAVE (" %s", qsql_be->fullpath); 00871 } 00872 00873 static void 00874 qsqlite_session_begin (QofBackend * be, QofSession * session, 00875 const gchar * book_path, gboolean ignore_lock, 00876 gboolean create_if_nonexistent) 00877 { 00878 QSQLiteBackend *qsql_be; 00879 gchar **pp; 00880 struct stat statinfo; 00881 gint G_GNUC_UNUSED stat_val; 00882 00883 g_return_if_fail (be); 00884 ENTER (" book_path=%s", book_path); 00885 qsql_be = (QSQLiteBackend *) be; 00886 qsql_be->fullpath = NULL; 00887 if (book_path == NULL) 00888 { 00889 qof_error_set_be (be, qof_error_register 00890 (_("Please provide a filename for sqlite."), FALSE)); 00891 qsql_be->error = TRUE; 00892 LEAVE (" bad URL"); 00893 return; 00894 } 00895 /* book_path => sqlite_file_name */ 00896 pp = g_strsplit (book_path, ":", 2); 00897 if (0 == safe_strcmp (pp[0], ACCESS_METHOD)) 00898 { 00899 qsql_be->fullpath = g_strdup (pp[1]); 00900 g_strfreev (pp); 00901 } 00902 else 00903 qsql_be->fullpath = g_strdup (book_path); 00904 be->fullpath = g_strdup (qsql_be->fullpath); 00905 PINFO (" final path = %s", qsql_be->fullpath); 00906 stat_val = g_stat (qsql_be->fullpath, &statinfo); 00907 if (!S_ISREG (statinfo.st_mode) || statinfo.st_size == 0) 00908 qsql_backend_createdb (be, session); 00909 if (!qsql_be->error) 00910 qsql_backend_opendb (be, session); 00911 if (qof_error_check_be (be) || qsql_be->error) 00912 { 00913 LEAVE (" open failed"); 00914 return; 00915 } 00916 qsql_be->create_handler = 00917 qof_event_register_handler (create_event, qsql_be); 00918 qsql_be->delete_handler = 00919 qof_event_register_handler (delete_event, qsql_be); 00920 LEAVE (" db=%s", qsql_be->fullpath); 00921 } 00922 00923 static void 00924 qsqlite_db_load (QofBackend * be, QofBook * book) 00925 { 00926 QSQLiteBackend *qsql_be; 00927 00928 g_return_if_fail (be); 00929 ENTER (" "); 00930 loading = TRUE; 00931 qsql_be = (QSQLiteBackend *) be; 00932 qsql_be->stm_type = SQL_LOAD; 00933 qsql_be->book = book; 00934 /* iterate over registered objects */ 00935 qof_object_foreach_type (qsql_class_foreach, qsql_be); 00936 qsql_load_kvp (qsql_be); 00937 loading = FALSE; 00938 LEAVE (" "); 00939 } 00940 00941 static void 00942 qsqlite_write_db (QofBackend * be, QofBook * book) 00943 { 00944 QSQLiteBackend *qsql_be; 00945 00946 g_return_if_fail (be); 00947 qsql_be = (QSQLiteBackend *) be; 00948 qsql_be->stm_type = SQL_WRITE; 00949 qsql_be->book = book; 00950 /* update each record with current state */ 00951 qof_object_foreach_type (qsql_class_foreach, qsql_be); 00952 } 00953 00954 static gboolean 00955 qsql_determine_file_type (const gchar * path) 00956 { 00957 if (!path) 00958 return FALSE; 00959 return TRUE; 00960 } 00961 00962 static void 00963 qsqlite_session_end (QofBackend * be) 00964 { 00965 QSQLiteBackend *qsql_be; 00966 00967 g_return_if_fail (be); 00968 qsql_be = (QSQLiteBackend *) be; 00969 if (qsql_be->sqliteh) 00970 sqlite_close (qsql_be->sqliteh); 00971 } 00972 00973 static void 00974 qsqlite_destroy_backend (QofBackend * be) 00975 { 00976 QSQLiteBackend *qsql_be; 00977 00978 g_return_if_fail (be); 00979 qsql_be = (QSQLiteBackend *) be; 00980 g_hash_table_destroy (qsql_be->kvp_table); 00981 g_hash_table_destroy (qsql_be->kvp_id); 00982 qof_event_unregister_handler (qsql_be->create_handler); 00983 qof_event_unregister_handler (qsql_be->delete_handler); 00984 g_free (be); 00985 g_free (qsql_be); 00986 } 00987 00988 static void 00989 qsql_provider_free (QofBackendProvider * prov) 00990 { 00991 prov->provider_name = NULL; 00992 prov->access_method = NULL; 00993 g_free (prov); 00994 } 00995 01011 static QofBackend * 01012 qsql_backend_new (void) 01013 { 01014 QSQLiteBackend *qsql_be; 01015 QofBackend *be; 01016 01017 ENTER (" "); 01018 qsql_be = g_new0 (QSQLiteBackend, 1); 01019 be = (QofBackend *) qsql_be; 01020 qof_backend_init (be); 01021 qsql_be->kvp_table = g_hash_table_new (g_str_hash, g_str_equal); 01022 qsql_be->kvp_id = g_hash_table_new (g_str_hash, g_str_equal); 01023 qsql_be->dbversion = QOF_OBJECT_VERSION; 01024 qsql_be->stm_type = SQL_NONE; 01025 qsql_be->err_delete = 01026 qof_error_register (_("Unable to delete record."), FALSE); 01027 qsql_be->err_create = 01028 qof_error_register (_("Unable to create record."), FALSE); 01029 qsql_be->err_insert = 01030 qof_error_register (_("Unable to insert a new record."), FALSE); 01031 qsql_be->err_update = 01032 qof_error_register (_("Unable to update existing record."), FALSE); 01033 be->session_begin = qsqlite_session_begin; 01034 01035 be->session_end = qsqlite_session_end; 01036 be->destroy_backend = qsqlite_destroy_backend; 01037 be->load = qsqlite_db_load; 01038 be->save_may_clobber_data = NULL; 01039 /* begin: create an empty entity if none exists, 01040 even if events are suspended. */ 01041 be->begin = qsql_create; 01042 /* commit: write to sqlite, commit undo record. */ 01043 be->commit = qsql_modify; 01044 be->rollback = NULL; 01045 /* would need a QofQuery back to QofSqlQuery conversion. */ 01046 be->compile_query = NULL; 01047 /* unused */ 01048 be->free_query = NULL; 01049 be->run_query = NULL; 01050 be->counter = NULL; 01051 /* The QOF SQLite backend is not multi-user - all QOF users are the same. */ 01052 be->events_pending = NULL; 01053 be->process_events = NULL; 01054 01055 be->sync = qsqlite_write_db; 01056 be->load_config = NULL; 01057 be->get_config = NULL; 01058 LEAVE (" "); 01059 return be; 01060 } 01061 01062 void 01063 qof_sqlite_provider_init (void) 01064 { 01065 QofBackendProvider *prov; 01066 01067 ENTER (" "); 01068 bindtextdomain (PACKAGE, LOCALE_DIR); 01069 qof_sql_entity_set_kvp_tablename (QSQL_KVP_TABLE); 01070 prov = g_new0 (QofBackendProvider, 1); 01071 prov->provider_name = "QOF SQLite Backend Version 0.4"; 01072 prov->access_method = ACCESS_METHOD; 01073 prov->partial_book_supported = TRUE; 01074 prov->backend_new = qsql_backend_new; 01075 prov->check_data_type = qsql_determine_file_type; 01076 prov->provider_free = qsql_provider_free; 01077 qof_backend_register_provider (prov); 01078 LEAVE (" "); 01079 } 01080 01081 /* ================= END OF FILE =================== */