QOF
0.7.5
|
00001 /**************************************************************** 00002 * qof-sqlite.c 00003 * 00004 * Sun Jan 15 12:52:46 2006 00005 * Copyright 2006-2007 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 00034 #define _(String) dgettext (GETTEXT_PACKAGE, String) 00035 #define ACCESS_METHOD "sqlite" 00036 00043 #define PRIORITY_HIGH 9 00044 00045 #define PRIORITY_STANDARD 5 00046 00047 #define PRIORITY_LOW 0 00048 00049 #define QSQL_ERROR -1 00050 00051 #define QSQL_KVP_TABLE "sqlite_kvp" 00052 00053 #define END_DB_VERSION " dbversion int );" 00054 00055 static QofLogModule log_module = QOF_MOD_SQLITE; 00056 static gboolean loading = FALSE; 00057 00058 typedef enum 00059 { 00061 SQL_NONE = 0, 00063 SQL_CREATE, 00065 SQL_LOAD, 00067 SQL_WRITE, 00069 SQL_INSERT, 00071 SQL_DELETE, 00073 SQL_UPDATE 00074 } QsqlStatementType; 00075 00082 typedef struct 00083 { 00084 QofBackend be; 00085 sqlite *sqliteh; 00086 QsqlStatementType stm_type; 00087 gint dbversion; 00088 gint create_handler; 00089 gint delete_handler; 00090 const gchar *fullpath; 00091 gchar *err; 00092 gboolean error; 00093 /* full hashtable of kvp records */ 00094 GHashTable *kvp_table; 00095 /* hashtable relating the GUID to the kvp_id */ 00096 GHashTable *kvp_id; 00097 /* highest kvp_id in the table */ 00098 glong index; 00099 QofBook *book; 00100 QofErrorId err_delete, err_insert, err_update, err_create; 00101 } QSQLiteBackend; 00102 00109 struct QsqlBuilder 00110 { 00112 QSQLiteBackend *qsql_be; 00114 QofEntity *ent; 00116 QofIdType e_type; 00118 gchar *sql_str; 00120 GList *dirty_list; 00122 gboolean exists; 00124 gboolean has_slots; 00126 const QofParam *dirty; 00127 }; 00128 00129 static inline gchar * 00130 add_to_sql (gchar * sql_str, const gchar * add) 00131 { 00132 gchar *old; 00133 old = g_strdup (sql_str); 00134 g_free (sql_str); 00135 sql_str = g_strconcat (old, add, NULL); 00136 g_free (old); 00137 return sql_str; 00138 } 00139 00144 static QofIdTypeConst 00145 kvp_value_to_qof_type_helper (KvpValueType n) 00146 { 00147 switch (n) 00148 { 00149 case KVP_TYPE_GINT64: 00150 { 00151 return QOF_TYPE_INT64; 00152 break; 00153 } 00154 case KVP_TYPE_DOUBLE: 00155 { 00156 return QOF_TYPE_DOUBLE; 00157 break; 00158 } 00159 case KVP_TYPE_NUMERIC: 00160 { 00161 return QOF_TYPE_NUMERIC; 00162 break; 00163 } 00164 case KVP_TYPE_STRING: 00165 { 00166 return QOF_TYPE_STRING; 00167 break; 00168 } 00169 case KVP_TYPE_GUID: 00170 { 00171 return QOF_TYPE_GUID; 00172 break; 00173 } 00174 #ifndef QOF_DISABLE_DEPRECATED 00175 case KVP_TYPE_TIMESPEC: 00176 { 00177 return QOF_TYPE_DATE; 00178 break; 00179 } 00180 #endif 00181 case KVP_TYPE_BOOLEAN: 00182 { 00183 return QOF_TYPE_BOOLEAN; 00184 break; 00185 } 00186 case KVP_TYPE_TIME: 00187 { 00188 return QOF_TYPE_TIME; 00189 break; 00190 } 00191 default: 00192 { 00193 return NULL; 00194 } 00195 } 00196 } 00197 00199 static KvpValueType 00200 sql_to_kvp_helper (const gchar * type_string) 00201 { 00202 if (0 == safe_strcmp (QOF_TYPE_INT64, type_string)) 00203 return KVP_TYPE_GINT64; 00204 if (0 == safe_strcmp (QOF_TYPE_DOUBLE, type_string)) 00205 return KVP_TYPE_DOUBLE; 00206 if (0 == safe_strcmp (QOF_TYPE_NUMERIC, type_string)) 00207 return KVP_TYPE_NUMERIC; 00208 if (0 == safe_strcmp (QOF_TYPE_STRING, type_string)) 00209 return KVP_TYPE_STRING; 00210 if (0 == safe_strcmp (QOF_TYPE_GUID, type_string)) 00211 return KVP_TYPE_GUID; 00212 #ifndef QOF_DISABLE_DEPRECATED 00213 if (0 == safe_strcmp (QOF_TYPE_DATE, type_string)) 00214 return KVP_TYPE_TIMESPEC; 00215 #endif 00216 if (0 == safe_strcmp (QOF_TYPE_TIME, type_string)) 00217 return KVP_TYPE_TIME; 00218 return 0; 00219 } 00220 00222 KvpValue * 00223 string_to_kvp_value (const gchar * content, KvpValueType type) 00224 { 00225 gchar *tail; 00226 gint64 cm_i64; 00227 gdouble cm_double; 00228 QofNumeric cm_numeric; 00229 GUID *cm_guid; 00230 #ifndef QOF_DISABLE_DEPRECATED 00231 struct tm kvp_time; 00232 time_t kvp_time_t; 00233 Timespec cm_date; 00234 #endif 00235 00236 switch (type) 00237 { 00238 case KVP_TYPE_GINT64: 00239 { 00240 errno = 0; 00241 cm_i64 = strtoll (content, &tail, 0); 00242 if (errno == 0) 00243 { 00244 return kvp_value_new_gint64 (cm_i64); 00245 } 00246 break; 00247 } 00248 case KVP_TYPE_DOUBLE: 00249 { 00250 errno = 0; 00251 cm_double = strtod (content, &tail); 00252 if (errno == 0) 00253 return kvp_value_new_double (cm_double); 00254 break; 00255 } 00256 case KVP_TYPE_NUMERIC: 00257 { 00258 qof_numeric_from_string (content, &cm_numeric); 00259 return kvp_value_new_numeric (cm_numeric); 00260 break; 00261 } 00262 case KVP_TYPE_STRING: 00263 { 00264 return kvp_value_new_string (content); 00265 break; 00266 } 00267 case KVP_TYPE_GUID: 00268 { 00269 cm_guid = g_new0 (GUID, 1); 00270 if (TRUE == string_to_guid (content, cm_guid)) 00271 return kvp_value_new_guid (cm_guid); 00272 break; 00273 } 00274 case KVP_TYPE_TIME: 00275 { 00276 QofDate *qd; 00277 QofTime *qt; 00278 KvpValue *retval; 00279 00280 qd = qof_date_parse (content, QOF_DATE_FORMAT_UTC); 00281 if (qd) 00282 { 00283 qt = qof_date_to_qtime (qd); 00284 retval = kvp_value_new_time (qt); 00285 qof_date_free (qd); 00286 qof_time_free (qt); 00287 return retval; 00288 } 00289 else 00290 PERR (" failed to parse date"); 00291 } 00292 #ifndef QOF_DISABLE_DEPRECATED 00293 case KVP_TYPE_TIMESPEC: 00294 { 00295 strptime (content, QOF_UTC_DATE_FORMAT, &kvp_time); 00296 kvp_time_t = mktime (&kvp_time); 00297 timespecFromTime_t (&cm_date, kvp_time_t); 00298 return kvp_value_new_timespec (cm_date); 00299 break; 00300 } 00301 #endif 00302 case KVP_TYPE_BOOLEAN: 00303 { 00304 gboolean val; 00305 val = qof_util_bool_to_int (content); 00306 return kvp_value_new_boolean (val); 00307 } 00308 default: 00309 break; 00310 } 00311 return NULL; 00312 } 00313 00315 static void 00316 kvpvalue_to_sql (const gchar * key, KvpValue * val, gpointer builder) 00317 { 00318 QSQLiteBackend *qsql_be; 00319 struct QsqlBuilder *qb; 00320 KvpValueType n; 00321 gchar *full_path; 00322 00323 full_path = NULL; 00324 ENTER (" "); 00325 qb = (struct QsqlBuilder *) builder; 00326 qsql_be = qb->qsql_be; 00327 g_return_if_fail (key && val && qsql_be); 00328 n = kvp_value_get_type (val); 00329 switch (n) 00330 { 00331 case KVP_TYPE_GINT64: 00332 case KVP_TYPE_DOUBLE: 00333 case KVP_TYPE_NUMERIC: 00334 case KVP_TYPE_STRING: 00335 case KVP_TYPE_GUID: 00336 case KVP_TYPE_TIME: 00337 case KVP_TYPE_BOOLEAN: 00338 #ifndef QOF_DISABLE_DEPRECATED 00339 case KVP_TYPE_TIMESPEC: 00340 #endif 00341 { 00342 /* ("kvp_id int primary key not null", "guid char(32)", "path mediumtext", 00343 "type mediumtext", "value text", */ 00344 00345 qb->sql_str = 00346 g_strdup_printf (" kvp key=%s val=%s type=%s", key, 00347 kvp_value_to_bare_string (val), 00348 kvp_value_to_qof_type_helper (n)); 00349 DEBUG (" %s", qb->sql_str); 00350 qb->has_slots = TRUE; 00351 break; 00352 } 00353 case KVP_TYPE_FRAME: 00354 { 00355 kvp_frame_for_each_slot (kvp_value_get_frame (val), 00356 kvpvalue_to_sql, qb); 00357 break; 00358 } 00359 default: 00360 { 00361 PERR (" unsupported value = %d", kvp_value_get_type (val)); 00362 break; 00363 } 00364 } 00365 LEAVE (" %s", qb->sql_str); 00366 } 00367 00368 static gchar * 00369 string_param_to_sql (QofParam * param) 00370 { 00371 /* Handle the entity GUID. Ensure that reference GUIDs 00372 must not also try to be primary keys and can be NULL. */ 00373 if ((0 == safe_strcmp (param->param_type, QOF_TYPE_GUID)) && 00374 (0 == safe_strcmp (param->param_name, QOF_PARAM_GUID))) 00375 return g_strdup_printf (" %s char(32) primary key not null", 00376 param->param_name); 00377 if (0 == safe_strcmp (param->param_type, QOF_TYPE_GUID)) 00378 return g_strdup_printf (" %s char(32)", param->param_name); 00379 /* avoid creating database fields for calculated values */ 00380 if (!param->param_setfcn) 00381 return NULL; 00382 if (0 == safe_strcmp (param->param_type, QOF_TYPE_STRING)) 00383 return g_strdup_printf (" %s mediumtext", param->param_name); 00384 if (0 == safe_strcmp (param->param_type, QOF_TYPE_BOOLEAN)) 00385 return g_strdup_printf (" %s int", param->param_name); 00386 if ((0 == safe_strcmp (param->param_type, QOF_TYPE_NUMERIC)) 00387 || (0 == safe_strcmp (param->param_type, QOF_TYPE_DOUBLE)) 00388 || (0 == safe_strcmp (param->param_type, QOF_TYPE_DEBCRED))) 00389 { 00390 return g_strdup_printf (" %s text", param->param_name); 00391 } 00392 if (0 == safe_strcmp (param->param_type, QOF_TYPE_INT32)) 00393 return g_strdup_printf (" %s int", param->param_name); 00394 #ifndef QOF_DISABLE_DEPRECATED 00395 if ((0 == safe_strcmp (param->param_type, QOF_TYPE_DATE)) || 00396 (0 == safe_strcmp (param->param_type, QOF_TYPE_TIME))) 00397 #else 00398 if (0 == safe_strcmp (param->param_type, QOF_TYPE_TIME)) 00399 #endif 00400 return g_strdup_printf (" %s datetime", param->param_name); 00401 if (0 == safe_strcmp (param->param_type, QOF_TYPE_CHAR)) 00402 return g_strdup_printf (" %s char(1)", param->param_name); 00403 /* kvp data is stored separately - actually this is really 00404 a no-op because entities do not need a param_setfcn for kvp data. */ 00405 if (0 == safe_strcmp (param->param_type, QOF_TYPE_KVP)) 00406 return g_strdup (""); 00407 if (0 == safe_strcmp (param->param_type, QOF_TYPE_COLLECT)) 00408 return g_strdup_printf (" %s char(32)", param->param_name); 00409 /* catch references */ 00410 return g_strdup_printf (" %s char(32)", param->param_name); 00411 } 00412 00418 static void 00419 create_param_list (QofParam * param, gpointer builder) 00420 { 00421 struct QsqlBuilder *qb; 00422 qb = (struct QsqlBuilder *) builder; 00423 00424 /* avoid creating database fields for calculated values */ 00425 if (!param->param_setfcn) 00426 return; 00427 /* avoid setting KVP even if a param_setfcn has been set 00428 because a QofSetterFunc for KVP is quite pointless. */ 00429 if (0 == safe_strcmp (param->param_type, QOF_TYPE_KVP)) 00430 { 00431 PINFO (" kvp support tag"); 00432 return; 00433 } 00434 if (!g_str_has_suffix (qb->sql_str, "(")) 00435 { 00436 gchar *add; 00437 add = g_strconcat (", ", param->param_name, NULL); 00438 qb->sql_str = add_to_sql (qb->sql_str, add); 00439 g_free (add); 00440 } 00441 else 00442 qb->sql_str = add_to_sql (qb->sql_str, param->param_name); 00443 } 00444 00446 static void 00447 create_each_param (QofParam * param, gpointer builder) 00448 { 00449 gchar *value; 00450 struct QsqlBuilder *qb; 00451 qb = (struct QsqlBuilder *) builder; 00452 GList *references; 00453 00454 /* avoid creating database fields for calculated values */ 00455 if (!param->param_setfcn) 00456 return; 00457 /* avoid setting KVP even if a param_setfcn has been set 00458 because a QofSetterFunc for KVP is quite pointless. */ 00459 if (0 == safe_strcmp (param->param_type, QOF_TYPE_KVP)) 00460 return; 00461 references = qof_class_get_referenceList (qb->ent->e_type); 00462 if (g_list_find (references, param)) 00463 { 00466 QofEntity *e; 00467 e = param->param_getfcn (qb->ent, param); 00468 value = g_strnfill (GUID_ENCODING_LENGTH + 1, ' '); 00469 guid_to_string_buff (qof_entity_get_guid (e), value); 00470 PINFO (" ref=%p GUID=%s", e, value); 00471 } 00472 else 00473 value = qof_util_param_to_string (qb->ent, param); 00474 if (value) 00475 g_strescape (value, NULL); 00476 if (!value) 00477 value = g_strdup (""); 00478 if (!g_str_has_suffix (qb->sql_str, "(")) 00479 { 00480 gchar *val; 00481 val = g_strconcat (", \"", value, "\"", NULL); 00482 qb->sql_str = add_to_sql (qb->sql_str, val); 00483 g_free (val); 00484 } 00485 else 00486 { 00487 gchar *val; 00488 val = g_strconcat ("\"", value, "\"", NULL); 00489 qb->sql_str = add_to_sql (qb->sql_str, val); 00490 g_free (val); 00491 } 00492 } 00493 00498 static void 00499 delete_event (QofEntity * ent, QofEventId event_type, 00500 gpointer handler_data, gpointer event_data) 00501 { 00502 QofBackend *be; 00503 QSQLiteBackend *qsql_be; 00504 gchar *gstr, *sql_str; 00505 00506 qsql_be = (QSQLiteBackend *) handler_data; 00507 be = (QofBackend *) qsql_be; 00508 if (!ent) 00509 return; 00510 if (0 == safe_strcmp (ent->e_type, QOF_ID_BOOK)) 00511 return; 00512 /* do not try to delete if only a QofObject has been loaded. */ 00513 if (!qof_class_is_registered (ent->e_type)) 00514 return; 00515 switch (event_type) 00516 { 00517 case QOF_EVENT_DESTROY: 00518 { 00519 ENTER (" %s do_free=%d", ent->e_type, 00520 ((QofInstance *) ent)->do_free); 00521 gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' '); 00522 guid_to_string_buff (qof_entity_get_guid (ent), gstr); 00523 sql_str = g_strconcat ("DELETE from ", ent->e_type, " WHERE ", 00524 QOF_TYPE_GUID, "='", gstr, "';", NULL); 00525 DEBUG (" sql_str=%s", sql_str); 00526 if (sqlite_exec (qsql_be->sqliteh, sql_str, 00527 NULL, qsql_be, &qsql_be->err) != SQLITE_OK) 00528 { 00529 qof_error_set_be (be, qsql_be->err_delete); 00530 qsql_be->error = TRUE; 00531 LEAVE (" error on delete:%s", qsql_be->err); 00532 break; 00533 } 00535 /* SELECT kvp_id from QSQL_KVP_TABLE where guid = gstr */ 00536 LEAVE (" %d", event_type); 00537 qsql_be->error = FALSE; 00538 g_free (gstr); 00539 break; 00540 } 00541 default: 00542 break; 00543 } 00544 } 00545 00547 static void 00548 create_event (QofEntity * ent, QofEventId event_type, 00549 gpointer handler_data, gpointer event_data) 00550 { 00551 QofBackend *be; 00552 struct QsqlBuilder qb; 00553 QSQLiteBackend *qsql_be; 00554 gchar *gstr; 00555 KvpFrame *slots; 00556 00557 qsql_be = (QSQLiteBackend *) handler_data; 00558 be = (QofBackend *) qsql_be; 00559 if (!ent) 00560 return; 00561 if (0 == safe_strcmp (ent->e_type, QOF_ID_BOOK)) 00562 return; 00563 if (!qof_class_is_registered (ent->e_type)) 00564 return; 00565 switch (event_type) 00566 { 00567 case QOF_EVENT_CREATE: 00568 { 00569 gchar *tmp; 00570 ENTER (" create:%s", ent->e_type); 00571 gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' '); 00572 guid_to_string_buff (qof_instance_get_guid ((QofInstance *) 00573 ent), gstr); 00574 DEBUG (" guid=%s", gstr); 00575 qb.ent = ent; 00576 qb.sql_str = 00577 g_strdup_printf ("INSERT into %s (guid ", ent->e_type); 00578 qof_class_param_foreach (ent->e_type, create_param_list, &qb); 00579 tmp = g_strconcat (") VALUES (\"", gstr, "\" ", NULL); 00580 qb.sql_str = add_to_sql (qb.sql_str, tmp); 00581 g_free (tmp); 00582 qof_class_param_foreach (ent->e_type, create_each_param, &qb); 00583 qb.sql_str = add_to_sql (qb.sql_str, ");"); 00584 DEBUG (" sql_str=%s", qb.sql_str); 00585 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str, 00586 NULL, &qb, &qsql_be->err) != SQLITE_OK) 00587 { 00588 qof_error_set_be (be, qsql_be->err_insert); 00589 qsql_be->error = TRUE; 00590 PERR (" error on create_event:%s", qsql_be->err); 00591 } 00592 else 00593 { 00594 ((QofInstance *) ent)->dirty = FALSE; 00595 qsql_be->error = FALSE; 00596 g_free (qb.sql_str); 00597 g_free (gstr); 00598 LEAVE (" "); 00599 break; 00600 } 00601 /* insert sqlite_kvp data */ 00602 slots = qof_instance_get_slots ((QofInstance *) ent); 00603 if (slots) 00604 { 00605 /* id, guid, path, type, value */ 00606 qb.sql_str = g_strconcat ("INSERT into ", QSQL_KVP_TABLE, 00607 " (kvp_id \"", gstr, "\", ", NULL); 00608 kvp_frame_for_each_slot (slots, kvpvalue_to_sql, &qb); 00609 qb.sql_str = add_to_sql (qb.sql_str, END_DB_VERSION); 00610 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str, 00611 NULL, &qb, &qsql_be->err) != SQLITE_OK) 00612 { 00613 qof_error_set_be (be, qsql_be->err_insert); 00614 qsql_be->error = TRUE; 00615 PERR (" error on KVP create_event:%s", qsql_be->err); 00616 } 00617 else 00618 { 00619 ((QofInstance *) ent)->dirty = FALSE; 00620 qsql_be->error = FALSE; 00621 g_free (qb.sql_str); 00622 g_free (gstr); 00623 LEAVE (" "); 00624 break; 00625 } 00626 } 00627 g_free (qb.sql_str); 00628 g_free (gstr); 00629 LEAVE (" "); 00630 break; 00631 } 00632 default: 00633 break; 00634 } 00635 } 00636 00637 static void 00638 qsql_modify (QofBackend * be, QofInstance * inst) 00639 { 00640 struct QsqlBuilder qb; 00641 QSQLiteBackend *qsql_be; 00642 gchar *gstr, *param_str; 00643 KvpFrame *slots; 00644 00645 qsql_be = (QSQLiteBackend *) be; 00646 qb.qsql_be = qsql_be; 00647 if (!inst) 00648 return; 00649 if (!inst->param) 00650 return; 00651 if (loading) 00652 return; 00653 if (!inst->param->param_setfcn) 00654 return; 00655 ENTER (" modified %s param:%s", ((QofEntity *) inst)->e_type, 00656 inst->param->param_name); 00657 gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' '); 00658 guid_to_string_buff (qof_instance_get_guid (inst), gstr); 00659 qb.ent = (QofEntity *) inst; 00660 param_str = qof_util_param_to_string (qb.ent, inst->param); 00661 if (param_str) 00662 g_strescape (param_str, NULL); 00663 qb.sql_str = g_strconcat ("UPDATE ", qb.ent->e_type, " SET ", 00664 inst->param->param_name, " = \"", param_str, 00665 "\" WHERE ", QOF_TYPE_GUID, "='", gstr, "';", NULL); 00666 DEBUG (" sql_str=%s param_Str=%s", qb.sql_str, param_str); 00667 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str, 00668 NULL, &qb, &qsql_be->err) != SQLITE_OK) 00669 { 00670 qof_error_set_be (be, qsql_be->err_update); 00671 qsql_be->error = TRUE; 00672 PERR (" error on modify:%s", qsql_be->err); 00673 } 00674 else 00675 { 00676 inst->dirty = FALSE; 00677 g_free (qb.sql_str); 00678 g_free (gstr); 00679 qsql_be->error = FALSE; 00680 LEAVE (" "); 00681 return; 00682 } 00683 /* modify slot data */ 00684 slots = qof_instance_get_slots (inst); 00685 if (slots) 00686 { 00687 /* update and delete KVP data */ 00688 /* id, guid, path, type, value */ 00689 qb.sql_str = g_strconcat ("UPDATE ", QSQL_KVP_TABLE, 00690 " SET (kvp_id \"", gstr, "\", ", NULL); 00691 kvp_frame_for_each_slot (slots, kvpvalue_to_sql, &qb); 00692 qb.sql_str = add_to_sql (qb.sql_str, END_DB_VERSION); 00693 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str, 00694 NULL, &qb, &qsql_be->err) != SQLITE_OK) 00695 { 00696 qof_error_set_be (be, qsql_be->err_insert); 00697 qsql_be->error = TRUE; 00698 PERR (" error on KVP create_event:%s", qsql_be->err); 00699 } 00700 else 00701 { 00702 ((QofInstance *) qb.ent)->dirty = FALSE; 00703 qsql_be->error = FALSE; 00704 g_free (qb.sql_str); 00705 } 00706 } 00707 g_free (gstr); 00708 LEAVE (" "); 00709 } 00710 00712 static gint 00713 record_foreach (gpointer builder, gint col_num, gchar ** strings, 00714 gchar ** columnNames) 00715 { 00716 QSQLiteBackend *qsql_be; 00717 struct QsqlBuilder *qb; 00718 const QofParam *param; 00719 QofInstance *inst; 00720 QofEntity *ent; 00721 gint i; 00722 00723 g_return_val_if_fail (builder, QSQL_ERROR); 00724 qb = (struct QsqlBuilder *) builder; 00725 qsql_be = qb->qsql_be; 00726 qof_event_suspend (); 00727 inst = (QofInstance *) qof_object_new_instance (qb->e_type, qsql_be->book); 00728 ent = &inst->entity; 00729 for (i = 0; i < col_num; i++) 00730 { 00731 /* get param and set as string */ 00732 param = qof_class_get_parameter (qb->e_type, columnNames[i]); 00733 if (!param) 00734 continue; 00735 /* set the inst->param entry */ 00736 inst->param = param; 00737 if (0 == safe_strcmp (columnNames[i], QOF_TYPE_GUID)) 00738 { 00739 GUID *guid; 00740 guid = guid_malloc (); 00741 if (!string_to_guid (strings[i], guid)) 00742 { 00743 DEBUG (" set guid failed:%s", strings[i]); 00744 return QSQL_ERROR; 00745 } 00746 qof_entity_set_guid (ent, guid); 00747 } 00748 if (strings[i]) 00749 qof_util_param_set_string (ent, param, strings[i]); 00750 } 00751 qof_event_resume (); 00752 return SQLITE_OK; 00753 } 00754 00755 /* used by create/insert */ 00756 static void 00757 string_param_foreach (QofParam * param, gpointer builder) 00758 { 00759 struct QsqlBuilder *qb; 00760 QSQLiteBackend *qsql_be; 00761 gchar *p_str, *old; 00762 00763 qb = (struct QsqlBuilder *) builder; 00764 qsql_be = qb->qsql_be; 00765 if (0 == safe_strcmp (param->param_type, QOF_TYPE_KVP)) 00766 return; 00767 p_str = string_param_to_sql (param); 00768 /* skip empty values (no param_setfcn) */ 00769 if (!p_str) 00770 return; 00771 old = g_strconcat (p_str, ",", NULL); 00772 qb->sql_str = add_to_sql (qb->sql_str, old); 00773 g_free (old); 00774 g_free (p_str); 00775 } 00776 00777 static void 00778 update_param_foreach (QofParam * param, gpointer builder) 00779 { 00780 struct QsqlBuilder *qb; 00781 gchar *value, *add; 00782 00783 qb = (struct QsqlBuilder *) builder; 00784 if (param != qb->dirty) 00785 return; 00786 /* update table set name=val,name=val where guid=gstr; */ 00787 value = qof_util_param_to_string (qb->ent, param); 00788 if (value) 00789 g_strescape (value, NULL); 00790 if (!value) 00791 value = g_strdup (""); 00792 if (g_str_has_suffix (qb->sql_str, " ")) 00793 { 00794 add = g_strconcat (param->param_name, "=\"", value, "\"", NULL); 00795 qb->sql_str = add_to_sql (qb->sql_str, add); 00796 g_free (add); 00797 } 00798 else 00799 { 00800 add = 00801 g_strconcat (",", param->param_name, "=\"", value, "\"", NULL); 00802 qb->sql_str = add_to_sql (qb->sql_str, add); 00803 g_free (add); 00804 } 00805 } 00806 00807 static void 00808 update_dirty (gpointer value, gpointer builder) 00809 { 00810 QofInstance *inst; 00811 QofEntity *ent; 00812 struct QsqlBuilder *qb; 00813 QSQLiteBackend *qsql_be; 00814 QofBackend *be; 00815 gchar *gstr, *param_str; 00816 00817 qb = (struct QsqlBuilder *) builder; 00818 qsql_be = qb->qsql_be; 00819 be = (QofBackend *) qsql_be; 00820 ent = (QofEntity *) value; 00821 inst = (QofInstance *) ent; 00822 if (!inst->dirty) 00823 return; 00824 ENTER (" "); 00825 gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' '); 00826 guid_to_string_buff (qof_entity_get_guid (ent), gstr); 00827 /* qof_class_param_foreach */ 00828 qb->sql_str = g_strdup_printf ("UPDATE %s SET ", ent->e_type); 00829 qof_class_param_foreach (ent->e_type, update_param_foreach, qb); 00830 param_str = g_strdup_printf ("WHERE %s=\"%s\";", QOF_TYPE_GUID, gstr); 00831 qb->sql_str = add_to_sql (qb->sql_str, param_str); 00832 g_free (param_str); 00833 DEBUG (" update=%s", qb->sql_str); 00834 if (sqlite_exec (qsql_be->sqliteh, qb->sql_str, 00835 NULL, qb, &qsql_be->err) != SQLITE_OK) 00836 { 00837 qof_error_set_be (be, qsql_be->err_update); 00838 qsql_be->error = TRUE; 00839 PERR (" error on update_dirty:%s", qsql_be->err); 00840 } 00841 else 00842 { 00843 qof_error_get_message_be (be); 00844 qsql_be->error = FALSE; 00845 inst->dirty = FALSE; 00846 } 00847 LEAVE (" "); 00848 g_free (gstr); 00849 return; 00850 } 00851 00852 static gint 00853 create_dirty_list (gpointer builder, gint col_num, gchar ** strings, 00854 gchar ** columnNames) 00855 { 00856 struct QsqlBuilder *qb; 00857 QofInstance *inst; 00858 const QofParam *param; 00859 gchar *value, *columnName, *tmp; 00860 00861 param = NULL; 00862 qb = (struct QsqlBuilder *) builder; 00863 /* qb->ent is the live data, strings is the sqlite data */ 00864 inst = (QofInstance *) qb->ent; 00865 qb->exists = TRUE; 00866 if (!inst->dirty) 00867 return SQLITE_OK; 00868 columnName = columnNames[col_num]; 00869 tmp = strings[col_num]; 00870 param = qof_class_get_parameter (qb->ent->e_type, columnName); 00871 if (!param) 00872 return SQLITE_OK; 00873 value = qof_util_param_to_string (qb->ent, param); 00874 qb->dirty = param; 00875 qb->dirty_list = g_list_prepend (qb->dirty_list, qb->ent); 00876 DEBUG (" dirty_list=%d", g_list_length (qb->dirty_list)); 00877 return SQLITE_OK; 00878 } 00879 00880 static gint 00881 mark_entity (gpointer builder, gint col_num, gchar ** strings, 00882 gchar ** columnNames) 00883 { 00884 struct QsqlBuilder *qb; 00885 00886 qb = (struct QsqlBuilder *) builder; 00887 qb->exists = TRUE; 00888 return SQLITE_OK; 00889 } 00890 00891 static void 00892 qsql_create (QofBackend * be, QofInstance * inst) 00893 { 00894 gchar *gstr; 00895 QSQLiteBackend *qsql_be; 00896 struct QsqlBuilder qb; 00897 QofEntity *ent; 00898 KvpFrame *slots; 00899 00900 qsql_be = (QSQLiteBackend *) be; 00901 if (!inst) 00902 return; 00903 if (loading) 00904 return; 00905 ent = (QofEntity *) inst; 00906 qof_event_suspend (); 00907 qb.has_slots = FALSE; 00908 ENTER (" %s", ent->e_type); 00909 gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' '); 00910 guid_to_string_buff (qof_entity_get_guid (ent), gstr); 00911 qb.sql_str = 00912 g_strdup_printf ("SELECT * FROM %s where guid = \"%s\";", 00913 ent->e_type, gstr); 00914 PINFO (" check exists: %s", qb.sql_str); 00915 qb.ent = ent; 00916 qb.dirty_list = NULL; 00917 qb.exists = FALSE; 00918 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str, 00919 mark_entity, &qb, &qsql_be->err) != SQLITE_OK) 00920 { 00921 qof_error_set_be (be, qsql_be->err_update); 00922 qsql_be->error = TRUE; 00923 PERR (" error on select :%s", qsql_be->err); 00924 } 00925 if (!qb.exists) 00926 { 00927 gchar *add; 00928 /* create new entity */ 00929 qb.sql_str = 00930 g_strdup_printf ("INSERT into %s (guid ", ent->e_type); 00931 qof_class_param_foreach (ent->e_type, create_param_list, &qb); 00932 add = g_strconcat (") VALUES (\"", gstr, "\"", NULL); 00933 qb.sql_str = add_to_sql (qb.sql_str, add); 00934 g_free (add); 00935 qof_class_param_foreach (ent->e_type, create_each_param, &qb); 00936 qb.sql_str = add_to_sql (qb.sql_str, ");"); 00937 DEBUG (" sql_str= %s", qb.sql_str); 00938 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str, 00939 NULL, qsql_be, &qsql_be->err) != SQLITE_OK) 00940 { 00941 qof_error_set_be (be, qsql_be->err_insert); 00942 qsql_be->error = TRUE; 00943 PERR (" error creating new entity:%s", qsql_be->err); 00944 } 00945 /* KVP here */ 00946 slots = qof_instance_get_slots ((QofInstance *) ent); 00947 if (slots) 00948 { 00949 /* id, guid, path, type, value */ 00950 qb.sql_str = g_strconcat ("INSERT into ", QSQL_KVP_TABLE, 00951 " (kvp_id, \"", gstr, "\", ", NULL); 00952 kvp_frame_for_each_slot (slots, kvpvalue_to_sql, &qb); 00953 qb.sql_str = add_to_sql (qb.sql_str, ");"); 00954 } 00955 if (qb.has_slots) 00956 { 00957 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str, 00958 NULL, &qb, &qsql_be->err) != SQLITE_OK) 00959 { 00960 qof_error_set_be (be, qsql_be->err_insert); 00961 qsql_be->error = TRUE; 00962 PERR (" error on KVP create_event:%s:%s", qsql_be->err, 00963 qb.sql_str); 00964 } 00965 else 00966 { 00967 ((QofInstance *) ent)->dirty = FALSE; 00968 qsql_be->error = FALSE; 00969 } 00970 } 00971 } 00972 g_free (qb.sql_str); 00973 g_free (gstr); 00974 qof_event_resume (); 00975 LEAVE (" "); 00976 } 00977 00978 static void 00979 check_state (QofEntity * ent, gpointer builder) 00980 { 00981 gchar *gstr; 00982 QSQLiteBackend *qsql_be; 00983 struct QsqlBuilder *qb; 00984 QofBackend *be; 00985 QofInstance *inst; 00986 KvpFrame *slots; 00987 00988 qb = (struct QsqlBuilder *) builder; 00989 qsql_be = qb->qsql_be; 00990 be = (QofBackend *) qsql_be; 00991 inst = (QofInstance *) ent; 00992 if (!inst->dirty) 00993 return; 00994 /* check if this entity already exists */ 00995 gstr = g_strnfill (GUID_ENCODING_LENGTH + 1, ' '); 00996 guid_to_string_buff (qof_entity_get_guid (ent), gstr); 00997 qb->sql_str = 00998 g_strdup_printf ("SELECT * FROM %s where guid = \"%s\";", 00999 ent->e_type, gstr); 01000 qb->ent = ent; 01001 qb->dirty_list = NULL; 01002 /* assume entity does not yet exist in backend, 01003 e.g. being copied from another session. */ 01004 qb->exists = FALSE; 01005 qb->qsql_be = qsql_be; 01006 /* update each dirty instance */ 01007 /* Make a GList of dirty instances 01008 Don't update during a SELECT, 01009 UPDATE will fail with DB_LOCKED */ 01010 if (sqlite_exec (qsql_be->sqliteh, qb->sql_str, 01011 create_dirty_list, qb, &qsql_be->err) != SQLITE_OK) 01012 { 01013 qof_error_set_be (be, qsql_be->err_update); 01014 qsql_be->error = TRUE; 01015 PERR (" error on check_state:%s", qsql_be->err); 01016 } 01017 if (!qb->exists) 01018 { 01019 gchar *add; 01020 /* create new entity */ 01021 qb->sql_str = 01022 g_strdup_printf ("INSERT into %s (guid ", ent->e_type); 01023 qof_class_param_foreach (ent->e_type, create_param_list, &qb); 01024 add = g_strconcat (") VALUES (\"", gstr, "\" ", NULL); 01025 qb->sql_str = add_to_sql (qb->sql_str, add); 01026 g_free (add); 01027 qof_class_param_foreach (ent->e_type, create_each_param, &qb); 01028 qb->sql_str = add_to_sql (qb->sql_str, ");"); 01029 DEBUG (" sql_str= %s", qb->sql_str); 01030 if (sqlite_exec (qsql_be->sqliteh, qb->sql_str, 01031 NULL, qb, &qsql_be->err) != SQLITE_OK) 01032 { 01033 qof_error_set_be (be, qsql_be->err_insert); 01034 qsql_be->error = TRUE; 01035 PERR (" error on check_state create_new:%s", qsql_be->err); 01036 } 01037 g_free (qb->sql_str); 01038 /* create KVP data too */ 01039 slots = qof_instance_get_slots ((QofInstance *) ent); 01040 if (slots) 01041 { 01042 /* id, guid, path, type, value */ 01043 qb->sql_str = g_strconcat ("INSERT into ", QSQL_KVP_TABLE, 01044 " (kvp_id \"", gstr, "\", ", NULL); 01045 kvp_frame_for_each_slot (slots, kvpvalue_to_sql, &qb); 01046 qb->sql_str = add_to_sql (qb->sql_str, END_DB_VERSION); 01047 if (sqlite_exec (qsql_be->sqliteh, qb->sql_str, 01048 NULL, &qb, &qsql_be->err) != SQLITE_OK) 01049 { 01050 qof_error_set_be (be, qsql_be->err_insert); 01051 qsql_be->error = TRUE; 01052 PERR (" error on KVP create_event:%s", qsql_be->err); 01053 } 01054 else 01055 { 01056 ((QofInstance *) ent)->dirty = FALSE; 01057 qsql_be->error = FALSE; 01058 } 01059 } 01060 } 01061 /* update instead */ 01062 g_list_foreach (qb->dirty_list, update_dirty, &qb); 01063 g_free (qb->sql_str); 01064 g_free (gstr); 01065 } 01066 01075 static gint 01076 build_kvp_table (gpointer builder, gint col_num, gchar ** strings, 01077 gchar ** columnNames) 01078 { 01079 QSQLiteBackend *qsql_be; 01080 struct QsqlBuilder *qb; 01081 KvpFrame *frame; 01082 KvpValueType type; 01083 KvpValue *value; 01084 glong max; 01085 gchar *tail; 01086 01087 g_return_val_if_fail (builder, QSQL_ERROR); 01088 qb = (struct QsqlBuilder *) builder; 01089 max = 0; 01090 qsql_be = qb->qsql_be; 01091 g_return_val_if_fail ((col_num < 4), QSQL_ERROR); 01092 g_return_val_if_fail (strings[2], QSQL_ERROR); 01093 frame = kvp_frame_new (); 01094 /* columnNames = fields strings = values 01095 [0]=kvp_id, [1]=guid, [2]=path, [3]=type, [4]=value 01096 get type from type_string */ 01097 type = sql_to_kvp_helper (strings[3]); 01098 if (type == 0) 01099 { 01100 PERR (" invalid type returned from kvp table"); 01101 return QSQL_ERROR; 01102 } 01103 /* use the type to make a KvpValue from value */ 01104 value = string_to_kvp_value (strings[4], type); 01105 if (!value) 01106 { 01107 PERR (" invalid KvpValue for type: %d", type); 01108 return QSQL_ERROR; 01109 } 01110 /* add the KvpValue to the frame at path */ 01111 kvp_frame_set_value (frame, strings[2], value); 01112 /* index the frame under the entity GUID */ 01113 g_hash_table_insert (qsql_be->kvp_table, strings[1], frame); 01114 /* index the guid under the kvp_id */ 01115 g_hash_table_insert (qsql_be->kvp_id, strings[0], strings[1]); 01116 errno = 0; 01117 max = strtol (strings[0], &tail, 0); 01118 if (errno == 0) 01119 { 01120 qsql_be->index = (max > qsql_be->index) ? max : qsql_be->index; 01121 } 01122 return SQLITE_OK; 01123 } 01124 01126 static void 01127 qsql_load_kvp (QSQLiteBackend * qsql_be) 01128 { 01129 struct QsqlBuilder qb; 01130 QofBackend *be; 01131 gint sq_code; 01132 01133 g_return_if_fail (qsql_be); 01134 sq_code = SQLITE_OK; 01135 be = (QofBackend *) qsql_be; 01136 qb.sql_str = 01137 g_strdup_printf ("SELECT kvp_id from %s;", QSQL_KVP_TABLE); 01138 sq_code = sqlite_exec (qsql_be->sqliteh, qb.sql_str, build_kvp_table, 01139 &qb, &qsql_be->err); 01140 /* catch older files without a sqlite_kvp table */ 01141 if (sq_code == SQLITE_ERROR) 01142 { 01143 g_free (qb.sql_str); 01144 qb.sql_str = 01145 g_strdup_printf ("CREATE TABLE %s (%s, %s, %s, %s, %s, %s", 01146 QSQL_KVP_TABLE, "kvp_id int primary key not null", 01147 "guid char(32)", "path mediumtext", "type mediumtext", 01148 "value text", END_DB_VERSION); 01149 PINFO (" creating kvp table. sql=%s", qb.sql_str); 01150 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str, 01151 record_foreach, &qb, &qsql_be->err) != SQLITE_OK) 01152 { 01153 qsql_be->error = TRUE; 01154 PERR (" unable to create kvp table:%s", qsql_be->err); 01155 } 01156 } 01157 else if (sq_code != SQLITE_OK) 01158 { 01159 qof_error_set_be (be, qsql_be->err_create); 01160 qsql_be->error = TRUE; 01161 PERR (" error on KVP select:%s:%s:%d", qb.sql_str, qsql_be->err, sq_code); 01162 } 01163 g_free (qb.sql_str); 01164 } 01165 01167 static void 01168 qsql_class_foreach (QofObject * obj, gpointer data) 01169 { 01170 struct QsqlBuilder qb; 01171 QSQLiteBackend *qsql_be; 01172 QofBackend *be; 01173 01174 qsql_be = (QSQLiteBackend *) data; 01175 be = (QofBackend *) qsql_be; 01176 qb.qsql_be = qsql_be; 01177 qb.e_type = obj->e_type; 01178 ENTER (" obj_type=%s", qb.e_type); 01179 switch (qsql_be->stm_type) 01180 { 01181 case SQL_NONE: 01182 case SQL_INSERT: 01183 case SQL_DELETE: 01184 case SQL_UPDATE: 01185 { 01186 break; 01187 } 01188 case SQL_CREATE: 01189 { 01190 /* KVP is handled separately */ 01191 qb.sql_str = 01192 g_strdup_printf ("CREATE TABLE %s (", obj->e_type); 01193 qof_class_param_foreach (obj->e_type, string_param_foreach, 01194 &qb); 01195 qb.sql_str = add_to_sql (qb.sql_str, END_DB_VERSION); 01196 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str, 01197 NULL, NULL, &qsql_be->err) != SQLITE_OK) 01198 { 01199 qof_error_set_be (be, qsql_be->err_create); 01200 qsql_be->error = TRUE; 01201 PERR (" error on SQL_CREATE:%s", qsql_be->err); 01202 } 01203 g_free (qb.sql_str); 01204 break; 01205 } 01206 case SQL_LOAD: 01207 { 01208 qb.sql_str = 01209 g_strdup_printf ("SELECT * FROM %s;", obj->e_type); 01210 PINFO (" sql=%s", qb.sql_str); 01211 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str, 01212 record_foreach, &qb, &qsql_be->err) != SQLITE_OK) 01213 { 01214 qsql_be->error = TRUE; 01215 PERR (" error on SQL_LOAD:%s", qsql_be->err); 01216 } 01217 break; 01218 } 01219 case SQL_WRITE: 01220 { 01221 if (!qof_book_not_saved (qsql_be->book)) 01222 break; 01223 qof_object_foreach (obj->e_type, qsql_be->book, check_state, 01224 &qb); 01225 break; 01226 } 01227 } 01228 LEAVE (" "); 01229 } 01230 01231 static void 01232 qsql_backend_createdb (QofBackend * be, QofSession * session) 01233 { 01234 FILE *f; 01235 QSQLiteBackend *qsql_be; 01236 struct QsqlBuilder qb; 01237 01238 g_return_if_fail (be || session); 01239 ENTER (" "); 01240 qsql_be = (QSQLiteBackend *) be; 01241 qsql_be->stm_type = SQL_CREATE; 01242 qb.qsql_be = qsql_be; 01243 qsql_be->book = qof_session_get_book (session); 01244 DEBUG (" create_file %s", qsql_be->fullpath); 01245 f = fopen (qsql_be->fullpath, "a+"); 01246 if (f) 01247 fclose (f); 01248 else 01249 { 01250 qof_error_set (session, qof_error_register 01251 (_("Unable to open the output file '%s' - do you have " 01252 "permission to create this file?"), TRUE)); 01253 qsql_be->error = TRUE; 01254 LEAVE (" unable to create new file '%s'", qsql_be->fullpath); 01255 return; 01256 } 01257 qsql_be->sqliteh = 01258 sqlite_open (qsql_be->fullpath, 0644, &qsql_be->err); 01259 if (!qsql_be->sqliteh) 01260 { 01261 qof_error_set_be (be, qsql_be->err_create); 01262 qsql_be->error = TRUE; 01263 LEAVE (" unable to open sqlite:%s", qsql_be->err); 01264 return; 01265 } 01266 qof_object_foreach_type (qsql_class_foreach, qsql_be); 01267 /* create the KVP table here 01268 preset table name, internal_id, guid_as_string, path, type, value 01269 */ 01270 qb.sql_str = 01271 g_strdup_printf ("CREATE TABLE %s (%s, %s, %s, %s, %s, %s", 01272 QSQL_KVP_TABLE, "kvp_id int primary key not null", 01273 "guid char(32)", "path mediumtext", "type mediumtext", 01274 "value text", END_DB_VERSION); 01275 PINFO (" sql=%s", qb.sql_str); 01276 if (sqlite_exec (qsql_be->sqliteh, qb.sql_str, 01277 record_foreach, &qb, &qsql_be->err) != SQLITE_OK) 01278 { 01279 qsql_be->error = TRUE; 01280 PERR (" unable to create kvp table:%s", qsql_be->err); 01281 } 01282 g_free (qb.sql_str); 01283 LEAVE (" "); 01284 } 01285 01286 static void 01287 qsql_backend_opendb (QofBackend * be, QofSession * session) 01288 { 01289 QSQLiteBackend *qsql_be; 01290 01291 g_return_if_fail (be || session); 01292 ENTER (" "); 01293 qsql_be = (QSQLiteBackend *) be; 01294 qsql_be->sqliteh = 01295 sqlite_open (qsql_be->fullpath, 0666, &qsql_be->err); 01296 if (!qsql_be->sqliteh) 01297 { 01298 qof_error_set_be (be, qof_error_register 01299 (_("Unable to open the sqlite database '%s'."), TRUE)); 01300 qsql_be->error = TRUE; 01301 PERR (" %s", qsql_be->err); 01302 } 01303 LEAVE (" %s", qsql_be->fullpath); 01304 } 01305 01306 static void 01307 qsqlite_session_begin (QofBackend * be, QofSession * session, 01308 const gchar * book_path, gboolean ignore_lock, 01309 gboolean create_if_nonexistent) 01310 { 01311 QSQLiteBackend *qsql_be; 01312 gchar **pp; 01313 struct stat statinfo; 01314 gint stat_val; 01315 01316 g_return_if_fail (be); 01317 ENTER (" book_path=%s", book_path); 01318 qsql_be = (QSQLiteBackend *) be; 01319 qsql_be->fullpath = NULL; 01320 if (book_path == NULL) 01321 { 01322 qof_error_set_be (be, qof_error_register 01323 (_("Please provide a filename for sqlite."), FALSE)); 01324 qsql_be->error = TRUE; 01325 LEAVE (" bad URL"); 01326 return; 01327 } 01328 /* book_path => sqlite_file_name */ 01329 pp = g_strsplit (book_path, ":", 2); 01330 if (0 == safe_strcmp (pp[0], ACCESS_METHOD)) 01331 { 01332 qsql_be->fullpath = g_strdup (pp[1]); 01333 g_strfreev (pp); 01334 } 01335 else 01336 qsql_be->fullpath = g_strdup (book_path); 01337 be->fullpath = g_strdup (qsql_be->fullpath); 01338 PINFO (" final path = %s", qsql_be->fullpath); 01339 stat_val = g_stat (qsql_be->fullpath, &statinfo); 01340 if (!S_ISREG (statinfo.st_mode) || statinfo.st_size == 0) 01341 qsql_backend_createdb (be, session); 01342 if (!qsql_be->error) 01343 qsql_backend_opendb (be, session); 01344 if (qof_error_check_be (be) || qsql_be->error) 01345 { 01346 LEAVE (" open failed"); 01347 return; 01348 } 01349 qsql_be->create_handler = 01350 qof_event_register_handler (create_event, qsql_be); 01351 qsql_be->delete_handler = 01352 qof_event_register_handler (delete_event, qsql_be); 01353 LEAVE (" db=%s", qsql_be->fullpath); 01354 } 01355 01356 static void 01357 qsqlite_db_load (QofBackend * be, QofBook * book) 01358 { 01359 QSQLiteBackend *qsql_be; 01360 01361 g_return_if_fail (be); 01362 ENTER (" "); 01363 loading = TRUE; 01364 qsql_be = (QSQLiteBackend *) be; 01365 qsql_be->stm_type = SQL_LOAD; 01366 qsql_be->book = book; 01367 /* iterate over registered objects */ 01368 qof_object_foreach_type (qsql_class_foreach, qsql_be); 01369 qsql_load_kvp (qsql_be); 01370 loading = FALSE; 01371 LEAVE (" "); 01372 } 01373 01374 static void 01375 qsqlite_write_db (QofBackend * be, QofBook * book) 01376 { 01377 QSQLiteBackend *qsql_be; 01378 01379 g_return_if_fail (be); 01380 qsql_be = (QSQLiteBackend *) be; 01381 qsql_be->stm_type = SQL_WRITE; 01382 qsql_be->book = book; 01383 /* update each record with current state */ 01384 qof_object_foreach_type (qsql_class_foreach, qsql_be); 01385 /* update KVP */ 01386 } 01387 01388 static gboolean 01389 qsql_determine_file_type (const gchar * path) 01390 { 01391 if (!path) 01392 return FALSE; 01393 return TRUE; 01394 } 01395 01396 static void 01397 qsqlite_session_end (QofBackend * be) 01398 { 01399 QSQLiteBackend *qsql_be; 01400 01401 g_return_if_fail (be); 01402 qsql_be = (QSQLiteBackend *) be; 01403 if (qsql_be->sqliteh) 01404 sqlite_close (qsql_be->sqliteh); 01405 } 01406 01407 static void 01408 qsqlite_destroy_backend (QofBackend * be) 01409 { 01410 QSQLiteBackend *qsql_be; 01411 01412 g_return_if_fail (be); 01413 qsql_be = (QSQLiteBackend *) be; 01414 g_hash_table_destroy (qsql_be->kvp_table); 01415 g_hash_table_destroy (qsql_be->kvp_id); 01416 qof_event_unregister_handler (qsql_be->create_handler); 01417 qof_event_unregister_handler (qsql_be->delete_handler); 01418 g_free (be); 01419 g_free (qsql_be); 01420 } 01421 01422 static void 01423 qsql_provider_free (QofBackendProvider * prov) 01424 { 01425 prov->provider_name = NULL; 01426 prov->access_method = NULL; 01427 g_free (prov); 01428 } 01429 01445 static QofBackend * 01446 qsql_backend_new (void) 01447 { 01448 QSQLiteBackend *qsql_be; 01449 QofBackend *be; 01450 01451 ENTER (" "); 01452 qsql_be = g_new0 (QSQLiteBackend, 1); 01453 be = (QofBackend *) qsql_be; 01454 qof_backend_init (be); 01455 qsql_be->kvp_table = g_hash_table_new (g_str_hash, g_str_equal); 01456 qsql_be->kvp_id = g_hash_table_new (g_str_hash, g_str_equal); 01457 qsql_be->dbversion = QOF_OBJECT_VERSION; 01458 qsql_be->stm_type = SQL_NONE; 01459 qsql_be->err_delete = 01460 qof_error_register (_("Unable to delete record."), FALSE); 01461 qsql_be->err_create = 01462 qof_error_register (_("Unable to create record."), FALSE); 01463 qsql_be->err_insert = 01464 qof_error_register (_("Unable to insert a new record."), FALSE); 01465 qsql_be->err_update = 01466 qof_error_register (_("Unable to update existing record."), FALSE); 01467 be->session_begin = qsqlite_session_begin; 01468 01469 be->session_end = qsqlite_session_end; 01470 be->destroy_backend = qsqlite_destroy_backend; 01471 be->load = qsqlite_db_load; 01472 be->save_may_clobber_data = NULL; 01473 /* begin: create an empty entity if none exists, 01474 even if events are suspended. */ 01475 be->begin = qsql_create; 01476 /* commit: write to sqlite, commit undo record. */ 01477 be->commit = qsql_modify; 01478 be->rollback = NULL; 01479 /* would need a QofQuery back to QofSqlQuery conversion. */ 01480 be->compile_query = NULL; 01481 /* unused */ 01482 be->free_query = NULL; 01483 be->run_query = NULL; 01484 be->counter = NULL; 01485 /* The QOF SQLite backend is not multi-user - all QOF users are the same. */ 01486 be->events_pending = NULL; 01487 be->process_events = NULL; 01488 01489 be->sync = qsqlite_write_db; 01490 be->load_config = NULL; 01491 be->get_config = NULL; 01492 LEAVE (" "); 01493 return be; 01494 } 01495 01496 void 01497 qof_sqlite_provider_init (void) 01498 { 01499 QofBackendProvider *prov; 01500 01501 ENTER (" "); 01502 bindtextdomain (PACKAGE, LOCALE_DIR); 01503 prov = g_new0 (QofBackendProvider, 1); 01504 prov->provider_name = "QOF SQLite Backend Version 0.3"; 01505 prov->access_method = ACCESS_METHOD; 01506 prov->partial_book_supported = TRUE; 01507 prov->backend_new = qsql_backend_new; 01508 prov->check_data_type = qsql_determine_file_type; 01509 prov->provider_free = qsql_provider_free; 01510 qof_backend_register_provider (prov); 01511 LEAVE (" "); 01512 } 01513 01514 /* ================= END OF FILE =================== */