Example #1
0
static bool
create_database(DbType type, QofBackend *qbe, dbi_conn conn, const char* db)
{
    const char *dbname;
    const char *dbcreate;
    if (type == DbType::DBI_MYSQL)
    {
        dbname = "mysql";
        dbcreate = "CREATE DATABASE %s CHARACTER SET utf8";
    }
    else
    {
        dbname = "postgres";
        dbcreate = "CREATE DATABASE %s WITH TEMPLATE template0 ENCODING 'UTF8'";
    }
    PairVec options;
    options.push_back(std::make_pair("dbname", dbname));
    try
    {
        set_options(conn, options);
    }
    catch (std::runtime_error& err)
    {
        qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR);
        return false;
    }

    auto result = dbi_conn_connect (conn);
    if (result < 0)
    {
        PERR ("Unable to connect to %s database", dbname);
        qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR);
        return false;
    }
    if (type == DbType::DBI_MYSQL)
        adjust_sql_options(conn);
    auto dresult = dbi_conn_queryf (conn, dbcreate, db);
    if (dresult == nullptr)
    {
        PERR ("Unable to create database '%s'\n", db);
        qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR);
        return false;
    }
    if (type == DbType::DBI_PGSQL)
    {
        const char *alterdb = "ALTER DATABASE %s SET "
            "standard_conforming_strings TO on";
        dbi_conn_queryf (conn, alterdb, db);
    }
    dbi_conn_close(conn);
    conn = nullptr;
    return true;
}
Example #2
0
gboolean
qof_commit_edit_part2(QofInstance *inst,
                      void (*on_error)(QofInstance *, QofBackendError),
                      void (*on_done)(QofInstance *),
                      void (*on_free)(QofInstance *))
{
    QofInstancePrivate *priv;
    QofBackend * be;
    gboolean dirty;

    priv = GET_PRIVATE(inst);
    dirty = priv->dirty;

    /* See if there's a backend.  If there is, invoke it. */
    be = qof_book_get_backend(priv->book);
    if (be && qof_backend_commit_exists(be))
    {
        QofBackendError errcode;

        /* clear errors */
        do
        {
            errcode = qof_backend_get_error(be);
        }
        while (ERR_BACKEND_NO_ERR != errcode);

        qof_backend_run_commit(be, inst);
        errcode = qof_backend_get_error(be);
        if (ERR_BACKEND_NO_ERR != errcode)
        {
            /* XXX Should perform a rollback here */
            priv->do_free = FALSE;

            /* Push error back onto the stack */
            qof_backend_set_error (be, errcode);
            if (on_error)
                on_error(inst, errcode);
            return FALSE;
        }
        /* XXX the backend commit code should clear dirty!! */
        priv->dirty = FALSE;
    }
//    if (dirty && qof_get_alt_dirty_mode() &&
//        !(priv->infant && priv->do_free)) {
//      qof_collection_mark_dirty(priv->collection);
//      qof_book_mark_dirty(priv->book);
//    }
    priv->infant = FALSE;

    if (priv->do_free)
    {
        if (on_free)
            on_free(inst);
        return TRUE;
    }

    if (on_done)
        on_done(inst);
    return TRUE;
}
Example #3
0
static /*@ null @*/ Transaction*
load_single_tx( GncSqlBackend* be, GncSqlRow* row )
{
    const GncGUID* guid;
    GncGUID tx_guid;
    Transaction* pTx;

    g_return_val_if_fail( be != NULL, NULL );
    g_return_val_if_fail( row != NULL, NULL );

    guid = gnc_sql_load_guid( be, row );
    if ( guid == NULL ) return NULL;
    tx_guid = *guid;

    // Don't overwrite the transaction if it's already been loaded (and possibly modified).
    pTx = xaccTransLookup( &tx_guid, be->book );
    if ( pTx != NULL )
    {
        return NULL;
    }

    pTx = xaccMallocTransaction( be->book );
    xaccTransBeginEdit( pTx );
    gnc_sql_load_object( be, row, GNC_ID_TRANS, pTx, tx_col_table );

    if (pTx != xaccTransLookup( &tx_guid, be->book ))
    {
	PERR("A malformed transaction with id %s was found in the dataset.",
	     guid_to_string(qof_instance_get_guid(pTx)));
	qof_backend_set_error( &be->be, ERR_BACKEND_DATA_CORRUPT);
	pTx = NULL;
    }

    return pTx;
}
Example #4
0
/**
 * Sets standard db options in a dbi_conn.
 *
 * @param qbe QOF backend
 * @param conn dbi_conn connection
 * @param uri UriStrings containing the needed paramters.
 * @return TRUE if successful, FALSE if error
 */
