QOF
0.8.0
|
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 gchar *full_path; 00201 00202 full_path = NULL; 00203 ENTER (" "); 00204 qb = (struct QsqlBuilder *) builder; 00205 qsql_be = qb->qsql_be; 00206 g_return_if_fail (key && val && qsql_be); 00207 n = kvp_value_get_type (val); 00208 switch (n) 00209 { 00210 case KVP_TYPE_GINT64: 00211 case KVP_TYPE_DOUBLE: 00212 case KVP_TYPE_NUMERIC: 00213 case KVP_TYPE_STRING: 00214 case KVP_TYPE_GUID: 00215 case KVP_TYPE_TIME: 00216 case KVP_TYPE_BOOLEAN: 00217 { 00218 /* ("kvp_id int primary key not null", "guid char(32)", "path mediumtext", 00219 "type mediumtext", "value text", */ 00220 00221 qb->sql_str = 00222 g_strdup_printf (" kvp key=%s val=%s type=%s", key, 00223 kvp_value_to_bare_string (val), 00224 kvp_value_type_to_qof_id (n)); 00225 DEBUG (" %s", qb->sql_str); 00226 qb->has_slots = TRUE; 00227 break; 00228 } 00229 case KVP_TYPE_FRAME: 00230 { 00231 kvp_frame_for_each_slot (kvp_value_get_frame (val), 00232 kvpvalue_to_sql, qb); 00233 break; 00234 } 00235 default: 00236 { 00237 PERR (" unsupported value = %d", kvp_value_get_type (val)); 00238 break; 00239 } 00240 } 00241 LEAVE (" %s", qb->sql_str); 00242 } 00243 00248 static void 00249 delete_event (QofEntity * ent, QofEventId event_type, 00250 gpointer handler_data, gpointer event_data) 00251 { 00252 QofBackend *be; 00253 QSQLiteBackend *qsql_be; 00254 gchar *gstr, *sql_str; 00255 00256 qsql_be = (QSQLiteBackend *) handler_data; 00257 be = (QofBackend *) qsql_be; 00258 if (!ent) 00259 return; 00260 if (0 == safe_strcmp (ent->e_type, QOF_ID_BOOK)) 00261 return; 00262 /* do not try to delete if only a QofObject has been loaded. */ 00263 if (!qof_class_is_registered (ent->e_type)) 00264 return; 00265 switch (event_type) 00266 { 00267 case QOF_EVENT_DESTROY: 00268 { 00269 ENTER (" %s do_free=%d", ent->e_type, 00270 ((QofInstance *) ent)->do_free); 00271 gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' '); 00272 guid_to_string_buff (qof_entity_get_guid (ent), gstr); 00273 sql_str = qof_sql_entity_delete (ent); 00274 DEBUG (" sql_str=%s", sql_str); 00275 if (sqlite_exec (qsql_be->sqliteh, sql_str, 00276 NULL, qsql_be, &qsql_be->err) != SQLITE_OK) 00277 { 00278 qof_error_set_be (be, qsql_be->err_delete); 00279 qsql_be->error = TRUE; 00280 LEAVE (" error on delete:%s", qsql_be->err); 00281 break; 00282 } 00283 LEAVE (" %d", event_type); 00284 qsql_be->error = FALSE; 00285 g_free (gstr); 00286 break; 00287 } 00288 default: 00289 break; 00290 } 00291 } 00292 00294 static void 00295 create_event (QofEntity * ent, QofEventId event_type, 00296 gpointer handler_data, gpointer event_data) 00297 { 00298 QofBackend *be; 00299 struct QsqlBuilder qb; 00300 QSQLiteBackend *qsql_be; 00301 gchar *gstr; 00302 KvpFrame *slots; 00303 00304 qsql_be = (QSQLiteBackend *) handler_data; 00305 be = (QofBackend *) qsql_be; 00306 if (!ent) 00307 return; 00308 if (0 == safe_strcmp (ent->e_type, QOF_ID_BOOK)) 00309 return; 00310 if (!qof_class_is_registered (ent->e_type)) 00311 return; 00312 switch (event_type) 00313 { 00314 case QOF_EVENT_CREATE: 00315 { 00316 ENTER (" insert:%s", ent->e_type); 00317 gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' '); 00318 guid_to_string_buff (qof_instance_get_guid ((QofInstance *) 00319 ent), gstr); 00320 DEBUG (" guid=%s", gstr); 00321 qb.ent = ent; 00322 qb.sql_str = qof_sql_entity_insert (ent); 00324 DEBUG (" sql_str=%s", qb.sql_str); 00325 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str, 00326 NULL, &qb, &qsql_be->err) != SQLITE_OK) 00327 { 00328 qof_error_set_be (be, qsql_be->err_insert); 00329 qsql_be->error = TRUE; 00330 PERR (" error on create_event:%s", qsql_be->err); 00331 } 00332 else 00333 { 00334 ((QofInstance *) ent)->dirty = FALSE; 00335 qsql_be->error = FALSE; 00336 g_free (qb.sql_str); 00337 g_free (gstr); 00338 LEAVE (" "); 00339 break; 00340 } 00341 /* insert sqlite_kvp data */ 00342 slots = qof_instance_get_slots ((QofInstance *) ent); 00343 if (slots) 00344 { 00345 /* id, guid, path, type, value */ 00346 qb.sql_str = qof_sql_entity_insert (ent); 00347 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str, 00348 NULL, &qb, &qsql_be->err) != SQLITE_OK) 00349 { 00350 qof_error_set_be (be, qsql_be->err_insert); 00351 qsql_be->error = TRUE; 00352 PERR (" error on KVP create_event:%s", qsql_be->err); 00353 } 00354 else 00355 { 00356 ((QofInstance *) ent)->dirty = FALSE; 00357 qsql_be->error = FALSE; 00358 g_free (qb.sql_str); 00359 g_free (gstr); 00360 LEAVE (" "); 00361 break; 00362 } 00363 } 00364 g_free (qb.sql_str); 00365 g_free (gstr); 00366 LEAVE (" "); 00367 break; 00368 } 00369 default: 00370 break; 00371 } 00372 } 00373 00374 static void 00375 qsql_modify (QofBackend * be, QofInstance * inst) 00376 { 00377 struct QsqlBuilder qb; 00378 QSQLiteBackend *qsql_be; 00379 00380 qsql_be = (QSQLiteBackend *) be; 00381 qb.qsql_be = qsql_be; 00382 if (!inst) 00383 return; 00384 if (!inst->param) 00385 return; 00386 if (loading) 00387 return; 00388 if (!inst->param->param_setfcn) 00389 return; 00390 ENTER (" modified %s param:%s", ((QofEntity *) inst)->e_type, 00391 inst->param->param_name); 00392 qb.sql_str = qof_sql_entity_update ((QofEntity*)inst); 00393 if (!qb.sql_str) 00394 { 00395 LEAVE (" null string"); 00396 return; 00397 } 00398 DEBUG (" sql_str=%s", qb.sql_str); 00399 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str, 00400 NULL, &qb, &qsql_be->err) != SQLITE_OK) 00401 { 00402 qof_error_set_be (be, qsql_be->err_update); 00403 qsql_be->error = TRUE; 00404 PERR (" error on modify:%s", qsql_be->err); 00405 } 00406 else 00407 { 00408 inst->dirty = FALSE; 00409 g_free (qb.sql_str); 00410 qsql_be->error = FALSE; 00411 LEAVE (" "); 00412 return; 00413 } 00414 LEAVE (" "); 00415 } 00416 00418 static gint 00419 record_foreach (gpointer builder, gint col_num, gchar ** strings, 00420 gchar ** columnNames) 00421 { 00422 QSQLiteBackend *qsql_be; 00423 struct QsqlBuilder *qb; 00424 const QofParam *param; 00425 QofInstance *inst; 00426 QofEntity *ent; 00427 gint i; 00428 00429 g_return_val_if_fail (builder, QSQL_ERROR); 00430 qb = (struct QsqlBuilder *) builder; 00431 qsql_be = qb->qsql_be; 00432 qof_event_suspend (); 00433 inst = (QofInstance *) qof_object_new_instance (qb->e_type, qsql_be->book); 00434 ent = &inst->entity; 00435 for (i = 0; i < col_num; i++) 00436 { 00437 /* get param and set as string */ 00438 param = qof_class_get_parameter (qb->e_type, columnNames[i]); 00439 if (!param) 00440 continue; 00441 /* set the inst->param entry */ 00442 inst->param = param; 00443 if (0 == safe_strcmp (columnNames[i], QOF_TYPE_GUID)) 00444 { 00445 GUID *guid; 00446 guid = guid_malloc (); 00447 if (!string_to_guid (strings[i], guid)) 00448 { 00449 DEBUG (" set guid failed:%s", strings[i]); 00450 return QSQL_ERROR; 00451 } 00452 qof_entity_set_guid (ent, guid); 00453 } 00454 if (strings[i]) 00455 qof_util_param_set_string (ent, param, strings[i]); 00456 } 00457 qof_event_resume (); 00458 return SQLITE_OK; 00459 } 00460 00461 static void 00462 update_dirty (gpointer value, gpointer builder) 00463 { 00464 QofInstance *inst; 00465 QofEntity *ent; 00466 struct QsqlBuilder *qb; 00467 QSQLiteBackend *qsql_be; 00468 QofBackend *be; 00469 gchar *gstr; 00470 00471 qb = (struct QsqlBuilder *) builder; 00472 qsql_be = qb->qsql_be; 00473 be = (QofBackend *) qsql_be; 00474 ent = (QofEntity *) value; 00475 inst = (QofInstance *) ent; 00476 if (!inst->dirty) 00477 return; 00478 ENTER (" "); 00479 gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' '); 00480 guid_to_string_buff (qof_entity_get_guid (ent), gstr); 00481 /* qof_class_param_foreach */ 00482 qb->sql_str = qof_sql_entity_update (ent); 00483 if (!qb->sql_str) 00484 { 00485 LEAVE (" null string"); 00486 return; 00487 } 00488 DEBUG (" update=%s", qb->sql_str); 00489 if (sqlite_exec (qsql_be->sqliteh, qb->sql_str, 00490 NULL, qb, &qsql_be->err) != SQLITE_OK) 00491 { 00492 qof_error_set_be (be, qsql_be->err_update); 00493 qsql_be->error = TRUE; 00494 PERR (" error on update_dirty:%s", qsql_be->err); 00495 } 00496 else 00497 { 00498 qof_error_get_message_be (be); 00499 qsql_be->error = FALSE; 00500 inst->dirty = FALSE; 00501 } 00502 LEAVE (" "); 00503 g_free (gstr); 00504 return; 00505 } 00506 00507 static gint 00508 create_dirty_list (gpointer builder, gint col_num, gchar ** strings, 00509 gchar ** columnNames) 00510 { 00511 struct QsqlBuilder *qb; 00512 QofInstance *inst; 00513 const QofParam *param; 00514 gchar *value, *columnName, *tmp; 00515 00516 param = NULL; 00517 qb = (struct QsqlBuilder *) builder; 00518 /* qb->ent is the live data, strings is the sqlite data */ 00519 inst = (QofInstance *) qb->ent; 00520 qb->exists = TRUE; 00521 if (!inst->dirty) 00522 return SQLITE_OK; 00523 columnName = columnNames[col_num]; 00524 tmp = strings[col_num]; 00525 param = qof_class_get_parameter (qb->ent->e_type, columnName); 00526 if (!param) 00527 return SQLITE_OK; 00528 value = qof_util_param_to_string (qb->ent, param); 00529 qb->dirty = param; 00530 qb->dirty_list = g_list_prepend (qb->dirty_list, qb->ent); 00531 DEBUG (" dirty_list=%d", g_list_length (qb->dirty_list)); 00532 return SQLITE_OK; 00533 } 00534 00535 static gint 00536 mark_entity (gpointer builder, gint col_num, gchar ** strings, 00537 gchar ** columnNames) 00538 { 00539 struct QsqlBuilder *qb; 00540 00541 qb = (struct QsqlBuilder *) builder; 00542 qb->exists = TRUE; 00543 return SQLITE_OK; 00544 } 00545 00546 static void 00547 qsql_create (QofBackend * be, QofInstance * inst) 00548 { 00549 gchar *gstr; 00550 QSQLiteBackend *qsql_be; 00551 struct QsqlBuilder qb; 00552 QofEntity *ent; 00553 00554 qsql_be = (QSQLiteBackend *) be; 00555 if (!inst) 00556 return; 00557 if (loading) 00558 return; 00559 ent = (QofEntity *) inst; 00560 qof_event_suspend (); 00561 qb.has_slots = FALSE; 00562 ENTER (" %s", ent->e_type); 00563 gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' '); 00564 guid_to_string_buff (qof_entity_get_guid (ent), gstr); 00565 qb.sql_str = 00566 g_strdup_printf ("SELECT * FROM %s where guid = \"%s\";", 00567 ent->e_type, gstr); 00568 PINFO (" check exists: %s", qb.sql_str); 00569 qb.ent = ent; 00570 qb.dirty_list = NULL; 00571 qb.exists = FALSE; 00572 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str, 00573 mark_entity, &qb, &qsql_be->err) != SQLITE_OK) 00574 { 00575 qof_error_set_be (be, qsql_be->err_update); 00576 qsql_be->error = TRUE; 00577 PERR (" error on select :%s", qsql_be->err); 00578 } 00579 if (!qb.exists) 00580 { 00581 /* create new entity */ 00582 qb.sql_str = qof_sql_entity_insert (ent); 00583 DEBUG (" sql_str= %s", qb.sql_str); 00584 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str, 00585 NULL, qsql_be, &qsql_be->err) != SQLITE_OK) 00586 { 00587 qof_error_set_be (be, qsql_be->err_insert); 00588 qsql_be->error = TRUE; 00589 PERR (" error creating new entity:%s", qsql_be->err); 00590 } 00591 } 00592 g_free (qb.sql_str); 00593 g_free (gstr); 00594 qof_event_resume (); 00595 LEAVE (" "); 00596 } 00597 00598 static void 00599 check_state (QofEntity * ent, gpointer builder) 00600 { 00601 gchar *gstr; 00602 QSQLiteBackend *qsql_be; 00603 struct QsqlBuilder *qb; 00604 QofBackend *be; 00605 QofInstance *inst; 00606 00607 qb = (struct QsqlBuilder *) builder; 00608 qsql_be = qb->qsql_be; 00609 be = (QofBackend *) qsql_be; 00610 inst = (QofInstance *) ent; 00611 if (!inst->dirty) 00612 return; 00613 /* check if this entity already exists */ 00614 gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' '); 00615 guid_to_string_buff (qof_entity_get_guid (ent), gstr); 00616 qb->sql_str = 00617 g_strdup_printf ("SELECT * FROM %s where guid = \"%s\";", 00618 ent->e_type, gstr); 00619 qb->ent = ent; 00620 qb->dirty_list = NULL; 00621 /* assume entity does not yet exist in backend, 00622 e.g. being copied from another session. */ 00623 qb->exists = FALSE; 00624 qb->qsql_be = qsql_be; 00625 /* update each dirty instance */ 00626 /* Make a GList of dirty instances 00627 Don't update during a SELECT, 00628 UPDATE will fail with DB_LOCKED */ 00629 if (sqlite_exec (qsql_be->sqliteh, qb->sql_str, 00630 create_dirty_list, qb, &qsql_be->err) != SQLITE_OK) 00631 { 00632 qof_error_set_be (be, qsql_be->err_update); 00633 qsql_be->error = TRUE; 00634 PERR (" error on check_state:%s", qsql_be->err); 00635 } 00636 if (!qb->exists) 00637 { 00638 /* create new entity */ 00639 qb->sql_str = qof_sql_entity_insert (ent); 00640 DEBUG (" sql_str= %s", qb->sql_str); 00641 if (sqlite_exec (qsql_be->sqliteh, qb->sql_str, 00642 NULL, qb, &qsql_be->err) != SQLITE_OK) 00643 { 00644 qof_error_set_be (be, qsql_be->err_insert); 00645 qsql_be->error = TRUE; 00646 PERR (" error on check_state create_new:%s", qsql_be->err); 00647 } 00648 g_free (qb->sql_str); 00649 } 00650 /* update instead */ 00651 g_list_foreach (qb->dirty_list, update_dirty, &qb); 00652 g_free (qb->sql_str); 00653 g_free (gstr); 00654 } 00655 00664 static gint 00665 build_kvp_table (gpointer builder, gint col_num, gchar ** strings, 00666 gchar ** columnNames) 00667 { 00668 QSQLiteBackend *qsql_be; 00669 struct QsqlBuilder *qb; 00670 KvpFrame *frame; 00671 KvpValueType type; 00672 KvpValue *value; 00673 gulong max; 00674 gchar *tail; 00675 00676 g_return_val_if_fail (builder, QSQL_ERROR); 00677 qb = (struct QsqlBuilder *) builder; 00678 max = 0; 00679 qsql_be = qb->qsql_be; 00680 g_return_val_if_fail ((col_num < 4), QSQL_ERROR); 00681 g_return_val_if_fail (strings[2], QSQL_ERROR); 00682 frame = kvp_frame_new (); 00683 /* columnNames = fields strings = values 00684 [0]=kvp_id, [1]=guid, [2]=path, [3]=type, [4]=value 00685 get type from type_string */ 00686 type = qof_id_to_kvp_value_type (strings[3]); 00687 if (type == 0) 00688 { 00689 PERR (" invalid type returned from kvp table"); 00690 return QSQL_ERROR; 00691 } 00692 /* use the type to make a KvpValue from value */ 00693 value = string_to_kvp_value (strings[4], type); 00694 if (!value) 00695 { 00696 PERR (" invalid KvpValue for type: %d", type); 00697 return QSQL_ERROR; 00698 } 00699 /* add the KvpValue to the frame at path */ 00700 kvp_frame_set_value (frame, strings[2], value); 00701 /* index the frame under the entity GUID */ 00702 g_hash_table_insert (qsql_be->kvp_table, strings[1], frame); 00703 /* index the guid under the kvp_id */ 00704 g_hash_table_insert (qsql_be->kvp_id, strings[0], strings[1]); 00705 errno = 0; 00706 max = strtol (strings[0], &tail, 0); 00707 if (errno == 0) 00708 { 00709 qsql_be->index = (max > qsql_be->index) ? max : qsql_be->index; 00710 } 00711 return SQLITE_OK; 00712 } 00713 00715 static void 00716 qsql_load_kvp (QSQLiteBackend * qsql_be) 00717 { 00718 struct QsqlBuilder qb; 00719 QofBackend *be; 00720 gint sq_code; 00721 00722 g_return_if_fail (qsql_be); 00723 sq_code = SQLITE_OK; 00724 be = (QofBackend *) qsql_be; 00725 qb.sql_str = 00726 g_strdup_printf ("SELECT kvp_id from %s;", QSQL_KVP_TABLE); 00727 sq_code = sqlite_exec (qsql_be->sqliteh, qb.sql_str, build_kvp_table, 00728 &qb, &qsql_be->err); 00729 /* catch older files without a sqlite_kvp table */ 00730 if (sq_code == SQLITE_ERROR) 00731 { 00732 g_free (qb.sql_str); 00733 qb.sql_str = 00734 g_strdup_printf ("CREATE TABLE %s (%s, %s, %s, %s, %s, %s", 00735 QSQL_KVP_TABLE, "kvp_id int primary key not null", 00736 "guid char(32)", "path mediumtext", "type mediumtext", 00737 "value text", END_DB_VERSION); 00738 PINFO (" creating kvp table. sql=%s", qb.sql_str); 00739 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str, 00740 record_foreach, &qb, &qsql_be->err) != SQLITE_OK) 00741 { 00742 qsql_be->error = TRUE; 00743 PERR (" unable to create kvp table:%s", qsql_be->err); 00744 } 00745 } 00746 else if (sq_code != SQLITE_OK) 00747 { 00748 qof_error_set_be (be, qsql_be->err_create); 00749 qsql_be->error = TRUE; 00750 PERR (" error on KVP select:%s:%s:%d", qb.sql_str, qsql_be->err, sq_code); 00751 } 00752 g_free (qb.sql_str); 00753 } 00754 00756 static void 00757 qsql_class_foreach (QofObject * obj, gpointer data) 00758 { 00759 struct QsqlBuilder qb; 00760 QSQLiteBackend *qsql_be; 00761 QofBackend *be; 00762 00763 qsql_be = (QSQLiteBackend *) data; 00764 be = (QofBackend *) qsql_be; 00765 qb.qsql_be = qsql_be; 00766 qb.e_type = obj->e_type; 00767 ENTER (" obj_type=%s", qb.e_type); 00768 switch (qsql_be->stm_type) 00769 { 00770 case SQL_NONE: 00771 case SQL_INSERT: 00772 case SQL_DELETE: 00773 case SQL_UPDATE: 00774 { 00775 break; 00776 } 00777 case SQL_CREATE: 00778 { 00779 /* KVP is handled separately */ 00780 qb.sql_str = qof_sql_object_create_table (obj); 00781 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str, 00782 NULL, NULL, &qsql_be->err) != SQLITE_OK) 00783 { 00784 qof_error_set_be (be, qsql_be->err_create); 00785 qsql_be->error = TRUE; 00786 PERR (" error on SQL_CREATE:%s", qsql_be->err); 00787 } 00788 g_free (qb.sql_str); 00789 break; 00790 } 00791 case SQL_LOAD: 00792 { 00793 qb.sql_str = 00794 g_strdup_printf ("SELECT * FROM %s;", obj->e_type); 00795 PINFO (" sql=%s", qb.sql_str); 00796 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str, 00797 record_foreach, &qb, &qsql_be->err) != SQLITE_OK) 00798 { 00799 qsql_be->error = TRUE; 00800 PERR (" error on SQL_LOAD:%s", qsql_be->err); 00801 } 00802 break; 00803 } 00804 case SQL_WRITE: 00805 { 00806 if (!qof_book_not_saved (qsql_be->book)) 00807 break; 00808 qof_object_foreach (obj->e_type, qsql_be->book, check_state, 00809 &qb); 00810 break; 00811 } 00812 } 00813 LEAVE (" "); 00814 } 00815 00816 static void 00817 qsql_backend_createdb (QofBackend * be, QofSession * session) 00818 { 00819 FILE *f; 00820 QSQLiteBackend *qsql_be; 00821 struct QsqlBuilder qb; 00822 00823 g_return_if_fail (be || session); 00824 ENTER (" "); 00825 qsql_be = (QSQLiteBackend *) be; 00826 qsql_be->stm_type = SQL_CREATE; 00827 qb.qsql_be = qsql_be; 00828 qsql_be->book = qof_session_get_book (session); 00829 DEBUG (" create_file %s", qsql_be->fullpath); 00830 f = fopen (qsql_be->fullpath, "a+"); 00831 if (f) 00832 fclose (f); 00833 else 00834 { 00835 qof_error_set (session, qof_error_register 00836 (_("Unable to open the output file '%s' - do you have " 00837 "permission to create this file?"), TRUE)); 00838 qsql_be->error = TRUE; 00839 LEAVE (" unable to create new file '%s'", qsql_be->fullpath); 00840 return; 00841 } 00842 qsql_be->sqliteh = 00843 sqlite_open (qsql_be->fullpath, 0644, &qsql_be->err); 00844 if (!qsql_be->sqliteh) 00845 { 00846 qof_error_set_be (be, qsql_be->err_create); 00847 qsql_be->error = TRUE; 00848 LEAVE (" unable to open sqlite:%s", qsql_be->err); 00849 return; 00850 } 00851 qof_object_foreach_type (qsql_class_foreach, qsql_be); 00852 LEAVE (" "); 00853 } 00854 00855 static void 00856 qsql_backend_opendb (QofBackend * be, QofSession * session) 00857 { 00858 QSQLiteBackend *qsql_be; 00859 00860 g_return_if_fail (be || session); 00861 ENTER (" "); 00862 qsql_be = (QSQLiteBackend *) be; 00863 qsql_be->sqliteh = 00864 sqlite_open (qsql_be->fullpath, 0666, &qsql_be->err); 00865 if (!qsql_be->sqliteh) 00866 { 00867 qof_error_set_be (be, qof_error_register 00868 (_("Unable to open the sqlite database '%s'."), TRUE)); 00869 qsql_be->error = TRUE; 00870 PERR (" %s", qsql_be->err); 00871 } 00872 LEAVE (" %s", qsql_be->fullpath); 00873 } 00874 00875 static void 00876 qsqlite_session_begin (QofBackend * be, QofSession * session, 00877 const gchar * book_path, gboolean ignore_lock, 00878 gboolean create_if_nonexistent) 00879 { 00880 QSQLiteBackend *qsql_be; 00881 gchar **pp; 00882 struct stat statinfo; 00883 gint stat_val; 00884 00885 g_return_if_fail (be); 00886 ENTER (" book_path=%s", book_path); 00887 qsql_be = (QSQLiteBackend *) be; 00888 qsql_be->fullpath = NULL; 00889 if (book_path == NULL) 00890 { 00891 qof_error_set_be (be, qof_error_register 00892 (_("Please provide a filename for sqlite."), FALSE)); 00893 qsql_be->error = TRUE; 00894 LEAVE (" bad URL"); 00895 return; 00896 } 00897 /* book_path => sqlite_file_name */ 00898 pp = g_strsplit (book_path, ":", 2); 00899 if (0 == safe_strcmp (pp[0], ACCESS_METHOD)) 00900 { 00901 qsql_be->fullpath = g_strdup (pp[1]); 00902 g_strfreev (pp); 00903 } 00904 else 00905 qsql_be->fullpath = g_strdup (book_path); 00906 be->fullpath = g_strdup (qsql_be->fullpath); 00907 PINFO (" final path = %s", qsql_be->fullpath); 00908 stat_val = g_stat (qsql_be->fullpath, &statinfo); 00909 if (!S_ISREG (statinfo.st_mode) || statinfo.st_size == 0) 00910 qsql_backend_createdb (be, session); 00911 if (!qsql_be->error) 00912 qsql_backend_opendb (be, session); 00913 if (qof_error_check_be (be) || qsql_be->error) 00914 { 00915 LEAVE (" open failed"); 00916 return; 00917 } 00918 qsql_be->create_handler = 00919 qof_event_register_handler (create_event, qsql_be); 00920 qsql_be->delete_handler = 00921 qof_event_register_handler (delete_event, qsql_be); 00922 LEAVE (" db=%s", qsql_be->fullpath); 00923 } 00924 00925 static void 00926 qsqlite_db_load (QofBackend * be, QofBook * book) 00927 { 00928 QSQLiteBackend *qsql_be; 00929 00930 g_return_if_fail (be); 00931 ENTER (" "); 00932 loading = TRUE; 00933 qsql_be = (QSQLiteBackend *) be; 00934 qsql_be->stm_type = SQL_LOAD; 00935 qsql_be->book = book; 00936 /* iterate over registered objects */ 00937 qof_object_foreach_type (qsql_class_foreach, qsql_be); 00938 qsql_load_kvp (qsql_be); 00939 loading = FALSE; 00940 LEAVE (" "); 00941 } 00942 00943 static void 00944 qsqlite_write_db (QofBackend * be, QofBook * book) 00945 { 00946 QSQLiteBackend *qsql_be; 00947 00948 g_return_if_fail (be); 00949 qsql_be = (QSQLiteBackend *) be; 00950 qsql_be->stm_type = SQL_WRITE; 00951 qsql_be->book = book; 00952 /* update each record with current state */ 00953 qof_object_foreach_type (qsql_class_foreach, qsql_be); 00954 } 00955 00956 static gboolean 00957 qsql_determine_file_type (const gchar * path) 00958 { 00959 if (!path) 00960 return FALSE; 00961 return TRUE; 00962 } 00963 00964 static void 00965 qsqlite_session_end (QofBackend * be) 00966 { 00967 QSQLiteBackend *qsql_be; 00968 00969 g_return_if_fail (be); 00970 qsql_be = (QSQLiteBackend *) be; 00971 if (qsql_be->sqliteh) 00972 sqlite_close (qsql_be->sqliteh); 00973 } 00974 00975 static void 00976 qsqlite_destroy_backend (QofBackend * be) 00977 { 00978 QSQLiteBackend *qsql_be; 00979 00980 g_return_if_fail (be); 00981 qsql_be = (QSQLiteBackend *) be; 00982 g_hash_table_destroy (qsql_be->kvp_table); 00983 g_hash_table_destroy (qsql_be->kvp_id); 00984 qof_event_unregister_handler (qsql_be->create_handler); 00985 qof_event_unregister_handler (qsql_be->delete_handler); 00986 g_free (be); 00987 g_free (qsql_be); 00988 } 00989 00990 static void 00991 qsql_provider_free (QofBackendProvider * prov) 00992 { 00993 prov->provider_name = NULL; 00994 prov->access_method = NULL; 00995 g_free (prov); 00996 } 00997 01013 static QofBackend * 01014 qsql_backend_new (void) 01015 { 01016 QSQLiteBackend *qsql_be; 01017 QofBackend *be; 01018 01019 ENTER (" "); 01020 qsql_be = g_new0 (QSQLiteBackend, 1); 01021 be = (QofBackend *) qsql_be; 01022 qof_backend_init (be); 01023 qsql_be->kvp_table = g_hash_table_new (g_str_hash, g_str_equal); 01024 qsql_be->kvp_id = g_hash_table_new (g_str_hash, g_str_equal); 01025 qsql_be->dbversion = QOF_OBJECT_VERSION; 01026 qsql_be->stm_type = SQL_NONE; 01027 qsql_be->err_delete = 01028 qof_error_register (_("Unable to delete record."), FALSE); 01029 qsql_be->err_create = 01030 qof_error_register (_("Unable to create record."), FALSE); 01031 qsql_be->err_insert = 01032 qof_error_register (_("Unable to insert a new record."), FALSE); 01033 qsql_be->err_update = 01034 qof_error_register (_("Unable to update existing record."), FALSE); 01035 be->session_begin = qsqlite_session_begin; 01036 01037 be->session_end = qsqlite_session_end; 01038 be->destroy_backend = qsqlite_destroy_backend; 01039 be->load = qsqlite_db_load; 01040 be->save_may_clobber_data = NULL; 01041 /* begin: create an empty entity if none exists, 01042 even if events are suspended. */ 01043 be->begin = qsql_create; 01044 /* commit: write to sqlite, commit undo record. */ 01045 be->commit = qsql_modify; 01046 be->rollback = NULL; 01047 /* would need a QofQuery back to QofSqlQuery conversion. */ 01048 be->compile_query = NULL; 01049 /* unused */ 01050 be->free_query = NULL; 01051 be->run_query = NULL; 01052 be->counter = NULL; 01053 /* The QOF SQLite backend is not multi-user - all QOF users are the same. */ 01054 be->events_pending = NULL; 01055 be->process_events = NULL; 01056 01057 be->sync = qsqlite_write_db; 01058 be->load_config = NULL; 01059 be->get_config = NULL; 01060 LEAVE (" "); 01061 return be; 01062 } 01063 01064 void 01065 qof_sqlite_provider_init (void) 01066 { 01067 QofBackendProvider *prov; 01068 01069 ENTER (" "); 01070 bindtextdomain (PACKAGE, LOCALE_DIR); 01071 qof_sql_entity_set_kvp_tablename (QSQL_KVP_TABLE); 01072 prov = g_new0 (QofBackendProvider, 1); 01073 prov->provider_name = "QOF SQLite Backend Version 0.4"; 01074 prov->access_method = ACCESS_METHOD; 01075 prov->partial_book_supported = TRUE; 01076 prov->backend_new = qsql_backend_new; 01077 prov->check_data_type = qsql_determine_file_type; 01078 prov->provider_free = qsql_provider_free; 01079 qof_backend_register_provider (prov); 01080 LEAVE (" "); 01081 } 01082 01083 /* ================= END OF FILE =================== */