|
GnuCash 2.4.99
|
00001 /******************************************************************** 00002 * gnc-backend-dbi.c: load and save data to SQL via libdbi * 00003 * * 00004 * This program is free software; you can redistribute it and/or * 00005 * modify it under the terms of the GNU General Public License as * 00006 * published by the Free Software Foundation; either version 2 of * 00007 * the License, or (at your option) any later version. * 00008 * * 00009 * This program is distributed in the hope that it will be useful, * 00010 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 00011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 00012 * GNU General Public License for more details. * 00013 * * 00014 * You should have received a copy of the GNU General Public License* 00015 * along with this program; if not, contact: * 00016 * * 00017 * Free Software Foundation Voice: +1-617-542-5942 * 00018 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * 00019 * Boston, MA 02110-1301, USA gnu@gnu.org * 00020 \********************************************************************/ 00029 #include "config.h" 00030 00031 #include <errno.h> 00032 #include <glib.h> 00033 #include <glib/gstdio.h> 00034 #if !HAVE_GMTIME_R 00035 #include "gmtime_r.h" 00036 #endif 00037 00038 #include "gnc-backend-dbi-priv.h" 00039 00040 #include "qof.h" 00041 #include "qofquery-p.h" 00042 #include "qofquerycore-p.h" 00043 #include "Account.h" 00044 #include "TransLog.h" 00045 #include "gnc-engine.h" 00046 #include "SX-book.h" 00047 #include "Recurrence.h" 00048 00049 #include "gnc-gconf-utils.h" 00050 #include "gnc-uri-utils.h" 00051 #include "gnc-filepath-utils.h" 00052 #include "gnc-locale-utils.h" 00053 00054 #include "gnc-backend-dbi.h" 00055 00056 #ifdef S_SPLINT_S 00057 #include "splint-defs.h" 00058 #endif 00059 00060 #ifdef G_OS_WIN32 00061 #include <winsock2.h> 00062 #define GETPID() GetCurrentProcessId() 00063 #else 00064 #include <limits.h> 00065 #include <unistd.h> 00066 #define GETPID() getpid() 00067 #endif 00068 00069 #define GNC_HOST_NAME_MAX 255 00070 #define TRANSACTION_NAME "trans" 00071 00072 static QofLogModule log_module = G_LOG_DOMAIN; 00073 00074 static gchar lock_table[] = "gnclock"; 00075 00076 #define FILE_URI_TYPE "file" 00077 #define FILE_URI_PREFIX (FILE_URI_TYPE "://") 00078 #define SQLITE3_URI_TYPE "sqlite3" 00079 #define SQLITE3_URI_PREFIX (SQLITE3_URI_TYPE "://") 00080 #define PGSQL_DEFAULT_PORT 5432 00081 00082 static /*@ null @*/ gchar* conn_create_table_ddl_sqlite3( GncSqlConnection* conn, 00083 const gchar* table_name, 00084 const GList* col_info_list ); 00085 static GSList* conn_get_table_list( dbi_conn conn, const gchar* dbname ); 00086 static GSList* conn_get_table_list_sqlite3( dbi_conn conn, const gchar* dbname ); 00087 static void append_sqlite3_col_def( GString* ddl, GncSqlColumnInfo* info ); 00088 static GSList *conn_get_index_list_sqlite3( dbi_conn conn ); 00089 static provider_functions_t provider_sqlite3 = 00090 { 00091 conn_create_table_ddl_sqlite3, 00092 conn_get_table_list_sqlite3, 00093 append_sqlite3_col_def, 00094 conn_get_index_list_sqlite3 00095 }; 00096 #define SQLITE3_TIMESPEC_STR_FORMAT "%04d%02d%02d%02d%02d%02d" 00097 00098 static /*@ null @*/ gchar* conn_create_table_ddl_mysql( GncSqlConnection* conn, 00099 const gchar* table_name, 00100 const GList* col_info_list ); 00101 static void append_mysql_col_def( GString* ddl, GncSqlColumnInfo* info ); 00102 static GSList *conn_get_index_list_mysql( dbi_conn conn ); 00103 static provider_functions_t provider_mysql = 00104 { 00105 conn_create_table_ddl_mysql, 00106 conn_get_table_list, 00107 append_mysql_col_def, 00108 conn_get_index_list_mysql 00109 }; 00110 #define MYSQL_TIMESPEC_STR_FORMAT "%04d%02d%02d%02d%02d%02d" 00111 00112 static /*@ null @*/ gchar* conn_create_table_ddl_pgsql( GncSqlConnection* conn, 00113 const gchar* table_name, 00114 const GList* col_info_list ); 00115 static GSList* conn_get_table_list_pgsql( dbi_conn conn, const gchar* dbname ); 00116 static void append_pgsql_col_def( GString* ddl, GncSqlColumnInfo* info ); 00117 static GSList *conn_get_index_list_pgsql( dbi_conn conn ); 00118 00119 static provider_functions_t provider_pgsql = 00120 { 00121 conn_create_table_ddl_pgsql, 00122 conn_get_table_list_pgsql, 00123 append_pgsql_col_def, 00124 conn_get_index_list_pgsql 00125 }; 00126 #define PGSQL_TIMESPEC_STR_FORMAT "%04d%02d%02d %02d%02d%02d" 00127 00128 static gboolean gnc_dbi_lock_database( QofBackend *qbe, gboolean ignore_lock ); 00129 static void gnc_dbi_unlock( QofBackend *qbe ); 00130 static gboolean save_may_clobber_data( QofBackend* qbe ); 00131 00132 static /*@ null @*/ gchar* create_index_ddl( GncSqlConnection* conn, 00133 const gchar* index_name, 00134 const gchar* table_name, 00135 const GncSqlColumnTableEntry* col_table ); 00136 static /*@ null @*/ gchar* add_columns_ddl( GncSqlConnection* conn, 00137 const gchar* table_name, 00138 GList* col_info_list ); 00139 static GncSqlConnection* create_dbi_connection( /*@ observer @*/ provider_functions_t* provider, /*@ observer @*/ QofBackend* qbe, /*@ observer @*/ dbi_conn conn ); 00140 static GncDbiTestResult conn_test_dbi_library( dbi_conn conn ); 00141 #define GNC_DBI_PROVIDER_SQLITE (&provider_sqlite3) 00142 #define GNC_DBI_PROVIDER_MYSQL (&provider_mysql) 00143 #define GNC_DBI_PROVIDER_PGSQL (&provider_pgsql) 00144 00145 00146 #define DBI_MAX_CONN_ATTEMPTS 5 00147 00148 /* ================================================================= */ 00149 00150 /* Free the contents of a GSList, then free the list. Don't use this 00151 * if the elements of the list weren't created with g_new! */ 00152 static void 00153 gnc_table_slist_free( GSList *table_list ) 00154 { 00155 GSList *list; 00156 for ( list = table_list; list != NULL; list = g_slist_next( list )) 00157 { 00158 g_free( list->data ); 00159 } 00160 g_slist_free( table_list ); 00161 } 00162 00163 static void 00164 gnc_dbi_set_error( GncDbiSqlConnection* dbi_conn, gint last_error, 00165 gint error_repeat, gboolean retry ) 00166 { 00167 g_return_if_fail( dbi_conn != NULL ); 00168 00169 dbi_conn->last_error = last_error; 00170 if ( error_repeat > 0 ) 00171 dbi_conn->error_repeat = dbi_conn->error_repeat + error_repeat; 00172 else 00173 dbi_conn->error_repeat = 0; 00174 dbi_conn->retry = retry; 00175 } 00176 00177 static void 00178 gnc_dbi_init_error( GncDbiSqlConnection* dbi_conn ) 00179 { 00180 gnc_dbi_set_error( dbi_conn, ERR_BACKEND_NO_ERR, 0, FALSE ); 00181 } 00182 00183 /* Check if the dbi connection is valid. If not attempt to re-establish it 00184 * Returns TRUE is there is a valid connection in the end or FALSE otherwise 00185 */ 00186 static gboolean 00187 gnc_dbi_verify_conn( GncDbiSqlConnection* dbi_conn ) 00188 { 00189 if ( dbi_conn->conn_ok ) 00190 return TRUE; 00191 00192 /* We attempt to connect only once here. The error function will automatically 00193 * re-attempt up until DBI_MAX_CONN_ATTEMPTS time to connect if this call fails. 00194 * After all these attempts, conn_ok will indicate if there is a valid connection 00195 * or not. 00196 */ 00197 gnc_dbi_init_error( dbi_conn ); 00198 dbi_conn->conn_ok = TRUE; 00199 (void)dbi_conn_connect( dbi_conn->conn ); 00200 00201 return dbi_conn->conn_ok; 00202 } 00203 00204 /* ================================================================= */ 00205 00206 static void 00207 create_tables_cb( const gchar* type, gpointer data_p, gpointer be_p ) 00208 { 00209 GncSqlObjectBackend* pData = data_p; 00210 GncDbiBackend* be = be_p; 00211 00212 g_return_if_fail( type != NULL && data_p != NULL && be_p != NULL ); 00213 g_return_if_fail( pData->version == GNC_SQL_BACKEND_VERSION ); 00214 00215 if ( pData->create_tables != NULL ) 00216 { 00217 (pData->create_tables)( &be->sql_be ); 00218 } 00219 } 00220 00221 static void 00222 sqlite3_error_fn( dbi_conn conn, /*@ unused @*/ void* user_data ) 00223 { 00224 GncDbiBackend *be = (GncDbiBackend*)user_data; 00225 GncDbiSqlConnection *dbi_conn = (GncDbiSqlConnection*)be->sql_be.conn; 00226 const gchar* msg; 00227 00228 (void)dbi_conn_error( conn, &msg ); 00229 PERR( "DBI error: %s\n", msg ); 00230 gnc_dbi_set_error( conn, ERR_BACKEND_MISC, 0, FALSE ); 00231 } 00232 00233 static void 00234 gnc_dbi_sqlite3_session_begin( QofBackend *qbe, QofSession *session, 00235 const gchar *book_id, gboolean ignore_lock, 00236 gboolean create, gboolean force ) 00237 { 00238 GncDbiBackend *be = (GncDbiBackend*)qbe; 00239 gint result; 00240 gchar* dirname = NULL; 00241 gchar* basename = NULL; 00242 gchar *filepath = NULL; 00243 gchar *msg = " "; 00244 gboolean file_exists; 00245 GncDbiTestResult dbi_test_result = GNC_DBI_PASS; 00246 00247 g_return_if_fail( qbe != NULL ); 00248 g_return_if_fail( session != NULL ); 00249 g_return_if_fail( book_id != NULL ); 00250 00251 ENTER (" "); 00252 00253 /* Remove uri type if present */ 00254 filepath = gnc_uri_get_path ( book_id ); 00255 file_exists = g_file_test( filepath, 00256 G_FILE_TEST_IS_REGULAR | G_FILE_TEST_EXISTS ); 00257 if ( !create && !file_exists ) 00258 { 00259 qof_backend_set_error( qbe, ERR_FILEIO_FILE_NOT_FOUND ); 00260 qof_backend_set_message(qbe, "Sqlite3 file %s not found", filepath); 00261 PWARN ("Sqlite3 file %s not found", filepath); 00262 goto exit; 00263 } 00264 00265 if ( create && !force && file_exists ) 00266 { 00267 qof_backend_set_error (qbe, ERR_BACKEND_STORE_EXISTS); 00268 msg = "Might clobber, no force"; 00269 PWARN ("%s", msg); 00270 goto exit; 00271 } 00272 00273 00274 if ( be->conn != NULL ) 00275 { 00276 dbi_conn_close( be->conn ); 00277 } 00278 be->conn = dbi_conn_new( "sqlite3" ); 00279 if ( be->conn == NULL ) 00280 { 00281 PERR( "Unable to create sqlite3 dbi connection\n" ); 00282 qof_backend_set_error( qbe, ERR_BACKEND_BAD_URL ); 00283 goto exit; 00284 } 00285 00286 dirname = g_path_get_dirname( filepath ); 00287 basename = g_path_get_basename( filepath ); 00288 dbi_conn_error_handler( be->conn, sqlite3_error_fn, be ); 00289 /* dbi-sqlite3 documentation says that sqlite3 doesn't take a "host" option */ 00290 result = dbi_conn_set_option( be->conn, "host", "localhost" ); 00291 if ( result < 0 ) 00292 { 00293 PERR( "Error setting 'host' option\n" ); 00294 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 00295 goto exit; 00296 } 00297 result = dbi_conn_set_option( be->conn, "dbname", basename ); 00298 if ( result < 0 ) 00299 { 00300 PERR( "Error setting 'dbname' option\n" ); 00301 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 00302 goto exit; 00303 } 00304 result = dbi_conn_set_option( be->conn, "sqlite3_dbdir", dirname ); 00305 if ( result < 0 ) 00306 { 00307 PERR( "Error setting 'sqlite3_dbdir' option\n" ); 00308 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 00309 goto exit; 00310 } 00311 result = dbi_conn_connect( be->conn ); 00312 00313 if ( result < 0 ) 00314 { 00315 PERR( "Unable to connect to %s: %d\n", book_id, result ); 00316 qof_backend_set_error( qbe, ERR_BACKEND_BAD_URL ); 00317 goto exit; 00318 } 00319 00320 dbi_test_result = conn_test_dbi_library( be->conn ); 00321 switch ( dbi_test_result ) 00322 { 00323 case GNC_DBI_PASS: 00324 break; 00325 00326 case GNC_DBI_FAIL_SETUP: 00327 qof_backend_set_error( qbe, ERR_SQL_DBI_UNTESTABLE ); 00328 qof_backend_set_message( qbe, 00329 "SQLite3: Failed to setup for large number test" ); 00330 break; 00331 00332 case GNC_DBI_FAIL_TEST: 00333 qof_backend_set_error( qbe, ERR_SQL_BAD_DBI ); 00334 qof_backend_set_message( qbe, 00335 "SQLite3 DBI library fails large number test" ); 00336 break; 00337 } 00338 if ( dbi_test_result != GNC_DBI_PASS ) 00339 { 00340 if ( create && !file_exists ) /* File didn't exist before, but it */ 00341 { 00342 /* does now, and we don't want to */ 00343 dbi_conn_close( be->conn );/* leave it lying around. */ 00344 be->conn = NULL; 00345 g_unlink( filepath ); 00346 } 00347 msg = "Bad DBI Library"; 00348 goto exit; 00349 } 00350 if ( !gnc_dbi_lock_database( qbe, ignore_lock ) ) 00351 { 00352 qof_backend_set_error( qbe, ERR_BACKEND_LOCKED ); 00353 msg = "Locked"; 00354 goto exit; 00355 } 00356 00357 if ( be->sql_be.conn != NULL ) 00358 { 00359 gnc_sql_connection_dispose( be->sql_be.conn ); 00360 } 00361 be->sql_be.conn = create_dbi_connection( GNC_DBI_PROVIDER_SQLITE, qbe, be->conn ); 00362 be->sql_be.timespec_format = SQLITE3_TIMESPEC_STR_FORMAT; 00363 00364 /* We should now have a proper session set up. 00365 * Let's start logging */ 00366 xaccLogSetBaseName (filepath); 00367 PINFO ("logpath=%s", filepath ? filepath : "(null)"); 00368 00369 exit: 00370 if ( filepath != NULL ) g_free ( filepath ); 00371 if ( basename != NULL ) g_free( basename ); 00372 if ( dirname != NULL ) g_free( dirname ); 00373 LEAVE ( "%s", msg ); 00374 } 00375 00376 static GSList* 00377 conn_get_index_list_sqlite3( dbi_conn conn ) 00378 { 00379 GSList *list = NULL; 00380 const gchar *errmsg; 00381 dbi_result result = dbi_conn_query( conn, "SELECT name FROM sqlite_master WHERE type = 'index' AND name NOT LIKE 'sqlite_autoindex%'" ); 00382 if ( dbi_conn_error( conn, &errmsg ) != DBI_ERROR_NONE ) 00383 { 00384 g_print( "Index Table Retrieval Error: %s\n", errmsg ); 00385 return NULL; 00386 } 00387 while ( dbi_result_next_row( result ) != 0 ) 00388 { 00389 const gchar* index_name; 00390 00391 index_name = dbi_result_get_string_idx( result, 1 ); 00392 list = g_slist_prepend( list, strdup( index_name ) ); 00393 } 00394 dbi_result_free( result ); 00395 return list; 00396 } 00397 00398 static void 00399 mysql_error_fn( dbi_conn conn, void* user_data ) 00400 { 00401 GncDbiBackend *be = (GncDbiBackend*)user_data; 00402 GncDbiSqlConnection *dbi_conn = (GncDbiSqlConnection*)be->sql_be.conn; 00403 const gchar* msg; 00404 gint err_num; 00405 00406 err_num = dbi_conn_error( conn, &msg ); 00407 00408 /* Note: the sql connection may not have been initialized yet 00409 * so let's be careful with using it 00410 */ 00411 00412 /* Database doesn't exist. When this error is triggered the 00413 * GncDbiSqlConnection may not exist yet either, so don't use it here 00414 */ 00415 if ( err_num == 1049 ) // Database doesn't exist 00416 { 00417 PINFO( "DBI error: %s\n", msg ); 00418 be->exists = FALSE; 00419 return; 00420 } 00421 00422 /* All the other error handling code assumes the GncDbiSqlConnection 00423 * has been initialized. So let's assert it exits here, otherwise 00424 * simply return. 00425 */ 00426 if (!dbi_conn) 00427 { 00428 PINFO( "DBI error: %s\n", msg ); 00429 PINFO( "Note: GbcDbiSqlConnection not yet initialized. Skipping further error processing." ); 00430 return; 00431 } 00432 00433 /* Test for other errors */ 00434 if ( err_num == 2006 ) // Server has gone away 00435 { 00436 PINFO( "DBI error: %s - Reconnecting...\n", msg ); 00437 if (dbi_conn) 00438 gnc_dbi_set_error( dbi_conn, ERR_BACKEND_CONN_LOST, 1, TRUE ); 00439 dbi_conn->conn_ok = TRUE; 00440 (void)dbi_conn_connect( conn ); 00441 } 00442 else if ( err_num == 2003 ) // Unable to connect 00443 { 00444 if (dbi_conn->error_repeat >= DBI_MAX_CONN_ATTEMPTS ) 00445 { 00446 PERR( "DBI error: %s - Giving up after %d consecutive attempts.\n", msg, DBI_MAX_CONN_ATTEMPTS ); 00447 if (dbi_conn) 00448 gnc_dbi_set_error( dbi_conn, ERR_BACKEND_CANT_CONNECT, 0, FALSE ); 00449 dbi_conn->conn_ok = FALSE; 00450 } 00451 else 00452 { 00453 PINFO( "DBI error: %s - Reconnecting...\n", msg ); 00454 if (dbi_conn) 00455 gnc_dbi_set_error( dbi_conn, ERR_BACKEND_CANT_CONNECT, 1, TRUE ); 00456 dbi_conn->conn_ok = TRUE; 00457 (void)dbi_conn_connect( conn ); 00458 } 00459 } 00460 else // Any other error 00461 { 00462 PERR( "DBI error: %s\n", msg ); 00463 if (dbi_conn) 00464 gnc_dbi_set_error( dbi_conn, ERR_BACKEND_MISC, 0, FALSE ); 00465 } 00466 } 00467 00480 static gboolean 00481 set_standard_connection_options( QofBackend* qbe, dbi_conn conn, const gchar* host, int port, 00482 const gchar* dbname, const gchar* username, const gchar* password ) 00483 { 00484 gint result; 00485 00486 result = dbi_conn_set_option( conn, "host", host ); 00487 if ( result < 0 ) 00488 { 00489 PERR( "Error setting 'host' option\n" ); 00490 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 00491 return FALSE; 00492 } 00493 result = dbi_conn_set_option_numeric( conn, "port", port ); 00494 if ( result < 0 ) 00495 { 00496 PERR( "Error setting 'port' option\n" ); 00497 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 00498 return FALSE; 00499 } 00500 result = dbi_conn_set_option( conn, "dbname", dbname ); 00501 if ( result < 0 ) 00502 { 00503 PERR( "Error setting 'dbname' option\n" ); 00504 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 00505 return FALSE; 00506 } 00507 result = dbi_conn_set_option( conn, "username", username ); 00508 if ( result < 0 ) 00509 { 00510 PERR( "Error setting 'username' option\n" ); 00511 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 00512 return FALSE; 00513 } 00514 result = dbi_conn_set_option( conn, "password", password ); 00515 if ( result < 0 ) 00516 { 00517 PERR( "Error setting 'password' option\n" ); 00518 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 00519 return FALSE; 00520 } 00521 00522 return TRUE; 00523 } 00524 00525 00526 static gboolean 00527 gnc_dbi_lock_database ( QofBackend* qbe, gboolean ignore_lock ) 00528 { 00529 00530 GncDbiBackend *qe = (GncDbiBackend*)qbe; 00531 dbi_conn dcon = qe->conn; 00532 dbi_result result; 00533 const gchar *dbname = dbi_conn_get_option( dcon, "dbname" ); 00534 /* Create the table if it doesn't exist */ 00535 result = dbi_conn_get_table_list( dcon, dbname, lock_table); 00536 if (!( result && dbi_result_get_numrows( result ) )) 00537 { 00538 if ( result ) 00539 { 00540 dbi_result_free( result ); 00541 result = NULL; 00542 } 00543 result = dbi_conn_queryf( dcon, "CREATE TABLE %s ( Hostname varchar(%d), PID int )", lock_table, GNC_HOST_NAME_MAX ); 00544 if ( dbi_conn_error( dcon, NULL ) ) 00545 { 00546 const gchar *errstr; 00547 dbi_conn_error( dcon, &errstr ); 00548 PERR( "Error %s creating lock table", errstr ); 00549 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 00550 if ( result ) 00551 { 00552 dbi_result_free( result ); 00553 result = NULL; 00554 } 00555 return FALSE; 00556 } 00557 if ( result ) 00558 { 00559 dbi_result_free( result ); 00560 result = NULL; 00561 } 00562 } 00563 if (result) 00564 { 00565 dbi_result_free( result ); 00566 result = NULL; 00567 } 00568 00569 /* Protect everything with a single transaction to prevent races */ 00570 if ( (result = dbi_conn_query( dcon, "BEGIN" )) ) 00571 { 00572 /* Check for an existing entry; delete it if ignore_lock is true, otherwise fail */ 00573 gchar hostname[ GNC_HOST_NAME_MAX + 1 ]; 00574 if (result) 00575 { 00576 dbi_result_free( result ); 00577 result = NULL; 00578 } 00579 result = dbi_conn_queryf( dcon, "SELECT * FROM %s", lock_table ); 00580 if ( result && dbi_result_get_numrows( result ) ) 00581 { 00582 dbi_result_free( result ); 00583 result = NULL; 00584 if ( !ignore_lock ) 00585 { 00586 qof_backend_set_error( qbe, ERR_BACKEND_LOCKED ); 00587 /* FIXME: After enhancing the qof_backend_error mechanism, report in the dialog what is the hostname of the machine holding the lock. */ 00588 dbi_conn_query( dcon, "ROLLBACK" ); 00589 return FALSE; 00590 } 00591 result = dbi_conn_queryf( dcon, "DELETE FROM %s", lock_table ); 00592 if ( !result) 00593 { 00594 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 00595 qof_backend_set_message( qbe, "Failed to delete lock record" ); 00596 result = dbi_conn_query( dcon, "ROLLBACK" ); 00597 if (result) 00598 { 00599 dbi_result_free( result ); 00600 result = NULL; 00601 } 00602 return FALSE; 00603 } 00604 if (result) 00605 { 00606 dbi_result_free( result ); 00607 result = NULL; 00608 } 00609 } 00610 /* Add an entry and commit the transaction */ 00611 memset( hostname, 0, sizeof(hostname) ); 00612 gethostname( hostname, GNC_HOST_NAME_MAX ); 00613 result = dbi_conn_queryf( dcon, 00614 "INSERT INTO %s VALUES ('%s', '%d')", 00615 lock_table, hostname, (int)GETPID() ); 00616 if ( !result) 00617 { 00618 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 00619 qof_backend_set_message( qbe, "Failed to create lock record" ); 00620 result = dbi_conn_query( dcon, "ROLLBACK" ); 00621 if (result) 00622 { 00623 dbi_result_free( result ); 00624 result = NULL; 00625 } 00626 return FALSE; 00627 } 00628 if (result) 00629 { 00630 dbi_result_free( result ); 00631 result = NULL; 00632 } 00633 result = dbi_conn_query( dcon, "COMMIT" ); 00634 if (result) 00635 { 00636 dbi_result_free( result ); 00637 result = NULL; 00638 } 00639 return TRUE; 00640 } 00641 /* Couldn't get a transaction (probably couldn't get a lock), so fail */ 00642 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 00643 qof_backend_set_message( qbe, "SQL Backend failed to obtain a transaction" ); 00644 if (result) 00645 { 00646 dbi_result_free( result ); 00647 result = NULL; 00648 } 00649 return FALSE; 00650 } 00651 static void 00652 gnc_dbi_unlock( QofBackend *qbe ) 00653 { 00654 GncDbiBackend *qe = (GncDbiBackend*)qbe; 00655 dbi_conn dcon = qe->conn; 00656 dbi_result result; 00657 const gchar *dbname = NULL; 00658 00659 g_return_if_fail( dcon != NULL ); 00660 g_return_if_fail( dbi_conn_error( dcon, NULL ) == 0 ); 00661 00662 dbname = dbi_conn_get_option( dcon, "dbname" ); 00663 /* Check if the lock table exists */ 00664 g_return_if_fail( dbname != NULL ); 00665 result = dbi_conn_get_table_list( dcon, dbname, lock_table); 00666 if (!( result && dbi_result_get_numrows( result ) )) 00667 { 00668 if (result) 00669 { 00670 dbi_result_free( result ); 00671 result = NULL; 00672 } 00673 PWARN("No lock table in database, so not unlocking it."); 00674 return; 00675 } 00676 dbi_result_free( result ); 00677 00678 result = dbi_conn_query( dcon, "BEGIN" ); 00679 if ( result ) 00680 { 00681 /* Delete the entry if it's our hostname and PID */ 00682 gchar hostname[ GNC_HOST_NAME_MAX + 1 ]; 00683 00684 dbi_result_free( result ); 00685 result = NULL; 00686 memset( hostname, 0, sizeof(hostname) ); 00687 gethostname( hostname, GNC_HOST_NAME_MAX ); 00688 result = dbi_conn_queryf( dcon, "SELECT * FROM %s WHERE Hostname = '%s' AND PID = '%d'", lock_table, hostname, (int)GETPID() ); 00689 if ( result && dbi_result_get_numrows( result ) ) 00690 { 00691 if (result) 00692 { 00693 dbi_result_free( result ); 00694 result = NULL; 00695 } 00696 result = dbi_conn_queryf( dcon, "DELETE FROM %s", lock_table ); 00697 if ( !result) 00698 { 00699 PERR("Failed to delete the lock entry"); 00700 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 00701 result = dbi_conn_query( dcon, "ROLLBACK" ); 00702 if (result) 00703 { 00704 dbi_result_free( result ); 00705 result = NULL; 00706 } 00707 return; 00708 } 00709 else 00710 { 00711 dbi_result_free( result ); 00712 result = NULL; 00713 } 00714 result = dbi_conn_query( dcon, "COMMIT" ); 00715 if (result) 00716 { 00717 dbi_result_free( result ); 00718 result = NULL; 00719 } 00720 return; 00721 } 00722 result = dbi_conn_query( dcon, "ROLLBACK" ); 00723 if (result) 00724 { 00725 dbi_result_free( result ); 00726 result = NULL; 00727 } 00728 PWARN("There was no lock entry in the Lock table"); 00729 return; 00730 } 00731 if (result) 00732 { 00733 dbi_result_free( result ); 00734 result = NULL; 00735 } 00736 PWARN("Unable to get a lock on LOCK, so failed to clear the lock entry."); 00737 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 00738 } 00739 00740 static void 00741 gnc_dbi_mysql_session_begin( QofBackend* qbe, QofSession *session, 00742 const gchar *book_id, gboolean ignore_lock, 00743 gboolean create, gboolean force ) 00744 { 00745 GncDbiBackend *be = (GncDbiBackend*)qbe; 00746 gchar* protocol = NULL; 00747 gchar* host = NULL; 00748 gchar* dbname = NULL; 00749 gchar* username = NULL; 00750 gchar* password = NULL; 00751 gchar* basename = NULL; 00752 gchar* translog_path = NULL; 00753 gint portnum = 0; 00754 gint result; 00755 gboolean success = FALSE; 00756 GncDbiTestResult dbi_test_result = GNC_DBI_PASS; 00757 00758 g_return_if_fail( qbe != NULL ); 00759 g_return_if_fail( session != NULL ); 00760 g_return_if_fail( book_id != NULL ); 00761 00762 ENTER (" "); 00763 00764 /* Split the book-id 00765 * Format is protocol://username:password@hostname:port/dbname 00766 where username, password and port are optional) */ 00767 gnc_uri_get_components ( book_id, &protocol, &host, &portnum, 00768 &username, &password, &dbname ); 00769 00770 // Try to connect to the db. If it doesn't exist and the create 00771 // flag is TRUE, we'll need to connect to the 'mysql' db and execute the 00772 // CREATE DATABASE ddl statement there. 00773 if ( be->conn != NULL ) 00774 { 00775 dbi_conn_close( be->conn ); 00776 } 00777 be->conn = dbi_conn_new( "mysql" ); 00778 if ( be->conn == NULL ) 00779 { 00780 PERR( "Unable to create mysql dbi connection\n" ); 00781 qof_backend_set_error( qbe, ERR_BACKEND_BAD_URL ); 00782 goto exit; 00783 } 00784 dbi_conn_error_handler( be->conn, mysql_error_fn, be ); 00785 if ( !set_standard_connection_options( qbe, be->conn, host, portnum, dbname, username, password ) ) 00786 { 00787 goto exit; 00788 } 00789 be->exists = TRUE; 00790 result = dbi_conn_connect( be->conn ); 00791 if ( result == 0 ) 00792 { 00793 dbi_test_result = conn_test_dbi_library( be->conn ); 00794 switch ( dbi_test_result ) 00795 { 00796 case GNC_DBI_PASS: 00797 break; 00798 00799 case GNC_DBI_FAIL_SETUP: 00800 qof_backend_set_error( qbe, ERR_SQL_DBI_UNTESTABLE ); 00801 qof_backend_set_message( qbe, 00802 "DBI library large number test incomplete" ); 00803 break; 00804 00805 case GNC_DBI_FAIL_TEST: 00806 qof_backend_set_error( qbe, ERR_SQL_BAD_DBI ); 00807 qof_backend_set_message( qbe, 00808 "DBI library fails large number test" ); 00809 break; 00810 } 00811 if ( GNC_DBI_PASS != dbi_test_result ) 00812 { 00813 goto exit; 00814 } 00815 if (create && !force && save_may_clobber_data( qbe ) ) 00816 { 00817 qof_backend_set_error ( qbe, ERR_BACKEND_STORE_EXISTS ); 00818 PWARN("Databse already exists, Might clobber it."); 00819 goto exit; 00820 } 00821 00822 success = gnc_dbi_lock_database ( qbe, ignore_lock ); 00823 } 00824 else 00825 { 00826 00827 if ( be->exists ) 00828 { 00829 PERR( "Unable to connect to database '%s'\n", dbname ); 00830 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 00831 goto exit; 00832 } 00833 00834 // The db does not already exist. Connect to the 'mysql' db and try to create it. 00835 if ( create ) 00836 { 00837 dbi_result dresult; 00838 result = dbi_conn_set_option( be->conn, "dbname", "mysql" ); 00839 if ( result < 0 ) 00840 { 00841 PERR( "Error setting 'dbname' option\n" ); 00842 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 00843 goto exit; 00844 } 00845 result = dbi_conn_connect( be->conn ); 00846 if ( result < 0 ) 00847 { 00848 PERR( "Unable to connect to 'mysql' database\n" ); 00849 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 00850 goto exit; 00851 } 00852 dresult = dbi_conn_queryf( be->conn, "CREATE DATABASE %s CHARACTER SET utf8", dbname ); 00853 if ( dresult == NULL ) 00854 { 00855 PERR( "Unable to create database '%s'\n", dbname ); 00856 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 00857 goto exit; 00858 } 00859 dbi_conn_close( be->conn ); 00860 00861 // Try again to connect to the db 00862 be->conn = dbi_conn_new( "mysql" ); 00863 if ( be->conn == NULL ) 00864 { 00865 PERR( "Unable to create mysql dbi connection\n" ); 00866 qof_backend_set_error( qbe, ERR_BACKEND_BAD_URL ); 00867 goto exit; 00868 } 00869 dbi_conn_error_handler( be->conn, mysql_error_fn, be ); 00870 if ( !set_standard_connection_options( qbe, be->conn, host, 0, dbname, username, password ) ) 00871 { 00872 goto exit; 00873 } 00874 result = dbi_conn_connect( be->conn ); 00875 if ( result < 0 ) 00876 { 00877 PERR( "Unable to create database '%s'\n", dbname ); 00878 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 00879 goto exit; 00880 } 00881 dbi_test_result = conn_test_dbi_library( be->conn ); 00882 switch ( dbi_test_result ) 00883 { 00884 case GNC_DBI_PASS: 00885 break; 00886 00887 case GNC_DBI_FAIL_SETUP: 00888 qof_backend_set_error( qbe, ERR_SQL_DBI_UNTESTABLE ); 00889 qof_backend_set_message( qbe, 00890 "MySql: Failed to setup for large number test" ); 00891 break; 00892 00893 case GNC_DBI_FAIL_TEST: 00894 qof_backend_set_error( qbe, ERR_SQL_BAD_DBI ); 00895 qof_backend_set_message( qbe, 00896 "MySql DBI library fails large number test" ); 00897 break; 00898 } 00899 if ( dbi_test_result != GNC_DBI_PASS ) 00900 { 00901 dbi_conn_queryf( be->conn, "DROP DATABASE %s", dbname ); 00902 goto exit; 00903 } 00904 success = gnc_dbi_lock_database ( qbe, ignore_lock ); 00905 } 00906 else 00907 { 00908 qof_backend_set_error( qbe, ERR_BACKEND_NO_SUCH_DB ); 00909 qof_backend_set_message( qbe, "Database %s not found", dbname ); 00910 } 00911 } 00912 00913 if ( success ) 00914 { 00915 dbi_result dresult; 00916 00917 /* Set connection char set to utf8 */ 00918 dresult = dbi_conn_queryf( be->conn, "SET NAMES 'utf8'" ); 00919 if ( dresult == NULL ) 00920 { 00921 PERR( "Unable to set connection char set" ); 00922 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 00923 goto exit; 00924 } 00925 00926 if ( be->sql_be.conn != NULL ) 00927 { 00928 gnc_sql_connection_dispose( be->sql_be.conn ); 00929 } 00930 be->sql_be.conn = create_dbi_connection( GNC_DBI_PROVIDER_MYSQL, qbe, be->conn ); 00931 } 00932 be->sql_be.timespec_format = MYSQL_TIMESPEC_STR_FORMAT; 00933 00934 /* We should now have a proper session set up. 00935 * Let's start logging */ 00936 basename = g_strjoin("_", protocol, host, username, dbname, NULL); 00937 translog_path = gnc_build_translog_path (basename); 00938 xaccLogSetBaseName (translog_path); 00939 PINFO ("logpath=%s", translog_path ? translog_path : "(null)"); 00940 00941 exit: 00942 g_free( protocol ); 00943 g_free( host ); 00944 g_free( username ); 00945 g_free( password ); 00946 g_free( basename ); 00947 g_free( translog_path ); 00948 g_free( dbname ); 00949 00950 LEAVE (" "); 00951 } 00952 00953 static GSList* 00954 conn_get_index_list_mysql( dbi_conn conn ) 00955 { 00956 GSList *index_list = NULL; 00957 dbi_result table_list; 00958 const char *errmsg; 00959 const gchar *dbname = dbi_conn_get_option( conn, "dbname" ); 00960 g_return_val_if_fail( conn != NULL, NULL ); 00961 table_list = dbi_conn_get_table_list( conn, dbname, NULL ); 00962 if ( dbi_conn_error( conn, &errmsg ) != DBI_ERROR_NONE ) 00963 { 00964 g_print( "Table Retrieval Error: %s\n", errmsg ); 00965 return NULL; 00966 } 00967 while ( dbi_result_next_row( table_list ) != 0 ) 00968 { 00969 dbi_result result; 00970 const gchar *table_name = dbi_result_get_string_idx( table_list, 1 ); 00971 result = dbi_conn_queryf( conn, 00972 "SHOW INDEXES IN %s WHERE Key_name != 'PRIMARY'", 00973 table_name ); 00974 if ( dbi_conn_error( conn, &errmsg ) != DBI_ERROR_NONE ) 00975 { 00976 g_print( "Index Table Retrieval Error: %s\n", errmsg ); 00977 continue; 00978 } 00979 00980 while ( dbi_result_next_row( result ) != 0 ) 00981 { 00982 const gchar* index_name = dbi_result_get_string_idx( result, 3 ); 00983 index_list = g_slist_prepend( index_list, strdup( index_name ) ); 00984 } 00985 dbi_result_free( result ); 00986 } 00987 00988 return index_list; 00989 } 00990 00991 static void 00992 pgsql_error_fn( dbi_conn conn, void* user_data ) 00993 { 00994 GncDbiBackend *be = (GncDbiBackend*)user_data; 00995 GncDbiSqlConnection *dbi_conn = (GncDbiSqlConnection*)be->sql_be.conn; 00996 const gchar* msg; 00997 00998 (void)dbi_conn_error( conn, &msg ); 00999 if ( g_str_has_prefix( msg, "FATAL: database" ) && 01000 g_str_has_suffix( msg, "does not exist\n" ) ) 01001 { 01002 PINFO( "DBI error: %s\n", msg ); 01003 be->exists = FALSE; 01004 gnc_dbi_set_error( dbi_conn, ERR_BACKEND_NO_SUCH_DB, 0, FALSE ); 01005 } 01006 else if ( g_strrstr( msg, "server closed the connection unexpectedly" ) ) // Connection lost 01007 { 01008 if ( dbi_conn == NULL ) 01009 { 01010 PWARN( "DBI Error: Connection lost, connection pointer invalid"); 01011 return; 01012 } 01013 PINFO( "DBI error: %s - Reconnecting...\n", msg ); 01014 gnc_dbi_set_error( dbi_conn, ERR_BACKEND_CONN_LOST, 1, TRUE ); 01015 dbi_conn->conn_ok = TRUE; 01016 (void)dbi_conn_connect( conn ); 01017 } 01018 else if ( dbi_conn && 01019 ( g_str_has_prefix( msg, "connection pointer is NULL" ) || 01020 g_str_has_prefix(msg, "could not connect to server" ) ) ) // No connection 01021 { 01022 if (dbi_conn->error_repeat >= DBI_MAX_CONN_ATTEMPTS ) 01023 { 01024 PERR( "DBI error: %s - Giving up after %d consecutive attempts.\n", msg, DBI_MAX_CONN_ATTEMPTS ); 01025 gnc_dbi_set_error( dbi_conn, ERR_BACKEND_CANT_CONNECT, 0, FALSE ); 01026 dbi_conn->conn_ok = FALSE; 01027 } 01028 else 01029 { 01030 PINFO( "DBI error: %s - Reconnecting...\n", msg ); 01031 gnc_dbi_set_error( dbi_conn, ERR_BACKEND_CANT_CONNECT, 1, TRUE ); 01032 dbi_conn->conn_ok = TRUE; 01033 (void)dbi_conn_connect( conn ); 01034 } 01035 } 01036 else 01037 { 01038 PERR( "DBI error: %s\n", msg ); 01039 gnc_dbi_set_error( dbi_conn, ERR_BACKEND_MISC, 0, FALSE ); 01040 } 01041 } 01042 01043 static void 01044 gnc_dbi_postgres_session_begin( QofBackend *qbe, QofSession *session, 01045 const gchar *book_id, gboolean ignore_lock, 01046 gboolean create, gboolean force ) 01047 { 01048 GncDbiBackend *be = (GncDbiBackend*)qbe; 01049 gint result = 0; 01050 gchar* protocol = NULL; 01051 gchar* host = NULL; 01052 gchar *dbname = NULL, *dbnamelc = NULL; 01053 gchar* username = NULL; 01054 gchar* password = NULL; 01055 gchar* basename = NULL; 01056 gchar* translog_path = NULL; 01057 gboolean success = FALSE; 01058 gint portnum = 0; 01059 GncDbiTestResult dbi_test_result = GNC_DBI_PASS; 01060 01061 g_return_if_fail( qbe != NULL ); 01062 g_return_if_fail( session != NULL ); 01063 g_return_if_fail( book_id != NULL ); 01064 01065 ENTER (" "); 01066 01067 /* Split the book-id 01068 * Format is protocol://username:password@hostname:port/dbname 01069 where username, password and port are optional) */ 01070 gnc_uri_get_components ( book_id, &protocol, &host, &portnum, 01071 &username, &password, &dbname ); 01072 if ( portnum == 0 ) 01073 portnum = PGSQL_DEFAULT_PORT; 01074 /* Postgres's SQL interface coerces identifiers to lower case, but the 01075 * C interface is case-sensitive. This results in a mixed-case dbname 01076 * being created (with a lower case name) but then dbi can't conect to 01077 * it. To work around this, coerce the name to lowercase first. */ 01078 dbnamelc = g_utf8_strdown( dbname, -1 ); 01079 01080 // Try to connect to the db. If it doesn't exist and the create 01081 // flag is TRUE, we'll need to connect to the 'postgres' db and execute the 01082 // CREATE DATABASE ddl statement there. 01083 if ( be->conn != NULL ) 01084 { 01085 dbi_conn_close( be->conn ); 01086 } 01087 be->conn = dbi_conn_new( "pgsql" ); 01088 if ( be->conn == NULL ) 01089 { 01090 PERR( "Unable to create pgsql dbi connection\n" ); 01091 qof_backend_set_error( qbe, ERR_BACKEND_BAD_URL ); 01092 goto exit; 01093 } 01094 dbi_conn_error_handler( be->conn, pgsql_error_fn, be ); 01095 if ( !set_standard_connection_options( qbe, be->conn, host, portnum, dbnamelc, username, password ) ) 01096 { 01097 goto exit; 01098 } 01099 be->exists = TRUE; 01100 result = dbi_conn_connect( be->conn ); 01101 if ( result == 0 ) 01102 { 01103 dbi_test_result = conn_test_dbi_library( be->conn ); 01104 switch ( dbi_test_result ) 01105 { 01106 case GNC_DBI_PASS: 01107 break; 01108 01109 case GNC_DBI_FAIL_SETUP: 01110 qof_backend_set_error( qbe, ERR_SQL_DBI_UNTESTABLE ); 01111 qof_backend_set_message( qbe, 01112 "Postgresql: Failed to setup for large number test" ); 01113 break; 01114 01115 case GNC_DBI_FAIL_TEST: 01116 qof_backend_set_error( qbe, ERR_SQL_BAD_DBI ); 01117 qof_backend_set_message( qbe, 01118 "Postgresql DBI library fails large number test" ); 01119 break; 01120 } 01121 if ( dbi_test_result != GNC_DBI_PASS ) 01122 { 01123 goto exit; 01124 } 01125 if (create && !force && save_may_clobber_data( qbe ) ) 01126 { 01127 qof_backend_set_error ( qbe, ERR_BACKEND_STORE_EXISTS ); 01128 PWARN("Databse already exists, Might clobber it."); 01129 goto exit; 01130 } 01131 01132 success = gnc_dbi_lock_database ( qbe, ignore_lock ); 01133 } 01134 else 01135 { 01136 01137 if ( be->exists ) 01138 { 01139 PERR( "Unable to connect to database '%s'\n", dbname ); 01140 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 01141 goto exit; 01142 } 01143 01144 // The db does not already exist. Connect to the 'postgres' db and try to create it. 01145 if ( create ) 01146 { 01147 dbi_result dresult; 01148 result = dbi_conn_set_option( be->conn, "dbname", "postgres" ); 01149 if ( result < 0 ) 01150 { 01151 PERR( "Error setting 'dbname' option\n" ); 01152 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 01153 goto exit; 01154 } 01155 result = dbi_conn_connect( be->conn ); 01156 if ( result < 0 ) 01157 { 01158 PERR( "Unable to connect to 'postgres' database\n" ); 01159 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 01160 goto exit; 01161 } 01162 dresult = dbi_conn_queryf( be->conn, "CREATE DATABASE %s WITH TEMPLATE template0 ENCODING 'UTF8'", dbnamelc ); 01163 if ( dresult == NULL ) 01164 { 01165 PERR( "Unable to create database '%s'\n", dbname ); 01166 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 01167 goto exit; 01168 } 01169 dbi_conn_queryf( be->conn, "ALTER DATABASE %s SET standard_conforming_strings TO on", dbnamelc ); 01170 dbi_conn_close( be->conn ); 01171 01172 // Try again to connect to the db 01173 be->conn = dbi_conn_new( "pgsql" ); 01174 if ( be->conn == NULL ) 01175 { 01176 PERR( "Unable to create pgsql dbi connection\n" ); 01177 qof_backend_set_error( qbe, ERR_BACKEND_BAD_URL ); 01178 goto exit; 01179 } 01180 dbi_conn_error_handler( be->conn, pgsql_error_fn, be ); 01181 if ( !set_standard_connection_options( qbe, be->conn, host, PGSQL_DEFAULT_PORT, dbnamelc, username, password ) ) 01182 { 01183 goto exit; 01184 } 01185 result = dbi_conn_connect( be->conn ); 01186 if ( result < 0 ) 01187 { 01188 PERR( "Unable to create database '%s'\n", dbname ); 01189 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 01190 goto exit; 01191 } 01192 dbi_test_result = conn_test_dbi_library( be->conn ); 01193 switch ( dbi_test_result ) 01194 { 01195 case GNC_DBI_PASS: 01196 break; 01197 01198 case GNC_DBI_FAIL_SETUP: 01199 qof_backend_set_error( qbe, ERR_SQL_DBI_UNTESTABLE ); 01200 qof_backend_set_message( qbe, 01201 "DBI library large number test incomplete" ); 01202 break; 01203 01204 case GNC_DBI_FAIL_TEST: 01205 qof_backend_set_error( qbe, ERR_SQL_BAD_DBI ); 01206 qof_backend_set_message( qbe, 01207 "DBI library fails large number test" ); 01208 break; 01209 } 01210 if ( GNC_DBI_PASS != dbi_test_result ) 01211 { 01212 dbi_conn_select_db( be->conn, "template1" ); 01213 dbi_conn_queryf( be->conn, "DROP DATABASE %s", dbnamelc ); 01214 goto exit; 01215 } 01216 success = gnc_dbi_lock_database ( qbe, ignore_lock ); 01217 } 01218 else 01219 { 01220 qof_backend_set_error( qbe, ERR_BACKEND_NO_SUCH_DB ); 01221 qof_backend_set_message( qbe, "Database %s not found", dbname ); 01222 } 01223 } 01224 if ( success ) 01225 { 01226 if ( be->sql_be.conn != NULL ) 01227 { 01228 gnc_sql_connection_dispose( be->sql_be.conn ); 01229 } 01230 be->sql_be.conn = create_dbi_connection( GNC_DBI_PROVIDER_PGSQL, qbe, be->conn ); 01231 } 01232 be->sql_be.timespec_format = PGSQL_TIMESPEC_STR_FORMAT; 01233 01234 /* We should now have a proper session set up. 01235 * Let's start logging */ 01236 basename = g_strjoin("_", protocol, host, username, dbname, NULL); 01237 translog_path = gnc_build_translog_path (basename); 01238 xaccLogSetBaseName (translog_path); 01239 PINFO ("logpath=%s", translog_path ? translog_path : "(null)"); 01240 01241 exit: 01242 g_free( protocol ); 01243 g_free( host ); 01244 g_free( username ); 01245 g_free( password ); 01246 g_free( basename ); 01247 g_free( translog_path ); 01248 g_free( dbname ); 01249 g_free( dbnamelc ); 01250 01251 LEAVE (" "); 01252 } 01253 01254 static GSList* 01255 conn_get_index_list_pgsql( dbi_conn conn ) 01256 { 01257 GSList *list = NULL; 01258 const gchar *errmsg; 01259 dbi_result result; 01260 g_print( "Retrieving postgres index list\n"); 01261 result = dbi_conn_query( conn, "SELECT relname FROM pg_class AS a INNER JOIN pg_index AS b ON (b.indexrelid = a.oid) INNER JOIN pg_namespace AS c ON (a.relnamespace = c.oid) WHERE reltype = '0' AND indisprimary = 'f' AND nspname = 'public'" ); 01262 if ( dbi_conn_error( conn, &errmsg ) != DBI_ERROR_NONE ) 01263 { 01264 g_print( "Index Table Retrieval Error: %s\n", errmsg ); 01265 return NULL; 01266 } 01267 while ( dbi_result_next_row( result ) != 0 ) 01268 { 01269 const gchar* index_name; 01270 01271 index_name = dbi_result_get_string_idx( result, 1 ); 01272 list = g_slist_prepend( list, strdup( index_name ) ); 01273 } 01274 dbi_result_free( result ); 01275 return list; 01276 } 01277 01278 01279 /* ================================================================= */ 01280 01281 static void 01282 gnc_dbi_session_end( QofBackend *be_start ) 01283 { 01284 GncDbiBackend *be = (GncDbiBackend*)be_start; 01285 01286 g_return_if_fail( be_start != NULL ); 01287 01288 ENTER (" "); 01289 01290 if ( be->conn != NULL ) 01291 { 01292 gnc_dbi_unlock( be_start ); 01293 dbi_conn_close( be->conn ); 01294 be->conn = NULL; 01295 } 01296 if ( be->sql_be.conn != NULL ) 01297 { 01298 gnc_sql_connection_dispose( be->sql_be.conn ); 01299 be->sql_be.conn = NULL; 01300 } 01301 gnc_sql_finalize_version_info( &be->sql_be ); 01302 01303 LEAVE (" "); 01304 } 01305 01306 static void 01307 gnc_dbi_destroy_backend( /*@ only @*/ QofBackend *be ) 01308 { 01309 g_return_if_fail( be != NULL ); 01310 01311 /* Stop transaction logging */ 01312 xaccLogSetBaseName (NULL); 01313 01314 qof_backend_destroy( be ); 01315 01316 g_free( be ); 01317 } 01318 01319 /* ================================================================= */ 01320 01321 /* GNUCASH_RESAVE_VERSION indicates the earliest database version 01322 * compatible with this version of Gnucash; the stored value is the 01323 * earliest version of Gnucash conpatible with the database. If the 01324 * GNUCASH_RESAVE_VERSION for this Gnucash is newer than the Gnucash 01325 * version which created the database, a resave is offered. If the 01326 * version of this Gnucash is older than the saved resave version, 01327 * then the database will be loaded read-only. A resave will update 01328 * both values to match this version of Gnucash. 01329 */ 01330 static void 01331 gnc_dbi_load( QofBackend* qbe, /*@ dependent @*/ QofBook *book, QofBackendLoadType loadType ) 01332 { 01333 GncDbiBackend *be = (GncDbiBackend*)qbe; 01334 01335 g_return_if_fail( qbe != NULL ); 01336 g_return_if_fail( book != NULL ); 01337 01338 ENTER( "be=%p, book=%p", be, book ); 01339 01340 if ( loadType == LOAD_TYPE_INITIAL_LOAD ) 01341 { 01342 g_assert( be->primary_book == NULL ); 01343 be->primary_book = book; 01344 01345 // Set up table version information 01346 gnc_sql_init_version_info( &be->sql_be ); 01347 01348 // Call all object backends to create any required tables 01349 qof_object_foreach_backend( GNC_SQL_BACKEND, create_tables_cb, be ); 01350 } 01351 01352 gnc_sql_load( &be->sql_be, book, loadType ); 01353 01354 if ( GNUCASH_RESAVE_VERSION > gnc_sql_get_table_version( &be->sql_be, "Gnucash" ) ) 01355 { 01356 /* The database was loaded with an older database schema or 01357 * data semantics. In order to ensure consistency, the whole 01358 * thing needs to be saved anew. */ 01359 qof_backend_set_error( qbe, ERR_SQL_DB_TOO_OLD ); 01360 } 01361 else if ( GNUCASH_RESAVE_VERSION < gnc_sql_get_table_version( &be->sql_be, 01362 "Gnucash-Resave")) 01363 { 01364 /* Worse, the database was created with a newer version. We 01365 * can't safely write to this database, so the user will have 01366 * to do a "save as" to make one that we can write to. 01367 */ 01368 qof_backend_set_error( qbe, ERR_SQL_DB_TOO_NEW ); 01369 } 01370 01371 01372 LEAVE( "" ); 01373 } 01374 01375 /* ================================================================= */ 01376 01377 static gboolean 01378 save_may_clobber_data( QofBackend* qbe ) 01379 { 01380 GncDbiBackend* be = (GncDbiBackend*)qbe; 01381 const gchar* dbname; 01382 dbi_result result; 01383 gboolean retval = FALSE; 01384 01385 /* Data may be clobbered iff the number of tables != 0 */ 01386 dbname = dbi_conn_get_option( be->conn, "dbname" ); 01387 result = dbi_conn_get_table_list( be->conn, dbname, NULL ); 01388 if ( result ) 01389 { 01390 retval = dbi_result_get_numrows( result ) > 0; 01391 dbi_result_free( result ); 01392 } 01393 return retval; 01394 } 01395 01396 static dbi_result 01397 conn_table_manage_backup (GncDbiSqlConnection *conn, 01398 gchar *table_name, TableOpType op ) 01399 { 01400 gchar *new_name = g_strdup_printf( "%s_%s", table_name, "back" ); 01401 dbi_result result = NULL; 01402 switch ( op ) 01403 { 01404 case backup: 01405 result = dbi_conn_queryf( conn->conn, "ALTER TABLE %s RENAME TO %s", 01406 table_name, new_name ); 01407 break; 01408 case rollback: 01409 result = dbi_conn_queryf( conn->conn, 01410 "ALTER TABLE %s RENAME TO %s", 01411 new_name, table_name ); 01412 break; 01413 case drop_backup: 01414 result = dbi_conn_queryf( conn->conn, "DROP TABLE %s", 01415 new_name ); 01416 default: 01417 break; 01418 } 01419 g_free( new_name ); 01420 return result; 01421 } 01422 01448 static gboolean 01449 conn_table_operation( GncSqlConnection *sql_conn, GSList *table_name_list, 01450 TableOpType op ) 01451 { 01452 GSList* node; 01453 gboolean result = TRUE; 01454 GncDbiSqlConnection *conn = (GncDbiSqlConnection*)(sql_conn); 01455 GSList *full_table_name_list = NULL; 01456 const gchar *dbname = dbi_conn_get_option( conn->conn, "dbname" ); 01457 01458 g_return_val_if_fail( table_name_list != NULL, FALSE ); 01459 if ( op == rollback ) 01460 full_table_name_list = 01461 conn->provider->get_table_list( conn->conn, dbname ); 01462 01463 for ( node = table_name_list; node != NULL && result; node = node->next ) 01464 { 01465 gchar* table_name = (gchar*)node->data; 01466 dbi_result result; 01467 /* Ignore the lock table */ 01468 if ( g_strcmp0(table_name, lock_table) == 0) 01469 { 01470 continue; 01471 } 01472 do 01473 { 01474 gnc_dbi_init_error( conn ); 01475 switch ( op ) 01476 { 01477 case rollback: 01478 if (g_slist_find(full_table_name_list, table_name)) 01479 { 01480 result = dbi_conn_queryf( conn->conn, "DROP TABLE %s", 01481 table_name ); 01482 if ( result ) 01483 break; 01484 } 01485 /* Fall through */ 01486 case backup: 01487 case drop_backup: 01488 result = conn_table_manage_backup( conn, table_name, op ); 01489 break; 01490 case empty: 01491 result = dbi_conn_queryf( conn->conn, "DELETE FROM TABLE %s", 01492 table_name ); 01493 break; 01494 case drop: 01495 default: 01496 result = dbi_conn_queryf( conn->conn, "DROP TABLE %s", 01497 table_name ); 01498 break; 01499 } 01500 } 01501 while ( conn->retry ); 01502 if ( result != NULL ) 01503 { 01504 if ( dbi_result_free( result ) < 0 ) 01505 { 01506 PERR( "Error in dbi_result_free() result\n" ); 01507 result = FALSE; 01508 } 01509 } 01510 } 01511 gnc_table_slist_free( full_table_name_list ); 01512 return result; 01513 } 01514 01525 static void 01526 gnc_dbi_sync_all( QofBackend* qbe, /*@ dependent @*/ QofBook *book ) 01527 { 01528 GncDbiBackend* be = (GncDbiBackend*)qbe; 01529 GncDbiSqlConnection *conn = (GncDbiSqlConnection*)(((GncSqlBackend*)be)->conn); 01530 GSList* table_name_list; 01531 const gchar* dbname; 01532 gint status; 01533 01534 g_return_if_fail( be != NULL ); 01535 g_return_if_fail( book != NULL ); 01536 01537 ENTER( "book=%p, primary=%p", book, be->primary_book ); 01538 01539 /* Destroy the current contents of the database */ 01540 dbname = dbi_conn_get_option( be->conn, "dbname" ); 01541 table_name_list = conn->provider->get_table_list( conn->conn, dbname ); 01542 if ( !conn_table_operation( (GncSqlConnection*)conn, table_name_list, 01543 drop ) ) 01544 { 01545 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 01546 return; 01547 } 01548 gnc_table_slist_free( table_name_list ); 01549 /* Save all contents */ 01550 be->is_pristine_db = TRUE; 01551 be->primary_book = book; 01552 gnc_sql_sync_all( &be->sql_be, book ); 01553 01554 LEAVE( "book=%p", book ); 01555 } 01556 01566 static void 01567 gnc_dbi_safe_sync_all( QofBackend *qbe, QofBook *book ) 01568 { 01569 GncDbiBackend *be = (GncDbiBackend*)qbe; 01570 GncDbiSqlConnection *conn = (GncDbiSqlConnection*)(((GncSqlBackend*)be)->conn); 01571 GSList *table_list, *index_list, *iter; 01572 const gchar* dbname = NULL; 01573 gint status; 01574 01575 g_return_if_fail( be != NULL ); 01576 g_return_if_fail( book != NULL ); 01577 01578 ENTER( "book=%p, primary=%p", book, be->primary_book ); 01579 dbname = dbi_conn_get_option( be->conn, "dbname" ); 01580 table_list = conn->provider->get_table_list( conn->conn, dbname ); 01581 if ( !conn_table_operation( (GncSqlConnection*)conn, table_list, 01582 backup ) ) 01583 { 01584 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 01585 conn_table_operation( (GncSqlConnection*)conn, table_list, 01586 rollback ); 01587 LEAVE( "Failed to rename tables" ); 01588 gnc_table_slist_free( table_list ); 01589 return; 01590 } 01591 index_list = conn->provider->get_index_list( conn->conn ); 01592 for ( iter = index_list; iter != NULL; iter = g_slist_next( iter) ) 01593 { 01594 const char *errmsg; 01595 dbi_result result = 01596 dbi_conn_queryf( conn->conn, "DROP INDEX %s", iter->data ); 01597 if ( result ) 01598 dbi_result_free( result ); 01599 if ( DBI_ERROR_NONE != dbi_conn_error( conn->conn, &errmsg ) ) 01600 { 01601 qof_backend_set_error( qbe, ERR_BACKEND_SERVER_ERR ); 01602 gnc_table_slist_free( index_list ); 01603 conn_table_operation( (GncSqlConnection*)conn, table_list, 01604 rollback ); 01605 gnc_table_slist_free( table_list ); 01606 LEAVE( "Failed to drop indexes %s", errmsg ); 01607 return; 01608 } 01609 } 01610 gnc_table_slist_free( index_list ); 01611 01612 be->is_pristine_db = TRUE; 01613 be->primary_book = book; 01614 01615 gnc_sql_sync_all( &be->sql_be, book ); 01616 if ( ERR_BACKEND_NO_ERR != qof_backend_get_error( qbe ) ) 01617 { 01618 conn_table_operation( (GncSqlConnection*)conn, table_list, 01619 rollback ); 01620 LEAVE( "Failed to create new database tables" ); 01621 return; 01622 } 01623 conn_table_operation( (GncSqlConnection*)conn, table_list, 01624 drop_backup ); 01625 gnc_table_slist_free( table_list ); 01626 LEAVE("book=%p", book); 01627 } 01628 /* ================================================================= */ 01629 static void 01630 gnc_dbi_begin_edit( QofBackend *qbe, QofInstance *inst ) 01631 { 01632 GncDbiBackend* be = (GncDbiBackend*)qbe; 01633 01634 g_return_if_fail( be != NULL ); 01635 g_return_if_fail( inst != NULL ); 01636 01637 gnc_sql_begin_edit( &be->sql_be, inst ); 01638 } 01639 01640 static void 01641 gnc_dbi_rollback_edit( QofBackend *qbe, QofInstance *inst ) 01642 { 01643 GncDbiBackend* be = (GncDbiBackend*)qbe; 01644 01645 g_return_if_fail( be != NULL ); 01646 g_return_if_fail( inst != NULL ); 01647 01648 gnc_sql_rollback_edit( &be->sql_be, inst ); 01649 } 01650 01651 static void 01652 gnc_dbi_commit_edit( QofBackend *qbe, QofInstance *inst ) 01653 { 01654 GncDbiBackend* be = (GncDbiBackend*)qbe; 01655 01656 g_return_if_fail( be != NULL ); 01657 g_return_if_fail( inst != NULL ); 01658 01659 gnc_sql_commit_edit( &be->sql_be, inst ); 01660 } 01661 01662 /* ================================================================= */ 01663 01664 static void 01665 init_sql_backend( GncDbiBackend* dbi_be ) 01666 { 01667 QofBackend* be; 01668 01669 be = (QofBackend*)dbi_be; 01670 01671 be->session_end = gnc_dbi_session_end; 01672 be->destroy_backend = gnc_dbi_destroy_backend; 01673 01674 be->load = gnc_dbi_load; 01675 01676 /* The gda backend treats accounting periods transactionally. */ 01677 be->begin = gnc_dbi_begin_edit; 01678 be->commit = gnc_dbi_commit_edit; 01679 be->rollback = gnc_dbi_rollback_edit; 01680 01681 /* The gda backend will not be multi-user (for now)... */ 01682 be->events_pending = NULL; 01683 be->process_events = NULL; 01684 01685 /* The SQL/DBI backend doesn't need to be synced until it is 01686 * configured for multiuser access. */ 01687 be->sync = gnc_dbi_safe_sync_all; 01688 be->safe_sync = gnc_dbi_safe_sync_all; 01689 be->load_config = NULL; 01690 be->get_config = NULL; 01691 01692 be->compile_query = gnc_sql_compile_query; 01693 be->run_query = gnc_sql_run_query; 01694 be->free_query = gnc_sql_free_query; 01695 01696 be->export_fn = NULL; 01697 01698 gnc_sql_init( &dbi_be->sql_be ); 01699 01700 dbi_be->sql_be.conn = NULL; 01701 dbi_be->sql_be.book = NULL; 01702 } 01703 01704 static QofBackend* 01705 new_backend( void (*session_begin)( QofBackend *, QofSession *, const gchar *, 01706 /*@ unused @*/ gboolean, 01707 /*@ unused @*/ gboolean, 01708 /*@ unused @*/ gboolean ) ) 01709 { 01710 GncDbiBackend *dbi_be; 01711 QofBackend *be; 01712 01713 dbi_be = g_new0( GncDbiBackend, 1 ); 01714 g_assert( dbi_be != NULL ); 01715 01716 be = (QofBackend*)dbi_be; 01717 qof_backend_init( be ); 01718 01719 be->session_begin = session_begin; 01720 init_sql_backend( dbi_be ); 01721 01722 return be; 01723 } 01724 01725 static QofBackend* 01726 gnc_dbi_backend_sqlite3_new( void ) 01727 { 01728 return new_backend( gnc_dbi_sqlite3_session_begin ); 01729 } 01730 01731 static QofBackend* 01732 gnc_dbi_backend_mysql_new( void ) 01733 { 01734 return new_backend( gnc_dbi_mysql_session_begin ); 01735 } 01736 01737 static QofBackend* 01738 gnc_dbi_backend_postgres_new( void ) 01739 { 01740 return new_backend( gnc_dbi_postgres_session_begin ); 01741 } 01742 01743 static void 01744 gnc_dbi_provider_free( /*@ only @*/ QofBackendProvider *prov ) 01745 { 01746 g_return_if_fail( prov != NULL ); 01747 01748 g_free( prov ); 01749 } 01750 01751 /* 01752 * Checks to see whether the file is an sqlite file or not 01753 * 01754 */ 01755 static gboolean 01756 gnc_dbi_check_sqlite3_file( const gchar *uri ) 01757 { 01758 FILE* f; 01759 gchar buf[50]; 01760 size_t chars_read; 01761 gint status; 01762 gchar *filename; 01763 01764 // BAD if the path is null 01765 g_return_val_if_fail( uri != NULL, FALSE ); 01766 01767 filename = gnc_uri_get_path ( uri ); 01768 f = g_fopen( filename, "r" ); 01769 g_free ( filename ); 01770 01771 // OK if the file doesn't exist - new file 01772 if ( f == NULL ) 01773 { 01774 PINFO( "doesn't exist (errno=%d) -> DBI", errno ); 01775 return TRUE; 01776 } 01777 01778 // OK if file has the correct header 01779 chars_read = fread( buf, sizeof(buf), 1, f ); 01780 status = fclose( f ); 01781 if ( status < 0 ) 01782 { 01783 PERR( "Error in fclose(): %d\n", errno ); 01784 } 01785 if ( g_str_has_prefix( buf, "SQLite format 3" ) ) 01786 { 01787 PINFO( "has SQLite format string -> DBI" ); 01788 return TRUE; 01789 } 01790 PINFO( "exists, does not have SQLite format string -> not DBI" ); 01791 01792 // Otherwise, BAD 01793 return FALSE; 01794 } 01795 01796 void 01797 gnc_module_init_backend_dbi(void) 01798 { 01799 QofBackendProvider *prov; 01800 const gchar* driver_dir; 01801 int num_drivers; 01802 gboolean have_sqlite3_driver = FALSE; 01803 gboolean have_mysql_driver = FALSE; 01804 gboolean have_pgsql_driver = FALSE; 01805 01806 /* Initialize libdbi and see which drivers are available. Only register qof backends which 01807 have drivers available. */ 01808 driver_dir = g_getenv( "GNC_DBD_DIR" ); 01809 if ( driver_dir == NULL ) 01810 { 01811 PINFO( "GNC_DBD_DIR not set: using libdbi built-in default\n"); 01812 } 01813 01814 /* dbi_initialize returns -1 in case of errors */ 01815 num_drivers = dbi_initialize( driver_dir ); 01816 if ( num_drivers <= 0 ) 01817 { 01818 PWARN( "No DBD drivers found\n" ); 01819 } 01820 else 01821 { 01822 dbi_driver driver = NULL; 01823 PINFO( "%d DBD drivers found\n", num_drivers ); 01824 01825 do 01826 { 01827 driver = dbi_driver_list( driver ); 01828 if ( driver != NULL ) 01829 { 01830 const gchar* name = dbi_driver_get_name( driver ); 01831 01832 PINFO( "Driver: %s\n", name ); 01833 if ( strcmp( name, "sqlite3" ) == 0 ) 01834 { 01835 have_sqlite3_driver = TRUE; 01836 } 01837 else if ( strcmp( name, "mysql" ) == 0 ) 01838 { 01839 have_mysql_driver = TRUE; 01840 } 01841 else if ( strcmp( name, "pgsql" ) == 0 ) 01842 { 01843 have_pgsql_driver = TRUE; 01844 } 01845 } 01846 } 01847 while ( driver != NULL ); 01848 } 01849 01850 if ( have_sqlite3_driver ) 01851 { 01852 prov = g_new0( QofBackendProvider, 1 ); 01853 g_assert( prov != NULL ); 01854 01855 prov->provider_name = "GnuCash Libdbi (SQLITE3) Backend"; 01856 prov->access_method = FILE_URI_TYPE; 01857 prov->partial_book_supported = FALSE; 01858 prov->backend_new = gnc_dbi_backend_sqlite3_new; 01859 prov->provider_free = gnc_dbi_provider_free; 01860 prov->check_data_type = gnc_dbi_check_sqlite3_file; 01861 qof_backend_register_provider( prov ); 01862 01863 prov = g_new0( QofBackendProvider, 1 ); 01864 g_assert( prov != NULL ); 01865 01866 prov->provider_name = "GnuCash Libdbi (SQLITE3) Backend"; 01867 prov->access_method = SQLITE3_URI_TYPE; 01868 prov->partial_book_supported = FALSE; 01869 prov->backend_new = gnc_dbi_backend_sqlite3_new; 01870 prov->provider_free = gnc_dbi_provider_free; 01871 prov->check_data_type = gnc_dbi_check_sqlite3_file; 01872 qof_backend_register_provider( prov ); 01873 } 01874 01875 if ( have_mysql_driver ) 01876 { 01877 prov = g_new0( QofBackendProvider, 1 ); 01878 g_assert( prov != NULL ); 01879 01880 prov->provider_name = "GnuCash Libdbi (MYSQL) Backend"; 01881 prov->access_method = "mysql"; 01882 prov->partial_book_supported = FALSE; 01883 prov->backend_new = gnc_dbi_backend_mysql_new; 01884 prov->provider_free = gnc_dbi_provider_free; 01885 prov->check_data_type = NULL; 01886 qof_backend_register_provider( prov ); 01887 } 01888 01889 if ( have_pgsql_driver ) 01890 { 01891 prov = g_new0( QofBackendProvider, 1 ); 01892 g_assert( prov != NULL ); 01893 01894 prov->provider_name = "GnuCash Libdbi (POSTGRESQL) Backend"; 01895 prov->access_method = "postgres"; 01896 prov->partial_book_supported = FALSE; 01897 prov->backend_new = gnc_dbi_backend_postgres_new; 01898 prov->provider_free = gnc_dbi_provider_free; 01899 prov->check_data_type = NULL; 01900 qof_backend_register_provider( prov ); 01901 } 01902 01903 /* If needed, set log level to DEBUG so that SQl statements will be put into 01904 the gnucash.trace file. */ 01905 /* qof_log_set_level( log_module, QOF_LOG_DEBUG ); */ 01906 } 01907 01908 #ifndef GNC_NO_LOADABLE_MODULES 01909 G_MODULE_EXPORT void 01910 qof_backend_module_init( void ) 01911 { 01912 gnc_module_init_backend_dbi(); 01913 } 01914 01915 G_MODULE_EXPORT void 01916 qof_backend_module_finalize( void ) 01917 { 01918 gnc_module_finalize_backend_dbi(); 01919 } 01920 #endif /* GNC_NO_LOADABLE_MODULES */ 01921 01922 void 01923 gnc_module_finalize_backend_dbi( void ) 01924 { 01925 dbi_shutdown(); 01926 } 01927 01928 /* --------------------------------------------------------- */ 01929 typedef struct 01930 { 01931 GncSqlRow base; 01932 01933 /*@ dependent @*/ 01934 dbi_result result; 01935 GList* gvalue_list; 01936 } GncDbiSqlRow; 01937 01938 static void 01939 row_dispose( /*@ only @*/ GncSqlRow* row ) 01940 { 01941 GncDbiSqlRow* dbi_row = (GncDbiSqlRow*)row; 01942 GList* node; 01943 01944 if ( dbi_row->gvalue_list != NULL ) 01945 { 01946 for ( node = dbi_row->gvalue_list; node != NULL; node = node->next ) 01947 { 01948 GValue* value; 01949 if ( !G_IS_VALUE(node->data) ) 01950 continue; 01951 value = (GValue*)node->data; 01952 if ( G_VALUE_HOLDS_STRING(value) ) 01953 { 01954 g_free( (gpointer)g_value_get_string( value ) ); 01955 } 01956 g_free( value ); 01957 } 01958 g_list_free( dbi_row->gvalue_list ); 01959 } 01960 g_free( dbi_row ); 01961 } 01962 01963 static /*@ null @*/ const GValue* 01964 row_get_value_at_col_name( GncSqlRow* row, const gchar* col_name ) 01965 { 01966 GncDbiSqlRow* dbi_row = (GncDbiSqlRow*)row; 01967 gushort type; 01968 guint attrs; 01969 GValue* value; 01970 time_t time; 01971 struct tm tm_struct; 01972 01973 type = dbi_result_get_field_type( dbi_row->result, col_name ); 01974 attrs = dbi_result_get_field_attribs( dbi_row->result, col_name ); 01975 value = g_new0( GValue, 1 ); 01976 g_assert( value != NULL ); 01977 01978 switch ( type ) 01979 { 01980 case DBI_TYPE_INTEGER: 01981 (void)g_value_init( value, G_TYPE_INT64 ); 01982 g_value_set_int64( value, dbi_result_get_longlong( dbi_row->result, col_name ) ); 01983 break; 01984 case DBI_TYPE_DECIMAL: 01985 gnc_push_locale( LC_NUMERIC, "C" ); 01986 if ( (attrs & DBI_DECIMAL_SIZEMASK) == DBI_DECIMAL_SIZE4 ) 01987 { 01988 (void)g_value_init( value, G_TYPE_FLOAT ); 01989 g_value_set_float( value, dbi_result_get_float( dbi_row->result, col_name ) ); 01990 } 01991 else if ( (attrs & DBI_DECIMAL_SIZEMASK) == DBI_DECIMAL_SIZE8 ) 01992 { 01993 (void)g_value_init( value, G_TYPE_DOUBLE ); 01994 g_value_set_double( value, dbi_result_get_double( dbi_row->result, col_name ) ); 01995 } 01996 else 01997 { 01998 PERR( "Field %s: strange decimal length attrs=%d\n", col_name, attrs ); 01999 } 02000 gnc_pop_locale( LC_NUMERIC ); 02001 break; 02002 case DBI_TYPE_STRING: 02003 (void)g_value_init( value, G_TYPE_STRING ); 02004 g_value_take_string( value, dbi_result_get_string_copy( dbi_row->result, col_name ) ); 02005 break; 02006 case DBI_TYPE_DATETIME: 02007 if ( dbi_result_field_is_null( dbi_row->result, col_name ) ) 02008 { 02009 return NULL; 02010 } 02011 else 02012 { 02013 time = dbi_result_get_datetime( dbi_row->result, col_name ); 02014 (void)gmtime_r( &time, &tm_struct ); 02015 (void)g_value_init( value, G_TYPE_STRING ); 02016 g_value_take_string( value, 02017 g_strdup_printf( "%d%02d%02d%02d%02d%02d", 02018 1900 + tm_struct.tm_year, tm_struct.tm_mon + 1, tm_struct.tm_mday, 02019 tm_struct.tm_hour, tm_struct.tm_min, tm_struct.tm_sec ) ); 02020 } 02021 break; 02022 default: 02023 PERR( "Field %s: unknown DBI_TYPE: %d\n", col_name, type ); 02024 g_free( value ); 02025 return NULL; 02026 } 02027 02028 dbi_row->gvalue_list = g_list_prepend( dbi_row->gvalue_list, value ); 02029 return value; 02030 } 02031 02032 static GncSqlRow* 02033 create_dbi_row( /*@ dependent @*/ dbi_result result ) 02034 { 02035 GncDbiSqlRow* row; 02036 02037 row = g_new0( GncDbiSqlRow, 1 ); 02038 g_assert( row != NULL ); 02039 02040 row->base.getValueAtColName = row_get_value_at_col_name; 02041 row->base.dispose = row_dispose; 02042 row->result = result; 02043 02044 return (GncSqlRow*)row; 02045 } 02046 /* --------------------------------------------------------- */ 02047 typedef struct 02048 { 02049 GncSqlResult base; 02050 02051 /*@ observer @*/ 02052 GncDbiSqlConnection* dbi_conn; 02053 /*@ owned @*/ 02054 dbi_result result; 02055 guint num_rows; 02056 guint cur_row; 02057 GncSqlRow* row; 02058 } GncDbiSqlResult; 02059 02060 static void 02061 result_dispose( /*@ only @*/ GncSqlResult* result ) 02062 { 02063 GncDbiSqlResult* dbi_result = (GncDbiSqlResult*)result; 02064 02065 if ( dbi_result->row != NULL ) 02066 { 02067 gnc_sql_row_dispose( dbi_result->row ); 02068 } 02069 if ( dbi_result->result != NULL ) 02070 { 02071 gint status; 02072 02073 status = dbi_result_free( dbi_result->result ); 02074 if ( status < 0 ) 02075 { 02076 PERR( "Error in dbi_result_free() result\n" ); 02077 qof_backend_set_error( dbi_result->dbi_conn->qbe, ERR_BACKEND_SERVER_ERR ); 02078 } 02079 } 02080 g_free( result ); 02081 } 02082 02083 static guint 02084 result_get_num_rows( GncSqlResult* result ) 02085 { 02086 GncDbiSqlResult* dbi_result = (GncDbiSqlResult*)result; 02087 02088 return dbi_result->num_rows; 02089 } 02090 02091 static /*@ null @*/ GncSqlRow* 02092 result_get_first_row( GncSqlResult* result ) 02093 { 02094 GncDbiSqlResult* dbi_result = (GncDbiSqlResult*)result; 02095 02096 if ( dbi_result->row != NULL ) 02097 { 02098 gnc_sql_row_dispose( dbi_result->row ); 02099 dbi_result->row = NULL; 02100 } 02101 if ( dbi_result->num_rows > 0 ) 02102 { 02103 gint status = dbi_result_first_row( dbi_result->result ); 02104 if ( status == 0 ) 02105 { 02106 PERR( "Error in dbi_result_first_row()\n" ); 02107 qof_backend_set_error( dbi_result->dbi_conn->qbe, ERR_BACKEND_SERVER_ERR ); 02108 } 02109 dbi_result->cur_row = 1; 02110 dbi_result->row = create_dbi_row( dbi_result->result ); 02111 return dbi_result->row; 02112 } 02113 else 02114 { 02115 return NULL; 02116 } 02117 } 02118 02119 static /*@ null @*/ GncSqlRow* 02120 result_get_next_row( GncSqlResult* result ) 02121 { 02122 GncDbiSqlResult* dbi_result = (GncDbiSqlResult*)result; 02123 02124 if ( dbi_result->row != NULL ) 02125 { 02126 gnc_sql_row_dispose( dbi_result->row ); 02127 dbi_result->row = NULL; 02128 } 02129 if ( dbi_result->cur_row < dbi_result->num_rows ) 02130 { 02131 gint status = dbi_result_next_row( dbi_result->result ); 02132 if ( status == 0 ) 02133 { 02134 PERR( "Error in dbi_result_first_row()\n" ); 02135 qof_backend_set_error( dbi_result->dbi_conn->qbe, ERR_BACKEND_SERVER_ERR ); 02136 } 02137 dbi_result->cur_row++; 02138 dbi_result->row = create_dbi_row( dbi_result->result ); 02139 return dbi_result->row; 02140 } 02141 else 02142 { 02143 return NULL; 02144 } 02145 } 02146 02147 static GncSqlResult* 02148 create_dbi_result( /*@ observer @*/ GncDbiSqlConnection* dbi_conn, /*@ owned @*/ dbi_result result ) 02149 { 02150 GncDbiSqlResult* dbi_result; 02151 02152 dbi_result = g_new0( GncDbiSqlResult, 1 ); 02153 g_assert( dbi_result != NULL ); 02154 02155 dbi_result->base.dispose = result_dispose; 02156 dbi_result->base.getNumRows = result_get_num_rows; 02157 dbi_result->base.getFirstRow = result_get_first_row; 02158 dbi_result->base.getNextRow = result_get_next_row; 02159 dbi_result->result = result; 02160 dbi_result->num_rows = (guint)dbi_result_get_numrows( result ); 02161 dbi_result->cur_row = 0; 02162 dbi_result->dbi_conn = dbi_conn; 02163 02164 return (GncSqlResult*)dbi_result; 02165 } 02166 /* --------------------------------------------------------- */ 02167 typedef struct 02168 { 02169 GncSqlStatement base; 02170 02171 GString* sql; 02172 /*@ observer @*/ 02173 GncSqlConnection* conn; 02174 } GncDbiSqlStatement; 02175 02176 static void 02177 stmt_dispose( /*@ only @*/ GncSqlStatement* stmt ) 02178 { 02179 GncDbiSqlStatement* dbi_stmt = (GncDbiSqlStatement*)stmt; 02180 02181 if ( dbi_stmt->sql != NULL ) 02182 { 02183 (void)g_string_free( dbi_stmt->sql, TRUE ); 02184 } 02185 g_free( stmt ); 02186 } 02187 02188 static gchar* 02189 stmt_to_sql( GncSqlStatement* stmt ) 02190 { 02191 GncDbiSqlStatement* dbi_stmt = (GncDbiSqlStatement*)stmt; 02192 02193 return dbi_stmt->sql->str; 02194 } 02195 02196 static void 02197 stmt_add_where_cond( GncSqlStatement* stmt, /*@ unused @*/ QofIdTypeConst type_name, 02198 /*@ unused @*/ gpointer obj, const GncSqlColumnTableEntry* table_row, GValue* value ) 02199 { 02200 GncDbiSqlStatement* dbi_stmt = (GncDbiSqlStatement*)stmt; 02201 gchar* buf; 02202 gchar* value_str; 02203 02204 value_str = gnc_sql_get_sql_value( dbi_stmt->conn, value ); 02205 buf = g_strdup_printf( " WHERE %s = %s", table_row->col_name, 02206 value_str ); 02207 g_free( value_str ); 02208 (void)g_string_append( dbi_stmt->sql, buf ); 02209 g_free( buf ); 02210 } 02211 02212 static GncSqlStatement* 02213 create_dbi_statement( /*@ observer @*/ GncSqlConnection* conn, const gchar* sql ) 02214 { 02215 GncDbiSqlStatement* stmt; 02216 02217 stmt = g_new0( GncDbiSqlStatement, 1 ); 02218 g_assert( stmt != NULL ); 02219 02220 stmt->base.dispose = stmt_dispose; 02221 stmt->base.toSql = stmt_to_sql; 02222 stmt->base.addWhereCond = stmt_add_where_cond; 02223 stmt->sql = g_string_new( sql ); 02224 stmt->conn = conn; 02225 02226 return (GncSqlStatement*)stmt; 02227 } 02228 /* --------------------------------------------------------- */ 02229 static void 02230 conn_dispose( /*@ only @*/ GncSqlConnection* conn ) 02231 { 02232 //GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn; 02233 02234 g_free( conn ); 02235 } 02236 02237 static /*@ null @*/ GncSqlResult* 02238 conn_execute_select_statement( GncSqlConnection* conn, GncSqlStatement* stmt ) 02239 { 02240 GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn; 02241 GncDbiSqlStatement* dbi_stmt = (GncDbiSqlStatement*)stmt; 02242 dbi_result result; 02243 02244 DEBUG( "SQL: %s\n", dbi_stmt->sql->str ); 02245 gnc_push_locale( LC_NUMERIC, "C" ); 02246 do 02247 { 02248 gnc_dbi_init_error( dbi_conn ); 02249 result = dbi_conn_query( dbi_conn->conn, dbi_stmt->sql->str ); 02250 } 02251 while ( dbi_conn->retry ); 02252 if ( result == NULL ) 02253 { 02254 PERR( "Error executing SQL %s\n", dbi_stmt->sql->str ); 02255 return NULL; 02256 } 02257 gnc_pop_locale( LC_NUMERIC ); 02258 return create_dbi_result( dbi_conn, result ); 02259 } 02260 02261 static gint 02262 conn_execute_nonselect_statement( GncSqlConnection* conn, GncSqlStatement* stmt ) 02263 { 02264 GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn; 02265 GncDbiSqlStatement* dbi_stmt = (GncDbiSqlStatement*)stmt; 02266 dbi_result result; 02267 gint num_rows; 02268 gint status; 02269 02270 DEBUG( "SQL: %s\n", dbi_stmt->sql->str ); 02271 do 02272 { 02273 gnc_dbi_init_error( dbi_conn ); 02274 result = dbi_conn_query( dbi_conn->conn, dbi_stmt->sql->str ); 02275 } 02276 while ( dbi_conn->retry ); 02277 if ( result == NULL ) 02278 { 02279 PERR( "Error executing SQL %s\n", dbi_stmt->sql->str ); 02280 return -1; 02281 } 02282 num_rows = (gint)dbi_result_get_numrows_affected( result ); 02283 status = dbi_result_free( result ); 02284 if ( status < 0 ) 02285 { 02286 PERR( "Error in dbi_result_free() result\n" ); 02287 qof_backend_set_error( dbi_conn->qbe, ERR_BACKEND_SERVER_ERR ); 02288 } 02289 return num_rows; 02290 } 02291 02292 static GncSqlStatement* 02293 conn_create_statement_from_sql( /*@ observer @*/ GncSqlConnection* conn, const gchar* sql ) 02294 { 02295 //GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn; 02296 02297 return create_dbi_statement( conn, sql ); 02298 } 02299 02300 static GValue* 02301 create_gvalue_from_string( /*@ only @*/ gchar* s ) 02302 { 02303 GValue* s_gval; 02304 02305 s_gval = g_new0( GValue, 1 ); 02306 g_assert( s_gval != NULL ); 02307 02308 (void)g_value_init( s_gval, G_TYPE_STRING ); 02309 g_value_take_string( s_gval, s ); 02310 02311 return s_gval; 02312 } 02313 02314 static gboolean 02315 conn_does_table_exist( GncSqlConnection* conn, const gchar* table_name ) 02316 { 02317 GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn; 02318 gint nTables; 02319 dbi_result tables; 02320 const gchar* dbname; 02321 gint status; 02322 02323 g_return_val_if_fail( conn != NULL, FALSE ); 02324 g_return_val_if_fail( table_name != NULL, FALSE ); 02325 02326 dbname = dbi_conn_get_option( dbi_conn->conn, "dbname" ); 02327 tables = dbi_conn_get_table_list( dbi_conn->conn, dbname, table_name ); 02328 nTables = (gint)dbi_result_get_numrows( tables ); 02329 status = dbi_result_free( tables ); 02330 if ( status < 0 ) 02331 { 02332 PERR( "Error in dbi_result_free() result\n" ); 02333 qof_backend_set_error( dbi_conn->qbe, ERR_BACKEND_SERVER_ERR ); 02334 } 02335 02336 if ( nTables == 1 ) 02337 { 02338 return TRUE; 02339 } 02340 else 02341 { 02342 return FALSE; 02343 } 02344 } 02345 02346 static gboolean 02347 conn_begin_transaction( /*@ unused @*/ GncSqlConnection* conn ) 02348 { 02349 GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn; 02350 dbi_result result; 02351 gint status; 02352 gboolean success = FALSE; 02353 02354 DEBUG( "BEGIN\n" ); 02355 02356 if ( !gnc_dbi_verify_conn (dbi_conn) ) 02357 { 02358 PERR( "gnc_dbi_verify_conn() failed\n" ); 02359 qof_backend_set_error( dbi_conn->qbe, ERR_BACKEND_SERVER_ERR ); 02360 return FALSE; 02361 } 02362 02363 do 02364 { 02365 gnc_dbi_init_error( dbi_conn ); 02366 result = dbi_conn_queryf( dbi_conn->conn, "BEGIN" ); 02367 } 02368 while ( dbi_conn->retry ); 02369 02370 success = ( result != NULL ); 02371 status = dbi_result_free( result ); 02372 if ( status < 0 ) 02373 { 02374 PERR( "Error in dbi_result_free() result\n" ); 02375 qof_backend_set_error( dbi_conn->qbe, ERR_BACKEND_SERVER_ERR ); 02376 } 02377 if ( !success ) 02378 { 02379 PERR( "BEGIN transaction failed()\n" ); 02380 qof_backend_set_error( dbi_conn->qbe, ERR_BACKEND_SERVER_ERR ); 02381 } 02382 02383 return success; 02384 } 02385 02386 static gboolean 02387 conn_rollback_transaction( /*@ unused @*/ GncSqlConnection* conn ) 02388 { 02389 GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn; 02390 dbi_result result; 02391 gint status; 02392 gboolean success = FALSE; 02393 02394 DEBUG( "ROLLBACK\n" ); 02395 result = dbi_conn_queryf( dbi_conn->conn, "ROLLBACK" ); 02396 success = ( result != NULL ); 02397 02398 status = dbi_result_free( result ); 02399 if ( status < 0 ) 02400 { 02401 PERR( "Error in dbi_result_free() result\n" ); 02402 qof_backend_set_error( dbi_conn->qbe, ERR_BACKEND_SERVER_ERR ); 02403 } 02404 if ( !success ) 02405 { 02406 PERR( "Error in conn_rollback_transaction()\n" ); 02407 qof_backend_set_error( dbi_conn->qbe, ERR_BACKEND_SERVER_ERR ); 02408 } 02409 02410 return success; 02411 } 02412 02413 static gboolean 02414 conn_commit_transaction( /*@ unused @*/ GncSqlConnection* conn ) 02415 { 02416 GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn; 02417 dbi_result result; 02418 gint status; 02419 gboolean success = FALSE; 02420 02421 DEBUG( "COMMIT\n" ); 02422 result = dbi_conn_queryf( dbi_conn->conn, "COMMIT" ); 02423 success = ( result != NULL ); 02424 02425 status = dbi_result_free( result ); 02426 if ( status < 0 ) 02427 { 02428 PERR( "Error in dbi_result_free() result\n" ); 02429 qof_backend_set_error( dbi_conn->qbe, ERR_BACKEND_SERVER_ERR ); 02430 } 02431 if ( !success ) 02432 { 02433 PERR( "Error in conn_commit_transaction()\n" ); 02434 qof_backend_set_error( dbi_conn->qbe, ERR_BACKEND_SERVER_ERR ); 02435 } 02436 02437 return success; 02438 } 02439 02440 static /*@ null @*/ gchar* 02441 create_index_ddl( GncSqlConnection* conn, 02442 const gchar* index_name, 02443 const gchar* table_name, 02444 const GncSqlColumnTableEntry* col_table ) 02445 { 02446 GString* ddl; 02447 const GncSqlColumnTableEntry* table_row; 02448 02449 g_return_val_if_fail( conn != NULL, NULL ); 02450 g_return_val_if_fail( index_name != NULL, NULL ); 02451 g_return_val_if_fail( table_name != NULL, NULL ); 02452 g_return_val_if_fail( col_table != NULL, NULL ); 02453 02454 ddl = g_string_new( "" ); 02455 g_string_printf( ddl, "CREATE INDEX %s ON %s (", index_name, table_name ); 02456 for ( table_row = col_table; table_row->col_name != NULL; ++table_row ) 02457 { 02458 if ( table_row != col_table ) 02459 { 02460 (void)g_string_append( ddl, ", " ); 02461 } 02462 g_string_append_printf( ddl, "%s", table_row->col_name ); 02463 } 02464 (void)g_string_append( ddl, ")" ); 02465 02466 return g_string_free( ddl, FALSE ); 02467 } 02468 02469 static /*@ null @*/ gchar* 02470 add_columns_ddl( GncSqlConnection* conn, 02471 const gchar* table_name, 02472 GList* col_info_list ) 02473 { 02474 GString* ddl; 02475 const GList* list_node; 02476 const GncSqlColumnTableEntry* table_row; 02477 guint col_num; 02478 GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn; 02479 02480 g_return_val_if_fail( conn != NULL, NULL ); 02481 g_return_val_if_fail( table_name != NULL, NULL ); 02482 g_return_val_if_fail( col_info_list != NULL, NULL ); 02483 02484 ddl = g_string_new( "" ); 02485 g_string_printf( ddl, "ALTER TABLE %s ", table_name ); 02486 for ( list_node = col_info_list, col_num = 0; list_node != NULL; 02487 list_node = list_node->next, col_num++ ) 02488 { 02489 GncSqlColumnInfo* info = (GncSqlColumnInfo*)(list_node->data); 02490 02491 if ( col_num != 0 ) 02492 { 02493 (void)g_string_append( ddl, ", " ); 02494 } 02495 g_string_append( ddl, "ADD COLUMN " ); 02496 dbi_conn->provider->append_col_def( ddl, info ); 02497 g_free( info->name ); 02498 g_free( info ); 02499 } 02500 02501 return g_string_free( ddl, FALSE ); 02502 } 02503 02504 static void 02505 append_sqlite3_col_def( GString* ddl, GncSqlColumnInfo* info ) 02506 { 02507 gchar* type_name; 02508 02509 if ( info->type == BCT_INT ) 02510 { 02511 type_name = "integer"; 02512 } 02513 else if ( info->type == BCT_INT64 ) 02514 { 02515 type_name = "bigint"; 02516 } 02517 else if ( info->type == BCT_DOUBLE ) 02518 { 02519 type_name = "float8"; 02520 } 02521 else if ( info->type == BCT_STRING || info->type == BCT_DATE 02522 || info->type == BCT_DATETIME ) 02523 { 02524 type_name = "text"; 02525 } 02526 else 02527 { 02528 PERR( "Unknown column type: %d\n", info->type ); 02529 type_name = ""; 02530 } 02531 g_string_append_printf( ddl, "%s %s", info->name, type_name ); 02532 if ( info->size != 0 ) 02533 { 02534 (void)g_string_append_printf( ddl, "(%d)", info->size ); 02535 } 02536 if ( info->is_primary_key ) 02537 { 02538 (void)g_string_append( ddl, " PRIMARY KEY" ); 02539 } 02540 if ( info->is_autoinc ) 02541 { 02542 (void)g_string_append( ddl, " AUTOINCREMENT" ); 02543 } 02544 if ( !info->null_allowed ) 02545 { 02546 (void)g_string_append( ddl, " NOT NULL" ); 02547 } 02548 } 02549 02550 static /*@ null @*/ gchar* 02551 conn_create_table_ddl_sqlite3( GncSqlConnection* conn, 02552 const gchar* table_name, 02553 const GList* col_info_list ) 02554 { 02555 GString* ddl; 02556 const GList* list_node; 02557 guint col_num; 02558 02559 g_return_val_if_fail( conn != NULL, NULL ); 02560 g_return_val_if_fail( table_name != NULL, NULL ); 02561 g_return_val_if_fail( col_info_list != NULL, NULL ); 02562 02563 ddl = g_string_new( "" ); 02564 g_string_printf( ddl, "CREATE TABLE %s (", table_name ); 02565 for ( list_node = col_info_list, col_num = 0; list_node != NULL; 02566 list_node = list_node->next, col_num++ ) 02567 { 02568 GncSqlColumnInfo* info = (GncSqlColumnInfo*)(list_node->data); 02569 02570 if ( col_num != 0 ) 02571 { 02572 (void)g_string_append( ddl, ", " ); 02573 } 02574 append_sqlite3_col_def( ddl, info ); 02575 g_free( info->name ); 02576 g_free( info ); 02577 } 02578 (void)g_string_append( ddl, ")" ); 02579 02580 return g_string_free( ddl, FALSE ); 02581 } 02582 02583 static void 02584 append_mysql_col_def( GString* ddl, GncSqlColumnInfo* info ) 02585 { 02586 gchar* type_name; 02587 02588 if ( info->type == BCT_INT ) 02589 { 02590 type_name = "integer"; 02591 } 02592 else if ( info->type == BCT_INT64 ) 02593 { 02594 type_name = "bigint"; 02595 } 02596 else if ( info->type == BCT_DOUBLE ) 02597 { 02598 type_name = "double"; 02599 } 02600 else if ( info->type == BCT_STRING ) 02601 { 02602 type_name = "varchar"; 02603 } 02604 else if ( info->type == BCT_DATE ) 02605 { 02606 info->size = 0; 02607 type_name = "date"; 02608 } 02609 else if ( info->type == BCT_DATETIME ) 02610 { 02611 info->size = 0; 02612 type_name = "TIMESTAMP NULL DEFAULT 0"; 02613 } 02614 else 02615 { 02616 PERR( "Unknown column type: %d\n", info->type ); 02617 type_name = ""; 02618 } 02619 g_string_append_printf( ddl, "%s %s", info->name, type_name ); 02620 if ( info->size != 0 ) 02621 { 02622 g_string_append_printf( ddl, "(%d)", info->size ); 02623 } 02624 if ( info->is_unicode ) 02625 { 02626 (void)g_string_append( ddl, " CHARACTER SET utf8" ); 02627 } 02628 if ( info->is_primary_key ) 02629 { 02630 (void)g_string_append( ddl, " PRIMARY KEY" ); 02631 } 02632 if ( info->is_autoinc ) 02633 { 02634 (void)g_string_append( ddl, " AUTO_INCREMENT" ); 02635 } 02636 if ( !info->null_allowed ) 02637 { 02638 (void)g_string_append( ddl, " NOT NULL" ); 02639 } 02640 } 02641 02642 static /*@ null @*/ gchar* 02643 conn_create_table_ddl_mysql( GncSqlConnection* conn, const gchar* table_name, 02644 const GList* col_info_list ) 02645 { 02646 GString* ddl; 02647 const GList* list_node; 02648 guint col_num; 02649 02650 g_return_val_if_fail( conn != NULL, NULL ); 02651 g_return_val_if_fail( table_name != NULL, NULL ); 02652 g_return_val_if_fail( col_info_list != NULL, NULL ); 02653 02654 ddl = g_string_new( "" ); 02655 g_string_printf( ddl, "CREATE TABLE %s (", table_name ); 02656 for ( list_node = col_info_list, col_num = 0; list_node != NULL; 02657 list_node = list_node->next, col_num++ ) 02658 { 02659 GncSqlColumnInfo* info = (GncSqlColumnInfo*)(list_node->data); 02660 02661 if ( col_num != 0 ) 02662 { 02663 (void)g_string_append( ddl, ", " ); 02664 } 02665 append_mysql_col_def( ddl, info ); 02666 g_free( info->name ); 02667 g_free( info ); 02668 } 02669 (void)g_string_append( ddl, ")" ); 02670 02671 return g_string_free( ddl, FALSE ); 02672 } 02673 02674 static void 02675 append_pgsql_col_def( GString* ddl, GncSqlColumnInfo* info ) 02676 { 02677 gchar* type_name; 02678 02679 if ( info->type == BCT_INT ) 02680 { 02681 if ( info->is_autoinc ) 02682 { 02683 type_name = "serial"; 02684 } 02685 else 02686 { 02687 type_name = "integer"; 02688 } 02689 } 02690 else if ( info->type == BCT_INT64 ) 02691 { 02692 type_name = "int8"; 02693 } 02694 else if ( info->type == BCT_DOUBLE ) 02695 { 02696 type_name = "double precision"; 02697 } 02698 else if ( info->type == BCT_STRING ) 02699 { 02700 type_name = "varchar"; 02701 } 02702 else if ( info->type == BCT_DATE ) 02703 { 02704 info->size = 0; 02705 type_name = "date"; 02706 } 02707 else if ( info->type == BCT_DATETIME ) 02708 { 02709 info->size = 0; 02710 type_name = "timestamp without time zone"; 02711 } 02712 else 02713 { 02714 PERR( "Unknown column type: %d\n", info->type ); 02715 type_name = ""; 02716 } 02717 g_string_append_printf( ddl, "%s %s", info->name, type_name ); 02718 if ( info->size != 0 ) 02719 { 02720 g_string_append_printf( ddl, "(%d)", info->size ); 02721 } 02722 if ( info->is_primary_key ) 02723 { 02724 (void)g_string_append( ddl, " PRIMARY KEY" ); 02725 } 02726 if ( !info->null_allowed ) 02727 { 02728 (void)g_string_append( ddl, " NOT NULL" ); 02729 } 02730 } 02731 02732 static /*@ null @*/ gchar* 02733 conn_create_table_ddl_pgsql( GncSqlConnection* conn, const gchar* table_name, 02734 const GList* col_info_list ) 02735 { 02736 GString* ddl; 02737 const GList* list_node; 02738 guint col_num; 02739 gboolean is_unicode = FALSE; 02740 02741 g_return_val_if_fail( conn != NULL, NULL ); 02742 g_return_val_if_fail( table_name != NULL, NULL ); 02743 g_return_val_if_fail( col_info_list != NULL, NULL ); 02744 02745 ddl = g_string_new( "" ); 02746 g_string_printf( ddl, "CREATE TABLE %s (", table_name ); 02747 for ( list_node = col_info_list, col_num = 0; list_node != NULL; 02748 list_node = list_node->next, col_num++ ) 02749 { 02750 GncSqlColumnInfo* info = (GncSqlColumnInfo*)(list_node->data); 02751 02752 if ( col_num != 0 ) 02753 { 02754 (void)g_string_append( ddl, ", " ); 02755 } 02756 append_pgsql_col_def( ddl, info ); 02757 is_unicode = is_unicode || info->is_unicode; 02758 g_free( info->name ); 02759 g_free( info ); 02760 } 02761 (void)g_string_append( ddl, ")" ); 02762 if ( is_unicode ) 02763 { 02764 } 02765 02766 return g_string_free( ddl, FALSE ); 02767 } 02768 02769 static gboolean 02770 conn_create_table( GncSqlConnection* conn, const gchar* table_name, 02771 GList* col_info_list ) 02772 { 02773 GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn; 02774 gchar* ddl; 02775 dbi_result result; 02776 02777 g_return_val_if_fail( conn != NULL, FALSE ); 02778 g_return_val_if_fail( table_name != NULL, FALSE ); 02779 g_return_val_if_fail( col_info_list != NULL, FALSE ); 02780 02781 02782 ddl = dbi_conn->provider->create_table_ddl( conn, table_name, 02783 col_info_list ); 02784 g_list_free( col_info_list ); 02785 if ( ddl != NULL ) 02786 { 02787 gint status; 02788 02789 DEBUG( "SQL: %s\n", ddl ); 02790 result = dbi_conn_query( dbi_conn->conn, ddl ); 02791 g_free( ddl ); 02792 status = dbi_result_free( result ); 02793 if ( status < 0 ) 02794 { 02795 PERR( "Error in dbi_result_free() result\n" ); 02796 qof_backend_set_error( dbi_conn->qbe, ERR_BACKEND_SERVER_ERR ); 02797 } 02798 } 02799 else 02800 { 02801 return FALSE; 02802 } 02803 02804 return TRUE; 02805 } 02806 02807 static gboolean 02808 conn_create_index( /*@ unused @*/ GncSqlConnection* conn, /*@ unused @*/ const gchar* index_name, 02809 /*@ unused @*/ const gchar* table_name, /*@ unused @*/ const GncSqlColumnTableEntry* col_table ) 02810 { 02811 GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn; 02812 gchar* ddl; 02813 dbi_result result; 02814 gint status; 02815 02816 g_return_val_if_fail( conn != NULL, FALSE ); 02817 g_return_val_if_fail( index_name != NULL, FALSE ); 02818 g_return_val_if_fail( table_name != NULL, FALSE ); 02819 g_return_val_if_fail( col_table != NULL, FALSE ); 02820 02821 ddl = create_index_ddl( conn, index_name, table_name, col_table ); 02822 if ( ddl != NULL ) 02823 { 02824 gint status; 02825 02826 DEBUG( "SQL: %s\n", ddl ); 02827 result = dbi_conn_query( dbi_conn->conn, ddl ); 02828 g_free( ddl ); 02829 status = dbi_result_free( result ); 02830 if ( status < 0 ) 02831 { 02832 PERR( "Error in dbi_result_free() result\n" ); 02833 qof_backend_set_error( dbi_conn->qbe, ERR_BACKEND_SERVER_ERR ); 02834 } 02835 } 02836 else 02837 { 02838 return FALSE; 02839 } 02840 02841 return TRUE; 02842 } 02843 02844 static gboolean 02845 conn_add_columns_to_table( /*@ unused @*/ GncSqlConnection* conn, /*@ unused @*/ const gchar* table_name, 02846 GList* col_info_list ) 02847 { 02848 GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn; 02849 gchar* ddl; 02850 dbi_result result; 02851 gint status; 02852 02853 g_return_val_if_fail( conn != NULL, FALSE ); 02854 g_return_val_if_fail( table_name != NULL, FALSE ); 02855 g_return_val_if_fail( col_info_list != NULL, FALSE ); 02856 02857 ddl = add_columns_ddl( conn, table_name, col_info_list ); 02858 if ( ddl != NULL ) 02859 { 02860 gint status; 02861 02862 DEBUG( "SQL: %s\n", ddl ); 02863 result = dbi_conn_query( dbi_conn->conn, ddl ); 02864 g_free( ddl ); 02865 status = dbi_result_free( result ); 02866 if ( status < 0 ) 02867 { 02868 PERR( "Error in dbi_result_free() result\n" ); 02869 qof_backend_set_error( dbi_conn->qbe, ERR_BACKEND_SERVER_ERR ); 02870 } 02871 } 02872 else 02873 { 02874 return FALSE; 02875 } 02876 02877 return TRUE; 02878 } 02879 02880 static /*@ null @*/ gchar* 02881 conn_quote_string( const GncSqlConnection* conn, gchar* unquoted_str ) 02882 { 02883 GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn; 02884 gchar* quoted_str; 02885 size_t size; 02886 02887 size = dbi_conn_quote_string_copy( dbi_conn->conn, unquoted_str, 02888 "ed_str ); 02889 if ( size != 0 ) 02890 { 02891 return quoted_str; 02892 } 02893 else 02894 { 02895 return NULL; 02896 } 02897 } 02898 02899 static GSList* 02900 conn_get_table_list( dbi_conn conn, const gchar* dbname ) 02901 { 02902 dbi_result tables; 02903 GSList* list = NULL; 02904 02905 tables = dbi_conn_get_table_list( conn, dbname, NULL ); 02906 while ( dbi_result_next_row( tables ) != 0 ) 02907 { 02908 const gchar* table_name; 02909 dbi_result result; 02910 02911 table_name = dbi_result_get_string_idx( tables, 1 ); 02912 list = g_slist_prepend( list, strdup( table_name ) ); 02913 } 02914 dbi_result_free( tables ); 02915 return list; 02916 } 02917 02918 static GSList* 02919 conn_get_table_list_sqlite3( dbi_conn conn, const gchar* dbname ) 02920 { 02921 gboolean change_made; 02922 02923 /* Return the list, but remove the tables that sqlite3 adds for 02924 * its own use. */ 02925 GSList* list = conn_get_table_list( conn, dbname ); 02926 change_made = TRUE; 02927 while ( list != NULL && change_made ) 02928 { 02929 GSList* node; 02930 02931 change_made = FALSE; 02932 for ( node = list; node != NULL; node = node->next ) 02933 { 02934 const gchar* table_name = (const gchar*)node->data; 02935 02936 if ( strcmp( table_name, "sqlite_sequence" ) == 0 ) 02937 { 02938 g_free( node->data ); 02939 list = g_slist_delete_link( list, node ); 02940 change_made = TRUE; 02941 break; 02942 } 02943 } 02944 } 02945 return list; 02946 } 02947 02948 static GSList* 02949 conn_get_table_list_pgsql( dbi_conn conn, const gchar* dbname ) 02950 { 02951 gboolean change_made; 02952 02953 /* Return the list, but remove the tables that postgresql adds from the information schema. */ 02954 GSList* list = conn_get_table_list( conn, dbname ); 02955 change_made = TRUE; 02956 while ( list != NULL && change_made ) 02957 { 02958 GSList* node; 02959 02960 change_made = FALSE; 02961 for ( node = list; node != NULL; node = node->next ) 02962 { 02963 const gchar* table_name = (const gchar*)node->data; 02964 02965 if ( strcmp( table_name, "sql_features" ) == 0 || 02966 strcmp( table_name, "sql_implementation_info" ) == 0 || 02967 strcmp( table_name, "sql_languages" ) == 0 || 02968 strcmp( table_name, "sql_packages" ) == 0 || 02969 strcmp( table_name, "sql_parts" ) == 0 || 02970 strcmp( table_name, "sql_sizing" ) == 0 || 02971 strcmp( table_name, "sql_sizing_profiles" ) == 0 ) 02972 { 02973 g_free( node->data ); 02974 list = g_slist_delete_link( list, node ); 02975 change_made = TRUE; 02976 break; 02977 } 02978 } 02979 } 02980 return list; 02981 } 02982 02992 static GncDbiTestResult 02993 conn_test_dbi_library( dbi_conn conn ) 02994 { 02995 gint64 testlonglong = -9223372036854775807LL, resultlonglong = 0; 02996 guint64 testulonglong = 9223372036854775807LLU, resultulonglong = 0; 02997 gdouble testdouble = 1.7976921348623157E+307, resultdouble = 0.0; 02998 dbi_result result; 02999 gchar doublestr[G_ASCII_DTOSTR_BUF_SIZE], *querystr; 03000 GncDbiTestResult retval = GNC_DBI_PASS; 03001 memset( doublestr, 0, sizeof(doublestr)); 03002 03003 result = dbi_conn_query( conn, "CREATE TEMPORARY TABLE numtest " 03004 "( test_int BIGINT, test_unsigned BIGINT," 03005 " test_double FLOAT8 )" ); 03006 if ( result == NULL ) 03007 { 03008 PWARN("Test_DBI_Library: Create table failed"); 03009 return GNC_DBI_FAIL_SETUP; 03010 } 03011 dbi_result_free( result ); 03012 g_ascii_dtostr( doublestr, sizeof(doublestr), testdouble ); 03013 querystr = g_strdup_printf( "INSERT INTO numtest VALUES (%" G_GINT64_FORMAT 03014 ", %" G_GUINT64_FORMAT ", %s)", 03015 testlonglong, testulonglong, doublestr ); 03016 result = dbi_conn_query( conn, querystr ); 03017 g_free( querystr ); 03018 if ( result == NULL ) 03019 { 03020 PWARN("Test_DBI_Library: Failed to insert test row into table" ); 03021 return GNC_DBI_FAIL_SETUP; 03022 } 03023 dbi_result_free( result ); 03024 gnc_push_locale( LC_NUMERIC, "C"); 03025 result = dbi_conn_query( conn, "SELECT * FROM numtest" ); 03026 if ( result == NULL ) 03027 { 03028 const char *errmsg; 03029 dbi_conn_error( conn, &errmsg ); 03030 PWARN("Test_DBI_Library: Failed to retrieve test row into table: %s", 03031 errmsg ); 03032 result = dbi_conn_query( conn, "DROP TABLE numtest" ); 03033 gnc_pop_locale( LC_NUMERIC ); 03034 return GNC_DBI_FAIL_SETUP; 03035 } 03036 while ( dbi_result_next_row( result )) 03037 { 03038 resultlonglong = dbi_result_get_longlong( result, "test_int" ); 03039 resultulonglong = dbi_result_get_ulonglong( result, "test_unsigned" ); 03040 resultdouble = dbi_result_get_double( result, "test_double" ); 03041 } 03042 gnc_pop_locale( LC_NUMERIC ); 03043 if ( testlonglong != resultlonglong ) 03044 { 03045 PWARN( "Test_DBI_Library: LongLong Failed %" G_GINT64_FORMAT " != % " G_GINT64_FORMAT, 03046 testlonglong, resultlonglong ); 03047 retval = GNC_DBI_FAIL_TEST; 03048 } 03049 if ( testulonglong != resultulonglong ) 03050 { 03051 PWARN( "Test_DBI_Library: Unsigned longlong Failed %" G_GUINT64_FORMAT " != %" G_GUINT64_FORMAT, 03052 testulonglong, resultulonglong ); 03053 retval = GNC_DBI_FAIL_TEST; 03054 } 03055 /* A bug in libdbi stores only 7 digits of precision */ 03056 if ( testdouble >= resultdouble + 0.000001e307 || 03057 testdouble <= resultdouble - 0.000001e307 ) 03058 { 03059 PWARN( "Test_DBI_Library: Double Failed %17e != %17e", 03060 testdouble, resultdouble ); 03061 retval = GNC_DBI_FAIL_TEST; 03062 } 03063 return retval; 03064 } 03065 03066 03067 static GncSqlConnection* 03068 create_dbi_connection( /*@ observer @*/ provider_functions_t* provider, 03069 /*@ observer @*/ QofBackend* qbe, 03070 /*@ observer @*/ dbi_conn conn ) 03071 { 03072 GncDbiSqlConnection* dbi_conn; 03073 03074 dbi_conn = g_new0( GncDbiSqlConnection, 1 ); 03075 g_assert( dbi_conn != NULL ); 03076 03077 dbi_conn->base.dispose = conn_dispose; 03078 dbi_conn->base.executeSelectStatement = conn_execute_select_statement; 03079 dbi_conn->base.executeNonSelectStatement = conn_execute_nonselect_statement; 03080 dbi_conn->base.createStatementFromSql = conn_create_statement_from_sql; 03081 dbi_conn->base.doesTableExist = conn_does_table_exist; 03082 dbi_conn->base.beginTransaction = conn_begin_transaction; 03083 dbi_conn->base.rollbackTransaction = conn_rollback_transaction; 03084 dbi_conn->base.commitTransaction = conn_commit_transaction; 03085 dbi_conn->base.createTable = conn_create_table; 03086 dbi_conn->base.createIndex = conn_create_index; 03087 dbi_conn->base.addColumnsToTable = conn_add_columns_to_table; 03088 dbi_conn->base.quoteString = conn_quote_string; 03089 dbi_conn->qbe = qbe; 03090 dbi_conn->conn = conn; 03091 dbi_conn->provider = provider; 03092 dbi_conn->conn_ok = TRUE; 03093 gnc_dbi_init_error(dbi_conn); 03094 03095 return (GncSqlConnection*)dbi_conn; 03096 } 03097 03098 /* ========================== END OF FILE ===================== */
1.7.4