static bool
set_standard_connection_options (QofBackend* qbe, dbi_conn conn,
                                 const UriStrings& uri)

{
    gint result;
    PairVec options;
    options.push_back(std::make_pair("host", uri.m_host));
    options.push_back(std::make_pair("dbname", uri.m_dbname));
    options.push_back(std::make_pair("username", uri.m_username));
    options.push_back(std::make_pair("password", uri.m_password));
    options.push_back(std::make_pair("encoding", "UTF-8"));
    try
    {
        set_options(conn, options);
        auto result = dbi_conn_set_option_numeric(conn, "port", uri.m_portnum);
        if (result < 0)
        {
            const char *msg = nullptr;
            auto err = dbi_conn_error(conn, &msg);
            PERR("Error setting port option to %d: %s", uri.m_portnum, msg);
            throw std::runtime_error(msg);
        }
    }
    catch (std::runtime_error& err)
    {
        qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR);
        return false;
    }

    return true;
}
Example #5
0
template <DbType Type> dbi_conn
conn_setup (QofBackend* qbe, PairVec& options,
            UriStrings& uri)
{
    const char* dbstr = (Type == DbType::DBI_SQLITE ? "sqlite3" :
                         Type == DbType::DBI_MYSQL ? "mysql" : "pgsql");
#if HAVE_LIBDBI_R
    dbi_conn conn = nullptr;
    if (dbi_instance)
        conn = dbi_conn_new_r (dbstr, dbi_instance);
    else
        PERR ("Attempt to connect with an uninitialized dbi_instance");
#else
    auto conn = dbi_conn_new (dbstr);
#endif

    if (conn == nullptr)
    {
        PERR ("Unable to create %s dbi connection", dbstr);
        qof_backend_set_error (qbe, ERR_BACKEND_BAD_URL);
	return nullptr;
    }

    dbi_conn_error_handler (conn, error_handler<Type>, qbe);
    if (!uri.m_dbname.empty() &&
        !set_standard_connection_options(qbe, conn, uri))
    {
        dbi_conn_close(conn);
        return nullptr;
    }
    if(!options.empty())
    {
        try {
            set_options(conn, options);
        }
        catch (std::runtime_error& err)
        {
            dbi_conn_close(conn);
            qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR);
            return nullptr;
        }
    }

    return conn;
}
Example #6
0
/* GNUCASH_RESAVE_VERSION indicates the earliest database version
 * compatible with this version of Gnucash; the stored value is the
 * earliest version of Gnucash conpatible with the database. If the
 * GNUCASH_RESAVE_VERSION for this Gnucash is newer than the Gnucash
 * version which created the database, a resave is offered. If the
 * version of this Gnucash is older than the saved resave version,
 * then the database will be loaded read-only. A resave will update
 * both values to match this version of Gnucash.
 */
void
gnc_dbi_load (QofBackend* qbe,  QofBook* book, QofBackendLoadType loadType)
{
    GncDbiBackend* be = (GncDbiBackend*)qbe;

    g_return_if_fail (qbe != nullptr);
    g_return_if_fail (book != nullptr);

    ENTER ("be=%p, book=%p", be, book);

    if (loadType == LOAD_TYPE_INITIAL_LOAD)
    {

        // Set up table version information
        be->init_version_info ();
        assert (be->m_book == nullptr);

        // Call all object backends to create any required tables
        auto registry = gnc_sql_get_backend_registry();
        for (auto entry : registry)
            create_tables(entry, be);
    }

    gnc_sql_load (be, book, loadType);

    if (GNUCASH_RESAVE_VERSION > be->get_table_version("Gnucash"))
    {
        /* The database was loaded with an older database schema or
         * data semantics. In order to ensure consistency, the whole
         * thing needs to be saved anew. */
        qof_backend_set_error (qbe, ERR_SQL_DB_TOO_OLD);
    }
    else if (GNUCASH_RESAVE_VERSION < be->get_table_version("Gnucash-Resave"))
    {
        /* Worse, the database was created with a newer version. We
         * can't safely write to this database, so the user will have
         * to do a "save as" to make one that we can write to.
         */
        qof_backend_set_error (qbe, ERR_SQL_DB_TOO_NEW);
    }


    LEAVE ("");
}
Example #7
0
/**
 * Safely resave a database by renaming all of its tables, recreating
 * everything, and then dropping the backup tables only if there were
 * no errors. If there are errors, drop the new tables and restore the
 * originals.
 *
 * @param qbe: QofBackend for the session.
 * @param book: QofBook to be saved in the database.
 */
