GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
gnc-backend-dbi.c
Go to the documentation of this file.
1 /********************************************************************
2  * gnc-backend-dbi.c: load and save data to SQL via libdbi *
3  * *
4  * This program is free software; you can redistribute it and/or *
5  * modify it under the terms of the GNU General Public License as *
6  * published by the Free Software Foundation; either version 2 of *
7  * the License, or (at your option) any later version. *
8  * *
9  * This program is distributed in the hope that it will be useful, *
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
12  * GNU General Public License for more details. *
13  * *
14  * You should have received a copy of the GNU General Public License*
15  * along with this program; if not, contact: *
16  * *
17  * Free Software Foundation Voice: +1-617-542-5942 *
18  * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
19  * Boston, MA 02110-1301, USA [email protected] *
20 \********************************************************************/
29 #include "config.h"
30 
31 #include <errno.h>
32 #include <glib.h>
33 #include <glib/gstdio.h>
34 #if !HAVE_GMTIME_R
35 #include "gmtime_r.h"
36 #endif
37 
38 #include "gnc-backend-dbi-priv.h"
39 
40 #include "qof.h"
41 #include "qofquery-p.h"
42 #include "qofquerycore-p.h"
43 #include "Account.h"
44 #include "TransLog.h"
45 #include "gnc-engine.h"
46 #include "SX-book.h"
47 #include "Recurrence.h"
48 
49 #include "gnc-uri-utils.h"
50 #include "gnc-filepath-utils.h"
51 #include "gnc-locale-utils.h"
52 
53 #include "gnc-backend-dbi.h"
54 
55 #include "gnc-prefs.h"
56 
57 #ifdef S_SPLINT_S
58 #include "splint-defs.h"
59 #endif
60 
61 #ifdef G_OS_WIN32
62 #include <winsock2.h>
63 #define GETPID() GetCurrentProcessId()
64 #else
65 #include <limits.h>
66 #include <unistd.h>
67 #define GETPID() getpid()
68 #endif
69 
70 #if LIBDBI_VERSION >= 900
71 #define HAVE_LIBDBI_R 1
72 static dbi_inst dbi_instance = NULL;
73 #else
74 #define HAVE_LIBDBI_R 0
75 #endif
76 
77 /* For direct access to dbi data structs, sadly needed for datetime */
78 #include <dbi/dbi-dev.h>
79 
80 #define GNC_HOST_NAME_MAX 255
81 #define TRANSACTION_NAME "trans"
82 
83 static QofLogModule log_module = G_LOG_DOMAIN;
84 
85 static gchar lock_table[] = "gnclock";
86 
87 #define FILE_URI_TYPE "file"
88 #define FILE_URI_PREFIX (FILE_URI_TYPE "://")
89 #define SQLITE3_URI_TYPE "sqlite3"
90 #define SQLITE3_URI_PREFIX (SQLITE3_URI_TYPE "://")
91 #define PGSQL_DEFAULT_PORT 5432
92 
93 static /*@ null @*/ gchar* conn_create_table_ddl_sqlite3( GncSqlConnection* conn,
94  const gchar* table_name,
95  const GList* col_info_list );
96 static GSList* conn_get_table_list( dbi_conn conn, const gchar* dbname );
97 static GSList* conn_get_table_list_sqlite3( dbi_conn conn, const gchar* dbname );
98 static void append_sqlite3_col_def( GString* ddl, GncSqlColumnInfo* info );
99 static GSList *conn_get_index_list_sqlite3( dbi_conn conn );
100 static void conn_drop_index_sqlite3 (dbi_conn conn, const gchar *index );
101 static provider_functions_t provider_sqlite3 =
102 {
103  conn_create_table_ddl_sqlite3,
104  conn_get_table_list_sqlite3,
105  append_sqlite3_col_def,
106  conn_get_index_list_sqlite3,
107  conn_drop_index_sqlite3
108 };
109 #define SQLITE3_TIMESPEC_STR_FORMAT "%04d%02d%02d%02d%02d%02d"
110 
111 static /*@ null @*/ gchar* conn_create_table_ddl_mysql( GncSqlConnection* conn,
112  const gchar* table_name,
113  const GList* col_info_list );
114 static void append_mysql_col_def( GString* ddl, GncSqlColumnInfo* info );
115 static GSList *conn_get_index_list_mysql( dbi_conn conn );
116 static void conn_drop_index_mysql (dbi_conn conn, const gchar *index );
117 static provider_functions_t provider_mysql =
118 {
119  conn_create_table_ddl_mysql,
120  conn_get_table_list,
121  append_mysql_col_def,
122  conn_get_index_list_mysql,
123  conn_drop_index_mysql
124 };
125 #define MYSQL_TIMESPEC_STR_FORMAT "%04d%02d%02d%02d%02d%02d"
126 
127 static /*@ null @*/ gchar* conn_create_table_ddl_pgsql( GncSqlConnection* conn,
128  const gchar* table_name,
129  const GList* col_info_list );
130 static GSList* conn_get_table_list_pgsql( dbi_conn conn, const gchar* dbname );
131 static void append_pgsql_col_def( GString* ddl, GncSqlColumnInfo* info );
132 static GSList *conn_get_index_list_pgsql( dbi_conn conn );
133 static void conn_drop_index_pgsql (dbi_conn conn, const gchar *index );
134 
135 static provider_functions_t provider_pgsql =
136 {
137  conn_create_table_ddl_pgsql,
138  conn_get_table_list_pgsql,
139  append_pgsql_col_def,
140  conn_get_index_list_pgsql,
141  conn_drop_index_pgsql
142 };
143 #define PGSQL_TIMESPEC_STR_FORMAT "%04d%02d%02d %02d%02d%02d"
144 
145 static gboolean gnc_dbi_lock_database( QofBackend *qbe, gboolean ignore_lock );
146 static void gnc_dbi_unlock( QofBackend *qbe );
147 static gboolean save_may_clobber_data( QofBackend* qbe );
148 
149 static /*@ null @*/ gchar* create_index_ddl( GncSqlConnection* conn,
150  const gchar* index_name,
151  const gchar* table_name,
152  const GncSqlColumnTableEntry* col_table );
153 static /*@ null @*/ gchar* add_columns_ddl( GncSqlConnection* conn,
154  const gchar* table_name,
155  GList* col_info_list );
156 static GncSqlConnection* create_dbi_connection( /*@ observer @*/ provider_functions_t* provider, /*@ observer @*/ QofBackend* qbe, /*@ observer @*/ dbi_conn conn );
157 static GncDbiTestResult conn_test_dbi_library( dbi_conn conn );
158 #define GNC_DBI_PROVIDER_SQLITE (&provider_sqlite3)
159 #define GNC_DBI_PROVIDER_MYSQL (&provider_mysql)
160 #define GNC_DBI_PROVIDER_PGSQL (&provider_pgsql)
161 
162 
163 #define DBI_MAX_CONN_ATTEMPTS 5
164 
165 /* ================================================================= */
166 
167 /* Free the contents of a GSList, then free the list. Don't use this
168  * if the elements of the list weren't created with g_new! */
169 static void
170 gnc_table_slist_free( GSList *table_list )
171 {
172  GSList *list;
173  for ( list = table_list; list != NULL; list = g_slist_next( list ))
174  {
175  g_free( list->data );
176  }
177  g_slist_free( table_list );
178 }
179 
180 static void
181 gnc_dbi_set_error( GncDbiSqlConnection* dbi_conn, gint last_error,
182  gint error_repeat, gboolean retry )
183 {
184  g_return_if_fail( dbi_conn != NULL );
185 
186  dbi_conn->last_error = last_error;
187  if ( error_repeat > 0 )
188  dbi_conn->error_repeat = dbi_conn->error_repeat + error_repeat;
189  else
190  dbi_conn->error_repeat = 0;
191  dbi_conn->retry = retry;
192 }
193 
194 static void
195 gnc_dbi_init_error( GncDbiSqlConnection* dbi_conn )
196 {
197  gnc_dbi_set_error( dbi_conn, ERR_BACKEND_NO_ERR, 0, FALSE );
198 }
199 
200 /* Check if the dbi connection is valid. If not attempt to re-establish it
201  * Returns TRUE is there is a valid connection in the end or FALSE otherwise
202  */
203 static gboolean
204 gnc_dbi_verify_conn( GncDbiSqlConnection* dbi_conn )
205 {
206  if ( dbi_conn->conn_ok )
207  return TRUE;
208 
209  /* We attempt to connect only once here. The error function will automatically
210  * re-attempt up until DBI_MAX_CONN_ATTEMPTS time to connect if this call fails.
211  * After all these attempts, conn_ok will indicate if there is a valid connection
212  * or not.
213  */
214  gnc_dbi_init_error( dbi_conn );
215  dbi_conn->conn_ok = TRUE;
216  (void)dbi_conn_connect( dbi_conn->conn );
217 
218  return dbi_conn->conn_ok;
219 }
220 
221 /* ================================================================= */
222 
223 static void
224 create_tables_cb( const gchar* type, gpointer data_p, gpointer be_p )
225 {
226  GncSqlObjectBackend* pData = data_p;
227  GncDbiBackend* be = be_p;
228 
229  g_return_if_fail( type != NULL && data_p != NULL && be_p != NULL );
230  g_return_if_fail( pData->version == GNC_SQL_BACKEND_VERSION );
231 
232  if ( pData->create_tables != NULL )
233  {
234  (pData->create_tables)( &be->sql_be );
235  }
236 }
237 
238 static void
239 sqlite3_error_fn( dbi_conn conn, /*@ unused @*/ void* user_data )
240 {
241  const gchar* msg;
242 
243  (void)dbi_conn_error( conn, &msg );
244  PERR( "DBI error: %s\n", msg );
245  gnc_dbi_set_error( conn, ERR_BACKEND_MISC, 0, FALSE );
246 }
247 
248 static void
249 gnc_dbi_sqlite3_session_begin( QofBackend *qbe, QofSession *session,
250  const gchar *book_id, gboolean ignore_lock,
251  gboolean create, gboolean force )
252 {
253  GncDbiBackend *be = (GncDbiBackend*)qbe;
254  gint result;
255  gchar* dirname = NULL;
256  gchar* basename = NULL;
257  gchar *filepath = NULL;
258  gchar *msg = " ";
259  gboolean file_exists;
260  GncDbiTestResult dbi_test_result = GNC_DBI_PASS;
261 
262  g_return_if_fail( qbe != NULL );
263  g_return_if_fail( session != NULL );
264  g_return_if_fail( book_id != NULL );
265 
266  ENTER (" ");
267 
268  /* Remove uri type if present */
269  filepath = gnc_uri_get_path ( book_id );
270  file_exists = g_file_test( filepath,
271  G_FILE_TEST_IS_REGULAR | G_FILE_TEST_EXISTS );
272  if ( !create && !file_exists )
273  {
275  qof_backend_set_message(qbe, "Sqlite3 file %s not found", filepath);
276  PWARN ("Sqlite3 file %s not found", filepath);
277  goto exit;
278  }
279 
280  if ( create && !force && file_exists )
281  {
283  msg = "Might clobber, no force";
284  PWARN ("%s", msg);
285  goto exit;
286  }
287 
288 
289  if ( be->conn != NULL )
290  {
291  dbi_conn_close( be->conn );
292  }
293 
294  #if HAVE_LIBDBI_R
295  if (dbi_instance)
296  be->conn = dbi_conn_new_r( "sqlite3", dbi_instance );
297  else
298  PERR ("Attempt to connect with an uninitialized dbi_instance");
299  #else
300  be->conn = dbi_conn_new( "sqlite3" );
301  #endif
302 
303  if ( be->conn == NULL )
304  {
305  PERR( "Unable to create sqlite3 dbi connection\n" );
307  goto exit;
308  }
309 
310  dirname = g_path_get_dirname( filepath );
311  basename = g_path_get_basename( filepath );
312  dbi_conn_error_handler( be->conn, sqlite3_error_fn, be );
313  /* dbi-sqlite3 documentation says that sqlite3 doesn't take a "host" option */
314  result = dbi_conn_set_option( be->conn, "host", "localhost" );
315  if ( result < 0 )
316  {
317  PERR( "Error setting 'host' option\n" );
319  goto exit;
320  }
321  result = dbi_conn_set_option( be->conn, "dbname", basename );
322  if ( result < 0 )
323  {
324  PERR( "Error setting 'dbname' option\n" );
326  goto exit;
327  }
328  result = dbi_conn_set_option( be->conn, "sqlite3_dbdir", dirname );
329  if ( result < 0 )
330  {
331  PERR( "Error setting 'sqlite3_dbdir' option\n" );
333  goto exit;
334  }
335  result = dbi_conn_connect( be->conn );
336 
337  if ( result < 0 )
338  {
339  PERR( "Unable to connect to %s: %d\n", book_id, result );
341  goto exit;
342  }
343 
344  dbi_test_result = conn_test_dbi_library( be->conn );
345  switch ( dbi_test_result )
346  {
347  case GNC_DBI_PASS:
348  break;
349 
350  case GNC_DBI_FAIL_SETUP:
353  "SQLite3: Failed to setup for large number test" );
354  break;
355 
356  case GNC_DBI_FAIL_TEST:
359  "SQLite3 DBI library fails large number test" );
360  break;
361  }
362  if ( dbi_test_result != GNC_DBI_PASS )
363  {
364  if ( create && !file_exists ) /* File didn't exist before, but it */
365  {
366  /* does now, and we don't want to */
367  dbi_conn_close( be->conn );/* leave it lying around. */
368  be->conn = NULL;
369  g_unlink( filepath );
370  }
371  msg = "Bad DBI Library";
372  goto exit;
373  }
374  if ( !gnc_dbi_lock_database( qbe, ignore_lock ) )
375  {
377  msg = "Locked";
378  goto exit;
379  }
380 
381  if ( be->sql_be.conn != NULL )
382  {
383  gnc_sql_connection_dispose( be->sql_be.conn );
384  }
385  be->sql_be.conn = create_dbi_connection( GNC_DBI_PROVIDER_SQLITE, qbe, be->conn );
386  be->sql_be.timespec_format = SQLITE3_TIMESPEC_STR_FORMAT;
387 
388  /* We should now have a proper session set up.
389  * Let's start logging */
390  xaccLogSetBaseName (filepath);
391  PINFO ("logpath=%s", filepath ? filepath : "(null)");
392 
393 exit:
394  if ( filepath != NULL ) g_free ( filepath );
395  if ( basename != NULL ) g_free( basename );
396  if ( dirname != NULL ) g_free( dirname );
397  LEAVE ( "%s", msg );
398 }
399 
400 static GSList*
401 conn_get_index_list_sqlite3( dbi_conn conn )
402 {
403  GSList *list = NULL;
404  const gchar *errmsg;
405  dbi_result result = dbi_conn_query( conn, "SELECT name FROM sqlite_master WHERE type = 'index' AND name NOT LIKE 'sqlite_autoindex%'" );
406  if ( dbi_conn_error( conn, &errmsg ) != DBI_ERROR_NONE )
407  {
408  g_print( "Index Table Retrieval Error: %s\n", errmsg );
409  return NULL;
410  }
411  while ( dbi_result_next_row( result ) != 0 )
412  {
413  const gchar* index_name;
414 
415  index_name = dbi_result_get_string_idx( result, 1 );
416  list = g_slist_prepend( list, strdup( index_name ) );
417  }
418  dbi_result_free( result );
419  return list;
420 }
421 
422 static void
423 conn_drop_index_sqlite3 (dbi_conn conn, const gchar *index )
424 {
425  dbi_result result = dbi_conn_queryf (conn, "DROP INDEX %s", index);
426  if ( result )
427  dbi_result_free( result );
428 }
429 
430 static void
431 mysql_error_fn( dbi_conn conn, void* user_data )
432 {
433  GncDbiBackend *be = (GncDbiBackend*)user_data;
434  GncDbiSqlConnection *dbi_conn = (GncDbiSqlConnection*)be->sql_be.conn;
435  const gchar* msg;
436  gint err_num;
437 #ifdef G_OS_WIN32
438  const guint backoff_msecs = 1;
439 #else
440  const guint backoff_usecs = 1000;
441 #endif
442 
443  err_num = dbi_conn_error( conn, &msg );
444 
445  /* Note: the sql connection may not have been initialized yet
446  * so let's be careful with using it
447  */
448 
449  /* Database doesn't exist. When this error is triggered the
450  * GncDbiSqlConnection may not exist yet either, so don't use it here
451  */
452  if ( err_num == 1049 ) // Database doesn't exist
453  {
454  PINFO( "DBI error: %s\n", msg );
455  be->exists = FALSE;
456  return;
457  }
458 
459  /* All the other error handling code assumes the GncDbiSqlConnection
460  * has been initialized. So let's assert it exits here, otherwise
461  * simply return.
462  */
463  if (!dbi_conn)
464  {
465  PINFO( "DBI error: %s\n", msg );
466  PINFO( "Note: GbcDbiSqlConnection not yet initialized. Skipping further error processing." );
467  return;
468  }
469 
470  /* Test for other errors */
471  if ( err_num == 2006 ) // Server has gone away
472  {
473  PINFO( "DBI error: %s - Reconnecting...\n", msg );
474  if (dbi_conn)
475  gnc_dbi_set_error( dbi_conn, ERR_BACKEND_CONN_LOST, 1, TRUE );
476  dbi_conn->conn_ok = TRUE;
477  (void)dbi_conn_connect( conn );
478  }
479  else if ( err_num == 2003 ) // Unable to connect
480  {
481  if (dbi_conn->error_repeat >= DBI_MAX_CONN_ATTEMPTS )
482  {
483  PERR( "DBI error: %s - Giving up after %d consecutive attempts.\n", msg, DBI_MAX_CONN_ATTEMPTS );
484  if (dbi_conn)
485  gnc_dbi_set_error( dbi_conn, ERR_BACKEND_CANT_CONNECT, 0, FALSE );
486  dbi_conn->conn_ok = FALSE;
487  }
488  else
489  {
490 #ifdef G_OS_WIN32
491  Sleep (backoff_msecs * 2 << dbi_conn->error_repeat);
492 #else
493  usleep (backoff_usecs * 2 << dbi_conn->error_repeat);
494 #endif
495  PINFO( "DBI error: %s - Reconnecting...\n", msg );
496  if (dbi_conn)
497  gnc_dbi_set_error( dbi_conn, ERR_BACKEND_CANT_CONNECT, 1, TRUE );
498  dbi_conn->conn_ok = TRUE;
499  (void)dbi_conn_connect( conn );
500  }
501  }
502  else // Any other error
503  {
504  PERR( "DBI error: %s\n", msg );
505  if (dbi_conn)
506  gnc_dbi_set_error( dbi_conn, ERR_BACKEND_MISC, 0, FALSE );
507  }
508 }
509 
522 static gboolean
523 set_standard_connection_options( QofBackend* qbe, dbi_conn conn, const gchar* host, int port,
524  const gchar* dbname, const gchar* username, const gchar* password )
525 {
526  gint result;
527 
528  result = dbi_conn_set_option( conn, "host", host );
529  if ( result < 0 )
530  {
531  PERR( "Error setting 'host' option\n" );
533  return FALSE;
534  }
535  result = dbi_conn_set_option_numeric( conn, "port", port );
536  if ( result < 0 )
537  {
538  PERR( "Error setting 'port' option\n" );
540  return FALSE;
541  }
542  result = dbi_conn_set_option( conn, "dbname", dbname );
543  if ( result < 0 )
544  {
545  PERR( "Error setting 'dbname' option\n" );
547  return FALSE;
548  }
549  result = dbi_conn_set_option( conn, "username", username );
550  if ( result < 0 )
551  {
552  PERR( "Error setting 'username' option\n" );
554  return FALSE;
555  }
556  result = dbi_conn_set_option( conn, "password", password );
557  if ( result < 0 )
558  {
559  PERR( "Error setting 'password' option\n" );
561  return FALSE;
562  }
563 
564  result = dbi_conn_set_option( conn, "encoding", "UTF-8" );
565  if ( result < 0 )
566  {
567  PERR( "Error setting 'encoding' option\n" );
569  return FALSE;
570  }
571 
572  return TRUE;
573 }
574 
575 
576 static gboolean
577 gnc_dbi_lock_database ( QofBackend* qbe, gboolean ignore_lock )
578 {
579 
580  GncDbiBackend *qe = (GncDbiBackend*)qbe;
581  dbi_conn dcon = qe->conn;
582  dbi_result result;
583  const gchar *dbname = dbi_conn_get_option( dcon, "dbname" );
584  /* Create the table if it doesn't exist */
585  result = dbi_conn_get_table_list( dcon, dbname, lock_table);
586  if (!( result && dbi_result_get_numrows( result ) ))
587  {
588  if ( result )
589  {
590  dbi_result_free( result );
591  result = NULL;
592  }
593  result = dbi_conn_queryf( dcon, "CREATE TABLE %s ( Hostname varchar(%d), PID int )", lock_table, GNC_HOST_NAME_MAX );
594  if ( dbi_conn_error( dcon, NULL ) )
595  {
596  const gchar *errstr;
597  dbi_conn_error( dcon, &errstr );
598  PERR( "Error %s creating lock table", errstr );
600  if ( result )
601  {
602  dbi_result_free( result );
603  result = NULL;
604  }
605  return FALSE;
606  }
607  if ( result )
608  {
609  dbi_result_free( result );
610  result = NULL;
611  }
612  }
613  if (result)
614  {
615  dbi_result_free( result );
616  result = NULL;
617  }
618 
619  /* Protect everything with a single transaction to prevent races */
620  if ( (result = dbi_conn_query( dcon, "BEGIN" )) )
621  {
622  /* Check for an existing entry; delete it if ignore_lock is true, otherwise fail */
623  gchar hostname[ GNC_HOST_NAME_MAX + 1 ];
624  if (result)
625  {
626  dbi_result_free( result );
627  result = NULL;
628  }
629  result = dbi_conn_queryf( dcon, "SELECT * FROM %s", lock_table );
630  if ( result && dbi_result_get_numrows( result ) )
631  {
632  dbi_result_free( result );
633  result = NULL;
634  if ( !ignore_lock )
635  {
637  /* FIXME: After enhancing the qof_backend_error mechanism, report in the dialog what is the hostname of the machine holding the lock. */
638  dbi_conn_query( dcon, "ROLLBACK" );
639  return FALSE;
640  }
641  result = dbi_conn_queryf( dcon, "DELETE FROM %s", lock_table );
642  if ( !result)
643  {
645  qof_backend_set_message( qbe, "Failed to delete lock record" );
646  result = dbi_conn_query( dcon, "ROLLBACK" );
647  if (result)
648  {
649  dbi_result_free( result );
650  result = NULL;
651  }
652  return FALSE;
653  }
654  if (result)
655  {
656  dbi_result_free( result );
657  result = NULL;
658  }
659  }
660  /* Add an entry and commit the transaction */
661  memset( hostname, 0, sizeof(hostname) );
662  gethostname( hostname, GNC_HOST_NAME_MAX );
663  result = dbi_conn_queryf( dcon,
664  "INSERT INTO %s VALUES ('%s', '%d')",
665  lock_table, hostname, (int)GETPID() );
666  if ( !result)
667  {
669  qof_backend_set_message( qbe, "Failed to create lock record" );
670  result = dbi_conn_query( dcon, "ROLLBACK" );
671  if (result)
672  {
673  dbi_result_free( result );
674  result = NULL;
675  }
676  return FALSE;
677  }
678  if (result)
679  {
680  dbi_result_free( result );
681  result = NULL;
682  }
683  result = dbi_conn_query( dcon, "COMMIT" );
684  if (result)
685  {
686  dbi_result_free( result );
687  result = NULL;
688  }
689  return TRUE;
690  }
691  /* Couldn't get a transaction (probably couldn't get a lock), so fail */
693  qof_backend_set_message( qbe, "SQL Backend failed to obtain a transaction" );
694  if (result)
695  {
696  dbi_result_free( result );
697  result = NULL;
698  }
699  return FALSE;
700 }
701 static void
702 gnc_dbi_unlock( QofBackend *qbe )
703 {
704  GncDbiBackend *qe = (GncDbiBackend*)qbe;
705  dbi_conn dcon = qe->conn;
706  dbi_result result;
707  const gchar *dbname = NULL;
708 
709  g_return_if_fail( dcon != NULL );
710  g_return_if_fail( dbi_conn_error( dcon, NULL ) == 0 );
711 
712  dbname = dbi_conn_get_option( dcon, "dbname" );
713  /* Check if the lock table exists */
714  g_return_if_fail( dbname != NULL );
715  result = dbi_conn_get_table_list( dcon, dbname, lock_table);
716  if (!( result && dbi_result_get_numrows( result ) ))
717  {
718  if (result)
719  {
720  dbi_result_free( result );
721  result = NULL;
722  }
723  PWARN("No lock table in database, so not unlocking it.");
724  return;
725  }
726  dbi_result_free( result );
727 
728  result = dbi_conn_query( dcon, "BEGIN" );
729  if ( result )
730  {
731  /* Delete the entry if it's our hostname and PID */
732  gchar hostname[ GNC_HOST_NAME_MAX + 1 ];
733 
734  dbi_result_free( result );
735  result = NULL;
736  memset( hostname, 0, sizeof(hostname) );
737  gethostname( hostname, GNC_HOST_NAME_MAX );
738  result = dbi_conn_queryf( dcon, "SELECT * FROM %s WHERE Hostname = '%s' AND PID = '%d'", lock_table, hostname, (int)GETPID() );
739  if ( result && dbi_result_get_numrows( result ) )
740  {
741  if (result)
742  {
743  dbi_result_free( result );
744  result = NULL;
745  }
746  result = dbi_conn_queryf( dcon, "DELETE FROM %s", lock_table );
747  if ( !result)
748  {
749  PERR("Failed to delete the lock entry");
751  result = dbi_conn_query( dcon, "ROLLBACK" );
752  if (result)
753  {
754  dbi_result_free( result );
755  result = NULL;
756  }
757  return;
758  }
759  else
760  {
761  dbi_result_free( result );
762  result = NULL;
763  }
764  result = dbi_conn_query( dcon, "COMMIT" );
765  if (result)
766  {
767  dbi_result_free( result );
768  result = NULL;
769  }
770  return;
771  }
772  result = dbi_conn_query( dcon, "ROLLBACK" );
773  if (result)
774  {
775  dbi_result_free( result );
776  result = NULL;
777  }
778  PWARN("There was no lock entry in the Lock table");
779  return;
780  }
781  if (result)
782  {
783  dbi_result_free( result );
784  result = NULL;
785  }
786  PWARN("Unable to get a lock on LOCK, so failed to clear the lock entry.");
788 }
789 
790 static void
791 gnc_dbi_mysql_session_begin( QofBackend* qbe, QofSession *session,
792  const gchar *book_id, gboolean ignore_lock,
793  gboolean create, gboolean force )
794 {
795  GncDbiBackend *be = (GncDbiBackend*)qbe;
796  gchar* protocol = NULL;
797  gchar* host = NULL;
798  gchar* dbname = NULL;
799  gchar* username = NULL;
800  gchar* password = NULL;
801  gchar* basename = NULL;
802  gchar* translog_path = NULL;
803  gint portnum = 0;
804  gint result;
805  gboolean success = FALSE;
806  GncDbiTestResult dbi_test_result = GNC_DBI_PASS;
807 
808  g_return_if_fail( qbe != NULL );
809  g_return_if_fail( session != NULL );
810  g_return_if_fail( book_id != NULL );
811 
812  ENTER (" ");
813 
814  /* Split the book-id
815  * Format is protocol://username:password@hostname:port/dbname
816  where username, password and port are optional) */
817  gnc_uri_get_components ( book_id, &protocol, &host, &portnum,
818  &username, &password, &dbname );
819 
820  // Try to connect to the db. If it doesn't exist and the create
821  // flag is TRUE, we'll need to connect to the 'mysql' db and execute the
822  // CREATE DATABASE ddl statement there.
823  if ( be->conn != NULL )
824  {
825  dbi_conn_close( be->conn );
826  }
827 #if HAVE_LIBDBI_R
828  if (dbi_instance)
829  be->conn = dbi_conn_new_r( "mysql", dbi_instance );
830  else
831  PERR ("Attempt to connect with an uninitialized dbi_instance");
832 #else
833  be->conn = dbi_conn_new( "mysql" );
834 #endif
835  if ( be->conn == NULL )
836  {
837  PERR( "Unable to create mysql dbi connection\n" );
839  goto exit;
840  }
841  dbi_conn_error_handler( be->conn, mysql_error_fn, be );
842  if ( !set_standard_connection_options( qbe, be->conn, host, portnum, dbname, username, password ) )
843  {
844  goto exit;
845  }
846  be->exists = TRUE;
847  result = dbi_conn_connect( be->conn );
848  if ( result == 0 )
849  {
850  dbi_test_result = conn_test_dbi_library( be->conn );
851  switch ( dbi_test_result )
852  {
853  case GNC_DBI_PASS:
854  break;
855 
856  case GNC_DBI_FAIL_SETUP:
859  "DBI library large number test incomplete" );
860  break;
861 
862  case GNC_DBI_FAIL_TEST:
865  "DBI library fails large number test" );
866  break;
867  }
868  if ( GNC_DBI_PASS != dbi_test_result )
869  {
870  goto exit;
871  }
872  if (create && !force && save_may_clobber_data( qbe ) )
873  {
875  PWARN("Databse already exists, Might clobber it.");
876  goto exit;
877  }
878 
879  success = gnc_dbi_lock_database ( qbe, ignore_lock );
880  }
881  else
882  {
883 
884  if ( be->exists )
885  {
886  PERR( "Unable to connect to database '%s'\n", dbname );
888  goto exit;
889  }
890 
891  // The db does not already exist. Connect to the 'mysql' db and try to create it.
892  if ( create )
893  {
894  dbi_result dresult;
895  result = dbi_conn_set_option( be->conn, "dbname", "mysql" );
896  if ( result < 0 )
897  {
898  PERR( "Error setting 'dbname' option\n" );
900  goto exit;
901  }
902  result = dbi_conn_connect( be->conn );
903  if ( result < 0 )
904  {
905  PERR( "Unable to connect to 'mysql' database\n" );
907  goto exit;
908  }
909  dresult = dbi_conn_queryf( be->conn, "CREATE DATABASE %s CHARACTER SET utf8", dbname );
910  if ( dresult == NULL )
911  {
912  PERR( "Unable to create database '%s'\n", dbname );
914  goto exit;
915  }
916  dbi_conn_close( be->conn );
917 
918  // Try again to connect to the db
919  #if HAVE_LIBDBI_R
920  if (dbi_instance)
921  be->conn = dbi_conn_new_r( "mysql", dbi_instance );
922  else
923  PERR ("Attempt to connect with an uninitialized dbi_instance");
924  #else
925  be->conn = dbi_conn_new( "mysql" );
926  #endif
927 
928  if ( be->conn == NULL )
929  {
930  PERR( "Unable to create mysql dbi connection\n" );
932  goto exit;
933  }
934  dbi_conn_error_handler( be->conn, mysql_error_fn, be );
935  if ( !set_standard_connection_options( qbe, be->conn, host, 0, dbname, username, password ) )
936  {
937  goto exit;
938  }
939  result = dbi_conn_connect( be->conn );
940  if ( result < 0 )
941  {
942  PERR( "Unable to create database '%s'\n", dbname );
944  goto exit;
945  }
946  dbi_test_result = conn_test_dbi_library( be->conn );
947  switch ( dbi_test_result )
948  {
949  case GNC_DBI_PASS:
950  break;
951 
952  case GNC_DBI_FAIL_SETUP:
955  "MySql: Failed to setup for large number test" );
956  break;
957 
958  case GNC_DBI_FAIL_TEST:
961  "MySql DBI library fails large number test" );
962  break;
963  }
964  if ( dbi_test_result != GNC_DBI_PASS )
965  {
966  dbi_conn_queryf( be->conn, "DROP DATABASE %s", dbname );
967  goto exit;
968  }
969  success = gnc_dbi_lock_database ( qbe, ignore_lock );
970  }
971  else
972  {
974  qof_backend_set_message( qbe, "Database %s not found", dbname );
975  }
976  }
977 
978  if ( success )
979  {
980  dbi_result dresult;
981 
982  if ( be->sql_be.conn != NULL )
983  {
984  gnc_sql_connection_dispose( be->sql_be.conn );
985  }
986  be->sql_be.conn = create_dbi_connection( GNC_DBI_PROVIDER_MYSQL, qbe, be->conn );
987  }
988  be->sql_be.timespec_format = MYSQL_TIMESPEC_STR_FORMAT;
989 
990  /* We should now have a proper session set up.
991  * Let's start logging */
992  basename = g_strjoin("_", protocol, host, username, dbname, NULL);
993  translog_path = gnc_build_translog_path (basename);
994  xaccLogSetBaseName (translog_path);
995  PINFO ("logpath=%s", translog_path ? translog_path : "(null)");
996 
997 exit:
998  g_free( protocol );
999  g_free( host );
1000  g_free( username );
1001  g_free( password );
1002  g_free( basename );
1003  g_free( translog_path );
1004  g_free( dbname );
1005 
1006  LEAVE (" ");
1007 }
1008 
1009 static GSList*
1010 conn_get_index_list_mysql( dbi_conn conn )
1011 {
1012  GSList *index_list = NULL;
1013  dbi_result table_list;
1014  const char *errmsg;
1015  const gchar *dbname = dbi_conn_get_option( conn, "dbname" );
1016  g_return_val_if_fail( conn != NULL, NULL );
1017  table_list = dbi_conn_get_table_list( conn, dbname, NULL );
1018  if ( dbi_conn_error( conn, &errmsg ) != DBI_ERROR_NONE )
1019  {
1020  g_print( "Table Retrieval Error: %s\n", errmsg );
1021  return NULL;
1022  }
1023  while ( dbi_result_next_row( table_list ) != 0 )
1024  {
1025  dbi_result result;
1026  const gchar *table_name = dbi_result_get_string_idx( table_list, 1 );
1027  result = dbi_conn_queryf( conn,
1028  "SHOW INDEXES IN %s WHERE Key_name != 'PRIMARY'",
1029  table_name );
1030  if ( dbi_conn_error( conn, &errmsg ) != DBI_ERROR_NONE )
1031  {
1032  g_print( "Index Table Retrieval Error: %s\n", errmsg );
1033  continue;
1034  }
1035 
1036  while ( dbi_result_next_row( result ) != 0 )
1037  {
1038  const gchar* index_name = dbi_result_get_string_idx( result, 3 );
1039  index_list = g_slist_prepend( index_list, g_strjoin( " ", index_name, table_name, NULL ) );
1040  }
1041  dbi_result_free( result );
1042  }
1043 
1044  return index_list;
1045 }
1046 
1047 static void
1048 conn_drop_index_mysql (dbi_conn conn, const gchar *index )
1049 {
1050  dbi_result result;
1051  gchar **index_table_split = g_strsplit (index, " ", 2);
1052  int splitlen = -1;
1053 
1054  /* Check if the index split can be valid */
1055  while (index_table_split[++splitlen] != NULL)
1056  { /* do nothing, just count split members */ }
1057 
1058  if (splitlen != 2)
1059  {
1060  g_print ("Drop index error: invalid MySQL index format (<index> <table>): %s", index);
1061  return;
1062  }
1063 
1064  result = dbi_conn_queryf (conn, "DROP INDEX %s ON %s",
1065  index_table_split[0], index_table_split[1]);
1066  if ( result )
1067  dbi_result_free( result );
1068 
1069  g_strfreev (index_table_split);
1070 }
1071 
1072 static void
1073 pgsql_error_fn( dbi_conn conn, void* user_data )
1074 {
1075  GncDbiBackend *be = (GncDbiBackend*)user_data;
1076  GncDbiSqlConnection *dbi_conn = (GncDbiSqlConnection*)be->sql_be.conn;
1077  const gchar* msg;
1078 #ifdef G_OS_WIN32
1079  const guint backoff_msecs = 1;
1080 #else
1081  const guint backoff_usecs = 1000;
1082 #endif
1083 
1084  (void)dbi_conn_error( conn, &msg );
1085  if ( g_str_has_prefix( msg, "FATAL: database" ) &&
1086  g_str_has_suffix( msg, "does not exist\n" ) )
1087  {
1088  PINFO( "DBI error: %s\n", msg );
1089  be->exists = FALSE;
1090  gnc_dbi_set_error( dbi_conn, ERR_BACKEND_NO_SUCH_DB, 0, FALSE );
1091  }
1092  else if ( g_strrstr( msg, "server closed the connection unexpectedly" ) ) // Connection lost
1093  {
1094  if ( dbi_conn == NULL )
1095  {
1096  PWARN( "DBI Error: Connection lost, connection pointer invalid");
1097  return;
1098  }
1099  PINFO( "DBI error: %s - Reconnecting...\n", msg );
1100  gnc_dbi_set_error( dbi_conn, ERR_BACKEND_CONN_LOST, 1, TRUE );
1101  dbi_conn->conn_ok = TRUE;
1102  (void)dbi_conn_connect( conn );
1103  }
1104  else if ( dbi_conn &&
1105  ( g_str_has_prefix( msg, "connection pointer is NULL" ) ||
1106  g_str_has_prefix(msg, "could not connect to server" ) ) ) // No connection
1107  {
1108  if (dbi_conn->error_repeat >= DBI_MAX_CONN_ATTEMPTS )
1109  {
1110  PERR( "DBI error: %s - Giving up after %d consecutive attempts.\n", msg, DBI_MAX_CONN_ATTEMPTS );
1111  gnc_dbi_set_error( dbi_conn, ERR_BACKEND_CANT_CONNECT, 0, FALSE );
1112  dbi_conn->conn_ok = FALSE;
1113  }
1114  else
1115  {
1116 #ifdef G_OS_WIN32
1117  Sleep (backoff_msecs * 2 << dbi_conn->error_repeat);
1118 #else
1119  usleep (backoff_usecs * 2 << dbi_conn->error_repeat);
1120 #endif
1121  PINFO( "DBI error: %s - Reconnecting...\n", msg );
1122  gnc_dbi_set_error( dbi_conn, ERR_BACKEND_CANT_CONNECT, 1, TRUE );
1123  dbi_conn->conn_ok = TRUE;
1124  (void)dbi_conn_connect( conn );
1125  }
1126  }
1127  else
1128  {
1129  PERR( "DBI error: %s\n", msg );
1130  gnc_dbi_set_error( dbi_conn, ERR_BACKEND_MISC, 0, FALSE );
1131  }
1132 }
1133 
1134 static void
1135 gnc_dbi_postgres_session_begin( QofBackend *qbe, QofSession *session,
1136  const gchar *book_id, gboolean ignore_lock,
1137  gboolean create, gboolean force )
1138 {
1139  GncDbiBackend *be = (GncDbiBackend*)qbe;
1140  gint result = 0;
1141  gchar* protocol = NULL;
1142  gchar* host = NULL;
1143  gchar *dbname = NULL, *dbnamelc = NULL;
1144  gchar* username = NULL;
1145  gchar* password = NULL;
1146  gchar* basename = NULL;
1147  gchar* translog_path = NULL;
1148  gboolean success = FALSE;
1149  gint portnum = 0;
1150  GncDbiTestResult dbi_test_result = GNC_DBI_PASS;
1151 
1152  g_return_if_fail( qbe != NULL );
1153  g_return_if_fail( session != NULL );
1154  g_return_if_fail( book_id != NULL );
1155 
1156  ENTER (" ");
1157 
1158  /* Split the book-id
1159  * Format is protocol://username:password@hostname:port/dbname
1160  where username, password and port are optional) */
1161  gnc_uri_get_components ( book_id, &protocol, &host, &portnum,
1162  &username, &password, &dbname );
1163  if ( portnum == 0 )
1164  portnum = PGSQL_DEFAULT_PORT;
1165  /* Postgres's SQL interface coerces identifiers to lower case, but the
1166  * C interface is case-sensitive. This results in a mixed-case dbname
1167  * being created (with a lower case name) but then dbi can't conect to
1168  * it. To work around this, coerce the name to lowercase first. */
1169  dbnamelc = g_utf8_strdown( dbname, -1 );
1170 
1171  // Try to connect to the db. If it doesn't exist and the create
1172  // flag is TRUE, we'll need to connect to the 'postgres' db and execute the
1173  // CREATE DATABASE ddl statement there.
1174  if ( be->conn != NULL )
1175  {
1176  dbi_conn_close( be->conn );
1177  }
1178 
1179  #if HAVE_LIBDBI_R
1180  if (dbi_instance)
1181  be->conn = dbi_conn_new_r( "pgsql", dbi_instance );
1182  else
1183  PERR ("Attempt to connect with an uninitialized dbi_instance");
1184  #else
1185  be->conn = dbi_conn_new( "pgsql" );
1186  #endif
1187 
1188  if ( be->conn == NULL )
1189  {
1190  PERR( "Unable to create pgsql dbi connection\n" );
1192  goto exit;
1193  }
1194  dbi_conn_error_handler( be->conn, pgsql_error_fn, be );
1195  if ( !set_standard_connection_options( qbe, be->conn, host, portnum, dbnamelc, username, password ) )
1196  {
1197  goto exit;
1198  }
1199  be->exists = TRUE;
1200  result = dbi_conn_connect( be->conn );
1201  if ( result == 0 )
1202  {
1203  dbi_test_result = conn_test_dbi_library( be->conn );
1204  switch ( dbi_test_result )
1205  {
1206  case GNC_DBI_PASS:
1207  break;
1208 
1209  case GNC_DBI_FAIL_SETUP:
1212  "Postgresql: Failed to setup for large number test" );
1213  break;
1214 
1215  case GNC_DBI_FAIL_TEST:
1218  "Postgresql DBI library fails large number test" );
1219  break;
1220  }
1221  if ( dbi_test_result != GNC_DBI_PASS )
1222  {
1223  goto exit;
1224  }
1225  if (create && !force && save_may_clobber_data( qbe ) )
1226  {
1228  PWARN("Databse already exists, Might clobber it.");
1229  goto exit;
1230  }
1231 
1232  success = gnc_dbi_lock_database ( qbe, ignore_lock );
1233  }
1234  else
1235  {
1236 
1237  if ( be->exists )
1238  {
1239  PERR( "Unable to connect to database '%s'\n", dbname );
1241  goto exit;
1242  }
1243 
1244  // The db does not already exist. Connect to the 'postgres' db and try to create it.
1245  if ( create )
1246  {
1247  dbi_result dresult;
1248  result = dbi_conn_set_option( be->conn, "dbname", "postgres" );
1249  if ( result < 0 )
1250  {
1251  PERR( "Error setting 'dbname' option\n" );
1253  goto exit;
1254  }
1255  result = dbi_conn_connect( be->conn );
1256  if ( result < 0 )
1257  {
1258  PERR( "Unable to connect to 'postgres' database\n" );
1260  goto exit;
1261  }
1262  dresult = dbi_conn_queryf( be->conn, "CREATE DATABASE %s WITH TEMPLATE template0 ENCODING 'UTF8'", dbnamelc );
1263  if ( dresult == NULL )
1264  {
1265  PERR( "Unable to create database '%s'\n", dbname );
1267  goto exit;
1268  }
1269  dbi_conn_queryf( be->conn, "ALTER DATABASE %s SET standard_conforming_strings TO on", dbnamelc );
1270  dbi_conn_close( be->conn );
1271 
1272  // Try again to connect to the db
1273  #if HAVE_LIBDBI_R
1274  if (dbi_instance)
1275  be->conn = dbi_conn_new_r( "pgsql", dbi_instance );
1276  else
1277  PERR ("Attempt to connect with an uninitialized dbi_instance");
1278  #else
1279  be->conn = dbi_conn_new( "pgsql" );
1280  #endif
1281 
1282  if ( be->conn == NULL )
1283  {
1284  PERR( "Unable to create pgsql dbi connection\n" );
1286  goto exit;
1287  }
1288  dbi_conn_error_handler( be->conn, pgsql_error_fn, be );
1289  if ( !set_standard_connection_options( qbe, be->conn, host, PGSQL_DEFAULT_PORT, dbnamelc, username, password ) )
1290  {
1291  goto exit;
1292  }
1293  result = dbi_conn_connect( be->conn );
1294  if ( result < 0 )
1295  {
1296  PERR( "Unable to create database '%s'\n", dbname );
1298  goto exit;
1299  }
1300  dbi_test_result = conn_test_dbi_library( be->conn );
1301  switch ( dbi_test_result )
1302  {
1303  case GNC_DBI_PASS:
1304  break;
1305 
1306  case GNC_DBI_FAIL_SETUP:
1309  "DBI library large number test incomplete" );
1310  break;
1311 
1312  case GNC_DBI_FAIL_TEST:
1315  "DBI library fails large number test" );
1316  break;
1317  }
1318  if ( GNC_DBI_PASS != dbi_test_result )
1319  {
1320  dbi_conn_select_db( be->conn, "template1" );
1321  dbi_conn_queryf( be->conn, "DROP DATABASE %s", dbnamelc );
1322  goto exit;
1323  }
1324  success = gnc_dbi_lock_database ( qbe, ignore_lock );
1325  }
1326  else
1327  {
1329  qof_backend_set_message( qbe, "Database %s not found", dbname );
1330  }
1331  }
1332  if ( success )
1333  {
1334  if ( be->sql_be.conn != NULL )
1335  {
1336  gnc_sql_connection_dispose( be->sql_be.conn );
1337  }
1338  be->sql_be.conn = create_dbi_connection( GNC_DBI_PROVIDER_PGSQL, qbe, be->conn );
1339  }
1340  be->sql_be.timespec_format = PGSQL_TIMESPEC_STR_FORMAT;
1341 
1342  /* We should now have a proper session set up.
1343  * Let's start logging */
1344  basename = g_strjoin("_", protocol, host, username, dbname, NULL);
1345  translog_path = gnc_build_translog_path (basename);
1346  xaccLogSetBaseName (translog_path);
1347  PINFO ("logpath=%s", translog_path ? translog_path : "(null)");
1348 
1349 exit:
1350  g_free( protocol );
1351  g_free( host );
1352  g_free( username );
1353  g_free( password );
1354  g_free( basename );
1355  g_free( translog_path );
1356  g_free( dbname );
1357  g_free( dbnamelc );
1358 
1359  LEAVE (" ");
1360 }
1361 
1362 static GSList*
1363 conn_get_index_list_pgsql( dbi_conn conn )
1364 {
1365  GSList *list = NULL;
1366  const gchar *errmsg;
1367  dbi_result result;
1368  PINFO ( "Retrieving postgres index list\n");
1369  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'" );
1370  if ( dbi_conn_error( conn, &errmsg ) != DBI_ERROR_NONE )
1371  {
1372  g_print( "Index Table Retrieval Error: %s\n", errmsg );
1373  return NULL;
1374  }
1375  while ( dbi_result_next_row( result ) != 0 )
1376  {
1377  const gchar* index_name;
1378 
1379  index_name = dbi_result_get_string_idx( result, 1 );
1380  list = g_slist_prepend( list, strdup( index_name ) );
1381  }
1382  dbi_result_free( result );
1383  return list;
1384 }
1385 
1386 static void
1387 conn_drop_index_pgsql (dbi_conn conn, const gchar *index )
1388 {
1389  dbi_result result = dbi_conn_queryf (conn, "DROP INDEX %s", index);
1390  if ( result )
1391  dbi_result_free( result );
1392 }
1393 
1394 
1395 /* ================================================================= */
1396 
1397 static void
1398 gnc_dbi_session_end( QofBackend *be_start )
1399 {
1400  GncDbiBackend *be = (GncDbiBackend*)be_start;
1401 
1402  g_return_if_fail( be_start != NULL );
1403 
1404  ENTER (" ");
1405 
1406  if ( be->conn != NULL )
1407  {
1408  gnc_dbi_unlock( be_start );
1409  dbi_conn_close( be->conn );
1410  be->conn = NULL;
1411  }
1412  if ( be->sql_be.conn != NULL )
1413  {
1414  gnc_sql_connection_dispose( be->sql_be.conn );
1415  be->sql_be.conn = NULL;
1416  }
1417  gnc_sql_finalize_version_info( &be->sql_be );
1418 
1419  LEAVE (" ");
1420 }
1421 
1422 static void
1423 gnc_dbi_destroy_backend( /*@ only @*/ QofBackend *be )
1424 {
1425  g_return_if_fail( be != NULL );
1426 
1427  /* Stop transaction logging */
1428  xaccLogSetBaseName (NULL);
1429 
1430  qof_backend_destroy( be );
1431 
1432  g_free( be );
1433 }
1434 
1435 /* ================================================================= */
1436 
1437 /* GNUCASH_RESAVE_VERSION indicates the earliest database version
1438  * compatible with this version of Gnucash; the stored value is the
1439  * earliest version of Gnucash conpatible with the database. If the
1440  * GNUCASH_RESAVE_VERSION for this Gnucash is newer than the Gnucash
1441  * version which created the database, a resave is offered. If the
1442  * version of this Gnucash is older than the saved resave version,
1443  * then the database will be loaded read-only. A resave will update
1444  * both values to match this version of Gnucash.
1445  */
1446 static void
1447 gnc_dbi_load( QofBackend* qbe, /*@ dependent @*/ QofBook *book, QofBackendLoadType loadType )
1448 {
1449  GncDbiBackend *be = (GncDbiBackend*)qbe;
1450 
1451  g_return_if_fail( qbe != NULL );
1452  g_return_if_fail( book != NULL );
1453 
1454  ENTER( "be=%p, book=%p", be, book );
1455 
1456  if ( loadType == LOAD_TYPE_INITIAL_LOAD )
1457  {
1458  g_assert( be->primary_book == NULL );
1459  be->primary_book = book;
1460 
1461  // Set up table version information
1462  gnc_sql_init_version_info (&be->sql_be);
1463 
1464  // Call all object backends to create any required tables
1465  qof_object_foreach_backend( GNC_SQL_BACKEND, create_tables_cb, be );
1466  }
1467 
1468  gnc_sql_load( &be->sql_be, book, loadType );
1469 
1470  if ( GNUCASH_RESAVE_VERSION > gnc_sql_get_table_version( &be->sql_be, "Gnucash" ) )
1471  {
1472  /* The database was loaded with an older database schema or
1473  * data semantics. In order to ensure consistency, the whole
1474  * thing needs to be saved anew. */
1476  }
1477  else if ( GNUCASH_RESAVE_VERSION < gnc_sql_get_table_version( &be->sql_be,
1478  "Gnucash-Resave"))
1479  {
1480  /* Worse, the database was created with a newer version. We
1481  * can't safely write to this database, so the user will have
1482  * to do a "save as" to make one that we can write to.
1483  */
1485  }
1486 
1487 
1488  LEAVE( "" );
1489 }
1490 
1491 /* ================================================================= */
1492 
1493 static gboolean
1494 save_may_clobber_data( QofBackend* qbe )
1495 {
1496  GncDbiBackend* be = (GncDbiBackend*)qbe;
1497  const gchar* dbname;
1498  dbi_result result;
1499  gboolean retval = FALSE;
1500 
1501  /* Data may be clobbered iff the number of tables != 0 */
1502  dbname = dbi_conn_get_option( be->conn, "dbname" );
1503  result = dbi_conn_get_table_list( be->conn, dbname, NULL );
1504  if ( result )
1505  {
1506  retval = dbi_result_get_numrows( result ) > 0;
1507  dbi_result_free( result );
1508  }
1509  return retval;
1510 }
1511 
1512 static dbi_result
1513 conn_table_manage_backup (GncDbiSqlConnection *conn,
1514  gchar *table_name, TableOpType op )
1515 {
1516  gchar *new_name = g_strdup_printf( "%s_%s", table_name, "back" );
1517  dbi_result result = NULL;
1518  switch ( op )
1519  {
1520  case backup:
1521  result = dbi_conn_queryf( conn->conn, "ALTER TABLE %s RENAME TO %s",
1522  table_name, new_name );
1523  break;
1524  case rollback:
1525  result = dbi_conn_queryf( conn->conn,
1526  "ALTER TABLE %s RENAME TO %s",
1527  new_name, table_name );
1528  break;
1529  case drop_backup:
1530  result = dbi_conn_queryf( conn->conn, "DROP TABLE %s",
1531  new_name );
1532  break;
1533  default:
1534  break;
1535  }
1536  g_free( new_name );
1537  return result;
1538 }
1539 
1565 static gboolean
1566 conn_table_operation( GncSqlConnection *sql_conn, GSList *table_name_list,
1567  TableOpType op )
1568 {
1569  GSList* node;
1570  gboolean result = TRUE;
1571  GncDbiSqlConnection *conn = (GncDbiSqlConnection*)(sql_conn);
1572  GSList *full_table_name_list = NULL;
1573  const gchar *dbname = dbi_conn_get_option( conn->conn, "dbname" );
1574 
1575  g_return_val_if_fail( table_name_list != NULL, FALSE );
1576  if ( op == rollback )
1577  full_table_name_list =
1578  conn->provider->get_table_list( conn->conn, dbname );
1579 
1580  for ( node = table_name_list; node != NULL && result; node = node->next )
1581  {
1582  gchar* table_name = (gchar*)node->data;
1583  dbi_result result;
1584  /* Ignore the lock table */
1585  if ( g_strcmp0(table_name, lock_table) == 0)
1586  {
1587  continue;
1588  }
1589  do
1590  {
1591  gnc_dbi_init_error( conn );
1592  switch ( op )
1593  {
1594  case rollback:
1595  if (g_slist_find(full_table_name_list, table_name))
1596  {
1597  result = dbi_conn_queryf( conn->conn, "DROP TABLE %s",
1598  table_name );
1599  if ( result )
1600  break;
1601  }
1602  /* Fall through */
1603  case backup:
1604  case drop_backup:
1605  result = conn_table_manage_backup( conn, table_name, op );
1606  break;
1607  case empty:
1608  result = dbi_conn_queryf( conn->conn, "DELETE FROM TABLE %s",
1609  table_name );
1610  break;
1611  case drop:
1612  default:
1613  result = dbi_conn_queryf( conn->conn, "DROP TABLE %s",
1614  table_name );
1615  break;
1616  }
1617  }
1618  while ( conn->retry );
1619  if ( result != NULL )
1620  {
1621  if ( dbi_result_free( result ) < 0 )
1622  {
1623  PERR( "Error in dbi_result_free() result\n" );
1624  result = FALSE;
1625  }
1626  }
1627  }
1628  gnc_table_slist_free( full_table_name_list );
1629  return result;
1630 }
1631 
1641 static void
1642 gnc_dbi_safe_sync_all( QofBackend *qbe, QofBook *book )
1643 {
1644  GncDbiBackend *be = (GncDbiBackend*)qbe;
1645  GncDbiSqlConnection *conn = (GncDbiSqlConnection*)(((GncSqlBackend*)be)->conn);
1646  GSList *table_list, *index_list, *iter;
1647  const gchar* dbname = NULL;
1648 
1649  g_return_if_fail( be != NULL );
1650  g_return_if_fail( book != NULL );
1651 
1652  ENTER( "book=%p, primary=%p", book, be->primary_book );
1653  dbname = dbi_conn_get_option( be->conn, "dbname" );
1654  table_list = conn->provider->get_table_list( conn->conn, dbname );
1655  if ( !conn_table_operation( (GncSqlConnection*)conn, table_list,
1656  backup ) )
1657  {
1659  conn_table_operation( (GncSqlConnection*)conn, table_list,
1660  rollback );
1661  LEAVE( "Failed to rename tables" );
1662  gnc_table_slist_free( table_list );
1663  return;
1664  }
1665  index_list = conn->provider->get_index_list( conn->conn );
1666  for ( iter = index_list; iter != NULL; iter = g_slist_next( iter) )
1667  {
1668  const char *errmsg;
1669  conn->provider->drop_index (conn->conn, iter->data);
1670  if ( DBI_ERROR_NONE != dbi_conn_error( conn->conn, &errmsg ) )
1671  {
1673  gnc_table_slist_free( index_list );
1674  conn_table_operation( (GncSqlConnection*)conn, table_list,
1675  rollback );
1676  gnc_table_slist_free( table_list );
1677  LEAVE( "Failed to drop indexes %s", errmsg );
1678  return;
1679  }
1680  }
1681  gnc_table_slist_free( index_list );
1682 
1683  be->is_pristine_db = TRUE;
1684  be->primary_book = book;
1685 
1686  gnc_sql_sync_all( &be->sql_be, book );
1687  if ( ERR_BACKEND_NO_ERR != qof_backend_get_error( qbe ) )
1688  {
1689  conn_table_operation( (GncSqlConnection*)conn, table_list,
1690  rollback );
1691  LEAVE( "Failed to create new database tables" );
1692  return;
1693  }
1694  conn_table_operation( (GncSqlConnection*)conn, table_list,
1695  drop_backup );
1696  gnc_table_slist_free( table_list );
1697  LEAVE("book=%p", book);
1698 }
1699 /* ================================================================= */
1700 static void
1701 gnc_dbi_begin_edit( QofBackend *qbe, QofInstance *inst )
1702 {
1703  GncDbiBackend* be = (GncDbiBackend*)qbe;
1704 
1705  g_return_if_fail( be != NULL );
1706  g_return_if_fail( inst != NULL );
1707 
1708  gnc_sql_begin_edit( &be->sql_be, inst );
1709 }
1710 
1711 static void
1712 gnc_dbi_rollback_edit( QofBackend *qbe, QofInstance *inst )
1713 {
1714  GncDbiBackend* be = (GncDbiBackend*)qbe;
1715 
1716  g_return_if_fail( be != NULL );
1717  g_return_if_fail( inst != NULL );
1718 
1719  gnc_sql_rollback_edit( &be->sql_be, inst );
1720 }
1721 
1722 static void
1723 gnc_dbi_commit_edit( QofBackend *qbe, QofInstance *inst )
1724 {
1725  GncDbiBackend* be = (GncDbiBackend*)qbe;
1726 
1727  g_return_if_fail( be != NULL );
1728  g_return_if_fail( inst != NULL );
1729 
1730  gnc_sql_commit_edit( &be->sql_be, inst );
1731 }
1732 
1733 /* ================================================================= */
1734 
1735 static void
1736 init_sql_backend( GncDbiBackend* dbi_be )
1737 {
1738  QofBackend* be;
1739 
1740  be = (QofBackend*)dbi_be;
1741 
1742  be->session_end = gnc_dbi_session_end;
1743  be->destroy_backend = gnc_dbi_destroy_backend;
1744 
1745  be->load = gnc_dbi_load;
1746 
1747  /* The gda backend treats accounting periods transactionally. */
1748  be->begin = gnc_dbi_begin_edit;
1749  be->commit = gnc_dbi_commit_edit;
1750  be->rollback = gnc_dbi_rollback_edit;
1751 
1752  /* The gda backend will not be multi-user (for now)... */
1753  be->events_pending = NULL;
1754  be->process_events = NULL;
1755 
1756  /* The SQL/DBI backend doesn't need to be synced until it is
1757  * configured for multiuser access. */
1758  be->sync = gnc_dbi_safe_sync_all;
1759  be->safe_sync = gnc_dbi_safe_sync_all;
1760  be->load_config = NULL;
1761  be->get_config = NULL;
1762 
1763  be->compile_query = gnc_sql_compile_query;
1764  be->run_query = gnc_sql_run_query;
1765  be->free_query = gnc_sql_free_query;
1766 
1767  be->export_fn = NULL;
1768 
1769  gnc_sql_init( &dbi_be->sql_be );
1770 
1771  dbi_be->sql_be.conn = NULL;
1772  dbi_be->sql_be.book = NULL;
1773 }
1774 
1775 static QofBackend*
1776 new_backend( void (*session_begin)( QofBackend *, QofSession *, const gchar *,
1777  /*@ unused @*/ gboolean,
1778  /*@ unused @*/ gboolean,
1779  /*@ unused @*/ gboolean ) )
1780 {
1781  GncDbiBackend *dbi_be;
1782  QofBackend *be;
1783 
1784  dbi_be = g_new0( GncDbiBackend, 1 );
1785  g_assert( dbi_be != NULL );
1786 
1787  be = (QofBackend*)dbi_be;
1788  qof_backend_init( be );
1789 
1790  be->session_begin = session_begin;
1791  init_sql_backend( dbi_be );
1792 
1793  return be;
1794 }
1795 
1796 static QofBackend*
1797 gnc_dbi_backend_sqlite3_new( void )
1798 {
1799  return new_backend( gnc_dbi_sqlite3_session_begin );
1800 }
1801 
1802 static QofBackend*
1803 gnc_dbi_backend_mysql_new( void )
1804 {
1805  return new_backend( gnc_dbi_mysql_session_begin );
1806 }
1807 
1808 static QofBackend*
1809 gnc_dbi_backend_postgres_new( void )
1810 {
1811  return new_backend( gnc_dbi_postgres_session_begin );
1812 }
1813 
1814 static void
1815 gnc_dbi_provider_free( /*@ only @*/ QofBackendProvider *prov )
1816 {
1817  g_return_if_fail( prov != NULL );
1818 
1819  g_free( prov );
1820 }
1821 
1822 /*
1823  * Checks to see whether the file is an sqlite file or not
1824  *
1825  */
1826 static gboolean
1827 gnc_dbi_check_sqlite3_file( const gchar *uri )
1828 {
1829  FILE* f;
1830  gchar buf[50];
1831  G_GNUC_UNUSED size_t chars_read;
1832  gint status;
1833  gchar *filename;
1834 
1835  // BAD if the path is null
1836  g_return_val_if_fail( uri != NULL, FALSE );
1837 
1838  filename = gnc_uri_get_path ( uri );
1839  f = g_fopen( filename, "r" );
1840  g_free ( filename );
1841 
1842  // OK if the file doesn't exist - new file
1843  if ( f == NULL )
1844  {
1845  PINFO( "doesn't exist (errno=%d) -> DBI", errno );
1846  return TRUE;
1847  }
1848 
1849  // OK if file has the correct header
1850  chars_read = fread( buf, sizeof(buf), 1, f );
1851  status = fclose( f );
1852  if ( status < 0 )
1853  {
1854  PERR( "Error in fclose(): %d\n", errno );
1855  }
1856  if ( g_str_has_prefix( buf, "SQLite format 3" ) )
1857  {
1858  PINFO( "has SQLite format string -> DBI" );
1859  return TRUE;
1860  }
1861  PINFO( "exists, does not have SQLite format string -> not DBI" );
1862 
1863  // Otherwise, BAD
1864  return FALSE;
1865 }
1866 
1867 void
1869 {
1870  QofBackendProvider *prov;
1871  const gchar* driver_dir;
1872  int num_drivers;
1873  gboolean have_sqlite3_driver = FALSE;
1874  gboolean have_mysql_driver = FALSE;
1875  gboolean have_pgsql_driver = FALSE;
1876 
1877  /* Initialize libdbi and see which drivers are available. Only register qof backends which
1878  have drivers available. */
1879  driver_dir = g_getenv( "GNC_DBD_DIR" );
1880  if ( driver_dir == NULL )
1881  {
1882  PINFO( "GNC_DBD_DIR not set: using libdbi built-in default\n");
1883  }
1884 
1885  /* dbi_initialize returns -1 in case of errors */
1886  #if HAVE_LIBDBI_R
1887  if (dbi_instance)
1888  return;
1889  num_drivers = dbi_initialize_r( driver_dir, &dbi_instance );
1890  #else
1891  num_drivers = dbi_initialize( driver_dir );
1892  #endif
1893  if ( num_drivers <= 0 )
1894  {
1895  PWARN( "No DBD drivers found\n" );
1896  }
1897  else
1898  {
1899  dbi_driver driver = NULL;
1900  PINFO( "%d DBD drivers found\n", num_drivers );
1901 
1902  do
1903  {
1904  #if HAVE_LIBDBI_R
1905  driver = dbi_driver_list_r( driver, dbi_instance );
1906  #else
1907  driver = dbi_driver_list( driver );
1908  #endif
1909 
1910  if ( driver != NULL )
1911  {
1912  const gchar* name = dbi_driver_get_name( driver );
1913 
1914  PINFO( "Driver: %s\n", name );
1915  if ( strcmp( name, "sqlite3" ) == 0 )
1916  {
1917  have_sqlite3_driver = TRUE;
1918  }
1919  else if ( strcmp( name, "mysql" ) == 0 )
1920  {
1921  have_mysql_driver = TRUE;
1922  }
1923  else if ( strcmp( name, "pgsql" ) == 0 )
1924  {
1925  have_pgsql_driver = TRUE;
1926  }
1927  }
1928  }
1929  while ( driver != NULL );
1930  }
1931 
1932  if ( have_sqlite3_driver )
1933  {
1934  prov = g_new0( QofBackendProvider, 1 );
1935  g_assert( prov != NULL );
1936 
1937  prov->provider_name = "GnuCash Libdbi (SQLITE3) Backend";
1938  prov->access_method = FILE_URI_TYPE;
1939  prov->backend_new = gnc_dbi_backend_sqlite3_new;
1940  prov->provider_free = gnc_dbi_provider_free;
1941  prov->check_data_type = gnc_dbi_check_sqlite3_file;
1943 
1944  prov = g_new0( QofBackendProvider, 1 );
1945  g_assert( prov != NULL );
1946 
1947  prov->provider_name = "GnuCash Libdbi (SQLITE3) Backend";
1948  prov->access_method = SQLITE3_URI_TYPE;
1949  prov->backend_new = gnc_dbi_backend_sqlite3_new;
1950  prov->provider_free = gnc_dbi_provider_free;
1951  prov->check_data_type = gnc_dbi_check_sqlite3_file;
1953  }
1954 
1955  if ( have_mysql_driver )
1956  {
1957  prov = g_new0( QofBackendProvider, 1 );
1958  g_assert( prov != NULL );
1959 
1960  prov->provider_name = "GnuCash Libdbi (MYSQL) Backend";
1961  prov->access_method = "mysql";
1962  prov->backend_new = gnc_dbi_backend_mysql_new;
1963  prov->provider_free = gnc_dbi_provider_free;
1964  prov->check_data_type = NULL;
1966  }
1967 
1968  if ( have_pgsql_driver )
1969  {
1970  prov = g_new0( QofBackendProvider, 1 );
1971  g_assert( prov != NULL );
1972 
1973  prov->provider_name = "GnuCash Libdbi (POSTGRESQL) Backend";
1974  prov->access_method = "postgres";
1975  prov->backend_new = gnc_dbi_backend_postgres_new;
1976  prov->provider_free = gnc_dbi_provider_free;
1977  prov->check_data_type = NULL;
1979  }
1980 
1981  /* If needed, set log level to DEBUG so that SQl statements will be put into
1982  the gnucash.trace file. */
1983  /* qof_log_set_level( log_module, QOF_LOG_DEBUG ); */
1984 }
1985 
1986 #ifndef GNC_NO_LOADABLE_MODULES
1987 G_MODULE_EXPORT void
1989 {
1991 }
1992 
1993 G_MODULE_EXPORT void
1994 qof_backend_module_finalize( void )
1995 {
1997 }
1998 #endif /* GNC_NO_LOADABLE_MODULES */
1999 
2000 void
2002 {
2003 #if HAVE_LIBDBI_R
2004  if (dbi_instance)
2005  {
2006  dbi_shutdown_r(dbi_instance);
2007  dbi_instance = NULL;
2008  }
2009 #else
2010  dbi_shutdown();
2011 #endif
2012 }
2013 
2014 /* --------------------------------------------------------- */
2015 typedef struct
2016 {
2017  GncSqlRow base;
2018 
2019  /*@ dependent @*/
2020  dbi_result result;
2021  GList* gvalue_list;
2022 } GncDbiSqlRow;
2023 
2024 static void
2025 row_dispose( /*@ only @*/ GncSqlRow* row )
2026 {
2027  GncDbiSqlRow* dbi_row = (GncDbiSqlRow*)row;
2028  GList* node;
2029 
2030  if ( dbi_row->gvalue_list != NULL )
2031  {
2032  for ( node = dbi_row->gvalue_list; node != NULL; node = node->next )
2033  {
2034  GValue* value;
2035  if ( !G_IS_VALUE(node->data) )
2036  continue;
2037  value = (GValue*)node->data;
2038  if ( G_VALUE_HOLDS_STRING(value) )
2039  {
2040  g_free( (gpointer)g_value_get_string( value ) );
2041  }
2042  g_free( value );
2043  }
2044  g_list_free( dbi_row->gvalue_list );
2045  }
2046  g_free( dbi_row );
2047 }
2048 
2049 static /*@ null @*/ const GValue*
2050 row_get_value_at_col_name( GncSqlRow* row, const gchar* col_name )
2051 {
2052  GncDbiSqlRow* dbi_row = (GncDbiSqlRow*)row;
2053  gushort type;
2054  guint attrs;
2055  GValue* value;
2056 
2057  type = dbi_result_get_field_type( dbi_row->result, col_name );
2058  attrs = dbi_result_get_field_attribs( dbi_row->result, col_name );
2059  value = g_new0( GValue, 1 );
2060  g_assert( value != NULL );
2061 
2062  switch ( type )
2063  {
2064  case DBI_TYPE_INTEGER:
2065  (void)g_value_init( value, G_TYPE_INT64 );
2066  g_value_set_int64( value, dbi_result_get_longlong( dbi_row->result, col_name ) );
2067  break;
2068  case DBI_TYPE_DECIMAL:
2069  gnc_push_locale( LC_NUMERIC, "C" );
2070  if ( (attrs & DBI_DECIMAL_SIZEMASK) == DBI_DECIMAL_SIZE4 )
2071  {
2072  (void)g_value_init( value, G_TYPE_FLOAT );
2073  g_value_set_float( value, dbi_result_get_float( dbi_row->result, col_name ) );
2074  }
2075  else if ( (attrs & DBI_DECIMAL_SIZEMASK) == DBI_DECIMAL_SIZE8 )
2076  {
2077  (void)g_value_init( value, G_TYPE_DOUBLE );
2078  g_value_set_double( value, dbi_result_get_double( dbi_row->result, col_name ) );
2079  }
2080  else
2081  {
2082  PERR( "Field %s: strange decimal length attrs=%d\n", col_name, attrs );
2083  }
2084  gnc_pop_locale( LC_NUMERIC );
2085  break;
2086  case DBI_TYPE_STRING:
2087  (void)g_value_init( value, G_TYPE_STRING );
2088  g_value_take_string( value, dbi_result_get_string_copy( dbi_row->result, col_name ) );
2089  break;
2090  case DBI_TYPE_DATETIME:
2091  if ( dbi_result_field_is_null( dbi_row->result, col_name ) )
2092  {
2093  return NULL;
2094  }
2095  else
2096  {
2097  /* A seriously evil hack to work around libdbi bug #15
2098  * https://sourceforge.net/p/libdbi/bugs/15/. When libdbi
2099  * v0.9 is widely available this can be replaced with
2100  * dbi_result_get_as_longlong.
2101  */
2102  dbi_result_t *result = (dbi_result_t*)(dbi_row->result);
2103  guint64 row = dbi_result_get_currow (result);
2104  guint idx = dbi_result_get_field_idx (result, col_name) - 1;
2105  time64 time = result->rows[row]->field_values[idx].d_datetime;
2106  (void)g_value_init( value, G_TYPE_INT64 );
2107  g_value_set_int64 (value, time);
2108  }
2109  break;
2110  default:
2111  PERR( "Field %s: unknown DBI_TYPE: %d\n", col_name, type );
2112  g_free( value );
2113  return NULL;
2114  }
2115 
2116  dbi_row->gvalue_list = g_list_prepend( dbi_row->gvalue_list, value );
2117  return value;
2118 }
2119 
2120 static GncSqlRow*
2121 create_dbi_row( /*@ dependent @*/ dbi_result result )
2122 {
2123  GncDbiSqlRow* row;
2124 
2125  row = g_new0( GncDbiSqlRow, 1 );
2126  g_assert( row != NULL );
2127 
2128  row->base.getValueAtColName = row_get_value_at_col_name;
2129  row->base.dispose = row_dispose;
2130  row->result = result;
2131 
2132  return (GncSqlRow*)row;
2133 }
2134 /* --------------------------------------------------------- */
2135 typedef struct
2136 {
2137  GncSqlResult base;
2138 
2139  /*@ observer @*/
2140  GncDbiSqlConnection* dbi_conn;
2141  /*@ owned @*/
2142  dbi_result result;
2143  guint num_rows;
2144  guint cur_row;
2145  GncSqlRow* row;
2146 } GncDbiSqlResult;
2147 
2148 static void
2149 result_dispose( /*@ only @*/ GncSqlResult* result )
2150 {
2151  GncDbiSqlResult* dbi_result = (GncDbiSqlResult*)result;
2152 
2153  if ( dbi_result->row != NULL )
2154  {
2155  gnc_sql_row_dispose( dbi_result->row );
2156  }
2157  if ( dbi_result->result != NULL )
2158  {
2159  gint status;
2160 
2161  status = dbi_result_free( dbi_result->result );
2162  if ( status < 0 )
2163  {
2164  PERR( "Error in dbi_result_free() result\n" );
2165  qof_backend_set_error( dbi_result->dbi_conn->qbe, ERR_BACKEND_SERVER_ERR );
2166  }
2167  }
2168  g_free( result );
2169 }
2170 
2171 static guint
2172 result_get_num_rows( GncSqlResult* result )
2173 {
2174  GncDbiSqlResult* dbi_result = (GncDbiSqlResult*)result;
2175 
2176  return dbi_result->num_rows;
2177 }
2178 
2179 static /*@ null @*/ GncSqlRow*
2180 result_get_first_row( GncSqlResult* result )
2181 {
2182  GncDbiSqlResult* dbi_result = (GncDbiSqlResult*)result;
2183 
2184  if ( dbi_result->row != NULL )
2185  {
2186  gnc_sql_row_dispose( dbi_result->row );
2187  dbi_result->row = NULL;
2188  }
2189  if ( dbi_result->num_rows > 0 )
2190  {
2191  gint status = dbi_result_first_row( dbi_result->result );
2192  if ( status == 0 )
2193  {
2194  PERR( "Error in dbi_result_first_row()\n" );
2195  qof_backend_set_error( dbi_result->dbi_conn->qbe, ERR_BACKEND_SERVER_ERR );
2196  }
2197  dbi_result->cur_row = 1;
2198  dbi_result->row = create_dbi_row( dbi_result->result );
2199  return dbi_result->row;
2200  }
2201  else
2202  {
2203  return NULL;
2204  }
2205 }
2206 
2207 static /*@ null @*/ GncSqlRow*
2208 result_get_next_row( GncSqlResult* result )
2209 {
2210  GncDbiSqlResult* dbi_result = (GncDbiSqlResult*)result;
2211 
2212  if ( dbi_result->row != NULL )
2213  {
2214  gnc_sql_row_dispose( dbi_result->row );
2215  dbi_result->row = NULL;
2216  }
2217  if ( dbi_result->cur_row < dbi_result->num_rows )
2218  {
2219  gint status = dbi_result_next_row( dbi_result->result );
2220  if ( status == 0 )
2221  {
2222  PERR( "Error in dbi_result_first_row()\n" );
2223  qof_backend_set_error( dbi_result->dbi_conn->qbe, ERR_BACKEND_SERVER_ERR );
2224  }
2225  dbi_result->cur_row++;
2226  dbi_result->row = create_dbi_row( dbi_result->result );
2227  return dbi_result->row;
2228  }
2229  else
2230  {
2231  return NULL;
2232  }
2233 }
2234 
2235 static GncSqlResult*
2236 create_dbi_result( /*@ observer @*/ GncDbiSqlConnection* dbi_conn, /*@ owned @*/ dbi_result result )
2237 {
2238  GncDbiSqlResult* dbi_result;
2239 
2240  dbi_result = g_new0( GncDbiSqlResult, 1 );
2241  g_assert( dbi_result != NULL );
2242 
2243  dbi_result->base.dispose = result_dispose;
2244  dbi_result->base.getNumRows = result_get_num_rows;
2245  dbi_result->base.getFirstRow = result_get_first_row;
2246  dbi_result->base.getNextRow = result_get_next_row;
2247  dbi_result->result = result;
2248  dbi_result->num_rows = (guint)dbi_result_get_numrows( result );
2249  dbi_result->cur_row = 0;
2250  dbi_result->dbi_conn = dbi_conn;
2251 
2252  return (GncSqlResult*)dbi_result;
2253 }
2254 /* --------------------------------------------------------- */
2255 typedef struct
2256 {
2257  GncSqlStatement base;
2258 
2259  GString* sql;
2260  /*@ observer @*/
2261  GncSqlConnection* conn;
2263 
2264 static void
2265 stmt_dispose( /*@ only @*/ GncSqlStatement* stmt )
2266 {
2267  GncDbiSqlStatement* dbi_stmt = (GncDbiSqlStatement*)stmt;
2268 
2269  if ( dbi_stmt->sql != NULL )
2270  {
2271  (void)g_string_free( dbi_stmt->sql, TRUE );
2272  }
2273  g_free( stmt );
2274 }
2275 
2276 static gchar*
2277 stmt_to_sql( GncSqlStatement* stmt )
2278 {
2279  GncDbiSqlStatement* dbi_stmt = (GncDbiSqlStatement*)stmt;
2280 
2281  return dbi_stmt->sql->str;
2282 }
2283 
2284 static void
2285 stmt_add_where_cond( GncSqlStatement* stmt, /*@ unused @*/ QofIdTypeConst type_name,
2286  /*@ unused @*/ gpointer obj, const GncSqlColumnTableEntry* table_row, GValue* value )
2287 {
2288  GncDbiSqlStatement* dbi_stmt = (GncDbiSqlStatement*)stmt;
2289  gchar* buf;
2290  gchar* value_str;
2291 
2292  value_str = gnc_sql_get_sql_value( dbi_stmt->conn, value );
2293  buf = g_strdup_printf( " WHERE %s = %s", table_row->col_name,
2294  value_str );
2295  g_free( value_str );
2296  (void)g_string_append( dbi_stmt->sql, buf );
2297  g_free( buf );
2298 }
2299 
2300 static GncSqlStatement*
2301 create_dbi_statement( /*@ observer @*/ GncSqlConnection* conn, const gchar* sql )
2302 {
2303  GncDbiSqlStatement* stmt;
2304 
2305  stmt = g_new0( GncDbiSqlStatement, 1 );
2306  g_assert( stmt != NULL );
2307 
2308  stmt->base.dispose = stmt_dispose;
2309  stmt->base.toSql = stmt_to_sql;
2310  stmt->base.addWhereCond = stmt_add_where_cond;
2311  stmt->sql = g_string_new( sql );
2312  stmt->conn = conn;
2313 
2314  return (GncSqlStatement*)stmt;
2315 }
2316 /* --------------------------------------------------------- */
2317 static void
2318 conn_dispose( /*@ only @*/ GncSqlConnection* conn )
2319 {
2320  //GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn;
2321 
2322  g_free( conn );
2323 }
2324 
2325 static /*@ null @*/ GncSqlResult*
2326 conn_execute_select_statement( GncSqlConnection* conn, GncSqlStatement* stmt )
2327 {
2328  GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn;
2329  GncDbiSqlStatement* dbi_stmt = (GncDbiSqlStatement*)stmt;
2330  dbi_result result;
2331 
2332  DEBUG( "SQL: %s\n", dbi_stmt->sql->str );
2333  gnc_push_locale( LC_NUMERIC, "C" );
2334  do
2335  {
2336  gnc_dbi_init_error( dbi_conn );
2337  result = dbi_conn_query( dbi_conn->conn, dbi_stmt->sql->str );
2338  }
2339  while ( dbi_conn->retry );
2340  if ( result == NULL )
2341  {
2342  PERR( "Error executing SQL %s\n", dbi_stmt->sql->str );
2343  return NULL;
2344  }
2345  gnc_pop_locale( LC_NUMERIC );
2346  return create_dbi_result( dbi_conn, result );
2347 }
2348 
2349 static gint
2350 conn_execute_nonselect_statement( GncSqlConnection* conn, GncSqlStatement* stmt )
2351 {
2352  GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn;
2353  GncDbiSqlStatement* dbi_stmt = (GncDbiSqlStatement*)stmt;
2354  dbi_result result;
2355  gint num_rows;
2356  gint status;
2357 
2358  DEBUG( "SQL: %s\n", dbi_stmt->sql->str );
2359  do
2360  {
2361  gnc_dbi_init_error( dbi_conn );
2362  result = dbi_conn_query( dbi_conn->conn, dbi_stmt->sql->str );
2363  }
2364  while ( dbi_conn->retry );
2365  if ( result == NULL )
2366  {
2367  PERR( "Error executing SQL %s\n", dbi_stmt->sql->str );
2368  return -1;
2369  }
2370  num_rows = (gint)dbi_result_get_numrows_affected( result );
2371  status = dbi_result_free( result );
2372  if ( status < 0 )
2373  {
2374  PERR( "Error in dbi_result_free() result\n" );
2376  }
2377  return num_rows;
2378 }
2379 
2380 static GncSqlStatement*
2381 conn_create_statement_from_sql( /*@ observer @*/ GncSqlConnection* conn, const gchar* sql )
2382 {
2383  //GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn;
2384 
2385  return create_dbi_statement( conn, sql );
2386 }
2387 
2388 static gboolean
2389 conn_does_table_exist( GncSqlConnection* conn, const gchar* table_name )
2390 {
2391  GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn;
2392  gint nTables;
2393  dbi_result tables;
2394  const gchar* dbname;
2395  gint status;
2396 
2397  g_return_val_if_fail( conn != NULL, FALSE );
2398  g_return_val_if_fail( table_name != NULL, FALSE );
2399 
2400  dbname = dbi_conn_get_option( dbi_conn->conn, "dbname" );
2401  tables = dbi_conn_get_table_list( dbi_conn->conn, dbname, table_name );
2402  nTables = (gint)dbi_result_get_numrows( tables );
2403  status = dbi_result_free( tables );
2404  if ( status < 0 )
2405  {
2406  PERR( "Error in dbi_result_free() result\n" );
2408  }
2409 
2410  if ( nTables == 1 )
2411  {
2412  return TRUE;
2413  }
2414  else
2415  {
2416  return FALSE;
2417  }
2418 }
2419 
2420 static gboolean
2421 conn_begin_transaction( /*@ unused @*/ GncSqlConnection* conn )
2422 {
2423  GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn;
2424  dbi_result result;
2425  gint status;
2426  gboolean success = FALSE;
2427 
2428  DEBUG( "BEGIN\n" );
2429 
2430  if ( !gnc_dbi_verify_conn (dbi_conn) )
2431  {
2432  PERR( "gnc_dbi_verify_conn() failed\n" );
2434  return FALSE;
2435  }
2436 
2437  do
2438  {
2439  gnc_dbi_init_error( dbi_conn );
2440  result = dbi_conn_queryf( dbi_conn->conn, "BEGIN" );
2441  }
2442  while ( dbi_conn->retry );
2443 
2444  success = ( result != NULL );
2445  status = dbi_result_free( result );
2446  if ( status < 0 )
2447  {
2448  PERR( "Error in dbi_result_free() result\n" );
2450  }
2451  if ( !success )
2452  {
2453  PERR( "BEGIN transaction failed()\n" );
2455  }
2456 
2457  return success;
2458 }
2459 
2460 static gboolean
2461 conn_rollback_transaction( /*@ unused @*/ GncSqlConnection* conn )
2462 {
2463  GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn;
2464  dbi_result result;
2465  gint status;
2466  gboolean success = FALSE;
2467 
2468  DEBUG( "ROLLBACK\n" );
2469  result = dbi_conn_queryf( dbi_conn->conn, "ROLLBACK" );
2470  success = ( result != NULL );
2471 
2472  status = dbi_result_free( result );
2473  if ( status < 0 )
2474  {
2475  PERR( "Error in dbi_result_free() result\n" );
2477  }
2478  if ( !success )
2479  {
2480  PERR( "Error in conn_rollback_transaction()\n" );
2482  }
2483 
2484  return success;
2485 }
2486 
2487 static gboolean
2488 conn_commit_transaction( /*@ unused @*/ GncSqlConnection* conn )
2489 {
2490  GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn;
2491  dbi_result result;
2492  gint status;
2493  gboolean success = FALSE;
2494 
2495  DEBUG( "COMMIT\n" );
2496  result = dbi_conn_queryf( dbi_conn->conn, "COMMIT" );
2497  success = ( result != NULL );
2498 
2499  status = dbi_result_free( result );
2500  if ( status < 0 )
2501  {
2502  PERR( "Error in dbi_result_free() result\n" );
2504  }
2505  if ( !success )
2506  {
2507  PERR( "Error in conn_commit_transaction()\n" );
2509  }
2510 
2511  return success;
2512 }
2513 
2514 static /*@ null @*/ gchar*
2515 create_index_ddl( GncSqlConnection* conn,
2516  const gchar* index_name,
2517  const gchar* table_name,
2518  const GncSqlColumnTableEntry* col_table )
2519 {
2520  GString* ddl;
2521  const GncSqlColumnTableEntry* table_row;
2522 
2523  g_return_val_if_fail( conn != NULL, NULL );
2524  g_return_val_if_fail( index_name != NULL, NULL );
2525  g_return_val_if_fail( table_name != NULL, NULL );
2526  g_return_val_if_fail( col_table != NULL, NULL );
2527 
2528  ddl = g_string_new( "" );
2529  g_string_printf( ddl, "CREATE INDEX %s ON %s (", index_name, table_name );
2530  for ( table_row = col_table; table_row->col_name != NULL; ++table_row )
2531  {
2532  if ( table_row != col_table )
2533  {
2534  (void)g_string_append( ddl, ", " );
2535  }
2536  g_string_append_printf( ddl, "%s", table_row->col_name );
2537  }
2538  (void)g_string_append( ddl, ")" );
2539 
2540  return g_string_free( ddl, FALSE );
2541 }
2542 
2543 static /*@ null @*/ gchar*
2544 add_columns_ddl( GncSqlConnection* conn,
2545  const gchar* table_name,
2546  GList* col_info_list )
2547 {
2548  GString* ddl;
2549  const GList* list_node;
2550  guint col_num;
2551  GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn;
2552 
2553  g_return_val_if_fail( conn != NULL, NULL );
2554  g_return_val_if_fail( table_name != NULL, NULL );
2555  g_return_val_if_fail( col_info_list != NULL, NULL );
2556 
2557  ddl = g_string_new( "" );
2558  g_string_printf( ddl, "ALTER TABLE %s ", table_name );
2559  for ( list_node = col_info_list, col_num = 0; list_node != NULL;
2560  list_node = list_node->next, col_num++ )
2561  {
2562  GncSqlColumnInfo* info = (GncSqlColumnInfo*)(list_node->data);
2563 
2564  if ( col_num != 0 )
2565  {
2566  (void)g_string_append( ddl, ", " );
2567  }
2568  g_string_append( ddl, "ADD COLUMN " );
2569  dbi_conn->provider->append_col_def( ddl, info );
2570  g_free( info->name );
2571  g_free( info );
2572  }
2573 
2574  return g_string_free( ddl, FALSE );
2575 }
2576 
2577 static void
2578 append_sqlite3_col_def( GString* ddl, GncSqlColumnInfo* info )
2579 {
2580  gchar* type_name;
2581 
2582  if ( info->type == BCT_INT )
2583  {
2584  type_name = "integer";
2585  }
2586  else if ( info->type == BCT_INT64 )
2587  {
2588  type_name = "bigint";
2589  }
2590  else if ( info->type == BCT_DOUBLE )
2591  {
2592  type_name = "float8";
2593  }
2594  else if ( info->type == BCT_STRING || info->type == BCT_DATE
2595  || info->type == BCT_DATETIME )
2596  {
2597  type_name = "text";
2598  }
2599  else
2600  {
2601  PERR( "Unknown column type: %d\n", info->type );
2602  type_name = "";
2603  }
2604  g_string_append_printf( ddl, "%s %s", info->name, type_name );
2605  if ( info->size != 0 )
2606  {
2607  (void)g_string_append_printf( ddl, "(%d)", info->size );
2608  }
2609  if ( info->is_primary_key )
2610  {
2611  (void)g_string_append( ddl, " PRIMARY KEY" );
2612  }
2613  if ( info->is_autoinc )
2614  {
2615  (void)g_string_append( ddl, " AUTOINCREMENT" );
2616  }
2617  if ( !info->null_allowed )
2618  {
2619  (void)g_string_append( ddl, " NOT NULL" );
2620  }
2621 }
2622 
2623 static /*@ null @*/ gchar*
2624 conn_create_table_ddl_sqlite3( GncSqlConnection* conn,
2625  const gchar* table_name,
2626  const GList* col_info_list )
2627 {
2628  GString* ddl;
2629  const GList* list_node;
2630  guint col_num;
2631 
2632  g_return_val_if_fail( conn != NULL, NULL );
2633  g_return_val_if_fail( table_name != NULL, NULL );
2634  g_return_val_if_fail( col_info_list != NULL, NULL );
2635 
2636  ddl = g_string_new( "" );
2637  g_string_printf( ddl, "CREATE TABLE %s (", table_name );
2638  for ( list_node = col_info_list, col_num = 0; list_node != NULL;
2639  list_node = list_node->next, col_num++ )
2640  {
2641  GncSqlColumnInfo* info = (GncSqlColumnInfo*)(list_node->data);
2642 
2643  if ( col_num != 0 )
2644  {
2645  (void)g_string_append( ddl, ", " );
2646  }
2647  append_sqlite3_col_def( ddl, info );
2648  g_free( info->name );
2649  g_free( info );
2650  }
2651  (void)g_string_append( ddl, ")" );
2652 
2653  return g_string_free( ddl, FALSE );
2654 }
2655 
2656 static void
2657 append_mysql_col_def( GString* ddl, GncSqlColumnInfo* info )
2658 {
2659  gchar* type_name;
2660 
2661  if ( info->type == BCT_INT )
2662  {
2663  type_name = "integer";
2664  }
2665  else if ( info->type == BCT_INT64 )
2666  {
2667  type_name = "bigint";
2668  }
2669  else if ( info->type == BCT_DOUBLE )
2670  {
2671  type_name = "double";
2672  }
2673  else if ( info->type == BCT_STRING )
2674  {
2675  type_name = "varchar";
2676  }
2677  else if ( info->type == BCT_DATE )
2678  {
2679  info->size = 0;
2680  type_name = "date";
2681  }
2682  else if ( info->type == BCT_DATETIME )
2683  {
2684  info->size = 0;
2685  type_name = "TIMESTAMP NULL DEFAULT 0";
2686  }
2687  else
2688  {
2689  PERR( "Unknown column type: %d\n", info->type );
2690  type_name = "";
2691  }
2692  g_string_append_printf( ddl, "%s %s", info->name, type_name );
2693  if ( info->size != 0 )
2694  {
2695  g_string_append_printf( ddl, "(%d)", info->size );
2696  }
2697  if ( info->is_unicode )
2698  {
2699  (void)g_string_append( ddl, " CHARACTER SET utf8" );
2700  }
2701  if ( info->is_primary_key )
2702  {
2703  (void)g_string_append( ddl, " PRIMARY KEY" );
2704  }
2705  if ( info->is_autoinc )
2706  {
2707  (void)g_string_append( ddl, " AUTO_INCREMENT" );
2708  }
2709  if ( !info->null_allowed )
2710  {
2711  (void)g_string_append( ddl, " NOT NULL" );
2712  }
2713 }
2714 
2715 static /*@ null @*/ gchar*
2716 conn_create_table_ddl_mysql( GncSqlConnection* conn, const gchar* table_name,
2717  const GList* col_info_list )
2718 {
2719  GString* ddl;
2720  const GList* list_node;
2721  guint col_num;
2722 
2723  g_return_val_if_fail( conn != NULL, NULL );
2724  g_return_val_if_fail( table_name != NULL, NULL );
2725  g_return_val_if_fail( col_info_list != NULL, NULL );
2726 
2727  ddl = g_string_new( "" );
2728  g_string_printf( ddl, "CREATE TABLE %s (", table_name );
2729  for ( list_node = col_info_list, col_num = 0; list_node != NULL;
2730  list_node = list_node->next, col_num++ )
2731  {
2732  GncSqlColumnInfo* info = (GncSqlColumnInfo*)(list_node->data);
2733 
2734  if ( col_num != 0 )
2735  {
2736  (void)g_string_append( ddl, ", " );
2737  }
2738  append_mysql_col_def( ddl, info );
2739  g_free( info->name );
2740  g_free( info );
2741  }
2742  (void)g_string_append( ddl, ")" );
2743 
2744  return g_string_free( ddl, FALSE );
2745 }
2746 
2747 static void
2748 append_pgsql_col_def( GString* ddl, GncSqlColumnInfo* info )
2749 {
2750  gchar* type_name;
2751 
2752  if ( info->type == BCT_INT )
2753  {
2754  if ( info->is_autoinc )
2755  {
2756  type_name = "serial";
2757  }
2758  else
2759  {
2760  type_name = "integer";
2761  }
2762  }
2763  else if ( info->type == BCT_INT64 )
2764  {
2765  type_name = "int8";
2766  }
2767  else if ( info->type == BCT_DOUBLE )
2768  {
2769  type_name = "double precision";
2770  }
2771  else if ( info->type == BCT_STRING )
2772  {
2773  type_name = "varchar";
2774  }
2775  else if ( info->type == BCT_DATE )
2776  {
2777  info->size = 0;
2778  type_name = "date";
2779  }
2780  else if ( info->type == BCT_DATETIME )
2781  {
2782  info->size = 0;
2783  type_name = "timestamp without time zone";
2784  }
2785  else
2786  {
2787  PERR( "Unknown column type: %d\n", info->type );
2788  type_name = "";
2789  }
2790  g_string_append_printf( ddl, "%s %s", info->name, type_name );
2791  if ( info->size != 0 )
2792  {
2793  g_string_append_printf( ddl, "(%d)", info->size );
2794  }
2795  if ( info->is_primary_key )
2796  {
2797  (void)g_string_append( ddl, " PRIMARY KEY" );
2798  }
2799  if ( !info->null_allowed )
2800  {
2801  (void)g_string_append( ddl, " NOT NULL" );
2802  }
2803 }
2804 
2805 static /*@ null @*/ gchar*
2806 conn_create_table_ddl_pgsql( GncSqlConnection* conn, const gchar* table_name,
2807  const GList* col_info_list )
2808 {
2809  GString* ddl;
2810  const GList* list_node;
2811  guint col_num;
2812  gboolean is_unicode = FALSE;
2813 
2814  g_return_val_if_fail( conn != NULL, NULL );
2815  g_return_val_if_fail( table_name != NULL, NULL );
2816  g_return_val_if_fail( col_info_list != NULL, NULL );
2817 
2818  ddl = g_string_new( "" );
2819  g_string_printf( ddl, "CREATE TABLE %s (", table_name );
2820  for ( list_node = col_info_list, col_num = 0; list_node != NULL;
2821  list_node = list_node->next, col_num++ )
2822  {
2823  GncSqlColumnInfo* info = (GncSqlColumnInfo*)(list_node->data);
2824 
2825  if ( col_num != 0 )
2826  {
2827  (void)g_string_append( ddl, ", " );
2828  }
2829  append_pgsql_col_def( ddl, info );
2830  is_unicode = is_unicode || info->is_unicode;
2831  g_free( info->name );
2832  g_free( info );
2833  }
2834  (void)g_string_append( ddl, ")" );
2835  if ( is_unicode )
2836  {
2837  }
2838 
2839  return g_string_free( ddl, FALSE );
2840 }
2841 
2842 static gboolean
2843 conn_create_table( GncSqlConnection* conn, const gchar* table_name,
2844  GList* col_info_list )
2845 {
2846  GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn;
2847  gchar* ddl;
2848  dbi_result result;
2849 
2850  g_return_val_if_fail( conn != NULL, FALSE );
2851  g_return_val_if_fail( table_name != NULL, FALSE );
2852  g_return_val_if_fail( col_info_list != NULL, FALSE );
2853 
2854 
2855  ddl = dbi_conn->provider->create_table_ddl( conn, table_name,
2856  col_info_list );
2857  g_list_free( col_info_list );
2858  if ( ddl != NULL )
2859  {
2860  gint status;
2861 
2862  DEBUG( "SQL: %s\n", ddl );
2863  result = dbi_conn_query( dbi_conn->conn, ddl );
2864  g_free( ddl );
2865  status = dbi_result_free( result );
2866  if ( status < 0 )
2867  {
2868  PERR( "Error in dbi_result_free() result\n" );
2870  }
2871  }
2872  else
2873  {
2874  return FALSE;
2875  }
2876 
2877  return TRUE;
2878 }
2879 
2880 static gboolean
2881 conn_create_index( /*@ unused @*/ GncSqlConnection* conn, /*@ unused @*/ const gchar* index_name,
2882  /*@ unused @*/ const gchar* table_name, /*@ unused @*/ const GncSqlColumnTableEntry* col_table )
2883 {
2884  GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn;
2885  gchar* ddl;
2886  dbi_result result;
2887 
2888  g_return_val_if_fail( conn != NULL, FALSE );
2889  g_return_val_if_fail( index_name != NULL, FALSE );
2890  g_return_val_if_fail( table_name != NULL, FALSE );
2891  g_return_val_if_fail( col_table != NULL, FALSE );
2892 
2893  ddl = create_index_ddl( conn, index_name, table_name, col_table );
2894  if ( ddl != NULL )
2895  {
2896  gint status;
2897 
2898  DEBUG( "SQL: %s\n", ddl );
2899  result = dbi_conn_query( dbi_conn->conn, ddl );
2900  g_free( ddl );
2901  status = dbi_result_free( result );
2902  if ( status < 0 )
2903  {
2904  PERR( "Error in dbi_result_free() result\n" );
2906  }
2907  }
2908  else
2909  {
2910  return FALSE;
2911  }
2912 
2913  return TRUE;
2914 }
2915 
2916 static gboolean
2917 conn_add_columns_to_table( /*@ unused @*/ GncSqlConnection* conn, /*@ unused @*/ const gchar* table_name,
2918  GList* col_info_list )
2919 {
2920  GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn;
2921  gchar* ddl;
2922  dbi_result result;
2923 
2924  g_return_val_if_fail( conn != NULL, FALSE );
2925  g_return_val_if_fail( table_name != NULL, FALSE );
2926  g_return_val_if_fail( col_info_list != NULL, FALSE );
2927 
2928  ddl = add_columns_ddl( conn, table_name, col_info_list );
2929  if ( ddl != NULL )
2930  {
2931  gint status;
2932 
2933  DEBUG( "SQL: %s\n", ddl );
2934  result = dbi_conn_query( dbi_conn->conn, ddl );
2935  g_free( ddl );
2936  status = dbi_result_free( result );
2937  if ( status < 0 )
2938  {
2939  PERR( "Error in dbi_result_free() result\n" );
2941  }
2942  }
2943  else
2944  {
2945  return FALSE;
2946  }
2947 
2948  return TRUE;
2949 }
2950 
2951 static /*@ null @*/ gchar*
2952 conn_quote_string( const GncSqlConnection* conn, gchar* unquoted_str )
2953 {
2954  GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn;
2955  gchar* quoted_str;
2956  size_t size;
2957 
2958  size = dbi_conn_quote_string_copy( dbi_conn->conn, unquoted_str,
2959  &quoted_str );
2960  if ( size != 0 )
2961  {
2962  return quoted_str;
2963  }
2964  else
2965  {
2966  return NULL;
2967  }
2968 }
2969 
2970 static GSList*
2971 conn_get_table_list( dbi_conn conn, const gchar* dbname )
2972 {
2973  dbi_result tables;
2974  GSList* list = NULL;
2975 
2976  tables = dbi_conn_get_table_list( conn, dbname, NULL );
2977  while ( dbi_result_next_row( tables ) != 0 )
2978  {
2979  const gchar* table_name;
2980 
2981  table_name = dbi_result_get_string_idx( tables, 1 );
2982  list = g_slist_prepend( list, strdup( table_name ) );
2983  }
2984  dbi_result_free( tables );
2985  return list;
2986 }
2987 
2988 static GSList*
2989 conn_get_table_list_sqlite3( dbi_conn conn, const gchar* dbname )
2990 {
2991  gboolean change_made;
2992 
2993  /* Return the list, but remove the tables that sqlite3 adds for
2994  * its own use. */
2995  GSList* list = conn_get_table_list( conn, dbname );
2996  change_made = TRUE;
2997  while ( list != NULL && change_made )
2998  {
2999  GSList* node;
3000 
3001  change_made = FALSE;
3002  for ( node = list; node != NULL; node = node->next )
3003  {
3004  const gchar* table_name = (const gchar*)node->data;
3005 
3006  if ( strcmp( table_name, "sqlite_sequence" ) == 0 )
3007  {
3008  g_free( node->data );
3009  list = g_slist_delete_link( list, node );
3010  change_made = TRUE;
3011  break;
3012  }
3013  }
3014  }
3015  return list;
3016 }
3017 
3018 static GSList*
3019 conn_get_table_list_pgsql( dbi_conn conn, const gchar* dbname )
3020 {
3021  gboolean change_made;
3022 
3023  /* Return the list, but remove the tables that postgresql adds from the information schema. */
3024  GSList* list = conn_get_table_list( conn, dbname );
3025  change_made = TRUE;
3026  while ( list != NULL && change_made )
3027  {
3028  GSList* node;
3029 
3030  change_made = FALSE;
3031  for ( node = list; node != NULL; node = node->next )
3032  {
3033  const gchar* table_name = (const gchar*)node->data;
3034 
3035  if ( strcmp( table_name, "sql_features" ) == 0 ||
3036  strcmp( table_name, "sql_implementation_info" ) == 0 ||
3037  strcmp( table_name, "sql_languages" ) == 0 ||
3038  strcmp( table_name, "sql_packages" ) == 0 ||
3039  strcmp( table_name, "sql_parts" ) == 0 ||
3040  strcmp( table_name, "sql_sizing" ) == 0 ||
3041  strcmp( table_name, "sql_sizing_profiles" ) == 0 )
3042  {
3043  g_free( node->data );
3044  list = g_slist_delete_link( list, node );
3045  change_made = TRUE;
3046  break;
3047  }
3048  }
3049  }
3050  return list;
3051 }
3052 
3062 static GncDbiTestResult
3063 conn_test_dbi_library( dbi_conn conn )
3064 {
3065  gint64 testlonglong = -9223372036854775807LL, resultlonglong = 0;
3066  guint64 testulonglong = 9223372036854775807LLU, resultulonglong = 0;
3067  gdouble testdouble = 1.7976921348623157E+307, resultdouble = 0.0;
3068  dbi_result result;
3069  gchar doublestr[G_ASCII_DTOSTR_BUF_SIZE], *querystr;
3070  GncDbiTestResult retval = GNC_DBI_PASS;
3071  memset( doublestr, 0, sizeof(doublestr));
3072 
3073  result = dbi_conn_query( conn, "CREATE TEMPORARY TABLE numtest "
3074  "( test_int BIGINT, test_unsigned BIGINT,"
3075  " test_double FLOAT8 )" );
3076  if ( result == NULL )
3077  {
3078  PWARN("Test_DBI_Library: Create table failed");
3079  return GNC_DBI_FAIL_SETUP;
3080  }
3081  dbi_result_free( result );
3082  g_ascii_dtostr( doublestr, sizeof(doublestr), testdouble );
3083  querystr = g_strdup_printf( "INSERT INTO numtest VALUES (%" G_GINT64_FORMAT
3084  ", %" G_GUINT64_FORMAT ", %s)",
3085  testlonglong, testulonglong, doublestr );
3086  result = dbi_conn_query( conn, querystr );
3087  g_free( querystr );
3088  if ( result == NULL )
3089  {
3090  PWARN("Test_DBI_Library: Failed to insert test row into table" );
3091  return GNC_DBI_FAIL_SETUP;
3092  }
3093  dbi_result_free( result );
3094  gnc_push_locale( LC_NUMERIC, "C");
3095  result = dbi_conn_query( conn, "SELECT * FROM numtest" );
3096  if ( result == NULL )
3097  {
3098  const char *errmsg;
3099  dbi_conn_error( conn, &errmsg );
3100  PWARN("Test_DBI_Library: Failed to retrieve test row into table: %s",
3101  errmsg );
3102  result = dbi_conn_query( conn, "DROP TABLE numtest" );
3103  gnc_pop_locale( LC_NUMERIC );
3104  return GNC_DBI_FAIL_SETUP;
3105  }
3106  while ( dbi_result_next_row( result ))
3107  {
3108  resultlonglong = dbi_result_get_longlong( result, "test_int" );
3109  resultulonglong = dbi_result_get_ulonglong( result, "test_unsigned" );
3110  resultdouble = dbi_result_get_double( result, "test_double" );
3111  }
3112  gnc_pop_locale( LC_NUMERIC );
3113  if ( testlonglong != resultlonglong )
3114  {
3115  PWARN( "Test_DBI_Library: LongLong Failed %" G_GINT64_FORMAT " != % " G_GINT64_FORMAT,
3116  testlonglong, resultlonglong );
3117  retval = GNC_DBI_FAIL_TEST;
3118  }
3119  if ( testulonglong != resultulonglong )
3120  {
3121  PWARN( "Test_DBI_Library: Unsigned longlong Failed %" G_GUINT64_FORMAT " != %" G_GUINT64_FORMAT,
3122  testulonglong, resultulonglong );
3123  retval = GNC_DBI_FAIL_TEST;
3124  }
3125  /* A bug in libdbi stores only 7 digits of precision */
3126  if ( testdouble >= resultdouble + 0.000001e307 ||
3127  testdouble <= resultdouble - 0.000001e307 )
3128  {
3129  PWARN( "Test_DBI_Library: Double Failed %17e != %17e",
3130  testdouble, resultdouble );
3131  retval = GNC_DBI_FAIL_TEST;
3132  }
3133  return retval;
3134 }
3135 
3136 
3137 static GncSqlConnection*
3138 create_dbi_connection( /*@ observer @*/ provider_functions_t* provider,
3139  /*@ observer @*/ QofBackend* qbe,
3140  /*@ observer @*/ dbi_conn conn )
3141 {
3142  GncDbiSqlConnection* dbi_conn;
3143 
3144  dbi_conn = g_new0( GncDbiSqlConnection, 1 );
3145  g_assert( dbi_conn != NULL );
3146 
3147  dbi_conn->base.dispose = conn_dispose;
3148  dbi_conn->base.executeSelectStatement = conn_execute_select_statement;
3149  dbi_conn->base.executeNonSelectStatement = conn_execute_nonselect_statement;
3150  dbi_conn->base.createStatementFromSql = conn_create_statement_from_sql;
3151  dbi_conn->base.doesTableExist = conn_does_table_exist;
3152  dbi_conn->base.beginTransaction = conn_begin_transaction;
3153  dbi_conn->base.rollbackTransaction = conn_rollback_transaction;
3154  dbi_conn->base.commitTransaction = conn_commit_transaction;
3155  dbi_conn->base.createTable = conn_create_table;
3156  dbi_conn->base.createIndex = conn_create_index;
3157  dbi_conn->base.addColumnsToTable = conn_add_columns_to_table;
3158  dbi_conn->base.quoteString = conn_quote_string;
3159  dbi_conn->qbe = qbe;
3160  dbi_conn->conn = conn;
3161  dbi_conn->provider = provider;
3162  dbi_conn->conn_ok = TRUE;
3163  gnc_dbi_init_error(dbi_conn);
3164 
3165  return (GncSqlConnection*)dbi_conn;
3166 }
3167 
3168 /* ========================== END OF FILE ===================== */
G_MODULE_EXPORT void qof_backend_module_init(void)
void gnc_sql_begin_edit(GncSqlBackend *be, QofInstance *inst)
void gnc_sql_finalize_version_info(GncSqlBackend *be)
QofBackend *(* backend_new)(void)
Definition: qofbackend-p.h:255
void qof_backend_set_error(QofBackend *be, QofBackendError err)
load and save data to SQL via libdbi
void(* provider_free)(QofBackendProvider *)
Definition: qofbackend-p.h:275
gint gnc_sql_get_table_version(const GncSqlBackend *be, const gchar *table_name)
void gnc_sql_rollback_edit(GncSqlBackend *be, QofInstance *inst)
void qof_backend_register_provider(QofBackendProvider *)
#define G_LOG_DOMAIN
Functions providing the SX List as a plugin page.
#define PINFO(format, args...)
Definition: qoflog.h:249
void gnc_sql_init(GncSqlBackend *be)
const gchar * QofIdTypeConst
Definition: qofid.h:87
#define DEBUG(format, args...)
Definition: qoflog.h:255
gboolean(* doesTableExist)(GncSqlConnection *, const gchar *)
gchar * gnc_uri_get_path(const gchar *uri)
GncSqlConnection * conn
void(* export_fn)(QofBackend *, QofBook *)
Definition: qofbackend-p.h:343
#define PERR(format, args...)
Definition: qoflog.h:237
#define ENTER(format, args...)
Definition: qoflog.h:261
void gnc_module_init_backend_dbi(void)
QofBook * book
#define PWARN(format, args...)
Definition: qoflog.h:243
gboolean(* addColumnsToTable)(GncSqlConnection *, const gchar *table, GList *)
GncSqlBasicColumnType type
Account handling public routines.
void qof_backend_set_message(QofBackend *be, const char *format,...)
Anchor Scheduled Transaction info in a book. See src/doc/books.txt for design overview.
void gnc_uri_get_components(const gchar *uri, gchar **protocol, gchar **hostname, gint32 *port, gchar **username, gchar **password, gchar **path)
Definition: gnc-uri-utils.c:82
gchar * gnc_sql_get_sql_value(const GncSqlConnection *conn, const GValue *value)
gboolean(* check_data_type)(const char *)
Distinguish two providers with same access method.
Definition: qofbackend-p.h:272
gboolean(* createIndex)(GncSqlConnection *, const gchar *, const gchar *, const GncSqlColumnTableEntry *)
QofBackendError qof_backend_get_error(QofBackend *be)
All type declarations for the whole Gnucash engine.
gchar * gnc_build_translog_path(const gchar *filename)
Make a path to filename in the translog subdirectory of the user's configuration directory.
Generic api to store and retrieve preferences.
void gnc_sql_commit_edit(GncSqlBackend *be, QofInstance *inst)
API for the transaction logger.
gboolean(* createTable)(GncSqlConnection *, const gchar *, GList *)
const char * provider_name
Definition: qofbackend-p.h:242
void gnc_sql_sync_all(GncSqlBackend *be, QofBook *book)
void xaccLogSetBaseName(const char *basepath)
Definition: TransLog.c:117
gint(* executeNonSelectStatement)(GncSqlConnection *, GncSqlStatement *)
const char * access_method
Definition: qofbackend-p.h:248
#define LEAVE(format, args...)
Definition: qoflog.h:271
gboolean(* rollbackTransaction)(GncSqlConnection *)
Utility functions for convert uri in separate components and back.
gint64 time64
Definition: gnc-date.h:83
void(* create_tables)(GncSqlBackend *be)
const gchar * timespec_format
File path resolution utility functions.
gboolean(* beginTransaction)(GncSqlConnection *)
gboolean(* commitTransaction)(GncSqlConnection *)
void gnc_module_finalize_backend_dbi(void)
void gnc_sql_init_version_info(GncSqlBackend *be)
void gnc_sql_load(GncSqlBackend *be, QofBook *book, QofBackendLoadType loadType)
const gchar * QofLogModule
Definition: qofid.h:89
GncSqlResult *(* executeSelectStatement)(GncSqlConnection *, GncSqlStatement *)