static int cdbi_connect_database (cdbi_database_t *db) /* {{{ */ { dbi_driver driver; dbi_conn connection; size_t i; int status; if (db->connection != NULL) { status = dbi_conn_ping (db->connection); if (status != 0) /* connection is alive */ return (0); dbi_conn_close (db->connection); db->connection = NULL; } driver = dbi_driver_open_r (db->driver, dbi_instance); if (driver == NULL) { ERROR ("dbi plugin: cdbi_connect_database: dbi_driver_open_r (%s) failed.", db->driver); INFO ("dbi plugin: Maybe the driver isn't installed? " "Known drivers are:"); for (driver = dbi_driver_list_r (NULL, dbi_instance); driver != NULL; driver = dbi_driver_list_r (driver, dbi_instance)) { INFO ("dbi plugin: * %s", dbi_driver_get_name (driver)); } return (-1); } connection = dbi_conn_open (driver); if (connection == NULL) { ERROR ("dbi plugin: cdbi_connect_database: dbi_conn_open (%s) failed.", db->driver); return (-1); } /* Set all the driver options. Because this is a very very very generic * interface, the error handling is kind of long. If an invalid option is * encountered, it will get a list of options understood by the driver and * report that as `INFO'. This way, users hopefully don't have too much * trouble finding out how to configure the plugin correctly.. */ for (i = 0; i < db->driver_options_num; i++) { if (db->driver_options[i].is_numeric) { status = dbi_conn_set_option_numeric (connection, db->driver_options[i].key, db->driver_options[i].value.numeric); if (status != 0) { char errbuf[1024]; ERROR ("dbi plugin: cdbi_connect_database (%s): " "dbi_conn_set_option_numeric (\"%s\", %i) failed: %s.", db->name, db->driver_options[i].key, db->driver_options[i].value.numeric, cdbi_strerror (connection, errbuf, sizeof (errbuf))); } } else { status = dbi_conn_set_option (connection, db->driver_options[i].key, db->driver_options[i].value.string); if (status != 0) { char errbuf[1024]; ERROR ("dbi plugin: cdbi_connect_database (%s): " "dbi_conn_set_option (\"%s\", \"%s\") failed: %s.", db->name, db->driver_options[i].key, db->driver_options[i].value.string, cdbi_strerror (connection, errbuf, sizeof (errbuf))); } } if (status != 0) { char const *opt; INFO ("dbi plugin: This is a list of all options understood " "by the `%s' driver:", db->driver); for (opt = dbi_conn_get_option_list (connection, NULL); opt != NULL; opt = dbi_conn_get_option_list (connection, opt)) { INFO ("dbi plugin: * %s", opt); } dbi_conn_close (connection); return (-1); } } /* for (i = 0; i < db->driver_options_num; i++) */ status = dbi_conn_connect (connection); if (status != 0) { char errbuf[1024]; ERROR ("dbi plugin: cdbi_connect_database (%s): " "dbi_conn_connect failed: %s", db->name, cdbi_strerror (connection, errbuf, sizeof (errbuf))); dbi_conn_close (connection); return (-1); } if (db->select_db != NULL) { status = dbi_conn_select_db (connection, db->select_db); if (status != 0) { char errbuf[1024]; WARNING ("dbi plugin: cdbi_connect_database (%s): " "dbi_conn_select_db (%s) failed: %s. Check the `SelectDB' option.", db->name, db->select_db, cdbi_strerror (connection, errbuf, sizeof (errbuf))); dbi_conn_close (connection); return (-1); } } db->connection = connection; return (0); } /* }}} int cdbi_connect_database */
template <DbType T> void gnc_dbi_session_begin (QofBackend* qbe, QofSession* session, const char* book_id, gboolean ignore_lock, gboolean create, gboolean force) { GncDbiBackend* be = (GncDbiBackend*)qbe; GncDbiTestResult dbi_test_result = GNC_DBI_PASS; PairVec options; g_return_if_fail (qbe != nullptr); g_return_if_fail (session != nullptr); g_return_if_fail (book_id != nullptr); ENTER (" "); /* Split the book-id * Format is protocol://username:password@hostname:port/dbname where username, password and port are optional) */ UriStrings uri(book_id); if (T == DbType::DBI_PGSQL) { if (uri.m_portnum == 0) uri.m_portnum = PGSQL_DEFAULT_PORT; /* Postgres's SQL interface coerces identifiers to lower case, but the * C interface is case-sensitive. This results in a mixed-case dbname * being created (with a lower case name) but then dbi can't conect to * it. To work around this, coerce the name to lowercase first. */ auto lcname = g_utf8_strdown (uri.dbname(), -1); uri.m_dbname = std::string{lcname}; g_free(lcname); } be->connect(nullptr); auto conn = conn_setup<T>(qbe, options, uri); if (conn == nullptr) { LEAVE("Error"); return; } be->set_exists(true); //May be unset in the error handler. auto result = dbi_conn_connect (conn); if (result == 0) { if (T == DbType::DBI_MYSQL) adjust_sql_options (conn); if(!conn_test_dbi_library(conn, qbe)) { dbi_conn_close(conn); LEAVE("Error"); return; } if (create && !force && save_may_clobber_data (conn, uri.quote_dbname(T))) { qof_backend_set_error (qbe, ERR_BACKEND_STORE_EXISTS); PWARN ("Databse already exists, Might clobber it."); dbi_conn_close(conn); LEAVE("Error"); return; } } else { if (be->exists()) { PERR ("Unable to connect to database '%s'\n", uri.dbname()); qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR); dbi_conn_close(conn); LEAVE("Error"); return; } if (create) { if (!create_database(T, qbe, conn, uri.quote_dbname(T).c_str())) { dbi_conn_close(conn); LEAVE("Error"); return; } conn = conn_setup<T>(qbe, options, uri); result = dbi_conn_connect (conn); if (result < 0) { PERR ("Unable to create database '%s'\n", uri.dbname()); qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR); dbi_conn_close(conn); LEAVE("Error"); return; } if (T == DbType::DBI_MYSQL) adjust_sql_options (conn); if (!conn_test_dbi_library(conn, qbe)) { if (T == DbType::DBI_PGSQL) dbi_conn_select_db (conn, "template1"); dbi_conn_queryf (conn, "DROP DATABASE %s", uri.quote_dbname(T).c_str()); dbi_conn_close(conn); return; } } else { qof_backend_set_error (qbe, ERR_BACKEND_NO_SUCH_DB); qof_backend_set_message (qbe, "Database %s not found", uri.dbname()); } } be->connect(nullptr); try { be->connect(new GncDbiSqlConnection(T, qbe, conn, ignore_lock)); } catch (std::runtime_error& err) { return; } /* We should now have a proper session set up. * Let's start logging */ auto translog_path = gnc_build_translog_path (uri.basename().c_str()); xaccLogSetBaseName (translog_path); PINFO ("logpath=%s", translog_path ? translog_path : "(null)"); g_free (translog_path); LEAVE (" "); }