void
gnc_dbi_safe_sync_all (QofBackend* qbe, QofBook* book)
{
    GncDbiBackend* be = (GncDbiBackend*)qbe;
    auto conn = dynamic_cast<GncDbiSqlConnection*>(be->m_conn);

    g_return_if_fail (conn != nullptr);
    g_return_if_fail (be != nullptr);
    g_return_if_fail (book != nullptr);

    ENTER ("book=%p, primary=%p", book, be->m_book);
    auto table_list = conn->m_provider->get_table_list (conn->conn(), "");
    if (!conn->table_operation (table_list, backup))
    {
        qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR);
        conn->table_operation (table_list, rollback);
        LEAVE ("Failed to rename tables");
        return;
    }
    auto index_list = conn->m_provider->get_index_list (conn->m_conn);
    for (auto index : index_list)
    {
        const char* errmsg;
        conn->m_provider->drop_index (conn->m_conn, index);
        if (DBI_ERROR_NONE != dbi_conn_error (conn->m_conn, &errmsg))
        {
            qof_backend_set_error (qbe, ERR_BACKEND_SERVER_ERR);
            conn->table_operation (table_list, rollback);
            LEAVE ("Failed to drop indexes %s", errmsg);
            return;
        }
    }

    gnc_sql_sync_all (be, book);
    if (qof_backend_check_error (qbe))
    {
        conn->table_operation (table_list, rollback);
        LEAVE ("Failed to create new database tables");
        return;
    }
    conn->table_operation (table_list, drop_backup);
    LEAVE ("book=%p", book);
}
Example #8
0
int
GncSqlBackend::execute_nonselect_statement(const GncSqlStatementPtr& stmt) const noexcept
{
    auto result = m_conn->execute_nonselect_statement(stmt);
    if (result == -1)
    {
        PERR ("SQL error: %s\n", stmt->to_sql());
        qof_backend_set_error ((QofBackend*)this, ERR_BACKEND_SERVER_ERR);
    }
    return result;
}
Example #9
0
GncSqlStatementPtr
GncSqlBackend::create_statement_from_sql(const std::string& str) const noexcept
{
    auto stmt = m_conn->create_statement_from_sql(str);
    if (stmt == nullptr)
    {
        PERR ("SQL error: %s\n", str.c_str());
        qof_backend_set_error ((QofBackend*)this, ERR_BACKEND_SERVER_ERR);
    }
    return stmt;
}
Example #10
0
/* GNUCASH_RESAVE_VERSION indicates the earliest database version
 * compatible with this version of Gnucash; the stored value is the
 * earliest version of Gnucash conpatible with the database. If the
 * GNUCASH_RESAVE_VERSION for this Gnucash is newer than the Gnucash
 * version which created the database, a resave is offered. If the
 * version of this Gnucash is older than the saved resave version,
 * then the database will be loaded read-only. A resave will update
 * both values to match this version of Gnucash.
 */
void
gnc_dbi_load (QofBackend* qof_be,  QofBook* book, QofBackendLoadType loadType)
{
    GncDbiBackend* dbi_be = reinterpret_cast<decltype(dbi_be)>(qof_be);

    g_return_if_fail (qof_be != nullptr);
    g_return_if_fail (book != nullptr);

    ENTER ("dbi_be=%p, book=%p", dbi_be, book);

    if (loadType == LOAD_TYPE_INITIAL_LOAD)
    {

        // Set up table version information
        dbi_be->init_version_info ();
        assert (dbi_be->m_book == nullptr);
        dbi_be->create_tables();
    }

    dbi_be->load(book, loadType);

    if (GNUCASH_RESAVE_VERSION > dbi_be->get_table_version("Gnucash"))
    {
        /* The database was loaded with an older database schema or
         * data semantics. In order to ensure consistency, the whole
         * thing needs to be saved anew. */
        qof_backend_set_error (qof_be, ERR_SQL_DB_TOO_OLD);
    }
    else if (GNUCASH_RESAVE_VERSION < dbi_be->get_table_version("Gnucash-Resave"))
    {
        /* Worse, the database was created with a newer version. We
         * can't safely write to this database, so the user will have
         * to do a "save as" to make one that we can write to.
         */
        qof_backend_set_error (qof_be, ERR_SQL_DB_TOO_NEW);
    }


    LEAVE ("");
}
Example #11
0
static bool
conn_test_dbi_library(dbi_conn conn, QofBackend* qbe)
{
    auto result = dbi_library_test (conn);
    switch (result)
    {
        case GNC_DBI_PASS:
            break;

        case GNC_DBI_FAIL_SETUP:
            qof_backend_set_error (qbe, ERR_SQL_DBI_UNTESTABLE);
            qof_backend_set_message (qbe,
                                     "DBI library large number test incomplete");
            break;

        case GNC_DBI_FAIL_TEST:
            qof_backend_set_error (qbe, ERR_SQL_BAD_DBI);
            qof_backend_set_message (qbe,
                                     "DBI library fails large number test");
            break;
    }
    return result == GNC_DBI_PASS;
}
Example #12
0
static /*@ null @*/ Split*
load_single_split( GncSqlBackend* be, GncSqlRow* row )
{
    const GncGUID* guid;
    GncGUID split_guid;
    Split* pSplit = NULL;
    gboolean bad_guid = FALSE;

    g_return_val_if_fail( be != NULL, NULL );
    g_return_val_if_fail( row != NULL, NULL );

    guid = gnc_sql_load_guid( be, row );
    if ( guid == NULL ) return NULL;
    if (guid_equal(guid, guid_null()))
    {
	PWARN("Bad GUID, creating new");
	bad_guid = TRUE;
	split_guid = guid_new_return();
    }
    else
    {
	split_guid = *guid;
	pSplit = xaccSplitLookup( &split_guid, be->book );
    }

    if ( pSplit == NULL )
    {
	pSplit = xaccMallocSplit( be->book );
    }

    /* If the split is dirty, don't overwrite it */
    if ( !qof_instance_is_dirty( QOF_INSTANCE(pSplit) ) )
    {
        gnc_sql_load_object( be, row, GNC_ID_SPLIT, pSplit, split_col_table );
    }

    /*# -ifempty */
    if (pSplit != xaccSplitLookup( &split_guid, be->book ))
    {
        gchar guidstr[GUID_ENCODING_LENGTH+1];
        guid_to_string_buff(qof_instance_get_guid(pSplit), guidstr);
        PERR("A malformed split with id %s was found in the dataset.", guidstr);
        qof_backend_set_error( &be->be, ERR_BACKEND_DATA_CORRUPT);
        pSplit = NULL;
    }
    return pSplit;
}
template<> void
error_handler<DbType::DBI_PGSQL> (dbi_conn conn, void* user_data)
{
    GncDbiBackend<DbType::DBI_PGSQL>* dbi_be =
        static_cast<decltype(dbi_be)>(user_data);
    const char* msg;

    (void)dbi_conn_error (conn, &msg);
    if (g_str_has_prefix (msg, "FATAL:  database") &&
        g_str_has_suffix (msg, "does not exist\n"))
    {
        PINFO ("DBI error: %s\n", msg);
        dbi_be->set_exists(false);
    }
    else if (g_strrstr (msg,
                        "server closed the connection unexpectedly"))    // Connection lost
    {
        if (!dbi_be->connected())
        {
            PWARN ("DBI Error: Connection lost, connection pointer invalid");
            return;
        }
        PINFO ("DBI error: %s - Reconnecting...\n", msg);
        dbi_be->set_dbi_error (ERR_BACKEND_CONN_LOST, 1, true);
        dbi_be->retry_connection(msg);
    }
    else if (g_str_has_prefix (msg, "connection pointer is NULL") ||
             g_str_has_prefix (msg, "could not connect to server"))       // No connection
    {

        if (!dbi_be->connected())
            qof_backend_set_error(reinterpret_cast<QofBackend*>(dbi_be),
                                  ERR_BACKEND_CANT_CONNECT);
        else
        {
            dbi_be->set_dbi_error(ERR_BACKEND_CANT_CONNECT, 1, true);
            dbi_be->retry_connection (msg);
        }
    }
    else
    {
        PERR ("DBI error: %s\n", msg);
        if (dbi_be->connected())
            dbi_be->set_dbi_error (ERR_BACKEND_MISC, 0, false);
    }
}
Example #14
0
/**
 * Registers the version for a table.  Registering involves updating the
 * db version table and also the hash table.
 *
 * @param be Backend struct
 * @param table_name Table name
 * @param version Version number
 * @return TRUE if successful, FALSE if unsuccessful
 */
bool
GncSqlBackend::set_table_version (const std::string& table_name,
                                  uint_t version) noexcept
{
    g_return_val_if_fail (version > 0, false);

    unsigned int cur_version{0};
    std::stringstream sql;
    auto ver_entry = std::find_if(m_versions.begin(), m_versions.end(),
                                [table_name](const VersionPair& ver) {
                                    return ver.first == table_name; });
    if (ver_entry != m_versions.end())
        cur_version = ver_entry->second;
    if (cur_version != version)
    {
        if (cur_version == 0)
        {
            sql << "INSERT INTO " << VERSION_TABLE_NAME << " VALUES('" <<
                table_name << "'," << version <<")";
            m_versions.push_back(std::make_pair(table_name, version));
        }
        else
        {
            sql << "UPDATE " <<  VERSION_TABLE_NAME << " SET " <<
                VERSION_COL_NAME << "=" << version << " WHERE " <<
                TABLE_COL_NAME << "='" << table_name << "'";
            ver_entry->second = version;
        }
        auto stmt = create_statement_from_sql(sql.str());
        auto status = execute_nonselect_statement (stmt);
        if (status == -1)
        {
            PERR ("SQL error: %s\n", sql.str().c_str());
            qof_backend_set_error ((QofBackend*)this, ERR_BACKEND_SERVER_ERR);
            return false;
        }
    }

    return true;
}
Example #15
0
static gboolean
save_transaction( GncSqlBackend* be, Transaction* pTx, gboolean do_save_splits )
{
    const GncGUID* guid;
    gint op;
    gboolean is_infant;
    QofInstance* inst;
    gboolean is_ok = TRUE;
    gchar* err = NULL;

    g_return_val_if_fail( be != NULL, FALSE );
    g_return_val_if_fail( pTx != NULL, FALSE );

    inst = QOF_INSTANCE(pTx);
    is_infant = qof_instance_get_infant( inst );
    if ( qof_instance_get_destroying( inst ) )
    {
        op = OP_DB_DELETE;
    }
    else if ( be->is_pristine_db || is_infant )
    {
        op = OP_DB_INSERT;
    }
    else
    {
        op = OP_DB_UPDATE;
    }

    if ( op != OP_DB_DELETE )
    {
        gnc_commodity *commodity = xaccTransGetCurrency( pTx );
        // Ensure the commodity is in the db
        is_ok = gnc_sql_save_commodity( be, commodity );
        if ( ! is_ok )
        {
            err = "Commodity save failed: Probably an invalid or missing currency";
            qof_backend_set_error( &be->be, ERR_BACKEND_DATA_CORRUPT);
        }
    }

    if ( is_ok )
    {
        is_ok = gnc_sql_do_db_operation( be, op, TRANSACTION_TABLE, GNC_ID_TRANS, pTx, tx_col_table );
        if ( ! is_ok )
        {
            err = "Transaction header save failed. Check trace log for SQL errors";
        }
    }

    if ( is_ok )
    {
        // Commit slots and splits
        guid = qof_instance_get_guid( inst );
        if ( !qof_instance_get_destroying(inst) )
        {
            is_ok = gnc_sql_slots_save( be, guid, is_infant, qof_instance_get_slots( inst ) );
            if ( ! is_ok )
            {
                err = "Slots save failed. Check trace log for SQL errors";
            }
            if ( is_ok && do_save_splits )
            {
                is_ok = save_splits( be, guid, xaccTransGetSplitList( pTx ) );
                if ( ! is_ok )
                {
                    err = "Split save failed. Check trace log for SQL errors";
                }
            }
        }
        else
        {
            is_ok = gnc_sql_slots_delete( be, guid );
            if ( ! is_ok )
            {
                err = "Slots delete failed. Check trace log for SQL errors";
            }
            if ( is_ok )
            {
                is_ok = delete_splits( be, pTx );
                if ( ! is_ok )
                {
                    err = "Split delete failed. Check trace log for SQL errors";
                }
            }
        }
    }
    if (! is_ok )
    {
        G_GNUC_UNUSED gchar *message1 = "Transaction %s dated %s in account %s not saved due to %s.%s";
        G_GNUC_UNUSED gchar *message2 = "\nDatabase may be corrupted, check your data carefully.";
        Split* split = xaccTransGetSplit( pTx, 0);
        Account *acc = xaccSplitGetAccount( split );
        /* FIXME: This needs to be implemented
        qof_error_format_secondary_text( GTK_MESSAGE_DIALOG( msg ),
        					  message1,
        					 xaccTransGetDescription( pTx ),
        					  qof_print_date( xaccTransGetDate( pTx ) ),
        					  xaccAccountGetName( acc ),
        					  err,
        					  message2 );
        */
        PERR( "Transaction %s dated %s in account %s not saved due to %s.\n",
              xaccTransGetDescription( pTx ),
              qof_print_date( xaccTransGetDate( pTx ) ),
              xaccAccountGetName( acc ),
              err );
    }
    return is_ok;
}
Example #16
0
template <> void
gnc_dbi_session_begin<DbType::DBI_SQLITE>(QofBackend* qbe, QofSession* session,
                               const char* book_id, gboolean ignore_lock,
                               gboolean create, gboolean force)
{
    GncDbiBackend* be = (GncDbiBackend*)qbe;
    const char* msg = nullptr;
    gboolean file_exists;
    PairVec options;

    g_return_if_fail (qbe != nullptr);
    g_return_if_fail (session != nullptr);
    g_return_if_fail (book_id != nullptr);

    ENTER (" ");

    /* Remove uri type if present */
    auto path = gnc_uri_get_path (book_id);
    std::string filepath{path};
    g_free(path);
    GFileTest ftest = static_cast<decltype (ftest)> (
        G_FILE_TEST_IS_REGULAR | G_FILE_TEST_EXISTS) ;
    file_exists = g_file_test (filepath.c_str(), ftest);
    if (!create && !file_exists)
    {
        qof_backend_set_error (qbe, ERR_FILEIO_FILE_NOT_FOUND);
        qof_backend_set_message (qbe, "Sqlite3 file %s not found",
                                 filepath.c_str());
        PWARN ("Sqlite3 file %s not found", filepath.c_str());
        LEAVE("Error");
	return;
    }

    if (create && !force && file_exists)
    {
        qof_backend_set_error (qbe, ERR_BACKEND_STORE_EXISTS);
        msg = "Might clobber, no force";
        PWARN ("%s", msg);
        LEAVE("Error");
	return;
    }

    be->connect(nullptr);
    /* dbi-sqlite3 documentation says that sqlite3 doesn't take a "host" option */
    options.push_back(std::make_pair("host", "localhost"));
    auto dirname = g_path_get_dirname (filepath.c_str());
    auto basename = g_path_get_basename (filepath.c_str());
    options.push_back(std::make_pair("dbname", basename));
    options.push_back(std::make_pair("sqlite3_dbdir", dirname));
    if (basename != nullptr) g_free (basename);
    if (dirname != nullptr) g_free (dirname);
    UriStrings uri;
    auto conn = conn_setup<DbType::DBI_SQLITE>(qbe, options, uri);
    if (conn == nullptr)
    {
        LEAVE("Error");
        return;
    }

    auto result = dbi_conn_connect (conn);

    if (result < 0)
    {
        dbi_conn_close(conn);
        PERR ("Unable to connect to %s: %d\n", book_id, result);
        qof_backend_set_error (qbe, ERR_BACKEND_BAD_URL);
        LEAVE("Error");
	return;
    }

    if (!conn_test_dbi_library(conn, qbe))
    {
        if (create && !file_exists)
        {
         /* File didn't exist before, but it does now, and we don't want to
          * leave it lying around.
          */
            dbi_conn_close (conn);
            conn = nullptr;
            g_unlink (filepath.c_str());
        }
        dbi_conn_close(conn);
        LEAVE("Bad DBI Library");
        return;
    }

    try
    {
        be->connect(new GncDbiSqlConnection(DbType::DBI_SQLITE,
                                            qbe, conn, ignore_lock));
    }
    catch (std::runtime_error& err)
    {
        return;
    }

    /* We should now have a proper session set up.
     * Let's start logging */
    xaccLogSetBaseName (filepath.c_str());
    PINFO ("logpath=%s", filepath.c_str() ? filepath.c_str() : "(null)");

    LEAVE ("");
}
Example #17
0
/* Commit_edit handler - find the correct backend handler for this object
 * type and call its commit handler
 */
void
GncSqlBackend::commit_edit (QofInstance* inst)
{
    sql_backend be_data;
    gboolean is_dirty;
    gboolean is_destroying;
    gboolean is_infant;

    g_return_if_fail (inst != NULL);

    if (qof_book_is_readonly(m_book))
    {
        qof_backend_set_error (&qof_be, ERR_BACKEND_READONLY);
        (void)m_conn->rollback_transaction ();
        return;
    }
    /* During initial load where objects are being created, don't commit
    anything, but do mark the object as clean. */
    if (m_loading)
    {
        qof_instance_mark_clean (inst);
        return;
    }

    // The engine has a PriceDB object but it isn't in the database
    if (strcmp (inst->e_type, "PriceDB") == 0)
    {
        qof_instance_mark_clean (inst);
        qof_book_mark_session_saved (m_book);
        return;
    }

    ENTER (" ");

    is_dirty = qof_instance_get_dirty_flag (inst);
    is_destroying = qof_instance_get_destroying (inst);
    is_infant = qof_instance_get_infant (inst);

    DEBUG ("%s dirty = %d, do_free = %d, infant = %d\n",
           (inst->e_type ? inst->e_type : "(null)"),
           is_dirty, is_destroying, is_infant);

    if (!is_dirty && !is_destroying)
    {
        LEAVE ("!dirty OR !destroying");
        return;
    }

    if (!m_conn->begin_transaction ())
    {
        PERR ("begin_transaction failed\n");
        LEAVE ("Rolled back - database transaction begin error");
        return;
    }

    bool is_ok = true;

    auto obe = m_backend_registry.get_object_backend(std::string{inst->e_type});
    if (obe != nullptr)
        is_ok = obe->commit(this, inst);
    else
    {
        PERR ("Unknown object type '%s'\n", inst->e_type);
        (void)m_conn->rollback_transaction ();

        // Don't let unknown items still mark the book as being dirty
        qof_book_mark_session_saved(m_book);
        qof_instance_mark_clean (inst);
        LEAVE ("Rolled back - unknown object type");
        return;
    }
    if (!is_ok)
    {
        // Error - roll it back
        (void)m_conn->rollback_transaction();

        // This *should* leave things marked dirty
        LEAVE ("Rolled back - database error");
        return;
    }

    (void)m_conn->commit_transaction ();

    qof_book_mark_session_saved(m_book);
    qof_instance_mark_clean (inst);

    LEAVE ("");
}
Example #18
0
void
GncSqlBackend::sync_all(QofBook* book)
{
    g_return_if_fail (book != NULL);

    reset_version_info();
    ENTER ("book=%p, sql_be->book=%p", book, m_book);
    update_progress();

    /* Create new tables */
    m_is_pristine_db = true;
    create_tables();

    /* Save all contents */
    m_book = book;
    auto is_ok = m_conn->begin_transaction();

    // FIXME: should write the set of commodities that are used
    // write_commodities(sql_be, book);
    if (is_ok)
    {
        auto obe = m_backend_registry.get_object_backend(GNC_ID_BOOK);
        is_ok = obe->commit (this, QOF_INSTANCE (book));
    }
    if (is_ok)
    {
        is_ok = write_accounts();
    }
    if (is_ok)
    {
        is_ok = write_transactions();
    }
    if (is_ok)
    {
        is_ok = write_template_transactions();
    }
    if (is_ok)
    {
        is_ok = write_schedXactions();
    }
    if (is_ok)
    {
        for (auto entry : m_backend_registry)
            std::get<1>(entry)->write (this);
    }
    if (is_ok)
    {
        is_ok = m_conn->commit_transaction();
    }
    if (is_ok)
    {
        m_is_pristine_db = false;

        /* Mark the session as clean -- though it shouldn't ever get
         * marked dirty with this backend
         */
        qof_book_mark_session_saved(book);
    }
    else
    {
        if (!qof_backend_check_error (&qof_be))
            qof_backend_set_error (&qof_be, ERR_BACKEND_SERVER_ERR);
        is_ok = m_conn->rollback_transaction ();
    }
    finish_progress();
    LEAVE ("book=%p", book);
}
Example #19
0
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 (" ");
}