diff --git a/DBI_STATUS b/DBI_STATUS new file mode 100644 index 0000000000..4f446d164f --- /dev/null +++ b/DBI_STATUS @@ -0,0 +1,25 @@ +Status of the DBI backend. + +Code for all standard and business objects has been written. The following +bugs/issues are known. + +* Initial save performance is better than it used to be, but is still slow. +For example, saving my file with ~8000 transactions and ~21000 splits takes +13 minutes to an sqlite db. +* Foreign key definitions should be used for referential integrity. +* (Maybe) Keep an 'old values' table for each main table. When a record is +updated, copy the old record to the 'old values' table first to keep track of +any changes. +* Use prepared statements +* Use cursor-based data models when reading from the db for better performance +* Write automated tests +* Write doxygen comment headers +* Don't need a log file +* Certain dialogs create an object when opened and update it as they go along. +This can result in trying to save invalid objects: + - Price editor (new price has no commodity or currency) + - New invoice +* Need transactions to group updates to multiple objects +* Possibly mark commodities that are in the db so that they don't need to be +queried every time. +* The sqlite db file is *much* larger than the gzipped XML file. diff --git a/configure.in b/configure.in index 1d3de97456..cd56916b8b 100644 --- a/configure.in +++ b/configure.in @@ -604,6 +604,55 @@ AC_SUBST(QOF_PREFIX) AC_SUBST(QOF_LIB_DIR) AC_SUBST(QOF_XML_DIR) +AC_DEFINE(QOF_DISABLE_DEPRECATED,1, [Don't use deprecated qof functions]) + +### ----------------------- +### LIBGDA +#AC_ARG_ENABLE(gda, +# [AS_HELP_STRING([--enable-gda],[build with the libgda backend])], +# [case "${enableval}" in +# yes) want_gda=true ;; +# no) want_gda=false ;; +# *) want_gda=false ;; +# esac], +# [want_gda=false]) +#if test x${want_gda} = xtrue +#then +# PKG_CHECK_MODULES(LIBGDA, libgda-4.0 >= 3.99.2, [GDA_DIR=gda]) +#fi +#AC_SUBST(GDA_DIR) +#AC_SUBST(LIBGDA_CFLAGS) +#AC_SUBST(LIBGDA_LIBS) + +### ---------------------- +### LIBDBI +AC_ARG_ENABLE(dbi, + [AS_HELP_STRING([--enable-dbi],[build with the libdbi backend])], + [case "${enableval}" in + yes) want_dbi=true ;; + no) want_dbi=false ;; + *) want_dbi=false ;; + esac], + [want_dbi=false]) +if test x${want_dbi} = xtrue +then + AC_CHECK_HEADERS(dbi/dbi.h) + if test "x$ac_cv_header_dbi_dbi_h" != xno; then + AC_ARG_WITH( dbi-dbd-dir, + [AS_HELP_STRING([--with-dbi-dbd-dir=PATH],[specify location of libdbi drivers @<:@default=/usr/lib/dbd@:>@])], + GNC_DBD_DIR="$with_dbi_dbd_dir", + GNC_DBD_DIR="/usr/lib/dbd") + + LIBDBI_LIBS=-ldbi + DBI_DIR=dbi + else + AC_MSG_ERROR( Unable to find dbi/dbi.h ) + fi +fi +AC_SUBST(LIBDBI_LIBS) +AC_SUBST(DBI_DIR) +AC_SUBST(GNC_DBD_DIR) + ### -------------------------------------------------------------------------- ### Variables ### Set up all the initial variable values... @@ -653,94 +702,6 @@ AC_ARG_ENABLE( ref-counts-dumps, AC_DEFINE(DEBUG_REFERENCE_COUNTING,0,Enable reference count dumps) ) -### -------------------------------------------------------------------------- -### SQL -# Check to see if the user wants to have Postgres support -# -# hack alert ... we should use 'pg_config --includedir' and -# 'pg_config --libdir' to find paths; unfortunately pg_config itself -# is hard to find :-( - -AC_ARG_ENABLE( sql, - [ --enable-sql compile with sql support], - [ - PG_CONFIG=`which pg_config` - - if test "x$PG_CONFIG" = "x" ; then - PG_CONFIG="/usr/lib/postgresql/bin/pg_config" - fi - - if test "x$enableval" != "xno" ; then - - PGSQL_CFLAGS=`${PG_CONFIG} --includedir` - if test "x$PGSQL_CFLAGS" != x; then - PGSQL_CFLAGS="-I${PGSQL_CFLAGS}" - fi - - AS_SCRUB_INCLUDE(PGSQL_CFLAGS) -# XXX Fixme: CPPFLAGS are saved but CFLAGS are altered later on and not restored. - saved_CPPFLAGS="${CPPFLAGS}" - saved_CFLAGS="${CFLAGS}" - CPPFLAGS="${CPPFLAGS} ${PGSQL_CFLAGS}" - AC_CHECK_HEADERS(pgsql/libpq-fe.h postgresql/libpq-fe.h libpq-fe.h) - if test "x$ac_cv_header_pgsql_libpq_fe_h$ac_cv_header_postgresql_libpq_fe_h$ac_cv_header_libpq_fe_h" = xnonono; then - AC_MSG_ERROR([Cannot find PostgreSQL headers; won't build sql backend]) - else - master_dirs="/usr/include /usr/local/include" - if test "x$ac_cv_header_pgsql_libpq_fe_h" != xno; then - for dir in $master_dirs; do - if test -f "$dir/pgsql/libpq-fe.h"; then - CFLAGS="${CFLAGS} -I$dir/pgsql" - break - fi - done - elif test "x$ac_cv_header_postgresql_libpq_fe_h" != xno; then - for dir in $master_dirs; do - if test -f "$dir/postgresql/libpq-fe.h"; then - CFLAGS="${CFLAGS} -I$dir/postgresql" - break - fi - done - fi - - AS_SCRUB_INCLUDE(CFLAGS) - - PGSQL_LIBS=`${PG_CONFIG} --libdir` - if test "x$PGSQL_LIBS" != x; then - PGSQL_LIBS="-L${PGSQL_LIBS}" - fi - - saved_LIBS="$LIBS" - LIBS="${PGSQL_LIBS} -lpq $LIBS" - AC_MSG_CHECKING(for libpq) - AC_TRY_LINK( - [ - #include - ], - [ - PQconnectdb("asdf"); - ], - [ - AC_MSG_RESULT(yes) - SQL_DIR=postgres - ], - [ - AC_MSG_ERROR([Cannot find PostgreSQL libraries; will not build sql backend]) - ] - ) - LIBS="$saved_LIBS" - fi - CPPFLAGS="$saved_CPPFLAGS" - CFLAGS="$saved_CFLAGS" - fi - ] -) - -AC_SUBST(PGSQL_CFLAGS) -AC_SUBST(PGSQL_LIBS) -AC_SUBST(SQL_DIR) - - ### -------------------------------------------------------------------------- ### RPC has been removed in gnucash 1.9.0 @@ -1497,12 +1458,16 @@ AC_CONFIG_FILES(po/Makefile.in src/app-utils/Makefile src/app-utils/test/Makefile src/backend/Makefile + src/backend/dbi/Makefile + src/backend/dbi/test/Makefile src/backend/file/Makefile src/backend/file/test/Makefile src/backend/file/test/test-files/Makefile src/backend/file/test/test-files/xml2/Makefile src/backend/postgres/Makefile src/backend/postgres/test/Makefile + src/backend/sql/Makefile + src/backend/sql/test/Makefile src/bin/Makefile src/bin/overrides/Makefile src/bin/test/Makefile @@ -1587,6 +1552,7 @@ AC_CONFIG_FILES(po/Makefile.in src/test-core/Makefile src/business/Makefile src/business/business-core/Makefile + src/business/business-core/sql/Makefile src/business/business-core/test/Makefile src/business/business-core/file/Makefile src/business/business-utils/Makefile @@ -1611,8 +1577,11 @@ output_qof_prefix=`eval eval eval echo $QOF_PREFIX` output_qof_lib_dir=`eval eval eval echo $QOF_LIB_DIR` output_qof_xml_dir=`eval eval eval echo $QOF_XML_DIR` -if test x${SQL_DIR} != x; then -components="$components sql" +if test x${DBI_DIR} != x; then +components="$components dbi" +fi +if test x${GDA_DIR} != x; then +components="$components gda" fi if test x${OFX_DIR} != x; then components="$components ofx" diff --git a/lib/libqof/qof/gnc-date.c b/lib/libqof/qof/gnc-date.c index f2933a0b01..c1007b0d5e 100644 --- a/lib/libqof/qof/gnc-date.c +++ b/lib/libqof/qof/gnc-date.c @@ -1052,7 +1052,7 @@ gnc_iso8601_to_timespec_gmt(const char *str) /* Timezone format can be +hh or +hhmm or +hh.mm (or -) (or not present) */ str += strcspn (str, "+-"); - if (str) + if (*str) { buf[0] = str[0]; buf[1] = str[1]; diff --git a/lib/libqof/qof/qofinstance.c b/lib/libqof/qof/qofinstance.c index 0b3cf8230e..6e0fce5cd3 100644 --- a/lib/libqof/qof/qofinstance.c +++ b/lib/libqof/qof/qofinstance.c @@ -970,11 +970,11 @@ qof_commit_edit_part2(QofInstance *inst, /* 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); - } +// 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) { diff --git a/lib/libqof/qof/qofsession.c b/lib/libqof/qof/qofsession.c index 3309aeb4c9..5fa439c7d4 100644 --- a/lib/libqof/qof/qofsession.c +++ b/lib/libqof/qof/qofsession.c @@ -63,6 +63,20 @@ qof_backend_register_provider (QofBackendProvider *prov) provider_list = g_slist_append (provider_list, prov); } +GList* +qof_backend_get_registered_access_method_list(void) +{ + GList* list = NULL; + GSList* node; + + for( node = provider_list; node != NULL; node = node->next ) { + QofBackendProvider *prov = node->data; + list = g_list_append( list, (gchar*)prov->access_method ); + } + + return list; +} + /* ====================================================================== */ /* hook routines */ diff --git a/lib/libqof/qof/qofsession.h b/lib/libqof/qof/qofsession.h index 8f1f72af34..32f4e1b267 100644 --- a/lib/libqof/qof/qofsession.h +++ b/lib/libqof/qof/qofsession.h @@ -436,5 +436,10 @@ gboolean qof_session_export (QofSession *tmp_session, QofSession *real_session, QofPercentageFunc percentage_func); +/** Return a list of strings for the registered access methods. The owner is + * responsible for freeing the list but not the strings. + */ +GList* qof_backend_get_registered_access_method_list(void); + #endif /* QOF_SESSION_H */ /** @} */ diff --git a/packaging/win32/Makefile.am b/packaging/win32/Makefile.am index 443769de08..dfccd74cb7 100644 --- a/packaging/win32/Makefile.am +++ b/packaging/win32/Makefile.am @@ -9,6 +9,8 @@ EXTRA_DIST = \ install-fq-mods.bat \ libofx-0.8.3-patch.diff \ opensp-1.5.2-patch.diff \ + libgda-3.1.2-patch.diff \ + libgda-3.1.2-patch2.diff \ pi.sh \ Greek-4-5.1.11.isl \ reset.sh diff --git a/packaging/win32/defaults.sh b/packaging/win32/defaults.sh index fbfd776df6..4b6f3e136f 100644 --- a/packaging/win32/defaults.sh +++ b/packaging/win32/defaults.sh @@ -262,6 +262,11 @@ set_default AQBANKING_WITH_QT yes # If set to yes, download Qt from http://www.trolltech.com/developer/downloads/qt/windows, # install it and set QTDIR in custom.sh, like "QTDIR=/c/Qt/4.2.3". +set_default LIBGDA_URL "http://ftp.acc.umu.se/pub/GNOME/sources/libgda/3.1/libgda-3.1.2.tar.gz" +set_default LIBGDA_DIR $GLOBAL_DIR\\libgda +set_default LIBGDA_PATCH `pwd`/libgda-3.1.2-patch.diff +set_default LIBGDA_PATCH2 `pwd`/libgda-3.1.2-patch2.diff + set_default DOCBOOK_XSL_URL "$SF_MIRROR/docbook/docbook-xsl-1.72.0.zip" set_default UPDATE_DOCS yes set_default DOCS_REV "HEAD" @@ -305,6 +310,7 @@ add_step inst_libofx add_step inst_gwenhywfar add_step inst_ktoblzcheck add_step inst_aqbanking +add_step inst_libgda ## if [ "$CROSS_COMPILE" != "yes" ]; then add_step inst_inno diff --git a/packaging/win32/dist.sh b/packaging/win32/dist.sh index d56a833452..3c4828a18a 100755 --- a/packaging/win32/dist.sh +++ b/packaging/win32/dist.sh @@ -34,6 +34,7 @@ function prepare() { _LIBOFX_UDIR=`unix_path $LIBOFX_DIR` _GWENHYWFAR_UDIR=`unix_path $GWENHYWFAR_DIR` _AQBANKING_UDIR=`unix_path $AQBANKING_DIR` + _LIBGDA_UDIR=`unix_path $LIBGDA_DIR` _GNUCASH_UDIR=`unix_path $GNUCASH_DIR` _REPOS_UDIR=`unix_path $REPOS_DIR` _BUILD_UDIR=`unix_path $BUILD_DIR` @@ -171,6 +172,14 @@ function dist_aqbanking() { cp -a ${_AQBANKING_UDIR}/share/locale ${DIST_UDIR}/lib } +function dist_libgda() { + setup Libgda + cp -a ${_LIBGDA_UDIR}/bin/* ${DIST_UDIR}/bin + cp -a ${_LIBGDA_UDIR}/lib/libgda-3.0 ${DIST_UDIR}/lib + cp -a ${_LIBGDA_UDIR}/share/libgda-3.0 ${DIST_UDIR}/share + cp -a ${_LIBGDA_UDIR}/share/locale ${DIST_UDIR}/lib +} + function dist_gnucash() { setup GnuCash mkdir -p $DIST_UDIR/bin @@ -245,6 +254,7 @@ dist_goffice dist_libofx dist_gwenhywfar dist_aqbanking +dist_libgda dist_gnucash finish qpopd diff --git a/packaging/win32/install.sh b/packaging/win32/install.sh index ca2efcd83e..0dfd02b628 100755 --- a/packaging/win32/install.sh +++ b/packaging/win32/install.sh @@ -929,6 +929,33 @@ function inst_aqbanking() { fi } +function inst_libgda() { + setup LibGDA + _LIBGDA_UDIR=`unix_path ${LIBGDA_DIR}` + add_to_env ${_LIBGDA_UDIR}/bin PATH + add_to_env ${_LIBGDA_UDIR}/lib/pkgconfig PKG_CONFIG_PATH + if quiet ${PKG_CONFIG} --exists libgda-3.0 + then + echo "Libgda already installed. skipping." + else + wget_unpacked $LIBGDA_URL $DOWNLOAD_DIR $TMP_DIR + assert_one_dir $TMP_UDIR/libgda-* + qpushd $TMP_UDIR/libgda-* + #patch to ignore vfs, as libgda uses depriciated header + patch libgda/gda-data-model-dir.c $LIBGDA_PATCH + #patch to use g_setenv instead of setenv (bug #510739) + patch tools/gda-sql.c $LIBGDA_PATCH2 + ./configure \ + --prefix=${_LIBGDA_UDIR} \ + CPPFLAGS="${REGEX_CPPFLAGS} ${GNOME_CPPFLAGS}" \ + LDFLAGS="${REGEX_LDFLAGS} ${GNOME_LDFLAGS} -lintl" + make + make install + qpopd + ${PKG_CONFIG} --exists libgda-3.0 || die "Libgda not installed correctly" + fi +} + function svn_up() { mkdir -p $_REPOS_UDIR qpushd $REPOS_DIR @@ -992,7 +1019,7 @@ function inst_gnucash() { qpushd src/bin rm gnucash make PATH_SEPARATOR=";" \ - bindir="${_INSTALL_UDIR}/bin:${_INSTALL_UDIR}/lib:${_INSTALL_UDIR}/lib/gnucash:${_GOFFICE_UDIR}/bin:${_LIBGSF_UDIR}/bin:${_PCRE_UDIR}/bin:${_GNOME_UDIR}/bin:${_LIBXML2_UDIR}/bin:${_GUILE_UDIR}/bin:${_REGEX_UDIR}/bin:${_AUTOTOOLS_UDIR}/bin:${AQBANKING_UPATH}:${_LIBOFX_UDIR}/bin:${_OPENSP_UDIR}/bin" \ + bindir="${_INSTALL_UDIR}/bin:${_INSTALL_UDIR}/lib:${_INSTALL_UDIR}/lib/gnucash:${_GOFFICE_UDIR}/bin:${_LIBGSF_UDIR}/bin:${_PCRE_UDIR}/bin:${_GNOME_UDIR}/bin:${_LIBXML2_UDIR}/bin:${_GUILE_UDIR}/bin:${_REGEX_UDIR}/bin:${_AUTOTOOLS_UDIR}/bin:${AQBANKING_UPATH}:${_LIBOFX_UDIR}/bin:${_OPENSP_UDIR}/bin:${LIBGDA_DIR}/bin" \ gnucash qpopd @@ -1035,7 +1062,7 @@ function make_install() { # Create a startup script that works without the msys shell qpushd $_INSTALL_UDIR/bin echo "setlocal" > gnucash.bat - echo "set PATH=${INSTALL_DIR}\\bin;${INSTALL_DIR}\\lib;${INSTALL_DIR}\\lib\\gnucash;${GOFFICE_DIR}\\bin;${LIBGSF_DIR}\\bin;${PCRE_DIR}\\bin;${GNOME_DIR}\\bin;${LIBXML2_DIR}\\bin;${GUILE_DIR}\\bin;${REGEX_DIR}\\bin;${AUTOTOOLS_DIR}\\bin;${AQBANKING_PATH};${LIBOFX_DIR}\\bin;${OPENSP_DIR}\\bin;%PATH%" >> gnucash.bat + echo "set PATH=${INSTALL_DIR}\\bin;${INSTALL_DIR}\\lib;${INSTALL_DIR}\\lib\\gnucash;${GOFFICE_DIR}\\bin;${LIBGSF_DIR}\\bin;${PCRE_DIR}\\bin;${GNOME_DIR}\\bin;${LIBXML2_DIR}\\bin;${GUILE_DIR}\\bin;${REGEX_DIR}\\bin;${AUTOTOOLS_DIR}\\bin;${AQBANKING_PATH};${LIBOFX_DIR}\\bin;${OPENSP_DIR}\\bin;${LIBGDA_DIR}\\bin;%PATH%" > gnucash.bat echo "set GUILE_WARN_DEPRECATED=no" >> gnucash.bat echo "set GNC_MODULE_PATH=${INSTALL_DIR}\\lib\\gnucash" >> gnucash.bat echo "set GUILE_LOAD_PATH=${INSTALL_DIR}\\share\\gnucash\\guile-modules;${INSTALL_DIR}\\share\\gnucash\\scm;%GUILE_LOAD_PATH%" >> gnucash.bat diff --git a/packaging/win32/libgda-3.1.2-patch.diff b/packaging/win32/libgda-3.1.2-patch.diff new file mode 100644 index 0000000000..5041be4f8c --- /dev/null +++ b/packaging/win32/libgda-3.1.2-patch.diff @@ -0,0 +1,4 @@ +31c31 +< #include +--- +> #undef HAVE_GNOMEVFS diff --git a/packaging/win32/libgda-3.1.2-patch2.diff b/packaging/win32/libgda-3.1.2-patch2.diff new file mode 100644 index 0000000000..1158b0420e --- /dev/null +++ b/packaging/win32/libgda-3.1.2-patch2.diff @@ -0,0 +1,6 @@ +880,881c880,881 +< setenv ("GDA_DATA_MODEL_DUMP_TITLE", "Yes", TRUE); +< setenv ("GDA_DATA_MODEL_NULL_AS_EMPTY", "Yes", TRUE); +--- +> g_setenv ("GDA_DATA_MODEL_DUMP_TITLE", "Yes", TRUE); +> g_setenv ("GDA_DATA_MODEL_NULL_AS_EMPTY", "Yes", TRUE); diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index f523c5e14d..db9a5a9605 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -1,3 +1,3 @@ -SUBDIRS = file ${SQL_DIR} -DIST_SUBDIRS = file postgres +SUBDIRS = file sql ${SQL_DIR} ${GDA_DIR} ${DBI_DIR} +DIST_SUBDIRS = file sql ${SQL_DIR} ${GDA_DIR} ${DBI_DIR} diff --git a/src/backend/dbi/Makefile.am b/src/backend/dbi/Makefile.am new file mode 100644 index 0000000000..4de15295c5 --- /dev/null +++ b/src/backend/dbi/Makefile.am @@ -0,0 +1,34 @@ +SUBDIRS = . test + +# Now a shared library AND a GModule +pkglib_LTLIBRARIES = libgncmod-backend-dbi.la + + +AM_CFLAGS = \ + -I.. -I../.. \ + -DLOCALE_DIR=\""$(datadir)/locale"\" \ + -I${top_srcdir}/src/backend \ + -I${top_srcdir}/src/backend/sql \ + -I${top_srcdir}/src/engine \ + -I${top_srcdir}/src/core-utils\ + -I${top_srcdir}/lib/libc\ + ${QOF_CFLAGS} \ + ${GLIB_CFLAGS} \ + ${GCONF_CFLAGS} \ + ${WARN_CFLAGS} + +libgncmod_backend_dbi_la_SOURCES = \ + gnc-backend-dbi.c + +noinst_HEADERS = \ + gnc-backend-dbi.h + +libgncmod_backend_dbi_la_LDFLAGS = -module -avoid-version +libgncmod_backend_dbi_la_LIBADD = \ + ${GLIB_LIBS} ${GCONF_LIBS} \ + ${top_builddir}/src/backend/sql/libgnc-backend-sql.la \ + ${top_builddir}/src/engine/libgncmod-engine.la \ + ${LIBDBI_LIBS} \ + ${QOF_LIBS} + +INCLUDES = -DG_LOG_DOMAIN=\"gnc.backend.dbi\" diff --git a/src/backend/dbi/gnc-backend-dbi.c b/src/backend/dbi/gnc-backend-dbi.c new file mode 100644 index 0000000000..30ef421f8c --- /dev/null +++ b/src/backend/dbi/gnc-backend-dbi.c @@ -0,0 +1,1168 @@ +/******************************************************************** + * gnc-backend-dbi.c: load and save data to SQL via libdbi * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ +/** @file gnc-backend-dbi.c + * @brief load and save data to SQL + * @author Copyright (c) 2006-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL db using libdbi + */ + +#include "config.h" + +#include +#include +#include +#include + +#include + +#include "gnc-backend-sql.h" + +#include "qof.h" +#include "qofquery-p.h" +#include "qofquerycore-p.h" +#include "Account.h" +#include "TransLog.h" +#include "gnc-engine.h" +#include "SX-book.h" +#include "Recurrence.h" + +#include "gnc-gconf-utils.h" + +#include "gnc-backend-dbi.h" + +#define TRANSACTION_NAME "trans" + +static QofLogModule log_module = G_LOG_DOMAIN; + +static GncSqlConnection* create_dbi_connection( gint provider, dbi_conn conn ); + +#define GNC_DBI_PROVIDER_SQLITE 0 +#define GNC_DBI_PROVIDER_MYSQL 1 +#define GNC_DBI_PROVIDER_PGSQL 2 + +struct GncDbiBackend_struct +{ + GncSqlBackend sql_be; + + dbi_conn conn; + + QofBook *primary_book; /* The primary, main open book */ + gboolean loading; /* We are performing an initial load */ + gboolean in_query; + gboolean supports_transactions; + gboolean is_pristine_db; // Are we saving to a new pristine db? + + gint obj_total; // Total # of objects (for percentage calculation) + gint operations_done; // Number of operations (save/load) done +// GHashTable* versions; // Version number for each table +}; +typedef struct GncDbiBackend_struct GncDbiBackend; + +/* ================================================================= */ + +static void +create_tables_cb( const gchar* type, gpointer data_p, gpointer be_p ) +{ + GncSqlObjectBackend* pData = data_p; + GncDbiBackend* be = be_p; + + g_return_if_fail( type != NULL && data_p != NULL && be_p != NULL ); + g_return_if_fail( pData->version == GNC_SQL_BACKEND_VERSION ); + + if( pData->create_tables != NULL ) { + (pData->create_tables)( &be->sql_be ); + } +} + +static void +error_fn( dbi_conn conn, void* user_data ) +{ + GncDbiBackend *be = (GncDbiBackend*)user_data; + const gchar* msg; + + dbi_conn_error( conn, &msg ); + PERR( "DBI error: %s\n", msg ); +} + +static void +gnc_dbi_sqlite3_session_begin( QofBackend *qbe, QofSession *session, + const gchar *book_id, + gboolean ignore_lock, + gboolean create_if_nonexistent ) +{ + GncDbiBackend *be = (GncDbiBackend*)qbe; + gint result; + gchar* dirname; + gchar* basename; + + g_return_if_fail( qbe != NULL ); + g_return_if_fail( session != NULL ); + g_return_if_fail( book_id != NULL ); + + ENTER (" "); + + dirname = g_path_get_dirname( book_id ); + basename = g_path_get_basename( book_id ); + + be->conn = dbi_conn_new( "sqlite3" ); + if( be->conn == NULL ) { + PERR( "Unable to create sqlite3 dbi connection\n" ); + qof_backend_set_error( qbe, ERR_BACKEND_BAD_URL ); + LEAVE( " " ); + return; + } + dbi_conn_error_handler( be->conn, error_fn, be ); + dbi_conn_set_option( be->conn, "host", "localhost" ); + dbi_conn_set_option( be->conn, "dbname", basename ); + dbi_conn_set_option( be->conn, "sqlite3_dbdir", dirname ); + result = dbi_conn_connect( be->conn ); + g_free( basename ); + g_free( dirname ); + if( result < 0 ) { + PERR( "Unable to connect to %s: %d\n", book_id, result ); + qof_backend_set_error( qbe, ERR_BACKEND_BAD_URL ); + LEAVE( " " ); + return; + } + + be->sql_be.conn = create_dbi_connection( GNC_DBI_PROVIDER_SQLITE, be->conn ); + + LEAVE (" "); +} + +static void +gnc_dbi_mysql_session_begin( QofBackend *qbe, QofSession *session, + const gchar *book_id, + gboolean ignore_lock, + gboolean create_if_nonexistent ) +{ + GncDbiBackend *be = (GncDbiBackend*)qbe; + gchar* dsn; + gchar* host; + gchar* dbname; + gchar* username; + gchar* password; + gint result; + + g_return_if_fail( qbe != NULL ); + g_return_if_fail( session != NULL ); + g_return_if_fail( book_id != NULL ); + + ENTER (" "); + + /* Split the book-id (format host:dbname:username:password) */ + dsn = g_strdup( book_id ); + for( host = dsn; *host != '/'; host++ ) {} + host += 2; + for( dbname = host; *dbname != ':'; dbname++ ) {} + *dbname++ = '\0'; + for( username = dbname; *username != ':'; username++ ) {} + *username++ = '\0'; + for( password = username; *password != ':'; password++ ) {} + *password++ = '\0'; + + be->conn = dbi_conn_new( "mysql" ); + if( be->conn == NULL ) { + PERR( "Unable to create mysql dbi connection\n" ); + qof_backend_set_error( qbe, ERR_BACKEND_BAD_URL ); + LEAVE( " " ); + return; + } + dbi_conn_error_handler( be->conn, error_fn, be ); + dbi_conn_set_option( be->conn, "host", host ); + dbi_conn_set_option_numeric( be->conn, "port", 0 ); + dbi_conn_set_option( be->conn, "dbname", dbname ); + dbi_conn_set_option( be->conn, "username", username ); + dbi_conn_set_option( be->conn, "password", password ); + result = dbi_conn_connect( be->conn ); + g_free( dsn ); + if( result < 0 ) { + PERR( "Unable to connect to %s: %d\n", book_id, result ); + qof_backend_set_error( qbe, ERR_BACKEND_BAD_URL ); + LEAVE( " " ); + return; + } + + be->sql_be.conn = create_dbi_connection( GNC_DBI_PROVIDER_MYSQL, be->conn ); + + LEAVE (" "); +} + +static void +gnc_dbi_postgres_session_begin( QofBackend *qbe, QofSession *session, + const gchar *book_id, + gboolean ignore_lock, + gboolean create_if_nonexistent ) +{ + GncDbiBackend *be = (GncDbiBackend*)qbe; + gint result; + gchar* dsn; + gchar* host; + gchar* dbname; + gchar* username; + gchar* password; + + g_return_if_fail( qbe != NULL ); + g_return_if_fail( session != NULL ); + g_return_if_fail( book_id != NULL ); + + ENTER (" "); + + /* Split the book-id (format host:dbname:username:password) */ + dsn = g_strdup( book_id ); + for( host = dsn; *host != '/'; host++ ) {} + host += 2; + for( dbname = host; *dbname != ':'; dbname++ ) {} + *dbname++ = '\0'; + for( username = dbname; *username != ':'; username++ ) {} + *username++ = '\0'; + for( password = username; *password != ':'; password++ ) {} + *password++ = '\0'; + + be->conn = dbi_conn_new( "pgsql" ); + if( be->conn == NULL ) { + PERR( "Unable to create pgsql dbi connection\n" ); + qof_backend_set_error( qbe, ERR_BACKEND_BAD_URL ); + LEAVE( " " ); + return; + } + dbi_conn_error_handler( be->conn, error_fn, be ); + dbi_conn_set_option( be->conn, "host", host ); + dbi_conn_set_option_numeric( be->conn, "port", 0 ); + dbi_conn_set_option( be->conn, "dbname", dbname ); + dbi_conn_set_option( be->conn, "username", username ); + dbi_conn_set_option( be->conn, "password", password ); + result = dbi_conn_connect( be->conn ); + g_free( dsn ); + if( result < 0 ) { + PERR( "Unable to connect to %s: %d\n", book_id, result ); + qof_backend_set_error( qbe, ERR_BACKEND_BAD_URL ); + LEAVE( " " ); + return; + } + + be->sql_be.conn = create_dbi_connection( GNC_DBI_PROVIDER_PGSQL, be->conn ); + + LEAVE (" "); +} + +/* ================================================================= */ + +static void +gnc_dbi_session_end( QofBackend *be_start ) +{ + GncDbiBackend *be = (GncDbiBackend*)be_start; + + g_return_if_fail( be_start != NULL ); + + ENTER (" "); + + dbi_conn_close( be->conn ); + gnc_sql_finalize_version_info( &be->sql_be ); + + LEAVE (" "); +} + +static void +gnc_dbi_destroy_backend( QofBackend *be ) +{ + g_return_if_fail( be != NULL ); + + g_free( be ); +} + +/* ================================================================= */ + +static void +gnc_dbi_load( QofBackend* qbe, QofBook *book ) +{ + GncDbiBackend *be = (GncDbiBackend*)qbe; + GncSqlObjectBackend* pData; + int i; + Account* root; + + g_return_if_fail( qbe != NULL ); + g_return_if_fail( book != NULL ); + + ENTER( "be=%p, book=%p", be, book ); + + g_assert( be->primary_book == NULL ); + be->primary_book = book; + + // Set up table version information + gnc_sql_init_version_info( &be->sql_be ); + + // Call all object backends to create any required tables + qof_object_foreach_backend( GNC_SQL_BACKEND, create_tables_cb, be ); + + gnc_sql_load( &be->sql_be, book ); + + LEAVE( "" ); +} + +/* ================================================================= */ + +static gboolean +gnc_dbi_save_may_clobber_data( QofBackend* qbe ) +{ + GncDbiBackend* be = (GncDbiBackend*)qbe; + const gchar* dbname; + dbi_result tables; + gint numTables; + + /* Data may be clobbered iff the number of tables != 0 */ + dbname = dbi_conn_get_option( be->conn, "dbname" ); + tables = dbi_conn_get_table_list( be->conn, dbname, NULL ); + numTables = dbi_result_get_numrows( tables ); + dbi_result_free( tables ); + + return (numTables != 0); +} + +static void +gnc_dbi_sync_all( QofBackend* qbe, QofBook *book ) +{ + GncDbiBackend* be = (GncDbiBackend*)qbe; + dbi_result tables; + GError* error = NULL; + gint row; + gint numTables; + gboolean status; + const gchar* dbname; + + g_return_if_fail( be != NULL ); + g_return_if_fail( book != NULL ); + + ENTER( "book=%p, primary=%p", book, be->primary_book ); + + /* Destroy the current contents of the database */ + dbname = dbi_conn_get_option( be->conn, "dbname" ); + tables = dbi_conn_get_table_list( be->conn, dbname, NULL ); + while( dbi_result_next_row( tables ) ) { + const gchar* table_name; + dbi_result result; + + table_name = dbi_result_get_string_idx( tables, 1 ); + result = dbi_conn_queryf( be->conn, "DROP TABLE %s", table_name ); + dbi_result_free( result ); + } + dbi_result_free( tables ); + + /* Save all contents */ + be->is_pristine_db = TRUE; + be->primary_book = book; + gnc_sql_sync_all( &be->sql_be, book ); + + LEAVE( "book=%p", book ); +} + +/* ================================================================= */ +static void +gnc_dbi_begin_edit( QofBackend *qbe, QofInstance *inst ) +{ + GncDbiBackend* be = (GncDbiBackend*)qbe; + + g_return_if_fail( be != NULL ); + g_return_if_fail( inst != NULL ); + + gnc_sql_begin_edit( &be->sql_be, inst ); +} + +static void +gnc_dbi_rollback_edit( QofBackend *qbe, QofInstance *inst ) +{ + GncDbiBackend* be = (GncDbiBackend*)qbe; + + g_return_if_fail( be != NULL ); + g_return_if_fail( inst != NULL ); + + gnc_sql_rollback_edit( &be->sql_be, inst ); +} + +static void +gnc_dbi_commit_edit( QofBackend *qbe, QofInstance *inst ) +{ + GncDbiBackend* be = (GncDbiBackend*)qbe; + + g_return_if_fail( be != NULL ); + g_return_if_fail( inst != NULL ); + + gnc_sql_commit_edit( &be->sql_be, inst ); +} + +/* ================================================================= */ + +static void +init_sql_backend( GncDbiBackend* dbi_be ) +{ + static gboolean initialized = FALSE; + QofBackend* be; + + be = (QofBackend*)dbi_be; + + be->session_end = gnc_dbi_session_end; + be->destroy_backend = gnc_dbi_destroy_backend; + + be->load = gnc_dbi_load; + be->save_may_clobber_data = gnc_dbi_save_may_clobber_data; + + /* The gda backend treats accounting periods transactionally. */ + be->begin = gnc_dbi_begin_edit; + be->commit = gnc_dbi_commit_edit; + be->rollback = gnc_dbi_rollback_edit; + + be->counter = NULL; + + /* The gda backend will not be multi-user (for now)... */ + be->events_pending = NULL; + be->process_events = NULL; + + be->sync = gnc_dbi_sync_all; + be->load_config = NULL; + be->get_config = NULL; + + be->export = NULL; + + if( !initialized ) { +#define DEFAULT_DBD_DIR "/usr/lib/dbd" + const gchar* driver_dir; + int num_drivers; + + driver_dir = g_getenv( "GNC_DBD_DIR" ); + if( driver_dir == NULL ) { + PWARN( "GNC_DBD_DIR not set: using %s\n", DEFAULT_DBD_DIR ); + driver_dir = DEFAULT_DBD_DIR; + } + + num_drivers = dbi_initialize( driver_dir ); + if( num_drivers == 0 ) { + PWARN( "No DBD drivers found\n" ); + } else { + dbi_driver driver = NULL; + PINFO( "%d DBD drivers found\n", num_drivers ); + + do { + driver = dbi_driver_list( driver ); + if( driver != NULL ) { + PINFO( "Driver: %s\n", dbi_driver_get_name( driver ) ); + } + } while( driver != NULL ); + } + gnc_sql_init( &dbi_be->sql_be ); + initialized = TRUE; + } +} + +static QofBackend* +gnc_dbi_backend_sqlite3_new( void ) +{ + GncDbiBackend *dbi_be; + QofBackend *be; + + dbi_be = g_new0( GncDbiBackend, 1 ); + be = (QofBackend*)dbi_be; + qof_backend_init( be ); + + be->session_begin = gnc_dbi_sqlite3_session_begin; + init_sql_backend( dbi_be ); + + return be; +} + +static QofBackend* +gnc_dbi_backend_mysql_new( void ) +{ + GncDbiBackend *dbi_be; + QofBackend *be; + + dbi_be = g_new0( GncDbiBackend, 1 ); + be = (QofBackend*)dbi_be; + qof_backend_init( be ); + + be->session_begin = gnc_dbi_mysql_session_begin; + init_sql_backend( dbi_be ); + + return be; +} + +static QofBackend* +gnc_dbi_backend_postgres_new( void ) +{ + GncDbiBackend *dbi_be; + QofBackend *be; + + dbi_be = g_new0( GncDbiBackend, 1 ); + be = (QofBackend*)dbi_be; + qof_backend_init( be ); + + be->session_begin = gnc_dbi_postgres_session_begin; + init_sql_backend( dbi_be ); + + return be; +} + +static void +gnc_dbi_provider_free( QofBackendProvider *prov ) +{ + g_return_if_fail( prov != NULL ); + + prov->provider_name = NULL; + prov->access_method = NULL; + g_free (prov); +} + +/* + * Checks to see whether the file is an sqlite file or not + * + */ +static gboolean +gnc_dbi_check_sqlite3_file( const gchar *path ) +{ + FILE* f; + gchar buf[50]; + gint chars_read; + + // BAD if the path is null + g_return_val_if_fail( path != NULL, FALSE ); + + f = g_fopen( path, "r" ); + + // OK if the file doesn't exist - new file + if( f == NULL ) { + PINFO( "doesn't exist (errno=%d) -> DBI", errno ); + return TRUE; + } + + // OK if file has the correct header + chars_read = fread( buf, sizeof(buf), 1, f ); + fclose( f ); + if( g_str_has_prefix( buf, "SQLite format 3" ) ) { + PINFO( "has SQLite format string -> DBI" ); + return TRUE; + } + PINFO( "exists, does not have SQLite format string -> not DBI" ); + + // Otherwise, BAD + return FALSE; +} + +G_MODULE_EXPORT void +qof_backend_module_init(void) +{ + QofBackendProvider *prov; + + prov = g_new0 (QofBackendProvider, 1); + prov->provider_name = "GnuCash Libdbi (SQLITE3) Backend"; + prov->access_method = "file"; + prov->partial_book_supported = FALSE; + prov->backend_new = gnc_dbi_backend_sqlite3_new; + prov->provider_free = gnc_dbi_provider_free; + prov->check_data_type = gnc_dbi_check_sqlite3_file; + qof_backend_register_provider( prov ); + + prov = g_new0 (QofBackendProvider, 1); + prov->provider_name = "GnuCash Libdbi (MYSQL) Backend"; + prov->access_method = "mysql"; + prov->partial_book_supported = FALSE; + prov->backend_new = gnc_dbi_backend_mysql_new; + prov->provider_free = gnc_dbi_provider_free; + qof_backend_register_provider( prov ); + + prov = g_new0 (QofBackendProvider, 1); + prov->provider_name = "GnuCash Libdbi (POSTGRESQL) Backend"; + prov->access_method = "postgres"; + prov->partial_book_supported = FALSE; + prov->backend_new = gnc_dbi_backend_postgres_new; + prov->provider_free = gnc_dbi_provider_free; + qof_backend_register_provider( prov ); +} + +/* --------------------------------------------------------- */ +typedef struct +{ + GncSqlRow base; + + dbi_result result; + GList* gvalue_list; +} GncDbiSqlRow; + +static void +row_dispose( GncSqlRow* row ) +{ + GncDbiSqlRow* dbi_row = (GncDbiSqlRow*)row; + GList* node; + + if( dbi_row->gvalue_list != NULL ) { + for( node = dbi_row->gvalue_list; node != NULL; node = node->next ) { + GValue* value = (GValue*)node->data; + if( G_VALUE_HOLDS_STRING(value) ) { + g_free( (gpointer)g_value_get_string( value ) ); + } + g_free( value ); + } + g_list_free( dbi_row->gvalue_list ); + } + g_free( dbi_row ); +} + +static const GValue* +row_get_value_at_col_name( GncSqlRow* row, const gchar* col_name ) +{ + GncDbiSqlRow* dbi_row = (GncDbiSqlRow*)row; + gushort type; + GValue* value; + + type = dbi_result_get_field_type( dbi_row->result, col_name ); + value = g_new0( GValue, 1 ); + switch( type ) { + case DBI_TYPE_INTEGER: + g_value_init( value, G_TYPE_INT ); + g_value_set_int( value, dbi_result_get_int( dbi_row->result, col_name ) ); + break; + case DBI_TYPE_DECIMAL: + g_value_init( value, G_TYPE_DOUBLE ); + g_value_set_double( value, dbi_result_get_double( dbi_row->result, col_name ) ); + break; + case DBI_TYPE_STRING: + g_value_init( value, G_TYPE_STRING ); + g_value_take_string( value, dbi_result_get_string_copy( dbi_row->result, col_name ) ); + break; + default: + PERR( "Unknown DBI_TYPE: %d\n", type ); + g_free( value ); + return NULL; + } + + dbi_row->gvalue_list = g_list_prepend( dbi_row->gvalue_list, value ); + return value; +} + +static GncSqlRow* +create_dbi_row( dbi_result result ) +{ + GncDbiSqlRow* row; + + row = g_new0( GncDbiSqlRow, 1 ); + row->base.getValueAtColName = row_get_value_at_col_name; + row->base.dispose = row_dispose; + row->result = result; + + return (GncSqlRow*)row; +} +/* --------------------------------------------------------- */ +typedef struct +{ + GncSqlResult base; + + dbi_result result; + gint num_rows; + gint cur_row; + GncSqlRow* row; +} GncDbiSqlResult; + +static void +result_dispose( GncSqlResult* result ) +{ + GncDbiSqlResult* dbi_result = (GncDbiSqlResult*)result; + + if( dbi_result->row != NULL ) { + gnc_sql_row_dispose( dbi_result->row ); + } + if( dbi_result->result != NULL ) { + dbi_result_free( dbi_result->result ); + } + g_free( result ); +} + +static gint +result_get_num_rows( GncSqlResult* result ) +{ + GncDbiSqlResult* dbi_result = (GncDbiSqlResult*)result; + + return dbi_result->num_rows; +} + +static GncSqlRow* +result_get_first_row( GncSqlResult* result ) +{ + GncDbiSqlResult* dbi_result = (GncDbiSqlResult*)result; + + if( dbi_result->row != NULL ) { + gnc_sql_row_dispose( dbi_result->row ); + dbi_result->row = NULL; + } + if( dbi_result->num_rows > 0 ) { + dbi_result_first_row( dbi_result->result ); + dbi_result->cur_row = 1; + dbi_result->row = create_dbi_row( dbi_result->result ); + return dbi_result->row; + } else { + return NULL; + } +} + +static GncSqlRow* +result_get_next_row( GncSqlResult* result ) +{ + GncDbiSqlResult* dbi_result = (GncDbiSqlResult*)result; + + if( dbi_result->row != NULL ) { + gnc_sql_row_dispose( dbi_result->row ); + dbi_result->row = NULL; + } + if( dbi_result->cur_row < dbi_result->num_rows ) { + dbi_result_next_row( dbi_result->result ); + dbi_result->cur_row++; + dbi_result->row = create_dbi_row( dbi_result->result ); + return dbi_result->row; + } else { + return NULL; + } +} + +static GncSqlResult* +create_dbi_result( dbi_result result ) +{ + GncDbiSqlResult* dbi_result; + + dbi_result = g_new0( GncDbiSqlResult, 1 ); + dbi_result->base.dispose = result_dispose; + dbi_result->base.getNumRows = result_get_num_rows; + dbi_result->base.getFirstRow = result_get_first_row; + dbi_result->base.getNextRow = result_get_next_row; + dbi_result->result = result; + dbi_result->num_rows = dbi_result_get_numrows( result ); + dbi_result->cur_row = 0; + + return (GncSqlResult*)dbi_result; +} +/* --------------------------------------------------------- */ +typedef struct +{ + GncSqlStatement base; + + GString* sql; + GncSqlConnection* conn; +} GncDbiSqlStatement; + +static void +stmt_dispose( GncSqlStatement* stmt ) +{ + GncDbiSqlStatement* dbi_stmt = (GncDbiSqlStatement*)stmt; + + if( dbi_stmt->sql != NULL ) { + g_string_free( dbi_stmt->sql, TRUE ); + } + g_free( stmt ); +} + +static gchar* +stmt_to_sql( GncSqlStatement* stmt ) +{ + GncDbiSqlStatement* dbi_stmt = (GncDbiSqlStatement*)stmt; + + return dbi_stmt->sql->str; +} + +static void +stmt_add_where_cond( GncSqlStatement* stmt, QofIdTypeConst type_name, + gpointer obj, const GncSqlColumnTableEntry* table_row, GValue* value ) +{ + GncDbiSqlStatement* dbi_stmt = (GncDbiSqlStatement*)stmt; + gchar* buf; + gchar* value_str; + + value_str = gnc_sql_get_sql_value( dbi_stmt->conn, value ); + buf = g_strdup_printf( " WHERE %s = %s", table_row->col_name, + value_str ); + g_free( value_str ); + g_string_append( dbi_stmt->sql, buf ); + g_free( buf ); +} + +static GncSqlStatement* +create_dbi_statement( GncSqlConnection* conn, gchar* sql ) +{ + GncDbiSqlStatement* stmt; + + stmt = g_new0( GncDbiSqlStatement, 1 ); + stmt->base.dispose = stmt_dispose; + stmt->base.toSql = stmt_to_sql; + stmt->base.addWhereCond = stmt_add_where_cond; + stmt->sql = g_string_new( sql ); + g_free( sql ); + stmt->conn = conn; + + return (GncSqlStatement*)stmt; +} +/* --------------------------------------------------------- */ +typedef struct +{ + GncSqlConnection base; + + dbi_conn conn; + gint provider; +} GncDbiSqlConnection; + +static void +conn_dispose( GncSqlConnection* conn ) +{ + GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn; + + g_free( conn ); +} + +static GncSqlResult* +conn_execute_select_statement( GncSqlConnection* conn, GncSqlStatement* stmt ) +{ + GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn; + GncDbiSqlStatement* dbi_stmt = (GncDbiSqlStatement*)stmt; + dbi_result result; + + result = dbi_conn_query( dbi_conn->conn, dbi_stmt->sql->str ); + if( result == NULL ) { + PERR( "Error executing SQL %s\n", dbi_stmt->sql->str ); + return NULL; + } + DEBUG( "SQL: %s\n", dbi_stmt->sql->str ); + return create_dbi_result( result ); +} + +static gint +conn_execute_nonselect_statement( GncSqlConnection* conn, GncSqlStatement* stmt ) +{ + GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn; + GncDbiSqlStatement* dbi_stmt = (GncDbiSqlStatement*)stmt; + dbi_result result; + gint num_rows; + + result = dbi_conn_query( dbi_conn->conn, dbi_stmt->sql->str ); + if( result == NULL ) { + PERR( "Error executing SQL %s\n", dbi_stmt->sql->str ); + return 0; + } + DEBUG( "SQL: %s\n", dbi_stmt->sql->str ); + num_rows = dbi_result_get_numrows_affected( result ); + dbi_result_free( result ); + return num_rows; +} + +static GncSqlStatement* +conn_create_statement_from_sql( GncSqlConnection* conn, gchar* sql ) +{ + GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn; + + return create_dbi_statement( conn, sql ); +} + +static GValue* +create_gvalue_from_string( gchar* s ) +{ + GValue* s_gval; + + s_gval = g_new0( GValue, 1 ); + g_value_init( s_gval, G_TYPE_STRING ); + g_value_take_string( s_gval, s ); + + return s_gval; +} + +static gboolean +conn_does_table_exist( GncSqlConnection* conn, const gchar* table_name ) +{ + GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn; + gint nTables; + dbi_result tables; + const gchar* dbname; + + g_return_val_if_fail( conn != NULL, FALSE ); + g_return_val_if_fail( table_name != NULL, FALSE ); + + dbname = dbi_conn_get_option( dbi_conn->conn, "dbname" ); + tables = dbi_conn_get_table_list( dbi_conn->conn, dbname, table_name ); + nTables = dbi_result_get_numrows( tables ); + dbi_result_free( tables ); + + if( nTables == 1 ) { + return TRUE; + } else { + return FALSE; + } +} + +static void +conn_begin_transaction( GncSqlConnection* conn ) +{ + GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn; +} + +static void +conn_rollback_transaction( GncSqlConnection* conn ) +{ + GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn; +} + +static void +conn_commit_transaction( GncSqlConnection* conn ) +{ + GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn; +} + +static const gchar* +conn_get_column_type_name( GncSqlConnection* conn, GType type, gint size ) +{ + GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn; + + if( dbi_conn->provider == GNC_DBI_PROVIDER_SQLITE ) { + switch( type ) { + case G_TYPE_INT: + case G_TYPE_INT64: + return "integer"; + break; + case G_TYPE_DOUBLE: + return "real"; + break; + case G_TYPE_STRING: + return "text"; + break; + default: + PERR( "Unknown GType: %s\n", g_type_name( type ) ); + return ""; + } + } else if( dbi_conn->provider == GNC_DBI_PROVIDER_MYSQL ) { + switch( type ) { + case G_TYPE_INT: + case G_TYPE_INT64: + return "integer"; + break; + case G_TYPE_DOUBLE: + return "double"; + break; + case G_TYPE_STRING: + return "varchar"; + break; + default: + PERR( "Unknown GType: %s\n", g_type_name( type ) ); + return ""; + } + } else if( dbi_conn->provider == GNC_DBI_PROVIDER_PGSQL ) { + switch( type ) { + case G_TYPE_INT: + case G_TYPE_INT64: + return "integer"; + break; + case G_TYPE_DOUBLE: + return "double precision"; + break; + case G_TYPE_STRING: + return "varchar"; + break; + default: + PERR( "Unknown GType: %s\n", g_type_name( type ) ); + return ""; + } + } else { + PERR( "Unknown provider: %d\n", dbi_conn->provider ); + return ""; + } +} + +static void +add_table_column( GString* ddl, const GncSqlColumnInfo* info ) +{ + gchar* buf; + GError* error = NULL; + gboolean ok; + + g_return_if_fail( ddl != NULL ); + g_return_if_fail( info != NULL ); + + g_string_append_printf( ddl, "%s %s", info->name, info->type_name ); + if( info->size != 0 ) { + g_string_append_printf( ddl, "(%d)", info->size ); + } + if( info->is_primary_key ) { + g_string_append( ddl, " PRIMARY KEY" ); + } + if( !info->null_allowed ) { + g_string_append( ddl, " NOT NULL" ); + } +} + +static void +conn_create_table( GncSqlConnection* conn, const gchar* table_name, + const GList* col_info_list ) +{ + GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn; + GString* ddl; + const GList* list_node; + guint col_num; + dbi_result result; + + g_return_if_fail( conn != NULL ); + g_return_if_fail( table_name != NULL ); + g_return_if_fail( col_info_list != NULL ); + + ddl = g_string_new( "" ); + g_string_printf( ddl, "CREATE TABLE %s (", table_name ); + for( list_node = col_info_list, col_num = 0; list_node != NULL; + list_node = list_node->next, col_num++ ) { + GncSqlColumnInfo* info = (GncSqlColumnInfo*)(list_node->data); + + if( col_num != 0 ) { + g_string_append( ddl, ", " ); + } + add_table_column( ddl, info ); + } + g_string_append( ddl, ")" ); + + DEBUG( "SQL: %s\n", ddl->str ); + result = dbi_conn_query( dbi_conn->conn, ddl->str ); + dbi_result_free( result ); + g_string_free( ddl, TRUE ); +} + +static void +conn_create_index( GncSqlConnection* conn, const gchar* index_name, + const gchar* table_name, const GncSqlColumnTableEntry* col_table ) +{ +#if 0 + GdaServerOperation *op; + GdaServerProvider *server; + GdaConnection* cnn; + GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn; + GError* error = NULL; + + g_return_if_fail( conn != NULL ); + g_return_if_fail( index_name != NULL ); + g_return_if_fail( table_name != NULL ); + g_return_if_fail( col_table != NULL ); + + cnn = gda_conn->conn; + g_return_if_fail( cnn != NULL ); + g_return_if_fail( GDA_IS_CONNECTION(cnn) ); + g_return_if_fail( gda_connection_is_opened(cnn) ); + + server = gda_conn->server; + g_return_if_fail( server != NULL ); + + op = gda_server_provider_create_operation( server, cnn, + GDA_SERVER_OPERATION_CREATE_INDEX, NULL, &error ); + if( error != NULL ) { + PERR( "gda_server_provider_create_operation(): %s\n", error->message ); + } + if( op != NULL && GDA_IS_SERVER_OPERATION(op) ) { + gint col; + gboolean ok; + + ok = gda_server_operation_set_value_at( op, index_name, &error, + "/INDEX_DEF_P/INDEX_NAME" ); + if( error != NULL ) { + PERR( "set INDEX_NAME: %s\n", error->message ); + } + if( !ok ) return; + ok = gda_server_operation_set_value_at( op, "", &error, + "/INDEX_DEF_P/INDEX_TYPE" ); + if( error != NULL ) { + PERR( "set INDEX_TYPE: %s\n", error->message ); + } + if( !ok ) return; + ok = gda_server_operation_set_value_at( op, "TRUE", &error, + "/INDEX_DEF_P/INDEX_IFNOTEXISTS" ); + if( error != NULL ) { + PERR( "set INDEX_IFNOTEXISTS: %s\n", error->message ); + } + if( !ok ) return; + ok = gda_server_operation_set_value_at( op, table_name, &error, + "/INDEX_DEF_P/INDEX_ON_TABLE" ); + if( error != NULL ) { + PERR( "set INDEX_ON_TABLE: %s\n", error->message ); + } + if( !ok ) return; + + for( col = 0; col_table[col].col_name != NULL; col++ ) { + guint item; + + if( col != 0 ) { + item = gda_server_operation_add_item_to_sequence( op, "/INDEX_FIELDS_S" ); + g_assert( item == col ); + } + ok = gda_server_operation_set_value_at( op, col_table->col_name, &error, + "/INDEX_FIELDS_S/%d/INDEX_FIELD", col ); + if( error != NULL ) { + PERR( "set INDEX_FIELD %s: %s\n", col_table->col_name, error->message ); + } + if( !ok ) break; + } + + if( !gda_server_provider_perform_operation( server, cnn, op, &error ) ) { + g_object_unref( op ); + return; + } + + g_object_unref( op ); + } +#endif +} + +static gchar* +conn_quote_string( const GncSqlConnection* conn, gchar* unquoted_str ) +{ + GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn; + gchar* quoted_str; + gint size; + + size = dbi_conn_quote_string_copy( dbi_conn->conn, unquoted_str, + "ed_str ); + if( size != 0 ) { + return quoted_str; + } else { + return NULL; + } +} + +static GncSqlConnection* +create_dbi_connection( gint provider, dbi_conn conn ) +{ + GncDbiSqlConnection* dbi_conn; + + dbi_conn = g_new0( GncDbiSqlConnection, 1 ); + dbi_conn->base.dispose = conn_dispose; + dbi_conn->base.executeSelectStatement = conn_execute_select_statement; + dbi_conn->base.executeNonSelectStatement = conn_execute_nonselect_statement; + dbi_conn->base.createStatementFromSql = conn_create_statement_from_sql; + dbi_conn->base.doesTableExist = conn_does_table_exist; + dbi_conn->base.beginTransaction = conn_begin_transaction; + dbi_conn->base.rollbackTransaction = conn_rollback_transaction; + dbi_conn->base.commitTransaction = conn_commit_transaction; + dbi_conn->base.getColumnTypeName = conn_get_column_type_name; + dbi_conn->base.createTable = conn_create_table; + dbi_conn->base.createIndex = conn_create_index; + dbi_conn->base.quoteString = conn_quote_string; + dbi_conn->conn = conn; + dbi_conn->provider = provider; + + return (GncSqlConnection*)dbi_conn; +} + +/* ========================== END OF FILE ===================== */ diff --git a/src/backend/dbi/gnc-backend-dbi.h b/src/backend/dbi/gnc-backend-dbi.h new file mode 100644 index 0000000000..801ac22202 --- /dev/null +++ b/src/backend/dbi/gnc-backend-dbi.h @@ -0,0 +1,37 @@ +/******************************************************************** + * gnc-backend-dbi.h: load and save data to SQL via libdbi * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ +/** @file gnc-backend-dbi.h + * @brief load and save data to SQL via libdbi + * @author Copyright (c) 2006-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database via libdbi + */ + +#ifndef GNC_BACKEND_DBI_H_ +#define GNC_BACKEND_DBI_H_ + +#include + +G_MODULE_EXPORT void +qof_backend_module_init(void); + +#endif /* GNC_BACKEND_DBI_H_ */ diff --git a/src/backend/dbi/gncmod-backend-dbi.c b/src/backend/dbi/gncmod-backend-dbi.c new file mode 100644 index 0000000000..0f05aff916 --- /dev/null +++ b/src/backend/dbi/gncmod-backend-dbi.c @@ -0,0 +1,63 @@ +/********************************************************************* + * gncmod-backend-dbi.c + * module definition/initialization for the dbi backend module + * + * @author Copyright (c) 2006-2008 Phil Longstaff + *********************************************************************/ + +#include +#include +/* #include */ + +#include "gnc-module.h" +#include "gnc-module-api.h" + +/* version of the gnc module system interface we require */ +int gnc_module_system_interface = 0; + +/* module versioning uses libtool semantics. */ +int gnc_module_current = 0; +int gnc_module_revision = 0; +int gnc_module_age = 0; + +static GNCModule engine; + +gchar * +gnc_module_path(void) +{ + return g_strdup("gnucash/backend/dbi"); +} + +gchar * +gnc_module_description(void) +{ + return g_strdup("The DBI/SQL backend for GnuCash"); +} + +int +gnc_module_init(int refcount) +{ + engine = gnc_module_load("gnucash/engine", 0); + if(!engine) return FALSE; + + /* Need to initialize g-type engine for gconf */ + if (refcount == 0) { + g_type_init(); + } + + return TRUE; +} + +int +gnc_module_end(int refcount) +{ + int unload = TRUE; + + if (engine) + unload = gnc_module_unload(engine); + + if (refcount == 0) + engine = NULL; + + return unload; +} diff --git a/src/backend/dbi/test/Makefile.am b/src/backend/dbi/test/Makefile.am new file mode 100644 index 0000000000..5000c8251f --- /dev/null +++ b/src/backend/dbi/test/Makefile.am @@ -0,0 +1,50 @@ +SUBDIRS = . + +TESTS = \ + test-load-backend \ + test-dbi-account + +GNC_TEST_DEPS := \ + --gnc-module-dir ${top_builddir}/src/engine \ + --guile-load-dir ${top_builddir}/src/engine \ + --library-dir ${top_builddir}/lib/libqof/qof \ + --library-dir ${top_builddir}/src/core-utils \ + --library-dir ${top_builddir}/src/gnc-module \ + --library-dir ${top_builddir}/src/engine \ + --library-dir ${top_builddir}/src/backend/gda + +TESTS_ENVIRONMENT := \ + GNC_ACCOUNT_PATH=${top_srcdir}/accounts/C \ + SRCDIR=${srcdir} \ + $(shell ${top_srcdir}/src/gnc-test-env --no-exports ${GNC_TEST_DEPS}) + +check_PROGRAMS = \ + test-load-backend \ + test-dbi-account + +#noinst_HEADERS = test-file-stuff.h + +LDADD = ${top_builddir}/src/test-core/libgncmod-test.la \ + ${top_builddir}/src/gnc-module/libgnc-module.la \ + ${top_builddir}/src/engine/libgncmod-engine.la \ + ${top_builddir}/src/engine/test-core/libgncmod-test-engine.la \ + ${top_builddir}/src/core-utils/libgnc-core-utils.la \ + ${QOF_LIBS} \ + ${top_builddir}/lib/libc/libc-missing.la + +AM_CFLAGS = \ + -I${top_srcdir}/lib/libc \ + -I${top_srcdir}/src \ + -I${top_srcdir}/src/core-utils \ + -I${top_srcdir}/src/gnc-module \ + -I${top_srcdir}/src/test-core \ + -I${top_srcdir}/src/engine \ + -I${top_srcdir}/src/engine/test-core \ + -I${top_srcdir}/src/backend/gda \ + -I${top_srcdir}/src/backend/qsf \ + ${GLIB_CFLAGS} \ + ${QOF_CFLAGS} \ + ${GUILE_INCS} \ + ${GCONF_CFLAGS} + +INCLUDES = -DG_LOG_DOMAIN=\"gnc.backend.dbi\" diff --git a/src/backend/dbi/test/test-load-backend.c b/src/backend/dbi/test/test-load-backend.c new file mode 100644 index 0000000000..5a597e0333 --- /dev/null +++ b/src/backend/dbi/test/test-load-backend.c @@ -0,0 +1,45 @@ +/*************************************************************************** + * test-load-backend.c + * + * Replaces the guile version to test the GModule file backend loading. + * + * Sun Oct 9 18:58:47 2005 + * Copyright 2005 Neil Williams + * linux@codehelp.co.uk + ****************************************************************************/ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include "config.h" +#include "qof.h" +#include "cashobjects.h" +#include "test-stuff.h" + +#define GNC_LIB_NAME "gncmod-backend-dbi" + +int main (int argc, char ** argv) +{ + qof_init(); + cashobjects_register(); + do_test( + qof_load_backend_library ("../.libs/", GNC_LIB_NAME), + " loading gnc-backend-dbi GModule failed"); + print_test_results(); + qof_close(); + exit(get_rv()); +} diff --git a/src/backend/file/Makefile.am b/src/backend/file/Makefile.am index 5f206d9c6e..d2b7af325a 100644 --- a/src/backend/file/Makefile.am +++ b/src/backend/file/Makefile.am @@ -1,8 +1,8 @@ SUBDIRS = . test # Now a shared library AND a GModule -lib_LTLIBRARIES = libgnc-backend-file-utils.la -pkglib_LTLIBRARIES = libgncmod-backend-file.la +lib_LTLIBRARIES = libgnc-backend-xml-utils.la +pkglib_LTLIBRARIES = libgncmod-backend-xml.la AM_CFLAGS = \ -I.. -I../.. \ @@ -16,7 +16,7 @@ AM_CFLAGS = \ ${GLIB_CFLAGS} \ ${GCONF_CFLAGS} -libgnc_backend_file_utils_la_SOURCES = \ +libgnc_backend_xml_utils_la_SOURCES = \ gnc-account-xml-v2.c \ gnc-book-xml-v2.c \ gnc-budget-xml-v2.c \ @@ -39,7 +39,7 @@ libgnc_backend_file_utils_la_SOURCES = \ sixtp-utils.c \ sixtp.c -libgncmod_backend_file_la_SOURCES = \ +libgncmod_backend_xml_la_SOURCES = \ gnc-backend-file.c noinst_HEADERS = \ @@ -58,18 +58,18 @@ noinst_HEADERS = \ sixtp-stack.h \ sixtp-utils.h -libgnc_backend_file_utils_la_LIBADD = \ +libgnc_backend_xml_utils_la_LIBADD = \ ${GLIB_LIBS} ${GCONF_LIBS} ${LIBXML2_LIBS} \ ${top_builddir}/src/engine/libgncmod-engine.la \ ${top_builddir}/src/core-utils/libgnc-core-utils.la \ ${QOF_LIBS} -libgncmod_backend_file_la_LDFLAGS = -module -avoid-version -libgncmod_backend_file_la_LIBADD = \ +libgncmod_backend_xml_la_LDFLAGS = -module -avoid-version +libgncmod_backend_xml_la_LIBADD = \ ${GLIB_LIBS} ${GCONF_LIBS} ${LIBXML2_LIBS} \ ${top_builddir}/src/engine/libgncmod-engine.la \ ${top_builddir}/src/core-utils/libgnc-core-utils.la \ - libgnc-backend-file-utils.la \ + libgnc-backend-xml-utils.la \ ${QOF_LIBS} -INCLUDES = -DG_LOG_DOMAIN=\"gnc.backend.file\" +INCLUDES = -DG_LOG_DOMAIN=\"gnc.backend.xml\" diff --git a/src/backend/file/gnc-account-xml-v2.c b/src/backend/file/gnc-account-xml-v2.c index 5a2715c1c6..89b7fb90f7 100644 --- a/src/backend/file/gnc-account-xml-v2.c +++ b/src/backend/file/gnc-account-xml-v2.c @@ -219,7 +219,8 @@ account_commodity_handler (xmlNodePtr node, gpointer act_pdata) struct account_pdata *pdata = act_pdata; gnc_commodity *ref; - ref = dom_tree_to_commodity_ref_no_engine(node, pdata->book); +// ref = dom_tree_to_commodity_ref_no_engine(node, pdata->book); + ref = dom_tree_to_commodity_ref(node, pdata->book); xaccAccountSetCommodity(pdata->account, ref); return TRUE; diff --git a/src/backend/file/gnc-backend-file.c b/src/backend/file/gnc-backend-file.c index d1c8d57bdd..5db948de1d 100644 --- a/src/backend/file/gnc-backend-file.c +++ b/src/backend/file/gnc-backend-file.c @@ -1,5 +1,5 @@ /******************************************************************** - * gnc-backend-file.c: load and save data to files * + * gnc-backend-xml.c: load and save data to XML files * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * @@ -18,8 +18,8 @@ * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * * Boston, MA 02110-1301, USA gnu@gnu.org * \********************************************************************/ -/** @file gnc-backend-file.c - * @brief load and save data to files +/** @file gnc-backend-xml.c + * @brief load and save data to XML files * @author Copyright (c) 2000 Gnumatic Inc. * @author Copyright (c) 2002 Derek Atkins * @author Copyright (c) 2003 Linas Vepstas @@ -453,6 +453,12 @@ gnc_determine_file_type (const char *path) FILE *t; if (!path) { return FALSE; } + + // Since this can be called with "xml:" as a prefix, remove it if it exists + if( g_str_has_prefix( path, "xml:" ) ) { + path += 4; + } + if (0 == safe_strcmp(path, QOF_STDOUT)) { return FALSE; } t = g_fopen(path, "r"); if(!t) { PINFO (" new file"); return TRUE; } @@ -462,7 +468,7 @@ gnc_determine_file_type (const char *path) if (sbuf.st_size == 0) { PINFO (" empty file"); return TRUE; } if(gnc_is_xml_data_file_v2(path, NULL)) { return TRUE; } else if(gnc_is_xml_data_file(path)) { return TRUE; } - PINFO (" %s is not a gnc file", path); + PINFO (" %s is not a gnc XML file", path); return FALSE; } @@ -854,6 +860,11 @@ file_rollback_edit (QofBackend *be, QofInstance *inst) static void file_commit_edit (QofBackend *be, QofInstance *inst) { + if (qof_instance_get_dirty(inst) && qof_get_alt_dirty_mode() && + !(qof_instance_get_infant(inst) && qof_instance_get_destroying(inst))) { + qof_collection_mark_dirty(qof_instance_get_collection(inst)); + qof_book_mark_dirty(qof_instance_get_book(inst)); + } #if BORKEN_FOR_NOW FileBackend *fbe = (FileBackend *) be; QofBook *book = gp; @@ -1070,6 +1081,15 @@ qof_backend_module_init(void) prov->provider_free = gnc_provider_free; prov->check_data_type = gnc_determine_file_type; qof_backend_register_provider (prov); + + prov = g_new0 (QofBackendProvider, 1); + prov->provider_name = "GnuCash File Backend Version 2"; + prov->access_method = "xml"; + prov->partial_book_supported = FALSE; + prov->backend_new = gnc_backend_new; + prov->provider_free = gnc_provider_free; + prov->check_data_type = gnc_determine_file_type; + qof_backend_register_provider (prov); } /* ========================== END OF FILE ===================== */ diff --git a/src/backend/file/gnc-backend-file.h b/src/backend/file/gnc-backend-file.h index 1cc24b8511..69f3fa71b6 100644 --- a/src/backend/file/gnc-backend-file.h +++ b/src/backend/file/gnc-backend-file.h @@ -1,5 +1,5 @@ /******************************************************************** - * gnc-backend-file.h: load and save data to files * + * gnc-backend-xml.h: load and save data to XML files * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License as * @@ -28,8 +28,8 @@ * restoring data to/from an ordinary Unix filesystem file. */ -#ifndef GNC_BACKEND_FILE_H_ -#define GNC_BACKEND_FILE_H_ +#ifndef GNC_BACKEND_XML_H_ +#define GNC_BACKEND_XML_H_ #include "qof.h" #include @@ -59,4 +59,4 @@ typedef struct FileBackend_struct FileBackend; G_MODULE_EXPORT void qof_backend_module_init(void); -#endif /* GNC_BACKEND_FILE_H_ */ +#endif /* GNC_BACKEND_XML_H_ */ diff --git a/src/backend/file/gnc-schedxaction-xml-v2.c b/src/backend/file/gnc-schedxaction-xml-v2.c index cdbd347e78..16e2b3656e 100644 --- a/src/backend/file/gnc-schedxaction-xml-v2.c +++ b/src/backend/file/gnc-schedxaction-xml-v2.c @@ -787,6 +787,13 @@ tt_act_handler( xmlNodePtr node, gpointer data ) pre-7/11/2001-CIT-change SX template Account was parsed [but incorrectly]. */ if ( xaccAccountGetCommodity( acc ) == NULL ) { +#if 1 + gnc_commodity_table* table; + + table = gnc_commodity_table_get_table( txd->book ); + com = gnc_commodity_table_lookup( table, + "template", "template" ); +#else /* FIXME: This should first look in the table of the book, maybe? The right thing happens [WRT file load/save] if we just _new all the time, but it @@ -798,6 +805,7 @@ tt_act_handler( xmlNodePtr node, gpointer data ) "template", "template", "template", "template", 1 ); +#endif xaccAccountSetCommodity( acc, com ); } diff --git a/src/backend/file/gncmod-backend-xml.c b/src/backend/file/gncmod-backend-xml.c new file mode 100644 index 0000000000..57e592eaa7 --- /dev/null +++ b/src/backend/file/gncmod-backend-xml.c @@ -0,0 +1,65 @@ +/********************************************************************* + * gncmod-file-backend.c + * module definition/initialization for the file backend module + * + * Copyright (c) 2001 Linux Developers Group, Inc. + *********************************************************************/ + +#include +#include +/* #include */ + +#include "gnc-module.h" +#include "gnc-module-api.h" + +GNC_MODULE_API_DECL(libgncmod_backend_file) + +/* version of the gnc module system interface we require */ +int libgnc_backend_file_utils_gnc_module_system_interface = 0; + +/* module versioning uses libtool semantics. */ +int libgncmod_backend_file_gnc_module_current = 0; +int libgncmod_backend_file_gnc_module_revision = 0; +int libgncmod_backend_file_gnc_module_age = 0; + +static GNCModule engine; + + +char * +libgncmod_backend_file_gnc_module_path(void) +{ + return g_strdup("gnucash/backend/file"); +} + +char * +libgncmod_backend_file_gnc_module_description(void) +{ + return g_strdup("The binary and XML (v1 and v2) backends for GnuCash"); +} + +int +libgncmod_backend_file_gnc_module_init(int refcount) +{ + engine = gnc_module_load("gnucash/engine", 0); + if(!engine) return FALSE; + + /* Need to initialize g-type engine for gconf */ + if (refcount == 0) + g_type_init(); + + return TRUE; +} + +int +libgncmod_backend_file_gnc_module_end(int refcount) +{ + int unload = TRUE; + + if (engine) + unload = libgnc_backend_file_utils_gnc_module_unload(engine); + + if (refcount == 0) + engine = NULL; + + return unload; +} diff --git a/src/backend/file/test/Makefile.am b/src/backend/file/test/Makefile.am index 3e43ffad63..da5b80ac9e 100644 --- a/src/backend/file/test/Makefile.am +++ b/src/backend/file/test/Makefile.am @@ -1,3 +1,4 @@ +# SUBDIRS = test-files test_date_converting_SOURCES = \ @@ -27,7 +28,7 @@ test_kvp_frames_SOURCES = \ ${top_srcdir}/src/backend/file/sixtp-to-dom-parser.c \ test-kvp-frames.c -# the file backend is now a GModule - this test does +# the xml backend is now a GModule - this test does # not load it as a module and cannot link to it # and remain portable. @@ -249,4 +250,4 @@ EXTRA_DIST = \ test-real-data.sh \ test-xml2-is-file.c -INCLUDES = -DG_LOG_DOMAIN=\"gnc.backend.file\" +INCLUDES = -DG_LOG_DOMAIN=\"gnc.backend.xml\" diff --git a/src/backend/file/test/test-load-backend.c b/src/backend/file/test/test-load-backend.c index 7de4e6a302..426349409c 100644 --- a/src/backend/file/test/test-load-backend.c +++ b/src/backend/file/test/test-load-backend.c @@ -30,7 +30,7 @@ #include "cashobjects.h" #include "test-stuff.h" -#define GNC_LIB_NAME "gncmod-backend-file" +#define GNC_LIB_NAME "gncmod-backend-xml" int main (int argc, char ** argv) { @@ -38,7 +38,7 @@ int main (int argc, char ** argv) cashobjects_register(); do_test( qof_load_backend_library ("../.libs/", GNC_LIB_NAME), - " loading gnc-backend-file GModule failed"); + " loading gnc-backend-xml GModule failed"); print_test_results(); qof_close(); exit(get_rv()); diff --git a/src/backend/file/test/test-load-xml2.c b/src/backend/file/test/test-load-xml2.c index 5d9fac79f0..1d8ce89a0e 100644 --- a/src/backend/file/test/test-load-xml2.c +++ b/src/backend/file/test/test-load-xml2.c @@ -47,7 +47,7 @@ #include "test-engine-stuff.h" #include "test-file-stuff.h" -#define GNC_LIB_NAME "gncmod-backend-file" +#define GNC_LIB_NAME "gncmod-backend-xml" static void remove_files_pattern(const char *begining, const char *ending) @@ -112,7 +112,7 @@ main (int argc, char ** argv) qof_init(); cashobjects_register(); do_test(qof_load_backend_library ("../.libs/", GNC_LIB_NAME), - " loading gnc-backend-file GModule failed"); + " loading gnc-backend-xml GModule failed"); if (!location) { diff --git a/src/backend/postgres/test/Makefile.am b/src/backend/postgres/test/Makefile.am index 752e357d95..b8651513aa 100644 --- a/src/backend/postgres/test/Makefile.am +++ b/src/backend/postgres/test/Makefile.am @@ -7,7 +7,7 @@ GNC_TEST_DEPS := \ --gnc-module-dir ${top_builddir}/src/gnc-module \ --gnc-module-dir ${top_builddir}/src/engine \ --guile-load-dir ${top_srcdir}/src/engine \ - --gnc-module-dir ${top_builddir}/src/backend/file \ + --gnc-module-dir ${top_builddir}/src/backend/xml \ --gnc-module-dir ${top_builddir}/src/backend/postgres TESTS_ENVIRONMENT := \ diff --git a/src/backend/sql/Makefile.am b/src/backend/sql/Makefile.am new file mode 100644 index 0000000000..13aefed48f --- /dev/null +++ b/src/backend/sql/Makefile.am @@ -0,0 +1,50 @@ +SUBDIRS = . test + +# Now a shared library AND a GModule +lib_LTLIBRARIES = libgnc-backend-sql.la + +AM_CFLAGS = \ + -I.. -I../.. \ + -DLOCALE_DIR=\""$(datadir)/locale"\" \ + -I${top_srcdir}/src/backend \ + -I${top_srcdir}/src/engine \ + -I${top_srcdir}/src/core-utils\ + -I${top_srcdir}/lib/libc\ + ${QOF_CFLAGS} \ + ${GLIB_CFLAGS} \ + ${LIBGDA_CFLAGS} \ + ${GCONF_CFLAGS} \ + ${WARN_CFLAGS} + +libgnc_backend_sql_la_SOURCES = \ + gnc-backend-sql.c \ + gnc-account-sql.c \ + gnc-book-sql.c \ + gnc-budget-sql.c \ + gnc-commodity-sql.c \ + gnc-lots-sql.c \ + gnc-price-sql.c \ + gnc-recurrence-sql.c \ + gnc-schedxaction-sql.c \ + gnc-slots-sql.c \ + gnc-transaction-sql.c + +noinst_HEADERS = \ + gnc-account-sql.h \ + gnc-backend-sql.h \ + gnc-book-sql.h \ + gnc-budget-sql.h \ + gnc-commodity-sql.h \ + gnc-lots-sql.h \ + gnc-price-sql.h \ + gnc-recurrence-sql.h \ + gnc-schedxaction-sql.h \ + gnc-slots-sql.h \ + gnc-transaction-sql.h + +libgnc_backend_sql_la_LIBADD = \ + ${GLIB_LIBS} ${GCONF_LIBS} \ + ${top_builddir}/src/engine/libgncmod-engine.la \ + ${QOF_LIBS} + +INCLUDES = -DG_LOG_DOMAIN=\"gnc.backend.sql\" diff --git a/src/backend/sql/gnc-account-sql.c b/src/backend/sql/gnc-account-sql.c new file mode 100644 index 0000000000..9600507ef3 --- /dev/null +++ b/src/backend/sql/gnc-account-sql.c @@ -0,0 +1,366 @@ +/******************************************************************** + * gnc-account-sql.c: load and save data to SQL * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ +/** @file gnc-account-sql.c + * @brief load and save data to SQL + * @author Copyright (c) 2006-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL db + */ + +#include "config.h" + +#include + +#include "qof.h" +#include "Account.h" +#include "AccountP.h" +#include "gnc-commodity.h" + +#include "gnc-backend-sql.h" + +#include "gnc-account-sql.h" +#include "gnc-commodity-sql.h" +#include "gnc-slots-sql.h" +#include "gnc-transaction-sql.h" + +static QofLogModule log_module = G_LOG_DOMAIN; + +#define TABLE_NAME "accounts" +#define TABLE_VERSION 1 + +static gpointer get_parent( gpointer pObject, const QofParam* ); +static void set_parent( gpointer pObject, gpointer pValue ); +static void set_parent_guid( gpointer pObject, gpointer pValue ); + +#define ACCOUNT_MAX_NAME_LEN 2048 +#define ACCOUNT_MAX_TYPE_LEN 2048 +#define ACCOUNT_MAX_CODE_LEN 2048 +#define ACCOUNT_MAX_DESCRIPTION_LEN 2048 + +static const GncSqlColumnTableEntry col_table[] = +{ + { "guid", CT_GUID, 0, COL_NNUL|COL_PKEY, "guid" }, + { "name", CT_STRING, ACCOUNT_MAX_NAME_LEN, COL_NNUL, "name" }, + { "account_type", CT_STRING, ACCOUNT_MAX_TYPE_LEN, COL_NNUL, NULL, ACCOUNT_TYPE_ }, + { "commodity_guid", CT_COMMODITYREF, 0, COL_NNUL, "commodity" }, + { "commodity_scu", CT_INT, 0, COL_NNUL, "commodity-scu" }, + { "non_std_scu", CT_BOOLEAN, 0, COL_NNUL, "non-std-scu" }, + { "parent_guid", CT_GUID, 0, 0, NULL, NULL, get_parent, set_parent }, + { "code", CT_STRING, ACCOUNT_MAX_CODE_LEN, 0, "code" }, + { "description", CT_STRING, ACCOUNT_MAX_DESCRIPTION_LEN, 0, "description" }, + { NULL } +}; +static GncSqlColumnTableEntry parent_col_table[] = +{ + { "parent_guid", CT_GUID, 0, 0, NULL, NULL, NULL, set_parent_guid }, + { NULL } +}; + +typedef struct { + Account* pAccount; + GUID guid; +} account_parent_guid_struct; + +/* ================================================================= */ + +static gpointer +get_parent( gpointer pObject, const QofParam* param ) +{ + const Account* pAccount; + const Account* pParent; + const GUID* parent_guid; + + g_return_val_if_fail( pObject != NULL, NULL ); + g_return_val_if_fail( GNC_IS_ACCOUNT(pObject), NULL ); + + pAccount = GNC_ACCOUNT(pObject); + pParent = gnc_account_get_parent( pAccount ); + if( pParent == NULL ) { + parent_guid = NULL; + } else { + parent_guid = qof_instance_get_guid( QOF_INSTANCE(pParent) ); + } + + return (gpointer)parent_guid; +} + +static void +set_parent( gpointer pObject, gpointer pValue ) +{ + Account* pAccount; + QofBook* pBook; + GUID* guid = (GUID*)pValue; + Account* pParent; + + g_return_if_fail( pObject != NULL ); + g_return_if_fail( GNC_IS_ACCOUNT(pObject) ); + + pAccount = GNC_ACCOUNT(pObject); + pBook = qof_instance_get_book( QOF_INSTANCE(pAccount) ); + if( guid != NULL ) { + pParent = xaccAccountLookup( guid, pBook ); + if( pParent != NULL ) { + gnc_account_append_child( pParent, pAccount ); + } + } +} + +static void +set_parent_guid( gpointer pObject, gpointer pValue ) +{ + account_parent_guid_struct* s = (account_parent_guid_struct*)pObject; + GUID* guid = (GUID*)pValue; + + g_return_if_fail( pObject != NULL ); + g_return_if_fail( pValue != NULL ); + + s->guid = *guid; +} + +static Account* +load_single_account( GncSqlBackend* be, GncSqlRow* row, + GList** l_accounts_needing_parents ) +{ + const GUID* guid; + GUID acc_guid; + Account* pAccount; + + g_return_val_if_fail( be != NULL, NULL ); + g_return_val_if_fail( row != NULL, NULL ); + g_return_val_if_fail( l_accounts_needing_parents != NULL, NULL ); + + guid = gnc_sql_load_guid( be, row ); + acc_guid = *guid; + + pAccount = xaccAccountLookup( &acc_guid, be->primary_book ); + if( pAccount == NULL ) { + pAccount = xaccMallocAccount( be->primary_book ); + } + xaccAccountBeginEdit( pAccount ); + gnc_sql_load_object( be, row, GNC_ID_ACCOUNT, pAccount, col_table ); + xaccAccountCommitEdit( pAccount ); + + /* If we don't have a parent, it might be because the parent account hasn't + been loaded yet. Remember the account and its parent guid for later. */ + if( gnc_account_get_parent( pAccount ) == NULL ) { + account_parent_guid_struct* s = g_slice_new( account_parent_guid_struct ); + s->pAccount = pAccount; + gnc_sql_load_object( be, row, GNC_ID_ACCOUNT, s, parent_col_table ); + *l_accounts_needing_parents = g_list_prepend( *l_accounts_needing_parents, s ); + } + + return pAccount; +} + +static void +load_all_accounts( GncSqlBackend* be ) +{ + GncSqlStatement* stmt = NULL; + GncSqlResult* result; + QofBook* pBook; + gnc_commodity_table* pTable; + int numRows; + int r; + Account* parent; + GList* l_accounts_needing_parents = NULL; + GList* list = NULL; + + g_return_if_fail( be != NULL ); + + pBook = be->primary_book; + pTable = gnc_commodity_table_get_table( pBook ); + + stmt = gnc_sql_create_select_statement( be, TABLE_NAME ); + result = gnc_sql_execute_select_statement( be, stmt ); + gnc_sql_statement_dispose( stmt ); + if( result != NULL ) { + GncSqlRow* row = gnc_sql_result_get_first_row( result ); + Account* acc; + + while( row != NULL ) { + acc = load_single_account( be, row, &l_accounts_needing_parents ); + if( acc != NULL ) { + list = g_list_append( list, acc ); + } + row = gnc_sql_result_get_next_row( result ); + } + gnc_sql_result_dispose( result ); + + if( list != NULL ) { + gnc_sql_slots_load_for_list( be, list ); + } + + /* While there are items on the list of accounts needing parents, + try to see if the parent has now been loaded. Theory says that if + items are removed from the front and added to the back if the + parent is still not available, then eventually, the list will + shrink to size 0. */ + if( l_accounts_needing_parents != NULL ) { + gboolean progress_made = TRUE; + + Account* pParent; + GList* elem; + + while( progress_made ) { + progress_made = FALSE; + for( elem = l_accounts_needing_parents; elem != NULL; elem = g_list_next( elem ) ) { + account_parent_guid_struct* s = (account_parent_guid_struct*)elem->data; + const gchar* name = xaccAccountGetName( s->pAccount ); + pParent = xaccAccountLookup( &s->guid, be->primary_book ); + if( pParent != NULL ) { + gnc_account_append_child( pParent, s->pAccount ); + l_accounts_needing_parents = g_list_delete_link( l_accounts_needing_parents, elem ); + progress_made = TRUE; + } + } + } + + /* Any accounts left over must be parented by the root account */ + for( elem = l_accounts_needing_parents; elem != NULL; elem = g_list_next( elem ) ) { + account_parent_guid_struct* s = (account_parent_guid_struct*)elem->data; + Account* root; + root = gnc_book_get_root_account( pBook ); + if( root == NULL ) { + root = gnc_account_create_root( pBook ); + } + gnc_account_append_child( root, s->pAccount ); + } + } + } +} + +/* ================================================================= */ +static void +create_account_tables( GncSqlBackend* be ) +{ + gint version; + + g_return_if_fail( be != NULL ); + + version = gnc_sql_get_table_version( be, TABLE_NAME ); + if( version == 0 ) { + gnc_sql_create_table( be, TABLE_NAME, TABLE_VERSION, col_table ); + } +} + +/* ================================================================= */ +void +gnc_sql_save_account( GncSqlBackend* be, QofInstance* inst ) +{ + Account* pAcc = GNC_ACCOUNT(inst); + const GUID* guid; + gboolean is_infant; + + g_return_if_fail( be != NULL ); + g_return_if_fail( inst != NULL ); + g_return_if_fail( GNC_IS_ACCOUNT(inst) ); + + is_infant = qof_instance_get_infant( inst ); + + // If there is no commodity yet, this might be because a new account name + // has been entered directly into the register and an account window will + // be opened. The account info is not complete yet, but the name has been + // set, triggering this commit + if( xaccAccountGetCommodity( pAcc ) != NULL ) { + gint op; + + 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 not deleting the account, ensure the commodity is in the db + if( op != OP_DB_DELETE ) { + gnc_sql_save_commodity( be, xaccAccountGetCommodity( pAcc ) ); + } + + (void)gnc_sql_do_db_operation( be, op, TABLE_NAME, GNC_ID_ACCOUNT, pAcc, col_table ); + + // Now, commit or delete any slots + guid = qof_instance_get_guid( inst ); + if( !qof_instance_get_destroying(inst) ) { + gnc_sql_slots_save( be, guid, is_infant, qof_instance_get_slots( inst ) ); + } else { + gnc_sql_slots_delete( be, guid ); + } + } +} + +/* ================================================================= */ +static void +load_account_guid( const GncSqlBackend* be, GncSqlRow* row, + QofSetterFunc setter, gpointer pObject, + const GncSqlColumnTableEntry* table_row ) +{ + const GValue* val; + GUID guid; + const GUID* pGuid; + Account* account = NULL; + + g_return_if_fail( be != NULL ); + g_return_if_fail( row != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + + val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name ); + if( val == NULL ) { + pGuid = NULL; + } else { + string_to_guid( g_value_get_string( val ), &guid ); + pGuid = &guid; + } + if( pGuid != NULL ) { + account = xaccAccountLookup( pGuid, be->primary_book ); + } + if( table_row->gobj_param_name != NULL ) { + g_object_set( pObject, table_row->gobj_param_name, account, NULL ); + } else { + (*setter)( pObject, (const gpointer)account ); + } +} + +static col_type_handler_t account_guid_handler + = { load_account_guid, + gnc_sql_add_objectref_guid_col_info_to_list, + gnc_sql_add_colname_to_list, + gnc_sql_add_gvalue_objectref_guid_to_slist }; +/* ================================================================= */ +void +gnc_sql_init_account_handler( void ) +{ + static GncSqlObjectBackend be_data = + { + GNC_SQL_BACKEND_VERSION, + GNC_ID_ACCOUNT, + gnc_sql_save_account, /* commit */ + load_all_accounts, /* initial_load */ + create_account_tables /* create_tables */ + }; + + qof_object_register_backend( GNC_ID_ACCOUNT, GNC_SQL_BACKEND, &be_data ); + + gnc_sql_register_col_type_handler( CT_ACCOUNTREF, &account_guid_handler ); +} +/* ========================== END OF FILE ===================== */ diff --git a/src/backend/sql/gnc-account-sql.h b/src/backend/sql/gnc-account-sql.h new file mode 100644 index 0000000000..d0eb18a05c --- /dev/null +++ b/src/backend/sql/gnc-account-sql.h @@ -0,0 +1,38 @@ +/******************************************************************** + * gnc-account-sql.h: load and save data to SQL * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ +/** @file gnc-account-sql.h + * @brief load and save accounts data to SQL + * @author Copyright (c) 2006-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#ifndef GNC_ACCOUNT_SQL_H_ +#define GNC_ACCOUNT_SQL_H_ + +#include "qof.h" +#include + +void gnc_sql_init_account_handler( void ); +void gnc_sql_save_account( GncSqlBackend* be, QofInstance* inst ); + +#endif /* GNC_ACCOUNT_SQL_H_ */ diff --git a/src/backend/sql/gnc-backend-sql.c b/src/backend/sql/gnc-backend-sql.c new file mode 100644 index 0000000000..9ea16d51a4 --- /dev/null +++ b/src/backend/sql/gnc-backend-sql.c @@ -0,0 +1,2529 @@ +/******************************************************************** + * gnc-backend-sql.c: load and save data to SQL * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ +/** @file gnc-backend-sql.c + * @brief load and save data to SQL + * @author Copyright (c) 2006-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL db + */ + +#include +#include "config.h" + +#include +#include +#include +#include + +#include "qof.h" +#include "qofquery-p.h" +#include "qofquerycore-p.h" +#include "Account.h" +#include "TransLog.h" +#include "gnc-engine.h" +#include "SX-book.h" +#include "Recurrence.h" + +#include "gnc-gconf-utils.h" + +#include "gnc-backend-sql.h" + +#include "gnc-account-sql.h" +#include "gnc-book-sql.h" +#include "gnc-budget-sql.h" +#include "gnc-commodity-sql.h" +#include "gnc-lots-sql.h" +#include "gnc-price-sql.h" +#include "gnc-pricedb.h" +#include "gnc-recurrence-sql.h" +#include "gnc-schedxaction-sql.h" +#include "gnc-slots-sql.h" +#include "gnc-transaction-sql.h" + +static const gchar* convert_search_obj( QofIdType objType ); +static void gnc_sql_init_object_handlers( void ); +static void update_save_progress( GncSqlBackend* be ); +static void register_standard_col_type_handlers( void ); +static void reset_version_info( GncSqlBackend* be ); +static GncSqlStatement* build_insert_statement( GncSqlBackend* be, + const gchar* table_name, + QofIdTypeConst obj_name, gpointer pObject, + const GncSqlColumnTableEntry* table ); +static GncSqlStatement* build_update_statement( GncSqlBackend* be, + const gchar* table_name, + QofIdTypeConst obj_name, gpointer pObject, + const GncSqlColumnTableEntry* table ); +static GncSqlStatement* build_delete_statement( GncSqlBackend* be, + const gchar* table_name, + QofIdTypeConst obj_name, gpointer pObject, + const GncSqlColumnTableEntry* table ); + +#define TRANSACTION_NAME "trans" + +typedef struct { + QofIdType searchObj; + gpointer pCompiledQuery; +} gnc_sql_query_info; + +/* callback structure */ +typedef struct { + gboolean ok; + GncSqlBackend* be; + QofInstance* inst; + QofQuery* pQuery; + gpointer pCompiledQuery; + gnc_sql_query_info* pQueryInfo; +} sql_backend; + +static QofLogModule log_module = G_LOG_DOMAIN; + +#define SQLITE_PROVIDER_NAME "SQLite" +#define URI_PREFIX "gda://" + +/* ================================================================= */ + +void +gnc_sql_init( GncSqlBackend* be ) +{ + register_standard_col_type_handlers(); + gnc_sql_init_object_handlers(); +} + +/* ================================================================= */ + +static void +create_tables_cb( const gchar* type, gpointer data_p, gpointer be_p ) +{ + GncSqlObjectBackend* pData = data_p; + GncSqlBackend* be = be_p; + + g_return_if_fail( type != NULL && data_p != NULL && be_p != NULL ); + g_return_if_fail( pData->version == GNC_SQL_BACKEND_VERSION ); + + if( pData->create_tables != NULL ) { + (pData->create_tables)( be ); + } +} + +/* ================================================================= */ + +static const gchar* fixed_load_order[] = +{ GNC_ID_BOOK, GNC_ID_COMMODITY, GNC_ID_ACCOUNT, GNC_ID_LOT, NULL }; + +static void +initial_load_cb( const gchar* type, gpointer data_p, gpointer be_p ) +{ + GncSqlObjectBackend* pData = data_p; + GncSqlBackend* be = be_p; + int i; + + g_return_if_fail( type != NULL && data_p != NULL && be_p != NULL ); + g_return_if_fail( pData->version == GNC_SQL_BACKEND_VERSION ); + + // Don't need to load anything if it has already been loaded with the fixed order + for( i = 0; fixed_load_order[i] != NULL; i++ ) { + if( g_ascii_strcasecmp( type, fixed_load_order[i] ) == 0 ) return; + } + + if( pData->initial_load != NULL ) { + (pData->initial_load)( be ); + } +} + +void +gnc_sql_load( GncSqlBackend* be, QofBook *book ) +{ + GncSqlObjectBackend* pData; + int i; + Account* root; + GError* error = NULL; + + g_return_if_fail( be != NULL ); + g_return_if_fail( book != NULL ); + + ENTER( "be=%p, book=%p", be, book ); + + g_assert( be->primary_book == NULL ); + be->primary_book = book; + + /* Load any initial stuff */ + be->loading = TRUE; + + /* Some of this needs to happen in a certain order */ + for( i = 0; fixed_load_order[i] != NULL; i++ ) { + pData = qof_object_lookup_backend( fixed_load_order[i], GNC_SQL_BACKEND ); + if( pData->initial_load != NULL ) { + (pData->initial_load)( be ); + } + } + + root = gnc_book_get_root_account( book ); + gnc_account_foreach_descendant( root, (AccountCb)xaccAccountBeginEdit, NULL ); + + qof_object_foreach_backend( GNC_SQL_BACKEND, initial_load_cb, be ); + + gnc_account_foreach_descendant( root, (AccountCb)xaccAccountCommitEdit, NULL ); + + be->loading = FALSE; + + // Mark the book as clean + qof_book_mark_saved( book ); + + LEAVE( "" ); +} + +/* ================================================================= */ + +static gint +compare_namespaces(gconstpointer a, gconstpointer b) +{ + const gchar *sa = (const gchar *) a; + const gchar *sb = (const gchar *) b; + + return( safe_strcmp( sa, sb ) ); +} + +static gint +compare_commodity_ids(gconstpointer a, gconstpointer b) +{ + const gnc_commodity *ca = (const gnc_commodity *) a; + const gnc_commodity *cb = (const gnc_commodity *) b; + + return( safe_strcmp( gnc_commodity_get_mnemonic( ca ), + gnc_commodity_get_mnemonic( cb ) ) ); +} + +static void +write_commodities( GncSqlBackend* be, QofBook* book ) +{ + gnc_commodity_table* tbl; + GList* namespaces; + GList* lp; + + g_return_if_fail( be != NULL ); + g_return_if_fail( book != NULL ); + + tbl = gnc_book_get_commodity_table( book ); + namespaces = gnc_commodity_table_get_namespaces( tbl ); + if( namespaces != NULL ) { + namespaces = g_list_sort( namespaces, compare_namespaces ); + } + for( lp = namespaces; lp != NULL; lp = lp->next ) { + GList* comms; + GList* lp2; + + comms = gnc_commodity_table_get_commodities( tbl, lp->data ); + comms = g_list_sort( comms, compare_commodity_ids ); + + for( lp2 = comms; lp2 != NULL; lp2 = lp2->next ) { + gnc_sql_save_commodity( be, GNC_COMMODITY(lp2->data) ); + } + } +} + +static void +write_account_tree( GncSqlBackend* be, Account* root ) +{ + GList* descendants; + GList* node; + + g_return_if_fail( be != NULL ); + g_return_if_fail( root != NULL ); + + descendants = gnc_account_get_descendants( root ); + for( node = descendants; node != NULL; node = g_list_next(node) ) { + gnc_sql_save_account( be, QOF_INSTANCE(GNC_ACCOUNT(node->data)) ); + update_save_progress( be ); + } + g_list_free( descendants ); +} + +static void +write_accounts( GncSqlBackend* be ) +{ + g_return_if_fail( be != NULL ); + + write_account_tree( be, gnc_book_get_root_account( be->primary_book ) ); +} + +static int +write_tx( Transaction* tx, gpointer data ) +{ + GncSqlBackend* be = (GncSqlBackend*)data; + + g_return_val_if_fail( tx != NULL, 0 ); + g_return_val_if_fail( data != NULL, 0 ); + + gnc_sql_save_transaction( be, QOF_INSTANCE(tx) ); + update_save_progress( be ); + + return 0; +} + +static void +write_transactions( GncSqlBackend* be ) +{ + g_return_if_fail( be != NULL ); + + xaccAccountTreeForEachTransaction( gnc_book_get_root_account( be->primary_book ), + write_tx, + (gpointer)be ); +} + +static void +write_template_transactions( GncSqlBackend* be ) +{ + Account* ra; + + g_return_if_fail( be != NULL ); + + ra = gnc_book_get_template_root( be->primary_book ); + if( gnc_account_n_descendants( ra ) > 0 ) { + write_account_tree( be, ra ); + xaccAccountTreeForEachTransaction( ra, write_tx, (gpointer)be ); + } +} + +static void +write_schedXactions( GncSqlBackend* be ) +{ + GList* schedXactions; + SchedXaction* tmpSX; + + g_return_if_fail( be != NULL ); + + schedXactions = gnc_book_get_schedxactions( be->primary_book )->sx_list; + + for( ; schedXactions != NULL; schedXactions = schedXactions->next ) { + tmpSX = schedXactions->data; + gnc_sql_save_schedxaction( be, QOF_INSTANCE( tmpSX ) ); + } +} + +static void +write_cb( const gchar* type, gpointer data_p, gpointer be_p ) +{ + GncSqlObjectBackend* pData = data_p; + GncSqlBackend* be = (GncSqlBackend*)be_p; + + g_return_if_fail( type != NULL && data_p != NULL && be_p != NULL ); + g_return_if_fail( pData->version == GNC_SQL_BACKEND_VERSION ); + + if( pData->write != NULL ) { + (pData->write)( be ); + } +} + +static void +update_save_progress( GncSqlBackend* be ) +{ + if( be->be.percentage != NULL ) { + gint percent_done; + + be->operations_done++; + percent_done = be->operations_done * 100 / be->obj_total; + if( percent_done > 100 ) { + percent_done = 100; + } + (be->be.percentage)( NULL, percent_done ); + } +} + +void +gnc_sql_sync_all( GncSqlBackend* be, QofBook *book ) +{ + GError* error = NULL; + gint row; + gint numTables; + gboolean status; + + g_return_if_fail( be != NULL ); + g_return_if_fail( book != NULL ); + + ENTER( "book=%p, primary=%p", book, be->primary_book ); + + reset_version_info( be ); + + /* Create new tables */ + be->is_pristine_db = TRUE; + qof_object_foreach_backend( GNC_SQL_BACKEND, create_tables_cb, be ); + + /* Save all contents */ + be->primary_book = book; + be->obj_total = 0; + be->obj_total += 1 + gnc_account_n_descendants( gnc_book_get_root_account( book ) ); + be->obj_total += gnc_book_count_transactions( book ); + be->operations_done = 0; + + error = NULL; + gnc_sql_connection_begin_transaction( be->conn ); + + // FIXME: should write the set of commodities that are used + //write_commodities( be, book ); + gnc_sql_save_book( be, QOF_INSTANCE(book) ); + write_accounts( be ); + write_transactions( be ); + write_template_transactions( be ); + write_schedXactions( be ); + qof_object_foreach_backend( GNC_SQL_BACKEND, write_cb, be ); + + gnc_sql_connection_commit_transaction( be->conn ); + be->is_pristine_db = FALSE; + + // Mark the book as clean + qof_book_mark_saved( book ); + + LEAVE( "book=%p", book ); +} + +/* ================================================================= */ +/* Routines to deal with the creation of multiple books. */ + +void +gnc_sql_begin_edit( GncSqlBackend *be, QofInstance *inst ) +{ + g_return_if_fail( be != NULL ); + g_return_if_fail( inst != NULL ); +} + +void +gnc_sql_rollback_edit( GncSqlBackend *be, QofInstance *inst ) +{ + g_return_if_fail( be != NULL ); + g_return_if_fail( inst != NULL ); +} + +static void +commit_cb( const gchar* type, gpointer data_p, gpointer be_data_p ) +{ + GncSqlObjectBackend* pData = data_p; + sql_backend* be_data = be_data_p; + + g_return_if_fail( type != NULL && pData != NULL && be_data != NULL ); + g_return_if_fail( pData->version == GNC_SQL_BACKEND_VERSION ); + + /* If this has already been handled, or is not the correct handler, return */ + if( strcmp( pData->type_name, be_data->inst->e_type ) != 0 ) return; + if( be_data->ok ) return; + + if( pData->commit != NULL ) { + (pData->commit)( be_data->be, be_data->inst ); + be_data->ok = TRUE; + } +} + +/* Commit_edit handler - find the correct backend handler for this object + * type and call its commit handler + */ +void +gnc_sql_commit_edit( GncSqlBackend *be, QofInstance *inst ) +{ + sql_backend be_data; + GError* error; + gboolean status; + gboolean is_dirty; + gboolean is_destroying; + gboolean is_infant; + + g_return_if_fail( be != NULL ); + g_return_if_fail( inst != NULL ); + + /* During initial load where objects are being created, don't commit + anything, but do mark the object as clean. */ + if( be->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_saved( be->primary_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; + } + + error = NULL; + gnc_sql_connection_begin_transaction( be->conn ); + + be_data.ok = FALSE; + be_data.be = be; + be_data.inst = inst; + qof_object_foreach_backend( GNC_SQL_BACKEND, commit_cb, &be_data ); + + if( !be_data.ok ) { + PERR( "gnc_sql_commit_edit(): Unknown object type '%s'\n", inst->e_type ); + gnc_sql_connection_rollback_transaction( be->conn ); + + // Don't let unknown items still mark the book as being dirty + qof_instance_mark_clean(inst); + qof_book_mark_saved( be->primary_book ); + LEAVE( "Rolled back" ); + return; + } + gnc_sql_connection_commit_transaction( be->conn ); + + qof_instance_mark_clean(inst); + qof_book_mark_saved( be->primary_book ); + + LEAVE( "" ); +} +/* ---------------------------------------------------------------------- */ + +/* Query processing */ + +static const gchar* +convert_search_obj( QofIdType objType ) +{ + return (gchar*)objType; +} + +static void +handle_and_term( QofQueryTerm* pTerm, gchar* sql ) +{ + GSList* pParamPath; + QofQueryPredData* pPredData; + gboolean isInverted; + GSList* name; + gchar val[GUID_ENCODING_LENGTH+1]; + + g_return_if_fail( pTerm != NULL ); + g_return_if_fail( sql != NULL ); + + pParamPath = qof_query_term_get_param_path( pTerm ); + pPredData = qof_query_term_get_pred_data( pTerm ); + isInverted = qof_query_term_is_inverted( pTerm ); + + strcat( sql, "(" ); + if( isInverted ) { + strcat( sql, "!" ); + } + + for( name = pParamPath; name != NULL; name = name->next ) { + if( name != pParamPath ) strcat( sql, "." ); + strcat( sql, name->data ); + } + + if( pPredData->how == QOF_COMPARE_LT ) { + strcat( sql, "<" ); + } else if( pPredData->how == QOF_COMPARE_LTE ) { + strcat( sql, "<=" ); + } else if( pPredData->how == QOF_COMPARE_EQUAL ) { + strcat( sql, "=" ); + } else if( pPredData->how == QOF_COMPARE_GT ) { + strcat( sql, ">" ); + } else if( pPredData->how == QOF_COMPARE_GTE ) { + strcat( sql, ">=" ); + } else if( pPredData->how == QOF_COMPARE_NEQ ) { + strcat( sql, "~=" ); + } else { + strcat( sql, "??" ); + } + + if( strcmp( pPredData->type_name, "string" ) == 0 ) { + query_string_t pData = (query_string_t)pPredData; + strcat( sql, "'" ); + strcat( sql, pData->matchstring ); + strcat( sql, "'" ); + } else if( strcmp( pPredData->type_name, "date" ) == 0 ) { + query_date_t pData = (query_date_t)pPredData; + + (void)gnc_timespec_to_iso8601_buff( pData->date, val ); + strcat( sql, "'" ); + strncat( sql, val, 4+1+2+1+2 ); + strcat( sql, "'" ); + } else if( strcmp( pPredData->type_name, "numeric" ) == 0 ) { + query_numeric_t pData = (query_numeric_t)pPredData; + + strcat( sql, "numeric" ); + } else if( strcmp( pPredData->type_name, "guid" ) == 0 ) { + query_guid_t pData = (query_guid_t)pPredData; + (void)guid_to_string_buff( pData->guids->data, val ); + strcat( sql, "'" ); + strcat( sql, val ); + strcat( sql, "'" ); + } else if( strcmp( pPredData->type_name, "gint32" ) == 0 ) { + query_int32_t pData = (query_int32_t)pPredData; + + sprintf( val, "%d", pData->val ); + strcat( sql, val ); + } else if( strcmp( pPredData->type_name, "gint64" ) == 0 ) { + query_int64_t pData = (query_int64_t)pPredData; + + sprintf( val, "%" G_GINT64_FORMAT, pData->val ); + strcat( sql, val ); + } else if( strcmp( pPredData->type_name, "double" ) == 0 ) { + query_double_t pData = (query_double_t)pPredData; + + sprintf( val, "%f", pData->val ); + strcat( sql, val ); + } else if( strcmp( pPredData->type_name, "boolean" ) == 0 ) { + query_boolean_t pData = (query_boolean_t)pPredData; + + sprintf( val, "%d", pData->val ); + strcat( sql, val ); + } else { + g_assert( FALSE ); + } + + strcat( sql, ")" ); +} + +static void +compile_query_cb( const gchar* type, gpointer data_p, gpointer be_data_p ) +{ + GncSqlObjectBackend* pData = data_p; + sql_backend* be_data = be_data_p; + + g_return_if_fail( type != NULL && pData != NULL && be_data != NULL ); + g_return_if_fail( pData->version == GNC_SQL_BACKEND_VERSION ); + + // Is this the right item? + if( strcmp( type, be_data->pQueryInfo->searchObj ) != 0 ) return; + if( be_data->ok ) return; + + if( pData->compile_query != NULL ) { + be_data->pQueryInfo->pCompiledQuery = (pData->compile_query)( + be_data->be, + be_data->pQuery ); + be_data->ok = TRUE; + } +} + +static gpointer +gnc_sql_compile_query( QofBackend* pBEnd, QofQuery* pQuery ) +{ + GncSqlBackend *be = (GncSqlBackend*)pBEnd; + GList* pBookList; + QofIdType searchObj; + gchar sql[1000]; + sql_backend be_data; + gnc_sql_query_info* pQueryInfo; + + g_return_val_if_fail( pBEnd != NULL, NULL ); + g_return_val_if_fail( pQuery != NULL, NULL ); + + ENTER( " " ); + + searchObj = qof_query_get_search_for( pQuery ); + + pQueryInfo = g_malloc( sizeof( gnc_sql_query_info ) ); + + // Try various objects first + be_data.ok = FALSE; + be_data.be = be; + be_data.pQuery = pQuery; + pQueryInfo->searchObj = searchObj; + be_data.pQueryInfo = pQueryInfo; + + qof_object_foreach_backend( GNC_SQL_BACKEND, compile_query_cb, &be_data ); + if( be_data.ok ) { + LEAVE( "" ); + return be_data.pQueryInfo; + } + + pBookList = qof_query_get_books( pQuery ); + + /* Convert search object type to table name */ + sprintf( sql, "SELECT * from %s", convert_search_obj( searchObj ) ); + if( !qof_query_has_terms( pQuery ) ) { + strcat( sql, ";" ); + } else { + GList* pOrTerms = qof_query_get_terms( pQuery ); + GList* orTerm; + + strcat( sql, " WHERE " ); + + for( orTerm = pOrTerms; orTerm != NULL; orTerm = orTerm->next ) { + GList* pAndTerms = (GList*)orTerm->data; + GList* andTerm; + + if( orTerm != pOrTerms ) strcat( sql, " OR " ); + strcat( sql, "(" ); + for( andTerm = pAndTerms; andTerm != NULL; andTerm = andTerm->next ) { + if( andTerm != pAndTerms ) strcat( sql, " AND " ); + handle_and_term( (QofQueryTerm*)andTerm->data, sql ); + } + strcat( sql, ")" ); + } + } + + DEBUG( "Compiled: %s\n", sql ); + pQueryInfo->pCompiledQuery = g_strdup( sql ); + + LEAVE( "" ); + + return pQueryInfo; +} + +static void +free_query_cb( const gchar* type, gpointer data_p, gpointer be_data_p ) +{ + GncSqlObjectBackend* pData = data_p; + sql_backend* be_data = be_data_p; + + g_return_if_fail( type != NULL && pData != NULL && be_data != NULL ); + g_return_if_fail( pData->version == GNC_SQL_BACKEND_VERSION ); + if( be_data->ok ) return; + if( strcmp( type, be_data->pQueryInfo->searchObj ) != 0 ) return; + + if( pData->free_query != NULL ) { + (pData->free_query)( be_data->be, be_data->pCompiledQuery ); + be_data->ok = TRUE; + } +} + +static void +gnc_sql_free_query( QofBackend* pBEnd, gpointer pQuery ) +{ + GncSqlBackend *be = (GncSqlBackend*)pBEnd; + gnc_sql_query_info* pQueryInfo = (gnc_sql_query_info*)pQuery; + sql_backend be_data; + + g_return_if_fail( pBEnd != NULL ); + g_return_if_fail( pQuery != NULL ); + + ENTER( " " ); + + // Try various objects first + be_data.ok = FALSE; + be_data.be = be; + be_data.pCompiledQuery = pQuery; + be_data.pQueryInfo = pQueryInfo; + + qof_object_foreach_backend( GNC_SQL_BACKEND, free_query_cb, &be_data ); + if( be_data.ok ) { + LEAVE( "" ); + return; + } + + DEBUG( "%s\n", (gchar*)pQueryInfo->pCompiledQuery ); + g_free( pQueryInfo->pCompiledQuery ); + g_free( pQueryInfo ); + + LEAVE( "" ); +} + +static void +run_query_cb( const gchar* type, gpointer data_p, gpointer be_data_p ) +{ + GncSqlObjectBackend* pData = data_p; + sql_backend* be_data = be_data_p; + + g_return_if_fail( type != NULL && pData != NULL && be_data != NULL ); + g_return_if_fail( pData->version == GNC_SQL_BACKEND_VERSION ); + if( be_data->ok ) return; + + // Is this the right item? + if( strcmp( type, be_data->pQueryInfo->searchObj ) != 0 ) return; + + if( pData->run_query != NULL ) { + (pData->run_query)( be_data->be, be_data->pCompiledQuery ); + be_data->ok = TRUE; + } +} + +static void +gnc_sql_run_query( QofBackend* pBEnd, gpointer pQuery ) +{ + GncSqlBackend *be = (GncSqlBackend*)pBEnd; + gnc_sql_query_info* pQueryInfo = (gnc_sql_query_info*)pQuery; + sql_backend be_data; + + g_return_if_fail( pBEnd != NULL ); + g_return_if_fail( pQuery != NULL ); + g_return_if_fail( !be->in_query ); + + ENTER( " " ); + + be->loading = TRUE; + be->in_query = TRUE; + + qof_event_suspend(); + + // Try various objects first + be_data.ok = FALSE; + be_data.be = be; + be_data.pCompiledQuery = pQueryInfo->pCompiledQuery; + be_data.pQueryInfo = pQueryInfo; + + qof_object_foreach_backend( GNC_SQL_BACKEND, run_query_cb, &be_data ); + be->loading = FALSE; + be->in_query = FALSE; + qof_event_resume(); +// if( be_data.ok ) { +// LEAVE( "" ); +// return; +// } + + // Mark the book as clean + qof_instance_mark_clean( QOF_INSTANCE(be->primary_book) ); + +// DEBUG( "%s\n", (gchar*)pQueryInfo->pCompiledQuery ); + + LEAVE( "" ); +} + +/* ================================================================= */ +static void +gnc_sql_init_object_handlers( void ) +{ + gnc_sql_init_book_handler(); + gnc_sql_init_commodity_handler(); + gnc_sql_init_account_handler(); + gnc_sql_init_budget_handler(); + gnc_sql_init_price_handler(); + gnc_sql_init_transaction_handler(); + gnc_sql_init_slots_handler(); + gnc_sql_init_recurrence_handler(); + gnc_sql_init_schedxaction_handler(); + gnc_sql_init_lot_handler(); +} + +/* ================================================================= */ + +static void register_table_version( const GncSqlBackend* be, const gchar* table_name, gint version ); +static gint get_table_version( const GncSqlBackend* be, const gchar* table_name ); + +/* ================================================================= */ +static gint64 +get_integer_value( const GValue* value ) +{ + g_return_val_if_fail( value != NULL, 0 ); + + if( G_VALUE_HOLDS_INT(value) ) { + return g_value_get_int( value ); + } else if( G_VALUE_HOLDS_UINT(value) ) { + return g_value_get_uint( value ); + } else if( G_VALUE_HOLDS_LONG(value) ) { + return g_value_get_long( value ); + } else if( G_VALUE_HOLDS_ULONG(value) ) { + return g_value_get_ulong( value ); + } else if( G_VALUE_HOLDS_INT64(value) ) { + return g_value_get_int64( value ); + } else if( G_VALUE_HOLDS_UINT64(value) ) { + return g_value_get_uint64( value ); + } else { + PWARN( "Unknown type: %s", G_VALUE_TYPE_NAME( value ) ); + } + + return 0; +} + +/* ----------------------------------------------------------------- */ +static gpointer +get_autoinc_id( gpointer pObject, const QofParam* param ) +{ + // Just need a 0 to force a new recurrence id + return (gpointer)0; +} + +static void +set_autoinc_id( gpointer pObject, gpointer pValue ) +{ + // Nowhere to put the ID +} + +QofAccessFunc +gnc_sql_get_getter( QofIdTypeConst obj_name, const GncSqlColumnTableEntry* table_row ) +{ + QofAccessFunc getter; + + g_return_val_if_fail( obj_name != NULL, NULL ); + g_return_val_if_fail( table_row != NULL, NULL ); + + if( (table_row->flags & COL_AUTOINC) != 0 ) { + getter = get_autoinc_id; + } else if( table_row->qof_param_name != NULL ) { + getter = qof_class_get_parameter_getter( obj_name, + table_row->qof_param_name ); + } else { + getter = table_row->getter; + } + + return getter; +} + +/* ----------------------------------------------------------------- */ +void +gnc_sql_add_colname_to_list( const GncSqlColumnTableEntry* table_row, GList** pList ) +{ + (*pList) = g_list_append( (*pList), g_strdup( table_row->col_name ) ); +} + +/* ----------------------------------------------------------------- */ +void +gnc_sql_add_subtable_colnames_to_list( const GncSqlColumnTableEntry* table_row, const GncSqlColumnTableEntry* subtable, + GList** pList ) +{ + const GncSqlColumnTableEntry* subtable_row; + gchar* buf; + + for( subtable_row = subtable; subtable_row->col_name != NULL; subtable_row++ ) { + buf = g_strdup_printf( "%s_%s", table_row->col_name, subtable_row->col_name ); + (*pList) = g_list_append( (*pList), buf ); + } +} + +static GncSqlColumnInfo* +create_column_info( const GncSqlColumnTableEntry* table_row, const gchar* type, + gint size ) +{ + GncSqlColumnInfo* info; + + info = g_new0( GncSqlColumnInfo, 1 ); + info->name = table_row->col_name; + info->type_name = type; + info->size = size; + info->is_primary_key = (table_row->flags & COL_PKEY) ? TRUE : FALSE; + info->null_allowed = (table_row->flags & COL_NNUL) ? FALSE : TRUE; + + return info; +} + +/* ----------------------------------------------------------------- */ +static void +load_string( const GncSqlBackend* be, GncSqlRow* row, + QofSetterFunc setter, gpointer pObject, + const GncSqlColumnTableEntry* table_row ) +{ + const GValue* val; + const gchar* s; + + g_return_if_fail( be != NULL ); + g_return_if_fail( row != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + + val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name ); + if( val == NULL ) { + s = NULL; + } else { + s = g_value_get_string( val ); + } + if( table_row->gobj_param_name != NULL ) { + g_object_set( pObject, table_row->gobj_param_name, s, NULL ); + } else { + (*setter)( pObject, (const gpointer)s ); + } +} + +static void +add_string_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row, + GList** pList ) +{ + GncSqlColumnInfo* info; + + g_return_if_fail( be != NULL ); + g_return_if_fail( table_row != NULL ); + g_return_if_fail( pList != NULL ); + + info = create_column_info( table_row, + gnc_sql_connection_get_column_type_name( be->conn, + G_TYPE_STRING, table_row->size ), + table_row->size ); + + *pList = g_list_append( *pList, info ); +} + +static void +add_gvalue_string_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name, + const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList ) +{ + QofAccessFunc getter; + gchar* s; + GValue* value = g_new0( GValue, 1 ); + gchar* buf; + + g_return_if_fail( be != NULL ); + g_return_if_fail( obj_name != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + g_return_if_fail( pList != NULL ); + + memset( value, 0, sizeof( GValue ) ); + if( table_row->gobj_param_name != NULL ) { + g_object_get( pObject, table_row->gobj_param_name, &s, NULL ); + } else { + getter = gnc_sql_get_getter( obj_name, table_row ); + s = (gchar*)(*getter)( pObject, NULL ); + } + g_value_init( value, G_TYPE_STRING ); + if( s ) { + g_value_set_string( value, s ); + } + + (*pList) = g_slist_append( (*pList), value ); +} + +static col_type_handler_t string_handler + = { load_string, + add_string_col_info_to_list, + gnc_sql_add_colname_to_list, + add_gvalue_string_to_slist }; +/* ----------------------------------------------------------------- */ +typedef gint (*IntAccessFunc)( const gpointer ); +typedef void (*IntSetterFunc)( const gpointer, gint ); + +static void +load_int( const GncSqlBackend* be, GncSqlRow* row, + QofSetterFunc setter, gpointer pObject, + const GncSqlColumnTableEntry* table_row ) +{ + const GValue* val; + gint int_value; + IntSetterFunc i_setter; + + g_return_if_fail( be != NULL ); + g_return_if_fail( row != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + + val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name ); + if( val == NULL ) { + int_value = 0; + } else { + int_value = get_integer_value( val ); + } + if( table_row->gobj_param_name != NULL ) { + g_object_set( pObject, table_row->gobj_param_name, int_value, NULL ); + } else { + i_setter = (IntSetterFunc)setter; + (*i_setter)( pObject, int_value ); + } +} + +static void +add_int_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row, + GList** pList ) +{ + GncSqlColumnInfo* info; + + g_return_if_fail( be != NULL ); + g_return_if_fail( table_row != NULL ); + g_return_if_fail( pList != NULL ); + + info = create_column_info( table_row, + gnc_sql_connection_get_column_type_name( be->conn, + G_TYPE_INT, table_row->size ), + 0 ); + + *pList = g_list_append( *pList, info ); +} + +static void +add_gvalue_int_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name, + const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList ) +{ + gint int_value; + IntAccessFunc i_getter; + GValue* value; + + g_return_if_fail( be != NULL ); + g_return_if_fail( obj_name != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + g_return_if_fail( pList != NULL ); + + value = g_new0( GValue, 1 ); + g_value_init( value, G_TYPE_INT ); + + if( table_row->gobj_param_name != NULL ) { + g_object_get_property( pObject, table_row->gobj_param_name, value ); + } else { + i_getter = (IntAccessFunc)gnc_sql_get_getter( obj_name, table_row ); + int_value = (*i_getter)( pObject ); + g_value_set_int( value, int_value ); + } + + (*pList) = g_slist_append( (*pList), value ); +} + +static col_type_handler_t int_handler + = { load_int, + add_int_col_info_to_list, + gnc_sql_add_colname_to_list, + add_gvalue_int_to_slist }; +/* ----------------------------------------------------------------- */ +typedef gboolean (*BooleanAccessFunc)( const gpointer ); +typedef void (*BooleanSetterFunc)( const gpointer, gboolean ); + +static void +load_boolean( const GncSqlBackend* be, GncSqlRow* row, + QofSetterFunc setter, gpointer pObject, + const GncSqlColumnTableEntry* table_row ) +{ + const GValue* val; + gint int_value; + BooleanSetterFunc b_setter; + + g_return_if_fail( be != NULL ); + g_return_if_fail( row != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + + val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name ); + if( val == NULL ) { + int_value = 0; + } else { + int_value = g_value_get_int( val ); + } + if( table_row->gobj_param_name != NULL ) { + g_object_set( pObject, table_row->gobj_param_name, int_value, NULL ); + } else { + b_setter = (BooleanSetterFunc)setter; + (*b_setter)( pObject, int_value ? TRUE : FALSE ); + } +} + +static void +add_boolean_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row, + GList** pList ) +{ + GncSqlColumnInfo* info; + + g_return_if_fail( be != NULL ); + g_return_if_fail( table_row != NULL ); + g_return_if_fail( pList != NULL ); + + info = create_column_info( table_row, + gnc_sql_connection_get_column_type_name( be->conn, + G_TYPE_INT, table_row->size ), + 0 ); + + *pList = g_list_append( *pList, info ); +} + +static void +add_gvalue_boolean_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name, + const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList ) +{ + gint int_value; + BooleanAccessFunc b_getter; + GValue* value; + + g_return_if_fail( be != NULL ); + g_return_if_fail( obj_name != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + g_return_if_fail( pList != NULL ); + + value = g_new0( GValue, 1 ); + + if( table_row->gobj_param_name != NULL ) { + g_object_get( pObject, table_row->gobj_param_name, &int_value, NULL ); + } else { + b_getter = (BooleanAccessFunc)gnc_sql_get_getter( obj_name, table_row ); + int_value = ((*b_getter)( pObject )) ? 1 : 0; + } + g_value_init( value, G_TYPE_INT ); + g_value_set_int( value, int_value ); + + (*pList) = g_slist_append( (*pList), value ); +} + +static col_type_handler_t boolean_handler + = { load_boolean, + add_boolean_col_info_to_list, + gnc_sql_add_colname_to_list, + add_gvalue_boolean_to_slist }; +/* ----------------------------------------------------------------- */ +typedef gint64 (*Int64AccessFunc)( const gpointer ); +typedef void (*Int64SetterFunc)( const gpointer, gint64 ); + +static void +load_int64( const GncSqlBackend* be, GncSqlRow* row, + QofSetterFunc setter, gpointer pObject, + const GncSqlColumnTableEntry* table_row ) +{ + const GValue* val; + gint64 i64_value = 0; + Int64SetterFunc i64_setter = (Int64SetterFunc)setter; + + g_return_if_fail( be != NULL ); + g_return_if_fail( row != NULL ); + g_return_if_fail( setter != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + + val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name ); + if( val != NULL ) { + i64_value = get_integer_value( val ); + } + (*i64_setter)( pObject, i64_value ); +} + +static void +add_int64_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row, + GList** pList ) +{ + GncSqlColumnInfo* info; + + g_return_if_fail( be != NULL ); + g_return_if_fail( table_row != NULL ); + g_return_if_fail( pList != NULL ); + + info = create_column_info( table_row, + gnc_sql_connection_get_column_type_name( be->conn, + G_TYPE_INT64, table_row->size ), + 0 ); + + *pList = g_list_append( *pList, info ); +} + +static void +add_gvalue_int64_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name, + const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList ) +{ + gint64 i64_value; + Int64AccessFunc getter; + GValue* value; + + g_return_if_fail( be != NULL ); + g_return_if_fail( obj_name != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + g_return_if_fail( pList != NULL ); + + value = g_new0( GValue, 1 ); + if( table_row->gobj_param_name != NULL ) { + g_object_get( pObject, table_row->gobj_param_name, &i64_value, NULL ); + } else { + getter = (Int64AccessFunc)gnc_sql_get_getter( obj_name, table_row ); + i64_value = (*getter)( pObject ); + } + g_value_init( value, G_TYPE_INT64 ); + g_value_set_int64( value, i64_value ); + + (*pList) = g_slist_append( (*pList), value ); +} + +static col_type_handler_t int64_handler + = { load_int64, + add_int64_col_info_to_list, + gnc_sql_add_colname_to_list, + add_gvalue_int64_to_slist }; +/* ----------------------------------------------------------------- */ + +static void +load_double( const GncSqlBackend* be, GncSqlRow* row, + QofSetterFunc setter, gpointer pObject, + const GncSqlColumnTableEntry* table_row ) +{ + const GValue* val; + gdouble d_value; + + g_return_if_fail( be != NULL ); + g_return_if_fail( row != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + + val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name ); + if( val == NULL ) { + (*setter)( pObject, (gpointer)NULL ); + } else { + if( G_VALUE_HOLDS(val, G_TYPE_INT) ) { + d_value = g_value_get_int( val ); + } else { + d_value = g_value_get_double( val ); + } + (*setter)( pObject, (gpointer)&d_value ); + } +} + +static void +add_double_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row, + GList** pList ) +{ + GncSqlColumnInfo* info; + + g_return_if_fail( be != NULL ); + g_return_if_fail( table_row != NULL ); + g_return_if_fail( pList != NULL ); + + info = create_column_info( table_row, + gnc_sql_connection_get_column_type_name( be->conn, + G_TYPE_DOUBLE, table_row->size ), + 0 ); + + *pList = g_list_append( *pList, info ); +} + +static void +add_gvalue_double_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name, + const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList ) +{ + QofAccessFunc getter; + gdouble* pDouble; + gdouble d_value; + GValue* value; + + g_return_if_fail( be != NULL ); + g_return_if_fail( obj_name != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + + value = g_new0( GValue, 1 ); + getter = gnc_sql_get_getter( obj_name, table_row ); + pDouble = (*getter)( pObject, NULL ); + if( pDouble != NULL ) { + d_value = *pDouble; + g_value_init( value, G_TYPE_DOUBLE ); + g_value_set_double( value, d_value ); + } else { + g_value_init( value, G_TYPE_DOUBLE ); + g_value_set_double( value, 0 ); + } + + (*pList) = g_slist_append( (*pList), value ); +} + +static col_type_handler_t double_handler + = { load_double, + add_double_col_info_to_list, + gnc_sql_add_colname_to_list, + add_gvalue_double_to_slist }; +/* ----------------------------------------------------------------- */ + +static void +load_guid( const GncSqlBackend* be, GncSqlRow* row, + QofSetterFunc setter, gpointer pObject, + const GncSqlColumnTableEntry* table_row ) +{ + const GValue* val; + GUID guid; + const GUID* pGuid; + + g_return_if_fail( be != NULL ); + g_return_if_fail( row != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + + val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name ); + if( val == NULL ) { + pGuid = NULL; + } else { + string_to_guid( g_value_get_string( val ), &guid ); + pGuid = &guid; + } + if( table_row->gobj_param_name != NULL ) { + g_object_set( pObject, table_row->gobj_param_name, pGuid, NULL ); + } else { + (*setter)( pObject, (const gpointer)pGuid ); + } +} + +static void +add_guid_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row, + GList** pList ) +{ + GncSqlColumnInfo* info; + + g_return_if_fail( be != NULL ); + g_return_if_fail( table_row != NULL ); + g_return_if_fail( pList != NULL ); + + info = create_column_info( table_row, "CHAR", GUID_ENCODING_LENGTH ); + + *pList = g_list_append( *pList, info ); +} + +static void +add_gvalue_guid_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name, + const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList ) +{ + QofAccessFunc getter; + const GUID* guid; + gchar guid_buf[GUID_ENCODING_LENGTH+1]; + GValue* value; + + g_return_if_fail( be != NULL ); + g_return_if_fail( obj_name != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + + value = g_new0( GValue, 1 ); + if( table_row->gobj_param_name != NULL ) { + g_object_get( pObject, table_row->gobj_param_name, &guid, NULL ); + } else { + getter = gnc_sql_get_getter( obj_name, table_row ); + guid = (*getter)( pObject, NULL ); + } + g_value_init( value, G_TYPE_STRING ); + if( guid != NULL ) { + (void)guid_to_string_buff( guid, guid_buf ); + g_value_set_string( value, guid_buf ); + } + + (*pList) = g_slist_append( (*pList), value ); +} + +static col_type_handler_t guid_handler + = { load_guid, + add_guid_col_info_to_list, + gnc_sql_add_colname_to_list, + add_gvalue_guid_to_slist }; +/* ----------------------------------------------------------------- */ + +void +gnc_sql_add_gvalue_objectref_guid_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name, + const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList ) +{ + QofAccessFunc getter; + const GUID* guid = NULL; + gchar guid_buf[GUID_ENCODING_LENGTH+1]; + QofInstance* inst; + GValue* value; + + g_return_if_fail( be != NULL ); + g_return_if_fail( obj_name != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + + value = g_new0( GValue, 1 ); + if( table_row->gobj_param_name != NULL ) { + g_object_get( pObject, table_row->gobj_param_name, &inst, NULL ); + } else { + getter = gnc_sql_get_getter( obj_name, table_row ); + inst = (*getter)( pObject, NULL ); + } + if( inst != NULL ) { + guid = qof_instance_get_guid( inst ); + } + g_value_init( value, G_TYPE_STRING ); + if( guid != NULL ) { + (void)guid_to_string_buff( guid, guid_buf ); + g_value_set_string( value, guid_buf ); + } + + (*pList) = g_slist_append( (*pList), value ); +} + +void +gnc_sql_add_objectref_guid_col_info_to_list( const GncSqlBackend* be, + const GncSqlColumnTableEntry* table_row, + GList** pList ) +{ + add_guid_col_info_to_list( be, table_row, pList ); +} + +/* ----------------------------------------------------------------- */ +typedef Timespec (*TimespecAccessFunc)( const gpointer ); +typedef void (*TimespecSetterFunc)( const gpointer, Timespec ); + +#define TIMESPEC_STR_FORMAT "%04d%02d%02d%02d%02d%02d" +#define TIMESPEC_COL_SIZE (4+2+2+2+2+2) + +static void +load_timespec( const GncSqlBackend* be, GncSqlRow* row, + QofSetterFunc setter, gpointer pObject, + const GncSqlColumnTableEntry* table_row ) +{ + const GValue* val; + GDate* date; + Timespec ts = {0, 0}; + TimespecSetterFunc ts_setter; + + g_return_if_fail( be != NULL ); + g_return_if_fail( row != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + + ts_setter = (TimespecSetterFunc)setter; + val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name ); + if( val == NULL ) { + (*ts_setter)( pObject, ts ); + } else { + if( G_VALUE_HOLDS_STRING( val ) ) { + const gchar* s = g_value_get_string( val ); + gchar* buf; + buf = g_strdup_printf( "%c%c%c%c-%c%c-%c%c %c%c:%c%c:%c%c", + s[0], s[1], s[2], s[3], + s[4], s[5], + s[6], s[7], + s[8], s[9], + s[10], s[11], + s[12], s[13] ); + ts = gnc_iso8601_to_timespec_gmt( buf ); + (*ts_setter)( pObject, ts ); + g_free( buf ); + + } else { + PWARN( "Unknown timespec type: %s", G_VALUE_TYPE_NAME( val ) ); + } + } +} + +static void +add_timespec_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row, + GList** pList ) +{ + GncSqlColumnInfo* info; + + g_return_if_fail( be != NULL ); + g_return_if_fail( table_row != NULL ); + g_return_if_fail( pList != NULL ); + + info = create_column_info( table_row, "CHAR", TIMESPEC_COL_SIZE ); + + *pList = g_list_append( *pList, info ); +} + +static void +add_gvalue_timespec_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name, + const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList ) +{ + TimespecAccessFunc ts_getter; + Timespec ts; + gchar* datebuf; + time_t time; + struct tm* tm; + gint year; + GValue* value; + + g_return_if_fail( be != NULL ); + g_return_if_fail( obj_name != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + g_return_if_fail( pList != NULL ); + + value = g_new0( GValue, 1 ); + ts_getter = (TimespecAccessFunc)gnc_sql_get_getter( obj_name, table_row ); + ts = (*ts_getter)( pObject ); + + time = timespecToTime_t( ts ); + tm = gmtime( &time ); + + if( tm->tm_year < 60 ) year = tm->tm_year + 2000; + else year = tm->tm_year + 1900; + + datebuf = g_strdup_printf( TIMESPEC_STR_FORMAT, + year, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec ); + g_value_init( value, G_TYPE_STRING ); + g_value_take_string( value, datebuf ); + + (*pList) = g_slist_append( (*pList), value ); +} + +static col_type_handler_t timespec_handler + = { load_timespec, + add_timespec_col_info_to_list, + gnc_sql_add_colname_to_list, + add_gvalue_timespec_to_slist }; +/* ----------------------------------------------------------------- */ +#define DATE_COL_SIZE 8 + +static void +load_date( const GncSqlBackend* be, GncSqlRow* row, + QofSetterFunc setter, gpointer pObject, + const GncSqlColumnTableEntry* table_row ) +{ + const GValue* val; + GDate* date; + + g_return_if_fail( be != NULL ); + g_return_if_fail( row != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + + val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name ); + if( val == NULL ) { + date = g_date_new_dmy( 1, 1, 1970 ); + (*setter)( pObject, date ); + g_date_free( date ); + } else { + if( G_VALUE_HOLDS_STRING( val ) ) { + // Format of date is YYYYMMDD + const gchar* s = g_value_get_string( val ); + gchar buf[5]; + guint year, month, day; + + strncpy( buf, &s[0], 4 ); + buf[4] = '\0'; + year = atoi( buf ); + strncpy( buf, &s[4], 2 ); + buf[2] = '\0'; + month = atoi( buf ); + day = atoi( &s[6] ); + + if( year != 0 || month != 0 || day != 0 ) { + date = g_date_new_dmy( day, month, year ); + (*setter)( pObject, date ); + g_date_free( date ); + } + } else { + PWARN( "Unknown date type: %s", G_VALUE_TYPE_NAME( val ) ); + } + } +} + +static void +add_date_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row, + GList** pList ) +{ + GncSqlColumnInfo* info; + + g_return_if_fail( be != NULL ); + g_return_if_fail( table_row != NULL ); + g_return_if_fail( pList != NULL ); + + info = create_column_info( table_row, "CHAR", DATE_COL_SIZE ); + + *pList = g_list_append( *pList, info ); +} + +static void +add_gvalue_date_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name, + const gpointer pObject, + const GncSqlColumnTableEntry* table_row, GSList** pList ) +{ + GDate* date; + QofAccessFunc getter; + gchar* buf; + GValue* value; + + g_return_if_fail( be != NULL ); + g_return_if_fail( obj_name != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + + value = g_new0( GValue, 1 ); + getter = gnc_sql_get_getter( obj_name, table_row ); + date = (GDate*)(*getter)( pObject, NULL ); + buf = g_strdup_printf( "%04d%02d%02d", + g_date_get_year( date ), g_date_get_month( date ), g_date_get_day( date ) ); + g_value_init( value, G_TYPE_STRING ); + g_value_take_string( value, buf ); + + (*pList) = g_slist_append( (*pList), value ); +} + +static col_type_handler_t date_handler + = { load_date, + add_date_col_info_to_list, + gnc_sql_add_colname_to_list, + add_gvalue_date_to_slist }; +/* ----------------------------------------------------------------- */ +typedef gnc_numeric (*NumericGetterFunc)( const gpointer ); +typedef void (*NumericSetterFunc)( gpointer, gnc_numeric ); + +static const GncSqlColumnTableEntry numeric_col_table[] = +{ + { "num", CT_INT64, 0, COL_NNUL, "guid" }, + { "denom", CT_INT64, 0, COL_NNUL, "guid" }, + { NULL } +}; + +static void +load_numeric( const GncSqlBackend* be, GncSqlRow* row, + QofSetterFunc setter, gpointer pObject, + const GncSqlColumnTableEntry* table_row ) +{ + const GValue* val; + gchar* buf; + gint64 num, denom; + gnc_numeric n; + gboolean isNull = FALSE; + NumericSetterFunc n_setter = (NumericSetterFunc)setter; + + g_return_if_fail( be != NULL ); + g_return_if_fail( row != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + + buf = g_strdup_printf( "%s_num", table_row->col_name ); + val = gnc_sql_row_get_value_at_col_name( row, buf ); + g_free( buf ); + if( val == NULL ) { + isNull = TRUE; + num = 0; + } else { + num = get_integer_value( val ); + } + buf = g_strdup_printf( "%s_denom", table_row->col_name ); + val = gnc_sql_row_get_value_at_col_name( row, buf ); + g_free( buf ); + if( val == NULL ) { + isNull = TRUE; + denom = 1; + } else { + denom = get_integer_value( val ); + } + n = gnc_numeric_create( num, denom ); + if( !isNull ) { + (*n_setter)( pObject, n ); + } +} + +static void +add_numeric_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row, + GList** pList ) +{ + GncSqlColumnInfo* info; + gchar* buf; + const GncSqlColumnTableEntry* subtable_row; + const gchar* type; + + g_return_if_fail( be != NULL ); + g_return_if_fail( table_row != NULL ); + g_return_if_fail( pList != NULL ); + + for( subtable_row = numeric_col_table; subtable_row->col_name != NULL; subtable_row++ ) { + buf = g_strdup_printf( "%s_%s", table_row->col_name, subtable_row->col_name ); + info = g_new0( GncSqlColumnInfo, 1 ); + info->name = buf; + info->type_name = gnc_sql_connection_get_column_type_name( be->conn, + G_TYPE_INT64, table_row->size ); + info->is_primary_key = (table_row->flags & COL_PKEY) ? TRUE : FALSE; + info->null_allowed = (table_row->flags & COL_NNUL) ? FALSE : TRUE; + *pList = g_list_append( *pList, info ); + } +} + +static void +add_numeric_colname_to_list( const GncSqlColumnTableEntry* table_row, GList** pList ) +{ + gnc_sql_add_subtable_colnames_to_list( table_row, numeric_col_table, pList ); +} + +static void +add_gvalue_numeric_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name, + const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList ) +{ + NumericGetterFunc getter; + gnc_numeric n; + GValue* num_value; + GValue* denom_value; + + g_return_if_fail( be != NULL ); + g_return_if_fail( obj_name != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + +// if( table_row->gobj_param_name != NULL ) { +// g_object_get( pObject, table_row->gobj_param_name, &s, NULL ); +// } else { + getter = (NumericGetterFunc)gnc_sql_get_getter( obj_name, table_row ); + n = (*getter)( pObject ); +// } + + num_value = g_new0( GValue, 1 ); + g_value_init( num_value, G_TYPE_INT64 ); + g_value_set_int64( num_value, gnc_numeric_num( n ) ); + denom_value = g_new0( GValue, 1 ); + g_value_init( denom_value, G_TYPE_INT64 ); + g_value_set_int64( denom_value, gnc_numeric_denom( n ) ); + + (*pList) = g_slist_append( (*pList), num_value ); + (*pList) = g_slist_append( (*pList), denom_value ); +} + +static col_type_handler_t numeric_handler + = { load_numeric, + add_numeric_col_info_to_list, + add_numeric_colname_to_list, + add_gvalue_numeric_to_slist }; +/* ================================================================= */ + +static GHashTable* g_columnTypeHash = NULL; + +void +gnc_sql_register_col_type_handler( const gchar* colType, const col_type_handler_t* handler ) +{ + g_return_if_fail( colType != NULL ); + g_return_if_fail( handler != NULL ); + + if( g_columnTypeHash == NULL ) { + g_columnTypeHash = g_hash_table_new( g_str_hash, g_str_equal ); + } + + g_hash_table_insert( g_columnTypeHash, (gpointer)colType, (gpointer)handler ); + DEBUG( "Col type %s registered\n", colType ); +} + +static col_type_handler_t* +get_handler( const GncSqlColumnTableEntry* table_row ) +{ + col_type_handler_t* pHandler; + + g_return_val_if_fail( table_row != NULL, NULL ); + g_return_val_if_fail( table_row->col_type != NULL, NULL ); + + pHandler = g_hash_table_lookup( g_columnTypeHash, table_row->col_type ); + if( pHandler == NULL ) { + g_assert( FALSE ); + } + + return pHandler; +} + +static void +register_standard_col_type_handlers( void ) +{ + gnc_sql_register_col_type_handler( CT_STRING, &string_handler ); + gnc_sql_register_col_type_handler( CT_BOOLEAN, &boolean_handler ); + gnc_sql_register_col_type_handler( CT_INT, &int_handler ); + gnc_sql_register_col_type_handler( CT_INT64, &int64_handler ); + gnc_sql_register_col_type_handler( CT_DOUBLE, &double_handler ); + gnc_sql_register_col_type_handler( CT_GUID, &guid_handler ); + gnc_sql_register_col_type_handler( CT_TIMESPEC, ×pec_handler ); + gnc_sql_register_col_type_handler( CT_GDATE, &date_handler ); + gnc_sql_register_col_type_handler( CT_NUMERIC, &numeric_handler ); +} + +void +_retrieve_guid_( gpointer pObject, gpointer pValue ) +{ + GUID* pGuid = (GUID*)pObject; + GUID* guid = (GUID*)pValue; + + g_return_if_fail( pObject != NULL ); + g_return_if_fail( pValue != NULL ); + + memcpy( pGuid, guid, sizeof( GUID ) ); +} + + +// Table to retrieve just the guid +static GncSqlColumnTableEntry guid_table[] = +{ + { "guid", CT_GUID, 0, 0, NULL, NULL, NULL, _retrieve_guid_ }, + { NULL } +}; + +const GUID* +gnc_sql_load_guid( const GncSqlBackend* be, GncSqlRow* row ) +{ + static GUID guid; + + g_return_val_if_fail( be != NULL, NULL ); + g_return_val_if_fail( row != NULL, NULL ); + + gnc_sql_load_object( be, row, NULL, &guid, guid_table ); + + return &guid; +} + +// Table to retrieve just the guid +static GncSqlColumnTableEntry tx_guid_table[] = +{ + { "tx_guid", CT_GUID, 0, 0, NULL, NULL, NULL, _retrieve_guid_ }, + { NULL } +}; + +const GUID* +gnc_sql_load_tx_guid( const GncSqlBackend* be, GncSqlRow* row ) +{ + static GUID guid; + + g_return_val_if_fail( be != NULL, NULL ); + g_return_val_if_fail( row != NULL, NULL ); + + gnc_sql_load_object( be, row, NULL, &guid, tx_guid_table ); + + return &guid; +} + +void +gnc_sql_load_object( const GncSqlBackend* be, GncSqlRow* row, + QofIdTypeConst obj_name, gpointer pObject, + const GncSqlColumnTableEntry* table_row ) +{ + int col; + QofSetterFunc setter; + col_type_handler_t* pHandler; + + g_return_if_fail( be != NULL ); + g_return_if_fail( row != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + + for( col = 0; table_row[col].col_name != NULL; col++ ) { + if( (table_row[col].flags & COL_AUTOINC) != 0 ) { + setter = set_autoinc_id; + } else if( table_row[col].qof_param_name != NULL ) { + setter = qof_class_get_parameter_setter( obj_name, + table_row[col].qof_param_name ); + } else { + setter = table_row[col].setter; + } + pHandler = get_handler( &table_row[col] ); + pHandler->load_fn( be, row, setter, pObject, &table_row[col] ); + } +} + +/* ================================================================= */ +GncSqlStatement* +gnc_sql_create_select_statement( const GncSqlBackend* be, const gchar* table_name ) +{ + gchar* sql; + + g_return_val_if_fail( be != NULL, NULL ); + g_return_val_if_fail( table_name != NULL, NULL ); + + sql = g_strdup_printf( "SELECT * FROM %s", table_name ); + return gnc_sql_create_statement_from_sql( be, sql ); +} + +static GncSqlStatement* +create_single_col_select_statement( const GncSqlBackend* be, + const gchar* table_name, + const GncSqlColumnTableEntry* table_row ) +{ + gchar* sql; + + g_return_val_if_fail( be != NULL, NULL ); + g_return_val_if_fail( table_name != NULL, NULL ); + g_return_val_if_fail( table_row != NULL, NULL ); + + sql = g_strdup_printf( "SELECT %s FROM %s", table_row->col_name, table_name ); + return gnc_sql_create_statement_from_sql( be, sql ); +} + +/* ================================================================= */ + +GncSqlResult* +gnc_sql_execute_select_statement( GncSqlBackend* be, GncSqlStatement* stmt ) +{ + GError* error = NULL; + GncSqlResult* result; + + g_return_val_if_fail( be != NULL, NULL ); + g_return_val_if_fail( stmt != NULL, NULL ); + + result = gnc_sql_connection_execute_select_statement( be->conn, stmt ); + if( error != NULL ) { + PERR( "SQL error: %s\n%s\n", gnc_sql_statement_to_sql( stmt ), error->message ); + qof_backend_set_error( &be->be, ERR_BACKEND_SERVER_ERR ); + } + + return result; +} + +GncSqlStatement* +gnc_sql_create_statement_from_sql( const GncSqlBackend* be, gchar* sql ) +{ + GError* error = NULL; + GncSqlStatement* stmt; + + g_return_val_if_fail( be != NULL, NULL ); + g_return_val_if_fail( sql != NULL, NULL ); + + stmt = gnc_sql_connection_create_statement_from_sql( be->conn, sql ); + if( stmt == NULL ) { + PERR( "SQL error: %s\n%s\n", sql, error->message ); + } + + return stmt; +} + +GncSqlResult* +gnc_sql_execute_select_sql( const GncSqlBackend* be, gchar* sql ) +{ + GncSqlStatement* stmt; + GError* error = NULL; + GncSqlResult* result = NULL; + + g_return_val_if_fail( be != NULL, NULL ); + g_return_val_if_fail( sql != NULL, NULL ); + + stmt = gnc_sql_create_statement_from_sql( be, sql ); + if( stmt == NULL ) { + return NULL; + } + result = gnc_sql_connection_execute_select_statement( be->conn, stmt ); + if( error != NULL ) { + PERR( "SQL error: %s\n%s\n", sql, error->message ); + } + + return result; +} + +static gint +execute_nonselect_sql( const GncSqlBackend* be, gchar* sql ) +{ + GncSqlStatement* stmt; + gint result; + + g_return_val_if_fail( be != NULL, 0 ); + g_return_val_if_fail( sql != NULL, 0 ); + + stmt = gnc_sql_create_statement_from_sql( be, sql ); + if( stmt == NULL ) { + return 0; + } + result = gnc_sql_connection_execute_nonselect_statement( be->conn, stmt ); + gnc_sql_statement_dispose( stmt ); + return result; +} + +static int +execute_statement_get_count( GncSqlBackend* be, GncSqlStatement* stmt ) +{ + GncSqlResult* result; + int count = 0; + + g_return_val_if_fail( be != NULL, 0 ); + g_return_val_if_fail( stmt != NULL, 0 ); + + result = gnc_sql_execute_select_statement( be, stmt ); + if( result != NULL ) { + count = gnc_sql_result_get_num_rows( result ); + gnc_sql_result_dispose( result ); + } + + return count; +} + +guint +gnc_sql_append_guid_list_to_sql( GString* sql, GList* list, guint maxCount ) +{ + gchar guid_buf[GUID_ENCODING_LENGTH+1]; + gboolean first_guid = TRUE; + guint count; + + g_return_val_if_fail( sql != NULL, 0 ); + + if( list == NULL ) return 0; + + for( count = 0; list != NULL && count < maxCount; list = list->next, count++ ) { + QofInstance* inst = QOF_INSTANCE(list->data); + guid_to_string_buff( qof_instance_get_guid( inst ), guid_buf ); + + if( !first_guid ) { + g_string_append( sql, "," ); + } + g_string_append( sql, "'" ); + g_string_append( sql, guid_buf ); + g_string_append( sql, "'" ); + first_guid = FALSE; + } + + return count; +} +/* ================================================================= */ + +gboolean +gnc_sql_object_is_it_in_db( GncSqlBackend* be, const gchar* table_name, + QofIdTypeConst obj_name, gpointer pObject, + const GncSqlColumnTableEntry* table ) +{ + GncSqlStatement* sqlStmt; + int count; + col_type_handler_t* pHandler; + GSList* list = NULL; + + g_return_val_if_fail( be != NULL, FALSE ); + g_return_val_if_fail( table_name != NULL, FALSE ); + g_return_val_if_fail( obj_name != NULL, FALSE ); + g_return_val_if_fail( pObject != NULL, FALSE ); + g_return_val_if_fail( table != NULL, FALSE ); + + /* SELECT * FROM */ + sqlStmt = create_single_col_select_statement( be, table_name, table ); + + /* WHERE */ + pHandler = get_handler( table ); + pHandler->add_gvalue_to_slist_fn( be, obj_name, pObject, table, &list ); + gnc_sql_statement_add_where_cond( sqlStmt, obj_name, pObject, &table[0], (GValue*)(list->data) ); + + count = execute_statement_get_count( be, sqlStmt ); + gnc_sql_statement_dispose( sqlStmt ); + if( count == 0 ) { + return FALSE; + } else { + return TRUE; + } +} + +gboolean +gnc_sql_do_db_operation( GncSqlBackend* be, + E_DB_OPERATION op, + const gchar* table_name, + QofIdTypeConst obj_name, gpointer pObject, + const GncSqlColumnTableEntry* table ) +{ + GncSqlStatement* stmt; + + g_return_val_if_fail( be != NULL, FALSE ); + g_return_val_if_fail( table_name != NULL, FALSE ); + g_return_val_if_fail( obj_name != NULL, FALSE ); + g_return_val_if_fail( pObject != NULL, FALSE ); + g_return_val_if_fail( table != NULL, FALSE ); + + if( op == OP_DB_INSERT ) { + stmt = build_insert_statement( be, table_name, obj_name, pObject, table ); + } else if( op == OP_DB_UPDATE ) { + stmt = build_update_statement( be, table_name, obj_name, pObject, table ); + } else if( op == OP_DB_DELETE ) { + stmt = build_delete_statement( be, table_name, obj_name, pObject, table ); + } else { + g_assert( FALSE ); + } + if( stmt != NULL ) { + gnc_sql_connection_execute_nonselect_statement( be->conn, stmt ); + gnc_sql_statement_dispose( stmt ); + + return TRUE; + } else { + return FALSE; + } +} + +static GSList* +create_gslist_from_values( GncSqlBackend* be, + QofIdTypeConst obj_name, gpointer pObject, + const GncSqlColumnTableEntry* table ) +{ + GSList* list = NULL; + col_type_handler_t* pHandler; + const GncSqlColumnTableEntry* table_row; + + for( table_row = table; table_row->col_name != NULL; table_row++ ) { + if(( table_row->flags & COL_AUTOINC ) == 0 ) { + pHandler = get_handler( table_row ); + pHandler->add_gvalue_to_slist_fn( be, obj_name, pObject, table_row, &list ); + } + } + + return list; +} + +gchar* +gnc_sql_get_sql_value( const GncSqlConnection* conn, const GValue* value ) +{ + if( value != NULL && G_IS_VALUE( value ) ) { + if( G_VALUE_HOLDS_STRING(value) ) { + if( g_value_get_string( value ) != NULL ) { + gchar* before_str; + gchar* after_str; + before_str = g_value_dup_string( value ); + after_str = gnc_sql_connection_quote_string( conn, before_str ); + g_free( before_str ); + return after_str; + } else { + return g_strdup( "NULL" ); + } + } else if( g_value_type_transformable( G_VALUE_TYPE(value), G_TYPE_STRING ) ) { + GValue* string; + gchar* str; + + string = g_value_init( g_new0( GValue, 1 ), G_TYPE_STRING ); + g_value_transform( value, string ); + str = g_value_dup_string( string ); + g_value_unset( string ); + g_free( string ); + return str; + } else { + PWARN( "not transformable, gtype = '%s'\n", g_type_name( G_VALUE_TYPE(value) ) ); + return "$$$"; + } + } else { + PWARN( "value is NULL or not G_IS_VALUE()\n" ); + return ""; + } +} + +static GncSqlStatement* +build_insert_statement( GncSqlBackend* be, + const gchar* table_name, + QofIdTypeConst obj_name, gpointer pObject, + const GncSqlColumnTableEntry* table ) +{ + GncSqlStatement* stmt; + GString* sql; + GSList* values; + GSList* node; + gchar* sqlbuf; + + g_return_val_if_fail( be != NULL, NULL ); + g_return_val_if_fail( table_name != NULL, NULL ); + g_return_val_if_fail( obj_name != NULL, NULL ); + g_return_val_if_fail( pObject != NULL, NULL ); + g_return_val_if_fail( table != NULL, NULL ); + + sqlbuf = g_strdup_printf( "INSERT INTO %s VALUES(", table_name ); + sql = g_string_new( sqlbuf ); + g_free( sqlbuf ); + values = create_gslist_from_values( be, obj_name, pObject, table ); + for( node = values; node != NULL; node = node->next ) { + GValue* value = (GValue*)node->data; + gchar* value_str; + if( node != values ) { + g_string_append( sql, "," ); + } + value_str = gnc_sql_get_sql_value( be->conn, value ); + g_string_append( sql, value_str ); + g_free( value_str ); + g_value_reset( value ); + g_free( value ); + } + g_slist_free( values ); + g_string_append( sql, ")" ); + + stmt = gnc_sql_connection_create_statement_from_sql( be->conn, sql->str ); + return stmt; +} + +static GncSqlStatement* +build_update_statement( GncSqlBackend* be, + const gchar* table_name, + QofIdTypeConst obj_name, gpointer pObject, + const GncSqlColumnTableEntry* table ) +{ + GncSqlStatement* stmt; + GString* sql; + GSList* values; + GList* colnames = NULL; + GSList* value; + GList* colname; + gboolean firstCol; + const GncSqlColumnTableEntry* table_row = table; + gchar* sqlbuf; + + g_return_val_if_fail( be != NULL, NULL ); + g_return_val_if_fail( table_name != NULL, NULL ); + g_return_val_if_fail( obj_name != NULL, NULL ); + g_return_val_if_fail( pObject != NULL, NULL ); + g_return_val_if_fail( table != NULL, NULL ); + + // Get all col names and all values + for( ; table_row->col_name != NULL; table_row++ ) { + col_type_handler_t* pHandler; + + // Add col names to the list + pHandler = get_handler( table_row ); + pHandler->add_colname_to_list_fn( table_row, &colnames ); + } + values = create_gslist_from_values( be, obj_name, pObject, table ); + + // Create the SQL statement + sqlbuf = g_strdup_printf( "UPDATE %s SET ", table_name ); + sql = g_string_new( sqlbuf ); + g_free( sqlbuf ); + + firstCol = TRUE; + for( colname = colnames->next, value = values->next; + colname != NULL && value != NULL; + colname = colname->next, value = value->next ) { + gchar* value_str; + if( !firstCol ) { + g_string_append( sql, "," ); + } + g_string_append( sql, (gchar*)colname->data ); + g_string_append( sql, "=" ); + value_str = gnc_sql_get_sql_value( be->conn, (GValue*)(value->data) ); + g_string_append( sql, value_str ); + g_free( value_str ); + firstCol = FALSE; + } + g_list_free( colnames ); + if( value != NULL || colname != NULL ) { + PERR( "Mismatch in number of column names and values" ); + } + + stmt = gnc_sql_connection_create_statement_from_sql( be->conn, sql->str ); + gnc_sql_statement_add_where_cond( stmt, obj_name, pObject, &table[0], (GValue*)(values->data) ); + g_slist_free( values ); + + return stmt; +} + +static GncSqlStatement* +build_delete_statement( GncSqlBackend* be, + const gchar* table_name, + QofIdTypeConst obj_name, gpointer pObject, + const GncSqlColumnTableEntry* table ) +{ + GncSqlStatement* stmt; + GString* sql; + col_type_handler_t* pHandler; + GSList* list = NULL; + gchar* sqlbuf; + + g_return_val_if_fail( be != NULL, NULL ); + g_return_val_if_fail( table_name != NULL, NULL ); + g_return_val_if_fail( obj_name != NULL, NULL ); + g_return_val_if_fail( pObject != NULL, NULL ); + g_return_val_if_fail( table != NULL, NULL ); + + sqlbuf = g_strdup_printf( "DELETE FROM %s ", table_name ); + sql = g_string_new( sqlbuf ); + g_free( sqlbuf ); + stmt = gnc_sql_connection_create_statement_from_sql( be->conn, sql->str ); + + /* WHERE */ + pHandler = get_handler( table ); + pHandler->add_gvalue_to_slist_fn( be, obj_name, pObject, table, &list ); + gnc_sql_statement_add_where_cond( stmt, obj_name, pObject, &table[0], (GValue*)(list->data) ); + + return stmt; +} + +/* ================================================================= */ +void +gnc_sql_commit_standard_item( GncSqlBackend* be, QofInstance* inst, const gchar* tableName, + QofIdTypeConst obj_name, const GncSqlColumnTableEntry* col_table ) +{ + const GUID* guid; + gboolean is_infant; + gint op; + + 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; + } + (void)gnc_sql_do_db_operation( be, op, tableName, obj_name, inst, col_table ); + + // Now, commit any slots + guid = qof_instance_get_guid( inst ); + if( !qof_instance_get_destroying(inst) ) { + gnc_sql_slots_save( be, guid, is_infant, qof_instance_get_slots( inst ) ); + } else { + gnc_sql_slots_delete( be, guid ); + } +} + +/* ================================================================= */ + +static gboolean +create_table( const GncSqlBackend* be, const gchar* table_name, + const GncSqlColumnTableEntry* col_table ) +{ + GList* col_info_list = NULL; + + g_return_val_if_fail( be != NULL, FALSE ); + g_return_val_if_fail( table_name != NULL, FALSE ); + g_return_val_if_fail( col_table != NULL, FALSE ); + + for( ; col_table->col_name != NULL; col_table++ ) { + col_type_handler_t* pHandler; + + pHandler = get_handler( col_table ); + pHandler->add_col_info_to_list_fn( be, col_table, &col_info_list ); + } + gnc_sql_connection_create_table( be->conn, table_name, col_info_list ); + return TRUE; +} + +gboolean +gnc_sql_create_table( const GncSqlBackend* be, const gchar* table_name, + gint table_version, const GncSqlColumnTableEntry* col_table ) +{ + gboolean ok; + + ok = create_table( be, table_name, col_table ); + if( ok ) { + register_table_version( be, table_name, table_version ); + } + return ok; +} + +void +gnc_sql_create_index( const GncSqlBackend* be, const gchar* index_name, + const gchar* table_name, + const GncSqlColumnTableEntry* col_table ) +{ + g_return_if_fail( be != NULL ); + g_return_if_fail( index_name != NULL ); + g_return_if_fail( table_name != NULL ); + g_return_if_fail( col_table != NULL ); + + gnc_sql_connection_create_index( be->conn, index_name, table_name, + col_table ); +} + +gint +gnc_sql_get_table_version( const GncSqlBackend* be, const gchar* table_name ) +{ + g_return_val_if_fail( be != NULL, 0 ); + g_return_val_if_fail( table_name != NULL, 0 ); + + /* If the db is pristine because it's being saved, the table does not + * exist. This gets around a GDA-3 bug where deleting all tables and + * updating the meta-data leaves the meta-data still thinking 1 table + * exists. + */ + if( be->is_pristine_db ) { + return 0; + } + + return get_table_version( be, table_name ); + } + +/* ================================================================= */ +#define VERSION_TABLE_NAME "versions" +#define MAX_TABLE_NAME_LEN 50 +#define TABLE_COL_NAME "table_name" +#define VERSION_COL_NAME "table_version" + +static GncSqlColumnTableEntry version_table[] = +{ + { TABLE_COL_NAME, CT_STRING, MAX_TABLE_NAME_LEN }, + { VERSION_COL_NAME, CT_INT }, + { NULL } +}; + +/** + * Sees if the version table exists, and if it does, loads the info into + * the version hash table. Otherwise, it creates an empty version table. + * + * @param be Backend struct + */ +void +gnc_sql_init_version_info( GncSqlBackend* be ) +{ + g_return_if_fail( be != NULL ); + + be->versions = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, NULL ); + + if( gnc_sql_connection_does_table_exist( be->conn, VERSION_TABLE_NAME ) ) { + GncSqlResult* result; + gchar* sql; + + sql = g_strdup_printf( "SELECT * FROM %s", VERSION_TABLE_NAME ); + result = gnc_sql_execute_select_sql( be, sql ); + if( result != NULL ) { + const GValue* name; + const GValue* version; + GncSqlRow* row; + + row = gnc_sql_result_get_first_row( result ); + while( row != NULL ) { + name = gnc_sql_row_get_value_at_col_name( row, TABLE_COL_NAME ); + version = gnc_sql_row_get_value_at_col_name( row, VERSION_COL_NAME ); + g_hash_table_insert( be->versions, + g_strdup( g_value_get_string( name ) ), + GINT_TO_POINTER(g_value_get_int( version )) ); + row = gnc_sql_result_get_next_row( result ); + } + gnc_sql_result_dispose( result ); + } + } else { + gboolean ok; + + ok = create_table( be, VERSION_TABLE_NAME, version_table ); + } +} + +/** + * Resets the version table information by removing all version table info. + * It also recreates the version table in the db. + * + * @param be Backend struct + */ +static void +reset_version_info( GncSqlBackend* be ) +{ + gboolean ok; + + g_return_if_fail( be != NULL ); + + ok = create_table( be, VERSION_TABLE_NAME, version_table ); + if( be->versions == NULL ) { + be->versions = g_hash_table_new_full( g_str_hash, g_str_equal, g_free, NULL ); + } else { + g_hash_table_remove_all( be->versions ); + } +} + +/** + * Finalizes the version table info by destroying the hash table. + * + * @param be Backend struct + */ +void +gnc_sql_finalize_version_info( GncSqlBackend* be ) +{ + g_return_if_fail( be != NULL ); + + g_hash_table_destroy( be->versions ); +} + +/** + * 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 + */ +static void +register_table_version( const GncSqlBackend* be, const gchar* table_name, gint version ) +{ + gchar* sql; + gint cur_version; + + g_return_if_fail( be != NULL ); + g_return_if_fail( table_name != NULL ); + g_return_if_fail( version > 0 ); + + cur_version = get_table_version( be, table_name ); + if( cur_version != version ) { + if( cur_version == 0 ) { + sql = g_strdup_printf( "INSERT INTO %s VALUES('%s',%d)", VERSION_TABLE_NAME, + table_name, version ); + } else { + sql = g_strdup_printf( "UPDATE %s SET %s=%d WHERE %s='%s'", VERSION_TABLE_NAME, + VERSION_COL_NAME, version, + TABLE_COL_NAME, table_name ); + } + execute_nonselect_sql( be, sql ); + } + + g_hash_table_insert( be->versions, g_strdup( table_name ), GINT_TO_POINTER(version) ); +} + +/** + * Returns the registered version number for a table. + * + * @param be Backend struct + * @param table_name Table name + * @return Version number + */ +static gint +get_table_version( const GncSqlBackend* be, const gchar* table_name ) +{ + g_return_val_if_fail( be != NULL, 0 ); + g_return_val_if_fail( table_name != NULL, 0 ); + + return GPOINTER_TO_INT(g_hash_table_lookup( be->versions, table_name )); +} +/* ========================== END OF FILE ===================== */ diff --git a/src/backend/sql/gnc-backend-sql.h b/src/backend/sql/gnc-backend-sql.h new file mode 100644 index 0000000000..76baa784ce --- /dev/null +++ b/src/backend/sql/gnc-backend-sql.h @@ -0,0 +1,592 @@ +/******************************************************************** + * gnc-backend-sql.h: load and save data to SQL * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ + +/** + * @defgroup SQLBE SQL Backend Core + @{ +*/ + +/** @addtogroup Columns Columns + @ingroup SQLBE +*/ + +/** + @} +*/ + +/** @addtogroup SQLBE + @{ +*/ +/** @addtogroup SQLBE + + * The SQL backend core is a library which can form the core for a QOF + * backend based on an SQL library. + +*/ + +/** @file gnc-backend-sql.h + * @brief load and save data to SQL + * @author Copyright (c) 2006-2008 Phil Longstaff + * + @} + */ + +#ifndef GNC_BACKEND_SQL_H_ +#define GNC_BACKEND_SQL_H_ + +#include "qof.h" +#include "qofbackend-p.h" +#include + +typedef struct GncSqlConnection GncSqlConnection; + +/** + * @struct GncSqlBackend + * + * Main SQL backend structure. + */ +struct GncSqlBackend +{ + QofBackend be; /**< QOF backend */ + GncSqlConnection* conn; /**< SQL connection */ + QofBook *primary_book; /**< The primary, main open book */ + gboolean loading; /**< We are performing an initial load */ + gboolean in_query; /**< We are processing a query */ + gboolean is_pristine_db; /**< Are we saving to a new pristine db? */ + gint obj_total; /**< Total # of objects (for percentage calculation) */ + gint operations_done; /**< Number of operations (save/load) done */ + GHashTable* versions; /**< Version number for each table */ +}; +typedef struct GncSqlBackend GncSqlBackend; + +/** + * Initialize the SQL backend. + * + * @param be SQL backend + */ +void gnc_sql_init( GncSqlBackend* be ); + +/** + * Load the contents of an SQL database into a book. + * + * @param be SQL backend + * @param book Book to be loaded + */ +void gnc_sql_load( GncSqlBackend* be, QofBook *book ); + +/** + * Save the contents of a book to an SQL database. + * + * @param be SQL backend + * @param book Book to be saved + */ +void gnc_sql_sync_all( GncSqlBackend* be, QofBook *book ); + +/** + * An object is about to be edited. + * + * @param be SQL backend + * @param inst Object being edited + */ +void gnc_sql_begin_edit( GncSqlBackend* be, QofInstance *inst ); + +/** + * Object editing has been cancelled. + * + * @param be SQL backend + * @param inst Object being edited + */ +void gnc_sql_rollback_edit( GncSqlBackend* qbe, QofInstance *inst ); + +/** + * Object editting is complete and the object should be saved. + * + * @param be SQL backend + * @param inst Object being edited + */ +void gnc_sql_commit_edit( GncSqlBackend* qbe, QofInstance *inst ); + +/** + */ +typedef struct GncSqlColumnTableEntry GncSqlColumnTableEntry; +typedef struct GncSqlStatement GncSqlStatement; +typedef struct GncSqlResult GncSqlResult; +typedef struct GncSqlRow GncSqlRow; + +/** + *@struct GncSqlStatement + * + * Struct which represents an SQL statement. SQL backends must provide a + * structure which implements all of the functions. + */ +struct GncSqlStatement +{ + void (*dispose)( GncSqlStatement* ); + gchar* (*toSql)( GncSqlStatement* ); + void (*addWhereCond)( GncSqlStatement*, QofIdTypeConst, gpointer, const GncSqlColumnTableEntry*, GValue* ); +}; +#define gnc_sql_statement_dispose(STMT) \ + (STMT)->dispose(STMT) +#define gnc_sql_statement_to_sql(STMT) \ + (STMT)->toSql(STMT) +#define gnc_sql_statement_add_where_cond(STMT,TYPENAME,OBJ,COLDESC,VALUE) \ + (STMT)->addWhereCond(STMT, TYPENAME, OBJ, COLDESC, VALUE) + +/** + * @struct GncSqlConnection + * + * Struct which represents the connection to an SQL database. SQL backends + * must provide a structure which implements all of the functions. + */ +struct GncSqlConnection +{ + void (*dispose)( GncSqlConnection* ); + GncSqlResult* (*executeSelectStatement)( GncSqlConnection*, GncSqlStatement* ); + gint (*executeNonSelectStatement)( GncSqlConnection*, GncSqlStatement* ); + GncSqlStatement* (*createStatementFromSql)( GncSqlConnection*, gchar* ); + gboolean (*doesTableExist)( GncSqlConnection*, const gchar* ); + void (*beginTransaction)( GncSqlConnection* ); + void (*rollbackTransaction)( GncSqlConnection* ); + void (*commitTransaction)( GncSqlConnection* ); + const gchar* (*getColumnTypeName)( GncSqlConnection*, GType, gint size ); + void (*createTable)( GncSqlConnection*, const gchar*, const GList* ); + void (*createIndex)( GncSqlConnection*, const gchar*, const gchar*, const GncSqlColumnTableEntry* ); + gchar* (*quoteString)( const GncSqlConnection*, gchar* ); +}; +#define gnc_sql_connection_dispose(CONN) (CONN)->dispose(CONN) +#define gnc_sql_connection_execute_select_statement(CONN,STMT) \ + (CONN)->executeSelectStatement(CONN,STMT) +#define gnc_sql_connection_execute_nonselect_statement(CONN,STMT) \ + (CONN)->executeNonSelectStatement(CONN,STMT) +#define gnc_sql_connection_create_statement_from_sql(CONN,SQL) \ + (CONN)->createStatementFromSql(CONN,SQL) +#define gnc_sql_connection_does_table_exist(CONN,NAME) \ + (CONN)->doesTableExist(CONN,NAME) +#define gnc_sql_connection_begin_transaction(CONN) \ + (CONN)->beginTransaction(CONN) +#define gnc_sql_connection_rollback_transaction(CONN) \ + (CONN)->rollbackTransaction(CONN) +#define gnc_sql_connection_commit_transaction(CONN) \ + (CONN)->commitTransaction(CONN) +#define gnc_sql_connection_get_column_type_name(CONN,TYPE,SIZE) \ + (CONN)->getColumnTypeName(CONN,TYPE,SIZE) +#define gnc_sql_connection_create_table(CONN,NAME,COLLIST) \ + (CONN)->createTable(CONN,NAME,COLLIST) +#define gnc_sql_connection_create_index(CONN,INDEXNAME,TABLENAME,COLTABLE) \ + (CONN)->createIndex(CONN,INDEXNAME,TABLENAME,COLTABLE) +#define gnc_sql_connection_quote_string(CONN,STR) \ + (CONN)->quoteString(CONN,STR) + +/** + * @struct GncSqlRow + * + * Struct used to represent a row in the result of an SQL SELECT statement. + * SQL backends must provide a structure which implements all of the functions. + */ +struct GncSqlRow +{ + const GValue* (*getValueAtColName)( GncSqlRow*, const gchar* ); + void (*dispose)( GncSqlRow* ); +}; +#define gnc_sql_row_get_value_at_col_name(ROW,N) \ + (ROW)->getValueAtColName(ROW,N) +#define gnc_sql_row_dispose(ROW) \ + (ROW)->dispose(ROW) + +/** + * @struct GncSqlResult + * + * Struct used to represent the result of an SQL SELECT statement. SQL + * backends must provide a structure which implements all of the functions. + */ +struct GncSqlResult +{ + gint (*getNumRows)( GncSqlResult* ); + GncSqlRow* (*getFirstRow)( GncSqlResult* ); + GncSqlRow* (*getNextRow)( GncSqlResult* ); + void (*dispose)( GncSqlResult* ); +}; +#define gnc_sql_result_get_num_rows(RESULT) \ + (RESULT)->getNumRows(RESULT) +#define gnc_sql_result_get_first_row(RESULT) \ + (RESULT)->getFirstRow(RESULT) +#define gnc_sql_result_get_next_row(RESULT) \ + (RESULT)->getNextRow(RESULT) +#define gnc_sql_result_dispose(RESULT) \ + (RESULT)->dispose(RESULT) + +/** + * @struct GncSqlObjectBackend + * + * Struct used to handle a specific engine object type for an SQL backend. + * This * handler should be registered with qof_object_register_backend(). + * + * commit() - commit an object to the db + * initial_load() - load stuff when new db opened + * create_tables() - create any db tables + * compile_query() - compile a backend object query + * run_query() - run a compiled query + * free_query() - free a compiled query + * write() - write all objects + */ +typedef struct +{ + int version; /**< Backend version number */ + const gchar * type_name; /**< Engine object type name */ + /** Commit an instance of this object to the database */ + void (*commit)( GncSqlBackend* be, QofInstance* inst ); + /** Load all objects of this type from the database */ + void (*initial_load)( GncSqlBackend* be ); + /** Create database tables for this object */ + void (*create_tables)( GncSqlBackend* be ); + /** Compile a query on these objects */ + gpointer (*compile_query)( GncSqlBackend* be, QofQuery* pQuery ); + /** Run a query on these objects */ + void (*run_query)( GncSqlBackend* be, gpointer pQuery ); + /** Free a query on these objects */ + void (*free_query)( GncSqlBackend* be, gpointer pQuery ); + /** Write all objects of this type to the database */ + void (*write)( GncSqlBackend* be ); +} GncSqlObjectBackend; +#define GNC_SQL_BACKEND "gnc:sql:1" +#define GNC_SQL_BACKEND_VERSION 1 + +/** + * @struct GncSqlColumnInfo + */ +typedef struct { + const gchar* name; /**< Column name */ + const gchar* type_name; /**< Column SQL type name */ + gint size; /**< Column size (string types) */ + gboolean is_primary_key; + gboolean null_allowed; +} GncSqlColumnInfo; + +// Type for conversion of db row to object. +#define CT_STRING "ct_string" +#define CT_GUID "ct_guid" +#define CT_INT "ct_int" +#define CT_INT64 "ct_int64" +#define CT_TIMESPEC "ct_timespec" +#define CT_GDATE "ct_gdate" +#define CT_NUMERIC "ct_numeric" +#define CT_DOUBLE "ct_double" +#define CT_BOOLEAN "ct_boolean" +#define CT_ACCOUNTREF "ct_accountref" +#define CT_COMMODITYREF "ct_commodityref" +#define CT_TXREF "ct_txref" +#define CT_LOTREF "ct_lotref" + +/** + * @struct GncSqlColumnTableEntry + * + * The GncSqlColumnTableEntry struct contains all of the information + * required to copy information between an object and the database. + * The database description for an object consists of an array of + * GncSqlColumnTableEntry objects, with a final member having col_name == NULL. + */ +struct GncSqlColumnTableEntry { + const gchar* col_name; /**< Column name */ + const gchar* col_type; /**< Column type */ + gint size; /**< Column size in bytes, for string columns */ +#define COL_PKEY 0x01 +#define COL_NNUL 0x02 +#define COL_UNIQUE 0x04 +#define COL_AUTOINC 0x08 + gint flags; /**< Column flags */ + const gchar* gobj_param_name; /**< If non-null, g_object param name */ + const gchar* qof_param_name; /**< If non-null, qof parameter name */ + QofAccessFunc getter; /**< General access function */ + QofSetterFunc setter; /**< General setter function */ +}; + +typedef enum { + OP_DB_INSERT, + OP_DB_UPDATE, + OP_DB_DELETE +} E_DB_OPERATION; + +typedef void (*GNC_SQL_LOAD_FN)( const GncSqlBackend* be, + GncSqlRow* row, + QofSetterFunc setter, gpointer pObject, + const GncSqlColumnTableEntry* table ); +typedef void (*GNC_SQL_ADD_COL_INFO_TO_LIST_FN)( const GncSqlBackend* be, + const GncSqlColumnTableEntry* table_row, + GList** pList ); +typedef void (*GNC_SQL_ADD_COLNAME_TO_LIST_FN)( const GncSqlColumnTableEntry* table_row, GList** pList ); +typedef void (*GNC_SQL_ADD_GVALUE_TO_SLIST_FN)( const GncSqlBackend* be, + QofIdTypeConst obj_name, const gpointer pObject, + const GncSqlColumnTableEntry* table_row, GSList** pList ); + +typedef struct { + GNC_SQL_LOAD_FN load_fn; + GNC_SQL_ADD_COL_INFO_TO_LIST_FN add_col_info_to_list_fn; + GNC_SQL_ADD_COLNAME_TO_LIST_FN add_colname_to_list_fn; + GNC_SQL_ADD_GVALUE_TO_SLIST_FN add_gvalue_to_slist_fn; +} col_type_handler_t; + +/** + * Returns the QOF access function for a column. + * + * @param obj_name QOF object type name + * @param table_row DB table column + * @return Access function + */ +QofAccessFunc gnc_sql_get_getter( QofIdTypeConst obj_name, const GncSqlColumnTableEntry* table_row ); + +/** + * Adds a column name to a list. If the column type spans multiple columns, + * all of the column names for the pieces are added. + * + * @param table_row DB table column + * @pList List + */ +void gnc_sql_add_colname_to_list( const GncSqlColumnTableEntry* table_row, GList** pList ); + +/** + * Performs an operation on the database. + * + * @param be SQL backend struct + * @param op Operation type + * @param table_name SQL table name + * @param obj_name QOF object type name + * @param pObject Gnucash object + * @param table DB table description + * @return TRUE if successful, FALSE if not + */ +gboolean gnc_sql_do_db_operation( GncSqlBackend* be, + E_DB_OPERATION op, + const gchar* table_name, + QofIdTypeConst obj_name, + gpointer pObject, + const GncSqlColumnTableEntry* table ); + +/** + * Execute an SQL SELECT statement. + * + * @param be SQL backend struct + * @param statement Statement + * @return Results + */ +GncSqlResult* gnc_sql_execute_select_statement( GncSqlBackend* be, GncSqlStatement* statement ); + +/** + * Executes an SQL SELECT statement from an SQL char string. + * + * @param be SQL backend struct + * @param sql SQL SELECT string + * @return Results + */ +GncSqlResult* gnc_sql_execute_select_sql( const GncSqlBackend* be, gchar* sql ); + +/** + * Creates a statement from an SQL char string. + * + * @param be SQL backend struct + * @param sql SQL char string + * @return Statement + */ +GncSqlStatement* gnc_sql_create_statement_from_sql( const GncSqlBackend* be, gchar* sql ); + +/** + * Loads a Gnucash object from the database. + * + * @param be SQL backend struct + * @param row DB result row + * @param obj_name QOF object type name + * @param pObject Object to be loaded + * @param table DB table description + */ +void gnc_sql_load_object( const GncSqlBackend* be, GncSqlRow* row, + QofIdTypeConst obj_name, gpointer pObject, + const GncSqlColumnTableEntry* table ); + +/** + * Checks whether an object is in the database or not. + * + * @param be SQL backend struct + * @param table_name DB table name + * @param obj_name QOF object type name + * @param pObject Object to be checked + * @param table DB table description + * @return TRUE if the object is in the database, FALSE otherwise + */ +gboolean gnc_sql_object_is_it_in_db( GncSqlBackend* be, + const gchar* table_name, + QofIdTypeConst obj_name, const gpointer pObject, + const GncSqlColumnTableEntry* table ); + +/** + * Returns the version number for a DB table. + * + * @param be SQL backend struct + * @param table_name Table name + * @return Version number, or 0 if the table does not exist + */ +gint gnc_sql_get_table_version( const GncSqlBackend* be, const gchar* table_name ); + +/** + * Creates a table in the database + * + * @param be SQL backend struct + * @param table_name Table name + * @param table_version Table version + * @param col_table DB table description + * @return TRUE if successful, FALSE if unsuccessful + */ +gboolean gnc_sql_create_table( const GncSqlBackend* be, const gchar* table_name, + gint table_version, const GncSqlColumnTableEntry* col_table ); + +/** + * Creates an index in the database + * + * @param be SQL backend struct + * @param index_name Index name + * @param table_name Table name + * @param col_table Columns that the index should index + */ +void gnc_sql_create_index( const GncSqlBackend* be, const gchar* index_name, + const gchar* table_name, const GncSqlColumnTableEntry* col_table ); + +/** + * Loads the object guid from a database row. The table must have a column + * named "guid" with type CT_GUID. + * + * @param be SQL backend struct + * @param row Database row + * @return GUID + */ +const GUID* gnc_sql_load_guid( const GncSqlBackend* be, GncSqlRow* row ); + +/** + * Loads the transaction guid from a database row. The table must have a column + * named "tx_guid" with type CT_GUID. + * + * @param be SQL backend struct + * @param row Database row + * @return GUID + */ +const GUID* gnc_sql_load_tx_guid( const GncSqlBackend* be, GncSqlRow* row ); + +/** + * Creates a basic SELECT statement for a table. + * + * @param be SQL backend struct + * @param table_name Table name + * @return Statement + */ +GncSqlStatement* gnc_sql_create_select_statement( const GncSqlBackend* be, + const gchar* table_name ); + +/** + * Registers a column handler for a new column type. + * + * @param colType Column type + * @param handler Column handler + */ +void gnc_sql_register_col_type_handler( const gchar* colType, const col_type_handler_t* handler ); + +/** + * Adds a GValue for an object reference GUID to the end of a GSList. + * + * @param be SQL backend struct + * @param obj_name QOF object type name + * @param pObject Object + * @param table_row DB table column description + * @param pList List + */ +void gnc_sql_add_gvalue_objectref_guid_to_slist( const GncSqlBackend* be, + QofIdTypeConst obj_name, const gpointer pObject, + const GncSqlColumnTableEntry* table_row, GSList** pList ); + +/** + * Adds a column info structure for an object reference GUID to the end of a + * GList. + * + * @param be SQL backend struct + * @param table_row DB table column description + * @param pList List + */ +void gnc_sql_add_objectref_guid_col_info_to_list( const GncSqlBackend* be, + const GncSqlColumnTableEntry* table_row, GList** pList ); + +/** + * Appends the ascii strings for a list of GUIDs to the end of an SQL string. + * + * @param str SQL string + * @param list List of GUIDs + * @param maxCount Max # of GUIDs to append + * @return Number of GUIDs appended + */ +guint gnc_sql_append_guid_list_to_sql( GString* str, GList* list, guint maxCount ); + +/** + * Appends column names for a subtable to the end of a GList. + * + * @param table_row Main DB column description + * @param subtable Sub-column description table + * @param pList List + */ +void gnc_sql_add_subtable_colnames_to_list( const GncSqlColumnTableEntry* table_row, + const GncSqlColumnTableEntry* subtable, + GList** pList ); + +/** + * Returns a string corresponding to the SQL representation of a GValue. The + * caller must free the string. + * + * @param conn SQL connection + * @param value Value to be converted + * @return String + */ +gchar* gnc_sql_get_sql_value( const GncSqlConnection* conn, const GValue* value ); + +/** + * Initializes DB table version information. + * + * @param be SQL backend struct + */ +void gnc_sql_init_version_info( GncSqlBackend* be ); + +/** + * Finalizes DB table version information. + * + * @param be SQL backend struct + */ +void gnc_sql_finalize_version_info( GncSqlBackend* be ); + +/** + * + * Commits a "standard" item to the database. In most cases, a commit of one object vs + * another differs only in the table name and column table. + * + * @param be SQL backend + * @param inst Instance + * @param tableName SQL table name + * @param obj_name QOF object type name + * @param col_table Column table + */ +void gnc_sql_commit_standard_item( GncSqlBackend* be, QofInstance* inst, const gchar* tableName, + QofIdTypeConst obj_name, const GncSqlColumnTableEntry* col_table ); + +void _retrieve_guid_( gpointer pObject, gpointer pValue ); + +#endif /* GNC_BACKEND_SQL_H_ */ diff --git a/src/backend/sql/gnc-book-sql.c b/src/backend/sql/gnc-book-sql.c new file mode 100644 index 0000000000..60465a2563 --- /dev/null +++ b/src/backend/sql/gnc-book-sql.c @@ -0,0 +1,222 @@ +/******************************************************************** + * gnc-book-sql.c: load and save data to SQL * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ +/** @file gnc-book-sql.c + * @brief load and save data to SQL + * @author Copyright (c) 2006-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL db + */ + +#include "config.h" + +#include + +#include "qof.h" + +#include "gnc-backend-sql.h" + +#include "gnc-book-sql.h" +#include "gnc-slots-sql.h" + +#include "gnc-engine.h" +#include "gnc-book.h" +#include "SX-book.h" +#include "SX-book-p.h" + +#define BOOK_TABLE "books" +#define TABLE_VERSION 1 + +static QofLogModule log_module = G_LOG_DOMAIN; + +static gpointer get_root_account_guid( gpointer pObject, const QofParam* ); +static void set_root_account_guid( gpointer pObject, gpointer pValue ); +static gpointer get_root_template_guid( gpointer pObject, const QofParam* ); +static void set_root_template_guid( gpointer pObject, gpointer pValue ); + +static const GncSqlColumnTableEntry col_table[] = +{ + { "guid", CT_GUID, 0, COL_NNUL|COL_PKEY, "guid" }, + { "root_account_guid", CT_GUID, 0, COL_NNUL, NULL, NULL, get_root_account_guid, set_root_account_guid }, + { "root_template_guid", CT_GUID, 0, COL_NNUL, NULL, NULL, get_root_template_guid, set_root_template_guid }, + { NULL } +}; + +/* ================================================================= */ +static gpointer +get_root_account_guid( gpointer pObject, const QofParam* param ) +{ + GNCBook* book = QOF_BOOK(pObject); + const Account* root; + + g_return_val_if_fail( pObject != NULL, NULL ); + g_return_val_if_fail( QOF_IS_BOOK(pObject), NULL ); + + root = gnc_book_get_root_account( book ); + return (gpointer)qof_instance_get_guid( QOF_INSTANCE(root) ); +} + +static void +set_root_account_guid( gpointer pObject, gpointer pValue ) +{ + GNCBook* book = QOF_BOOK(pObject); + const Account* root; + GUID* guid = (GUID*)pValue; + + g_return_if_fail( pObject != NULL ); + g_return_if_fail( QOF_IS_BOOK(pObject) ); + g_return_if_fail( pValue != NULL ); + + root = gnc_book_get_root_account( book ); + qof_instance_set_guid( QOF_INSTANCE(root), guid ); +} + +static gpointer +get_root_template_guid( gpointer pObject, const QofParam* param ) +{ + const GNCBook* book = QOF_BOOK(pObject); + const Account* root; + + g_return_val_if_fail( pObject != NULL, NULL ); + g_return_val_if_fail( QOF_IS_BOOK(pObject), NULL ); + + root = gnc_book_get_template_root( book ); + return (gpointer)qof_instance_get_guid( QOF_INSTANCE(root) ); +} + +static void +set_root_template_guid( gpointer pObject, gpointer pValue ) +{ + GNCBook* book = QOF_BOOK(pObject); + GUID* guid = (GUID*)pValue; + Account* root; + + g_return_if_fail( pObject != NULL ); + g_return_if_fail( QOF_IS_BOOK(pObject) ); + g_return_if_fail( pValue != NULL ); + + root = gnc_book_get_template_root( book ); + if( root == NULL ) { + root = xaccMallocAccount( book ); + xaccAccountBeginEdit( root ); + xaccAccountSetType( root, ACCT_TYPE_ROOT ); + xaccAccountCommitEdit( root ); + gnc_book_set_template_root( book, root ); + } + qof_instance_set_guid( QOF_INSTANCE(root), guid ); +} + +/* ================================================================= */ +static void +load_single_book( GncSqlBackend* be, GncSqlRow* row ) +{ + const GUID* guid; + GUID book_guid; + GNCBook* pBook; + + g_return_if_fail( be != NULL ); + g_return_if_fail( row != NULL ); + + guid = gnc_sql_load_guid( be, row ); + book_guid = *guid; + + pBook = be->primary_book; + if( pBook == NULL ) { + pBook = gnc_book_new(); + } + + gnc_sql_load_object( be, row, GNC_ID_BOOK, pBook, col_table ); + gnc_sql_slots_load( be, QOF_INSTANCE(pBook) ); + + qof_instance_mark_clean( QOF_INSTANCE(pBook) ); +} + +static void +load_all_books( GncSqlBackend* be ) +{ + GncSqlStatement* stmt; + GncSqlResult* result; + + g_return_if_fail( be != NULL ); + + stmt = gnc_sql_create_select_statement( be, BOOK_TABLE ); + result = gnc_sql_execute_select_statement( be, stmt ); + gnc_sql_statement_dispose( stmt ); + if( result != NULL ) { + GncSqlRow* row = gnc_sql_result_get_first_row( result ); + + // If there are no rows, try committing the book + if( row == NULL ) { + gnc_sql_save_book( be, QOF_INSTANCE(be->primary_book) ); + } else { + // Otherwise, load the 1st book. + load_single_book( be, row ); + } + + gnc_sql_result_dispose( result ); + } +} + +/* ================================================================= */ +static void +create_book_tables( GncSqlBackend* be ) +{ + gint version; + + g_return_if_fail( be != NULL ); + + version = gnc_sql_get_table_version( be, BOOK_TABLE ); + if( version == 0 ) { + gnc_sql_create_table( be, BOOK_TABLE, TABLE_VERSION, col_table ); + } +} + +/* ================================================================= */ +void +gnc_sql_save_book( GncSqlBackend* be, QofInstance* inst) +{ + const GUID* guid; + gint op; + gboolean is_infant; + + g_return_if_fail( be != NULL ); + g_return_if_fail( inst != NULL ); + g_return_if_fail( QOF_IS_BOOK(inst) ); + + gnc_sql_commit_standard_item( be, inst, BOOK_TABLE, GNC_ID_BOOK, col_table ); +} + +/* ================================================================= */ +void +gnc_sql_init_book_handler( void ) +{ + static GncSqlObjectBackend be_data = + { + GNC_SQL_BACKEND_VERSION, + GNC_ID_BOOK, + gnc_sql_save_book, /* commit */ + load_all_books, /* initial_load */ + create_book_tables /* create_tables */ + }; + + qof_object_register_backend( GNC_ID_BOOK, GNC_SQL_BACKEND, &be_data ); +} +/* ========================== END OF FILE ===================== */ diff --git a/src/backend/sql/gnc-book-sql.h b/src/backend/sql/gnc-book-sql.h new file mode 100644 index 0000000000..4b23024bd3 --- /dev/null +++ b/src/backend/sql/gnc-book-sql.h @@ -0,0 +1,38 @@ +/******************************************************************** + * gnc-book-sql.h: load and save data to SQL * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ +/** @file gnc-book-sql.h + * @brief load and save data to SQL + * @author Copyright (c) 2006-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#ifndef GNC_BOOK_SQL_H_ +#define GNC_BOOK_SQL_H_ + +#include "qof.h" +#include + +void gnc_sql_init_book_handler( void ); +void gnc_sql_save_book( GncSqlBackend* be, QofInstance* inst ); + +#endif /* GNC_BOOK_SQL_H_ */ diff --git a/src/backend/sql/gnc-budget-sql.c b/src/backend/sql/gnc-budget-sql.c new file mode 100644 index 0000000000..ffda5cfaa9 --- /dev/null +++ b/src/backend/sql/gnc-budget-sql.c @@ -0,0 +1,203 @@ +/******************************************************************** + * gnc-budget-sql.c: load and save data to SQL * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ +/** @file gnc-budget-sql.c + * @brief load and save data to SQL + * @author Copyright (c) 2006-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL db + */ + +#include "config.h" + +#include + +#include "qof.h" + +#include "gnc-backend-sql.h" + +#include "Recurrence.h" + +#include "gnc-budget-sql.h" +#include "gnc-slots-sql.h" +#include "gnc-recurrence-sql.h" + +#include "gnc-budget.h" + +#define BUDGET_TABLE "budgets" +#define TABLE_VERSION 1 + +static QofLogModule log_module = G_LOG_DOMAIN; + +#define BUDGET_MAX_NAME_LEN 2048 +#define BUDGET_MAX_DESCRIPTION_LEN 2048 + +static const GncSqlColumnTableEntry col_table[] = +{ + { "guid", CT_GUID, 0, COL_NNUL|COL_PKEY, "guid" }, + { "name", CT_STRING, BUDGET_MAX_NAME_LEN, COL_NNUL, "name" }, + { "description", CT_STRING, BUDGET_MAX_DESCRIPTION_LEN, 0, "description" }, + { "num_periods", CT_INT, 0, COL_NNUL, "num_periods" }, + { NULL } +}; + +/* ================================================================= */ +static GncBudget* +load_single_budget( GncSqlBackend* be, GncSqlRow* row ) +{ + const GUID* guid; + GUID budget_guid; + GncBudget* pBudget; + Recurrence* r; + + g_return_val_if_fail( be != NULL, NULL ); + g_return_val_if_fail( row != NULL, NULL ); + + guid = gnc_sql_load_guid( be, row ); + budget_guid = *guid; + + pBudget = gnc_budget_lookup( &budget_guid, be->primary_book ); + if( pBudget == NULL ) { + pBudget = gnc_budget_new( be->primary_book ); + } + + gnc_budget_begin_edit( pBudget ); + gnc_sql_load_object( be, row, GNC_ID_BUDGET, pBudget, col_table ); + r = g_new0( Recurrence, 1 ); + gnc_sql_recurrence_load( be, gnc_budget_get_guid( pBudget ), r ); + gnc_budget_commit_edit( pBudget ); + + return pBudget; +} + +static void +load_all_budgets( GncSqlBackend* be ) +{ + GncSqlStatement* stmt; + GncSqlResult* result; + int r; + GList* list = NULL; + + g_return_if_fail( be != NULL ); + + stmt = gnc_sql_create_select_statement( be, BUDGET_TABLE ); + result = gnc_sql_execute_select_statement( be, stmt ); + gnc_sql_statement_dispose( stmt ); + if( result != NULL ) { + GncSqlRow* row = gnc_sql_result_get_first_row( result ); + GncBudget* b; + + while( row != NULL ) { + b = load_single_budget( be, row ); + if( b != NULL ) { + list = g_list_append( list, b ); + } + row = gnc_sql_result_get_next_row( result ); + } + gnc_sql_result_dispose( result ); + + if( list != NULL ) { + gnc_sql_slots_load_for_list( be, list ); + } + } +} + +/* ================================================================= */ +static void +create_budget_tables( GncSqlBackend* be ) +{ + gint version; + + g_return_if_fail( be != NULL ); + + version = gnc_sql_get_table_version( be, BUDGET_TABLE ); + if( version == 0 ) { + gnc_sql_create_table( be, BUDGET_TABLE, TABLE_VERSION, col_table ); + } +} + +/* ================================================================= */ +static void +save_budget( GncSqlBackend* be, QofInstance* inst ) +{ + GncBudget* pBudget = GNC_BUDGET(inst); + const GUID* guid; + gint op; + gboolean is_infant; + + g_return_if_fail( be != NULL ); + g_return_if_fail( inst != NULL ); + g_return_if_fail( GNC_IS_BUDGET(inst) ); + + 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; + } + (void)gnc_sql_do_db_operation( be, op, BUDGET_TABLE, GNC_ID_BUDGET, pBudget, col_table ); + + // Now, commit any slots and recurrence + guid = qof_instance_get_guid( inst ); + if( !qof_instance_get_destroying(inst) ) { + gnc_sql_recurrence_save( be, guid, gnc_budget_get_recurrence( pBudget ) ); + gnc_sql_slots_save( be, guid, is_infant, qof_instance_get_slots( inst ) ); + } else { + gnc_sql_recurrence_delete( be, guid ); + gnc_sql_slots_delete( be, guid ); + } +} + +static void +do_save_budget( QofInstance* inst, gpointer data ) +{ + save_budget( (GncSqlBackend*)data, inst ); +} + +static void +write_budgets( GncSqlBackend* be ) +{ + g_return_if_fail( be != NULL ); + + qof_collection_foreach( qof_book_get_collection( be->primary_book, GNC_ID_BUDGET ), + (QofInstanceForeachCB)do_save_budget, be ); +} + +/* ================================================================= */ +void +gnc_sql_init_budget_handler( void ) +{ + static GncSqlObjectBackend be_data = + { + GNC_SQL_BACKEND_VERSION, + GNC_ID_BUDGET, + save_budget, /* commit */ + load_all_budgets, /* initial_load */ + create_budget_tables, /* create_tables */ + NULL, NULL, NULL, + write_budgets /* write */ + }; + + qof_object_register_backend( GNC_ID_BUDGET, GNC_SQL_BACKEND, &be_data ); +} +/* ========================== END OF FILE ===================== */ diff --git a/src/backend/sql/gnc-budget-sql.h b/src/backend/sql/gnc-budget-sql.h new file mode 100644 index 0000000000..d58a871a65 --- /dev/null +++ b/src/backend/sql/gnc-budget-sql.h @@ -0,0 +1,37 @@ +/******************************************************************** + * gnc-budget-sql.h: load and save data to SQL * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ +/** @file gnc-budget-sql.h + * @brief load and save data to SQL + * @author Copyright (c) 2006-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#ifndef GNC_BUDGET_SQL_H_ +#define GNC_BUDGET_SQL_H_ + +#include "qof.h" +#include + +void gnc_sql_init_budget_handler( void ); + +#endif /* GNC_BUDGET_SQL_H_ */ diff --git a/src/backend/sql/gnc-commodity-sql.c b/src/backend/sql/gnc-commodity-sql.c new file mode 100644 index 0000000000..f1503f5c96 --- /dev/null +++ b/src/backend/sql/gnc-commodity-sql.c @@ -0,0 +1,261 @@ +/******************************************************************** + * gnc-commodity-sql.c: load and save data to SQL * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ +/** @file gnc-commodity-sql.c + * @brief load and save data to SQL + * @author Copyright (c) 2006-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL db + */ + +#include "config.h" + +#include + +#include "qof.h" + +#include "gnc-backend-sql.h" +#include "gnc-commodity.h" + +#include "gnc-commodity-sql.h" +#include "gnc-slots-sql.h" + +static QofLogModule log_module = G_LOG_DOMAIN; + +static gpointer get_quote_source_name( gpointer pObject, const QofParam* ); +static void set_quote_source_name( gpointer pObject, gpointer pValue ); + +#define COMMODITIES_TABLE "commodities" +#define TABLE_VERSION 1 + +#define COMMODITY_MAX_NAMESPACE_LEN 2048 +#define COMMODITY_MAX_MNEMONIC_LEN 2048 +#define COMMODITY_MAX_FULLNAME_LEN 2048 +#define COMMODITY_MAX_CUSIP_LEN 2048 +#define COMMODITY_MAX_QUOTESOURCE_LEN 2048 +#define COMMODITY_MAX_QUOTE_TZ_LEN 2048 + +static const GncSqlColumnTableEntry col_table[] = { + { "guid", CT_GUID, 0, COL_NNUL|COL_PKEY, "guid" }, + { "namespace", CT_STRING, COMMODITY_MAX_NAMESPACE_LEN, COL_NNUL, NULL, NULL, + (QofAccessFunc)gnc_commodity_get_namespace, + (QofSetterFunc)gnc_commodity_set_namespace }, + { "mnemonic", CT_STRING, COMMODITY_MAX_MNEMONIC_LEN, COL_NNUL, "mnemonic" }, + { "fullname", CT_STRING, COMMODITY_MAX_FULLNAME_LEN, 0, "fullname" }, + { "cusip", CT_STRING, COMMODITY_MAX_CUSIP_LEN, 0, "cusip" }, + { "fraction", CT_INT, 0, COL_NNUL, "fraction" }, + { "quote_flag", CT_BOOLEAN, 0, COL_NNUL, "quote_flag" }, + { "quote_source", CT_STRING, COMMODITY_MAX_QUOTESOURCE_LEN, 0, NULL, NULL, + get_quote_source_name, set_quote_source_name }, + { "quote_tz", CT_STRING, COMMODITY_MAX_QUOTE_TZ_LEN, 0, "quote-tz" }, + { NULL } +}; + +/* ================================================================= */ + +static gpointer +get_quote_source_name( gpointer pObject, const QofParam* param ) +{ + const gnc_commodity* pCommodity = GNC_COMMODITY(pObject); + + g_return_val_if_fail( pObject != NULL, NULL ); + g_return_val_if_fail( GNC_IS_COMMODITY(pObject), NULL ); + + return (gpointer)gnc_quote_source_get_internal_name( + gnc_commodity_get_quote_source(pCommodity)); +} + +static void +set_quote_source_name( gpointer pObject, gpointer pValue ) +{ + gnc_commodity* pCommodity = GNC_COMMODITY(pObject); + const gchar* quote_source_name = (const gchar*)pValue; + gnc_quote_source* quote_source; + + g_return_if_fail( pObject != NULL ); + g_return_if_fail( GNC_IS_COMMODITY(pObject) ); + + if( pValue == NULL ) return; + + quote_source = gnc_quote_source_lookup_by_internal( quote_source_name ); + gnc_commodity_set_quote_source( pCommodity, quote_source ); +} + +static gnc_commodity* +load_single_commodity( GncSqlBackend* be, GncSqlRow* row ) +{ + QofBook* pBook = be->primary_book; + int col; + const GValue* val; + gnc_commodity* pCommodity; + + pCommodity = gnc_commodity_new( pBook, NULL, NULL, NULL, NULL, 100 ); + gnc_commodity_begin_edit( pCommodity ); + gnc_sql_load_object( be, row, GNC_ID_COMMODITY, pCommodity, col_table ); + gnc_commodity_commit_edit( pCommodity ); + + return pCommodity; +} + +static void +load_all_commodities( GncSqlBackend* be ) +{ + GncSqlStatement* stmt; + GncSqlResult* result; + gnc_commodity_table* pTable; + + pTable = gnc_commodity_table_get_table( be->primary_book ); + stmt = gnc_sql_create_select_statement( be, COMMODITIES_TABLE ); + result = gnc_sql_execute_select_statement( be, stmt ); + gnc_sql_statement_dispose( stmt ); + if( result != NULL ) { + int r; + gnc_commodity* pCommodity; + GList* list = NULL; + GncSqlRow* row = gnc_sql_result_get_first_row( result ); + + while( row != NULL ) { + gnc_commodity* c; + + pCommodity = load_single_commodity( be, row ); + + if( pCommodity != NULL ) { + GUID guid; + + guid = *qof_instance_get_guid( QOF_INSTANCE(pCommodity) ); + pCommodity = gnc_commodity_table_insert( pTable, pCommodity ); + list = g_list_append( list, pCommodity ); + qof_instance_set_guid( QOF_INSTANCE(pCommodity), &guid ); + } + row = gnc_sql_result_get_next_row( result ); + } + gnc_sql_result_dispose( result ); + + if( list != NULL ) { + gnc_sql_slots_load_for_list( be, list ); + } + } +} +/* ================================================================= */ +static void +create_commodities_tables( GncSqlBackend* be ) +{ + gint version; + + g_return_if_fail( be != NULL ); + + version = gnc_sql_get_table_version( be, COMMODITIES_TABLE ); + if( version == 0 ) { + gnc_sql_create_table( be, COMMODITIES_TABLE, TABLE_VERSION, col_table ); + } +} + +/* ================================================================= */ +static void +commit_commodity( GncSqlBackend* be, QofInstance* inst ) +{ + const GUID* guid; + + g_return_if_fail( be != NULL ); + g_return_if_fail( inst != NULL ); + g_return_if_fail( GNC_IS_COMMODITY(inst) ); + + gnc_sql_commit_standard_item( be, inst, COMMODITIES_TABLE, GNC_ID_COMMODITY, col_table ); +} + +static gboolean +is_commodity_in_db( GncSqlBackend* be, gnc_commodity* pCommodity ) +{ + g_return_val_if_fail( be != NULL, FALSE ); + g_return_val_if_fail( pCommodity != NULL, FALSE ); + + return gnc_sql_object_is_it_in_db( be, COMMODITIES_TABLE, GNC_ID_COMMODITY, + pCommodity, col_table ); +} + +void +gnc_sql_save_commodity( GncSqlBackend* be, gnc_commodity* pCommodity ) +{ + g_return_if_fail( be != NULL ); + g_return_if_fail( pCommodity != NULL ); + + if( !is_commodity_in_db( be, pCommodity ) ) { + commit_commodity( be, QOF_INSTANCE(pCommodity) ); + } +} + +/* ----------------------------------------------------------------- */ + +static void +load_commodity_guid( const GncSqlBackend* be, GncSqlRow* row, + QofSetterFunc setter, gpointer pObject, + const GncSqlColumnTableEntry* table_row ) +{ + const GValue* val; + GUID guid; + const GUID* pGuid; + gnc_commodity* commodity = NULL; + + g_return_if_fail( be != NULL ); + g_return_if_fail( row != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + + val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name ); + if( val == NULL ) { + pGuid = NULL; + } else { + string_to_guid( g_value_get_string( val ), &guid ); + pGuid = &guid; + } + if( pGuid != NULL ) { + commodity = gnc_commodity_find_commodity_by_guid( pGuid, be->primary_book ); + } + if( table_row->gobj_param_name != NULL ) { + g_object_set( pObject, table_row->gobj_param_name, commodity, NULL ); + } else { + (*setter)( pObject, (const gpointer)commodity ); + } +} + +static col_type_handler_t commodity_guid_handler + = { load_commodity_guid, + gnc_sql_add_objectref_guid_col_info_to_list, + gnc_sql_add_colname_to_list, + gnc_sql_add_gvalue_objectref_guid_to_slist }; +/* ================================================================= */ +void +gnc_sql_init_commodity_handler( void ) +{ + static GncSqlObjectBackend be_data = + { + GNC_SQL_BACKEND_VERSION, + GNC_ID_COMMODITY, + commit_commodity, /* commit */ + load_all_commodities, /* initial_load */ + create_commodities_tables /* create_tables */ + }; + + qof_object_register_backend( GNC_ID_COMMODITY, GNC_SQL_BACKEND, &be_data ); + + gnc_sql_register_col_type_handler( CT_COMMODITYREF, &commodity_guid_handler ); +} +/* ========================== END OF FILE ===================== */ diff --git a/src/backend/sql/gnc-commodity-sql.h b/src/backend/sql/gnc-commodity-sql.h new file mode 100644 index 0000000000..39e80d0c05 --- /dev/null +++ b/src/backend/sql/gnc-commodity-sql.h @@ -0,0 +1,38 @@ +/******************************************************************** + * gnc-commodity-sql.h: load and save data to SQL * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ +/** @file gnc-commodity-sql.h + * @brief load and save data to SQL + * @author Copyright (c) 2006-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#ifndef GNC_COMMODITY_SQL_H_ +#define GNC_COMMODITY_SQL_H_ + +#include "qof.h" +#include + +void gnc_sql_init_commodity_handler( void ); +void gnc_sql_save_commodity( GncSqlBackend* be, gnc_commodity* pCommodity ); + +#endif /* GNC_COMMODITY_SQL_H_ */ diff --git a/src/backend/sql/gnc-lots-sql.c b/src/backend/sql/gnc-lots-sql.c new file mode 100644 index 0000000000..8acdb0ac7a --- /dev/null +++ b/src/backend/sql/gnc-lots-sql.c @@ -0,0 +1,249 @@ +/******************************************************************** + * gnc-lots-sql.c: load and save data to SQL * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ +/** @file gnc-lots-sql.c + * @brief load and save data to SQL + * @author Copyright (c) 2006-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL db + */ + +#include "config.h" + +#include + +#include "qof.h" +#include "gnc-lot.h" + +#include "gnc-backend-sql.h" +#include "gnc-slots-sql.h" + +#include "gnc-lots-sql.h" + +static QofLogModule log_module = G_LOG_DOMAIN; + +#define TABLE_NAME "lots" +#define TABLE_VERSION 1 + +static gpointer get_lot_account( gpointer pObject, const QofParam* param ); +static void set_lot_account( gpointer pObject, gpointer pValue ); +static void set_lot_is_closed( gpointer pObject, gboolean value ); + +static const GncSqlColumnTableEntry col_table[] = +{ + { "guid", CT_GUID, 0, COL_NNUL|COL_PKEY, "guid" }, + { "account_guid", CT_GUID, 0, COL_NNUL, NULL, NULL, get_lot_account, set_lot_account }, + { "is_closed", CT_BOOLEAN, 0, COL_NNUL, NULL, NULL, + (QofAccessFunc)gnc_lot_is_closed, (QofSetterFunc)set_lot_is_closed }, + { NULL } +}; + +/* ================================================================= */ +static gpointer +get_lot_account( gpointer pObject, const QofParam* param ) +{ + const GNCLot* lot = GNC_LOT(pObject); + const Account* pAccount; + + g_return_val_if_fail( pObject != NULL, NULL ); + g_return_val_if_fail( GNC_IS_LOT(pObject), NULL ); + + pAccount = gnc_lot_get_account( lot ); + return (gpointer)qof_instance_get_guid( QOF_INSTANCE(pAccount) ); +} + +static void +set_lot_account( gpointer pObject, gpointer pValue ) +{ + GNCLot* lot = GNC_LOT(pObject); + QofBook* pBook; + GUID* guid = (GUID*)pValue; + Account* pAccount; + + g_return_if_fail( pObject != NULL ); + g_return_if_fail( GNC_IS_LOT(pObject) ); + g_return_if_fail( pValue != NULL ); + + pBook = qof_instance_get_book( QOF_INSTANCE(lot) ); + pAccount = xaccAccountLookup( guid, pBook ); + xaccAccountInsertLot( pAccount, lot ); +} + +static void +set_lot_is_closed( gpointer pObject, gboolean closed ) +{ + GNCLot* lot = GNC_LOT(pObject); + + g_return_if_fail( pObject != NULL ); + g_return_if_fail( GNC_IS_LOT(pObject) ); + + lot->is_closed = closed; +} + +static GNCLot* +load_single_lot( GncSqlBackend* be, GncSqlRow* row ) +{ + GNCLot* lot; + + g_return_val_if_fail( be != NULL, NULL ); + g_return_val_if_fail( row != NULL, NULL ); + + lot = gnc_lot_new( be->primary_book ); + + gnc_lot_begin_edit( lot ); + gnc_sql_load_object( be, row, GNC_ID_LOT, lot, col_table ); + gnc_lot_commit_edit( lot ); + + return lot; +} + +static void +load_all_lots( GncSqlBackend* be ) +{ + GncSqlStatement* stmt; + GncSqlResult* result; + + g_return_if_fail( be != NULL ); + + stmt = gnc_sql_create_select_statement( be, TABLE_NAME ); + result = gnc_sql_execute_select_statement( be, stmt ); + gnc_sql_statement_dispose( stmt ); + if( result != NULL ) { + int r; + GList* list = NULL; + GncSqlRow* row = gnc_sql_result_get_first_row( result ); + GNCLot* lot; + + while( row != NULL ) { + lot = load_single_lot( be, row ); + if( lot != NULL ) { + list = g_list_append( list, lot ); + } + row = gnc_sql_result_get_next_row( result ); + } + gnc_sql_result_dispose( result ); + + if( list != NULL ) { + gnc_sql_slots_load_for_list( be, list ); + } + } +} + +/* ================================================================= */ +static void +create_lots_tables( GncSqlBackend* be ) +{ + gint version; + + g_return_if_fail( be != NULL ); + + version = gnc_sql_get_table_version( be, TABLE_NAME ); + if( version == 0 ) { + gnc_sql_create_table( be, TABLE_NAME, TABLE_VERSION, col_table ); + } +} + +/* ================================================================= */ + +static void +commit_lot( GncSqlBackend* be, QofInstance* inst ) +{ + g_return_if_fail( be != NULL ); + g_return_if_fail( inst != NULL ); + g_return_if_fail( GNC_IS_LOT(inst) ); + + gnc_sql_commit_standard_item( be, inst, TABLE_NAME, GNC_ID_LOT, col_table ); +} + +static void +do_save_lot( QofInstance* inst, gpointer data ) +{ + commit_lot( (GncSqlBackend*)data, inst ); +} + +static void +write_lots( GncSqlBackend* be ) +{ + g_return_if_fail( be != NULL ); + + qof_collection_foreach( qof_book_get_collection( be->primary_book, GNC_ID_LOT ), + (QofInstanceForeachCB)do_save_lot, be ); +} + +/* ================================================================= */ +static void +load_lot_guid( const GncSqlBackend* be, GncSqlRow* row, + QofSetterFunc setter, gpointer pObject, + const GncSqlColumnTableEntry* table_row ) +{ + const GValue* val; + GUID guid; + const GUID* pGuid; + GNCLot* lot = NULL; + + g_return_if_fail( be != NULL ); + g_return_if_fail( row != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + + val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name ); + if( val == NULL ) { + pGuid = NULL; + } else { + string_to_guid( g_value_get_string( val ), &guid ); + pGuid = &guid; + } + if( pGuid != NULL ) { + lot = gnc_lot_lookup( pGuid, be->primary_book ); + } + if( table_row->gobj_param_name != NULL ) { + g_object_set( pObject, table_row->gobj_param_name, lot, NULL ); + } else { + (*setter)( pObject, (const gpointer)lot ); + } +} + +static col_type_handler_t lot_guid_handler + = { load_lot_guid, + gnc_sql_add_objectref_guid_col_info_to_list, + gnc_sql_add_colname_to_list, + gnc_sql_add_gvalue_objectref_guid_to_slist }; +/* ================================================================= */ +void +gnc_sql_init_lot_handler( void ) +{ + static GncSqlObjectBackend be_data = + { + GNC_SQL_BACKEND_VERSION, + GNC_ID_LOT, + commit_lot, /* commit */ + load_all_lots, /* initial_load */ + create_lots_tables, /* create tables */ + NULL, NULL, NULL, + write_lots /* save all */ + }; + + qof_object_register_backend( GNC_ID_LOT, GNC_SQL_BACKEND, &be_data ); + + gnc_sql_register_col_type_handler( CT_LOTREF, &lot_guid_handler ); +} + +/* ========================== END OF FILE ===================== */ diff --git a/src/backend/sql/gnc-lots-sql.h b/src/backend/sql/gnc-lots-sql.h new file mode 100644 index 0000000000..bd86d99d20 --- /dev/null +++ b/src/backend/sql/gnc-lots-sql.h @@ -0,0 +1,37 @@ +/******************************************************************** + * gnc-lots-sql.h: load and save data to SQL * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ +/** @file gnc-lots-sql.h + * @brief load and save data to SQL + * @author Copyright (c) 2006-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#ifndef GNC_LOT_SQL_H_ +#define GNC_LOT_SQL_H_ + +#include "qof.h" +#include + +void gnc_sql_init_lot_handler( void ); + +#endif /* GNC_LOT_SQL_H_ */ diff --git a/src/backend/sql/gnc-price-sql.c b/src/backend/sql/gnc-price-sql.c new file mode 100644 index 0000000000..e631ed8f39 --- /dev/null +++ b/src/backend/sql/gnc-price-sql.c @@ -0,0 +1,207 @@ +/******************************************************************** + * gnc-price-sql.c: load and save data to SQL * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ +/** @file gnc-price-sql.c + * @brief load and save data to SQL + * @author Copyright (c) 2006-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL db + */ + +#include "config.h" + +#include + +#include "qof.h" +#include "gnc-pricedb.h" + +#include "gnc-backend-sql.h" + +#include "gnc-commodity-sql.h" +#include "gnc-price-sql.h" +#include "gnc-slots-sql.h" + +static QofLogModule log_module = G_LOG_DOMAIN; + +#define TABLE_NAME "prices" +#define TABLE_VERSION 1 + +#define PRICE_MAX_SOURCE_LEN 2048 +#define PRICE_MAX_TYPE_LEN 2048 + +static const GncSqlColumnTableEntry col_table[] = +{ + { "guid", CT_GUID, 0, COL_NNUL|COL_PKEY, "guid" }, + { "commodity_guid", CT_COMMODITYREF, 0, COL_NNUL, NULL, PRICE_COMMODITY }, + { "currency_guid", CT_COMMODITYREF, 0, COL_NNUL, NULL, PRICE_CURRENCY }, + { "date", CT_TIMESPEC, 0, COL_NNUL, NULL, PRICE_DATE }, + { "source", CT_STRING, PRICE_MAX_SOURCE_LEN, 0, NULL, PRICE_SOURCE }, + { "type", CT_STRING, PRICE_MAX_TYPE_LEN, 0, NULL, PRICE_TYPE }, + { "value", CT_NUMERIC, 0, COL_NNUL, NULL, PRICE_VALUE }, + { NULL } +}; + +/* ================================================================= */ + +static GNCPrice* +load_single_price( GncSqlBackend* be, GncSqlRow* row ) +{ + GNCPrice* pPrice; + + g_return_val_if_fail( be != NULL, NULL ); + g_return_val_if_fail( row != NULL, NULL ); + + pPrice = gnc_price_create( be->primary_book ); + + gnc_price_begin_edit( pPrice ); + gnc_sql_load_object( be, row, GNC_ID_PRICE, pPrice, col_table ); + gnc_price_commit_edit( pPrice ); + + return pPrice; +} + +static void +load_all_prices( GncSqlBackend* be ) +{ + GncSqlStatement* stmt; + GncSqlResult* result; + QofBook* pBook; + GNCPriceDB* pPriceDB; + + g_return_if_fail( be != NULL ); + + pBook = be->primary_book; + pPriceDB = gnc_book_get_pricedb( pBook ); + stmt = gnc_sql_create_select_statement( be, TABLE_NAME ); + result = gnc_sql_execute_select_statement( be, stmt ); + gnc_sql_statement_dispose( stmt ); + if( result != NULL ) { + int r; + GNCPrice* pPrice; + GList* list = NULL; + GncSqlRow* row = gnc_sql_result_get_first_row( result ); + + while( row != NULL ) { + pPrice = load_single_price( be, row ); + + if( pPrice != NULL ) { + list = g_list_append( list, pPrice ); + gnc_pricedb_add_price( pPriceDB, pPrice ); + } + row = gnc_sql_result_get_next_row( result ); + } + gnc_sql_result_dispose( result ); + + if( list != NULL ) { + gnc_sql_slots_load_for_list( be, list ); + } + } +} + +/* ================================================================= */ +static void +create_prices_tables( GncSqlBackend* be ) +{ + gint version; + + g_return_if_fail( be != NULL ); + + version = gnc_sql_get_table_version( be, TABLE_NAME ); + if( version == 0 ) { + gnc_sql_create_table( be, TABLE_NAME, TABLE_VERSION, col_table ); + } +} + +/* ================================================================= */ + +static void +save_price( GncSqlBackend* be, QofInstance* inst ) +{ + GNCPrice* pPrice = GNC_PRICE(inst); + gint op; + gboolean is_infant; + + g_return_if_fail( be != NULL ); + g_return_if_fail( inst != NULL ); + g_return_if_fail( GNC_IS_PRICE(inst) ); + + 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 ) { + /* Ensure commodity and currency are in the db */ + gnc_sql_save_commodity( be, gnc_price_get_commodity( pPrice ) ); + gnc_sql_save_commodity( be, gnc_price_get_currency( pPrice ) ); + } + + (void)gnc_sql_do_db_operation( be, op, TABLE_NAME, GNC_ID_PRICE, pPrice, col_table ); +} + +static gboolean +write_price( GNCPrice* p, gpointer data ) +{ + GncSqlBackend* be = (GncSqlBackend*)data; + + g_return_val_if_fail( p != NULL, FALSE ); + g_return_val_if_fail( data != NULL, FALSE ); + + save_price( be, QOF_INSTANCE(p) ); + + return TRUE; +} + +static void +write_prices( GncSqlBackend* be ) +{ + GNCPriceDB* priceDB; + + g_return_if_fail( be != NULL ); + + priceDB = gnc_book_get_pricedb( be->primary_book ); + + gnc_pricedb_foreach_price( priceDB, write_price, be, TRUE ); +} + +/* ================================================================= */ +void +gnc_sql_init_price_handler( void ) +{ + static GncSqlObjectBackend be_data = + { + GNC_SQL_BACKEND_VERSION, + GNC_ID_PRICE, + save_price, /* commit */ + load_all_prices, /* initial_load */ + create_prices_tables, /* create tables */ + NULL, NULL, NULL, + write_prices /* write */ + }; + + qof_object_register_backend( GNC_ID_PRICE, GNC_SQL_BACKEND, &be_data ); +} + +/* ========================== END OF FILE ===================== */ diff --git a/src/backend/sql/gnc-price-sql.h b/src/backend/sql/gnc-price-sql.h new file mode 100644 index 0000000000..a522152796 --- /dev/null +++ b/src/backend/sql/gnc-price-sql.h @@ -0,0 +1,37 @@ +/******************************************************************** + * gnc-price-sql.h: load and save data to SQL * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ +/** @file gnc-price-sql.h + * @brief load and save data to SQL + * @author Copyright (c) 2006-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#ifndef GNC_PRICE_SQL_H_ +#define GNC_PRICE_SQL_H_ + +#include "qof.h" +#include + +void gnc_sql_init_price_handler( void ); + +#endif /* GNC_PRICE_SQL_H_ */ diff --git a/src/backend/sql/gnc-recurrence-sql.c b/src/backend/sql/gnc-recurrence-sql.c new file mode 100644 index 0000000000..0e29fd3c7b --- /dev/null +++ b/src/backend/sql/gnc-recurrence-sql.c @@ -0,0 +1,342 @@ +/******************************************************************** + * gnc-recurrence-sql.c: load and save data to SQL * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ +/** @file gnc-recurrence-sql.c + * @brief load and save data to SQL + * @author Copyright (c) 2006-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL db + */ + +#include "config.h" + +#include + +#include "qof.h" +#include "gnc-engine.h" +#include "Recurrence.h" + +#include "gnc-backend-sql.h" + +#include "gnc-recurrence-sql.h" + +static QofLogModule log_module = G_LOG_DOMAIN; + +#define TABLE_NAME "recurrences" +#define TABLE_VERSION 1 + +#define BUDGET_MAX_RECURRENCE_PERIOD_TYPE_LEN 2048 + +typedef struct { + GncSqlBackend* be; + const GUID* guid; + Recurrence* pRecurrence; +} recurrence_info_t; + +static gpointer get_obj_guid( gpointer pObject, const QofParam* param ); +static void set_obj_guid( gpointer pObject, gpointer pValue ); +static gint get_recurrence_mult( gpointer pObject ); +static void set_recurrence_mult( gpointer pObject, gint value ); +static gpointer get_recurrence_period_type( gpointer pObject, const QofParam* ); +static void set_recurrence_period_type( gpointer pObject, gpointer pValue ); +static gpointer get_recurrence_period_start( gpointer pObject, const QofParam* ); +static void set_recurrence_period_start( gpointer pObject, gpointer pValue ); + +static const GncSqlColumnTableEntry col_table[] = +{ + { "obj_guid", CT_GUID, 0, COL_NNUL, NULL, NULL, + get_obj_guid, set_obj_guid }, + { "recurrence_mult", CT_INT, 0, COL_NNUL, NULL, NULL, + (QofAccessFunc)get_recurrence_mult, (QofSetterFunc)set_recurrence_mult }, + { "recurrence_period_type", CT_STRING, BUDGET_MAX_RECURRENCE_PERIOD_TYPE_LEN, COL_NNUL, NULL, NULL, + get_recurrence_period_type, set_recurrence_period_type }, + { "recurrence_period_start", CT_GDATE, 0, COL_NNUL, NULL, NULL, + get_recurrence_period_start, set_recurrence_period_start }, + { NULL } +}; + +/* Special column table because we need to be able to access the table by +a column other than the primary key */ +static const GncSqlColumnTableEntry guid_col_table[] = +{ + { "obj_guid", CT_GUID, 0, 0, NULL, NULL, get_obj_guid, set_obj_guid }, + { NULL } +}; + +/* ================================================================= */ + +static gpointer +get_obj_guid( gpointer pObject, const QofParam* param ) +{ + recurrence_info_t* pInfo = (recurrence_info_t*)pObject; + + g_return_val_if_fail( pObject != NULL, NULL ); + + return (gpointer)pInfo->guid; +} + +static void +set_obj_guid( gpointer pObject, gpointer pValue ) +{ + // Nowhere to put the GUID +} + +static gint +get_recurrence_mult( gpointer pObject ) +{ + recurrence_info_t* pInfo = (recurrence_info_t*)pObject; + + g_return_val_if_fail( pObject != NULL, 0 ); + g_return_val_if_fail( pInfo->pRecurrence != NULL, 0 ); + + return pInfo->pRecurrence->mult; +} + +static void +set_recurrence_mult( gpointer pObject, gint value ) +{ + recurrence_info_t* pInfo = (recurrence_info_t*)pObject; + + g_return_if_fail( pObject != NULL ); + g_return_if_fail( pInfo->pRecurrence != NULL ); + + pInfo->pRecurrence->mult = value; +} + +static gpointer +get_recurrence_period_type( gpointer pObject, const QofParam* param ) +{ + recurrence_info_t* pInfo = (recurrence_info_t*)pObject; + + g_return_val_if_fail( pObject != NULL, NULL ); + g_return_val_if_fail( pInfo->pRecurrence != NULL, NULL ); + + return (gpointer)recurrencePeriodTypeToString( + recurrenceGetPeriodType( pInfo->pRecurrence ) ); +} + +static void +set_recurrence_period_type( gpointer pObject, gpointer pValue ) +{ + recurrence_info_t* pInfo = (recurrence_info_t*)pObject; + + g_return_if_fail( pObject != NULL ); + g_return_if_fail( pInfo->pRecurrence != NULL ); + g_return_if_fail( pValue != NULL ); + + pInfo->pRecurrence->ptype = recurrencePeriodTypeFromString( (gchar*)pValue ); +} + +static gpointer +get_recurrence_period_start( gpointer pObject, const QofParam* param ) +{ + recurrence_info_t* pInfo = (recurrence_info_t*)pObject; + static GDate date; + + g_return_val_if_fail( pObject != NULL, NULL ); + g_return_val_if_fail( pInfo->pRecurrence != NULL, NULL ); + + date = recurrenceGetDate( pInfo->pRecurrence ); + return (gpointer)&date; +} + +static void +set_recurrence_period_start( gpointer pObject, gpointer pValue ) +{ + recurrence_info_t* pInfo = (recurrence_info_t*)pObject; + GDate* date = (GDate*)pValue; + + g_return_if_fail( pObject != NULL ); + g_return_if_fail( pInfo->pRecurrence != NULL ); + g_return_if_fail( pValue != NULL ); + + pInfo->pRecurrence->start = *date; +} + +/* ================================================================= */ + +void +gnc_sql_recurrence_save( GncSqlBackend* be, const GUID* guid, const Recurrence* r ) +{ + recurrence_info_t recurrence_info; + + g_return_if_fail( be != NULL ); + g_return_if_fail( guid != NULL ); + g_return_if_fail( r != NULL ); + + gnc_sql_recurrence_delete( be, guid ); + + recurrence_info.be = be; + recurrence_info.guid = guid; + recurrence_info.pRecurrence = (Recurrence*)r; + (void)gnc_sql_do_db_operation( be, OP_DB_INSERT, TABLE_NAME, + TABLE_NAME, &recurrence_info, col_table ); +} + +void +gnc_sql_recurrence_save_list( GncSqlBackend* be, const GUID* guid, GList* schedule ) +{ + recurrence_info_t recurrence_info; + GList* l; + + g_return_if_fail( be != NULL ); + g_return_if_fail( guid != NULL ); + + gnc_sql_recurrence_delete( be, guid ); + + recurrence_info.be = be; + recurrence_info.guid = guid; + for( l = schedule; l != NULL; l = g_list_next( l ) ) { + recurrence_info.pRecurrence = (Recurrence*)l->data; + (void)gnc_sql_do_db_operation( be, OP_DB_INSERT, TABLE_NAME, + TABLE_NAME, &recurrence_info, col_table ); + } +} + +void +gnc_sql_recurrence_delete( GncSqlBackend* be, const GUID* guid ) +{ + recurrence_info_t recurrence_info; + + g_return_if_fail( be != NULL ); + g_return_if_fail( guid != NULL ); + + recurrence_info.be = be; + recurrence_info.guid = guid; + (void)gnc_sql_do_db_operation( be, OP_DB_DELETE, TABLE_NAME, + TABLE_NAME, &recurrence_info, guid_col_table ); +} + +static void +load_recurrence( GncSqlBackend* be, GncSqlRow* row, Recurrence* r ) +{ + recurrence_info_t recurrence_info; + + g_return_if_fail( be != NULL ); + g_return_if_fail( row != NULL ); + g_return_if_fail( r != NULL ); + + recurrence_info.be = be; + recurrence_info.pRecurrence = r; + + gnc_sql_load_object( be, row, TABLE_NAME, &recurrence_info, col_table ); +} + +static GncSqlResult* +gnc_sql_set_recurrences_from_db( GncSqlBackend* be, const GUID* guid ) +{ + gchar* buf; + gchar guid_buf[GUID_ENCODING_LENGTH+1]; + gchar* field_name; + GncSqlStatement* stmt; + GError* error = NULL; + GncSqlResult* result; + + g_return_val_if_fail( be != NULL, NULL ); + g_return_val_if_fail( guid != NULL, NULL ); + + guid_to_string_buff( guid, guid_buf ); + buf = g_strdup_printf( "SELECT * FROM %s WHERE obj_guid='%s'", TABLE_NAME, guid_buf ); + stmt = gnc_sql_connection_create_statement_from_sql( be->conn, buf ); + result = gnc_sql_execute_select_statement( be, stmt ); + gnc_sql_statement_dispose( stmt ); + return result; +} + +void +gnc_sql_recurrence_load( GncSqlBackend* be, const GUID* guid, Recurrence* pRecurrence ) +{ + GncSqlResult* result; + + g_return_if_fail( be != NULL ); + g_return_if_fail( guid != NULL ); + g_return_if_fail( pRecurrence != NULL ); + + result = gnc_sql_set_recurrences_from_db( be, guid ); + if( result != NULL ) { + int numRows = gnc_sql_result_get_num_rows( result ); + + if( numRows > 0 ) { + if( numRows > 1 ) { + g_warning( "More than 1 recurrence found: first one used" ); + } + load_recurrence( be, gnc_sql_result_get_first_row( result ), + pRecurrence ); + } else { + g_warning( "No recurrences found" ); + } + gnc_sql_result_dispose( result ); + } +} + +void +gnc_sql_recurrence_load_list( GncSqlBackend* be, const GUID* guid, GList** pSchedule ) +{ + GncSqlResult* result; + + g_return_if_fail( be != NULL ); + g_return_if_fail( guid != NULL ); + g_return_if_fail( pSchedule != NULL ); + + result = gnc_sql_set_recurrences_from_db( be, guid ); + if( result != NULL ) { + GncSqlRow* row = gnc_sql_result_get_first_row( result ); + + while( row != NULL ) { + Recurrence* pRecurrence = g_new0( Recurrence, 1 ); + load_recurrence( be, row, pRecurrence ); + *pSchedule = g_list_append( *pSchedule, pRecurrence ); + row = gnc_sql_result_get_next_row( result ); + } + gnc_sql_result_dispose( result ); + } +} + +/* ================================================================= */ +static void +create_recurrence_tables( GncSqlBackend* be ) +{ + gint version; + + g_return_if_fail( be != NULL ); + + version = gnc_sql_get_table_version( be, TABLE_NAME ); + if( version == 0 ) { + gnc_sql_create_table( be, TABLE_NAME, TABLE_VERSION, col_table ); + } +} + +/* ================================================================= */ +void +gnc_sql_init_recurrence_handler( void ) +{ + static GncSqlObjectBackend be_data = + { + GNC_SQL_BACKEND_VERSION, + GNC_ID_ACCOUNT, + NULL, /* commit - cannot occur */ + NULL, /* initial_load - cannot occur */ + create_recurrence_tables /* create_tables */ + }; + + qof_object_register_backend( TABLE_NAME, GNC_SQL_BACKEND, &be_data ); +} +/* ========================== END OF FILE ===================== */ diff --git a/src/backend/sql/gnc-recurrence-sql.h b/src/backend/sql/gnc-recurrence-sql.h new file mode 100644 index 0000000000..e0808cd2c5 --- /dev/null +++ b/src/backend/sql/gnc-recurrence-sql.h @@ -0,0 +1,43 @@ +/******************************************************************** + * gnc-recurrence-sql.h: load and save data to SQL * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ +/** @file gnc-recurrence-sql.h + * @brief load and save accounts data to SQL + * @author Copyright (c) 2006-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#ifndef GNC_RECURRENCE_SQL_H_ +#define GNC_RECURRENCE_SQL_H_ + +#include "qof.h" +#include + +void gnc_sql_recurrence_save( GncSqlBackend* be, const GUID* guid, const Recurrence* pRecurrence ); +void gnc_sql_recurrence_save_list( GncSqlBackend* be, const GUID* guid, GList* schedule ); +void gnc_sql_recurrence_delete( GncSqlBackend* be, const GUID* guid ); +void gnc_sql_recurrence_load( GncSqlBackend* be, const GUID* guid, Recurrence* pRecurrence ); +void gnc_sql_recurrence_load_list( GncSqlBackend* be, const GUID* guid, GList** pSchedule ); + +void gnc_sql_init_recurrence_handler( void ); + +#endif /* GNC_RECURRENCE_SQL_H_ */ diff --git a/src/backend/sql/gnc-schedxaction-sql.c b/src/backend/sql/gnc-schedxaction-sql.c new file mode 100644 index 0000000000..169b018a1a --- /dev/null +++ b/src/backend/sql/gnc-schedxaction-sql.c @@ -0,0 +1,312 @@ +/******************************************************************** + * gnc-schedxaction-sql.c: load and save data to SQL * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ +/** @file gnc-schedxaction-sql.c + * @brief load and save data to SQL + * @author Copyright (c) 2006-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL db + */ + +#include "config.h" + +#include + +#include "qof.h" + +#include "gnc-backend-sql.h" + +#include "gnc-schedxaction-sql.h" +#include "gnc-slots-sql.h" + +#include "SchedXaction.h" +#include "SX-book.h" +#include "Recurrence.h" + +#include "gnc-recurrence-sql.h" + +#define SCHEDXACTION_TABLE "schedxactions" +#define TABLE_VERSION 1 + +static QofLogModule log_module = G_LOG_DOMAIN; + +#define SX_MAX_NAME_LEN 2048 + +static gboolean get_autocreate( gpointer pObject ); +static void set_autocreate( gpointer pObject, gboolean value ); +static gboolean get_autonotify( gpointer pObject ); +static void set_autonotify( gpointer pObject, gboolean value ); +static gint get_instance_count( gpointer pObject ); +static gpointer get_template_act_guid( gpointer pObject, const QofParam* param ); +static void set_template_act_guid( gpointer pObject, gpointer pValue ); + +static const GncSqlColumnTableEntry col_table[] = +{ + { "guid", CT_GUID, 0, COL_NNUL|COL_PKEY, "guid" }, + { "name", CT_STRING, SX_MAX_NAME_LEN, 0, NULL, GNC_SX_NAME }, + { "enabled", CT_BOOLEAN, 0, COL_NNUL, NULL, NULL, + (QofAccessFunc)xaccSchedXactionGetEnabled, (QofSetterFunc)xaccSchedXactionSetEnabled }, + { "start_date", CT_GDATE, 0, COL_NNUL, NULL, GNC_SX_START_DATE }, + { "last_occur", CT_GDATE, 0, 0, NULL, GNC_SX_LAST_DATE }, + { "num_occur", CT_INT, 0, COL_NNUL, NULL, GNC_SX_NUM_OCCUR }, + { "rem_occur", CT_INT, 0, COL_NNUL, NULL, GNC_SX_REM_OCCUR }, + { "auto_create", CT_BOOLEAN, 0, COL_NNUL, NULL, NULL, + (QofAccessFunc)get_autocreate, (QofSetterFunc)set_autocreate }, + { "auto_notify", CT_BOOLEAN, 0, COL_NNUL, NULL, NULL, + (QofAccessFunc)get_autonotify, (QofSetterFunc)set_autonotify }, + { "adv_creation", CT_INT, 0, COL_NNUL, NULL, NULL, + (QofAccessFunc)xaccSchedXactionGetAdvanceCreation, + (QofSetterFunc)xaccSchedXactionSetAdvanceCreation }, + { "adv_notify", CT_INT, 0, COL_NNUL, NULL, NULL, + (QofAccessFunc)xaccSchedXactionGetAdvanceReminder, + (QofSetterFunc)xaccSchedXactionSetAdvanceReminder }, + { "instance_count", CT_INT, 0, COL_NNUL, NULL, NULL, + (QofAccessFunc)get_instance_count, (QofSetterFunc)gnc_sx_set_instance_count }, + { "template_act_guid", CT_GUID, 0, COL_NNUL, NULL, NULL, + get_template_act_guid, set_template_act_guid }, + { NULL } +}; + +/* ================================================================= */ + +static gboolean +get_autocreate( gpointer pObject ) +{ + const SchedXaction* pSx = GNC_SX(pObject); + gboolean autoCreate; + gboolean autoNotify; + + g_return_val_if_fail( pObject != NULL, FALSE ); + g_return_val_if_fail( GNC_IS_SX(pObject), FALSE ); + + xaccSchedXactionGetAutoCreate( pSx, &autoCreate, &autoNotify ); + return autoCreate; +} + +static void +set_autocreate( gpointer pObject, gboolean value ) +{ + SchedXaction* pSx = GNC_SX(pObject); + gboolean autoNotify; + gboolean dummy; + + g_return_if_fail( pObject != NULL ); + g_return_if_fail( GNC_IS_SX(pObject) ); + + xaccSchedXactionGetAutoCreate( pSx, &dummy, &autoNotify ); + xaccSchedXactionSetAutoCreate( pSx, value, autoNotify ); +} + +static gboolean +get_autonotify( gpointer pObject ) +{ + const SchedXaction* pSx = GNC_SX(pObject); + gboolean autoCreate; + gboolean autoNotify; + + g_return_val_if_fail( pObject != NULL, FALSE ); + g_return_val_if_fail( GNC_IS_SX(pObject), FALSE ); + + xaccSchedXactionGetAutoCreate( pSx, &autoCreate, &autoNotify ); + return autoNotify; +} + +static void +set_autonotify( gpointer pObject, gboolean value ) +{ + SchedXaction* pSx = GNC_SX(pObject); + gboolean autoCreate; + gboolean dummy; + + g_return_if_fail( pObject != NULL ); + g_return_if_fail( GNC_IS_SX(pObject) ); + + xaccSchedXactionGetAutoCreate( pSx, &autoCreate, &dummy ); + xaccSchedXactionSetAutoCreate( pSx, autoCreate, value ); +} + +static gint +get_instance_count( gpointer pObject ) +{ + const SchedXaction* pSx = GNC_SX(pObject); + + g_return_val_if_fail( pObject != NULL, FALSE ); + g_return_val_if_fail( GNC_IS_SX(pObject), FALSE ); + + return gnc_sx_get_instance_count( pSx, NULL ); +} + +static gpointer +get_template_act_guid( gpointer pObject, const QofParam* param ) +{ + const SchedXaction* pSx = GNC_SX(pObject); + + g_return_val_if_fail( pObject != NULL, NULL ); + g_return_val_if_fail( GNC_IS_SX(pObject), NULL ); + + return (gpointer)xaccAccountGetGUID( pSx->template_acct ); +} + +static void +set_template_act_guid( gpointer pObject, gpointer pValue ) +{ + SchedXaction* pSx = GNC_SX(pObject); + QofBook* pBook; + GUID* guid = (GUID*)pValue; + Account* pAcct; + + g_return_if_fail( pObject != NULL ); + g_return_if_fail( GNC_IS_SX(pObject) ); + g_return_if_fail( pValue != NULL ); + + pBook = qof_instance_get_book( QOF_INSTANCE(pSx) ); + pAcct = xaccAccountLookup( guid, pBook ); + sx_set_template_account( pSx, pAcct ); +} + +/* ================================================================= */ +static SchedXaction* +load_single_sx( GncSqlBackend* be, GncSqlRow* row ) +{ + const GUID* guid; + GUID sx_guid; + SchedXaction* pSx; + GList* schedule = NULL; + + g_return_val_if_fail( be != NULL, NULL ); + g_return_val_if_fail( row != NULL, NULL ); + + guid = gnc_sql_load_guid( be, row ); + sx_guid = *guid; + + pSx = xaccSchedXactionMalloc( be->primary_book ); + + gnc_sx_begin_edit( pSx ); + gnc_sql_load_object( be, row, GNC_SX_ID, pSx, col_table ); + gnc_sql_recurrence_load_list( be, guid, &schedule ); + gnc_sx_set_schedule( pSx, schedule ); + gnc_sx_commit_edit( pSx ); + + return pSx; +} + +static void +load_all_sxes( GncSqlBackend* be ) +{ + GncSqlStatement* stmt = NULL; + GncSqlResult* result; + + g_return_if_fail( be != NULL ); + + stmt = gnc_sql_create_select_statement( be, SCHEDXACTION_TABLE ); + result = gnc_sql_execute_select_statement( be, stmt ); + gnc_sql_statement_dispose( stmt ); + if( result != NULL ) { + GncSqlRow* row; + int r; + SchedXactions *sxes; + GList* list = NULL; + sxes = gnc_book_get_schedxactions( be->primary_book ); + + row = gnc_sql_result_get_first_row( result ); + while( row != NULL ) { + SchedXaction* sx; + + sx = load_single_sx( be, row ); + if( sx != NULL ) { + gnc_sxes_add_sx( sxes, sx ); + list = g_list_append( list, sx ); + } + row = gnc_sql_result_get_next_row( result ); + } + gnc_sql_result_dispose( result ); + + if( list != NULL ) { + gnc_sql_slots_load_for_list( be, list ); + } + } +} + +/* ================================================================= */ +static void +create_sx_tables( GncSqlBackend* be ) +{ + gint version; + + g_return_if_fail( be != NULL ); + + version = gnc_sql_get_table_version( be, SCHEDXACTION_TABLE ); + if( version == 0 ) { + gnc_sql_create_table( be, SCHEDXACTION_TABLE, TABLE_VERSION, col_table ); + } +} + +/* ================================================================= */ +void +gnc_sql_save_schedxaction( GncSqlBackend* be, QofInstance* inst ) +{ + SchedXaction* pSx; + const GUID* guid; + gint op; + gboolean is_infant; + + g_return_if_fail( be != NULL ); + g_return_if_fail( inst != NULL ); + g_return_if_fail( GNC_IS_SX(inst) ); + + pSx = GNC_SX(inst); + + 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; + } + (void)gnc_sql_do_db_operation( be, op, SCHEDXACTION_TABLE, GNC_SX_ID, pSx, col_table ); + guid = qof_instance_get_guid( inst ); + gnc_sql_recurrence_save_list( be, guid, gnc_sx_get_schedule( pSx ) ); + + // Now, commit any slots + if( !qof_instance_get_destroying(inst) ) { + gnc_sql_slots_save( be, guid, is_infant, qof_instance_get_slots( inst ) ); + } else { + gnc_sql_slots_delete( be, guid ); + } +} + +/* ================================================================= */ +void +gnc_sql_init_schedxaction_handler( void ) +{ + static GncSqlObjectBackend be_data = + { + GNC_SQL_BACKEND_VERSION, + GNC_ID_SCHEDXACTION, + gnc_sql_save_schedxaction, /* commit */ + load_all_sxes, /* initial_load */ + create_sx_tables /* create_tables */ + }; + + qof_object_register_backend( GNC_ID_SCHEDXACTION, GNC_SQL_BACKEND, &be_data ); +} +/* ========================== END OF FILE ===================== */ diff --git a/src/backend/sql/gnc-schedxaction-sql.h b/src/backend/sql/gnc-schedxaction-sql.h new file mode 100644 index 0000000000..e2ce1b4073 --- /dev/null +++ b/src/backend/sql/gnc-schedxaction-sql.h @@ -0,0 +1,38 @@ +/******************************************************************** + * gnc-schedxaction-sql.h: load and save data to SQL * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ +/** @file gnc-backend-sql.h + * @brief load and save data to SQL + * @author Copyright (c) 2006-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#ifndef GNC_SCHEDXACTION_SQL_H_ +#define GNC_SCHEDXACTION_SQL_H_ + +#include "qof.h" +#include + +void gnc_sql_init_schedxaction_handler( void ); +void gnc_sql_save_schedxaction( GncSqlBackend* be, QofInstance* inst ); + +#endif /* GNC_SCHEDXACTION_SQL_H_ */ diff --git a/src/backend/sql/gnc-slots-sql.c b/src/backend/sql/gnc-slots-sql.c new file mode 100644 index 0000000000..3bd21943fe --- /dev/null +++ b/src/backend/sql/gnc-slots-sql.c @@ -0,0 +1,567 @@ +/******************************************************************** + * gnc-slots-sql.c: load and save data to SQL * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ +/** @file gnc-slots-sql.c + * @brief load and save data to SQL + * @author Copyright (c) 2006-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL db + */ + +#include "config.h" + +#include + +#include "qof.h" +#include "gnc-engine.h" + +#include "gnc-backend-sql.h" + +#include "gnc-slots-sql.h" + +static QofLogModule log_module = G_LOG_DOMAIN; + +#define TABLE_NAME "slots" +#define TABLE_VERSION 1 + +typedef struct { + GncSqlBackend* be; + const GUID* guid; + KvpFrame* pKvpFrame; + KvpValueType value_type; + KvpValue* pKvpValue; + GString* path; +} slot_info_t; + +static gpointer get_obj_guid( gpointer pObject, const QofParam* param ); +static void set_obj_guid( gpointer pObject, gpointer pValue ); +static gpointer get_path( gpointer pObject, const QofParam* param ); +static void set_path( gpointer pObject, gpointer pValue ); +static gpointer get_slot_type( gpointer pObject, const QofParam* param ); +static void set_slot_type( gpointer pObject, gpointer pValue ); +static gint64 get_int64_val( gpointer pObject, const QofParam* param ); +static void set_int64_val( gpointer pObject, gint64 pValue ); +static gpointer get_string_val( gpointer pObject, const QofParam* param ); +static void set_string_val( gpointer pObject, gpointer pValue ); +static gpointer get_double_val( gpointer pObject, const QofParam* param ); +static void set_double_val( gpointer pObject, gpointer pValue ); +static Timespec get_timespec_val( gpointer pObject, const QofParam* param ); +static void set_timespec_val( gpointer pObject, Timespec ts ); +static gpointer get_guid_val( gpointer pObject, const QofParam* param ); +static void set_guid_val( gpointer pObject, gpointer pValue ); +static gnc_numeric get_numeric_val( gpointer pObject, const QofParam* param ); +static void set_numeric_val( gpointer pObject, gnc_numeric value ); + +#define SLOT_MAX_PATHNAME_LEN 4096 +#define SLOT_MAX_STRINGVAL_LEN 4096 + +static const GncSqlColumnTableEntry col_table[] = +{ + { "obj_guid", CT_GUID, 0, COL_NNUL, NULL, NULL, + get_obj_guid, set_obj_guid }, + { "name", CT_STRING, SLOT_MAX_PATHNAME_LEN, COL_NNUL, NULL, NULL, + get_path, set_path }, + { "slot_type", CT_INT, 0, COL_NNUL, NULL, NULL, + get_slot_type, set_slot_type, }, + { "int64_val", CT_INT64, 0, 0, NULL, NULL, + (QofAccessFunc)get_int64_val, (QofSetterFunc)set_int64_val }, + { "string_val", CT_STRING, SLOT_MAX_PATHNAME_LEN, 0, NULL, NULL, + get_string_val, set_string_val }, + { "double_val", CT_DOUBLE, 0, 0, NULL, NULL, + get_double_val, set_double_val }, + { "timespec_val", CT_TIMESPEC, 0, 0, NULL, NULL, + (QofAccessFunc)get_timespec_val, (QofSetterFunc)set_timespec_val }, + { "guid_val", CT_GUID, 0, 0, NULL, NULL, + get_guid_val, set_guid_val }, + { "numeric_val", CT_NUMERIC, 0, 0, NULL, NULL, + (QofAccessFunc)get_numeric_val, (QofSetterFunc)set_numeric_val }, + { NULL } +}; + +/* Special column table because we need to be able to access the table by +a column other than the primary key */ +static const GncSqlColumnTableEntry obj_guid_col_table[] = +{ + { "obj_guid", CT_GUID, 0, 0, NULL, NULL, get_obj_guid, _retrieve_guid_ }, + { NULL } +}; + +/* ================================================================= */ + +static gpointer +get_obj_guid( gpointer pObject, const QofParam* param ) +{ + slot_info_t* pInfo = (slot_info_t*)pObject; + + g_return_val_if_fail( pObject != NULL, NULL ); + + return (gpointer)pInfo->guid; +} + +static void +set_obj_guid( gpointer pObject, gpointer pValue ) +{ + // Nowhere to put the GUID +} + +static gpointer +get_path( gpointer pObject, const QofParam* param ) +{ + slot_info_t* pInfo = (slot_info_t*)pObject; + + g_return_val_if_fail( pObject != NULL, NULL ); + + return (gpointer)pInfo->path->str; +} + +static void +set_path( gpointer pObject, gpointer pValue ) +{ + slot_info_t* pInfo = (slot_info_t*)pObject; + + g_return_if_fail( pObject != NULL ); + g_return_if_fail( pValue != NULL ); + + pInfo->path = g_string_new( (gchar*)pValue ); +} + +static gpointer +get_slot_type( gpointer pObject, const QofParam* param ) +{ + slot_info_t* pInfo = (slot_info_t*)pObject; + + g_return_val_if_fail( pObject != NULL, NULL ); + + return (gpointer)kvp_value_get_type( pInfo->pKvpValue ); +} + +static void +set_slot_type( gpointer pObject, gpointer pValue ) +{ + slot_info_t* pInfo = (slot_info_t*)pObject; + + g_return_if_fail( pObject != NULL ); + g_return_if_fail( pValue != NULL ); + + pInfo->value_type = (KvpValueType)pValue; +} + +static gint64 +get_int64_val( gpointer pObject, const QofParam* param ) +{ + slot_info_t* pInfo = (slot_info_t*)pObject; + + g_return_val_if_fail( pObject != NULL, 0 ); + + if( kvp_value_get_type( pInfo->pKvpValue ) == KVP_TYPE_GINT64 ) { + return kvp_value_get_gint64( pInfo->pKvpValue ); + } else { + return 0; + } +} + +static void +set_int64_val( gpointer pObject, gint64 value ) +{ + slot_info_t* pInfo = (slot_info_t*)pObject; + + g_return_if_fail( pObject != NULL ); + + if( pInfo->value_type == KVP_TYPE_GINT64 ) { + kvp_frame_set_gint64( pInfo->pKvpFrame, pInfo->path->str, value ); + } +} + +static gpointer +get_string_val( gpointer pObject, const QofParam* param ) +{ + slot_info_t* pInfo = (slot_info_t*)pObject; + + g_return_val_if_fail( pObject != NULL, NULL ); + + if( kvp_value_get_type( pInfo->pKvpValue ) == KVP_TYPE_STRING ) { + return (gpointer)kvp_value_get_string( pInfo->pKvpValue ); + } else { + return NULL; + } +} + +static void +set_string_val( gpointer pObject, gpointer pValue ) +{ + slot_info_t* pInfo = (slot_info_t*)pObject; + + g_return_if_fail( pObject != NULL ); + + if( pInfo->value_type == KVP_TYPE_STRING && pValue != NULL ) { + kvp_frame_set_string( pInfo->pKvpFrame, pInfo->path->str, (const gchar*)pValue ); + } +} + +static gpointer +get_double_val( gpointer pObject, const QofParam* param ) +{ + slot_info_t* pInfo = (slot_info_t*)pObject; + static double d_val; + + g_return_val_if_fail( pObject != NULL, NULL ); + + if( kvp_value_get_type( pInfo->pKvpValue ) == KVP_TYPE_DOUBLE ) { + d_val = kvp_value_get_double( pInfo->pKvpValue ); + return (gpointer)&d_val; + } else { + return NULL; + } +} + +static void +set_double_val( gpointer pObject, gpointer pValue ) +{ + slot_info_t* pInfo = (slot_info_t*)pObject; + + g_return_if_fail( pObject != NULL ); + + if( pInfo->value_type == KVP_TYPE_DOUBLE && pValue != NULL ) { + kvp_frame_set_double( pInfo->pKvpFrame, pInfo->path->str, *(double*)pValue ); + } +} + +static Timespec +get_timespec_val( gpointer pObject, const QofParam* param ) +{ + slot_info_t* pInfo = (slot_info_t*)pObject; + + g_return_val_if_fail( pObject != NULL, gnc_dmy2timespec( 1, 1, 1970 ) ); + +//if( kvp_value_get_type( pInfo->pKvpValue ) == KVP_TYPE_TIMESPEC ) { + return kvp_value_get_timespec( pInfo->pKvpValue ); +} + +static void +set_timespec_val( gpointer pObject, Timespec ts ) +{ + slot_info_t* pInfo = (slot_info_t*)pObject; + + g_return_if_fail( pObject != NULL ); + + if( pInfo->value_type == KVP_TYPE_TIMESPEC ) { + kvp_frame_set_timespec( pInfo->pKvpFrame, pInfo->path->str, ts ); + } +} + +static gpointer +get_guid_val( gpointer pObject, const QofParam* param ) +{ + slot_info_t* pInfo = (slot_info_t*)pObject; + + g_return_val_if_fail( pObject != NULL, NULL ); + + if( kvp_value_get_type( pInfo->pKvpValue ) == KVP_TYPE_GUID ) { + return (gpointer)kvp_value_get_guid( pInfo->pKvpValue ); + } else { + return NULL; + } +} + +static void +set_guid_val( gpointer pObject, gpointer pValue ) +{ + slot_info_t* pInfo = (slot_info_t*)pObject; + + g_return_if_fail( pObject != NULL ); + + if( pInfo->value_type == KVP_TYPE_GUID && pValue != NULL ) { + kvp_frame_set_guid( pInfo->pKvpFrame, pInfo->path->str, (GUID*)pValue ); + } +} + +static gnc_numeric +get_numeric_val( gpointer pObject, const QofParam* param ) +{ + slot_info_t* pInfo = (slot_info_t*)pObject; + + g_return_val_if_fail( pObject != NULL, gnc_numeric_zero() ); + + if( kvp_value_get_type( pInfo->pKvpValue ) == KVP_TYPE_NUMERIC ) { + return kvp_value_get_numeric( pInfo->pKvpValue ); + } else { + return gnc_numeric_zero(); + } +} + +static void +set_numeric_val( gpointer pObject, gnc_numeric value ) +{ + slot_info_t* pInfo = (slot_info_t*)pObject; + + g_return_if_fail( pObject != NULL ); + + if( pInfo->value_type == KVP_TYPE_NUMERIC ) { + kvp_frame_set_numeric( pInfo->pKvpFrame, pInfo->path->str, value ); + } +} + +static void +save_slot( const gchar* key, KvpValue* value, gpointer data ) +{ + slot_info_t* pSlot_info = (slot_info_t*)data; + gint curlen; + + g_return_if_fail( key != NULL ); + g_return_if_fail( value != NULL ); + g_return_if_fail( data != NULL ); + + curlen = pSlot_info->path->len; + pSlot_info->pKvpValue = value; + if( curlen != 0 ) { + g_string_append( pSlot_info->path, "/" ); + } + g_string_append( pSlot_info->path, key ); + + if( kvp_value_get_type( value ) == KVP_TYPE_FRAME ) { + KvpFrame* pKvpFrame = kvp_value_get_frame( value ); + kvp_frame_for_each_slot( pKvpFrame, save_slot, pSlot_info ); + } else { + (void)gnc_sql_do_db_operation( pSlot_info->be, OP_DB_INSERT, TABLE_NAME, + TABLE_NAME, pSlot_info, col_table ); + } + + g_string_truncate( pSlot_info->path, curlen ); +} + +void +gnc_sql_slots_save( GncSqlBackend* be, const GUID* guid, gboolean is_infant, KvpFrame* pFrame ) +{ + slot_info_t slot_info; + + g_return_if_fail( be != NULL ); + g_return_if_fail( guid != NULL ); + g_return_if_fail( pFrame != NULL ); + + // If this is not saving into a new db, clear out the old saved slots first + if( !be->is_pristine_db && !is_infant ) { + gnc_sql_slots_delete( be, guid ); + } + + slot_info.be = be; + slot_info.guid = guid; + slot_info.path = g_string_new( "" ); + kvp_frame_for_each_slot( pFrame, save_slot, &slot_info ); + g_string_free( slot_info.path, TRUE ); +} + +void +gnc_sql_slots_delete( GncSqlBackend* be, const GUID* guid ) +{ + slot_info_t slot_info; + + g_return_if_fail( be != NULL ); + g_return_if_fail( guid != NULL ); + + slot_info.be = be; + slot_info.guid = guid; + (void)gnc_sql_do_db_operation( be, OP_DB_DELETE, TABLE_NAME, + TABLE_NAME, &slot_info, obj_guid_col_table ); +} + +static void +load_slot( GncSqlBackend* be, GncSqlRow* row, KvpFrame* pFrame ) +{ + slot_info_t slot_info; + + g_return_if_fail( be != NULL ); + g_return_if_fail( row != NULL ); + g_return_if_fail( pFrame != NULL ); + + slot_info.be = be; + slot_info.pKvpFrame = pFrame; + slot_info.path = NULL; + + gnc_sql_load_object( be, row, TABLE_NAME, &slot_info, col_table ); + + if( slot_info.path != NULL ) { + g_string_free( slot_info.path, TRUE ); + } +} + +void +gnc_sql_slots_load( GncSqlBackend* be, QofInstance* inst ) +{ + gchar* buf; + GncSqlResult* result; + gchar guid_buf[GUID_ENCODING_LENGTH+1]; + gchar* field_name; + GncSqlStatement* stmt; + GValue value; + const GUID* guid; + KvpFrame* pFrame; + GError* error = NULL; + + g_return_if_fail( be != NULL ); + g_return_if_fail( inst != NULL ); + + guid = qof_instance_get_guid( inst ); + pFrame = qof_instance_get_slots( inst ); + guid_to_string_buff( guid, guid_buf ); + memset( &value, 0, sizeof( value ) ); + g_value_init( &value, G_TYPE_STRING ); + g_value_set_string( &value, guid_buf ); + + buf = g_strdup_printf( "SELECT * FROM %s WHERE obj_guid='%s'", TABLE_NAME, guid_buf ); + stmt = gnc_sql_create_statement_from_sql( be, buf ); + result = gnc_sql_execute_select_statement( be, stmt ); + gnc_sql_statement_dispose( stmt ); + if( result != NULL ) { + GncSqlRow* row = gnc_sql_result_get_first_row( result ); + + while( row != NULL ) { + load_slot( be, row, pFrame ); + row = gnc_sql_result_get_next_row( result ); + } + gnc_sql_result_dispose( result ); + } +} + +static const GUID* +load_obj_guid( const GncSqlBackend* be, GncSqlRow* row ) +{ + static GUID guid; + + g_return_val_if_fail( be != NULL, NULL ); + g_return_val_if_fail( row != NULL, NULL ); + + gnc_sql_load_object( be, row, NULL, &guid, obj_guid_col_table ); + + return &guid; +} + +static void +load_slot_for_list_item( GncSqlBackend* be, GncSqlRow* row, QofCollection* coll ) +{ + slot_info_t slot_info; + const GUID* guid; + QofInstance* inst; + + g_return_if_fail( be != NULL ); + g_return_if_fail( row != NULL ); + g_return_if_fail( coll != NULL ); + + guid = load_obj_guid( be, row ); + inst = qof_collection_lookup_entity( coll, guid ); + + slot_info.be = be; + slot_info.pKvpFrame = qof_instance_get_slots( inst ); + slot_info.path = NULL; + + gnc_sql_load_object( be, row, TABLE_NAME, &slot_info, col_table ); + + if( slot_info.path != NULL ) { + g_string_free( slot_info.path, TRUE ); + } +} + +void +gnc_sql_slots_load_for_list( GncSqlBackend* be, GList* list ) +{ + QofCollection* coll; + GncSqlStatement* stmt; + GString* sql; + gchar guid_buf[GUID_ENCODING_LENGTH+1]; + gboolean first_guid = TRUE; + GncSqlResult* result; + gboolean single_item; + + g_return_if_fail( be != NULL ); + + // Ignore empty list + if( list == NULL ) return; + + coll = qof_instance_get_collection( QOF_INSTANCE(list->data) ); + + // Create the query for all slots for all items on the list + sql = g_string_sized_new( 40+(GUID_ENCODING_LENGTH+3)*g_list_length( list ) ); + g_string_append_printf( sql, "SELECT * FROM %s WHERE %s ", TABLE_NAME, obj_guid_col_table[0].col_name ); + if( g_list_length( list ) != 1 ) { + g_string_append( sql, "IN (" ); + single_item = FALSE; + } else { + g_string_append( sql, "= " ); + single_item = TRUE; + } + (void)gnc_sql_append_guid_list_to_sql( sql, list, G_MAXUINT ); + if( !single_item ) { + g_string_append( sql, ")" ); + } + + // Execute the query and load the slots + stmt = gnc_sql_create_statement_from_sql( be, sql->str ); + result = gnc_sql_execute_select_statement( be, stmt ); + gnc_sql_statement_dispose( stmt ); + if( result != NULL ) { + GncSqlRow* row = gnc_sql_result_get_first_row( result ); + + while( row != NULL ) { + load_slot_for_list_item( be, row, coll ); + row = gnc_sql_result_get_next_row( result ); + } + gnc_sql_result_dispose( result ); + } + g_string_free( sql, FALSE ); +} + +/* ================================================================= */ +static void +create_slots_tables( GncSqlBackend* be ) +{ + gboolean ok; + gint version; + + g_return_if_fail( be != NULL ); + + version = gnc_sql_get_table_version( be, TABLE_NAME ); + if( version == 0 ) { + gnc_sql_create_table( be, TABLE_NAME, TABLE_VERSION, col_table ); +#if 0 + // FIXME: Create index + ok = gnc_sql_create_index( be, "slots_guid_index", TABLE_NAME, obj_guid_col_table, &error ); + if( !ok ) { + PERR( "Unable to create index: %s\n", error->message ); + } +#endif + } +} + +/* ================================================================= */ +void +gnc_sql_init_slots_handler( void ) +{ + static GncSqlObjectBackend be_data = + { + GNC_SQL_BACKEND_VERSION, + GNC_ID_ACCOUNT, + NULL, /* commit - cannot occur */ + NULL, /* initial_load - cannot occur */ + create_slots_tables /* create_tables */ + }; + + qof_object_register_backend( TABLE_NAME, GNC_SQL_BACKEND, &be_data ); +} +/* ========================== END OF FILE ===================== */ diff --git a/src/backend/sql/gnc-slots-sql.h b/src/backend/sql/gnc-slots-sql.h new file mode 100644 index 0000000000..84ea221c27 --- /dev/null +++ b/src/backend/sql/gnc-slots-sql.h @@ -0,0 +1,74 @@ +/******************************************************************** + * gnc-slots-sql.h: load and save data to SQL * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ +/** @file gnc-slots-sql.h + * @brief load and save accounts data to SQL + * @author Copyright (c) 2006-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#ifndef GNC_SLOTS_SQL_H_ +#define GNC_SLOTS_SQL_H_ + +#include "qof.h" +#include + +/** +* gnc_sql_slots_save - Saves slots for an object to the db. +* +* @param be SQL backend +* @param guid Object guid +* @param is_infant Is this an infant object? +* @param pFrame Top-level KVP frame +*/ +void gnc_sql_slots_save( GncSqlBackend* be, const GUID* guid, + gboolean is_infant, KvpFrame* pFrame ); + +/** +* gnc_sql_slots_delete - Deletes slots for an object from the db. +* +* @param be SQL backend +* @param guid Object guid +*/ +void gnc_sql_slots_delete( GncSqlBackend* be, const GUID* guid ); + +/** +* gnc_sql_slots_load - Loads slots for an object from the db. +* +* @param be SQL backend +* @param guid Object guid +*/ +void gnc_sql_slots_load( GncSqlBackend* be, QofInstance* inst ); + +/** +* gnc_sql_slots_load_for_list - Loads slots for a list of objects from the db. +* Loading slots for a list of objects can be faster than loading for one object +* at a time because fewer SQL queries are used. +* +* @param be SQL backend +* @param list List of objects +*/ +void gnc_sql_slots_load_for_list( GncSqlBackend* be, GList* list ); + +void gnc_sql_init_slots_handler( void ); + +#endif /* GNC_SLOTS_SQL_H_ */ diff --git a/src/backend/sql/gnc-transaction-sql.c b/src/backend/sql/gnc-transaction-sql.c new file mode 100644 index 0000000000..d7b67375b1 --- /dev/null +++ b/src/backend/sql/gnc-transaction-sql.c @@ -0,0 +1,752 @@ +/******************************************************************** + * gnc-transaction-sql.c: load and save data to SQL * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ +/** @file gnc-transaction-sql.c + * @brief load and save data to SQL + * @author Copyright (c) 2006-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL db + */ + +#include "config.h" + +#include + +#include "qof.h" +#include "qofquery-p.h" +#include "qofquerycore-p.h" + +#include "Account.h" +#include "Transaction.h" +#include "gnc-lot.h" +#include "engine-helpers.h" + +#include "gnc-backend-sql.h" +#include "gnc-transaction-sql.h" +#include "gnc-commodity.h" +#include "gnc-commodity-sql.h" +#include "gnc-slots-sql.h" + +#include "gnc-engine.h" + +static QofLogModule log_module = G_LOG_DOMAIN; + +#define TRANSACTION_TABLE "transactions" +#define TX_TABLE_VERSION 1 +#define SPLIT_TABLE "splits" +#define SPLIT_TABLE_VERSION 1 + +typedef struct { + GncSqlBackend* be; + const GUID* guid; +} split_info_t; + +#define TX_MAX_NUM_LEN 2048 +#define TX_MAX_DESCRIPTION_LEN 2048 + +static const GncSqlColumnTableEntry tx_col_table[] = +{ + { "guid", CT_GUID, 0, COL_NNUL|COL_PKEY, "guid" }, + { "currency_guid", CT_COMMODITYREF, 0, COL_NNUL, NULL, NULL, + (QofAccessFunc)xaccTransGetCurrency, (QofSetterFunc)xaccTransSetCurrency }, + { "num", CT_STRING, TX_MAX_NUM_LEN, COL_NNUL, NULL, NULL, + (QofAccessFunc)xaccTransGetNum, (QofSetterFunc)xaccTransSetNum }, + { "post_date", CT_TIMESPEC, 0, COL_NNUL, NULL, NULL, + (QofAccessFunc)xaccTransRetDatePostedTS, (QofSetterFunc)gnc_transaction_set_date_posted }, + { "enter_date", CT_TIMESPEC, 0, COL_NNUL, NULL, NULL, + (QofAccessFunc)xaccTransRetDateEnteredTS, (QofSetterFunc)gnc_transaction_set_date_entered }, + { "description", CT_STRING, TX_MAX_DESCRIPTION_LEN, 0, NULL, NULL, + (QofAccessFunc)xaccTransGetDescription, (QofSetterFunc)xaccTransSetDescription }, + { NULL } +}; + +static gpointer get_split_reconcile_state( gpointer pObject, const QofParam* param ); +static void set_split_reconcile_state( gpointer pObject, gpointer pValue ); +static void set_split_reconcile_date( gpointer pObject, Timespec ts ); +static void set_split_lot( gpointer pObject, gpointer pLot ); + +#define SPLIT_MAX_MEMO_LEN 2048 +#define SPLIT_MAX_ACTION_LEN 2048 + +static const GncSqlColumnTableEntry split_col_table[] = +{ + { "guid", CT_GUID, 0, COL_NNUL|COL_PKEY, "guid" }, + { "tx_guid", CT_TXREF, 0, COL_NNUL, NULL, SPLIT_TRANS }, + { "account_guid", CT_ACCOUNTREF, 0, COL_NNUL, NULL, SPLIT_ACCOUNT }, + { "memo", CT_STRING, SPLIT_MAX_MEMO_LEN, COL_NNUL, NULL, SPLIT_MEMO }, + { "action", CT_STRING, SPLIT_MAX_ACTION_LEN, COL_NNUL, NULL, SPLIT_ACTION }, + { "reconcile_state", CT_STRING, 1, COL_NNUL, NULL, NULL, + get_split_reconcile_state, set_split_reconcile_state }, + { "reconcile_date", CT_TIMESPEC, 0, COL_NNUL, NULL, NULL, + (QofAccessFunc)xaccSplitRetDateReconciledTS, (QofSetterFunc)set_split_reconcile_date }, + { "value", CT_NUMERIC, 0, COL_NNUL, NULL, SPLIT_VALUE }, + { "quantity", CT_NUMERIC, 0, COL_NNUL, NULL, SPLIT_AMOUNT }, + { "lot_guid", CT_LOTREF, 0, 0, NULL, NULL, + (QofAccessFunc)xaccSplitGetLot, set_split_lot }, + { NULL } +}; + +static const GncSqlColumnTableEntry guid_col_table[] = +{ + { "tx_guid", CT_GUID, 0, 0, "guid" }, + { NULL } +}; + +static void retrieve_numeric_value( gpointer pObject, gnc_numeric value ); + +/* ================================================================= */ + +static gpointer +get_split_reconcile_state( gpointer pObject, const QofParam* param ) +{ + const Split* pSplit = GNC_SPLIT(pObject); + static gchar c[2]; + + g_return_val_if_fail( pObject != NULL, NULL ); + g_return_val_if_fail( GNC_IS_SPLIT(pObject), NULL ); + + c[0] = xaccSplitGetReconcile( pSplit ); + c[1] = '\0'; + return (gpointer)c; +} + +static void +set_split_reconcile_state( gpointer pObject, gpointer pValue ) +{ + Split* pSplit = GNC_SPLIT(pObject); + const gchar* s = (const gchar*)pValue; + + g_return_if_fail( pObject != NULL ); + g_return_if_fail( GNC_IS_SPLIT(pObject) ); + g_return_if_fail( pValue != NULL ); + + xaccSplitSetReconcile( pSplit, s[0] ); +} + +static void +set_split_reconcile_date( gpointer pObject, Timespec ts ) +{ + g_return_if_fail( pObject != NULL ); + g_return_if_fail( GNC_IS_SPLIT(pObject) ); + + xaccSplitSetDateReconciledTS( GNC_SPLIT(pObject), &ts ); +} + +static void +retrieve_numeric_value( gpointer pObject, gnc_numeric value ) +{ + gnc_numeric* pResult = (gnc_numeric*)pObject; + + g_return_if_fail( pObject != NULL ); + + *pResult = value; +} + +static void +set_split_lot( gpointer pObject, gpointer pLot ) +{ + GNCLot* lot; + Split* split; + + g_return_if_fail( pObject != NULL ); + g_return_if_fail( GNC_IS_SPLIT(pObject) ); + + if( pLot == NULL ) return; + + g_return_if_fail( GNC_IS_LOT(pLot) ); + + split = GNC_SPLIT(pObject); + lot = GNC_LOT(pLot); + gnc_lot_add_split( lot, split ); +} + +// Table to retrieve just the quantity +static GncSqlColumnTableEntry quantity_table[] = +{ + { "quantity", CT_NUMERIC, 0, COL_NNUL, NULL, NULL, NULL, (QofSetterFunc)retrieve_numeric_value }, + { NULL } +}; + +static gnc_numeric +get_gnc_numeric_from_row( GncSqlBackend* be, GncSqlRow* row ) +{ + gnc_numeric val = gnc_numeric_zero(); + + g_return_val_if_fail( be != NULL, val ); + g_return_val_if_fail( row != NULL, val ); + + gnc_sql_load_object( be, row, NULL, &val, quantity_table ); + + return val; +} + +static Split* +load_single_split( GncSqlBackend* be, GncSqlRow* row ) +{ + const GUID* guid; + GUID split_guid; + Split* pSplit; + + g_return_val_if_fail( be != NULL, NULL ); + g_return_val_if_fail( row != NULL, NULL ); + + guid = gnc_sql_load_guid( be, row ); + split_guid = *guid; + + pSplit = xaccSplitLookup( &split_guid, be->primary_book ); + if( pSplit == NULL ) { + pSplit = xaccMallocSplit( be->primary_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 ); + } + + g_assert( pSplit == xaccSplitLookup( &split_guid, be->primary_book ) ); + + return pSplit; +} + +static void +load_all_splits_for_tx( GncSqlBackend* be, const GUID* tx_guid ) +{ + GncSqlResult* result; + gchar guid_buf[GUID_ENCODING_LENGTH+1]; + GncSqlStatement* stmt; + GValue value; + gchar* buf; + GError* error = NULL; + + g_return_if_fail( be != NULL ); + g_return_if_fail( tx_guid != NULL ); + + guid_to_string_buff( tx_guid, guid_buf ); + memset( &value, 0, sizeof( GValue ) ); + g_value_init( &value, G_TYPE_STRING ); + g_value_set_string( &value, guid_buf ); + + buf = g_strdup_printf( "SELECT * FROM %s WHERE tx_guid='%s'", SPLIT_TABLE, guid_buf ); + stmt = gnc_sql_create_statement_from_sql( be, buf ); + g_free( buf ); + + result = gnc_sql_execute_select_statement( be, stmt ); + gnc_sql_statement_dispose( stmt ); + if( result != NULL ) { + int r; + GList* list = NULL; + GncSqlRow* row; + + row = gnc_sql_result_get_first_row( result ); + while( row != NULL ) { + Split* s; + + s = load_single_split( be, row ); + if( s != NULL ) { + list = g_list_append( list, s ); + } + row = gnc_sql_result_get_next_row( result ); + } + gnc_sql_result_dispose( result ); + + if( list != NULL ) { + gnc_sql_slots_load_for_list( be, list ); + } + } +} + +static void +load_splits_for_tx_list( GncSqlBackend* be, GList* list ) +{ + GString* sql; + QofCollection* col; + GncSqlResult* result; + gboolean first_guid = TRUE; + + g_return_if_fail( be != NULL ); + + if( list == NULL ) return; + + sql = g_string_sized_new( 40+(GUID_ENCODING_LENGTH+3)*g_list_length( list ) ); + g_string_append_printf( sql, "SELECT * FROM %s WHERE %s IN (", SPLIT_TABLE, guid_col_table[0].col_name ); + (void)gnc_sql_append_guid_list_to_sql( sql, list, G_MAXUINT ); + g_string_append( sql, ")" ); + + // Execute the query and load the splits + result = gnc_sql_execute_select_sql( be, sql->str ); + if( result != NULL ) { + GList* list = NULL; + GncSqlRow* row; + + row = gnc_sql_result_get_first_row( result ); + while( row != NULL ) { + Split* s; + s = load_single_split( be, row ); + if( s != NULL ) { + list = g_list_append( list, s ); + } + row = gnc_sql_result_get_next_row( result ); + } + + if( list != NULL ) { + gnc_sql_slots_load_for_list( be, list ); + } + + gnc_sql_result_dispose( result ); + } + g_string_free( sql, FALSE ); +} + +static Transaction* +load_single_tx( GncSqlBackend* be, GncSqlRow* row ) +{ + const GUID* guid; + GUID 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 ); + tx_guid = *guid; + + pTx = xaccTransLookup( &tx_guid, be->primary_book ); + if( pTx == NULL ) { + pTx = xaccMallocTransaction( be->primary_book ); + } + xaccTransBeginEdit( pTx ); + gnc_sql_load_object( be, row, GNC_ID_TRANS, pTx, tx_col_table ); + + g_assert( pTx == xaccTransLookup( &tx_guid, be->primary_book ) ); + + return pTx; +} + +static void +query_transactions( GncSqlBackend* be, GncSqlStatement* stmt ) +{ + GncSqlResult* result; + + g_return_if_fail( be != NULL ); + g_return_if_fail( stmt != NULL ); + + result = gnc_sql_execute_select_statement( be, stmt ); + if( result != NULL ) { + GList* tx_list = NULL; + GList* node; + GncSqlRow* row; + Transaction* tx; + + row = gnc_sql_result_get_first_row( result ); + while( row != NULL ) { + tx = load_single_tx( be, row ); + if( tx != NULL ) { + tx_list = g_list_append( tx_list, tx ); + } + row = gnc_sql_result_get_next_row( result ); + } + gnc_sql_result_dispose( result ); + + if( tx_list != NULL ) { + gnc_sql_slots_load_for_list( be, tx_list ); + load_splits_for_tx_list( be, tx_list ); + } + + // Commit all of the transactions + for( node = tx_list; node != NULL; node = node->next ) { + Transaction* pTx = GNC_TRANSACTION(node->data); + xaccTransCommitEdit( pTx ); + } + } +} + +static void +load_tx_by_guid( GncSqlBackend* be, GUID* tx_guid ) +{ + GncSqlStatement* stmt; + gchar* sql; + gchar guid_buf[GUID_ENCODING_LENGTH+1]; + + g_return_if_fail( be != NULL ); + g_return_if_fail( tx_guid != NULL ); + + guid_to_string_buff( tx_guid, guid_buf ); + sql = g_strdup_printf( "SELECT * FROM %s WHERE guid = %s", TRANSACTION_TABLE, guid_buf ); + stmt = gnc_sql_create_statement_from_sql( be, sql ); + query_transactions( be, stmt ); + gnc_sql_statement_dispose( stmt ); +} + +/* ================================================================= */ +static void +load_all_tx( GncSqlBackend* be ) +{ + gchar* sql; + GncSqlStatement* stmt; + + g_return_if_fail( be != NULL ); + + sql = g_strdup_printf( "SELECT * FROM %s", TRANSACTION_TABLE ); + stmt = gnc_sql_create_statement_from_sql( be, sql ); + query_transactions( be, stmt ); + gnc_sql_statement_dispose( stmt ); +} + +/* ================================================================= */ +static void +create_transaction_tables( GncSqlBackend* be ) +{ + gint version; + + g_return_if_fail( be != NULL ); + + version = gnc_sql_get_table_version( be, TRANSACTION_TABLE ); + if( version == 0 ) { + gnc_sql_create_table( be, TRANSACTION_TABLE, TX_TABLE_VERSION, tx_col_table ); + } + + version = gnc_sql_get_table_version( be, SPLIT_TABLE ); + if( version == 0 ) { + gnc_sql_create_table( be, SPLIT_TABLE, SPLIT_TABLE_VERSION, split_col_table ); + } +} +/* ================================================================= */ +static void +delete_split_slots_cb( gpointer data, gpointer user_data ) +{ + split_info_t* split_info = (split_info_t*)user_data; + Split* pSplit = GNC_SPLIT(data); + + g_return_if_fail( data != NULL ); + g_return_if_fail( GNC_IS_SPLIT(data) ); + g_return_if_fail( user_data != NULL ); + + gnc_sql_slots_delete( split_info->be, + qof_instance_get_guid( QOF_INSTANCE(pSplit) ) ); +} + +static void +delete_splits( GncSqlBackend* be, Transaction* pTx ) +{ + split_info_t split_info; + + g_return_if_fail( be != NULL ); + g_return_if_fail( pTx != NULL ); + + (void)gnc_sql_do_db_operation( be, OP_DB_DELETE, SPLIT_TABLE, + SPLIT_TABLE, pTx, guid_col_table ); + split_info.be = be; + + g_list_foreach( xaccTransGetSplitList( pTx ), delete_split_slots_cb, &split_info ); +} + +static void +commit_split( GncSqlBackend* be, QofInstance* inst ) +{ + gint op; + gboolean is_infant; + + g_return_if_fail( inst != NULL ); + g_return_if_fail( be != NULL ); + + 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; + } + (void)gnc_sql_do_db_operation( be, op, SPLIT_TABLE, GNC_ID_SPLIT, inst, split_col_table ); + gnc_sql_slots_save( be, + qof_instance_get_guid( inst ), + is_infant, + qof_instance_get_slots( inst ) ); +} + +static void +save_split_cb( gpointer data, gpointer user_data ) +{ + split_info_t* split_info = (split_info_t*)user_data; + Split* pSplit = GNC_SPLIT(data); + + g_return_if_fail( data != NULL ); + g_return_if_fail( GNC_IS_SPLIT(data) ); + g_return_if_fail( user_data != NULL ); + + commit_split( split_info->be, QOF_INSTANCE(pSplit) ); +} + +static void +save_splits( GncSqlBackend* be, const GUID* tx_guid, SplitList* pSplitList ) +{ + split_info_t split_info; + + g_return_if_fail( be != NULL ); + g_return_if_fail( tx_guid != NULL ); + g_return_if_fail( pSplitList != NULL ); + + split_info.be = be; + split_info.guid = tx_guid; + g_list_foreach( pSplitList, save_split_cb, &split_info ); +} + +static void +save_transaction( GncSqlBackend* be, Transaction* pTx, gboolean do_save_splits ) +{ + const GUID* guid; + gint op; + gboolean is_infant; + QofInstance* inst; + + g_return_if_fail( be != NULL ); + g_return_if_fail( pTx != NULL ); + + 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 ) { + // Ensure the commodity is in the db + gnc_sql_save_commodity( be, xaccTransGetCurrency( pTx ) ); + } + + (void)gnc_sql_do_db_operation( be, op, TRANSACTION_TABLE, GNC_ID_TRANS, pTx, tx_col_table ); + + // Commit slots and splits + guid = qof_instance_get_guid( inst ); + if( !qof_instance_get_destroying(inst) ) { + gnc_sql_slots_save( be, guid, is_infant, qof_instance_get_slots( inst ) ); + if( do_save_splits ) { + save_splits( be, guid, xaccTransGetSplitList( pTx ) ); + } + } else { + gnc_sql_slots_delete( be, guid ); + delete_splits( be, pTx ); + } +} + +void +gnc_sql_save_transaction( GncSqlBackend* be, QofInstance* inst ) +{ + g_return_if_fail( be != NULL ); + g_return_if_fail( inst != NULL ); + g_return_if_fail( GNC_IS_TRANS(inst) ); + + save_transaction( be, GNC_TRANS(inst), TRUE ); +} + +static void +commit_transaction( GncSqlBackend* be, QofInstance* inst ) +{ + g_return_if_fail( be != NULL ); + g_return_if_fail( inst != NULL ); + g_return_if_fail( GNC_IS_TRANS(inst) ); + + save_transaction( be, GNC_TRANS(inst), FALSE ); +} + +/* ================================================================= */ +static const GUID* +get_guid_from_query( QofQuery* pQuery ) +{ + GList* pOrTerms; + GList* pAndTerms; + GList* andTerm; + QofQueryTerm* pTerm; + QofQueryPredData* pPredData; + GSList* pParamPath; + + g_return_val_if_fail( pQuery != NULL, NULL ); + + pOrTerms = qof_query_get_terms( pQuery ); + pAndTerms = (GList*)pOrTerms->data; + andTerm = pAndTerms->next; + pTerm = (QofQueryTerm*)andTerm->data; + + pPredData = qof_query_term_get_pred_data( pTerm ); + pParamPath = qof_query_term_get_param_path( pTerm ); + + if( strcmp( pPredData->type_name, "guid" ) == 0 ) { + query_guid_t pData = (query_guid_t)pPredData; + return pData->guids->data; + } else { + return NULL; + } +} + +static gpointer +compile_split_query( GncSqlBackend* be, QofQuery* pQuery ) +{ + GString* sql; + const GUID* acct_guid; + gchar guid_buf[GUID_ENCODING_LENGTH+1]; + GncSqlResult* result; + gchar* buf; + + g_return_val_if_fail( be != NULL, NULL ); + g_return_val_if_fail( pQuery != NULL, NULL ); + + acct_guid = get_guid_from_query( pQuery ); + guid_to_string_buff( acct_guid, guid_buf ); + sql = g_string_new( "" ); + g_string_printf( sql, "SELECT DISTINCT tx_guid FROM %s WHERE account_guid='%s'", SPLIT_TABLE, guid_buf ); + result = gnc_sql_execute_select_sql( be, sql->str ); + if( result != NULL ) { + int numRows; + int r; + GncSqlRow* row; + + numRows = gnc_sql_result_get_num_rows( result ); + sql = g_string_sized_new( 40+(GUID_ENCODING_LENGTH+3)*numRows ); + + if( numRows != 1 ) { + g_string_printf( sql, "SELECT * FROM %s WHERE guid IN (", TRANSACTION_TABLE ); + } else { + g_string_printf( sql, "SELECT * FROM %s WHERE guid =", TRANSACTION_TABLE ); + } + + row = gnc_sql_result_get_first_row( result ); + for( r = 0; row != NULL; r++ ) { + const GUID* guid; + + guid = gnc_sql_load_tx_guid( be, row ); + guid_to_string_buff( guid, guid_buf ); + if( r != 0 ) { + g_string_append( sql, "," ); + } + g_string_append( sql, "'" ); + g_string_append( sql, guid_buf ); + g_string_append( sql, "'" ); + row = gnc_sql_result_get_next_row( result ); + } + gnc_sql_result_dispose( result ); + + if( numRows != 1 ) { + g_string_append( sql, ")" ); + } + } + + buf = sql->str; + g_string_free( sql, FALSE ); + return buf; +} + +static void +run_split_query( GncSqlBackend* be, gpointer pQuery ) +{ + GncSqlStatement* stmt; + gchar* sql; + + g_return_if_fail( be != NULL ); + g_return_if_fail( pQuery != NULL ); + + sql = (gchar*)pQuery; + + stmt = gnc_sql_create_statement_from_sql( be, sql ); + query_transactions( be, stmt ); + gnc_sql_statement_dispose( stmt ); +} + +static void +free_split_query( GncSqlBackend* be, gpointer pQuery ) +{ + g_return_if_fail( be != NULL ); + g_return_if_fail( pQuery != NULL ); + + g_free( pQuery ); +} + +/* ----------------------------------------------------------------- */ +static void +load_tx_guid( const GncSqlBackend* be, GncSqlRow* row, + QofSetterFunc setter, gpointer pObject, + const GncSqlColumnTableEntry* table_row ) +{ + const GValue* val; + GUID guid; + const GUID* pGuid; + Transaction* tx = NULL; + + g_return_if_fail( be != NULL ); + g_return_if_fail( row != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + + val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name ); + if( val == NULL ) { + pGuid = NULL; + } else { + string_to_guid( g_value_get_string( val ), &guid ); + pGuid = &guid; + } + if( pGuid != NULL ) { + tx = xaccTransLookup( pGuid, be->primary_book ); + } + if( table_row->gobj_param_name != NULL ) { + g_object_set( pObject, table_row->gobj_param_name, tx, NULL ); + } else { + (*setter)( pObject, (const gpointer)tx ); + } +} + +static col_type_handler_t tx_guid_handler + = { load_tx_guid, + gnc_sql_add_objectref_guid_col_info_to_list, + gnc_sql_add_colname_to_list, + gnc_sql_add_gvalue_objectref_guid_to_slist }; +/* ================================================================= */ +void +gnc_sql_init_transaction_handler( void ) +{ + static GncSqlObjectBackend be_data_tx = + { + GNC_SQL_BACKEND_VERSION, + GNC_ID_TRANS, + commit_transaction, /* commit */ + load_all_tx, /* initial_load */ + create_transaction_tables /* create tables */ + }; + static GncSqlObjectBackend be_data_split = + { + GNC_SQL_BACKEND_VERSION, + GNC_ID_SPLIT, + commit_split, /* commit */ + NULL, /* initial_load */ + NULL, /* create tables */ + compile_split_query, + run_split_query, + free_split_query + }; + + qof_object_register_backend( GNC_ID_TRANS, GNC_SQL_BACKEND, &be_data_tx ); + qof_object_register_backend( GNC_ID_SPLIT, GNC_SQL_BACKEND, &be_data_split ); + + gnc_sql_register_col_type_handler( CT_TXREF, &tx_guid_handler ); +} + +/* ========================== END OF FILE ===================== */ diff --git a/src/backend/sql/gnc-transaction-sql.h b/src/backend/sql/gnc-transaction-sql.h new file mode 100644 index 0000000000..b18127341c --- /dev/null +++ b/src/backend/sql/gnc-transaction-sql.h @@ -0,0 +1,51 @@ +/******************************************************************** + * gnc-transaction-sql.h: load and save data to SQL * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ +/** @file gnc-transaction-sql.h + * @brief load and save data to SQL + * @author Copyright (c) 2006-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#ifndef GNC_TRANSACTION_SQL_H_ +#define GNC_TRANSACTION_SQL_H_ + +#include "qof.h" +#include + +void gnc_sql_init_transaction_handler( void ); +void gnc_sql_transaction_commit_splits( GncSqlBackend* be, Transaction* pTx ); +void gnc_sql_save_transaction( GncSqlBackend* be, QofInstance* inst ); +void gnc_sql_get_account_balances( GncSqlBackend* be, Account* pAccount, + gnc_numeric* start_balance, + gnc_numeric* cleared_balance, + gnc_numeric* reconciled_balance ); + +typedef struct { + Account* acct; + gnc_numeric start_balance; + gnc_numeric cleared_balance; + gnc_numeric reconciled_balance; +} acct_balances_t; +GList* gnc_sql_get_account_balances_for_list( GncSqlBackend* be, GList* list ); + +#endif /* GNC_TRANSACTION_SQL_H_ */ diff --git a/src/backend/sql/test/Makefile.am b/src/backend/sql/test/Makefile.am new file mode 100644 index 0000000000..156cdd73f7 --- /dev/null +++ b/src/backend/sql/test/Makefile.am @@ -0,0 +1,61 @@ +SUBDIRS = . + +test_column_types_SOURCES = \ + ${top_srcdir}/src/backend/sql/gnc-backend-sql.c \ + ${top_srcdir}/src/backend/sql/gnc-account-sql.c \ + ${top_srcdir}/src/backend/sql/gnc-book-sql.c \ + ${top_srcdir}/src/backend/sql/gnc-budget-sql.c \ + ${top_srcdir}/src/backend/sql/gnc-commodity-sql.c \ + ${top_srcdir}/src/backend/sql/gnc-lots-sql.c \ + ${top_srcdir}/src/backend/sql/gnc-price-sql.c \ + ${top_srcdir}/src/backend/sql/gnc-recurrence-sql.c \ + ${top_srcdir}/src/backend/sql/gnc-schedxaction-sql.c \ + ${top_srcdir}/src/backend/sql/gnc-slots-sql.c \ + ${top_srcdir}/src/backend/sql/gnc-transaction-sql.c \ + test-column-types.c + +TESTS = \ + test-column-types + +GNC_TEST_DEPS := \ + --gnc-module-dir ${top_builddir}/src/engine \ + --guile-load-dir ${top_builddir}/src/engine \ + --library-dir ${top_builddir}/lib/libqof/qof \ + --library-dir ${top_builddir}/src/core-utils \ + --library-dir ${top_builddir}/src/gnc-module \ + --library-dir ${top_builddir}/src/engine \ + --library-dir ${top_builddir}/src/backend/sql + +TESTS_ENVIRONMENT := \ + GNC_ACCOUNT_PATH=${top_srcdir}/accounts/C \ + SRCDIR=${srcdir} \ + $(shell ${top_srcdir}/src/gnc-test-env --no-exports ${GNC_TEST_DEPS}) + +check_PROGRAMS = \ + test-column-types + +#noinst_HEADERS = test-file-stuff.h + +LDADD = ${top_builddir}/src/test-core/libgncmod-test.la \ + ${top_builddir}/src/gnc-module/libgnc-module.la \ + ${top_builddir}/src/engine/libgncmod-engine.la \ + ${top_builddir}/src/engine/test-core/libgncmod-test-engine.la \ + ${top_builddir}/src/core-utils/libgnc-core-utils.la \ + ${QOF_LIBS} \ + ${top_builddir}/lib/libc/libc-missing.la + +AM_CFLAGS = \ + -I${top_srcdir}/lib/libc \ + -I${top_srcdir}/src \ + -I${top_srcdir}/src/core-utils \ + -I${top_srcdir}/src/gnc-module \ + -I${top_srcdir}/src/test-core \ + -I${top_srcdir}/src/engine \ + -I${top_srcdir}/src/engine/test-core \ + -I${top_srcdir}/src/backend/sql \ + ${GLIB_CFLAGS} \ + ${QOF_CFLAGS} \ + ${GUILE_INCS} \ + ${GCONF_CFLAGS} + +INCLUDES = -DG_LOG_DOMAIN=\"gnc.backend.sql\" diff --git a/src/backend/sql/test/test-column-types.c b/src/backend/sql/test/test-column-types.c new file mode 100644 index 0000000000..76daa5c1ad --- /dev/null +++ b/src/backend/sql/test/test-column-types.c @@ -0,0 +1,46 @@ +/*************************************************************************** + * test-column-types.c + * + * Tests the basic SQL column types + * + * Copyright 2008 Phil Longstaff + * plongstaff@rogers.com + ****************************************************************************/ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + +#include "config.h" +#include "qof.h" +#include "cashobjects.h" +#include "test-stuff.h" + +#include "gnc-backend-sql.h" + +int main( int argc, char ** argv ) +{ + qof_init(); + cashobjects_register(); + gnc_sql_init( NULL ); +/* do_test( + qof_load_backend_library ("../.libs/", GNC_LIB_NAME), + " loading gnc-backend-gda GModule failed"); +*/ + print_test_results(); + qof_close(); + exit( get_rv() ); +} diff --git a/src/bin/Makefile.am b/src/bin/Makefile.am index 32ad7b615a..d575c6c069 100644 --- a/src/bin/Makefile.am +++ b/src/bin/Makefile.am @@ -32,6 +32,7 @@ gnucash: gnucash.in ${top_builddir}/config.status Makefile rm -f $@.tmp sed < $< > $@.tmp \ -e 's#@-BIN_DIR-@#${bindir}#g' \ + -e 's#@-GNC_DBD_DIR-@#${GNC_DBD_DIR}#g' \ -e 's#@-GNC_GUILE_MODULE_DIR-@#${GNC_SHAREDIR}/guile-modules#g' \ -e 's#@-GNC_SCM_INSTALL_DIR-@#${GNC_SCM_INSTALL_DIR}#g' \ -e 's#@-GNC_LIB_INSTALLDIR-@#${libdir}#' \ @@ -40,10 +41,11 @@ gnucash: gnucash.in ${top_builddir}/config.status Makefile mv $@.tmp $@ chmod u+x $@ -gnucash-valgrind: gnucash-valgrind.in ${top_builddir}/config.status Makefile +gnucash-gdb: gnucash-gdb.in ${top_builddir}/config.status Makefile rm -f $@.tmp sed < $< > $@.tmp \ -e 's#@-BIN_DIR-@#${bindir}#g' \ + -e 's#@-GNC_DBD_DIR-@#${GNC_DBD_DIR}#g' \ -e 's#@-GNC_GUILE_MODULE_DIR-@#${GNC_SHAREDIR}/guile-modules#g' \ -e 's#@-GNC_SCM_INSTALL_DIR-@#${GNC_SCM_INSTALL_DIR}#g' \ -e 's#@-GNC_LIB_INSTALLDIR-@#${libdir}#' \ @@ -52,7 +54,20 @@ gnucash-valgrind: gnucash-valgrind.in ${top_builddir}/config.status Makefile mv $@.tmp $@ chmod u+x $@ -CLEANFILES = $(BUILT_SOURCES) gnucash gnucash-valgrind +gnucash-valgrind: gnucash-valgrind.in ${top_builddir}/config.status Makefile + rm -f $@.tmp + sed < $< > $@.tmp \ + -e 's#@-BIN_DIR-@#${bindir}#g' \ + -e 's#@-GNC_DBD_DIR-@#${GNC_DBD_DIR}#g' \ + -e 's#@-GNC_GUILE_MODULE_DIR-@#${GNC_SHAREDIR}/guile-modules#g' \ + -e 's#@-GNC_SCM_INSTALL_DIR-@#${GNC_SCM_INSTALL_DIR}#g' \ + -e 's#@-GNC_LIB_INSTALLDIR-@#${libdir}#' \ + -e 's#@-GNC_PKGLIB_INSTALLDIR-@#${pkglibdir}#g' \ + -e "s#@-TOP_SRC_DIR-@#`pwd`/${top_srcdir}#g" + mv $@.tmp $@ + chmod u+x $@ + +CLEANFILES = $(BUILT_SOURCES) gnucash gnucash-valgrind gnucash-gdb # We handle gnucash scripts in a somewhat unexpected way, but we do # this so that a user who doesn't necessarily have the right @@ -71,13 +86,13 @@ CLEANFILES = $(BUILT_SOURCES) gnucash gnucash-valgrind # by these top-level "common" scripts. gnc_common_scripts = gnucash-env gnucash-make-guids -bin_SCRIPTS = ${gnc_common_scripts} update-gnucash-gconf gnucash gnucash-valgrind +bin_SCRIPTS = ${gnc_common_scripts} update-gnucash-gconf gnucash gnucash-valgrind gnucash-gdb # if you change gncoverridedir, make sure you change ./overrides/Makefile.am too. gncoverridesdir = ${GNC_LIBEXECDIR}/overrides EXTRA_DIST = generate-gnc-script update-gnucash-gconf.in \ - gnucash.in gnucash-valgrind.in + gnucash.in gnucash-valgrind.in gnucash-gdb.in ## Gnucash scripts -- real code is in overrides, these just get you there. ${gnc_common_scripts}: generate-gnc-script ${top_builddir}/config.status diff --git a/src/bin/gnucash-gdb.in b/src/bin/gnucash-gdb.in new file mode 100644 index 0000000000..3a34fb3590 --- /dev/null +++ b/src/bin/gnucash-gdb.in @@ -0,0 +1,32 @@ +#!/bin/sh + +PATH="@-BIN_DIR-@:${PATH}" +export PATH + +GUILE_WARN_DEPRECATED="no" +export GUILE_WARN_DEPRECATED + +GNC_MODULE_PATH="@-GNC_PKGLIB_INSTALLDIR-@:${GNC_MODULE_PATH}" + +EXTRA_PATH="${EXTRA_PATH}:@-GNC_GUILE_MODULE_DIR-@" +EXTRA_PATH="${EXTRA_PATH}:@-GNC_SCM_INSTALL_DIR-@" +GUILE_LOAD_PATH="${EXTRA_PATH}:${GUILE_LOAD_PATH}" + +EXTRA_LIBS="${GNC_MODULE_PATH}" +EXTRA_LIBS="${EXTRA_LIBS}:@-GNC_LIB_INSTALLDIR-@" +EXTRA_LIBS="${EXTRA_LIBS}:@-GNC_PKGLIB_INSTALLDIR-@" + +LD_LIBRARY_PATH="${EXTRA_LIBS}:${LD_LIBRARY_PATH}" +TOP_SRC_DIR="@-TOP_SRC_DIR-@" +GNC_DBD_DIR="@-GNC_DBD_DIR-@" + +export GNC_MODULE_PATH +export GUILE_LOAD_PATH +export LD_LIBRARY_PATH +export GNC_DBD_DIR + +# +# Other potentially useful options, particularly for valgrind-2.x: +# --tool=memcheck --trace-children=yes +# +exec gdb gnucash-bin "$@" diff --git a/src/bin/gnucash-valgrind.in b/src/bin/gnucash-valgrind.in index 97be6f68f0..62029d29ab 100644 --- a/src/bin/gnucash-valgrind.in +++ b/src/bin/gnucash-valgrind.in @@ -18,19 +18,25 @@ EXTRA_LIBS="${EXTRA_LIBS}:@-GNC_PKGLIB_INSTALLDIR-@" LD_LIBRARY_PATH="${EXTRA_LIBS}:${LD_LIBRARY_PATH}" TOP_SRC_DIR="@-TOP_SRC_DIR-@" +GNC_DBD_DIR="@-GNC_DBD_DIR-@" export GNC_MODULE_PATH export GUILE_LOAD_PATH export LD_LIBRARY_PATH +export GNC_DBD_DIR # # Other potentially useful options, particularly for valgrind-2.x: # --tool=memcheck --trace-children=yes # exec valgrind -v \ - --suppressions=${TOP_SRC_DIR}/src/valgrind-gnucash.supp \ + --suppressions=${TOP_SRC_DIR}/src/valgrind-gnucash.supp \ + --suppressions=${TOP_SRC_DIR}/src/valgrind-glib.supp \ + --suppressions=${TOP_SRC_DIR}/src/valgrind-libfontconfig.supp \ + --suppressions=${TOP_SRC_DIR}/src/valgrind-libgda.supp \ + --suppressions=${TOP_SRC_DIR}/src/valgrind-libguile.supp \ + --num-callers=25 \ --error-limit=no \ - --tool=callgrind \ - --instr-atstart=no \ - --collect-atstart=no \ + --tool=memcheck \ + --leak-check=full \ gnucash-bin "$@" diff --git a/src/bin/gnucash.in b/src/bin/gnucash.in index 342a09c2bb..08ca5ff242 100644 --- a/src/bin/gnucash.in +++ b/src/bin/gnucash.in @@ -18,10 +18,12 @@ EXTRA_LIBS="${EXTRA_LIBS}@-PATH_SEPARATOR-@@-GNC_PKGLIB_INSTALLDIR-@" LD_LIBRARY_PATH="${EXTRA_LIBS}@-PATH_SEPARATOR-@${LD_LIBRARY_PATH}" DYLD_LIBRARY_PATH="${EXTRA_LIBS}@-PATH_SEPARATOR-@${DYLD_LIBRARY_PATH}" +GNC_DBD_DIR="@-GNC_DBD_DIR-@" export GNC_MODULE_PATH export GUILE_LOAD_PATH export LD_LIBRARY_PATH export DYLD_LIBRARY_PATH +export GNC_DBD_DIR exec gnucash-bin "$@" diff --git a/src/business/business-core/Makefile.am b/src/business/business-core/Makefile.am index 90616c3c34..1f345a7aa3 100644 --- a/src/business/business-core/Makefile.am +++ b/src/business/business-core/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = . test file +SUBDIRS = . sql test file pkglib_LTLIBRARIES = libgncmod-business-core.la diff --git a/src/business/business-core/file/Makefile.am b/src/business/business-core/file/Makefile.am index b786fbb2a9..c1f088fad6 100644 --- a/src/business/business-core/file/Makefile.am +++ b/src/business/business-core/file/Makefile.am @@ -1,6 +1,6 @@ SUBDIRS = . -pkglib_LTLIBRARIES = libgncmod-business-backend-file.la +pkglib_LTLIBRARIES = libgncmod-business-backend-xml.la AM_CFLAGS = \ -I${top_srcdir}/src \ @@ -13,8 +13,8 @@ AM_CFLAGS = \ ${QOF_CFLAGS} \ ${GLIB_CFLAGS} -libgncmod_business_backend_file_la_SOURCES = \ - gncmod-business-backend-file.c \ +libgncmod_business_backend_xml_la_SOURCES = \ + gncmod-business-backend-xml.c \ gnc-address-xml-v2.c \ gnc-bill-term-xml-v2.c \ gnc-customer-xml-v2.c \ @@ -41,11 +41,11 @@ noinst_HEADERS = \ gnc-vendor-xml-v2.h \ xml-helpers.h -libgncmod_business_backend_file_la_LDFLAGS = -module -avoid-version +libgncmod_business_backend_xml_la_LDFLAGS = -module -avoid-version -libgncmod_business_backend_file_la_LIBADD = \ +libgncmod_business_backend_xml_la_LIBADD = \ ${top_builddir}/src/business/business-core/libgncmod-business-core.la \ - ${top_builddir}/src/backend/file/libgnc-backend-file-utils.la \ + ${top_builddir}/src/backend/file/libgnc-backend-xml-utils.la \ ${top_builddir}/src/engine/libgncmod-engine.la \ ${top_builddir}/src/gnc-module/libgnc-module.la \ ${LIBXML2_LIBS} \ diff --git a/src/business/business-core/file/gncmod-business-backend-xml.c b/src/business/business-core/file/gncmod-business-backend-xml.c new file mode 100644 index 0000000000..5f37099f46 --- /dev/null +++ b/src/business/business-core/file/gncmod-business-backend-xml.c @@ -0,0 +1,112 @@ +/********************************************************************* + * gncmod-business-backend-file.c + * module definition/initialization for the file backend module + * + * Copyright (c) 2002 Derek Atkins + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, contact: + * + * Free Software Foundation Voice: +1-617-542-5942 + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 + * Boston, MA 02110-1301, USA gnu@gnu.org + * + *********************************************************************/ + +#include "config.h" +#include + +#include "gnc-module.h" +#include "gnc-module-api.h" +#include "gnc-engine.h" +#include "io-gncxml-v2.h" + +#include "gnc-address-xml-v2.h" +#include "gnc-bill-term-xml-v2.h" +#include "gnc-customer-xml-v2.h" +#include "gnc-employee-xml-v2.h" +#include "gnc-entry-xml-v2.h" +#include "gnc-invoice-xml-v2.h" +#include "gnc-job-xml-v2.h" +#include "gnc-order-xml-v2.h" +#include "gnc-owner-xml-v2.h" +#include "gnc-tax-table-xml-v2.h" +#include "gnc-vendor-xml-v2.h" + +GNC_MODULE_API_DECL(libgncmod_business_backend_xml) + +/* version of the gnc module system interface we require */ +int libgncmod_business_backend_xml_gnc_module_system_interface = 0; + +/* module versioning uses libtool semantics. */ +int libgncmod_business_backend_xml_gnc_module_current = 0; +int libgncmod_business_backend_xml_gnc_module_revision = 0; +int libgncmod_business_backend_xml_gnc_module_age = 0; + +static GNCModule bus_core; +static GNCModule file; + + +char * +libgncmod_business_backend_xml_gnc_module_path(void) +{ + return g_strdup("gnucash/business-core-xml"); +} + +char * +libgncmod_business_backend_xml_gnc_module_description(void) +{ + return g_strdup("The XML (v2) parsers for GnuCash business objects"); +} + +int +libgncmod_business_backend_xml_gnc_module_init(int refcount) +{ + if(!gnc_engine_is_initialized()) { return FALSE; } + + bus_core = gnc_module_load("gnucash/business-core", 0); + if(!bus_core) return FALSE; + + if (refcount == 0) { + /* Initialize our pointers into the backend subsystem */ + gnc_address_xml_initialize (); + gnc_billterm_xml_initialize (); + gnc_customer_xml_initialize (); + gnc_employee_xml_initialize (); + gnc_entry_xml_initialize (); + gnc_invoice_xml_initialize (); + gnc_job_xml_initialize (); + gnc_order_xml_initialize (); + gnc_owner_xml_initialize (); + gnc_taxtable_xml_initialize (); + gnc_vendor_xml_initialize (); + } + + return TRUE; +} + +int +libgncmod_business_backend_xml_gnc_module_end(int refcount) +{ + int unload = TRUE; + + if (bus_core) + unload = gnc_module_unload(bus_core); + + if (refcount == 0) { + bus_core = NULL; + file = NULL; + } + + return unload; +} diff --git a/src/business/business-core/gncAddress.c b/src/business/business-core/gncAddress.c index f2caa29c4d..00b5a5f5cf 100644 --- a/src/business/business-core/gncAddress.c +++ b/src/business/business-core/gncAddress.c @@ -127,7 +127,7 @@ qofAddressSetOwner(GncAddress *addr, QofInstance *ent) } static QofInstance* -qofAddressGetOwner(GncAddress *addr) +qofAddressGetOwner(const GncAddress *addr) { if(!addr) { return NULL; } @@ -135,7 +135,7 @@ qofAddressGetOwner(GncAddress *addr) } GncAddress * -gncCloneAddress (GncAddress *from, QofInstance *new_parent, QofBook *book) +gncCloneAddress (const GncAddress *from, QofInstance *new_parent, QofBook *book) { GncAddress *addr; diff --git a/src/business/business-core/gncAddressP.h b/src/business/business-core/gncAddressP.h index 6036ff26d4..df719eee50 100644 --- a/src/business/business-core/gncAddressP.h +++ b/src/business/business-core/gncAddressP.h @@ -33,7 +33,7 @@ gboolean gncAddressRegister (void); /** Make a copy of the address, setting the parent to 'new_parent' */ -GncAddress * gncCloneAddress (GncAddress *from, QofInstance *new_parent, QofBook *book); +GncAddress * gncCloneAddress (const GncAddress *from, QofInstance *new_parent, QofBook *book); #endif /* GNC_ADDRESSP_H_ */ diff --git a/src/business/business-core/gncBillTerm.c b/src/business/business-core/gncBillTerm.c index 233985fb35..4d353a9beb 100644 --- a/src/business/business-core/gncBillTerm.c +++ b/src/business/business-core/gncBillTerm.c @@ -367,7 +367,9 @@ void gncBillTermSetParent (GncBillTerm *term, GncBillTerm *parent) if (parent) gncBillTermAddChild(parent, term); term->refcount = 0; - gncBillTermMakeInvisible (term); + if( parent != NULL ) { + gncBillTermMakeInvisible (term); + } gncBillTermCommitEdit (term); } @@ -467,19 +469,19 @@ GList * gncBillTermGetTerms (QofBook *book) return bi->terms; } -const char *gncBillTermGetName (GncBillTerm *term) +const char *gncBillTermGetName (const GncBillTerm *term) { if (!term) return NULL; return term->name; } -const char *gncBillTermGetDescription (GncBillTerm *term) +const char *gncBillTermGetDescription (const GncBillTerm *term) { if (!term) return NULL; return term->desc; } -GncBillTermType gncBillTermGetType (GncBillTerm *term) +GncBillTermType gncBillTermGetType (const GncBillTerm *term) { if (!term) return 0; return term->type; @@ -489,37 +491,37 @@ GncBillTermType gncBillTermGetType (GncBillTerm *term) AS_STRING_FUNC(GncBillTermType, ENUM_TERMS_TYPE) static -const char* qofBillTermGetType (GncBillTerm *term) +const char* qofBillTermGetType (const GncBillTerm *term) { if (!term) { return NULL; } return GncBillTermTypeasString(term->type); } -gint gncBillTermGetDueDays (GncBillTerm *term) +gint gncBillTermGetDueDays (const GncBillTerm *term) { if (!term) return 0; return term->due_days; } -gint gncBillTermGetDiscountDays (GncBillTerm *term) +gint gncBillTermGetDiscountDays (const GncBillTerm *term) { if (!term) return 0; return term->disc_days; } -gnc_numeric gncBillTermGetDiscount (GncBillTerm *term) +gnc_numeric gncBillTermGetDiscount (const GncBillTerm *term) { if (!term) return gnc_numeric_zero (); return term->discount; } -gint gncBillTermGetCutoff (GncBillTerm *term) +gint gncBillTermGetCutoff (const GncBillTerm *term) { if (!term) return 0; return term->cutoff; } -static GncBillTerm *gncBillTermCopy (GncBillTerm *term) +static GncBillTerm *gncBillTermCopy (const GncBillTerm *term) { GncBillTerm *t; @@ -557,25 +559,25 @@ GncBillTerm *gncBillTermReturnChild (GncBillTerm *term, gboolean make_new) return child; } -GncBillTerm *gncBillTermGetParent (GncBillTerm *term) +GncBillTerm *gncBillTermGetParent (const GncBillTerm *term) { if (!term) return NULL; return term->parent; } -gint64 gncBillTermGetRefcount (GncBillTerm *term) +gint64 gncBillTermGetRefcount (const GncBillTerm *term) { if (!term) return 0; return term->refcount; } -gboolean gncBillTermGetInvisible (GncBillTerm *term) +gboolean gncBillTermGetInvisible (const GncBillTerm *term) { if (!term) return FALSE; return term->invisible; } -int gncBillTermCompare (GncBillTerm *a, GncBillTerm *b) +int gncBillTermCompare (const GncBillTerm *a, const GncBillTerm *b) { int ret; @@ -589,7 +591,7 @@ int gncBillTermCompare (GncBillTerm *a, GncBillTerm *b) return safe_strcmp (a->desc, b->desc); } -gboolean gncBillTermIsDirty (GncBillTerm *term) +gboolean gncBillTermIsDirty (const GncBillTerm *term) { if (!term) return FALSE; return qof_instance_get_dirty_flag(term); @@ -605,7 +607,7 @@ gboolean gncBillTermIsDirty (GncBillTerm *term) * XXX explain this, the logic is totally opaque to me. */ static void -compute_monthyear (GncBillTerm *term, Timespec post_date, +compute_monthyear (const GncBillTerm *term, Timespec post_date, int *month, int *year) { int iday, imonth, iyear; @@ -638,7 +640,7 @@ compute_monthyear (GncBillTerm *term, Timespec post_date, /* XXX explain this, the logic is totally opaque to me. */ static Timespec -compute_time (GncBillTerm *term, Timespec post_date, int days) +compute_time (const GncBillTerm *term, Timespec post_date, int days) { Timespec res = post_date; int day, month, year; @@ -659,7 +661,7 @@ compute_time (GncBillTerm *term, Timespec post_date, int days) } Timespec -gncBillTermComputeDueDate (GncBillTerm *term, Timespec post_date) +gncBillTermComputeDueDate (const GncBillTerm *term, Timespec post_date) { Timespec res = post_date; if (!term) return res; @@ -668,7 +670,7 @@ gncBillTermComputeDueDate (GncBillTerm *term, Timespec post_date) } Timespec -gncBillTermComputeDiscountDate (GncBillTerm *term, Timespec post_date) +gncBillTermComputeDiscountDate (const GncBillTerm *term, Timespec post_date) { Timespec res = post_date; if (!term) return res; diff --git a/src/business/business-core/gncBillTerm.h b/src/business/business-core/gncBillTerm.h index d0fd57d4f9..d880dfa927 100644 --- a/src/business/business-core/gncBillTerm.h +++ b/src/business/business-core/gncBillTerm.h @@ -124,30 +124,30 @@ void gncBillTermSetCutoff (GncBillTerm *term, gint cutoff); GncBillTerm *gncBillTermLookupByName (QofBook *book, const char *name); GList * gncBillTermGetTerms (QofBook *book); -const char *gncBillTermGetName (GncBillTerm *term); -const char *gncBillTermGetDescription (GncBillTerm *term); -GncBillTermType gncBillTermGetType (GncBillTerm *term); -gint gncBillTermGetDueDays (GncBillTerm *term); -gint gncBillTermGetDiscountDays (GncBillTerm *term); -gnc_numeric gncBillTermGetDiscount (GncBillTerm *term); -gint gncBillTermGetCutoff (GncBillTerm *term); +const char *gncBillTermGetName (const GncBillTerm *term); +const char *gncBillTermGetDescription (const GncBillTerm *term); +GncBillTermType gncBillTermGetType (const GncBillTerm *term); +gint gncBillTermGetDueDays (const GncBillTerm *term); +gint gncBillTermGetDiscountDays (const GncBillTerm *term); +gnc_numeric gncBillTermGetDiscount (const GncBillTerm *term); +gint gncBillTermGetCutoff (const GncBillTerm *term); -gboolean gncBillTermIsDirty (GncBillTerm *term); +gboolean gncBillTermIsDirty (const GncBillTerm *term); -GncBillTerm *gncBillTermGetParent (GncBillTerm *term); +GncBillTerm *gncBillTermGetParent (const GncBillTerm *term); GncBillTerm *gncBillTermReturnChild (GncBillTerm *term, gboolean make_new); #define gncBillTermGetChild(t) gncBillTermReturnChild((t),FALSE) -gint64 gncBillTermGetRefcount (GncBillTerm *term); +gint64 gncBillTermGetRefcount (const GncBillTerm *term); /** @} */ -int gncBillTermCompare (GncBillTerm *a, GncBillTerm *b); +int gncBillTermCompare (const GncBillTerm *a, const GncBillTerm *b); /********************************************************/ /* functions to compute dates from Bill Terms */ /* Compute the due date and discount dates from the post date */ -Timespec gncBillTermComputeDueDate (GncBillTerm *term, Timespec post_date); -Timespec gncBillTermComputeDiscountDate (GncBillTerm *term, Timespec post_date); +Timespec gncBillTermComputeDueDate (const GncBillTerm *term, Timespec post_date); +Timespec gncBillTermComputeDiscountDate (const GncBillTerm *term, Timespec post_date); /* deprecated */ #define gncBillTermGetGUID(x) qof_instance_get_guid (QOF_INSTANCE(x)) diff --git a/src/business/business-core/gncBillTermP.h b/src/business/business-core/gncBillTermP.h index 32c36011c1..18a41df8e7 100644 --- a/src/business/business-core/gncBillTermP.h +++ b/src/business/business-core/gncBillTermP.h @@ -38,7 +38,7 @@ void gncBillTermSetChild (GncBillTerm *term, GncBillTerm *child); void gncBillTermSetRefcount (GncBillTerm *term, gint64 refcount); void gncBillTermMakeInvisible (GncBillTerm *term); -gboolean gncBillTermGetInvisible (GncBillTerm *term); +gboolean gncBillTermGetInvisible (const GncBillTerm *term); /** The gncCloneBillTerm() routine makes a copy of the indicated * bill term, placing it in the indicated book. It copies diff --git a/src/business/business-core/gncCustomer.c b/src/business/business-core/gncCustomer.c index c4661553fa..9aff625ec1 100644 --- a/src/business/business-core/gncCustomer.c +++ b/src/business/business-core/gncCustomer.c @@ -418,19 +418,19 @@ void gncCustomerCommitEdit (GncCustomer *cust) /* ============================================================== */ /* Get Functions */ -const char * gncCustomerGetID (GncCustomer *cust) +const char * gncCustomerGetID (const GncCustomer *cust) { if (!cust) return NULL; return cust->id; } -const char * gncCustomerGetName (GncCustomer *cust) +const char * gncCustomerGetName (const GncCustomer *cust) { if (!cust) return NULL; return cust->name; } -GncAddress * gncCustomerGetAddr (GncCustomer *cust) +GncAddress * gncCustomerGetAddr (const GncCustomer *cust) { if (!cust) return NULL; return cust->addr; @@ -470,67 +470,67 @@ qofCustomerSetShipAddr (GncCustomer *cust, QofInstance *ship_addr_ent) gncCustomerCommitEdit(cust); } -GncAddress * gncCustomerGetShipAddr (GncCustomer *cust) +GncAddress * gncCustomerGetShipAddr (const GncCustomer *cust) { if (!cust) return NULL; return cust->shipaddr; } -const char * gncCustomerGetNotes (GncCustomer *cust) +const char * gncCustomerGetNotes (const GncCustomer *cust) { if (!cust) return NULL; return cust->notes; } -GncBillTerm * gncCustomerGetTerms (GncCustomer *cust) +GncBillTerm * gncCustomerGetTerms (const GncCustomer *cust) { if (!cust) return NULL; return cust->terms; } -GncTaxIncluded gncCustomerGetTaxIncluded (GncCustomer *cust) +GncTaxIncluded gncCustomerGetTaxIncluded (const GncCustomer *cust) { if (!cust) return GNC_TAXINCLUDED_USEGLOBAL; return cust->taxincluded; } -gnc_commodity * gncCustomerGetCurrency (GncCustomer *cust) +gnc_commodity * gncCustomerGetCurrency (const GncCustomer *cust) { if (!cust) return NULL; return cust->currency; } -gboolean gncCustomerGetActive (GncCustomer *cust) +gboolean gncCustomerGetActive (const GncCustomer *cust) { if (!cust) return FALSE; return cust->active; } -gnc_numeric gncCustomerGetDiscount (GncCustomer *cust) +gnc_numeric gncCustomerGetDiscount (const GncCustomer *cust) { if (!cust) return gnc_numeric_zero(); return cust->discount; } -gnc_numeric gncCustomerGetCredit (GncCustomer *cust) +gnc_numeric gncCustomerGetCredit (const GncCustomer *cust) { if (!cust) return gnc_numeric_zero(); return cust->credit; } -gboolean gncCustomerGetTaxTableOverride (GncCustomer *customer) +gboolean gncCustomerGetTaxTableOverride (const GncCustomer *customer) { if (!customer) return FALSE; return customer->taxtable_override; } -GncTaxTable* gncCustomerGetTaxTable (GncCustomer *customer) +GncTaxTable* gncCustomerGetTaxTable (const GncCustomer *customer) { if (!customer) return NULL; return customer->taxtable; } -GList * gncCustomerGetJoblist (GncCustomer *cust, gboolean show_all) +GList * gncCustomerGetJoblist (const GncCustomer *cust, gboolean show_all) { if (!cust) return NULL; @@ -557,7 +557,7 @@ gboolean gncCustomerIsDirty (GncCustomer *cust) /* Other functions */ -int gncCustomerCompare (GncCustomer *a, GncCustomer *b) +int gncCustomerCompare (const GncCustomer *a, const GncCustomer *b) { if (!a && !b) return 0; if (!a && b) return 1; diff --git a/src/business/business-core/gncCustomer.h b/src/business/business-core/gncCustomer.h index 319fc1982b..e30011a334 100644 --- a/src/business/business-core/gncCustomer.h +++ b/src/business/business-core/gncCustomer.h @@ -118,26 +118,26 @@ void gncCustomerRemoveJob (GncCustomer *customer, GncJob *job); #define gncCustomerLookup(book,guid) \ QOF_BOOK_LOOKUP_ENTITY((book),(guid),GNC_ID_CUSTOMER, GncCustomer) -const char * gncCustomerGetID (GncCustomer *customer); -const char * gncCustomerGetName (GncCustomer *customer); -GncAddress * gncCustomerGetAddr (GncCustomer *customer); -GncAddress * gncCustomerGetShipAddr (GncCustomer *customer); -const char * gncCustomerGetNotes (GncCustomer *customer); -GncBillTerm * gncCustomerGetTerms (GncCustomer *customer); -GncTaxIncluded gncCustomerGetTaxIncluded (GncCustomer *customer); -gboolean gncCustomerGetActive (GncCustomer *customer); -gnc_numeric gncCustomerGetDiscount (GncCustomer *customer); -gnc_numeric gncCustomerGetCredit (GncCustomer *customer); -gnc_commodity * gncCustomerGetCurrency (GncCustomer *customer); +const char * gncCustomerGetID (const GncCustomer *customer); +const char * gncCustomerGetName (const GncCustomer *customer); +GncAddress * gncCustomerGetAddr (const GncCustomer *customer); +GncAddress * gncCustomerGetShipAddr (const GncCustomer *customer); +const char * gncCustomerGetNotes (const GncCustomer *customer); +GncBillTerm * gncCustomerGetTerms (const GncCustomer *customer); +GncTaxIncluded gncCustomerGetTaxIncluded (const GncCustomer *customer); +gboolean gncCustomerGetActive (const GncCustomer *customer); +gnc_numeric gncCustomerGetDiscount (const GncCustomer *customer); +gnc_numeric gncCustomerGetCredit (const GncCustomer *customer); +gnc_commodity * gncCustomerGetCurrency (const GncCustomer *customer); -gboolean gncCustomerGetTaxTableOverride (GncCustomer *customer); -GncTaxTable* gncCustomerGetTaxTable (GncCustomer *customer); +gboolean gncCustomerGetTaxTableOverride (const GncCustomer *customer); +GncTaxTable* gncCustomerGetTaxTable (const GncCustomer *customer); -GList * gncCustomerGetJoblist (GncCustomer *customer, gboolean show_all); +GList * gncCustomerGetJoblist (const GncCustomer *customer, gboolean show_all); /** @} */ gboolean gncCustomerIsDirty (GncCustomer *customer); -int gncCustomerCompare (GncCustomer *a, GncCustomer *b); +int gncCustomerCompare (const GncCustomer *a, const GncCustomer *b); #define CUSTOMER_ID "id" #define CUSTOMER_NAME "name" diff --git a/src/business/business-core/gncEmployee.c b/src/business/business-core/gncEmployee.c index b0ff9238b2..15c82b53fc 100644 --- a/src/business/business-core/gncEmployee.c +++ b/src/business/business-core/gncEmployee.c @@ -299,67 +299,67 @@ qofEmployeeSetAddr (GncEmployee *employee, QofInstance *addr_ent) /* ============================================================== */ /* Get Functions */ -const char * gncEmployeeGetID (GncEmployee *employee) +const char * gncEmployeeGetID (const GncEmployee *employee) { if (!employee) return NULL; return employee->id; } -const char * gncEmployeeGetUsername (GncEmployee *employee) +const char * gncEmployeeGetUsername (const GncEmployee *employee) { if (!employee) return NULL; return employee->username; } -GncAddress * gncEmployeeGetAddr (GncEmployee *employee) +GncAddress * gncEmployeeGetAddr (const GncEmployee *employee) { if (!employee) return NULL; return employee->addr; } -const char * gncEmployeeGetLanguage (GncEmployee *employee) +const char * gncEmployeeGetLanguage (const GncEmployee *employee) { if (!employee) return NULL; return employee->language; } -const char * gncEmployeeGetAcl (GncEmployee *employee) +const char * gncEmployeeGetAcl (const GncEmployee *employee) { if (!employee) return NULL; return employee->acl; } -gnc_numeric gncEmployeeGetWorkday (GncEmployee *employee) +gnc_numeric gncEmployeeGetWorkday (const GncEmployee *employee) { if (!employee) return gnc_numeric_zero(); return employee->workday; } -gnc_numeric gncEmployeeGetRate (GncEmployee *employee) +gnc_numeric gncEmployeeGetRate (const GncEmployee *employee) { if (!employee) return gnc_numeric_zero(); return employee->rate; } -gnc_commodity * gncEmployeeGetCurrency (GncEmployee *employee) +gnc_commodity * gncEmployeeGetCurrency (const GncEmployee *employee) { if (!employee) return NULL; return employee->currency; } -gboolean gncEmployeeGetActive (GncEmployee *employee) +gboolean gncEmployeeGetActive (const GncEmployee *employee) { if (!employee) return FALSE; return employee->active; } -Account * gncEmployeeGetCCard (GncEmployee *employee) +Account * gncEmployeeGetCCard (const GncEmployee *employee) { if (!employee) return NULL; return employee->ccard_acc; } -gboolean gncEmployeeIsDirty (GncEmployee *employee) +gboolean gncEmployeeIsDirty (const GncEmployee *employee) { if (!employee) return FALSE; return (qof_instance_get_dirty_flag(employee) @@ -399,7 +399,7 @@ void gncEmployeeCommitEdit (GncEmployee *employee) /* ============================================================== */ /* Other functions */ -int gncEmployeeCompare (GncEmployee *a, GncEmployee *b) +int gncEmployeeCompare (const GncEmployee *a, const GncEmployee *b) { if (!a && !b) return 0; if (!a && b) return 1; diff --git a/src/business/business-core/gncEmployee.h b/src/business/business-core/gncEmployee.h index da7441eef9..e0a3e482f6 100644 --- a/src/business/business-core/gncEmployee.h +++ b/src/business/business-core/gncEmployee.h @@ -59,7 +59,7 @@ GncEmployee *gncEmployeeCreate (QofBook *book); void gncEmployeeDestroy (GncEmployee *employee); void gncEmployeeBeginEdit (GncEmployee *employee); void gncEmployeeCommitEdit (GncEmployee *employee); -int gncEmployeeCompare (GncEmployee *a, GncEmployee *b); +int gncEmployeeCompare (const GncEmployee *a, const GncEmployee *b); /** @} */ /** @name Set Functions @@ -80,16 +80,16 @@ void qofEmployeeSetAddr (GncEmployee *employee, QofInstance *addr_ent); /** @name Get Functions @{ */ QofBook * gncEmployeeGetBook (GncEmployee *employee); -const char * gncEmployeeGetID (GncEmployee *employee); -const char * gncEmployeeGetUsername (GncEmployee *employee); -GncAddress * gncEmployeeGetAddr (GncEmployee *employee); -const char * gncEmployeeGetLanguage (GncEmployee *employee); -const char * gncEmployeeGetAcl (GncEmployee *employee); -gnc_numeric gncEmployeeGetWorkday (GncEmployee *employee); -gnc_numeric gncEmployeeGetRate (GncEmployee *employee); -gnc_commodity * gncEmployeeGetCurrency (GncEmployee *employee); -gboolean gncEmployeeGetActive (GncEmployee *employee); -Account * gncEmployeeGetCCard (GncEmployee *employee); +const char * gncEmployeeGetID (const GncEmployee *employee); +const char * gncEmployeeGetUsername (const GncEmployee *employee); +GncAddress * gncEmployeeGetAddr (const GncEmployee *employee); +const char * gncEmployeeGetLanguage (const GncEmployee *employee); +const char * gncEmployeeGetAcl (const GncEmployee *employee); +gnc_numeric gncEmployeeGetWorkday (const GncEmployee *employee); +gnc_numeric gncEmployeeGetRate (const GncEmployee *employee); +gnc_commodity * gncEmployeeGetCurrency (const GncEmployee *employee); +gboolean gncEmployeeGetActive (const GncEmployee *employee); +Account * gncEmployeeGetCCard (const GncEmployee *employee); /** @} */ @@ -102,7 +102,7 @@ Account * gncEmployeeGetCCard (GncEmployee *employee); #define gncEmployeeLookup(book,guid) \ QOF_BOOK_LOOKUP_ENTITY((book),(guid),GNC_ID_EMPLOYEE, GncEmployee) -gboolean gncEmployeeIsDirty (GncEmployee *employee); +gboolean gncEmployeeIsDirty (const GncEmployee *employee); #define EMPLOYEE_ID "id" #define EMPLOYEE_USERNAME "username" diff --git a/src/business/business-core/gncEntry.c b/src/business/business-core/gncEntry.c index 2d639b5614..6c9056f622 100644 --- a/src/business/business-core/gncEntry.c +++ b/src/business/business-core/gncEntry.c @@ -658,39 +658,39 @@ void gncEntryCopy (const GncEntry *src, GncEntry *dest) /* ================================================================ */ /* Get Functions */ -Timespec gncEntryGetDate (GncEntry *entry) +Timespec gncEntryGetDate (const GncEntry *entry) { Timespec ts; ts.tv_sec = 0; ts.tv_nsec = 0; if (!entry) return ts; return entry->date; } -Timespec gncEntryGetDateEntered (GncEntry *entry) +Timespec gncEntryGetDateEntered (const GncEntry *entry) { Timespec ts; ts.tv_sec = 0; ts.tv_nsec = 0; if (!entry) return ts; return entry->date_entered; } -const char * gncEntryGetDescription (GncEntry *entry) +const char * gncEntryGetDescription (const GncEntry *entry) { if (!entry) return NULL; return entry->desc; } -const char * gncEntryGetAction (GncEntry *entry) +const char * gncEntryGetAction (const GncEntry *entry) { if (!entry) return NULL; return entry->action; } -const char * gncEntryGetNotes (GncEntry *entry) +const char * gncEntryGetNotes (const GncEntry *entry) { if (!entry) return NULL; return entry->notes; } -gnc_numeric gncEntryGetQuantity (GncEntry *entry) +gnc_numeric gncEntryGetQuantity (const GncEntry *entry) { if (!entry) return gnc_numeric_zero(); return entry->quantity; @@ -698,37 +698,37 @@ gnc_numeric gncEntryGetQuantity (GncEntry *entry) /* Customer Invoice */ -Account * gncEntryGetInvAccount (GncEntry *entry) +Account * gncEntryGetInvAccount (const GncEntry *entry) { if (!entry) return NULL; return entry->i_account; } -gnc_numeric gncEntryGetInvPrice (GncEntry *entry) +gnc_numeric gncEntryGetInvPrice (const GncEntry *entry) { if (!entry) return gnc_numeric_zero(); return entry->i_price; } -gnc_numeric gncEntryGetInvDiscount (GncEntry *entry) +gnc_numeric gncEntryGetInvDiscount (const GncEntry *entry) { if (!entry) return gnc_numeric_zero(); return entry->i_discount; } -GncAmountType gncEntryGetInvDiscountType (GncEntry *entry) +GncAmountType gncEntryGetInvDiscountType (const GncEntry *entry) { if (!entry) return 0; return entry->i_disc_type; } -GncDiscountHow gncEntryGetInvDiscountHow (GncEntry *entry) +GncDiscountHow gncEntryGetInvDiscountHow (const GncEntry *entry) { if (!entry) return 0; return entry->i_disc_how; } -char* qofEntryGetInvDiscType (GncEntry *entry) +char* qofEntryGetInvDiscType (const GncEntry *entry) { char *type_string; @@ -737,7 +737,7 @@ char* qofEntryGetInvDiscType (GncEntry *entry) return type_string; } -char* qofEntryGetInvDiscHow (GncEntry *entry) +char* qofEntryGetInvDiscHow (const GncEntry *entry) { char *type_string; @@ -746,19 +746,19 @@ char* qofEntryGetInvDiscHow (GncEntry *entry) return type_string; } -gboolean gncEntryGetInvTaxable (GncEntry *entry) +gboolean gncEntryGetInvTaxable (const GncEntry *entry) { if (!entry) return FALSE; return entry->i_taxable; } -gboolean gncEntryGetInvTaxIncluded (GncEntry *entry) +gboolean gncEntryGetInvTaxIncluded (const GncEntry *entry) { if (!entry) return FALSE; return entry->i_taxincluded; } -GncTaxTable * gncEntryGetInvTaxTable (GncEntry *entry) +GncTaxTable * gncEntryGetInvTaxTable (const GncEntry *entry) { if (!entry) return NULL; return entry->i_tax_table; @@ -766,37 +766,37 @@ GncTaxTable * gncEntryGetInvTaxTable (GncEntry *entry) /* vendor bills */ -Account * gncEntryGetBillAccount (GncEntry *entry) +Account * gncEntryGetBillAccount (const GncEntry *entry) { if (!entry) return NULL; return entry->b_account; } -gnc_numeric gncEntryGetBillPrice (GncEntry *entry) +gnc_numeric gncEntryGetBillPrice (const GncEntry *entry) { if (!entry) return gnc_numeric_zero(); return entry->b_price; } -gboolean gncEntryGetBillTaxable (GncEntry *entry) +gboolean gncEntryGetBillTaxable (const GncEntry *entry) { if (!entry) return FALSE; return entry->b_taxable; } -gboolean gncEntryGetBillTaxIncluded (GncEntry *entry) +gboolean gncEntryGetBillTaxIncluded (const GncEntry *entry) { if (!entry) return FALSE; return entry->b_taxincluded; } -GncTaxTable * gncEntryGetBillTaxTable (GncEntry *entry) +GncTaxTable * gncEntryGetBillTaxTable (const GncEntry *entry) { if (!entry) return NULL; return entry->b_tax_table; } -gboolean gncEntryGetBillable (GncEntry *entry) +gboolean gncEntryGetBillable (const GncEntry *entry) { if (!entry) return FALSE; return entry->billable; @@ -808,25 +808,25 @@ GncOwner * gncEntryGetBillTo (GncEntry *entry) return &entry->billto; } -GncEntryPaymentType gncEntryGetBillPayment (GncEntry* entry) +GncEntryPaymentType gncEntryGetBillPayment (const GncEntry* entry) { if (!entry) return 0; return entry->b_payment; } -GncInvoice * gncEntryGetInvoice (GncEntry *entry) +GncInvoice * gncEntryGetInvoice (const GncEntry *entry) { if (!entry) return NULL; return entry->invoice; } -GncInvoice * gncEntryGetBill (GncEntry *entry) +GncInvoice * gncEntryGetBill (const GncEntry *entry) { if (!entry) return NULL; return entry->bill; } -GncOrder * gncEntryGetOrder (GncEntry *entry) +GncOrder * gncEntryGetOrder (const GncEntry *entry) { if (!entry) return NULL; return entry->order; @@ -867,7 +867,7 @@ GncOrder * gncEntryGetOrder (GncEntry *entry) * to let a consumer know how much they saved. */ void gncEntryComputeValue (gnc_numeric qty, gnc_numeric price, - GncTaxTable *tax_table, gboolean tax_included, + const GncTaxTable *tax_table, gboolean tax_included, gnc_numeric discount, GncAmountType discount_type, GncDiscountHow discount_how, int SCU, gnc_numeric *value, gnc_numeric *discount_value, @@ -1044,7 +1044,7 @@ void gncEntryComputeValue (gnc_numeric qty, gnc_numeric price, } static int -get_entry_commodity_denom (GncEntry *entry) +get_entry_commodity_denom (const GncEntry *entry) { gnc_commodity *c; if (!entry) @@ -1178,7 +1178,7 @@ gnc_numeric gncEntryReturnDiscountValue (GncEntry *entry, gboolean is_inv) } /* XXXX this exsitnace of this routine is just wrong */ -gboolean gncEntryIsOpen (GncEntry *entry) +gboolean gncEntryIsOpen (const GncEntry *entry) { if (!entry) return FALSE; return (qof_instance_get_editlevel(entry) > 0); @@ -1211,7 +1211,7 @@ void gncEntryCommitEdit (GncEntry *entry) gncEntryOnDone, entry_free); } -int gncEntryCompare (GncEntry *a, GncEntry *b) +int gncEntryCompare (const GncEntry *a, const GncEntry *b) { int compare; diff --git a/src/business/business-core/gncEntry.h b/src/business/business-core/gncEntry.h index dde0f95995..2973799b9c 100644 --- a/src/business/business-core/gncEntry.h +++ b/src/business/business-core/gncEntry.h @@ -135,39 +135,39 @@ void gncEntrySetBillPayment (GncEntry *entry, GncEntryPaymentType type); /* GET FUNCTIONS */ /** @name Generic (shared) data @{ */ -Timespec gncEntryGetDate (GncEntry *entry); -Timespec gncEntryGetDateEntered (GncEntry *entry); -const char * gncEntryGetDescription (GncEntry *entry); -const char * gncEntryGetAction (GncEntry *entry); -const char * gncEntryGetNotes (GncEntry *notes); -gnc_numeric gncEntryGetQuantity (GncEntry *entry); +Timespec gncEntryGetDate (const GncEntry *entry); +Timespec gncEntryGetDateEntered (const GncEntry *entry); +const char * gncEntryGetDescription (const GncEntry *entry); +const char * gncEntryGetAction (const GncEntry *entry); +const char * gncEntryGetNotes (const GncEntry *notes); +gnc_numeric gncEntryGetQuantity (const GncEntry *entry); /** @} */ /** @name Customer Invoices @{ */ -Account * gncEntryGetInvAccount (GncEntry *entry); -gnc_numeric gncEntryGetInvPrice (GncEntry *entry); -gnc_numeric gncEntryGetInvDiscount (GncEntry *entry); -GncAmountType gncEntryGetInvDiscountType (GncEntry *entry); -GncDiscountHow gncEntryGetInvDiscountHow (GncEntry *entry); -char* qofEntryGetInvDiscType (GncEntry *entry); -char* qofEntryGetInvDiscHow (GncEntry *entry); -gboolean gncEntryGetInvTaxable (GncEntry *entry); -gboolean gncEntryGetInvTaxIncluded (GncEntry *entry); -GncTaxTable * gncEntryGetInvTaxTable (GncEntry *entry); +Account * gncEntryGetInvAccount (const GncEntry *entry); +gnc_numeric gncEntryGetInvPrice (const GncEntry *entry); +gnc_numeric gncEntryGetInvDiscount (const GncEntry *entry); +GncAmountType gncEntryGetInvDiscountType (const GncEntry *entry); +GncDiscountHow gncEntryGetInvDiscountHow (const GncEntry *entry); +char* qofEntryGetInvDiscType (const GncEntry *entry); +char* qofEntryGetInvDiscHow (const GncEntry *entry); +gboolean gncEntryGetInvTaxable (const GncEntry *entry); +gboolean gncEntryGetInvTaxIncluded (const GncEntry *entry); +GncTaxTable * gncEntryGetInvTaxTable (const GncEntry *entry); /** @} */ /** @name Vendor Bills (and Employee Expenses) @{ */ -Account * gncEntryGetBillAccount (GncEntry *entry); -gnc_numeric gncEntryGetBillPrice (GncEntry *entry); -gboolean gncEntryGetBillTaxable (GncEntry *entry); -gboolean gncEntryGetBillTaxIncluded (GncEntry *entry); -GncTaxTable * gncEntryGetBillTaxTable (GncEntry *entry); -gboolean gncEntryGetBillable (GncEntry *entry); +Account * gncEntryGetBillAccount (const GncEntry *entry); +gnc_numeric gncEntryGetBillPrice (const GncEntry *entry); +gboolean gncEntryGetBillTaxable (const GncEntry *entry); +gboolean gncEntryGetBillTaxIncluded (const GncEntry *entry); +GncTaxTable * gncEntryGetBillTaxTable (const GncEntry *entry); +gboolean gncEntryGetBillable (const GncEntry *entry); GncOwner *gncEntryGetBillTo (GncEntry *entry); -GncEntryPaymentType gncEntryGetBillPayment (GncEntry* entry); +GncEntryPaymentType gncEntryGetBillPayment (const GncEntry* entry); /** @} */ void gncEntryCopy (const GncEntry *src, GncEntry *dest); @@ -201,7 +201,7 @@ void gncEntryGetValue (GncEntry *entry, gboolean is_inv, gnc_numeric *value, GList **tax_values); void gncEntryComputeValue (gnc_numeric qty, gnc_numeric price, - GncTaxTable *tax_table, gboolean tax_included, + const GncTaxTable *tax_table, gboolean tax_included, gnc_numeric discount, GncAmountType discount_type, GncDiscountHow discount_how, int SCU, /* return values */ @@ -210,9 +210,9 @@ void gncEntryComputeValue (gnc_numeric qty, gnc_numeric price, /** @} */ -GncOrder * gncEntryGetOrder (GncEntry *entry); -GncInvoice * gncEntryGetInvoice (GncEntry *entry); -GncInvoice * gncEntryGetBill (GncEntry *entry); +GncOrder * gncEntryGetOrder (const GncEntry *entry); +GncInvoice * gncEntryGetInvoice (const GncEntry *entry); +GncInvoice * gncEntryGetBill (const GncEntry *entry); /** Return a pointer to the instance gncEntry that is identified * by the guid, and is residing in the book. Returns NULL if the @@ -223,10 +223,10 @@ GncInvoice * gncEntryGetBill (GncEntry *entry); #define gncEntryLookup(book,guid) \ QOF_BOOK_LOOKUP_ENTITY((book),(guid),GNC_ID_ENTRY, GncEntry) -gboolean gncEntryIsOpen (GncEntry *entry); +gboolean gncEntryIsOpen (const GncEntry *entry); void gncEntryBeginEdit (GncEntry *entry); void gncEntryCommitEdit (GncEntry *entry); -int gncEntryCompare (GncEntry *a, GncEntry *b); +int gncEntryCompare (const GncEntry *a, const GncEntry *b); #define ENTRY_DATE "date" #define ENTRY_DATE_ENTERED "date-entered" diff --git a/src/business/business-core/gncInvoice.c b/src/business/business-core/gncInvoice.c index b60a88609e..f01882a9c6 100644 --- a/src/business/business-core/gncInvoice.c +++ b/src/business/business-core/gncInvoice.c @@ -469,7 +469,7 @@ void gncInvoiceSortEntries (GncInvoice *invoice) /* ================================================================== */ /* Get Functions */ -const char * gncInvoiceGetID (GncInvoice *invoice) +const char * gncInvoiceGetID (const GncInvoice *invoice) { if (!invoice) return NULL; return invoice->id; @@ -501,21 +501,21 @@ qofInvoiceGetBillTo (GncInvoice *invoice) return QOF_INSTANCE(billto); } -Timespec gncInvoiceGetDateOpened (GncInvoice *invoice) +Timespec gncInvoiceGetDateOpened (const GncInvoice *invoice) { Timespec ts; ts.tv_sec = 0; ts.tv_nsec = 0; if (!invoice) return ts; return invoice->date_opened; } -Timespec gncInvoiceGetDatePosted (GncInvoice *invoice) +Timespec gncInvoiceGetDatePosted (const GncInvoice *invoice) { Timespec ts; ts.tv_sec = 0; ts.tv_nsec = 0; if (!invoice) return ts; return invoice->date_posted; } -Timespec gncInvoiceGetDateDue (GncInvoice *invoice) +Timespec gncInvoiceGetDateDue (const GncInvoice *invoice) { Transaction *txn; Timespec ts; ts.tv_sec = 0; ts.tv_nsec = 0; @@ -525,19 +525,19 @@ Timespec gncInvoiceGetDateDue (GncInvoice *invoice) return xaccTransRetDateDueTS (txn); } -GncBillTerm * gncInvoiceGetTerms (GncInvoice *invoice) +GncBillTerm * gncInvoiceGetTerms (const GncInvoice *invoice) { - if (!invoice) return 0; + if (!invoice) return NULL; return invoice->terms; } -const char * gncInvoiceGetBillingID (GncInvoice *invoice) +const char * gncInvoiceGetBillingID (const GncInvoice *invoice) { - if (!invoice) return 0; + if (!invoice) return NULL; return invoice->billing_id; } -const char * gncInvoiceGetNotes (GncInvoice *invoice) +const char * gncInvoiceGetNotes (const GncInvoice *invoice) { if (!invoice) return NULL; return invoice->notes; @@ -629,7 +629,7 @@ const char * gncInvoiceGetType (GncInvoice *invoice) } } -gnc_commodity * gncInvoiceGetCurrency (GncInvoice *invoice) +gnc_commodity * gncInvoiceGetCurrency (const GncInvoice *invoice) { if (!invoice) return NULL; return invoice->currency; @@ -641,32 +641,32 @@ GncOwner * gncInvoiceGetBillTo (GncInvoice *invoice) return &invoice->billto; } -GNCLot * gncInvoiceGetPostedLot (GncInvoice *invoice) +GNCLot * gncInvoiceGetPostedLot (const GncInvoice *invoice) { if (!invoice) return NULL; return invoice->posted_lot; } -Transaction * gncInvoiceGetPostedTxn (GncInvoice *invoice) +Transaction * gncInvoiceGetPostedTxn (const GncInvoice *invoice) { if (!invoice) return NULL; return invoice->posted_txn; } -Account * gncInvoiceGetPostedAcc (GncInvoice *invoice) +Account * gncInvoiceGetPostedAcc (const GncInvoice *invoice) { if (!invoice) return NULL; return invoice->posted_acc; } -gboolean gncInvoiceGetActive (GncInvoice *invoice) +gboolean gncInvoiceGetActive (const GncInvoice *invoice) { if (!invoice) return FALSE; return invoice->active; } -gnc_numeric gncInvoiceGetToChargeAmount (GncInvoice *invoice) +gnc_numeric gncInvoiceGetToChargeAmount (const GncInvoice *invoice) { if (!invoice) return gnc_numeric_zero(); return invoice->to_charge_amount; @@ -724,7 +724,7 @@ qofInvoiceSetEntries(GncInvoice *invoice, QofCollection *entry_coll) } static GncJob* -qofInvoiceGetJob (GncInvoice *invoice) +qofInvoiceGetJob (const GncInvoice *invoice) { if(!invoice) { return NULL; } return invoice->job; @@ -806,7 +806,7 @@ gncInvoiceAttachToTxn (GncInvoice *invoice, Transaction *txn) } GncInvoice * -gncInvoiceGetInvoiceFromTxn (Transaction *txn) +gncInvoiceGetInvoiceFromTxn (const Transaction *txn) { KvpFrame *kvp; KvpValue *value; @@ -1454,20 +1454,20 @@ gncOwnerApplyPayment (GncOwner *owner, GncInvoice* invoice, return txn; } -static gboolean gncInvoiceDateExists (Timespec *date) +static gboolean gncInvoiceDateExists (const Timespec *date) { g_return_val_if_fail (date, FALSE); if (date->tv_sec || date->tv_nsec) return TRUE; return FALSE; } -gboolean gncInvoiceIsPosted (GncInvoice *invoice) +gboolean gncInvoiceIsPosted (const GncInvoice *invoice) { if (!invoice) return FALSE; return gncInvoiceDateExists (&(invoice->date_posted)); } -gboolean gncInvoiceIsPaid (GncInvoice *invoice) +gboolean gncInvoiceIsPaid (const GncInvoice *invoice) { if (!invoice) return FALSE; if (!invoice->posted_lot) return FALSE; @@ -1501,7 +1501,7 @@ void gncInvoiceCommitEdit (GncInvoice *invoice) gncInvoiceOnDone, invoice_free); } -int gncInvoiceCompare (GncInvoice *a, GncInvoice *b) +int gncInvoiceCompare (const GncInvoice *a, const GncInvoice *b) { int compare; diff --git a/src/business/business-core/gncInvoice.h b/src/business/business-core/gncInvoice.h index 3f93b6fdce..b15e021633 100644 --- a/src/business/business-core/gncInvoice.h +++ b/src/business/business-core/gncInvoice.h @@ -97,23 +97,23 @@ void gncInvoiceSortEntries (GncInvoice *invoice); /** @name Get Functions @{ */ -const char * gncInvoiceGetID (GncInvoice *invoice); +const char * gncInvoiceGetID (const GncInvoice *invoice); GncOwner * gncInvoiceGetOwner (GncInvoice *invoice); -Timespec gncInvoiceGetDateOpened (GncInvoice *invoice); -Timespec gncInvoiceGetDatePosted (GncInvoice *invoice); -Timespec gncInvoiceGetDateDue (GncInvoice *invoice); -GncBillTerm * gncInvoiceGetTerms (GncInvoice *invoice); -const char * gncInvoiceGetBillingID (GncInvoice *invoice); -const char * gncInvoiceGetNotes (GncInvoice *invoice); +Timespec gncInvoiceGetDateOpened (const GncInvoice *invoice); +Timespec gncInvoiceGetDatePosted (const GncInvoice *invoice); +Timespec gncInvoiceGetDateDue (const GncInvoice *invoice); +GncBillTerm * gncInvoiceGetTerms (const GncInvoice *invoice); +const char * gncInvoiceGetBillingID (const GncInvoice *invoice); +const char * gncInvoiceGetNotes (const GncInvoice *invoice); const char * gncInvoiceGetType (GncInvoice *invoice); -gnc_commodity * gncInvoiceGetCurrency (GncInvoice *invoice); +gnc_commodity * gncInvoiceGetCurrency (const GncInvoice *invoice); GncOwner * gncInvoiceGetBillTo (GncInvoice *invoice); -gnc_numeric gncInvoiceGetToChargeAmount (GncInvoice *invoice); -gboolean gncInvoiceGetActive (GncInvoice *invoice); +gnc_numeric gncInvoiceGetToChargeAmount (const GncInvoice *invoice); +gboolean gncInvoiceGetActive (const GncInvoice *invoice); -GNCLot * gncInvoiceGetPostedLot (GncInvoice *invoice); -Transaction * gncInvoiceGetPostedTxn (GncInvoice *invoice); -Account * gncInvoiceGetPostedAcc (GncInvoice *invoice); +GNCLot * gncInvoiceGetPostedLot (const GncInvoice *invoice); +Transaction * gncInvoiceGetPostedTxn (const GncInvoice *invoice); +Account * gncInvoiceGetPostedAcc (const GncInvoice *invoice); /** @} */ /** return the "total" amount of the invoice */ @@ -165,7 +165,7 @@ gncOwnerApplyPayment (GncOwner *owner, GncInvoice *invoice, /** Given a transaction, find and return the Invoice */ -GncInvoice * gncInvoiceGetInvoiceFromTxn (Transaction *txn); +GncInvoice * gncInvoiceGetInvoiceFromTxn (const Transaction *txn); /** Given a LOT, find and return the Invoice attached to the lot */ GncInvoice * gncInvoiceGetInvoiceFromLot (GNCLot *lot); @@ -181,9 +181,9 @@ GncInvoice * gncInvoiceGetInvoiceFromLot (GNCLot *lot); void gncInvoiceBeginEdit (GncInvoice *invoice); void gncInvoiceCommitEdit (GncInvoice *invoice); -int gncInvoiceCompare (GncInvoice *a, GncInvoice *b); -gboolean gncInvoiceIsPosted (GncInvoice *invoice); -gboolean gncInvoiceIsPaid (GncInvoice *invoice); +int gncInvoiceCompare (const GncInvoice *a, const GncInvoice *b); +gboolean gncInvoiceIsPosted (const GncInvoice *invoice); +gboolean gncInvoiceIsPaid (const GncInvoice *invoice); #define INVOICE_ID "id" #define INVOICE_OWNER "owner" diff --git a/src/business/business-core/gncJob.c b/src/business/business-core/gncJob.c index 748ba14b84..46bfd8108d 100644 --- a/src/business/business-core/gncJob.c +++ b/src/business/business-core/gncJob.c @@ -307,19 +307,19 @@ void gncJobCommitEdit (GncJob *job) /* ================================================================== */ /* Get Functions */ -const char * gncJobGetID (GncJob *job) +const char * gncJobGetID (const GncJob *job) { if (!job) return NULL; return job->id; } -const char * gncJobGetName (GncJob *job) +const char * gncJobGetName (const GncJob *job) { if (!job) return NULL; return job->name; } -const char * gncJobGetReference (GncJob *job) +const char * gncJobGetReference (const GncJob *job) { if (!job) return NULL; return job->desc; @@ -331,7 +331,7 @@ GncOwner * gncJobGetOwner (GncJob *job) return &(job->owner); } -gboolean gncJobGetActive (GncJob *job) +gboolean gncJobGetActive (const GncJob *job) { if (!job) return FALSE; return job->active; diff --git a/src/business/business-core/gncJob.h b/src/business/business-core/gncJob.h index 63ee0d8cee..f97523b898 100644 --- a/src/business/business-core/gncJob.h +++ b/src/business/business-core/gncJob.h @@ -76,13 +76,13 @@ void gncJobCommitEdit (GncJob *job); @{ */ -const char * gncJobGetID (GncJob *job); -const char * gncJobGetName (GncJob *job); -const char * gncJobGetReference (GncJob *job); +const char * gncJobGetID (const GncJob *job); +const char * gncJobGetName (const GncJob *job); +const char * gncJobGetReference (const GncJob *job); GncOwner * gncJobGetOwner (GncJob *job); /** @} */ -gboolean gncJobGetActive (GncJob *job); +gboolean gncJobGetActive (const GncJob *job); /** Return a pointer to the instance gncJob that is identified * by the guid, and is residing in the book. Returns NULL if the diff --git a/src/business/business-core/gncOrder.c b/src/business/business-core/gncOrder.c index b3871ed5b2..bcc6d64eda 100644 --- a/src/business/business-core/gncOrder.c +++ b/src/business/business-core/gncOrder.c @@ -293,7 +293,7 @@ void gncOrderRemoveEntry (GncOrder *order, GncEntry *entry) /* Get Functions */ -const char * gncOrderGetID (GncOrder *order) +const char * gncOrderGetID (const GncOrder *order) { if (!order) return NULL; return order->id; @@ -305,33 +305,33 @@ GncOwner * gncOrderGetOwner (GncOrder *order) return &order->owner; } -Timespec gncOrderGetDateOpened (GncOrder *order) +Timespec gncOrderGetDateOpened (const GncOrder *order) { Timespec ts; ts.tv_sec = 0; ts.tv_nsec = 0; if (!order) return ts; return order->opened; } -Timespec gncOrderGetDateClosed (GncOrder *order) +Timespec gncOrderGetDateClosed (const GncOrder *order) { Timespec ts; ts.tv_sec = 0; ts.tv_nsec = 0; if (!order) return ts; return order->closed; } -const char * gncOrderGetNotes (GncOrder *order) +const char * gncOrderGetNotes (const GncOrder *order) { if (!order) return NULL; return order->notes; } -const char * gncOrderGetReference (GncOrder *order) +const char * gncOrderGetReference (const GncOrder *order) { if (!order) return NULL; return order->reference; } -gboolean gncOrderGetActive (GncOrder *order) +gboolean gncOrderGetActive (const GncOrder *order) { if (!order) return FALSE; return order->active; @@ -344,7 +344,7 @@ GList * gncOrderGetEntries (GncOrder *order) return order->entries; } -gboolean gncOrderIsClosed (GncOrder *order) +gboolean gncOrderIsClosed (const GncOrder *order) { if (!order) return FALSE; if (order->closed.tv_sec || order->closed.tv_nsec) return TRUE; @@ -378,7 +378,7 @@ void gncOrderCommitEdit (GncOrder *order) gncOrderOnDone, order_free); } -int gncOrderCompare (GncOrder *a, GncOrder *b) +int gncOrderCompare (const GncOrder *a, const GncOrder *b) { int compare; diff --git a/src/business/business-core/gncOrder.h b/src/business/business-core/gncOrder.h index 3ad1c1a0b3..215f4fd144 100644 --- a/src/business/business-core/gncOrder.h +++ b/src/business/business-core/gncOrder.h @@ -78,22 +78,22 @@ void gncOrderRemoveEntry (GncOrder *order, GncEntry *entry); /* Get Functions */ -const char * gncOrderGetID (GncOrder *order); +const char * gncOrderGetID (const GncOrder *order); GncOwner * gncOrderGetOwner (GncOrder *order); -Timespec gncOrderGetDateOpened (GncOrder *order); -Timespec gncOrderGetDateClosed (GncOrder *order); -const char * gncOrderGetNotes (GncOrder *order); -const char * gncOrderGetReference (GncOrder *order); -gboolean gncOrderGetActive (GncOrder *order); +Timespec gncOrderGetDateOpened (const GncOrder *order); +Timespec gncOrderGetDateClosed (const GncOrder *order); +const char * gncOrderGetNotes (const GncOrder *order); +const char * gncOrderGetReference (const GncOrder *order); +gboolean gncOrderGetActive (const GncOrder *order); /* Get the list Entries */ GList * gncOrderGetEntries (GncOrder *order); void gncOrderBeginEdit (GncOrder *order); void gncOrderCommitEdit (GncOrder *order); -int gncOrderCompare (GncOrder *a, GncOrder *b); +int gncOrderCompare (const GncOrder *a, const GncOrder *b); -gboolean gncOrderIsClosed (GncOrder *order); +gboolean gncOrderIsClosed (const GncOrder *order); /** Return a pointer to the instance gncOrder that is identified * by the guid, and is residing in the book. Returns NULL if the diff --git a/src/business/business-core/gncOwner.c b/src/business/business-core/gncOwner.c index fb08a1957f..3df3d3e68c 100644 --- a/src/business/business-core/gncOwner.c +++ b/src/business/business-core/gncOwner.c @@ -103,7 +103,7 @@ GncOwnerType gncOwnerGetType (const GncOwner *owner) } QofIdType -qofOwnerGetType(GncOwner *owner) +qofOwnerGetType(const GncOwner *owner) { QofIdType type; @@ -139,7 +139,7 @@ qofOwnerGetType(GncOwner *owner) } QofInstance* -qofOwnerGetOwner (GncOwner *owner) +qofOwnerGetOwner (const GncOwner *owner) { QofInstance *ent; @@ -234,7 +234,7 @@ GncEmployee * gncOwnerGetEmployee (const GncOwner *owner) return owner->owner.employee; } -gnc_commodity * gncOwnerGetCurrency (GncOwner *owner) +gnc_commodity * gncOwnerGetCurrency (const GncOwner *owner) { if (!owner) return NULL; switch (owner->type) { @@ -297,7 +297,7 @@ gboolean gncOwnerEqual (const GncOwner *a, const GncOwner *b) return (a->owner.undefined == b->owner.undefined); } -const char * gncOwnerGetName (GncOwner *owner) +const char * gncOwnerGetName (const GncOwner *owner) { if (!owner) return NULL; switch (owner->type) { @@ -316,7 +316,7 @@ const char * gncOwnerGetName (GncOwner *owner) } } -const GUID * gncOwnerGetGUID (GncOwner *owner) +const GUID * gncOwnerGetGUID (const GncOwner *owner) { if (!owner) return NULL; @@ -393,7 +393,7 @@ const GUID * gncOwnerGetEndGUID (GncOwner *owner) return gncOwnerGetGUID (owner); } -void gncOwnerAttachToLot (GncOwner *owner, GNCLot *lot) +void gncOwnerAttachToLot (const GncOwner *owner, GNCLot *lot) { KvpFrame *kvp; KvpValue *value; @@ -458,7 +458,7 @@ gboolean gncOwnerGetOwnerFromLot (GNCLot *lot, GncOwner *owner) return (owner->owner.undefined != NULL); } -gboolean gncOwnerIsValid (GncOwner *owner) +gboolean gncOwnerIsValid (const GncOwner *owner) { if (!owner) return FALSE; return (owner->owner.undefined != NULL); diff --git a/src/business/business-core/gncOwner.h b/src/business/business-core/gncOwner.h index 4baf1a154d..e539d6d1b6 100644 --- a/src/business/business-core/gncOwner.h +++ b/src/business/business-core/gncOwner.h @@ -61,9 +61,9 @@ to QOF as they can be used by objects like GncInvoice. @{ */ /** return the type for the collection. */ -QofIdType qofOwnerGetType(GncOwner *owner); +QofIdType qofOwnerGetType(const GncOwner *owner); /** return the owner itself as an entity. */ -QofInstance* qofOwnerGetOwner (GncOwner *owner); +QofInstance* qofOwnerGetOwner (const GncOwner *owner); /** set the owner from the entity. */ void qofOwnerSetEntity (GncOwner *owner, QofInstance *ent); @@ -112,14 +112,14 @@ void gncOwnerCopy (const GncOwner *src, GncOwner *dest); gboolean gncOwnerEqual (const GncOwner *a, const GncOwner *b); int gncOwnerCompare (const GncOwner *a, const GncOwner *b); -const char * gncOwnerGetName (GncOwner *owner); -gnc_commodity * gncOwnerGetCurrency (GncOwner *owner); +const char * gncOwnerGetName (const GncOwner *owner); +gnc_commodity * gncOwnerGetCurrency (const GncOwner *owner); /** Get the GUID of the immediate owner */ -const GUID * gncOwnerGetGUID (GncOwner *owner); +const GUID * gncOwnerGetGUID (const GncOwner *owner); GUID gncOwnerRetGUID (GncOwner *owner); -gboolean gncOwnerIsValid (GncOwner *owner); +gboolean gncOwnerIsValid (const GncOwner *owner); /** * Get the "parent" Owner or GUID thereof. The "parent" owner @@ -129,7 +129,7 @@ GncOwner * gncOwnerGetEndOwner (GncOwner *owner); const GUID * gncOwnerGetEndGUID (GncOwner *owner); /** attach an owner to a lot */ -void gncOwnerAttachToLot (GncOwner *owner, GNCLot *lot); +void gncOwnerAttachToLot (const GncOwner *owner, GNCLot *lot); /** Get the owner from the lot. If an owner is found in the lot, * fill in "owner" and return TRUE. Otherwise return FALSE. diff --git a/src/business/business-core/gncTaxTable.c b/src/business/business-core/gncTaxTable.c index 9e43a5911a..f2f4942566 100644 --- a/src/business/business-core/gncTaxTable.c +++ b/src/business/business-core/gncTaxTable.c @@ -66,7 +66,7 @@ struct _book_info GList * tables; /* visible tables */ }; -static GncTaxTableEntry * CloneTaxEntry (GncTaxTableEntry*, QofBook *); +static GncTaxTableEntry * CloneTaxEntry (const GncTaxTableEntry*, QofBook *); static QofLogModule log_module = GNC_MOD_BUSINESS; @@ -194,7 +194,7 @@ gncTaxTableAddChild (GncTaxTable *table, GncTaxTable *child) } static inline void -gncTaxTableRemoveChild (GncTaxTable *table, GncTaxTable *child) +gncTaxTableRemoveChild (GncTaxTable *table, const GncTaxTable *child) { g_return_if_fail(table); g_return_if_fail(child); @@ -291,7 +291,7 @@ gncCloneTaxTable (GncTaxTable *from, QofBook *book) } GncTaxTable * -gncTaxTableObtainTwin (GncTaxTable *from, QofBook *book) +gncTaxTableObtainTwin (const GncTaxTable *from, QofBook *book) { GncTaxTable *table; if (!from) return NULL; @@ -370,7 +370,7 @@ void gncTaxTableEntryDestroy (GncTaxTableEntry *entry) * we set it above, when cloning the table). */ static GncTaxTableEntry * -CloneTaxEntry (GncTaxTableEntry*from, QofBook *book) +CloneTaxEntry (const GncTaxTableEntry*from, QofBook *book) { QofInstance *acc; GncTaxTableEntry *entry; @@ -575,13 +575,13 @@ GList * gncTaxTableGetTables (QofBook *book) return bi->tables; } -const char *gncTaxTableGetName (GncTaxTable *table) +const char *gncTaxTableGetName (const GncTaxTable *table) { if (!table) return NULL; return table->name; } -static GncTaxTableEntry *gncTaxTableEntryCopy (GncTaxTableEntry *entry) +static GncTaxTableEntry *gncTaxTableEntryCopy (const GncTaxTableEntry *entry) { GncTaxTableEntry *e; if (!entry) return NULL; @@ -594,7 +594,7 @@ static GncTaxTableEntry *gncTaxTableEntryCopy (GncTaxTableEntry *entry) return e; } -static GncTaxTable *gncTaxTableCopy (GncTaxTable *table) +static GncTaxTable *gncTaxTableCopy (const GncTaxTable *table) { GncTaxTable *t; GList *list; @@ -626,56 +626,62 @@ GncTaxTable *gncTaxTableReturnChild (GncTaxTable *table, gboolean make_new) return child; } -GncTaxTable *gncTaxTableGetParent (GncTaxTable *table) +GncTaxTable *gncTaxTableGetParent (const GncTaxTable *table) { if (!table) return NULL; return table->parent; } -GList *gncTaxTableGetEntries (GncTaxTable *table) +GList *gncTaxTableGetEntries (const GncTaxTable *table) { if (!table) return NULL; return table->entries; } -gint64 gncTaxTableGetRefcount (GncTaxTable *table) +gint64 gncTaxTableGetRefcount (const GncTaxTable *table) { if (!table) return 0; return table->refcount; } -Timespec gncTaxTableLastModified (GncTaxTable *table) +Timespec gncTaxTableLastModified (const GncTaxTable *table) { Timespec ts = { 0 , 0 }; if (!table) return ts; return table->modtime; } -gboolean gncTaxTableGetInvisible (GncTaxTable *table) +gboolean gncTaxTableGetInvisible (const GncTaxTable *table) { if (!table) return FALSE; return table->invisible; } -Account * gncTaxTableEntryGetAccount (GncTaxTableEntry *entry) +Account * gncTaxTableEntryGetAccount (const GncTaxTableEntry *entry) { if (!entry) return NULL; return entry->account; } -GncAmountType gncTaxTableEntryGetType (GncTaxTableEntry *entry) +GncAmountType gncTaxTableEntryGetType (const GncTaxTableEntry *entry) { if (!entry) return 0; return entry->type; } -gnc_numeric gncTaxTableEntryGetAmount (GncTaxTableEntry *entry) +gnc_numeric gncTaxTableEntryGetAmount (const GncTaxTableEntry *entry) { if (!entry) return gnc_numeric_zero(); return entry->amount; } -int gncTaxTableEntryCompare (GncTaxTableEntry *a, GncTaxTableEntry *b) +GncTaxTable* gncTaxTableEntryGetTable( const GncTaxTableEntry* entry ) +{ + if (!entry) return NULL; + return entry->table; +} + +int gncTaxTableEntryCompare (const GncTaxTableEntry *a, const GncTaxTableEntry *b) { char *name_a, *name_b; int retval; @@ -696,7 +702,7 @@ int gncTaxTableEntryCompare (GncTaxTableEntry *a, GncTaxTableEntry *b) return gnc_numeric_compare (a->amount, b->amount); } -int gncTaxTableCompare (GncTaxTable *a, GncTaxTable *b) +int gncTaxTableCompare (const GncTaxTable *a, const GncTaxTable *b) { if (!a && !b) return 0; if (!a) return -1; diff --git a/src/business/business-core/gncTaxTable.h b/src/business/business-core/gncTaxTable.h index 25fe13b2da..94a63a99dc 100644 --- a/src/business/business-core/gncTaxTable.h +++ b/src/business/business-core/gncTaxTable.h @@ -149,21 +149,21 @@ GncTaxTable *gncTaxTableLookupByName (QofBook *book, const char *name); GList * gncTaxTableGetTables (QofBook *book); -const char *gncTaxTableGetName (GncTaxTable *table); -GncTaxTable *gncTaxTableGetParent (GncTaxTable *table); +const char *gncTaxTableGetName (const GncTaxTable *table); +GncTaxTable *gncTaxTableGetParent (const GncTaxTable *table); GncTaxTable *gncTaxTableReturnChild (GncTaxTable *table, gboolean make_new); #define gncTaxTableGetChild(t) gncTaxTableReturnChild((t),FALSE) -GList *gncTaxTableGetEntries (GncTaxTable *table); -gint64 gncTaxTableGetRefcount (GncTaxTable *table); -Timespec gncTaxTableLastModified (GncTaxTable *table); +GList *gncTaxTableGetEntries (const GncTaxTable *table); +gint64 gncTaxTableGetRefcount (const GncTaxTable *table); +Timespec gncTaxTableLastModified (const GncTaxTable *table); -Account * gncTaxTableEntryGetAccount (GncTaxTableEntry *entry); -GncAmountType gncTaxTableEntryGetType (GncTaxTableEntry *entry); -gnc_numeric gncTaxTableEntryGetAmount (GncTaxTableEntry *entry); +Account * gncTaxTableEntryGetAccount (const GncTaxTableEntry *entry); +GncAmountType gncTaxTableEntryGetType (const GncTaxTableEntry *entry); +gnc_numeric gncTaxTableEntryGetAmount (const GncTaxTableEntry *entry); /** @} */ -int gncTaxTableCompare (GncTaxTable *a, GncTaxTable *b); -int gncTaxTableEntryCompare (GncTaxTableEntry *a, GncTaxTableEntry *b); +int gncTaxTableCompare (const GncTaxTable *a, const GncTaxTable *b); +int gncTaxTableEntryCompare (const GncTaxTableEntry *a, const GncTaxTableEntry *b); /************************************************/ diff --git a/src/business/business-core/gncTaxTableP.h b/src/business/business-core/gncTaxTableP.h index 8ecdb52beb..71b28c547f 100644 --- a/src/business/business-core/gncTaxTableP.h +++ b/src/business/business-core/gncTaxTableP.h @@ -38,7 +38,7 @@ void gncTaxTableSetChild (GncTaxTable *table, GncTaxTable *child); void gncTaxTableSetRefcount (GncTaxTable *table, gint64 refcount); void gncTaxTableMakeInvisible (GncTaxTable *table); -gboolean gncTaxTableGetInvisible (GncTaxTable *table); +gboolean gncTaxTableGetInvisible (const GncTaxTable *table); /** The gncCloneTaxTable() routine makes a copy of the indicated * tax table, placing it in the indicated book. It copies @@ -61,7 +61,9 @@ GncTaxTable * gncCloneTaxTable (GncTaxTable *from, QofBook *book); * it from the other Get routines, which work in fundamentally * different ways. */ -GncTaxTable * gncTaxTableObtainTwin (GncTaxTable *from, QofBook *book); +GncTaxTable * gncTaxTableObtainTwin (const GncTaxTable *from, QofBook *book); + +GncTaxTable* gncTaxTableEntryGetTable( const GncTaxTableEntry* entry ); #define gncTaxTableSetGUID(E,G) qof_instance_set_guid(QOF_INSTANCE(E),(G)) diff --git a/src/business/business-core/gncVendor.c b/src/business/business-core/gncVendor.c index fbe9c009d0..524b4c5963 100644 --- a/src/business/business-core/gncVendor.c +++ b/src/business/business-core/gncVendor.c @@ -346,68 +346,68 @@ qofVendorSetTaxIncluded(GncVendor *vendor, const char* type_string) /* ============================================================== */ /* Get Functions */ -const char * gncVendorGetID (GncVendor *vendor) +const char * gncVendorGetID (const GncVendor *vendor) { if (!vendor) return NULL; return vendor->id; } -const char * gncVendorGetName (GncVendor *vendor) +const char * gncVendorGetName (const GncVendor *vendor) { if (!vendor) return NULL; return vendor->name; } -GncAddress * gncVendorGetAddr (GncVendor *vendor) +GncAddress * gncVendorGetAddr (const GncVendor *vendor) { if (!vendor) return NULL; return vendor->addr; } -const char * gncVendorGetNotes (GncVendor *vendor) +const char * gncVendorGetNotes (const GncVendor *vendor) { if (!vendor) return NULL; return vendor->notes; } -GncBillTerm * gncVendorGetTerms (GncVendor *vendor) +GncBillTerm * gncVendorGetTerms (const GncVendor *vendor) { if (!vendor) return 0; return vendor->terms; } -GncTaxIncluded gncVendorGetTaxIncluded (GncVendor *vendor) +GncTaxIncluded gncVendorGetTaxIncluded (const GncVendor *vendor) { if (!vendor) return GNC_TAXINCLUDED_USEGLOBAL; return vendor->taxincluded; } -gnc_commodity * gncVendorGetCurrency (GncVendor *vendor) +gnc_commodity * gncVendorGetCurrency (const GncVendor *vendor) { if (!vendor) return NULL; return vendor->currency; } -gboolean gncVendorGetActive (GncVendor *vendor) +gboolean gncVendorGetActive (const GncVendor *vendor) { if (!vendor) return FALSE; return vendor->active; } -gboolean gncVendorGetTaxTableOverride (GncVendor *vendor) +gboolean gncVendorGetTaxTableOverride (const GncVendor *vendor) { if (!vendor) return FALSE; return vendor->taxtable_override; } -GncTaxTable* gncVendorGetTaxTable (GncVendor *vendor) +GncTaxTable* gncVendorGetTaxTable (const GncVendor *vendor) { if (!vendor) return NULL; return vendor->taxtable; } static const char* -qofVendorGetTaxIncluded(GncVendor *vendor) +qofVendorGetTaxIncluded(const GncVendor *vendor) { return gncTaxIncludedTypeToString(vendor->taxincluded); } @@ -475,7 +475,7 @@ void gncVendorCommitEdit (GncVendor *vendor) /* ============================================================== */ /* Other functions */ -int gncVendorCompare (GncVendor *a, GncVendor *b) +int gncVendorCompare (const GncVendor *a, const GncVendor *b) { if (!a && !b) return 0; if (!a && b) return 1; @@ -484,7 +484,7 @@ int gncVendorCompare (GncVendor *a, GncVendor *b) return(strcmp(a->name, b->name)); } -GList * gncVendorGetJoblist (GncVendor *vendor, gboolean show_all) +GList * gncVendorGetJoblist (const GncVendor *vendor, gboolean show_all) { if (!vendor) return NULL; @@ -501,7 +501,7 @@ GList * gncVendorGetJoblist (GncVendor *vendor, gboolean show_all) } } -gboolean gncVendorIsDirty (GncVendor *vendor) +gboolean gncVendorIsDirty (const GncVendor *vendor) { if (!vendor) return FALSE; return (qof_instance_get_dirty_flag(vendor) diff --git a/src/business/business-core/gncVendor.h b/src/business/business-core/gncVendor.h index 3cfd57def2..962590961e 100644 --- a/src/business/business-core/gncVendor.h +++ b/src/business/business-core/gncVendor.h @@ -86,25 +86,25 @@ void gncVendorCommitEdit (GncVendor *vendor); @{ */ -const char * gncVendorGetID (GncVendor *vendor); -const char * gncVendorGetName (GncVendor *vendor); -GncAddress * gncVendorGetAddr (GncVendor *vendor); -const char * gncVendorGetNotes (GncVendor *vendor); -GncBillTerm * gncVendorGetTerms (GncVendor *vendor); -GncTaxIncluded gncVendorGetTaxIncluded (GncVendor *vendor); -gnc_commodity * gncVendorGetCurrency (GncVendor *vendor); -gboolean gncVendorGetActive (GncVendor *vendor); +const char * gncVendorGetID (const GncVendor *vendor); +const char * gncVendorGetName (const GncVendor *vendor); +GncAddress * gncVendorGetAddr (const GncVendor *vendor); +const char * gncVendorGetNotes (const GncVendor *vendor); +GncBillTerm * gncVendorGetTerms (const GncVendor *vendor); +GncTaxIncluded gncVendorGetTaxIncluded (const GncVendor *vendor); +gnc_commodity * gncVendorGetCurrency (const GncVendor *vendor); +gboolean gncVendorGetActive (const GncVendor *vendor); -gboolean gncVendorGetTaxTableOverride (GncVendor *vendor); -GncTaxTable* gncVendorGetTaxTable (GncVendor *vendor); +gboolean gncVendorGetTaxTableOverride (const GncVendor *vendor); +GncTaxTable* gncVendorGetTaxTable (const GncVendor *vendor); /** @} */ /** XXX should be renamed to RetJobList to be consistent with * other usage, since caller must free the copied list */ -GList * gncVendorGetJoblist (GncVendor *vendor, gboolean show_all); -gboolean gncVendorIsDirty (GncVendor *vendor); -int gncVendorCompare (GncVendor *a, GncVendor *b); +GList * gncVendorGetJoblist (const GncVendor *vendor, gboolean show_all); +gboolean gncVendorIsDirty (const GncVendor *vendor); +int gncVendorCompare (const GncVendor *a, const GncVendor *b); /** Return a pointer to the instance gncVendor that is identified * by the guid, and is residing in the book. Returns NULL if the diff --git a/src/business/business-core/sql/Makefile.am b/src/business/business-core/sql/Makefile.am new file mode 100644 index 0000000000..5833f44855 --- /dev/null +++ b/src/business/business-core/sql/Makefile.am @@ -0,0 +1,54 @@ +SUBDIRS = . + +pkglib_LTLIBRARIES = libgncmod-business-backend-sql.la + +AM_CFLAGS = \ + -I${top_srcdir}/src \ + -I${top_srcdir}/src/backend \ + -I${top_srcdir}/src/backend/sql \ + -I${top_srcdir}/src/engine \ + -I${top_srcdir}/src/gnc-module \ + -I${top_srcdir}/src/business/business-core \ + ${QOF_CFLAGS} \ + ${GLIB_CFLAGS} \ + ${LIBGDA_CFLAGS} + +libgncmod_business_backend_sql_la_SOURCES = \ + gncmod-business-backend-sql.c \ + gnc-address-sql.c \ + gnc-bill-term-sql.c \ + gnc-customer-sql.c \ + gnc-employee-sql.c \ + gnc-entry-sql.c \ + gnc-invoice-sql.c \ + gnc-job-sql.c \ + gnc-order-sql.c \ + gnc-owner-sql.c \ + gnc-tax-table-sql.c \ + gnc-vendor-sql.c + +noinst_HEADERS = \ + gnc-address-sql.h \ + gnc-bill-term-sql.h \ + gnc-customer-sql.h \ + gnc-employee-sql.h \ + gnc-entry-sql.h \ + gnc-invoice-sql.h \ + gnc-job-sql.h \ + gnc-order-sql.h \ + gnc-owner-sql.h \ + gnc-tax-table-sql.h \ + gnc-vendor-sql.h + +libgncmod_business_backend_sql_la_LDFLAGS = -module -avoid-version + +libgncmod_business_backend_sql_la_LIBADD = \ + ${top_builddir}/src/business/business-core/libgncmod-business-core.la \ + ${top_builddir}/src/backend/sql/libgnc-backend-sql.la \ + ${top_builddir}/src/engine/libgncmod-engine.la \ + ${top_builddir}/src/gnc-module/libgnc-module.la \ + ${QOF_LIBS} \ + ${GLIB_LIBS} \ + ${LIBGDA_LIBS} + +INCLUDES = -DG_LOG_DOMAIN=\"gnc.backend.sql\" diff --git a/src/business/business-core/sql/gnc-address-sql.c b/src/business/business-core/sql/gnc-address-sql.c new file mode 100644 index 0000000000..a5337860ee --- /dev/null +++ b/src/business/business-core/sql/gnc-address-sql.c @@ -0,0 +1,212 @@ +/********************************************************************\ + * gnc-address-sql.c -- address sql backend implementation * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * +\********************************************************************/ + +/** @file gnc-address-sql.c + * @brief load and save address data to SQL + * @author Copyright (c) 2007-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#include "config.h" + +#include +#include +#include + +#include "gnc-engine.h" + +#include "gnc-backend-sql.h" +#include "gnc-address-sql.h" + +static QofLogModule log_module = G_LOG_DOMAIN; + +#define ADDRESS_MAX_NAME_LEN 1024 +#define ADDRESS_MAX_ADDRESS_LINE_LEN 1024 +#define ADDRESS_MAX_PHONE_LEN 128 +#define ADDRESS_MAX_FAX_LEN 128 +#define ADDRESS_MAX_EMAIL_LEN 256 + +static GncSqlColumnTableEntry col_table[] = +{ + { "name", CT_STRING, ADDRESS_MAX_NAME_LEN, COL_NNUL, NULL, ADDRESS_NAME }, + { "addr1", CT_STRING, ADDRESS_MAX_ADDRESS_LINE_LEN, COL_NNUL, NULL, ADDRESS_ONE }, + { "addr2", CT_STRING, ADDRESS_MAX_ADDRESS_LINE_LEN, COL_NNUL, NULL, ADDRESS_TWO }, + { "addr3", CT_STRING, ADDRESS_MAX_ADDRESS_LINE_LEN, COL_NNUL, NULL, ADDRESS_THREE }, + { "addr4", CT_STRING, ADDRESS_MAX_ADDRESS_LINE_LEN, COL_NNUL, NULL, ADDRESS_FOUR }, + { "phone", CT_STRING, ADDRESS_MAX_PHONE_LEN, COL_NNUL, NULL, ADDRESS_PHONE }, + { "fax", CT_STRING, ADDRESS_MAX_FAX_LEN, COL_NNUL, NULL, ADDRESS_FAX }, + { "email", CT_STRING, ADDRESS_MAX_EMAIL_LEN, COL_NNUL, NULL, ADDRESS_EMAIL }, + { NULL } +}; + +typedef void (*AddressSetterFunc)( gpointer, GncAddress* ); +typedef GncAddress* (*AddressGetterFunc)( const gpointer ); + +static void +load_address( const GncSqlBackend* be, GncSqlRow* row, + QofSetterFunc setter, gpointer pObject, + const GncSqlColumnTableEntry* table_row ) +{ + const GValue* val; + gchar* buf; + GncAddress* addr; + AddressSetterFunc a_setter = (AddressSetterFunc)setter; + const GncSqlColumnTableEntry* subtable; + const gchar* s; + + g_return_if_fail( be != NULL ); + g_return_if_fail( row != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + + addr = gncAddressCreate( be->primary_book, NULL ); + for( subtable = col_table; subtable->col_name != NULL; subtable++ ) { + buf = g_strdup_printf( "%s_%s", table_row->col_name, subtable->col_name ); + val = gnc_sql_row_get_value_at_col_name( row, buf ); + g_free( buf ); + if( val == NULL ) { + s = NULL; + } else { + s = g_value_get_string( val ); + } + if( subtable->gobj_param_name != NULL ) { + g_object_set( addr, subtable->gobj_param_name, s, NULL ); + } else { + if( subtable->qof_param_name != NULL ) { + setter = qof_class_get_parameter_setter( GNC_ID_ADDRESS, subtable->qof_param_name ); + } else { + setter = subtable->setter; + } + (*setter)( addr, (const gpointer)s ); + } + } + (*a_setter)( pObject, addr ); +} + +static void +add_address_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row, + GList** pList ) +{ + GncSqlColumnInfo* info; + gchar* buf; + const GncSqlColumnTableEntry* subtable_row; + const gchar* type; + + g_return_if_fail( be != NULL ); + g_return_if_fail( table_row != NULL ); + g_return_if_fail( pList != NULL ); + + for( subtable_row = col_table; subtable_row->col_name != NULL; subtable_row++ ) { + buf = g_strdup_printf( "%s_%s", table_row->col_name, subtable_row->col_name ); + info = g_new0( GncSqlColumnInfo, 1 ); + info->name = buf; + info->type_name = gnc_sql_connection_get_column_type_name( be->conn, + G_TYPE_STRING, subtable_row->size ); + info->is_primary_key = (table_row->flags & COL_PKEY) ? TRUE : FALSE; + info->size = subtable_row->size; + info->null_allowed = (table_row->flags & COL_NNUL) ? FALSE : TRUE; + *pList = g_list_append( *pList, info ); + } +} + +static void +add_address_colname_to_list( const GncSqlColumnTableEntry* table_row, GList** pList ) +{ + gnc_sql_add_subtable_colnames_to_list( table_row, col_table, pList ); +} + +static void +get_gvalue_address( const GncSqlBackend* be, QofIdTypeConst obj_name, const gpointer pObject, + const GncSqlColumnTableEntry* table_row, GValue* value ) +{ + AddressGetterFunc getter; + GncAddress* addr; + + g_return_if_fail( be != NULL ); + g_return_if_fail( obj_name != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + g_return_if_fail( value != NULL ); + + memset( value, 0, sizeof( GValue ) ); + + getter = (AddressGetterFunc)gnc_sql_get_getter( obj_name, table_row ); + addr = (*getter)( pObject ); + g_value_init( value, gnc_address_get_type() ); + g_value_set_object( value, addr ); +} + +static void +add_gvalue_address_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name, + const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList ) +{ + GValue value; + GValue* subfield_value; + GncAddress* addr; + gchar* s; + QofAccessFunc getter; + const GncSqlColumnTableEntry* subtable_row; + + g_return_if_fail( be != NULL ); + g_return_if_fail( obj_name != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + + memset( &value, 0, sizeof( GValue ) ); + get_gvalue_address( be, obj_name, pObject, table_row, &value ); + + if( G_VALUE_TYPE(&value) != 0 ) { + addr = g_value_get_object( &value ); + for( subtable_row = col_table; subtable_row->col_name != NULL; subtable_row++ ) { + subfield_value = g_new0( GValue, 1 ); + if( subtable_row->gobj_param_name != NULL ) { + g_object_get( addr, subtable_row->gobj_param_name, &s, NULL ); + } else { + getter = gnc_sql_get_getter( GNC_ID_ADDRESS, subtable_row ); + s = (gchar*)(*getter)( addr, NULL ); + } + g_value_init( subfield_value, G_TYPE_STRING ); + if( s ) { + g_value_set_string( subfield_value, s ); + } else { + g_value_set_string( subfield_value, "NULL" ); + } + (*pList) = g_slist_append( (*pList), subfield_value ); + } + } +} + +static col_type_handler_t address_handler + = { load_address, + add_address_col_info_to_list, + add_address_colname_to_list, + add_gvalue_address_to_slist }; + +/* ================================================================= */ +void +gnc_address_sql_initialize( void ) +{ + gnc_sql_register_col_type_handler( CT_ADDRESS, &address_handler ); +} +/* ========================== END OF FILE ===================== */ diff --git a/src/business/business-core/sql/gnc-address-sql.h b/src/business/business-core/sql/gnc-address-sql.h new file mode 100644 index 0000000000..602e0ca31c --- /dev/null +++ b/src/business/business-core/sql/gnc-address-sql.h @@ -0,0 +1,38 @@ +/* gnc-address-sql.h -- Address SQL header + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, contact: + * + * Free Software Foundation Voice: +1-617-542-5942 + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 + * Boston, MA 02110-1301, USA gnu@gnu.org + */ + +/** @file gnc-address-sql.h + * @brief load and save address data to SQL + * @author Copyright (c) 2007-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#ifndef GNC_ADDRESS_SQL_H +#define GNC_ADDRESS_SQL_H + +#include "gncAddress.h" + +#define CT_ADDRESS "address" + +void gnc_address_sql_initialize( void ); + +#endif /* GNC_ADDRESS_SQL_H */ diff --git a/src/business/business-core/sql/gnc-bill-term-sql.c b/src/business/business-core/sql/gnc-bill-term-sql.c new file mode 100644 index 0000000000..fd1fbeab66 --- /dev/null +++ b/src/business/business-core/sql/gnc-bill-term-sql.c @@ -0,0 +1,242 @@ +/********************************************************************\ + * gnc-bill-term-sql.c -- billing term sql backend * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * +\********************************************************************/ + +/** @file gnc-bill-term-sql.c + * @brief load and save address data to SQL + * @author Copyright (c) 2007-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#include "config.h" + +#include +#include +#include + +#include "gnc-backend-sql.h" +#include "gnc-slots-sql.h" + +#include "gncBillTermP.h" +#include "gncInvoice.h" +#include "gnc-bill-term-sql.h" +#include "qof.h" + +#define _GNC_MOD_NAME GNC_ID_BILLTERM + +static QofLogModule log_module = G_LOG_DOMAIN; + +#define MAX_NAME_LEN 2048 +#define MAX_DESCRIPTION_LEN 2048 +#define MAX_TYPE_LEN 2048 + +static void set_invisible( gpointer data, gboolean value ); + +#define TABLE_NAME "billterms" +#define TABLE_VERSION 1 + +static GncSqlColumnTableEntry col_table[] = +{ + { "guid", CT_GUID, 0, COL_NNUL|COL_PKEY, "guid" }, + { "name", CT_STRING, MAX_NAME_LEN, COL_NNUL, NULL, GNC_BILLTERM_NAME }, + { "description", CT_STRING, MAX_DESCRIPTION_LEN, COL_NNUL, NULL, GNC_BILLTERM_DESC }, + { "refcount", CT_INT, 0, COL_NNUL, NULL, NULL, + (QofAccessFunc)gncBillTermGetRefcount, (QofSetterFunc)gncBillTermSetRefcount }, + { "invisible", CT_BOOLEAN, 0, COL_NNUL, NULL, NULL, + (QofAccessFunc)gncBillTermGetInvisible, (QofSetterFunc)set_invisible }, + { "parent", CT_BILLTERMREF, 0, 0, NULL, NULL, + (QofAccessFunc)gncBillTermGetParent, (QofSetterFunc)gncBillTermSetParent }, + { "child", CT_BILLTERMREF, 0, 0, NULL, NULL, + (QofAccessFunc)gncBillTermReturnChild, (QofSetterFunc)gncBillTermSetChild }, + { "type", CT_STRING, MAX_TYPE_LEN, COL_NNUL, NULL, GNC_BILLTERM_TYPE }, + { "duedays", CT_INT, 0, 0, 0, GNC_BILLTERM_DUEDAYS }, + { "discountdays", CT_INT, 0, 0, 0, GNC_BILLTERM_DISCDAYS }, + { "discount", CT_NUMERIC, 0, 0, 0, GNC_BILLTERM_DISCOUNT }, + { "cutoff", CT_INT, 0, 0, 0, GNC_BILLTERM_CUTOFF }, + { NULL } +}; + +static void +set_invisible( gpointer data, gboolean value ) +{ + GncBillTerm* term = GNC_BILLTERM(data); + + g_return_if_fail( term != NULL ); + + if( value ) { + gncBillTermMakeInvisible( term ); + } +} + +static GncBillTerm* +load_single_billterm( GncSqlBackend* be, GncSqlRow* row ) +{ + const GUID* guid; + GncBillTerm* pBillTerm; + + g_return_val_if_fail( be != NULL, NULL ); + g_return_val_if_fail( row != NULL, NULL ); + + guid = gnc_sql_load_guid( be, row ); + pBillTerm = gncBillTermLookup( be->primary_book, guid ); + if( pBillTerm == NULL ) { + pBillTerm = gncBillTermCreate( be->primary_book ); + } + gnc_sql_load_object( be, row, GNC_ID_BILLTERM, pBillTerm, col_table ); + qof_instance_mark_clean( QOF_INSTANCE(pBillTerm) ); + + return pBillTerm; +} + +static void +load_all_billterms( GncSqlBackend* be ) +{ + GncSqlStatement* stmt; + GncSqlResult* result; + QofBook* pBook; + + g_return_if_fail( be != NULL ); + + pBook = be->primary_book; + + stmt = gnc_sql_create_select_statement( be, TABLE_NAME ); + result = gnc_sql_execute_select_statement( be, stmt ); + gnc_sql_statement_dispose( stmt ); + if( result != NULL ) { + GncSqlRow* row; + GList* list = NULL; + + row = gnc_sql_result_get_first_row( result ); + while( row != NULL ) { + GncBillTerm* pBillTerm = load_single_billterm( be, row ); + if( pBillTerm != NULL ) { + list = g_list_append( list, pBillTerm ); + } + row = gnc_sql_result_get_next_row( result ); + } + gnc_sql_result_dispose( result ); + + if( list != NULL ) { + gnc_sql_slots_load_for_list( be, list ); + } + } +} + +/* ================================================================= */ +static void +do_save_billterm( QofInstance* inst, gpointer p2 ) +{ + gnc_sql_save_billterm( (GncSqlBackend*)p2, inst ); +} + +static void +write_billterms( GncSqlBackend* be ) +{ + g_return_if_fail( be != NULL ); + + qof_object_foreach( GNC_ID_BILLTERM, be->primary_book, do_save_billterm, (gpointer)be ); +} + +/* ================================================================= */ +static void +create_billterm_tables( GncSqlBackend* be ) +{ + gint version; + + g_return_if_fail( be != NULL ); + + version = gnc_sql_get_table_version( be, TABLE_NAME ); + if( version == 0 ) { + gnc_sql_create_table( be, TABLE_NAME, TABLE_VERSION, col_table ); + } +} + +/* ================================================================= */ +void +gnc_sql_save_billterm( GncSqlBackend* be, QofInstance* inst ) +{ + g_return_if_fail( inst != NULL ); + g_return_if_fail( GNC_IS_BILLTERM(inst) ); + g_return_if_fail( be != NULL ); + + gnc_sql_commit_standard_item( be, inst, TABLE_NAME, GNC_ID_BILLTERM, col_table ); +} + +/* ================================================================= */ +static void +load_billterm_guid( const GncSqlBackend* be, GncSqlRow* row, + QofSetterFunc setter, gpointer pObject, + const GncSqlColumnTableEntry* table_row ) +{ + const GValue* val; + GUID guid; + const GUID* pGuid; + GncBillTerm* term = NULL; + + g_return_if_fail( be != NULL ); + g_return_if_fail( row != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + + val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name ); + if( val == NULL || !G_VALUE_HOLDS_STRING( val ) || g_value_get_string( val ) == NULL ) { + pGuid = NULL; + } else { + string_to_guid( g_value_get_string( val ), &guid ); + pGuid = &guid; + } + if( pGuid != NULL ) { + term = gncBillTermLookup( be->primary_book, pGuid ); + } + if( table_row->gobj_param_name != NULL ) { + g_object_set( pObject, table_row->gobj_param_name, term, NULL ); + } else { + (*setter)( pObject, (const gpointer)term ); + } +} + +static col_type_handler_t billterm_guid_handler + = { load_billterm_guid, + gnc_sql_add_objectref_guid_col_info_to_list, + gnc_sql_add_colname_to_list, + gnc_sql_add_gvalue_objectref_guid_to_slist }; +/* ================================================================= */ +void +gnc_billterm_sql_initialize( void ) +{ + static GncSqlObjectBackend be_data = + { + GNC_SQL_BACKEND_VERSION, + GNC_ID_BILLTERM, + gnc_sql_save_billterm, /* commit */ + load_all_billterms, /* initial_load */ + create_billterm_tables, /* create_tables */ + NULL, NULL, NULL, + write_billterms /* write */ + }; + + qof_object_register_backend( GNC_ID_BILLTERM, GNC_SQL_BACKEND, &be_data ); + + gnc_sql_register_col_type_handler( CT_BILLTERMREF, &billterm_guid_handler ); +} +/* ========================== END OF FILE ===================== */ diff --git a/src/business/business-core/sql/gnc-bill-term-sql.h b/src/business/business-core/sql/gnc-bill-term-sql.h new file mode 100644 index 0000000000..54fdf1858d --- /dev/null +++ b/src/business/business-core/sql/gnc-bill-term-sql.h @@ -0,0 +1,40 @@ +/* + * gnc-bill-term-sql.h -- billing term sql backend + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, contact: + * + * Free Software Foundation Voice: +1-617-542-5942 + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 + * Boston, MA 02110-1301, USA gnu@gnu.org + */ + +/** @file gnc-bill-term-sql.h + * @brief load and save accounts data to SQL + * @author Copyright (c) 2006-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#ifndef GNC_BILLTERM_SQL_H +#define GNC_BILLTERM_SQL_H + +#include "gncBillTerm.h" + +#define CT_BILLTERMREF "billterm" + +void gnc_billterm_sql_initialize( void ); +void gnc_sql_save_billterm( GncSqlBackend* be, QofInstance* inst ); + +#endif /* GNC_BILLTERM_SQL_H */ diff --git a/src/business/business-core/sql/gnc-customer-sql.c b/src/business/business-core/sql/gnc-customer-sql.c new file mode 100644 index 0000000000..c450279d5f --- /dev/null +++ b/src/business/business-core/sql/gnc-customer-sql.c @@ -0,0 +1,216 @@ +/********************************************************************\ + * gnc-customer-sql.c -- customer sql backend * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * +\********************************************************************/ + +/** @file gnc-customer-sql.c + * @brief load and save address data to SQL + * @author Copyright (c) 2007-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#include "config.h" + +#include +#include +#include + +#include "gnc-backend-sql.h" +#include "gnc-slots-sql.h" + +#include "gncBillTermP.h" +#include "gncCustomerP.h" +#include "gncTaxTableP.h" +#include "gnc-customer-sql.h" +#include "gnc-address-sql.h" +#include "gnc-bill-term-sql.h" +#include "gnc-tax-table-sql.h" + +#define _GNC_MOD_NAME GNC_ID_CUSTOMER + +static QofLogModule log_module = G_LOG_DOMAIN; + +#define TABLE_NAME "customers" +#define TABLE_VERSION 1 + +#define MAX_NAME_LEN 2048 +#define MAX_ID_LEN 2048 +#define MAX_NOTES_LEN 2048 + +static GncSqlColumnTableEntry col_table[] = +{ + { "guid", CT_GUID, 0, COL_NNUL|COL_PKEY, "guid" }, + { "name", CT_STRING, MAX_NAME_LEN, COL_NNUL, NULL, CUSTOMER_NAME }, + { "id", CT_STRING, MAX_ID_LEN, COL_NNUL, NULL, CUSTOMER_ID }, + { "notes", CT_STRING, MAX_NOTES_LEN, COL_NNUL, NULL, CUSTOMER_NOTES }, + { "active", CT_BOOLEAN, 0, COL_NNUL, NULL, QOF_PARAM_ACTIVE }, + { "discount", CT_NUMERIC, 0, COL_NNUL, NULL, CUSTOMER_DISCOUNT }, + { "credit", CT_NUMERIC, 0, COL_NNUL, NULL, CUSTOMER_CREDIT }, + { "currency", CT_COMMODITYREF, 0, COL_NNUL, NULL, NULL, + (QofAccessFunc)gncCustomerGetCurrency, (QofSetterFunc)gncCustomerSetCurrency }, + { "tax_override", CT_BOOLEAN, 0, COL_NNUL, NULL, CUSTOMER_TT_OVER }, + { "addr", CT_ADDRESS, 0, 0, NULL, CUSTOMER_ADDR }, + { "shipaddr", CT_ADDRESS, 0, 0, NULL, CUSTOMER_SHIPADDR }, + { "terms", CT_BILLTERMREF, 0, 0, NULL, CUSTOMER_TERMS }, + { "tax_included", CT_INT, 0, 0, NULL, NULL, + (QofAccessFunc)gncCustomerGetTaxIncluded, (QofSetterFunc)gncCustomerSetTaxIncluded }, + { "taxtable", CT_TAXTABLEREF, 0, 0, NULL, NULL, + (QofAccessFunc)gncCustomerGetTaxTable, (QofSetterFunc)gncCustomerSetTaxTable }, + { NULL } +}; + +static GncCustomer* +load_single_customer( GncSqlBackend* be, GncSqlRow* row ) +{ + const GUID* guid; + GncCustomer* pCustomer; + + g_return_val_if_fail( be != NULL, NULL ); + g_return_val_if_fail( row != NULL, NULL ); + + guid = gnc_sql_load_guid( be, row ); + pCustomer = gncCustomerLookup( be->primary_book, guid ); + if( pCustomer == NULL ) { + pCustomer = gncCustomerCreate( be->primary_book ); + } + gnc_sql_load_object( be, row, GNC_ID_CUSTOMER, pCustomer, col_table ); + qof_instance_mark_clean( QOF_INSTANCE(pCustomer) ); + + return pCustomer; +} + +static void +load_all_customers( GncSqlBackend* be ) +{ + GncSqlStatement* stmt; + GncSqlResult* result; + QofBook* pBook; + + g_return_if_fail( be != NULL ); + + pBook = be->primary_book; + + stmt = gnc_sql_create_select_statement( be, TABLE_NAME ); + result = gnc_sql_execute_select_statement( be, stmt ); + gnc_sql_statement_dispose( stmt ); + if( result != NULL ) { + GList* list = NULL; + GncSqlRow* row; + + row = gnc_sql_result_get_first_row( result ); + while( row != NULL ) { + GncCustomer* pCustomer = load_single_customer( be, row ); + if( pCustomer != NULL ) { + list = g_list_append( list, pCustomer ); + } + row = gnc_sql_result_get_next_row( result ); + } + gnc_sql_result_dispose( result ); + + if( list != NULL ) { + gnc_sql_slots_load_for_list( be, list ); + } + } +} + +/* ================================================================= */ +static void +create_customer_tables( GncSqlBackend* be ) +{ + gint version; + + g_return_if_fail( be != NULL ); + + version = gnc_sql_get_table_version( be, TABLE_NAME ); + if( version == 0 ) { + gnc_sql_create_table( be, TABLE_NAME, TABLE_VERSION, col_table ); + } +} + +/* ================================================================= */ +static void +save_customer( GncSqlBackend* be, QofInstance* inst ) +{ + g_return_if_fail( inst != NULL ); + g_return_if_fail( GNC_CUSTOMER(inst) ); + g_return_if_fail( be != NULL ); + + gnc_sql_commit_standard_item( be, inst, TABLE_NAME, GNC_ID_CUSTOMER, col_table ); +} + +/* ================================================================= */ +static gboolean +customer_should_be_saved( GncCustomer *customer ) +{ + const char *id; + + g_return_val_if_fail( customer != NULL, FALSE ); + + /* Make sure this is a valid customer before we save it -- should have an ID */ + id = gncCustomerGetID( customer ); + if( id == NULL || *id == '\0' ) { + return FALSE; + } + + return TRUE; +} + +static void +write_single_customer( QofInstance *term_p, gpointer be_p ) +{ + GncSqlBackend* be = (GncSqlBackend*)be_p; + + g_return_if_fail( term_p != NULL ); + g_return_if_fail( GNC_IS_CUSTOMER(term_p) ); + g_return_if_fail( be_p != NULL ); + + if( customer_should_be_saved( GNC_CUSTOMER(term_p) ) ) { + save_customer( be, term_p ); + } +} + +static void +write_customers( GncSqlBackend* be ) +{ + g_return_if_fail( be != NULL ); + + qof_object_foreach( GNC_ID_CUSTOMER, be->primary_book, write_single_customer, (gpointer)be ); +} + +/* ================================================================= */ +void +gnc_customer_sql_initialize( void ) +{ + static GncSqlObjectBackend be_data = + { + GNC_SQL_BACKEND_VERSION, + GNC_ID_CUSTOMER, + save_customer, /* commit */ + load_all_customers, /* initial_load */ + create_customer_tables, /* create_tables */ + NULL, NULL, NULL, + write_customers /* write */ + }; + + qof_object_register_backend( GNC_ID_CUSTOMER, GNC_SQL_BACKEND, &be_data ); +} +/* ========================== END OF FILE ===================== */ diff --git a/src/business/business-core/sql/gnc-customer-sql.h b/src/business/business-core/sql/gnc-customer-sql.h new file mode 100644 index 0000000000..1a8b679a56 --- /dev/null +++ b/src/business/business-core/sql/gnc-customer-sql.h @@ -0,0 +1,35 @@ +/* + * gnc-customer-sql.h -- customer sql backend + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, contact: + * + * Free Software Foundation Voice: +1-617-542-5942 + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 + * Boston, MA 02110-1301, USA gnu@gnu.org + */ + +/** @file gnc-customer-sql.h + * @brief load and save customer data to SQL + * @author Copyright (c) 2007-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#ifndef GNC_CUSTOMER_SQL_H +#define GNC_CUSTOMER_SQL_H + +void gnc_customer_sql_initialize( void ); + +#endif /* GNC_CUSTOMER_SQL_H */ diff --git a/src/business/business-core/sql/gnc-employee-sql.c b/src/business/business-core/sql/gnc-employee-sql.c new file mode 100644 index 0000000000..c6321592a3 --- /dev/null +++ b/src/business/business-core/sql/gnc-employee-sql.c @@ -0,0 +1,240 @@ +/********************************************************************\ + * gnc-employee-sql.c -- employee sql implementation * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * +\********************************************************************/ + +/** @file gnc-employee-sql.c + * @brief load and save address data to SQL + * @author Copyright (c) 2007-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#include "config.h" + +#include +#include +#include + +#include "gnc-commodity.h" + +#include "gnc-backend-sql.h" +#include "gnc-slots-sql.h" +#include "gnc-commodity-sql.h" + +#include "gncEmployeeP.h" +#include "gnc-employee-sql.h" +#include "gnc-address-sql.h" + +#define _GNC_MOD_NAME GNC_ID_EMPLOYEE + +static QofLogModule log_module = G_LOG_DOMAIN; + +#define MAX_USERNAME_LEN 2048 +#define MAX_ID_LEN 2048 +#define MAX_LANGUAGE_LEN 2048 +#define MAX_ACL_LEN 2048 + +#define TABLE_NAME "employees" +#define TABLE_VERSION 1 + +static GncSqlColumnTableEntry col_table[] = +{ + { "guid", CT_GUID, 0, COL_NNUL|COL_PKEY, "guid" }, + { "username", CT_STRING, MAX_USERNAME_LEN, COL_NNUL, NULL, EMPLOYEE_USERNAME }, + { "id", CT_STRING, MAX_ID_LEN, COL_NNUL, NULL, EMPLOYEE_ID }, + { "language", CT_STRING, MAX_LANGUAGE_LEN, COL_NNUL, NULL, EMPLOYEE_LANGUAGE }, + { "acl", CT_STRING, MAX_ACL_LEN, COL_NNUL, NULL, EMPLOYEE_ACL }, + { "active", CT_BOOLEAN, 0, COL_NNUL, NULL, QOF_PARAM_ACTIVE }, + { "currency", CT_COMMODITYREF, 0, COL_NNUL, NULL, NULL, + (QofAccessFunc)gncEmployeeGetCurrency, (QofSetterFunc)gncEmployeeSetCurrency }, + { "ccard_guid", CT_ACCOUNTREF, 0, 0, NULL, EMPLOYEE_CC }, + { "workday", CT_NUMERIC, 0, COL_NNUL, NULL, EMPLOYEE_WORKDAY }, + { "rate", CT_NUMERIC, 0, COL_NNUL, NULL, EMPLOYEE_RATE }, + { "addr", CT_ADDRESS, 0, 0, NULL, EMPLOYEE_ADDR }, + { NULL } +}; + +static GncEmployee* +load_single_employee( GncSqlBackend* be, GncSqlRow* row ) +{ + const GUID* guid; + GncEmployee* pEmployee; + + g_return_val_if_fail( be != NULL, NULL ); + g_return_val_if_fail( row != NULL, NULL ); + + guid = gnc_sql_load_guid( be, row ); + pEmployee = gncEmployeeLookup( be->primary_book, guid ); + if( pEmployee == NULL ) { + pEmployee = gncEmployeeCreate( be->primary_book ); + } + gnc_sql_load_object( be, row, GNC_ID_EMPLOYEE, pEmployee, col_table ); + qof_instance_mark_clean( QOF_INSTANCE(pEmployee) ); + + return pEmployee; +} + +static void +load_all_employees( GncSqlBackend* be ) +{ + GncSqlStatement* stmt; + GncSqlResult* result; + QofBook* pBook; + gnc_commodity_table* pTable; + + g_return_if_fail( be != NULL ); + + pBook = be->primary_book; + pTable = gnc_commodity_table_get_table( pBook ); + + stmt = gnc_sql_create_select_statement( be, TABLE_NAME ); + result = gnc_sql_execute_select_statement( be, stmt ); + gnc_sql_statement_dispose( stmt ); + if( result != NULL ) { + GncSqlRow* row; + GList* list = NULL; + + row = gnc_sql_result_get_first_row( result ); + while( row != NULL ) { + GncEmployee* pEmployee = load_single_employee( be, row ); + if( pEmployee != NULL ) { + list = g_list_append( list, pEmployee ); + } + row = gnc_sql_result_get_next_row( result ); + } + gnc_sql_result_dispose( result ); + + if( list != NULL ) { + gnc_sql_slots_load_for_list( be, list ); + } + } +} + +/* ================================================================= */ +static void +create_employee_tables( GncSqlBackend* be ) +{ + gint version; + + g_return_if_fail( be != NULL ); + + version = gnc_sql_get_table_version( be, TABLE_NAME ); + if( version == 0 ) { + gnc_sql_create_table( be, TABLE_NAME, TABLE_VERSION, col_table ); + } +} + +/* ================================================================= */ +static void +save_employee( GncSqlBackend* be, QofInstance* inst ) +{ + GncEmployee* emp; + const GUID* guid; + gint op; + gboolean is_infant; + + g_return_if_fail( inst != NULL ); + g_return_if_fail( GNC_IS_EMPLOYEE(inst) ); + g_return_if_fail( be != NULL ); + + emp = GNC_EMPLOYEE(inst); + + 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 ) { + // Ensure the commodity is in the db + gnc_sql_save_commodity( be, gncEmployeeGetCurrency( emp ) ); + } + + (void)gnc_sql_do_db_operation( be, op, TABLE_NAME, GNC_ID_EMPLOYEE, emp, col_table ); + + // Now, commit or delete any slots + guid = qof_instance_get_guid( inst ); + if( !qof_instance_get_destroying(inst) ) { + gnc_sql_slots_save( be, guid, is_infant, qof_instance_get_slots( inst ) ); + } else { + gnc_sql_slots_delete( be, guid ); + } +} + +/* ================================================================= */ +static gboolean +employee_should_be_saved( GncEmployee *employee ) +{ + const char *id; + + g_return_val_if_fail( employee != NULL, FALSE ); + + /* make sure this is a valid employee before we save it -- should have an ID */ + id = gncEmployeeGetID( employee ); + if( id == NULL || *id == '\0' ) { + return FALSE; + } + + return TRUE; +} +static void +write_single_employee( QofInstance *term_p, gpointer be_p ) +{ + GncSqlBackend* be = (GncSqlBackend*)be_p; + + g_return_if_fail( term_p != NULL ); + g_return_if_fail( GNC_IS_EMPLOYEE(term_p) ); + g_return_if_fail( be_p != NULL ); + + if( employee_should_be_saved( GNC_EMPLOYEE(term_p) ) ) { + save_employee( be, term_p ); + } +} + +static void +write_employees( GncSqlBackend* be ) +{ + g_return_if_fail( be != NULL ); + + qof_object_foreach( GNC_ID_EMPLOYEE, be->primary_book, write_single_employee, (gpointer)be ); +} + +/* ================================================================= */ +void +gnc_employee_sql_initialize( void ) +{ + static GncSqlObjectBackend be_data = + { + GNC_SQL_BACKEND_VERSION, + GNC_ID_EMPLOYEE, + save_employee, /* commit */ + load_all_employees, /* initial_load */ + create_employee_tables, /* create_tables */ + NULL, NULL, NULL, + write_employees /* write */ + }; + + qof_object_register_backend( GNC_ID_EMPLOYEE, GNC_SQL_BACKEND, &be_data ); +} +/* ========================== END OF FILE ===================== */ diff --git a/src/business/business-core/sql/gnc-employee-sql.h b/src/business/business-core/sql/gnc-employee-sql.h new file mode 100644 index 0000000000..ca0c99a90f --- /dev/null +++ b/src/business/business-core/sql/gnc-employee-sql.h @@ -0,0 +1,35 @@ +/* + * gnc-employee-sql.h -- employee sql backend + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, contact: + * + * Free Software Foundation Voice: +1-617-542-5942 + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 + * Boston, MA 02110-1301, USA gnu@gnu.org + */ + +/** @file gnc-employee-sql.h + * @brief load and save employee data to SQL + * @author Copyright (c) 2007-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#ifndef GNC_EMPLOYEE_SQL_H +#define GNC_EMPLOYEE_SQL_H + +void gnc_employee_sql_initialize( void ); + +#endif /* GNC_EMPLOYEE_SQL_H */ diff --git a/src/business/business-core/sql/gnc-entry-sql.c b/src/business/business-core/sql/gnc-entry-sql.c new file mode 100644 index 0000000000..21fa5e0d34 --- /dev/null +++ b/src/business/business-core/sql/gnc-entry-sql.c @@ -0,0 +1,223 @@ +/********************************************************************\ + * gnc-entry-sql.c -- entry sql backend * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * +\********************************************************************/ + +/** @file gnc-entry-sql.c + * @brief load and save address data to SQL + * @author Copyright (c) 2007-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#include "config.h" + +#include +#include +#include + +#include "gnc-backend-sql.h" +#include "gnc-slots-sql.h" + +#include "gncEntryP.h" +#include "gncOrderP.h" +#include "gncInvoiceP.h" +#include "gncTaxTableP.h" +#include "gnc-bill-term-sql.h" +#include "gnc-entry-sql.h" +#include "gnc-invoice-sql.h" +#include "gnc-order-sql.h" +#include "gnc-owner-sql.h" +#include "gnc-tax-table-sql.h" + +#define _GNC_MOD_NAME GNC_ID_ENTRY + +static QofLogModule log_module = G_LOG_DOMAIN; + +#define TABLE_NAME "entries" +#define TABLE_VERSION 1 +#define MAX_DESCRIPTION_LEN 2048 +#define MAX_ACTION_LEN 2048 +#define MAX_NOTES_LEN 2048 +#define MAX_DISCTYPE_LEN 2048 +#define MAX_DISCHOW_LEN 2048 + +static GncSqlColumnTableEntry col_table[] = +{ + { "guid", CT_GUID, 0, COL_NNUL|COL_PKEY, "guid" }, + { "date", CT_TIMESPEC, 0, COL_NNUL, NULL, ENTRY_DATE }, + { "entered", CT_TIMESPEC, 0, COL_NNUL, NULL, ENTRY_DATE_ENTERED }, + { "description", CT_STRING, MAX_DESCRIPTION_LEN, 0, NULL, ENTRY_DESC }, + { "action", CT_STRING, MAX_ACTION_LEN, 0, NULL, ENTRY_ACTION }, + { "notes", CT_STRING, MAX_NOTES_LEN, 0, NULL, ENTRY_NOTES }, + { "quantity", CT_NUMERIC, 0, 0, NULL, ENTRY_QTY }, + { "i_acct", CT_ACCOUNTREF, 0, 0, NULL, ENTRY_IACCT }, + { "i_price", CT_NUMERIC, 0, 0, NULL, ENTRY_IPRICE }, + { "i_discount", CT_NUMERIC, 0, 0, NULL, NULL, + (QofAccessFunc)gncEntryGetInvDiscount, (QofSetterFunc)gncEntrySetInvDiscount }, + { "invoice", CT_INVOICEREF, 0, 0, NULL, NULL, + (QofAccessFunc)gncEntryGetInvoice, (QofSetterFunc)gncEntrySetInvoice }, + { "i_disc_type", CT_STRING, MAX_DISCTYPE_LEN, 0, NULL, ENTRY_INV_DISC_TYPE }, + { "i_disc_how", CT_STRING, MAX_DISCHOW_LEN, 0, NULL, ENTRY_INV_DISC_HOW }, + { "i_taxable", CT_BOOLEAN, 0, 0, NULL, ENTRY_INV_TAXABLE }, + { "i_taxincluded", CT_BOOLEAN, 0, 0, NULL, ENTRY_INV_TAX_INC }, + { "i_taxtable", CT_TAXTABLEREF, 0, 0, NULL, NULL, + (QofAccessFunc)gncEntryGetInvTaxTable, (QofSetterFunc)gncEntrySetInvTaxTable }, + { "b_acct", CT_ACCOUNTREF, 0, 0, NULL, ENTRY_BACCT }, + { "b_price", CT_NUMERIC, 0, 0, NULL, ENTRY_BPRICE }, + { "bill", CT_INVOICEREF, 0, 0, NULL, NULL, + (QofAccessFunc)gncEntryGetBill, (QofSetterFunc)gncEntrySetBill }, + { "b_taxable", CT_BOOLEAN, 0, 0, NULL, ENTRY_BILL_TAXABLE }, + { "b_taxincluded", CT_BOOLEAN, 0, 0, NULL, ENTRY_BILL_TAX_INC }, + { "b_taxtable", CT_TAXTABLEREF, 0, 0, NULL, NULL, + (QofAccessFunc)gncEntryGetBillTaxTable, (QofSetterFunc)gncEntrySetBillTaxTable }, + { "b_paytype", CT_INT, 0, 0, NULL, NULL, + (QofAccessFunc)gncEntryGetBillPayment, (QofSetterFunc)gncEntrySetBillPayment }, + { "billable", CT_BOOLEAN, 0, 0, NULL, ENTRY_BILLABLE }, + { "billto", CT_OWNERREF, 0, 0, NULL, ENTRY_BILLTO }, + { "order_guid", CT_ORDERREF, 0, 0, NULL, NULL, + (QofAccessFunc)gncEntryGetOrder, (QofSetterFunc)gncEntrySetOrder }, + { NULL } +}; + +static GncEntry* +load_single_entry( GncSqlBackend* be, GncSqlRow* row ) +{ + const GUID* guid; + GncEntry* pEntry; + + g_return_val_if_fail( be != NULL, NULL ); + g_return_val_if_fail( row != NULL, NULL ); + + guid = gnc_sql_load_guid( be, row ); + pEntry = gncEntryLookup( be->primary_book, guid ); + if( pEntry == NULL ) { + pEntry = gncEntryCreate( be->primary_book ); + } + gnc_sql_load_object( be, row, GNC_ID_ENTRY, pEntry, col_table ); + qof_instance_mark_clean( QOF_INSTANCE(pEntry) ); + + return pEntry; +} + +static void +load_all_entries( GncSqlBackend* be ) +{ + GncSqlStatement* stmt; + GncSqlResult* result; + QofBook* pBook; + + g_return_if_fail( be != NULL ); + + pBook = be->primary_book; + + stmt = gnc_sql_create_select_statement( be, TABLE_NAME ); + result = gnc_sql_execute_select_statement( be, stmt ); + gnc_sql_statement_dispose( stmt ); + if( result != NULL ) { + GncSqlRow* row; + GList* list = NULL; + + row = gnc_sql_result_get_first_row( result ); + while( row != NULL ) { + GncEntry* pEntry = load_single_entry( be, row ); + if( pEntry != NULL ) { + list = g_list_append( list, pEntry ); + } + row = gnc_sql_result_get_next_row( result ); + } + gnc_sql_result_dispose( result ); + + if( list != NULL ) { + gnc_sql_slots_load_for_list( be, list ); + } + } +} + +/* ================================================================= */ +static void +create_entry_tables( GncSqlBackend* be ) +{ + gint version; + + g_return_if_fail( be != NULL ); + + version = gnc_sql_get_table_version( be, TABLE_NAME ); + if( version == 0 ) { + gnc_sql_create_table( be, TABLE_NAME, TABLE_VERSION, col_table ); + } +} + +/* ================================================================= */ +static void +save_entry( GncSqlBackend* be, QofInstance* inst ) +{ + g_return_if_fail( inst != NULL ); + g_return_if_fail( GNC_IS_ENTRY(inst) ); + g_return_if_fail( be != NULL ); + + gnc_sql_commit_standard_item( be, inst, TABLE_NAME, GNC_ID_ENTRY, col_table ); +} + +/* ================================================================= */ +static void +write_single_entry( QofInstance *term_p, gpointer be_p ) +{ + GncSqlBackend* be = (GncSqlBackend*)be_p; + GncEntry* entry = GNC_ENTRY(term_p); + + g_return_if_fail( term_p != NULL ); + g_return_if_fail( GNC_IS_ENTRY(term_p) ); + g_return_if_fail( be_p != NULL ); + + /* Only save if attached */ + if( gncEntryGetOrder( entry ) != NULL || gncEntryGetInvoice( entry ) != NULL || + gncEntryGetBill( entry ) != NULL ) { + save_entry( be, term_p ); + } +} + +static void +write_entries( GncSqlBackend* be ) +{ + g_return_if_fail( be != NULL ); + + qof_object_foreach( GNC_ID_ENTRY, be->primary_book, write_single_entry, (gpointer)be ); +} + +/* ================================================================= */ +void +gnc_entry_sql_initialize( void ) +{ + static GncSqlObjectBackend be_data = + { + GNC_SQL_BACKEND_VERSION, + GNC_ID_ENTRY, + save_entry, /* commit */ + load_all_entries, /* initial_load */ + create_entry_tables, /* create_tables */ + NULL, NULL, NULL, + write_entries /* write */ + }; + + qof_object_register_backend( GNC_ID_ENTRY, GNC_SQL_BACKEND, &be_data ); +} +/* ========================== END OF FILE ===================== */ diff --git a/src/business/business-core/sql/gnc-entry-sql.h b/src/business/business-core/sql/gnc-entry-sql.h new file mode 100644 index 0000000000..bd111d10e2 --- /dev/null +++ b/src/business/business-core/sql/gnc-entry-sql.h @@ -0,0 +1,35 @@ +/* + * gnc-entry-sql.h -- entry sql backend + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, contact: + * + * Free Software Foundation Voice: +1-617-542-5942 + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 + * Boston, MA 02110-1301, USA gnu@gnu.org + */ + +/** @file gnc-entry-sql.h + * @brief load and save entry data to SQL + * @author Copyright (c) 2007-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#ifndef GNC_ENTRY_SQL_H +#define GNC_ENTRY_SQL_H + +void gnc_entry_sql_initialize( void ); + +#endif /* GNC_ENTRY_SQL_H */ diff --git a/src/business/business-core/sql/gnc-invoice-sql.c b/src/business/business-core/sql/gnc-invoice-sql.c new file mode 100644 index 0000000000..d4a6860393 --- /dev/null +++ b/src/business/business-core/sql/gnc-invoice-sql.c @@ -0,0 +1,288 @@ +/********************************************************************\ + * gnc-invoice-sql.c - invoice sql backend * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * +\********************************************************************/ + +/** @file gnc-invoice-sql.c + * @brief load and save address data to SQL + * @author Copyright (c) 2007-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#include "config.h" + +#include +#include +#include + +#include "gnc-commodity.h" + +#include "gnc-backend-sql.h" +#include "gnc-commodity-sql.h" +#include "gnc-slots-sql.h" + +#include "gncBillTermP.h" +#include "gncInvoiceP.h" +#include "gnc-invoice-sql.h" +#include "gnc-owner-sql.h" +#include "gnc-bill-term-sql.h" + +#define _GNC_MOD_NAME GNC_ID_INVOICE + +static QofLogModule log_module = G_LOG_DOMAIN; + +#define TABLE_NAME "invoices" +#define TABLE_VERSION 1 + +#define MAX_ID_LEN 2048 +#define MAX_NOTES_LEN 2048 +#define MAX_BILLING_ID_LEN 2048 + +static GncSqlColumnTableEntry col_table[] = +{ + { "guid", CT_GUID, 0, COL_NNUL|COL_PKEY, "guid" }, + { "id", CT_STRING, MAX_ID_LEN, COL_NNUL, NULL, INVOICE_ID }, + { "date_opened", CT_TIMESPEC, 0, COL_NNUL, NULL, INVOICE_OPENED }, + { "date_posted", CT_TIMESPEC, 0, 0, NULL, INVOICE_POSTED }, + { "notes", CT_STRING, MAX_NOTES_LEN, COL_NNUL, NULL, INVOICE_NOTES }, + { "active", CT_BOOLEAN, 0, COL_NNUL, NULL, QOF_PARAM_ACTIVE }, + { "currency", CT_COMMODITYREF, 0, COL_NNUL, NULL, NULL, + (QofAccessFunc)gncInvoiceGetCurrency, (QofSetterFunc)gncInvoiceSetCurrency }, + { "owner", CT_OWNERREF, 0, 0, NULL, NULL, + (QofAccessFunc)gncInvoiceGetOwner, (QofSetterFunc)gncInvoiceSetOwner }, + { "terms", CT_BILLTERMREF, 0, 0, NULL, INVOICE_TERMS }, + { "billing_id", CT_STRING, MAX_BILLING_ID_LEN, 0, NULL, INVOICE_BILLINGID }, + { "post_txn", CT_TXREF, 0, 0, NULL, INVOICE_POST_TXN }, + { "post_lot", CT_LOTREF, 0, 0, NULL, NULL, + (QofAccessFunc)gncInvoiceGetPostedLot, (QofSetterFunc)gncInvoiceSetPostedLot }, + { "post_acc", CT_ACCOUNTREF, 0, 0, NULL, INVOICE_ACC }, + { "billto", CT_OWNERREF, 0, 0, NULL, NULL, + (QofAccessFunc)gncInvoiceGetBillTo, (QofSetterFunc)gncInvoiceSetBillTo }, + { "charge_amt", CT_NUMERIC, 0, 0, NULL, NULL, + (QofAccessFunc)gncInvoiceGetToChargeAmount, (QofSetterFunc)gncInvoiceSetToChargeAmount }, + { NULL } +}; + +static GncInvoice* +load_single_invoice( GncSqlBackend* be, GncSqlRow* row ) +{ + const GUID* guid; + GncInvoice* pInvoice; + + g_return_val_if_fail( be != NULL, NULL ); + g_return_val_if_fail( row != NULL, NULL ); + + guid = gnc_sql_load_guid( be, row ); + pInvoice = gncInvoiceLookup( be->primary_book, guid ); + if( pInvoice == NULL ) { + pInvoice = gncInvoiceCreate( be->primary_book ); + } + gnc_sql_load_object( be, row, GNC_ID_INVOICE, pInvoice, col_table ); + qof_instance_mark_clean( QOF_INSTANCE(pInvoice) ); + + return pInvoice; +} + +static void +load_all_invoices( GncSqlBackend* be ) +{ + GncSqlStatement* stmt; + GncSqlResult* result; + QofBook* pBook; + + g_return_if_fail( be != NULL ); + + pBook = be->primary_book; + + stmt = gnc_sql_create_select_statement( be, TABLE_NAME ); + result = gnc_sql_execute_select_statement( be, stmt ); + gnc_sql_statement_dispose( stmt ); + if( result != NULL ) { + GncSqlRow* row; + GList* list = NULL; + + row = gnc_sql_result_get_first_row( result ); + while( row != NULL ) { + GncInvoice* pInvoice = load_single_invoice( be, row ); + if( pInvoice != NULL ) { + list = g_list_append( list, pInvoice ); + } + row = gnc_sql_result_get_next_row( result ); + } + gnc_sql_result_dispose( result ); + + if( list != NULL ) { + gnc_sql_slots_load_for_list( be, list ); + } + } +} + +/* ================================================================= */ +static void +create_invoice_tables( GncSqlBackend* be ) +{ + gint version; + + g_return_if_fail( be != NULL ); + + version = gnc_sql_get_table_version( be, TABLE_NAME ); + if( version == 0 ) { + gnc_sql_create_table( be, TABLE_NAME, TABLE_VERSION, col_table ); + } +} + +/* ================================================================= */ +static void +save_invoice( GncSqlBackend* be, QofInstance* inst ) +{ + const GUID* guid; + GncInvoice* invoice; + gint op; + gboolean is_infant; + + g_return_if_fail( inst != NULL ); + g_return_if_fail( GNC_IS_INVOICE(inst) ); + g_return_if_fail( be != NULL ); + + invoice = GNC_INVOICE(inst); + + 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 ) { + // Ensure the commodity is in the db + gnc_sql_save_commodity( be, gncInvoiceGetCurrency( invoice ) ); + } + + (void)gnc_sql_do_db_operation( be, op, TABLE_NAME, GNC_ID_INVOICE, inst, col_table ); + + // Now, commit or delete any slots + guid = qof_instance_get_guid( inst ); + if( !qof_instance_get_destroying(inst) ) { + gnc_sql_slots_save( be, guid, is_infant, qof_instance_get_slots( inst ) ); + } else { + gnc_sql_slots_delete( be, guid ); + } +} + +/* ================================================================= */ +static gboolean +invoice_should_be_saved( GncInvoice *invoice ) +{ + const char *id; + + g_return_val_if_fail( invoice != NULL, FALSE ); + + /* make sure this is a valid invoice before we save it -- should have an ID */ + id = gncInvoiceGetID( invoice ); + if( id == NULL || *id == '\0' ) { + return FALSE; + } + + return TRUE; +} + +static void +write_single_invoice( QofInstance *term_p, gpointer be_p ) +{ + GncSqlBackend* be = (GncSqlBackend*)be_p; + + g_return_if_fail( term_p != NULL ); + g_return_if_fail( GNC_IS_INVOICE(term_p) ); + g_return_if_fail( be_p != NULL ); + + if( invoice_should_be_saved( GNC_INVOICE(term_p) ) ) { + save_invoice( be, term_p ); + } +} + +static void +write_invoices( GncSqlBackend* be ) +{ + g_return_if_fail( be != NULL ); + + qof_object_foreach( GNC_ID_INVOICE, be->primary_book, write_single_invoice, (gpointer)be ); +} + +/* ================================================================= */ +static void +load_invoice_guid( const GncSqlBackend* be, GncSqlRow* row, + QofSetterFunc setter, gpointer pObject, + const GncSqlColumnTableEntry* table_row ) +{ + const GValue* val; + GUID guid; + const GUID* pGuid; + GncInvoice* invoice = NULL; + + g_return_if_fail( be != NULL ); + g_return_if_fail( row != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + + val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name ); + if( val == NULL || !G_VALUE_HOLDS_STRING( val ) || g_value_get_string( val ) == NULL ) { + pGuid = NULL; + } else { + string_to_guid( g_value_get_string( val ), &guid ); + pGuid = &guid; + } + if( pGuid != NULL ) { + invoice = gncInvoiceLookup( be->primary_book, pGuid ); + } + if( table_row->gobj_param_name != NULL ) { + g_object_set( pObject, table_row->gobj_param_name, invoice, NULL ); + } else { + (*setter)( pObject, (const gpointer)invoice ); + } +} + +static col_type_handler_t invoice_guid_handler + = { load_invoice_guid, + gnc_sql_add_objectref_guid_col_info_to_list, + gnc_sql_add_colname_to_list, + gnc_sql_add_gvalue_objectref_guid_to_slist }; +/* ================================================================= */ +void +gnc_invoice_sql_initialize( void ) +{ + static GncSqlObjectBackend be_data = + { + GNC_SQL_BACKEND_VERSION, + GNC_ID_INVOICE, + save_invoice, /* commit */ + load_all_invoices, /* initial_load */ + create_invoice_tables, /* create_tables */ + NULL, NULL, NULL, + write_invoices /* write */ + }; + + qof_object_register_backend( GNC_ID_INVOICE, GNC_SQL_BACKEND, &be_data ); + + gnc_sql_register_col_type_handler( CT_INVOICEREF, &invoice_guid_handler ); +} +/* ========================== END OF FILE ===================== */ diff --git a/src/business/business-core/sql/gnc-invoice-sql.h b/src/business/business-core/sql/gnc-invoice-sql.h new file mode 100644 index 0000000000..4d047afb6d --- /dev/null +++ b/src/business/business-core/sql/gnc-invoice-sql.h @@ -0,0 +1,37 @@ +/* + * gnc-invoice-sql.h -- invoice sql backend + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, contact: + * + * Free Software Foundation Voice: +1-617-542-5942 + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 + * Boston, MA 02110-1301, USA gnu@gnu.org + */ + +/** @file gnc-invoice-sql.h + * @brief load and save invoice data to SQL + * @author Copyright (c) 2007-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#ifndef GNC_INVOICE_SQL_H +#define GNC_INVOICE_SQL_H + +#define CT_INVOICEREF "invoice" + +void gnc_invoice_sql_initialize( void ); + +#endif /* GNC_INVOICE_SQL_H */ diff --git a/src/business/business-core/sql/gnc-job-sql.c b/src/business/business-core/sql/gnc-job-sql.c new file mode 100644 index 0000000000..dadbd09713 --- /dev/null +++ b/src/business/business-core/sql/gnc-job-sql.c @@ -0,0 +1,203 @@ +/********************************************************************\ + * gnc-job-sql.c -- job sql backend * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * +\********************************************************************/ + +/** @file gnc-job-sql.c + * @brief load and save address data to SQL + * @author Copyright (c) 2007-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#include "config.h" + +#include +#include +#include + +#include "gnc-backend-sql.h" +#include "gnc-slots-sql.h" + +#include "gncJobP.h" +#include "gnc-job-sql.h" +#include "gnc-owner-sql.h" + +#define _GNC_MOD_NAME GNC_ID_JOB + +static QofLogModule log_module = G_LOG_DOMAIN; + +#define TABLE_NAME "jobs" +#define TABLE_VERSION 1 + +#define MAX_ID_LEN 2048 +#define MAX_NAME_LEN 2048 +#define MAX_REFERENCE_LEN 2048 + +static GncSqlColumnTableEntry col_table[] = +{ + { "guid", CT_GUID, 0, COL_NNUL|COL_PKEY, "guid" }, + { "id", CT_STRING, MAX_ID_LEN, COL_NNUL, NULL, JOB_ID }, + { "name", CT_STRING, MAX_NAME_LEN, COL_NNUL, NULL, JOB_NAME }, + { "reference", CT_STRING, MAX_REFERENCE_LEN, COL_NNUL, NULL, JOB_REFERENCE }, + { "active", CT_BOOLEAN, 0, COL_NNUL, NULL, NULL, + (QofAccessFunc)gncJobGetActive, (QofSetterFunc)gncJobSetActive }, + { "owner", CT_OWNERREF, 0, COL_NNUL, NULL, NULL, + (QofAccessFunc)gncJobGetOwner, (QofSetterFunc)gncJobSetOwner }, + { NULL } +}; + +static GncJob* +load_single_job( GncSqlBackend* be, GncSqlRow* row ) +{ + const GUID* guid; + GncJob* pJob; + + g_return_val_if_fail( be != NULL, NULL ); + g_return_val_if_fail( row != NULL, NULL ); + + guid = gnc_sql_load_guid( be, row ); + pJob = gncJobLookup( be->primary_book, guid ); + if( pJob == NULL ) { + pJob = gncJobCreate( be->primary_book ); + } + gnc_sql_load_object( be, row, GNC_ID_JOB, pJob, col_table ); + qof_instance_mark_clean( QOF_INSTANCE(pJob) ); + + return pJob; +} + +static void +load_all_jobs( GncSqlBackend* be ) +{ + GncSqlStatement* stmt; + GncSqlResult* result; + QofBook* pBook; + + g_return_if_fail( be != NULL ); + + pBook = be->primary_book; + + stmt = gnc_sql_create_select_statement( be, TABLE_NAME ); + result = gnc_sql_execute_select_statement( be, stmt ); + gnc_sql_statement_dispose( stmt ); + if( result != NULL ) { + GncSqlRow* row; + GList* list = NULL; + + row = gnc_sql_result_get_first_row( result ); + while( row != NULL ) { + GncJob* pJob = load_single_job( be, row ); + if( pJob != NULL ) { + list = g_list_append( list, pJob ); + } + row = gnc_sql_result_get_next_row( result ); + } + gnc_sql_result_dispose( result ); + + if( list != NULL ) { + gnc_sql_slots_load_for_list( be, list ); + } + } +} + +/* ================================================================= */ +static void +create_job_tables( GncSqlBackend* be ) +{ + gint version; + + g_return_if_fail( be != NULL ); + + version = gnc_sql_get_table_version( be, TABLE_NAME ); + if( version == 0 ) { + gnc_sql_create_table( be, TABLE_NAME, TABLE_VERSION, col_table ); + } +} + +/* ================================================================= */ +static void +save_job( GncSqlBackend* be, QofInstance* inst ) +{ + g_return_if_fail( inst != NULL ); + g_return_if_fail( GNC_IS_JOB(inst) ); + g_return_if_fail( be != NULL ); + + gnc_sql_commit_standard_item( be, inst, TABLE_NAME, GNC_ID_JOB, col_table ); +} + +/* ================================================================= */ +static gboolean +job_should_be_saved( GncJob *job ) +{ + const char *id; + + g_return_val_if_fail( job != NULL, FALSE ); + + /* make sure this is a valid job before we save it -- should have an ID */ + id = gncJobGetID( job ); + if( id == NULL || *id == '\0' ) { + return FALSE; + } + + return TRUE; +} + +static void +write_single_job( QofInstance *term_p, gpointer be_p ) +{ + GncSqlBackend* be = (GncSqlBackend*)be_p; + + g_return_if_fail( term_p != NULL ); + g_return_if_fail( GNC_IS_JOB(term_p) ); + g_return_if_fail( be_p != NULL ); + + if( job_should_be_saved( GNC_JOB(term_p) ) ) { + save_job( be, term_p ); + } +} + +static void +write_jobs( GncSqlBackend* be ) +{ + g_return_if_fail( be != NULL ); + + qof_object_foreach( GNC_ID_JOB, be->primary_book, write_single_job, (gpointer)be ); +} + +/* ================================================================= */ +void +gnc_job_sql_initialize( void ) +{ + static GncSqlObjectBackend be_data = + { + GNC_SQL_BACKEND_VERSION, + GNC_ID_JOB, + save_job, /* commit */ + load_all_jobs, /* initial_load */ + create_job_tables, /* create_tables */ + NULL, NULL, NULL, + write_jobs /* write */ + }; + + qof_object_register_backend( GNC_ID_JOB, GNC_SQL_BACKEND, &be_data ); +} +/* ========================== END OF FILE ===================== */ diff --git a/src/business/business-core/sql/gnc-job-sql.h b/src/business/business-core/sql/gnc-job-sql.h new file mode 100644 index 0000000000..68e8e6d812 --- /dev/null +++ b/src/business/business-core/sql/gnc-job-sql.h @@ -0,0 +1,35 @@ +/* + * gnc-job-sql.h -- job sql backend + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, contact: + * + * Free Software Foundation Voice: +1-617-542-5942 + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 + * Boston, MA 02110-1301, USA gnu@gnu.org + */ + +/** @file gnc-job-sql.h + * @brief load and save job data to SQL + * @author Copyright (c) 2007-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#ifndef GNC_JOB_SQL_H +#define GNC_JOB_SQL_H + +void gnc_job_sql_initialize( void ); + +#endif /* GNC_JOB_SQL_H */ diff --git a/src/business/business-core/sql/gnc-order-sql.c b/src/business/business-core/sql/gnc-order-sql.c new file mode 100644 index 0000000000..af25b1b9de --- /dev/null +++ b/src/business/business-core/sql/gnc-order-sql.c @@ -0,0 +1,244 @@ +/********************************************************************\ + * gnc-order-sql.c -- order sql backend * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * +\********************************************************************/ + +/** @file gnc-order-sql.c + * @brief load and save address data to SQL + * @author Copyright (c) 2007-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#include "config.h" + +#include +#include +#include + +#include "gnc-backend-sql.h" +#include "gnc-slots-sql.h" + +#include "gncOrderP.h" + +#include "gnc-order-sql.h" +#include "gnc-owner-sql.h" + +#define _GNC_MOD_NAME GNC_ID_ORDER + +static QofLogModule log_module = G_LOG_DOMAIN; + +#define TABLE_NAME "orders" +#define TABLE_VERSION 1 + +#define MAX_ID_LEN 2048 +#define MAX_NOTES_LEN 2048 +#define MAX_REFERENCE_LEN 2048 + +static GncSqlColumnTableEntry col_table[] = +{ + { "guid", CT_GUID, 0, COL_NNUL|COL_PKEY, "guid" }, + { "id", CT_STRING, MAX_ID_LEN, COL_NNUL, NULL, ORDER_ID }, + { "notes", CT_STRING, MAX_NOTES_LEN, COL_NNUL, NULL, ORDER_NOTES }, + { "reference", CT_STRING, MAX_REFERENCE_LEN, COL_NNUL, NULL, ORDER_REFERENCE }, + { "active", CT_BOOLEAN, 0, COL_NNUL, NULL, QOF_PARAM_ACTIVE }, + { "date_opened", CT_TIMESPEC, 0, COL_NNUL, NULL, ORDER_OPENED }, + { "date_closed", CT_TIMESPEC, 0, COL_NNUL, NULL, ORDER_CLOSED }, + { "owner", CT_OWNERREF, 0, COL_NNUL, NULL, ORDER_OWNER }, + { NULL }, +}; + +static GncOrder* +load_single_order( GncSqlBackend* be, GncSqlRow* row ) +{ + const GUID* guid; + GncOrder* pOrder; + + g_return_val_if_fail( be != NULL, NULL ); + g_return_val_if_fail( row != NULL, NULL ); + + guid = gnc_sql_load_guid( be, row ); + pOrder = gncOrderLookup( be->primary_book, guid ); + if( pOrder == NULL ) { + pOrder = gncOrderCreate( be->primary_book ); + } + gnc_sql_load_object( be, row, GNC_ID_ORDER, pOrder, col_table ); + qof_instance_mark_clean( QOF_INSTANCE(pOrder) ); + + return pOrder; +} + +static void +load_all_orders( GncSqlBackend* be ) +{ + GncSqlStatement* stmt; + GncSqlResult* result; + QofBook* pBook; + + g_return_if_fail( be != NULL ); + + pBook = be->primary_book; + + stmt = gnc_sql_create_select_statement( be, TABLE_NAME ); + result = gnc_sql_execute_select_statement( be, stmt ); + gnc_sql_statement_dispose( stmt ); + if( result != NULL ) { + GncSqlRow* row; + GList* list = NULL; + + row = gnc_sql_result_get_first_row( result ); + while( row != NULL ) { + GncOrder* pOrder = load_single_order( be, row ); + if( pOrder != NULL ) { + list = g_list_append( list, pOrder ); + } + row = gnc_sql_result_get_next_row( result ); + } + gnc_sql_result_dispose( result ); + + if( list != NULL ) { + gnc_sql_slots_load_for_list( be, list ); + } + } +} + +/* ================================================================= */ +static void +create_order_tables( GncSqlBackend* be ) +{ + gint version; + + g_return_if_fail( be != NULL ); + + version = gnc_sql_get_table_version( be, TABLE_NAME ); + if( version == 0 ) { + gnc_sql_create_table( be, TABLE_NAME, TABLE_VERSION, col_table ); + } +} + +/* ================================================================= */ +static void +save_order( GncSqlBackend* be, QofInstance* inst ) +{ + g_return_if_fail( inst != NULL ); + g_return_if_fail( GNC_IS_ORDER(inst) ); + g_return_if_fail( be != NULL ); + + gnc_sql_commit_standard_item( be, inst, TABLE_NAME, GNC_ID_ORDER, col_table ); +} + +/* ================================================================= */ +static gboolean +order_should_be_saved( GncOrder *order ) +{ + const char *id; + + g_return_val_if_fail( order != NULL, FALSE ); + + /* make sure this is a valid order before we save it -- should have an ID */ + id = gncOrderGetID( order ); + if( id == NULL || *id == '\0' ) { + return FALSE; + } + + return TRUE; +} + +static void +write_single_order( QofInstance *term_p, gpointer be_p ) +{ + GncSqlBackend* be = (GncSqlBackend*)be_p; + + g_return_if_fail( term_p != NULL ); + g_return_if_fail( GNC_IS_ORDER(term_p) ); + g_return_if_fail( be_p != NULL ); + + if( order_should_be_saved( GNC_ORDER(term_p) ) ) { + save_order( be, term_p ); + } +} + +static void +write_orders( GncSqlBackend* be ) +{ + g_return_if_fail( be != NULL ); + + qof_object_foreach( GNC_ID_ORDER, be->primary_book, write_single_order, (gpointer)be ); +} + +/* ================================================================= */ +static void +load_order_guid( const GncSqlBackend* be, GncSqlRow* row, + QofSetterFunc setter, gpointer pObject, + const GncSqlColumnTableEntry* table_row ) +{ + const GValue* val; + GUID guid; + const GUID* pGuid; + GncOrder* order = NULL; + + g_return_if_fail( be != NULL ); + g_return_if_fail( row != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + + val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name ); + if( val == NULL || !G_VALUE_HOLDS_STRING( val ) || g_value_get_string( val ) == NULL ) { + pGuid = NULL; + } else { + string_to_guid( g_value_get_string( val ), &guid ); + pGuid = &guid; + } + if( pGuid != NULL ) { + order = gncOrderLookup( be->primary_book, pGuid ); + } + if( table_row->gobj_param_name != NULL ) { + g_object_set( pObject, table_row->gobj_param_name, order, NULL ); + } else { + (*setter)( pObject, (const gpointer)order ); + } +} + +static col_type_handler_t order_guid_handler + = { load_order_guid, + gnc_sql_add_objectref_guid_col_info_to_list, + gnc_sql_add_colname_to_list, + gnc_sql_add_gvalue_objectref_guid_to_slist }; +/* ================================================================= */ +void +gnc_order_sql_initialize( void ) +{ + static GncSqlObjectBackend be_data = + { + GNC_SQL_BACKEND_VERSION, + GNC_ID_ORDER, + save_order, /* commit */ + load_all_orders, /* initial_load */ + create_order_tables, /* create_tables */ + NULL, NULL, NULL, + write_orders /* write */ + }; + + qof_object_register_backend( GNC_ID_ORDER, GNC_SQL_BACKEND, &be_data ); + + gnc_sql_register_col_type_handler( CT_ORDERREF, &order_guid_handler ); +} +/* ========================== END OF FILE ===================== */ diff --git a/src/business/business-core/sql/gnc-order-sql.h b/src/business/business-core/sql/gnc-order-sql.h new file mode 100644 index 0000000000..f9a50a1926 --- /dev/null +++ b/src/business/business-core/sql/gnc-order-sql.h @@ -0,0 +1,37 @@ +/* + * gnc-order-sql.h -- order sql backend + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, contact: + * + * Free Software Foundation Voice: +1-617-542-5942 + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 + * Boston, MA 02110-1301, USA gnu@gnu.org + */ + +/** @file gnc-order-sql.h + * @brief load and save order data to SQL + * @author Copyright (c) 2007-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#ifndef GNC_ORDER_SQL_H +#define GNC_ORDER_SQL_H + +#define CT_ORDERREF "order" + +void gnc_order_sql_initialize( void ); + +#endif /* GNC_ORDER_SQL_H */ diff --git a/src/business/business-core/sql/gnc-owner-sql.c b/src/business/business-core/sql/gnc-owner-sql.c new file mode 100644 index 0000000000..62816e8740 --- /dev/null +++ b/src/business/business-core/sql/gnc-owner-sql.c @@ -0,0 +1,284 @@ +/********************************************************************\ + * gnc-owner-sql.c -- owner sql implementation * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * +\********************************************************************/ + +/** @file gnc-owner-sql.c + * @brief load and save address data to SQL + * @author Copyright (c) 2007-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#include "config.h" + +#include +#include +#include + +#include "gnc-backend-sql.h" + +#include "gnc-owner-sql.h" +#include "gncCustomerP.h" +#include "gncJobP.h" +#include "gncEmployeeP.h" +#include "gncVendorP.h" + +static QofLogModule log_module = G_LOG_DOMAIN; + +typedef void (*OwnerSetterFunc)( gpointer, GncOwner* ); +typedef GncOwner* (*OwnerGetterFunc)( const gpointer ); + +static void +load_owner( const GncSqlBackend* be, GncSqlRow* row, + QofSetterFunc setter, gpointer pObject, + const GncSqlColumnTableEntry* table_row ) +{ + const GValue* val; + gchar* buf; + GncOwnerType type; + GUID guid; + QofBook* book; + GncOwner owner; + GUID* pGuid = NULL; + + g_return_if_fail( be != NULL ); + g_return_if_fail( row != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + + book = be->primary_book; + buf = g_strdup_printf( "%s_type", table_row->col_name ); + val = gnc_sql_row_get_value_at_col_name( row, buf ); + type = (GncOwnerType)g_value_get_int( val ); + g_free( buf ); + buf = g_strdup_printf( "%s_guid", table_row->col_name ); + val = gnc_sql_row_get_value_at_col_name( row, buf ); + g_free( buf ); + + if( val != NULL && G_VALUE_HOLDS_STRING( val ) && g_value_get_string( val ) != NULL ) { + string_to_guid( g_value_get_string( val ), &guid ); + pGuid = &guid; + } + + switch( type ) { + case GNC_OWNER_CUSTOMER: + { + GncCustomer *cust = NULL; + + if( pGuid != NULL ) { + cust = gncCustomerLookup( book, pGuid ); + if( cust == NULL ) { + cust = gncCustomerCreate( book ); + gncCustomerSetGUID( cust, &guid ); + } + } + gncOwnerInitCustomer( &owner, cust ); + break; + } + + case GNC_OWNER_JOB: + { + GncJob *job = NULL; + + if( pGuid != NULL ) { + job = gncJobLookup( book, pGuid ); + if( job == NULL ) { + job = gncJobCreate( book ); + gncJobSetGUID( job, &guid ); + } + } + gncOwnerInitJob( &owner, job ); + break; + } + + case GNC_OWNER_VENDOR: + { + GncVendor *vendor = NULL; + + if( pGuid != NULL ) { + vendor = gncVendorLookup( book, pGuid ); + if( vendor == NULL ) { + vendor = gncVendorCreate( book ); + gncVendorSetGUID( vendor, &guid ); + } + } + gncOwnerInitVendor( &owner, vendor ); + break; + } + + case GNC_OWNER_EMPLOYEE: + { + GncEmployee *employee = NULL; + + if( pGuid != NULL ) { + employee = gncEmployeeLookup( book, pGuid ); + if( employee == NULL ) { + employee = gncEmployeeCreate( book ); + gncEmployeeSetGUID( employee, &guid ); + } + } + gncOwnerInitEmployee( &owner, employee ); + break; + } + + default: + PWARN("Invalid owner type: %d\n", type ); + } + + if( table_row->gobj_param_name != NULL ) { + g_object_set( pObject, table_row->gobj_param_name, &owner, NULL ); + } else { + (*setter)( pObject, &owner ); + } +} + +static void +add_owner_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* table_row, + GList** pList ) +{ + GncSqlColumnInfo* info; + gchar* buf; + const gchar* type; + + g_return_if_fail( be != NULL ); + g_return_if_fail( table_row != NULL ); + g_return_if_fail( pList != NULL ); + + buf = g_strdup_printf( "%s_type", table_row->col_name ); + info = g_new0( GncSqlColumnInfo, 1 ); + info->name = buf; + info->type_name = gnc_sql_connection_get_column_type_name( be->conn, + G_TYPE_INT, table_row->size ); + info->is_primary_key = (table_row->flags & COL_PKEY) ? TRUE : FALSE; + info->null_allowed = (table_row->flags & COL_NNUL) ? FALSE : TRUE; + info->size = table_row->size; + *pList = g_list_append( *pList, info ); + + buf = g_strdup_printf( "%s_guid", table_row->col_name ); + info = g_new0( GncSqlColumnInfo, 1 ); + info->name = buf; + info->type_name = gnc_sql_connection_get_column_type_name( be->conn, + G_TYPE_STRING, GUID_ENCODING_LENGTH ); + info->size = GUID_ENCODING_LENGTH; + info->is_primary_key = (table_row->flags & COL_PKEY) ? TRUE : FALSE; + info->null_allowed = (table_row->flags & COL_NNUL) ? FALSE : TRUE; + *pList = g_list_append( *pList, info ); +} + +static void +add_colname_to_list( const GncSqlColumnTableEntry* table_row, GList** pList ) +{ + gchar* buf; + + buf = g_strdup_printf( "%s_type", table_row->col_name ); + (*pList) = g_list_append( (*pList), buf ); + buf = g_strdup_printf( "%s_guid", table_row->col_name ); + (*pList) = g_list_append( (*pList), buf ); +} + +static void +add_gvalue_owner_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name, + const gpointer pObject, const GncSqlColumnTableEntry* table_row, GSList** pList ) +{ + GValue* subfield_value; + GncOwner* owner; + gchar* buf; + const GUID* guid; + gchar guid_buf[GUID_ENCODING_LENGTH+1]; + GncOwnerType type; + QofInstance* inst = NULL; + OwnerGetterFunc getter; + + g_return_if_fail( be != NULL ); + g_return_if_fail( obj_name != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + + getter = (OwnerGetterFunc)gnc_sql_get_getter( obj_name, table_row ); + owner = (*getter)( pObject ); + + if( owner != NULL ) { + buf = g_strdup_printf( "%s_type", table_row->col_name ); + subfield_value = g_new0( GValue, 1 ); + g_value_init( subfield_value, G_TYPE_INT ); + type = gncOwnerGetType( owner ); + g_value_set_int( subfield_value, type ); + (*pList) = g_slist_append( (*pList), subfield_value ); + g_free( buf ); + + buf = g_strdup_printf( "%s_guid", table_row->col_name ); + subfield_value = g_new0( GValue, 1 ); + switch( type ) { + case GNC_OWNER_CUSTOMER: + inst = QOF_INSTANCE(gncOwnerGetCustomer( owner )); + break; + + case GNC_OWNER_JOB: + inst = QOF_INSTANCE(gncOwnerGetJob( owner )); + break; + + case GNC_OWNER_VENDOR: + inst = QOF_INSTANCE(gncOwnerGetVendor( owner )); + break; + + case GNC_OWNER_EMPLOYEE: + inst = QOF_INSTANCE(gncOwnerGetEmployee( owner )); + break; + + default: + PWARN("Invalid owner type: %d\n", type ); + } + g_value_init( subfield_value, G_TYPE_STRING ); + if( inst != NULL ) { + guid = qof_instance_get_guid( inst ); + if( guid != NULL ) { + (void)guid_to_string_buff( guid, guid_buf ); + g_value_take_string( subfield_value, g_strdup_printf( "%s", guid_buf ) ); + } + } + (*pList) = g_slist_append( (*pList), subfield_value ); + g_free( buf ); + } else { + subfield_value = g_new0( GValue, 1 ); + g_value_init( subfield_value, G_TYPE_STRING ); + g_value_set_string( subfield_value, "NULL" ); + (*pList) = g_slist_append( (*pList), subfield_value ); + subfield_value = g_new0( GValue, 1 ); + g_value_init( subfield_value, G_TYPE_STRING ); + g_value_set_string( subfield_value, "NULL" ); + (*pList) = g_slist_append( (*pList), subfield_value ); + } +} + +static col_type_handler_t owner_handler + = { load_owner, + add_owner_col_info_to_list, + add_colname_to_list, + add_gvalue_owner_to_slist }; + +/* ================================================================= */ +void +gnc_owner_sql_initialize( void ) +{ + gnc_sql_register_col_type_handler( CT_OWNERREF, &owner_handler ); +} +/* ========================== END OF FILE ===================== */ diff --git a/src/business/business-core/sql/gnc-owner-sql.h b/src/business/business-core/sql/gnc-owner-sql.h new file mode 100644 index 0000000000..baba0307eb --- /dev/null +++ b/src/business/business-core/sql/gnc-owner-sql.h @@ -0,0 +1,39 @@ +/* gnc-owner-sql.h -- Owner SQL header + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, contact: + * + * Free Software Foundation Voice: +1-617-542-5942 + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 + * Boston, MA 02110-1301, USA gnu@gnu.org + */ + +/** @file gnc-owner-sql.h + * @brief load and save owner data to SQL + * @author Copyright (c) 2007-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#ifndef GNC_OWNER_SQL_H +#define GNC_OWNER_SQL_H + +#include "gncOwner.h" +#include "qof.h" + +#define CT_OWNERREF "owner" + +void gnc_owner_sql_initialize( void ); + +#endif /* GNC_OWNER_SQL_H */ diff --git a/src/business/business-core/sql/gnc-tax-table-sql.c b/src/business/business-core/sql/gnc-tax-table-sql.c new file mode 100644 index 0000000000..a4783ad83a --- /dev/null +++ b/src/business/business-core/sql/gnc-tax-table-sql.c @@ -0,0 +1,414 @@ +/********************************************************************\ + * gnc-tax-table-sql.c -- tax table sql implementation * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * +\********************************************************************/ + +/** @file gnc-tax-table-sql.c + * @brief load and save address data to SQL + * @author Copyright (c) 2007-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#include "config.h" + +#include +#include +#include + +#include "gnc-backend-sql.h" +#include "gnc-slots-sql.h" + +#include "gncEntry.h" +#include "gncTaxTableP.h" + +#include "gnc-tax-table-sql.h" + +#define _GNC_MOD_NAME GNC_ID_TAXTABLE + +static QofLogModule log_module = G_LOG_DOMAIN; + +static void set_invisible( gpointer data, gboolean value ); + +typedef struct { + GncSqlBackend* be; + const GUID* guid; +} guid_info_t; + +static gpointer get_obj_guid( gpointer pObject, const QofParam* param ); +static void set_obj_guid( gpointer pObject, gpointer pValue ); +static gpointer get_child( gpointer pObject, const QofParam* param ); +static void set_parent( gpointer pObject, gpointer pValue ); + +#define MAX_NAME_LEN 50 + +#define TT_TABLE_NAME "taxtables" +#define TT_TABLE_VERSION 1 + +static GncSqlColumnTableEntry tt_col_table[] = +{ + { "guid", CT_GUID, 0, COL_NNUL|COL_PKEY, "guid" }, + { "name", CT_STRING, MAX_NAME_LEN, COL_NNUL, NULL, GNC_TT_NAME }, + { "refcount", CT_INT64, 0, COL_NNUL, NULL, GNC_TT_REFCOUNT }, + { "invisible", CT_BOOLEAN, 0, COL_NNUL, NULL, NULL, + (QofAccessFunc)gncTaxTableGetInvisible, (QofSetterFunc)set_invisible }, +/* { "child", CT_TAXTABLEREF, 0, 0, NULL, NULL, + get_child, (QofSetterFunc)gncTaxTableSetChild }, */ + { "parent", CT_TAXTABLEREF, 0, 0, NULL, NULL, + (QofAccessFunc)gncTaxTableGetParent, set_parent }, + { NULL } +}; + +#define TTENTRIES_TABLE_NAME "taxtable_entries" +#define TTENTRIES_TABLE_VERSION 1 + +static GncSqlColumnTableEntry ttentries_col_table[] = +{ + { "taxtable", CT_TAXTABLEREF, 0, COL_NNUL, NULL, NULL, + (QofAccessFunc)gncTaxTableEntryGetTable, set_obj_guid }, + { "account", CT_ACCOUNTREF, 0, COL_NNUL, NULL, NULL, + (QofAccessFunc)gncTaxTableEntryGetAccount, (QofSetterFunc)gncTaxTableEntrySetAccount }, + { "amount", CT_NUMERIC, 0, COL_NNUL, NULL, NULL, + (QofAccessFunc)gncTaxTableEntryGetAmount, (QofSetterFunc)gncTaxTableEntrySetAmount }, + { "type", CT_INT, 0, COL_NNUL, NULL, NULL, + (QofAccessFunc)gncTaxTableEntryGetType, (QofSetterFunc)gncTaxTableEntrySetType }, + { NULL } +}; + +/* Special column table because we need to be able to access the table by +a column other than the primary key */ +static GncSqlColumnTableEntry guid_col_table[] = +{ + { "taxtable", CT_GUID, 0, 0, NULL, NULL, get_obj_guid, set_obj_guid }, + { NULL } +}; + +static gpointer +get_obj_guid( gpointer pObject, const QofParam* param ) +{ + guid_info_t* pInfo = (guid_info_t*)pObject; + + g_return_val_if_fail( pInfo != NULL, NULL ); + + return (gpointer)pInfo->guid; +} + +static void +set_obj_guid( gpointer pObject, gpointer pValue ) +{ + // Nowhere to put the GUID +} + +static void +set_invisible( gpointer data, gboolean value ) +{ + GncTaxTable* tt = GNC_TAXTABLE(data); + + g_return_if_fail( data != NULL ); + g_return_if_fail( GNC_IS_TAXTABLE(data) ); + + if( value ) { + gncTaxTableMakeInvisible( tt ); + } +} + +static gpointer +get_child( gpointer pObject, const QofParam* param ) +{ + GncTaxTable* tt = GNC_TAXTABLE(pObject); + + g_return_val_if_fail( pObject != NULL, NULL ); + g_return_val_if_fail( GNC_IS_TAXTABLE(pObject), NULL ); + + return gncTaxTableGetChild( tt ); +} + +static void +set_parent( gpointer data, gpointer value ) +{ + GncTaxTable* tt = GNC_TAXTABLE(data); + GncTaxTable* parent; + + g_return_if_fail( data != NULL ); + g_return_if_fail( GNC_IS_TAXTABLE(data) ); + + if( value != NULL ) { + parent = GNC_TAXTABLE(value); + gncTaxTableSetParent( tt, parent ); + gncTaxTableSetChild( parent, tt ); + } +} + +static void +load_single_ttentry( GncSqlBackend* be, GncSqlRow* row, GncTaxTable* tt ) +{ + GncTaxTableEntry* e = gncTaxTableEntryCreate(); + + g_return_if_fail( be != NULL ); + g_return_if_fail( row!= NULL ); + g_return_if_fail( tt != NULL ); + + gnc_sql_load_object( be, row, GNC_ID_TAXTABLE, e, ttentries_col_table ); + gncTaxTableAddEntry( tt, e ); +} + +static void +load_taxtable_entries( GncSqlBackend* be, GncTaxTable* tt ) +{ + GncSqlResult* result; + gchar guid_buf[GUID_ENCODING_LENGTH+1]; + GValue value; + gchar* buf; + GncSqlStatement* stmt; + GError* error = NULL; + + g_return_if_fail( be != NULL ); + g_return_if_fail( tt != NULL ); + + guid_to_string_buff( qof_instance_get_guid( QOF_INSTANCE(tt) ), guid_buf ); + memset( &value, 0, sizeof( GValue ) ); + g_value_init( &value, G_TYPE_STRING ); + g_value_set_string( &value, guid_buf ); + buf = g_strdup_printf( "SELECT * FROM %s WHERE taxtable='%s'", TTENTRIES_TABLE_NAME, guid_buf ); + stmt = gnc_sql_connection_create_statement_from_sql( be->conn, buf ); + result = gnc_sql_execute_select_statement( be, stmt ); + gnc_sql_statement_dispose( stmt ); + if( result != NULL ) { + GncSqlRow* row; + + row = gnc_sql_result_get_first_row( result ); + while( row != NULL ) { + load_single_ttentry( be, row, tt ); + row = gnc_sql_result_get_next_row( result ); + } + gnc_sql_result_dispose( result ); + } +} + +static void +load_single_taxtable( GncSqlBackend* be, GncSqlRow* row ) +{ + const GUID* guid; + GncTaxTable* tt; + + g_return_if_fail( be != NULL ); + g_return_if_fail( row != NULL ); + + guid = gnc_sql_load_guid( be, row ); + tt = gncTaxTableLookup( be->primary_book, guid ); + if( tt == NULL ) { + tt = gncTaxTableCreate( be->primary_book ); + } + gnc_sql_load_object( be, row, GNC_ID_TAXTABLE, tt, tt_col_table ); + gnc_sql_slots_load( be, QOF_INSTANCE(tt) ); + load_taxtable_entries( be, tt ); + + qof_instance_mark_clean( QOF_INSTANCE(tt) ); +} + +static void +load_all_taxtables( GncSqlBackend* be ) +{ + GncSqlStatement* stmt; + GncSqlResult* result; + + g_return_if_fail( be != NULL ); + + /* First time, create the query */ + stmt = gnc_sql_create_select_statement( be, TT_TABLE_NAME ); + result = gnc_sql_execute_select_statement( be, stmt ); + gnc_sql_statement_dispose( stmt ); + if( result != NULL ) { + GncSqlRow* row; + + row = gnc_sql_result_get_first_row( result ); + while( row != NULL ) { + load_single_taxtable( be, row ); + row = gnc_sql_result_get_next_row( result ); + } + gnc_sql_result_dispose( result ); + } +} + +/* ================================================================= */ +static void +create_taxtable_tables( GncSqlBackend* be ) +{ + gint version; + + g_return_if_fail( be != NULL ); + + version = gnc_sql_get_table_version( be, TT_TABLE_NAME ); + if( version == 0 ) { + gnc_sql_create_table( be, TT_TABLE_NAME, TT_TABLE_VERSION, tt_col_table ); + } + + version = gnc_sql_get_table_version( be, TTENTRIES_TABLE_NAME ); + if( version == 0 ) { + gnc_sql_create_table( be, TTENTRIES_TABLE_NAME, TTENTRIES_TABLE_VERSION, ttentries_col_table ); + } +} + +/* ================================================================= */ +static void +delete_all_tt_entries( GncSqlBackend* be, const GUID* guid ) +{ + guid_info_t guid_info; + + g_return_if_fail( be != NULL ); + g_return_if_fail( guid != NULL ); + + guid_info.be = be; + guid_info.guid = guid; + (void)gnc_sql_do_db_operation( be, OP_DB_DELETE, TTENTRIES_TABLE_NAME, + TTENTRIES_TABLE_NAME, &guid_info, guid_col_table ); +} + +static void +save_tt_entries( GncSqlBackend* be, const GUID* guid, GList* entries ) +{ + GList* entry; + + g_return_if_fail( be != NULL ); + g_return_if_fail( guid != NULL ); + + /* First, delete the old entries for this object */ + delete_all_tt_entries( be, guid ); + + for( entry = entries; entry != NULL; entry = entry->next ) { + GncTaxTableEntry* e = (GncTaxTableEntry*)entry->data; + (void)gnc_sql_do_db_operation( be, + OP_DB_INSERT, + TTENTRIES_TABLE_NAME, + GNC_ID_TAXTABLE, e, + ttentries_col_table ); + } +} + +static void +save_taxtable( GncSqlBackend* be, QofInstance* inst ) +{ + GncTaxTable* tt; + const GUID* guid; + gint op; + gboolean is_infant; + + g_return_if_fail( inst != NULL ); + g_return_if_fail( GNC_IS_TAXTABLE(inst) ); + g_return_if_fail( be != NULL ); + + tt = GNC_TAXTABLE(inst); + + 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; + } + (void)gnc_sql_do_db_operation( be, op, TT_TABLE_NAME, GNC_ID_TAXTABLE, tt, tt_col_table ); + + // Now, commit or delete any slots and tax table entries + guid = qof_instance_get_guid( inst ); + if( !qof_instance_get_destroying(inst) ) { + gnc_sql_slots_save( be, guid, is_infant, qof_instance_get_slots( inst ) ); + save_tt_entries( be, guid, gncTaxTableGetEntries( tt ) ); + } else { + gnc_sql_slots_delete( be, guid ); + delete_all_tt_entries( be, guid ); + } +} + +/* ================================================================= */ +static void +save_next_taxtable( QofInstance* inst, gpointer p2 ) +{ + save_taxtable( (GncSqlBackend*)p2, inst ); +} + +static void +write_taxtables( GncSqlBackend* be ) +{ + g_return_if_fail( be != NULL ); + + qof_object_foreach( GNC_ID_TAXTABLE, be->primary_book, save_next_taxtable, be ); +} + +/* ================================================================= */ +static void +load_taxtable_guid( const GncSqlBackend* be, GncSqlRow* row, + QofSetterFunc setter, gpointer pObject, + const GncSqlColumnTableEntry* table_row ) +{ + const GValue* val; + GUID guid; + const GUID* pGuid; + GncTaxTable* taxtable = NULL; + + g_return_if_fail( be != NULL ); + g_return_if_fail( row != NULL ); + g_return_if_fail( pObject != NULL ); + g_return_if_fail( table_row != NULL ); + + val = gnc_sql_row_get_value_at_col_name( row, table_row->col_name ); + if( val == NULL || !G_VALUE_HOLDS_STRING( val ) || g_value_get_string( val ) == NULL ) { + pGuid = NULL; + } else { + string_to_guid( g_value_get_string( val ), &guid ); + pGuid = &guid; + } + if( pGuid != NULL ) { + taxtable = gncTaxTableLookup( be->primary_book, pGuid ); + } + if( table_row->gobj_param_name != NULL ) { + g_object_set( pObject, table_row->gobj_param_name, taxtable, NULL ); + } else { + (*setter)( pObject, (const gpointer)taxtable ); + } +} + +static col_type_handler_t taxtable_guid_handler + = { load_taxtable_guid, + gnc_sql_add_objectref_guid_col_info_to_list, + gnc_sql_add_colname_to_list, + gnc_sql_add_gvalue_objectref_guid_to_slist }; +/* ================================================================= */ +void +gnc_taxtable_sql_initialize( void ) +{ + static GncSqlObjectBackend be_data = + { + GNC_SQL_BACKEND_VERSION, + GNC_ID_TAXTABLE, + save_taxtable, /* commit */ + load_all_taxtables, /* initial_load */ + create_taxtable_tables, /* create_tables */ + NULL, NULL, NULL, + write_taxtables /* write */ + }; + + qof_object_register_backend( GNC_ID_TAXTABLE, GNC_SQL_BACKEND, &be_data ); + + gnc_sql_register_col_type_handler( CT_TAXTABLEREF, &taxtable_guid_handler ); +} +/* ========================== END OF FILE ===================== */ + diff --git a/src/business/business-core/sql/gnc-tax-table-sql.h b/src/business/business-core/sql/gnc-tax-table-sql.h new file mode 100644 index 0000000000..25c0ae68d9 --- /dev/null +++ b/src/business/business-core/sql/gnc-tax-table-sql.h @@ -0,0 +1,37 @@ +/* + * gnc-tax-table-sql.h -- tax table sql backend + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, contact: + * + * Free Software Foundation Voice: +1-617-542-5942 + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 + * Boston, MA 02110-1301, USA gnu@gnu.org + */ + +/** @file gnc-tax-table-sql.h + * @brief load and save tax table data to SQL + * @author Copyright (c) 2007-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#ifndef GNC_TAXTABLE_SQL_H +#define GNC_TAXTABLE_SQL_H + +#define CT_TAXTABLEREF "tax-table" + +void gnc_taxtable_sql_initialize( void ); + +#endif /* GNC_TAXTABLE_SQL_H */ diff --git a/src/business/business-core/sql/gnc-vendor-sql.c b/src/business/business-core/sql/gnc-vendor-sql.c new file mode 100644 index 0000000000..14269c25da --- /dev/null +++ b/src/business/business-core/sql/gnc-vendor-sql.c @@ -0,0 +1,244 @@ +/********************************************************************\ + * gnc-vendor-sql.c -- vendor sql backend * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * + * * +\********************************************************************/ + +/** @file gnc-vendor-sql.c + * @brief load and save address data to SQL + * @author Copyright (c) 2007-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#include "config.h" + +#include +#include +#include + +#include "gnc-commodity.h" + +#include "gnc-backend-sql.h" +#include "gnc-commodity-sql.h" +#include "gnc-slots-sql.h" + +#include "gnc-commodity.h" +#include "gncBillTermP.h" +#include "gncVendorP.h" +#include "gncTaxTableP.h" +#include "gnc-vendor-sql.h" +#include "gnc-address-sql.h" +#include "gnc-bill-term-sql.h" +#include "gnc-tax-table-sql.h" + +#define _GNC_MOD_NAME GNC_ID_VENDOR + +static QofLogModule log_module = G_LOG_DOMAIN; + +#define MAX_NAME_LEN 2048 +#define MAX_ID_LEN 2048 +#define MAX_NOTES_LEN 2048 +#define MAX_TAX_INC_LEN 2048 + +#define TABLE_NAME "vendors" +#define TABLE_VERSION 1 + +static GncSqlColumnTableEntry col_table[] = +{ + { "guid", CT_GUID, 0, COL_NNUL|COL_PKEY, "guid" }, + { "name", CT_STRING, MAX_NAME_LEN, COL_NNUL, NULL, VENDOR_NAME }, + { "id", CT_STRING, MAX_ID_LEN, COL_NNUL, NULL, VENDOR_ID }, + { "notes", CT_STRING, MAX_NOTES_LEN, COL_NNUL, NULL, VENDOR_NOTES }, + { "currency", CT_COMMODITYREF, 0, COL_NNUL, NULL, NULL, + (QofAccessFunc)gncVendorGetCurrency, (QofSetterFunc)gncVendorSetCurrency }, + { "active", CT_BOOLEAN, 0, COL_NNUL, NULL, NULL, + (QofAccessFunc)gncVendorGetActive, (QofSetterFunc)gncVendorSetActive }, + { "tax_override", CT_BOOLEAN, 0, COL_NNUL, NULL, VENDOR_TAX_OVERRIDE }, + { "addr", CT_ADDRESS, 0, 0, NULL, VENDOR_ADDR }, + { "terms", CT_BILLTERMREF, 0, 0, NULL, VENDOR_TERMS }, + { "tax_inc", CT_STRING, MAX_TAX_INC_LEN, 0, NULL, VENDOR_TAX_INC }, + { "tax_table", CT_TAXTABLEREF, 0, 0, NULL, VENDOR_TAX_TABLE }, + { NULL } +}; + +static GncVendor* +load_single_vendor( GncSqlBackend* be, GncSqlRow* row ) +{ + const GUID* guid; + GncVendor* pVendor; + + g_return_val_if_fail( be != NULL, NULL ); + g_return_val_if_fail( row != NULL, NULL ); + + guid = gnc_sql_load_guid( be, row ); + pVendor = gncVendorLookup( be->primary_book, guid ); + if( pVendor == NULL ) { + pVendor = gncVendorCreate( be->primary_book ); + } + gnc_sql_load_object( be, row, GNC_ID_VENDOR, pVendor, col_table ); + qof_instance_mark_clean( QOF_INSTANCE(pVendor) ); + + return pVendor; +} + +static void +load_all_vendors( GncSqlBackend* be ) +{ + GncSqlStatement* stmt; + GncSqlResult* result; + QofBook* pBook; + + g_return_if_fail( be != NULL ); + + pBook = be->primary_book; + + stmt = gnc_sql_create_select_statement( be, TABLE_NAME ); + result = gnc_sql_execute_select_statement( be, stmt ); + gnc_sql_statement_dispose( stmt ); + if( result != NULL ) { + GncSqlRow* row; + GList* list = NULL; + + row = gnc_sql_result_get_first_row( result ); + while( row != NULL ) { + GncVendor* pVendor = load_single_vendor( be, row ); + if( pVendor != NULL ) { + list = g_list_append( list, pVendor ); + } + row = gnc_sql_result_get_next_row( result ); + } + gnc_sql_result_dispose( result ); + + if( list != NULL ) { + gnc_sql_slots_load_for_list( be, list ); + } + } +} + +/* ================================================================= */ +static void +create_vendor_tables( GncSqlBackend* be ) +{ + gint version; + + g_return_if_fail( be != NULL ); + + version = gnc_sql_get_table_version( be, TABLE_NAME ); + if( version == 0 ) { + gnc_sql_create_table( be, TABLE_NAME, TABLE_VERSION, col_table ); + } +} + +/* ================================================================= */ +static void +save_vendor( GncSqlBackend* be, QofInstance* inst ) +{ + GncVendor* v; + const GUID* guid; + gint op; + gboolean is_infant; + + g_return_if_fail( inst != NULL ); + g_return_if_fail( GNC_IS_VENDOR(inst) ); + g_return_if_fail( be != NULL ); + + v = GNC_VENDOR(inst); + + 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 ) { + // Ensure the commodity is in the db + gnc_sql_save_commodity( be, gncVendorGetCurrency( v ) ); + } + (void)gnc_sql_do_db_operation( be, op, TABLE_NAME, GNC_ID_VENDOR, v, col_table ); + + // Now, commit or delete any slots + guid = qof_instance_get_guid( inst ); + if( !qof_instance_get_destroying(inst) ) { + gnc_sql_slots_save( be, guid, is_infant, qof_instance_get_slots( inst ) ); + } else { + gnc_sql_slots_delete( be, guid ); + } +} + +/* ================================================================= */ +static gboolean +vendor_should_be_saved( GncVendor *vendor ) +{ + const char *id; + + g_return_val_if_fail( vendor != NULL, FALSE ); + + /* make sure this is a valid vendor before we save it -- should have an ID */ + id = gncVendorGetID( vendor ); + if( id == NULL || *id == '\0' ) { + return FALSE; + } + + return TRUE; +} + +static void +write_single_vendor( QofInstance *term_p, gpointer be_p ) +{ + GncSqlBackend* be = (GncSqlBackend*)be_p; + + g_return_if_fail( term_p != NULL ); + g_return_if_fail( GNC_IS_VENDOR(term_p) ); + g_return_if_fail( be_p != NULL ); + + if( vendor_should_be_saved( GNC_VENDOR(term_p) ) ) { + save_vendor( be, term_p ); + } +} + +static void +write_vendors( GncSqlBackend* be ) +{ + g_return_if_fail( be != NULL ); + + qof_object_foreach( GNC_ID_VENDOR, be->primary_book, write_single_vendor, (gpointer)be ); +} + +/* ================================================================= */ +void +gnc_vendor_sql_initialize( void ) +{ + static GncSqlObjectBackend be_data = + { + GNC_SQL_BACKEND_VERSION, + GNC_ID_VENDOR, + save_vendor, /* commit */ + load_all_vendors, /* initial_load */ + create_vendor_tables, /* create_tables */ + NULL, NULL, NULL, + write_vendors /* write */ + }; + + qof_object_register_backend( GNC_ID_VENDOR, GNC_SQL_BACKEND, &be_data ); +} +/* ========================== END OF FILE ===================== */ diff --git a/src/business/business-core/sql/gnc-vendor-sql.h b/src/business/business-core/sql/gnc-vendor-sql.h new file mode 100644 index 0000000000..daf93ca6fd --- /dev/null +++ b/src/business/business-core/sql/gnc-vendor-sql.h @@ -0,0 +1,35 @@ +/* + * gnc-vendor-sql.h -- vendor sql backend + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, contact: + * + * Free Software Foundation Voice: +1-617-542-5942 + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 + * Boston, MA 02110-1301, USA gnu@gnu.org + */ + +/** @file gnc-vendor-sql.h + * @brief load and save vendor data to SQL + * @author Copyright (c) 2007-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#ifndef GNC_VENDOR_SQL_H +#define GNC_VENDOR_SQL_H + +void gnc_vendor_sql_initialize( void ); + +#endif /* GNC_VENDOR_SQL_H */ diff --git a/src/business/business-core/sql/gncmod-business-backend-sql.c b/src/business/business-core/sql/gncmod-business-backend-sql.c new file mode 100644 index 0000000000..03f43eda80 --- /dev/null +++ b/src/business/business-core/sql/gncmod-business-backend-sql.c @@ -0,0 +1,118 @@ +/********************************************************************* + * gncmod-business-backend-sql.c + * module definition/initialization for the sql backend module + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, contact: + * + * Free Software Foundation Voice: +1-617-542-5942 + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 + * Boston, MA 02110-1301, USA gnu@gnu.org + * + *********************************************************************/ + +/** @file gncmod-business-backend-sql.c + * @brief load and save address data to SQL + * @author Copyright (c) 2007-2008 Phil Longstaff + * + * This file implements the top-level QofBackend API for saving/ + * restoring data to/from an SQL database + */ + +#include "config.h" +#include + +#include "gnc-module.h" +#include "gnc-module-api.h" +#include "gnc-engine.h" + +#include "gnc-backend-sql.h" + +#include "gnc-address-sql.h" +#include "gnc-bill-term-sql.h" +#include "gnc-customer-sql.h" +#include "gnc-employee-sql.h" +#include "gnc-entry-sql.h" +#include "gnc-invoice-sql.h" +#include "gnc-job-sql.h" +#include "gnc-order-sql.h" +#include "gnc-owner-sql.h" +#include "gnc-tax-table-sql.h" +#include "gnc-vendor-sql.h" + +GNC_MODULE_API_DECL(libgncmod_business_backend_sql) + +/* version of the gnc module system interface we require */ +int libgncmod_business_backend_sql_gnc_module_system_interface = 0; + +/* module versioning uses libtool semantics. */ +int libgncmod_business_backend_sql_gnc_module_current = 0; +int libgncmod_business_backend_sql_gnc_module_revision = 0; +int libgncmod_business_backend_sql_gnc_module_age = 0; + +static GNCModule bus_core; + + +gchar * +libgncmod_business_backend_sql_gnc_module_path(void) +{ + return g_strdup( "gnucash/business-core-sql" ); +} + +gchar * +libgncmod_business_backend_sql_gnc_module_description(void) +{ + return g_strdup( "The SQL backend for GnuCash business objects" ); +} + +int +libgncmod_business_backend_sql_gnc_module_init(int refcount) +{ + if(!gnc_engine_is_initialized()) { return FALSE; } + + bus_core = gnc_module_load( "gnucash/business-core", 0 ); + if( !bus_core ) return FALSE; + + if( refcount == 0 ) { + /* Initialize our pointers into the backend subsystem */ + gnc_address_sql_initialize(); + gnc_billterm_sql_initialize(); + gnc_customer_sql_initialize(); + gnc_employee_sql_initialize(); + gnc_entry_sql_initialize(); + gnc_invoice_sql_initialize(); + gnc_job_sql_initialize(); + gnc_order_sql_initialize(); + gnc_owner_sql_initialize(); + gnc_taxtable_sql_initialize(); + gnc_vendor_sql_initialize(); + } + + return TRUE; +} + +int +libgncmod_business_backend_sql_gnc_module_end(int refcount) +{ + int unload = TRUE; + + if( bus_core ) { + unload = gnc_module_unload( bus_core ); + } + + if( refcount == 0 ) { + bus_core = NULL; + } + + return unload; +} diff --git a/src/business/business-gnome/business-gnome.scm b/src/business/business-gnome/business-gnome.scm index b8abe6cf12..ad48a177c6 100644 --- a/src/business/business-gnome/business-gnome.scm +++ b/src/business/business-gnome/business-gnome.scm @@ -5,7 +5,8 @@ (gnc:module-load "gnucash/business-core" 0) (gnc:module-load "gnucash/business-utils" 0) (gnc:module-load "gnucash/gnome-search" 0) -(gnc:module-load "gnucash/business-core-file" 0) +(gnc:module-load "gnucash/business-core-xml" 0) +(gnc:module-load "gnucash/business-core-sql" 0) (gnc:module-load "gnucash/dialog-tax-table" 0) (gnc:module-load "gnucash/report/report-gnome" 0) diff --git a/src/business/business-gnome/dialog-billterms.c b/src/business/business-gnome/dialog-billterms.c index 49046d5451..6923c94306 100644 --- a/src/business/business-gnome/dialog-billterms.c +++ b/src/business/business-gnome/dialog-billterms.c @@ -161,7 +161,7 @@ set_numeric (GtkWidget *widget, GncBillTerm *term, static void get_numeric (GtkWidget *widget, GncBillTerm *term, - gnc_numeric (*func)(GncBillTerm *)) + gnc_numeric (*func)(const GncBillTerm *)) { gnc_numeric val; gdouble fl; @@ -183,7 +183,7 @@ set_int (GtkWidget *widget, GncBillTerm *term, static void get_int (GtkWidget *widget, GncBillTerm *term, - gint (*func)(GncBillTerm *)) + gint (*func)(const GncBillTerm *)) { gint val; diff --git a/src/engine/Account.c b/src/engine/Account.c index 273a8a3955..3207baca79 100644 --- a/src/engine/Account.c +++ b/src/engine/Account.c @@ -78,6 +78,9 @@ enum { PROP_TAX_RELATED, PROP_TAX_CODE, PROP_TAX_SOURCE, + + PROP_HIDDEN, + PROP_PLACEHOLDER, }; typedef struct AccountPrivate @@ -347,6 +350,12 @@ gnc_account_get_property (GObject *object, g_value_set_string(value, xaccAccountGetTaxUSPayerNameSource(account)); break; + case PROP_HIDDEN: + g_value_set_boolean(value, xaccAccountGetHidden(account)); + break; + case PROP_PLACEHOLDER: + g_value_set_boolean(value, xaccAccountGetPlaceholder(account)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -389,6 +398,9 @@ gnc_account_set_property (GObject *object, case PROP_COMMODITY_SCU: xaccAccountSetCommoditySCU(account, g_value_get_int(value)); break; + case PROP_NON_STD_SCU: + xaccAccountSetNonStdSCU(account, g_value_get_boolean(value)); + break; case PROP_SORT_DIRTY: gnc_account_set_sort_dirty(account); break; @@ -422,6 +434,12 @@ gnc_account_set_property (GObject *object, case PROP_TAX_SOURCE: xaccAccountSetTaxUSPayerNameSource(account, g_value_get_string(value)); + case PROP_HIDDEN: + xaccAccountSetHidden(account, g_value_get_boolean(value)); + break; + case PROP_PLACEHOLDER: + xaccAccountSetPlaceholder(account, g_value_get_boolean(value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -547,7 +565,7 @@ gnc_account_class_init (AccountClass *klass) PROP_NON_STD_SCU, g_param_spec_boolean ("non-std-scu", "Non-std SCU", - "TRUE id the account SCU doesn't match " + "TRUE if the account SCU doesn't match " "the commodity SCU. This indicates a case " "where the two were accidentally set to " "mismatched values in older versions of " @@ -718,6 +736,16 @@ gnc_account_class_init (AccountClass *klass) "This is an unknown tax related field.", NULL, G_PARAM_READWRITE)); + + g_object_class_install_property + (gobject_class, + PROP_HIDDEN, + g_param_spec_boolean ("hidden", + "Hidden", + "Whether the account should be hidden in the " + "account tree.", + FALSE, + G_PARAM_READWRITE)); } static void diff --git a/src/engine/SX-book.c b/src/engine/SX-book.c index 95b2baf537..a7d0f793fa 100644 --- a/src/engine/SX-book.c +++ b/src/engine/SX-book.c @@ -61,7 +61,7 @@ gnc_collection_get_template_root( const QofCollection *col ) } Account * -gnc_book_get_template_root( QofBook *book ) +gnc_book_get_template_root( const QofBook *book ) { QofCollection *col; if (!book) return NULL; @@ -113,12 +113,12 @@ sxtg_book_begin (QofBook *book) { Account *root; - root = xaccMallocAccount(book); - xaccAccountBeginEdit(root); - xaccAccountSetType(root, ACCT_TYPE_ROOT); - xaccAccountCommitEdit(root); - gnc_book_set_template_root (book, root); -} + root = xaccMallocAccount(book); + xaccAccountBeginEdit(root); + xaccAccountSetType(root, ACCT_TYPE_ROOT); + xaccAccountCommitEdit(root); + gnc_book_set_template_root (book, root); + } static void sxtg_book_end (QofBook *book) diff --git a/src/engine/SX-book.h b/src/engine/SX-book.h index 6b4508271d..9219ad1fdc 100644 --- a/src/engine/SX-book.h +++ b/src/engine/SX-book.h @@ -80,7 +80,7 @@ void gnc_sxes_add_sx(SchedXactions* sxes, SchedXaction* sx); void gnc_sxes_del_sx(SchedXactions* sxes, SchedXaction* sx); /** Returns the template group from the book. **/ -Account *gnc_book_get_template_root(QofBook *book); +Account *gnc_book_get_template_root(const QofBook *book); /** @return The list of SXes which reference the given Account. Caller should free this list. **/ GList* gnc_sx_get_sxes_referencing_account(QofBook *book, Account *acct); diff --git a/src/engine/SchedXaction.c b/src/engine/SchedXaction.c index 520faecb1f..8da9465321 100644 --- a/src/engine/SchedXaction.c +++ b/src/engine/SchedXaction.c @@ -88,13 +88,14 @@ xaccSchedXactionInit(SchedXaction *sx, QofBook *book) /* create a new template account for our splits */ sx->template_acct = xaccMallocAccount(book); guid = qof_instance_get_guid( sx ); + xaccAccountBeginEdit( sx->template_acct ); xaccAccountSetName( sx->template_acct, guid_to_string( guid )); xaccAccountSetCommodity (sx->template_acct, - gnc_commodity_new( book, - "template", "template", - "template", "template", 1 ) ); + gnc_commodity_table_lookup( gnc_commodity_table_get_table(book), + "template", "template") ); xaccAccountSetType( sx->template_acct, ACCT_TYPE_BANK ); + xaccAccountCommitEdit( sx->template_acct ); ra = gnc_book_get_template_root( book ); gnc_account_append_child( ra, sx->template_acct ); } diff --git a/src/engine/Split.c b/src/engine/Split.c index f4912521ed..64b6a16c86 100644 --- a/src/engine/Split.c +++ b/src/engine/Split.c @@ -1527,6 +1527,15 @@ xaccSplitGetLot (const Split *split) return split ? split->lot : NULL; } +void +xaccSplitSetLot(Split* split, GNCLot* lot) +{ + xaccTransBeginEdit (split->parent); + split->lot = lot; + qof_instance_set_dirty(QOF_INSTANCE(split)); + xaccTransCommitEdit(split->parent); +} + const char * xaccSplitGetMemo (const Split *split) { diff --git a/src/engine/Split.h b/src/engine/Split.h index a079b718f0..01cc97bc75 100644 --- a/src/engine/Split.h +++ b/src/engine/Split.h @@ -130,6 +130,9 @@ void xaccSplitSetParent (Split *split, Transaction *trans); * belongs to, or NULL if it doesn't belong to any. */ GNCLot * xaccSplitGetLot (const Split *split); +/** Assigns the split to a specific Lot */ +void xaccSplitSetLot(Split* split, GNCLot* lot); + /** Returns the KvpFrame slots of this split for direct editing. * diff --git a/src/engine/gnc-book.h b/src/engine/gnc-book.h index 0bd40d401b..3b0eaa0ae2 100644 --- a/src/engine/gnc-book.h +++ b/src/engine/gnc-book.h @@ -12,6 +12,3 @@ #define gnc_book_get_backend qof_book_get_backend #define gnc_book_get_entity_table qof_book_get_entity_table #define gnc_book_get_counter qof_book_get_counter -#define gnc_book_x qof_book_x -#define gnc_book_x qof_book_x -#define gnc_book_x qof_book_x diff --git a/src/engine/gnc-budget.c b/src/engine/gnc-budget.c index 9684fbad22..5a610e02c2 100644 --- a/src/engine/gnc-budget.c +++ b/src/engine/gnc-budget.c @@ -39,36 +39,194 @@ static QofLogModule log_module = GNC_MOD_ENGINE; -struct gnc_budget_private{ - QofInstance inst; - - gchar* name; - gchar* description; - Recurrence recurrence; - guint num_periods; +enum { + PROP_0, + PROP_NAME, + PROP_DESCRIPTION, + PROP_NUM_PERIODS, + PROP_RECURRENCE, }; +struct budget_s +{ + QofInstance inst; +}; + +typedef struct { + QofInstanceClass parent_class; +} BudgetClass; + +typedef struct BudgetPrivate { + /* The name is an arbitrary string assigned by the user. */ + gchar* name; + + /* The description is an arbitrary string assigned by the user. */ + gchar* description; + + /* Recurrence (period info) for the budget */ + Recurrence recurrence; + + /* Number of periods */ + guint num_periods; +} BudgetPrivate; + +#define GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), GNC_TYPE_BUDGET, BudgetPrivate)) + struct _GncBudgetClass { QofInstanceClass parent_class; }; /* GObject Initialization */ -QOF_GOBJECT_IMPL(gnc_budget, GncBudget, QOF_TYPE_INSTANCE); +G_DEFINE_TYPE(GncBudget, gnc_budget, QOF_TYPE_INSTANCE) static void gnc_budget_init(GncBudget* budget) { + BudgetPrivate* priv; + GDate date; + + priv = GET_PRIVATE(budget); + priv->name = CACHE_INSERT(_("Unnamed Budget")); + priv->description = CACHE_INSERT(""); + + priv->num_periods = 12; + g_date_set_time_t(&date, time(NULL)); + g_date_subtract_days(&date, g_date_get_day(&date)-1); + recurrenceSet(&priv->recurrence, 1, PERIOD_MONTH, &date); } static void -gnc_budget_dispose_real (GObject *budgetp) +gnc_budget_dispose (GObject *budgetp) { + G_OBJECT_CLASS(gnc_budget_parent_class)->dispose(budgetp); } static void -gnc_budget_finalize_real(GObject* budgetp) +gnc_budget_finalize(GObject* budgetp) { + G_OBJECT_CLASS(gnc_budget_parent_class)->finalize(budgetp); +} + +static void +gnc_budget_get_property( GObject* object, + guint prop_id, + GValue* value, + GParamSpec* pspec) +{ + GncBudget* budget; + BudgetPrivate* priv; + + g_return_if_fail(GNC_IS_BUDGET(object)); + + budget = GNC_BUDGET(object); + priv = GET_PRIVATE(budget); + switch( prop_id ) { + case PROP_NAME: + g_value_set_string(value, priv->name); + break; + case PROP_DESCRIPTION: + g_value_set_string(value, priv->description); + break; + case PROP_NUM_PERIODS: + g_value_set_uint(value, priv->num_periods); + break; + case PROP_RECURRENCE: + /* TODO: Make this a BOXED type */ + g_value_set_pointer(value, &priv->recurrence); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +gnc_budget_set_property( GObject* object, + guint prop_id, + const GValue* value, + GParamSpec* pspec) +{ + GncBudget* budget; + + g_return_if_fail(GNC_IS_BUDGET(object)); + + budget = GNC_BUDGET(object); + switch( prop_id ) { + case PROP_NAME: + gnc_budget_set_name(budget, g_value_get_string(value)); + break; + case PROP_DESCRIPTION: + gnc_budget_set_description(budget, g_value_get_string(value)); + break; + case PROP_NUM_PERIODS: + gnc_budget_set_num_periods(budget, g_value_get_uint(value)); + break; + case PROP_RECURRENCE: + gnc_budget_set_recurrence(budget, g_value_get_pointer(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +gnc_budget_class_init(GncBudgetClass* klass) +{ + GObjectClass* gobject_class = G_OBJECT_CLASS(klass); + + gobject_class->dispose = gnc_budget_dispose; + gobject_class->finalize = gnc_budget_finalize; + gobject_class->get_property = gnc_budget_get_property; + gobject_class->set_property = gnc_budget_set_property; + + g_type_class_add_private(klass, sizeof(BudgetPrivate)); + + g_object_class_install_property( + gobject_class, + PROP_NAME, + g_param_spec_string( "name", + "Budget Name", + "The name is an arbitrary string " + "assigned by the user. It is intended " + "to be a short, 5 to 30 character long string " + "that is displayed by the GUI as the " + "budget mnemonic", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property( + gobject_class, + PROP_DESCRIPTION, + g_param_spec_string( "description", + "Budget Description", + "The description is an arbitrary string " + "assigned by the user. It is intended " + "to be a longer, 1-5 sentence description of " + "what the budget is all about.", + NULL, + G_PARAM_READWRITE)); + + g_object_class_install_property( + gobject_class, + PROP_NUM_PERIODS, + g_param_spec_uint( "num-periods", + "Number of Periods", + "The number of periods for this budget.", + 0, + G_MAXUINT32, + 12, + G_PARAM_READWRITE)); + + g_object_class_install_property( + gobject_class, + PROP_RECURRENCE, + g_param_spec_pointer( "recurrence", + "Budget Recurrence", + "about.", + G_PARAM_READWRITE)); } static void commit_err (QofInstance *inst, QofBackendError errcode) @@ -79,19 +237,23 @@ static void commit_err (QofInstance *inst, QofBackendError errcode) static void gnc_budget_free(QofInstance *inst) { - GncBudget *budget = GNC_BUDGET(inst); - if (budget == NULL) - return; + GncBudget *budget; + BudgetPrivate* priv; - g_return_if_fail(GNC_IS_BUDGET(budget)); + if (inst == NULL) + return; + g_return_if_fail(GNC_IS_BUDGET(inst)); + + budget = GNC_BUDGET(inst); + priv = GET_PRIVATE(budget); /* We first send the message that this object is about to be * destroyed so that any GUI elements can remove it before it is * actually gone. */ qof_event_gen( &budget->inst, QOF_EVENT_DESTROY, NULL); - CACHE_REMOVE(budget->name); - CACHE_REMOVE(budget->description); + CACHE_REMOVE(priv->name); + CACHE_REMOVE(priv->description); /* qof_instance_release (&budget->inst); */ g_object_unref(budget); @@ -117,23 +279,13 @@ GncBudget* gnc_budget_new(QofBook *book) { GncBudget* budget; - GDate date; + g_return_val_if_fail(book, NULL); ENTER(" "); budget = g_object_new(GNC_TYPE_BUDGET, NULL); qof_instance_init_data (&budget->inst, GNC_ID_BUDGET, book); - g_date_set_time_t(&date, time(NULL)); - g_date_subtract_days(&date, g_date_get_day(&date)-1); - recurrenceSet(&budget->recurrence, 1, PERIOD_MONTH, &date); - - gnc_budget_begin_edit(budget); - gnc_budget_set_name(budget, _("Unnamed Budget")); - gnc_budget_set_description(budget, ""); - gnc_budget_set_num_periods(budget, 12); - gnc_budget_commit_edit(budget); - qof_event_gen( &budget->inst, QOF_EVENT_CREATE , NULL); LEAVE(" "); @@ -153,10 +305,15 @@ gnc_budget_destroy(GncBudget *budget) void gnc_budget_set_name(GncBudget* budget, const gchar* name) { + BudgetPrivate* priv; + g_return_if_fail(GNC_IS_BUDGET(budget) && name); + priv = GET_PRIVATE(budget); + if( name == priv->name ) return; + gnc_budget_begin_edit(budget); - CACHE_REPLACE(budget->name, name); + CACHE_REPLACE(priv->name, name); qof_instance_set_dirty(&budget->inst); gnc_budget_commit_edit(budget); @@ -167,17 +324,21 @@ const gchar* gnc_budget_get_name(GncBudget* budget) { g_return_val_if_fail(GNC_IS_BUDGET(budget), NULL); - return budget->name; + return GET_PRIVATE(budget)->name; } void gnc_budget_set_description(GncBudget* budget, const gchar* description) { + BudgetPrivate* priv; + g_return_if_fail(GNC_IS_BUDGET(budget)); g_return_if_fail(description); + priv = GET_PRIVATE(budget); + if( description == priv->description ) return; gnc_budget_begin_edit(budget); - CACHE_REPLACE(budget->description, description); + CACHE_REPLACE(priv->description, description); qof_instance_set_dirty(&budget->inst); gnc_budget_commit_edit(budget); @@ -188,15 +349,19 @@ const gchar* gnc_budget_get_description(GncBudget* budget) { g_return_val_if_fail(GNC_IS_BUDGET(budget), NULL); - return budget->description; + return GET_PRIVATE(budget)->description; } void gnc_budget_set_recurrence(GncBudget *budget, const Recurrence *r) { + BudgetPrivate* priv; + g_return_if_fail(budget && r); + priv = GET_PRIVATE(budget); + gnc_budget_begin_edit(budget); - budget->recurrence = *r; + priv->recurrence = *r; qof_instance_set_dirty(&budget->inst); gnc_budget_commit_edit(budget); @@ -207,7 +372,7 @@ const Recurrence * gnc_budget_get_recurrence(GncBudget *budget) { g_return_val_if_fail(budget, NULL); - return (&budget->recurrence); + return (&GET_PRIVATE(budget)->recurrence); } const GUID* @@ -221,10 +386,15 @@ gnc_budget_get_guid(GncBudget* budget) void gnc_budget_set_num_periods(GncBudget* budget, guint num_periods) { + BudgetPrivate* priv; + g_return_if_fail(GNC_IS_BUDGET(budget)); + priv = GET_PRIVATE(budget); + if( priv->num_periods == num_periods ) return; + gnc_budget_begin_edit(budget); - budget->num_periods = num_periods; + priv->num_periods = num_periods; qof_instance_set_dirty(&budget->inst); gnc_budget_commit_edit(budget); @@ -235,7 +405,7 @@ guint gnc_budget_get_num_periods(GncBudget* budget) { g_return_val_if_fail(GNC_IS_BUDGET(budget), 0); - return budget->num_periods; + return GET_PRIVATE(budget)->num_periods; } #define BUF_SIZE (10 + GUID_ENCODING_LENGTH + \ @@ -370,7 +540,7 @@ gnc_budget_get_period_start_date(GncBudget *budget, guint period_num) { Timespec ts; timespecFromTime_t( - &ts, recurrenceGetPeriodTime(&budget->recurrence, period_num, FALSE)); + &ts, recurrenceGetPeriodTime(&GET_PRIVATE(budget)->recurrence, period_num, FALSE)); return ts; } @@ -380,7 +550,7 @@ gnc_budget_get_account_period_actual_value( { // FIXME: maybe zero is not best error return val. g_return_val_if_fail(GNC_IS_BUDGET(budget) && acc, gnc_numeric_zero()); - return recurrenceGetAccountPeriodValue(&budget->recurrence, + return recurrenceGetAccountPeriodValue(&GET_PRIVATE(budget)->recurrence, acc, period_num); } @@ -441,11 +611,11 @@ static QofObject budget_object_def = /* Static wrapper getters for the recurrence params */ static PeriodType gnc_budget_get_rec_pt(const GncBudget *bgt) -{ return recurrenceGetPeriodType(&(bgt->recurrence)); } +{ return recurrenceGetPeriodType(&(GET_PRIVATE(bgt)->recurrence)); } static guint gnc_budget_get_rec_mult(const GncBudget *bgt) -{ return recurrenceGetMultiplier(&(bgt->recurrence)); } +{ return recurrenceGetMultiplier(&(GET_PRIVATE(bgt)->recurrence)); } static GDate gnc_budget_get_rec_date(const GncBudget *bgt) -{ return recurrenceGetDate(&(bgt->recurrence)); } +{ return recurrenceGetDate(&(GET_PRIVATE(bgt)->recurrence)); } /* Register ourselves with the engine. */ gboolean gnc_budget_register (void) diff --git a/src/engine/gnc-budget.h b/src/engine/gnc-budget.h index c9292ff32c..6ce362bfff 100644 --- a/src/engine/gnc-budget.h +++ b/src/engine/gnc-budget.h @@ -67,7 +67,7 @@ #include /** The budget data.*/ -typedef struct gnc_budget_private GncBudget; +typedef struct budget_s GncBudget; typedef struct _GncBudgetClass GncBudgetClass; #include "qof.h" diff --git a/src/engine/gnc-commodity.c b/src/engine/gnc-commodity.c index 5b90d2c38c..99b1ba724a 100644 --- a/src/engine/gnc-commodity.c +++ b/src/engine/gnc-commodity.c @@ -44,10 +44,27 @@ static QofLogModule log_module = GNC_MOD_COMMODITY; * of the smallest-transactional-units of the currency are there * in a 'unitname' unit. */ +enum { + PROP_0, + PROP_NAMESPACE, + PROP_FULL_NAME, + PROP_MNEMONIC, + PROP_PRINTNAME, + PROP_CUSIP, + PROP_FRACTION, + PROP_UNIQUE_NAME, + PROP_QUOTE_FLAG, + PROP_QUOTE_SOURCE, + PROP_QUOTE_TZ, +}; + struct gnc_commodity_s { QofInstance inst; +}; +typedef struct CommodityPrivate +{ gnc_commodity_namespace *namespace; char * fullname; @@ -65,13 +82,18 @@ struct gnc_commodity_s /* the number of accounts using this commodity - this field is not * persisted */ int usage_count; -}; +} CommodityPrivate; + +#define GET_PRIVATE(o) \ + (G_TYPE_INSTANCE_GET_PRIVATE((o), GNC_TYPE_COMMODITY, CommodityPrivate)) struct _GncCommodityClass { QofInstanceClass parent_class; }; +static void commodity_free(gnc_commodity * cm); + struct gnc_commodity_namespace_s { QofInstance inst; @@ -498,11 +520,17 @@ static void commit_err (QofInstance *inst, QofBackendError errcode) static void noop (QofInstance *inst) {} +static void +comm_free(QofInstance* inst) +{ + commodity_free( GNC_COMMODITY(inst) ); +} + void gnc_commodity_commit_edit (gnc_commodity *cm) { if (!qof_commit_edit (QOF_INSTANCE(cm))) return; - qof_commit_edit_part2 (&cm->inst, commit_err, noop, noop); + qof_commit_edit_part2 (&cm->inst, commit_err, noop, comm_free); } /******************************************************************** @@ -517,42 +545,245 @@ mark_commodity_dirty (gnc_commodity *cm) } static void -reset_printname(gnc_commodity *com) +reset_printname(CommodityPrivate *priv) { - g_free(com->printname); - com->printname = g_strdup_printf("%s (%s)", - com->mnemonic ? com->mnemonic : "", - com->fullname ? com->fullname : ""); + g_free(priv->printname); + priv->printname = g_strdup_printf("%s (%s)", + priv->mnemonic ? priv->mnemonic : "", + priv->fullname ? priv->fullname : ""); } static void -reset_unique_name(gnc_commodity *com) +reset_unique_name(CommodityPrivate *priv) { gnc_commodity_namespace *ns; - g_free(com->unique_name); - ns = com->namespace; - com->unique_name = g_strdup_printf("%s::%s", + g_free(priv->unique_name); + ns = priv->namespace; + priv->unique_name = g_strdup_printf("%s::%s", ns ? ns->name : "", - com->mnemonic ? com->mnemonic : ""); + priv->mnemonic ? priv->mnemonic : ""); } /* GObject Initialization */ -QOF_GOBJECT_IMPL(gnc_commodity, gnc_commodity, QOF_TYPE_INSTANCE); +G_DEFINE_TYPE(gnc_commodity, gnc_commodity, QOF_TYPE_INSTANCE); static void gnc_commodity_init(gnc_commodity* com) { + CommodityPrivate* priv; + + priv = GET_PRIVATE(com); + + priv->namespace = NULL; + priv->fullname = CACHE_INSERT(""); + priv->mnemonic = CACHE_INSERT(""); + priv->cusip = CACHE_INSERT(""); + priv->fraction = 10000; + priv->mark = 0; + priv->quote_flag = 0; + priv->quote_source = NULL; + priv->quote_tz = CACHE_INSERT(""); + + reset_printname(priv); + reset_unique_name(priv); } static void -gnc_commodity_dispose_real (GObject *nsp) +gnc_commodity_dispose(GObject *comp) { + G_OBJECT_CLASS(gnc_commodity_parent_class)->dispose(comp); } static void -gnc_commodity_finalize_real(GObject* comp) +gnc_commodity_finalize(GObject* comp) { + G_OBJECT_CLASS(gnc_commodity_parent_class)->finalize(comp); +} + +static void +gnc_commodity_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + gnc_commodity *commodity; + CommodityPrivate* priv; + + g_return_if_fail(GNC_IS_COMMODITY(object)); + + commodity = GNC_COMMODITY(object); + priv = GET_PRIVATE(commodity); + switch (prop_id) { + case PROP_NAMESPACE: + g_value_set_object(value, priv->namespace); + break; + case PROP_FULL_NAME: + g_value_set_string(value, priv->fullname); + break; + case PROP_MNEMONIC: + g_value_set_string(value, priv->mnemonic); + break; + case PROP_PRINTNAME: + g_value_set_string(value, priv->printname); + break; + case PROP_CUSIP: + g_value_set_string(value, priv->cusip); + break; + case PROP_FRACTION: + g_value_set_int(value, priv->fraction); + break; + case PROP_UNIQUE_NAME: + g_value_set_string(value, priv->unique_name); + break; + case PROP_QUOTE_FLAG: + g_value_set_boolean(value, priv->quote_flag); + break; + case PROP_QUOTE_SOURCE: + g_value_set_pointer(value, priv->quote_source); + break; + case PROP_QUOTE_TZ: + g_value_set_string(value, priv->quote_tz); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} + +static void +gnc_commodity_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + gnc_commodity *commodity; + gnc_numeric *number; + + g_return_if_fail(GNC_IS_COMMODITY(object)); + + commodity = GNC_COMMODITY(object); + + switch (prop_id) { + case PROP_NAMESPACE: + gnc_commodity_set_namespace(commodity, g_value_get_object(value)); + break; + case PROP_FULL_NAME: + gnc_commodity_set_fullname(commodity, g_value_get_string(value)); + break; + case PROP_MNEMONIC: + gnc_commodity_set_mnemonic(commodity, g_value_get_string(value)); + break; + case PROP_CUSIP: + gnc_commodity_set_cusip(commodity, g_value_get_string(value)); + break; + case PROP_FRACTION: + gnc_commodity_set_fraction(commodity, g_value_get_int(value)); + break; + case PROP_QUOTE_FLAG: + gnc_commodity_set_quote_flag(commodity, g_value_get_boolean(value)); + break; + case PROP_QUOTE_SOURCE: + gnc_commodity_set_quote_source(commodity, g_value_get_pointer(value)); + break; + case PROP_QUOTE_TZ: + gnc_commodity_set_quote_tz(commodity, g_value_get_string(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } +} +static void +gnc_commodity_class_init(struct _GncCommodityClass* klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + + gobject_class->dispose = gnc_commodity_dispose; + gobject_class->finalize = gnc_commodity_finalize; + gobject_class->set_property = gnc_commodity_set_property; + gobject_class->get_property = gnc_commodity_get_property; + + g_type_class_add_private(klass, sizeof(CommodityPrivate)); + + g_object_class_install_property(gobject_class, + PROP_NAMESPACE, + g_param_spec_object ("namespace", + "Namespace", + "The namespace field denotes the " + "namespace for this commodity, either " + "a currency or symbol from a quote source.", + GNC_TYPE_COMMODITY_NAMESPACE, + G_PARAM_READWRITE)); + g_object_class_install_property(gobject_class, + PROP_FULL_NAME, + g_param_spec_string ("fullname", + "Full Commodity Name", + "The fullname is the official full name of" + "the currency.", + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property(gobject_class, + PROP_MNEMONIC, + g_param_spec_string ("mnemonic", + "Commodity Mnemonic", + "The mnemonic is the official abbreviated" + "designation for the currency.", + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property(gobject_class, + PROP_PRINTNAME, + g_param_spec_string ("printname", + "Commodity Print Name", + "Printable form of the commodity name.", + NULL, + G_PARAM_READABLE)); + g_object_class_install_property(gobject_class, + PROP_CUSIP, + g_param_spec_string ("cusip", + "Commodity CUSIP Code", + "?????", + NULL, + G_PARAM_READWRITE)); + g_object_class_install_property(gobject_class, + PROP_FRACTION, + g_param_spec_int ("fraction", + "Fraction", + "The fraction is the number of sub-units that " + "the basic commodity can be divided into.", + 1, + 1000000, + 1, + G_PARAM_READWRITE)); + g_object_class_install_property(gobject_class, + PROP_UNIQUE_NAME, + g_param_spec_string ("unique-name", + "Commodity Unique Name", + "Unique form of the commodity name which combines " + "the namespace name and the commodity name.", + NULL, + G_PARAM_READABLE)); + g_object_class_install_property(gobject_class, + PROP_QUOTE_FLAG, + g_param_spec_boolean ("quote_flag", + "Quote Flag", + "TRUE if prices are to be downloaded for this " + "commodity from a quote source.", + FALSE, + G_PARAM_READWRITE)); + g_object_class_install_property(gobject_class, + PROP_QUOTE_SOURCE, + g_param_spec_pointer("quote-source", + "Quote Source", + "The quote source from which prices are downloaded.", + G_PARAM_READWRITE)); + g_object_class_install_property(gobject_class, + PROP_QUOTE_TZ, + g_param_spec_string ("quote-tz", + "Commodity Quote Timezone", + "?????", + NULL, + G_PARAM_READWRITE)); } gnc_commodity * @@ -564,28 +795,21 @@ gnc_commodity_new(QofBook *book, const char * fullname, gnc_commodity_table *table; qof_instance_init_data (&retval->inst, GNC_ID_COMMODITY, book); - table = gnc_commodity_table_get_table(book); - if (namespace) { - retval->namespace = gnc_commodity_table_find_namespace(table, namespace); - if (!retval->namespace) - retval->namespace = gnc_commodity_table_add_namespace(table, namespace, book); - } else { - retval->namespace = NULL; - } - - retval->fullname = CACHE_INSERT(fullname); - retval->mnemonic = CACHE_INSERT(mnemonic); - retval->cusip = CACHE_INSERT(cusip); - retval->fraction = fraction; - retval->mark = 0; - retval->quote_flag = 0; - retval->quote_source = NULL; - retval->quote_tz = CACHE_INSERT(""); + gnc_commodity_begin_edit(retval); + + if( namespace != NULL ) { + gnc_commodity_set_namespace(retval, namespace); + if (gnc_commodity_namespace_is_iso(namespace)) { + gnc_commodity_set_quote_source(retval, + gnc_quote_source_lookup_by_internal("currency") ); + } + } + gnc_commodity_set_fullname(retval, fullname); + gnc_commodity_set_mnemonic(retval, mnemonic); + gnc_commodity_set_cusip(retval, cusip); + gnc_commodity_set_fraction(retval, fraction); + gnc_commodity_commit_edit(retval); - reset_printname(retval); - reset_unique_name(retval); - if (gnc_commodity_namespace_is_iso(namespace)) - retval->quote_source = gnc_quote_source_lookup_by_internal("currency"); qof_event_gen (&retval->inst, QOF_EVENT_CREATE, NULL); return retval; @@ -596,46 +820,49 @@ gnc_commodity_new(QofBook *book, const char * fullname, * gnc_commodity_destroy ********************************************************************/ -void -gnc_commodity_destroy(gnc_commodity * cm) +static void +commodity_free(gnc_commodity * cm) { QofBook *book; gnc_commodity_table *table; + CommodityPrivate* priv; + if(!cm) return; book = qof_instance_get_book(&cm->inst); table = gnc_commodity_table_get_table(book); gnc_commodity_table_remove(table, cm); + priv = GET_PRIVATE(cm); qof_event_gen (&cm->inst, QOF_EVENT_DESTROY, NULL); /* Set at creation */ - CACHE_REMOVE (cm->fullname); - CACHE_REMOVE (cm->cusip); - CACHE_REMOVE (cm->mnemonic); - CACHE_REMOVE (cm->quote_tz); - cm->namespace = NULL; + CACHE_REMOVE (priv->fullname); + CACHE_REMOVE (priv->cusip); + CACHE_REMOVE (priv->mnemonic); + CACHE_REMOVE (priv->quote_tz); + priv->namespace = NULL; /* Set through accessor functions */ - cm->quote_source = NULL; + priv->quote_source = NULL; /* Automatically generated */ - g_free(cm->printname); - cm->printname = NULL; + g_free(priv->printname); + priv->printname = NULL; - g_free(cm->unique_name); - cm->unique_name = NULL; + g_free(priv->unique_name); + priv->unique_name = NULL; - cm->mark = 0; + priv->mark = 0; #ifdef ACCOUNTS_CLEANED_UP /* Account objects are not actually cleaned up when a book is closed (in fact * a memory leak), but commodities are, so in currently this warning gets hit * quite frequently. Disable the check until cleaning up of accounts objects * on close is implemented. */ - if(cm->usage_count != 0) { + if(priv->usage_count != 0) { PWARN("Destroying commodity (%p) with non-zero usage_count (%d).", cm, - cm->usage_count); + priv->usage_count); } #endif @@ -643,16 +870,29 @@ gnc_commodity_destroy(gnc_commodity * cm) g_object_unref(cm); } +void +gnc_commodity_destroy(gnc_commodity * cm) +{ + gnc_commodity_begin_edit(cm); + qof_instance_set_destroying(cm, TRUE); + gnc_commodity_commit_edit(cm); +} + void gnc_commodity_copy(gnc_commodity * dest, const gnc_commodity *src) { - gnc_commodity_set_fullname (dest, src->fullname); - dest->namespace = src->namespace; - gnc_commodity_set_fraction (dest, src->fraction); - gnc_commodity_set_cusip (dest, src->cusip); - gnc_commodity_set_quote_flag (dest, src->quote_flag); + CommodityPrivate* src_priv = GET_PRIVATE(src); + CommodityPrivate* dest_priv = GET_PRIVATE(dest); + + gnc_commodity_set_fullname (dest, src_priv->fullname); + dest_priv->namespace = src_priv->namespace; + gnc_commodity_set_fraction (dest, src_priv->fraction); + gnc_commodity_set_cusip (dest, src_priv->cusip); + gnc_commodity_set_quote_flag (dest, src_priv->quote_flag); gnc_commodity_set_quote_source (dest, gnc_commodity_get_quote_source (src)); - gnc_commodity_set_quote_tz (dest, src->quote_tz); + gnc_commodity_set_quote_tz (dest, src_priv->quote_tz); + kvp_frame_delete (dest->inst.kvp_data); + dest->inst.kvp_data = kvp_frame_copy (src->inst.kvp_data); kvp_frame_delete (dest->inst.kvp_data); dest->inst.kvp_data = kvp_frame_copy (src->inst.kvp_data); } @@ -660,27 +900,32 @@ gnc_commodity_copy(gnc_commodity * dest, const gnc_commodity *src) gnc_commodity * gnc_commodity_clone(const gnc_commodity *src, QofBook *dest_book) { + CommodityPrivate* src_priv; + CommodityPrivate* dest_priv; + gnc_commodity * dest = g_object_new(GNC_TYPE_COMMODITY, NULL); qof_instance_init_data (&dest->inst, GNC_ID_COMMODITY, dest_book); + src_priv = GET_PRIVATE(src); + dest_priv = GET_PRIVATE(dest); - dest->fullname = CACHE_INSERT(src->fullname); - dest->mnemonic = CACHE_INSERT(src->mnemonic); - dest->cusip = CACHE_INSERT(src->cusip); - dest->quote_tz = CACHE_INSERT(src->quote_tz); + dest_priv->fullname = CACHE_INSERT(src_priv->fullname); + dest_priv->mnemonic = CACHE_INSERT(src_priv->mnemonic); + dest_priv->cusip = CACHE_INSERT(src_priv->cusip); + dest_priv->quote_tz = CACHE_INSERT(src_priv->quote_tz); - dest->namespace = src->namespace; + dest_priv->namespace = src_priv->namespace; - dest->mark = 0; - dest->fraction = src->fraction; - dest->quote_flag = src->quote_flag; + dest_priv->mark = 0; + dest_priv->fraction = src_priv->fraction; + dest_priv->quote_flag = src_priv->quote_flag; gnc_commodity_set_quote_source (dest, gnc_commodity_get_quote_source (src)); kvp_frame_delete (dest->inst.kvp_data); dest->inst.kvp_data = kvp_frame_copy (src->inst.kvp_data); - reset_printname(dest); - reset_unique_name(dest); + reset_printname(dest_priv); + reset_unique_name(dest_priv); return dest; } @@ -693,7 +938,7 @@ const char * gnc_commodity_get_mnemonic(const gnc_commodity * cm) { if(!cm) return NULL; - return cm->mnemonic; + return GET_PRIVATE(cm)->mnemonic; } /******************************************************************** @@ -704,7 +949,7 @@ const char * gnc_commodity_get_printname(const gnc_commodity * cm) { if(!cm) return NULL; - return cm->printname; + return GET_PRIVATE(cm)->printname; } @@ -716,25 +961,29 @@ const char * gnc_commodity_get_namespace(const gnc_commodity * cm) { if(!cm) return NULL; - return gnc_commodity_namespace_get_name(cm->namespace); + return gnc_commodity_namespace_get_name(GET_PRIVATE(cm)->namespace); } const char * gnc_commodity_get_namespace_compat(const gnc_commodity * cm) { - if (!cm || !cm->namespace) return NULL; - if (cm->namespace->iso4217) { + CommodityPrivate* priv; + + if (!cm) return NULL; + priv = GET_PRIVATE(cm); + if(!priv->namespace) return NULL; + if (priv->namespace->iso4217) { /* Data files are still written with ISO4217. */ return GNC_COMMODITY_NS_ISO; } - return gnc_commodity_namespace_get_name(cm->namespace); + return gnc_commodity_namespace_get_name(priv->namespace); } gnc_commodity_namespace * gnc_commodity_get_namespace_ds(const gnc_commodity * cm) { if(!cm) return NULL; - return cm->namespace; + return GET_PRIVATE(cm)->namespace; } /******************************************************************** @@ -745,7 +994,7 @@ const char * gnc_commodity_get_fullname(const gnc_commodity * cm) { if(!cm) return NULL; - return cm->fullname; + return GET_PRIVATE(cm)->fullname; } @@ -757,7 +1006,7 @@ const char * gnc_commodity_get_unique_name(const gnc_commodity * cm) { if(!cm) return NULL; - return cm->unique_name; + return GET_PRIVATE(cm)->unique_name; } @@ -769,7 +1018,7 @@ const char * gnc_commodity_get_cusip(const gnc_commodity * cm) { if(!cm) return NULL; - return cm->cusip; + return GET_PRIVATE(cm)->cusip; } /******************************************************************** @@ -780,7 +1029,7 @@ int gnc_commodity_get_fraction(const gnc_commodity * cm) { if(!cm) return 0; - return cm->fraction; + return GET_PRIVATE(cm)->fraction; } /******************************************************************** @@ -791,7 +1040,7 @@ gint16 gnc_commodity_get_mark(const gnc_commodity * cm) { if(!cm) return 0; - return cm->mark; + return GET_PRIVATE(cm)->mark; } /******************************************************************** @@ -817,7 +1066,7 @@ gboolean gnc_commodity_get_quote_flag(const gnc_commodity *cm) { if(!cm) return FALSE; - return (cm->quote_flag); + return (GET_PRIVATE(cm)->quote_flag); } /******************************************************************** @@ -827,10 +1076,13 @@ gnc_commodity_get_quote_flag(const gnc_commodity *cm) gnc_quote_source* gnc_commodity_get_quote_source(const gnc_commodity *cm) { + CommodityPrivate* priv; + if(!cm) return NULL; - if (!cm->quote_source && gnc_commodity_is_iso(cm)) + priv = GET_PRIVATE(cm); + if (!priv->quote_source && gnc_commodity_is_iso(cm)) return ¤cy_quote_source; - return cm->quote_source; + return priv->quote_source; } gnc_quote_source* @@ -850,7 +1102,7 @@ const char* gnc_commodity_get_quote_tz(const gnc_commodity *cm) { if(!cm) return NULL; - return cm->quote_tz; + return GET_PRIVATE(cm)->quote_tz; } /******************************************************************** @@ -860,16 +1112,19 @@ gnc_commodity_get_quote_tz(const gnc_commodity *cm) void gnc_commodity_set_mnemonic(gnc_commodity * cm, const char * mnemonic) { + CommodityPrivate* priv; + if(!cm) return; - if(cm->mnemonic == mnemonic) return; + priv = GET_PRIVATE(cm); + if(priv->mnemonic == mnemonic) return; gnc_commodity_begin_edit(cm); - CACHE_REMOVE (cm->mnemonic); - cm->mnemonic = CACHE_INSERT(mnemonic); + CACHE_REMOVE (priv->mnemonic); + priv->mnemonic = CACHE_INSERT(mnemonic); mark_commodity_dirty (cm); - reset_printname(cm); - reset_unique_name(cm); + reset_printname(priv); + reset_unique_name(priv); gnc_commodity_commit_edit(cm); } @@ -883,21 +1138,23 @@ gnc_commodity_set_namespace(gnc_commodity * cm, const char * namespace) QofBook *book; gnc_commodity_table *table; gnc_commodity_namespace *nsp; + CommodityPrivate* priv; if(!cm) return; + priv = GET_PRIVATE(cm); book = qof_instance_get_book (&cm->inst); table = gnc_commodity_table_get_table(book); nsp = gnc_commodity_table_add_namespace(table, namespace, book); - if (cm->namespace == nsp) + if (priv->namespace == nsp) return; gnc_commodity_begin_edit(cm); - cm->namespace = nsp; + priv->namespace = nsp; if (nsp->iso4217) - cm->quote_source = gnc_quote_source_lookup_by_internal("currency"); + priv->quote_source = gnc_quote_source_lookup_by_internal("currency"); mark_commodity_dirty(cm); - reset_printname(cm); - reset_unique_name(cm); + reset_printname(priv); + reset_unique_name(priv); gnc_commodity_commit_edit(cm); } @@ -908,15 +1165,18 @@ gnc_commodity_set_namespace(gnc_commodity * cm, const char * namespace) void gnc_commodity_set_fullname(gnc_commodity * cm, const char * fullname) { - if(!cm) return; - if(cm->fullname == fullname) return; + CommodityPrivate* priv; - CACHE_REMOVE (cm->fullname); - cm->fullname = CACHE_INSERT (fullname); + if(!cm) return; + priv = GET_PRIVATE(cm); + if(priv->fullname == fullname) return; + + CACHE_REMOVE (priv->fullname); + priv->fullname = CACHE_INSERT (fullname); gnc_commodity_begin_edit(cm); mark_commodity_dirty(cm); - reset_printname(cm); + reset_printname(priv); gnc_commodity_commit_edit(cm); } @@ -928,12 +1188,16 @@ void gnc_commodity_set_cusip(gnc_commodity * cm, const char * cusip) { + CommodityPrivate* priv; + if(!cm) return; - if(cm->cusip == cusip) return; + + priv = GET_PRIVATE(cm); + if(priv->cusip == cusip) return; gnc_commodity_begin_edit(cm); - CACHE_REMOVE (cm->cusip); - cm->cusip = CACHE_INSERT (cusip); + CACHE_REMOVE (priv->cusip); + priv->cusip = CACHE_INSERT (cusip); mark_commodity_dirty(cm); gnc_commodity_commit_edit(cm); } @@ -947,7 +1211,7 @@ gnc_commodity_set_fraction(gnc_commodity * cm, int fraction) { if(!cm) return; gnc_commodity_begin_edit(cm); - cm->fraction = fraction; + GET_PRIVATE(cm)->fraction = fraction; mark_commodity_dirty(cm); gnc_commodity_commit_edit(cm); } @@ -960,7 +1224,7 @@ void gnc_commodity_set_mark(gnc_commodity * cm, gint16 mark) { if(!cm) return; - cm->mark = mark; + GET_PRIVATE(cm)->mark = mark; } /******************************************************************** @@ -993,6 +1257,8 @@ gnc_commodity_set_auto_quote_control_flag(gnc_commodity *cm, void gnc_commodity_user_set_quote_flag(gnc_commodity *cm, const gboolean flag) { + CommodityPrivate* priv; + ENTER ("(cm=%p, flag=%d)", cm, flag); if(!cm) { @@ -1000,6 +1266,7 @@ gnc_commodity_user_set_quote_flag(gnc_commodity *cm, const gboolean flag) return; } + priv = GET_PRIVATE(cm); gnc_commodity_begin_edit(cm); gnc_commodity_set_quote_flag(cm, flag); if(gnc_commodity_is_iso(cm)) { @@ -1011,7 +1278,7 @@ gnc_commodity_user_set_quote_flag(gnc_commodity *cm, const gboolean flag) * accounts using this currency OR flag is TRUE and there are accounts * using this currency; otherwise disable auto quote control */ gnc_commodity_set_auto_quote_control_flag(cm, - (!flag && (cm->usage_count == 0)) || (flag && (cm->usage_count != 0))); + (!flag && (priv->usage_count == 0)) || (flag && (priv->usage_count != 0))); } gnc_commodity_commit_edit(cm); LEAVE(""); @@ -1028,7 +1295,7 @@ gnc_commodity_set_quote_flag(gnc_commodity *cm, const gboolean flag) if(!cm) return; gnc_commodity_begin_edit(cm); - cm->quote_flag = flag; + GET_PRIVATE(cm)->quote_flag = flag; mark_commodity_dirty(cm); gnc_commodity_commit_edit(cm); LEAVE(" "); @@ -1045,7 +1312,7 @@ gnc_commodity_set_quote_source(gnc_commodity *cm, gnc_quote_source *src) if(!cm) return; gnc_commodity_begin_edit(cm); - cm->quote_source = src; + GET_PRIVATE(cm)->quote_source = src; mark_commodity_dirty(cm); gnc_commodity_commit_edit(cm); LEAVE(" "); @@ -1058,13 +1325,19 @@ gnc_commodity_set_quote_source(gnc_commodity *cm, gnc_quote_source *src) void gnc_commodity_set_quote_tz(gnc_commodity *cm, const char *tz) { + CommodityPrivate* priv; + ENTER ("(cm=%p, tz=%s)", cm, tz ? tz : "(null)"); - if(!cm || tz == cm->quote_tz) return; + if(!cm) return; + + priv = GET_PRIVATE(cm); + + if(tz == priv->quote_tz) return; gnc_commodity_begin_edit(cm); - CACHE_REMOVE (cm->quote_tz); - cm->quote_tz = CACHE_INSERT (tz); + CACHE_REMOVE (priv->quote_tz); + priv->quote_tz = CACHE_INSERT (tz); mark_commodity_dirty(cm); gnc_commodity_commit_edit(cm); LEAVE(" "); @@ -1078,6 +1351,7 @@ void gnc_commodity_increment_usage_count(gnc_commodity *cm) { const char *str; + CommodityPrivate* priv; ENTER("(cm=%p)", cm); @@ -1086,7 +1360,9 @@ gnc_commodity_increment_usage_count(gnc_commodity *cm) return; } - if((cm->usage_count == 0) && !cm->quote_flag + priv = GET_PRIVATE(cm); + + if((priv->usage_count == 0) && !priv->quote_flag && gnc_commodity_get_auto_quote_control_flag(cm) && gnc_commodity_is_iso(cm)) { /* compatability hack - Gnucash 1.8 gets currency quotes when a @@ -1097,8 +1373,8 @@ gnc_commodity_increment_usage_count(gnc_commodity *cm) gnc_commodity_get_default_quote_source(cm)); gnc_commodity_commit_edit(cm); } - cm->usage_count++; - LEAVE("(usage_count=%d)", cm->usage_count); + priv->usage_count++; + LEAVE("(usage_count=%d)", priv->usage_count); } /******************************************************************** @@ -1109,6 +1385,7 @@ void gnc_commodity_decrement_usage_count(gnc_commodity *cm) { const char *str; + CommodityPrivate* priv; ENTER("(cm=%p)", cm); @@ -1117,21 +1394,23 @@ gnc_commodity_decrement_usage_count(gnc_commodity *cm) return; } - if(cm->usage_count == 0) { + priv = GET_PRIVATE(cm); + + if(priv->usage_count == 0) { PWARN("usage_count already zero"); LEAVE(""); return; } - cm->usage_count--; - if((cm->usage_count == 0) && cm->quote_flag + priv->usage_count--; + if((priv->usage_count == 0) && priv->quote_flag && gnc_commodity_get_auto_quote_control_flag(cm) && gnc_commodity_is_iso(cm)) { /* if this is a currency with auto quote control enabled and no more * accounts reference this currency, disable quote retrieval */ gnc_commodity_set_quote_flag(cm, FALSE); } - LEAVE("(usage_count=%d)", cm->usage_count); + LEAVE("(usage_count=%d)", priv->usage_count); } /********************************************************************\ @@ -1146,16 +1425,25 @@ gnc_commodity_decrement_usage_count(gnc_commodity *cm) gboolean gnc_commodity_equiv(const gnc_commodity * a, const gnc_commodity * b) { + CommodityPrivate* priv_a; + CommodityPrivate* priv_b; + if(a == b) return TRUE; if(!a || !b) return FALSE; - if(a->namespace != b->namespace) return FALSE; - if(safe_strcmp(a->mnemonic, b->mnemonic) != 0) return FALSE; + + priv_a = GET_PRIVATE(a); + priv_b = GET_PRIVATE(b); + if(priv_a->namespace != priv_b->namespace) return FALSE; + if(safe_strcmp(priv_a->mnemonic, priv_b->mnemonic) != 0) return FALSE; return TRUE; } gboolean gnc_commodity_equal(const gnc_commodity * a, const gnc_commodity * b) { + CommodityPrivate* priv_a; + CommodityPrivate* priv_b; + if (a == b) return TRUE; if (!a || !b) @@ -1164,35 +1452,38 @@ gnc_commodity_equal(const gnc_commodity * a, const gnc_commodity * b) return FALSE; } - if (a->namespace != b->namespace) + priv_a = GET_PRIVATE(a); + priv_b = GET_PRIVATE(b); + + if (priv_a->namespace != priv_b->namespace) { DEBUG ("namespaces differ: %p(%s) vs %p(%s)", - a->namespace, gnc_commodity_namespace_get_name(a->namespace), - b->namespace, gnc_commodity_namespace_get_name(b->namespace)); + priv_a->namespace, gnc_commodity_namespace_get_name(priv_a->namespace), + priv_b->namespace, gnc_commodity_namespace_get_name(priv_b->namespace)); return FALSE; } - if (safe_strcmp(a->mnemonic, b->mnemonic) != 0) + if (safe_strcmp(priv_a->mnemonic, priv_b->mnemonic) != 0) { - DEBUG ("mnemonics differ: %s vs %s", a->mnemonic, b->mnemonic); + DEBUG ("mnemonics differ: %s vs %s", priv_a->mnemonic, priv_b->mnemonic); return FALSE; } - if (safe_strcmp(a->fullname, b->fullname) != 0) + if (safe_strcmp(priv_a->fullname, priv_b->fullname) != 0) { - DEBUG ("fullnames differ: %s vs %s", a->fullname, b->fullname); + DEBUG ("fullnames differ: %s vs %s", priv_a->fullname, priv_b->fullname); return FALSE; } - if (safe_strcmp(a->cusip, b->cusip) != 0) + if (safe_strcmp(priv_a->cusip, priv_b->cusip) != 0) { - DEBUG ("cusips differ: %s vs %s", a->cusip, b->cusip); + DEBUG ("cusips differ: %s vs %s", priv_a->cusip, priv_b->cusip); return FALSE; } - if (a->fraction != b->fraction) + if (priv_a->fraction != priv_b->fraction) { - DEBUG ("fractions differ: %d vs %d", a->fraction, b->fraction); + DEBUG ("fractions differ: %d vs %d", priv_a->fraction, priv_b->fraction); return FALSE; } @@ -1435,16 +1726,19 @@ gnc_commodity_table_insert(gnc_commodity_table * table, gnc_commodity_namespace * nsp = NULL; gnc_commodity *c; const char *ns_name; + CommodityPrivate* priv; QofBook *book; if (!table) return NULL; if (!comm) return NULL; + priv = GET_PRIVATE(comm); + ENTER ("(table=%p, comm=%p) %s %s", table, comm, - (comm->mnemonic == NULL ? "(null)" : comm->mnemonic), - (comm->fullname == NULL ? "(null)" : comm->fullname)); - ns_name = gnc_commodity_namespace_get_name(comm->namespace); - c = gnc_commodity_table_lookup (table, ns_name, comm->mnemonic); + (priv->mnemonic == NULL ? "(null)" : priv->mnemonic), + (priv->fullname == NULL ? "(null)" : priv->fullname)); + ns_name = gnc_commodity_namespace_get_name(priv->namespace); + c = gnc_commodity_table_lookup (table, ns_name, priv->mnemonic); if (c) { @@ -1462,10 +1756,10 @@ gnc_commodity_table_insert(gnc_commodity_table * table, book = qof_instance_get_book (&comm->inst); nsp = gnc_commodity_table_add_namespace(table, ns_name, book); - PINFO ("insert %p %s into nsp=%p %s", comm->mnemonic, comm->mnemonic, + PINFO ("insert %p %s into nsp=%p %s", priv->mnemonic, priv->mnemonic, nsp->cm_table, nsp->name); g_hash_table_insert(nsp->cm_table, - CACHE_INSERT(comm->mnemonic), + CACHE_INSERT(priv->mnemonic), (gpointer)comm); nsp->cm_list = g_list_append(nsp->cm_list, comm); @@ -1485,13 +1779,15 @@ gnc_commodity_table_remove(gnc_commodity_table * table, { gnc_commodity_namespace * nsp; gnc_commodity *c; + CommodityPrivate* priv; const char *ns_name; if (!table) return; if (!comm) return; - ns_name = gnc_commodity_namespace_get_name(comm->namespace); - c = gnc_commodity_table_lookup (table, ns_name, comm->mnemonic); + priv = GET_PRIVATE(comm); + ns_name = gnc_commodity_namespace_get_name(priv->namespace); + c = gnc_commodity_table_lookup (table, ns_name, priv->mnemonic); if (c != comm) return; qof_event_gen (&comm->inst, QOF_EVENT_REMOVE, NULL); @@ -1500,7 +1796,7 @@ gnc_commodity_table_remove(gnc_commodity_table * table, if (!nsp) return; nsp->cm_list = g_list_remove(nsp->cm_list, comm); - g_hash_table_remove (nsp->cm_table, comm->mnemonic); + g_hash_table_remove (nsp->cm_table, priv->mnemonic); /* XXX minor mem leak, should remove the key as well */ } @@ -1582,8 +1878,13 @@ gnc_commodity_table_get_namespaces_list(const gnc_commodity_table * table) gboolean gnc_commodity_is_iso(const gnc_commodity * cm) { - if (!cm || !cm->namespace) return FALSE; - return cm->namespace->iso4217; + CommodityPrivate* priv; + + if (!cm) return FALSE; + + priv = GET_PRIVATE(cm); + if( !priv->namespace) return FALSE; + return priv->namespace->iso4217; } gboolean @@ -1592,7 +1893,7 @@ gnc_commodity_is_currency(const gnc_commodity *cm) const char *ns_name; if (!cm) return FALSE; - ns_name = gnc_commodity_namespace_get_name(cm->namespace); + ns_name = gnc_commodity_namespace_get_name(GET_PRIVATE(cm)->namespace); return (!safe_strcmp(ns_name, GNC_COMMODITY_NS_LEGACY) || !safe_strcmp(ns_name, GNC_COMMODITY_NS_CURRENCY)); } @@ -1627,10 +1928,11 @@ static void get_quotables_helper1(gpointer key, gpointer value, gpointer data) { gnc_commodity *comm = value; + CommodityPrivate* priv = GET_PRIVATE(comm); GList ** l = data; - if (!comm->quote_flag || - !comm->quote_source || !comm->quote_source->supported) + if (!priv->quote_flag || + !priv->quote_source || !priv->quote_source->supported) return; *l = g_list_prepend(*l, value); } @@ -1639,9 +1941,10 @@ static gboolean get_quotables_helper2 (gnc_commodity *comm, gpointer data) { GList ** l = data; + CommodityPrivate* priv = GET_PRIVATE(comm); - if (!comm->quote_flag || - !comm->quote_source || !comm->quote_source->supported) + if (!priv->quote_flag || + !priv->quote_source || !priv->quote_source->supported) return TRUE; *l = g_list_prepend(*l, comm); return TRUE; @@ -1963,6 +2266,7 @@ gboolean gnc_commodity_table_add_default_data(gnc_commodity_table *table, QofBook *book) { QofCollection *col; + gnc_commodity* c; ENTER ("table=%p", table); gnc_commodity_table_add_namespace(table, GNC_COMMODITY_NS_AMEX, book); @@ -1970,6 +2274,9 @@ gnc_commodity_table_add_default_data(gnc_commodity_table *table, QofBook *book) gnc_commodity_table_add_namespace(table, GNC_COMMODITY_NS_NASDAQ, book); gnc_commodity_table_add_namespace(table, GNC_COMMODITY_NS_EUREX, book); gnc_commodity_table_add_namespace(table, GNC_COMMODITY_NS_MUTUAL, book); + gnc_commodity_table_add_namespace(table, "template", book); + c = gnc_commodity_new(book, "template", "template", "template", "template", 1); + gnc_commodity_table_insert(table, c); #include "iso-4217-currencies.c" diff --git a/src/engine/gnc-engine.c b/src/engine/gnc-engine.c index 1940d70429..0f0f53a84a 100644 --- a/src/engine/gnc-engine.c +++ b/src/engine/gnc-engine.c @@ -36,7 +36,7 @@ #include "gnc-pricedb-p.h" /** gnc file backend library name */ -#define GNC_LIB_NAME "gncmod-backend-file" +#define GNC_LIB_NAME "gncmod-backend-xml" /* gnc-backend-file location */ #include "gnc-path.h" @@ -76,11 +76,11 @@ gnc_engine_init(int argc, char ** argv) const gchar* lib; gboolean required; } libs[] = { - { GNC_LIB_NAME, TRUE }, -#ifdef SQL_DIR - /* shouldn't the PG gnc-module do this instead of US doing it? */ - { "gnc-backend-postgres", FALSE }, +#if defined( HAVE_DBI_DBI_H ) + { "gncmod-backend-dbi", TRUE }, #endif +// { "gncmod-backend-gda", TRUE }, + { "gncmod-backend-xml", TRUE }, { NULL, FALSE } }, *lib; gnc_engine_init_hook_t hook; GList * cur; diff --git a/src/engine/gnc-filepath-utils.c b/src/engine/gnc-filepath-utils.c index 631ae220c8..3ad100addc 100644 --- a/src/engine/gnc-filepath-utils.c +++ b/src/engine/gnc-filepath-utils.c @@ -205,6 +205,10 @@ xaccResolveFilePath (const char * filefrag) LEAVE("filefrag is file uri"); return g_strdup(filefrag + 5); } + if( g_ascii_strncasecmp( filefrag, "xml:", 4 ) == 0 ) { + LEAVE( "filefrag is xml file uri" ); + return g_strdup( filefrag + 4); + } /* get conservative on the length so that sprintf(getpid()) works ... */ /* strlen ("/.LCK") + sprintf (%x%d) */ @@ -281,6 +285,9 @@ xaccResolveFilePath (const char * filefrag) char * xaccResolveURL (const char * pathfrag) { + GList* list; + GList* node; + /* seriously invalid */ if (!pathfrag) return NULL; @@ -292,15 +299,34 @@ xaccResolveURL (const char * pathfrag) */ if (!g_ascii_strncasecmp (pathfrag, "http://", 7) || - !g_ascii_strncasecmp (pathfrag, "https://", 8) || - !g_ascii_strncasecmp (pathfrag, "postgres://", 11)) + !g_ascii_strncasecmp (pathfrag, "https://", 8)) { return g_strdup(pathfrag); } + /* Check the URL against the list of registered access methods */ + list = qof_backend_get_registered_access_method_list(); + for( node = list; node != NULL; node = node->next ) { + const gchar* access_method = node->data; + if( strcmp( access_method, "file" ) != 0 && + strcmp( access_method, "xml" ) != 0 ) { + gchar s[30]; + sprintf( s, "%s://", access_method ); + if( !g_ascii_strncasecmp( pathfrag, s, strlen(s) ) ) { + g_list_free(list); + return g_strdup(pathfrag); + } + } + } + g_list_free(list); + + /* "file:" and "xml:" are handled specially */ if (!g_ascii_strncasecmp (pathfrag, "file:", 5)) { return (xaccResolveFilePath (pathfrag)); } + if (!g_ascii_strncasecmp (pathfrag, "xml:", 4)) { + return (g_strdup_printf( "xml:%s", xaccResolveFilePath (pathfrag)) ); + } return (xaccResolveFilePath (pathfrag)); } diff --git a/src/engine/gnc-lot.c b/src/engine/gnc-lot.c index 76adeca161..a5ae770a7e 100644 --- a/src/engine/gnc-lot.c +++ b/src/engine/gnc-lot.c @@ -99,8 +99,8 @@ gnc_lot_new (QofBook *book) return lot; } -void -gnc_lot_destroy (GNCLot *lot) +static void +gnc_lot_free(GNCLot* lot) { GList *node; if (!lot) return; @@ -122,6 +122,16 @@ gnc_lot_destroy (GNCLot *lot) g_object_unref (lot); } +void +gnc_lot_destroy (GNCLot *lot) +{ + if (!lot) return; + + gnc_lot_begin_edit(lot); + qof_instance_set_destroying(lot, TRUE); + gnc_lot_commit_edit(lot); +} + /* ============================================================= */ void @@ -135,13 +145,20 @@ static void commit_err (QofInstance *inst, QofBackendError errcode) PERR ("Failed to commit: %d", errcode); } +static void lot_free(QofInstance* inst) +{ + GNCLot* lot = GNC_LOT(inst); + + gnc_lot_free(lot); +} + static void noop (QofInstance *inst) {} void gnc_lot_commit_edit (GNCLot *lot) { if (!qof_commit_edit (QOF_INSTANCE(lot))) return; - qof_commit_edit_part2 (&lot->inst, commit_err, noop, noop); + qof_commit_edit_part2 (&lot->inst, commit_err, noop, lot_free); } /* ============================================================= */ @@ -356,7 +373,7 @@ gnc_lot_add_split (GNCLot *lot, Split *split) { gnc_lot_remove_split (split->lot, split); } - split->lot = lot; + xaccSplitSetLot(split, lot); lot->splits = g_list_append (lot->splits, split); @@ -377,7 +394,7 @@ gnc_lot_remove_split (GNCLot *lot, Split *split) gnc_lot_begin_edit(lot); qof_instance_set_dirty(QOF_INSTANCE(lot)); lot->splits = g_list_remove (lot->splits, split); - split->lot = NULL; + xaccSplitSetLot(split, NULL); lot->is_closed = -1; /* force an is-closed computation */ if (NULL == lot->splits) diff --git a/src/engine/gnc-lot.h b/src/engine/gnc-lot.h index e2cf8fcae9..fb294ee849 100644 --- a/src/engine/gnc-lot.h +++ b/src/engine/gnc-lot.h @@ -92,7 +92,7 @@ void gnc_lot_commit_edit (GNCLot *lot); * that *all* splits in a lot must also be in the same account. * Note that this routine adds the split unconditionally, with * no regard for the accounting policy. To enforce a particular - * accounting polciy, use the xaccSplitAssignToLot() routine + * accounting policy, use the xaccSplitAssignToLot() routine * instead. */ void gnc_lot_add_split (GNCLot *, Split *); diff --git a/src/experimental/cgi-bin/Makefile.am b/src/experimental/cgi-bin/Makefile.am index bce86d66b1..5e128e694e 100644 --- a/src/experimental/cgi-bin/Makefile.am +++ b/src/experimental/cgi-bin/Makefile.am @@ -5,7 +5,7 @@ bin_PROGRAMS = hello hello2 fastcgi-hello hello3 gnc-server AM_CFLAGS = \ -I../../engine -I${srcdir}/../../engine \ - -I../../backend/file -I${srcdir}/../../backend/file \ + -I../../backend/xml -I${srcdir}/../../backend/xml \ ${GLIB_CFLAGS} LDADD = \ diff --git a/src/gnc-ui.h b/src/gnc-ui.h index 8f308bea09..d8faeead0b 100644 --- a/src/gnc-ui.h +++ b/src/gnc-ui.h @@ -111,7 +111,7 @@ typedef enum GNC_PRICE_NEW, } GNCPriceEditType; -GNCPrice* gnc_price_edit_dialog (gncUIWidget parent, QofSession *session, +void gnc_price_edit_dialog (gncUIWidget parent, QofSession *session, GNCPrice *price, GNCPriceEditType type); GNCPrice* gnc_price_edit_by_guid (GtkWidget * parent, const GUID * guid); void gnc_prices_dialog (gncUIWidget parent); diff --git a/src/gnome-utils/Makefile.am b/src/gnome-utils/Makefile.am index 2adf379bc8..96a12a8db3 100644 --- a/src/gnome-utils/Makefile.am +++ b/src/gnome-utils/Makefile.am @@ -20,7 +20,8 @@ AM_CFLAGS = \ ${GTKHTML_CFLAGS} \ ${GUILE_INCS} \ ${QOF_CFLAGS} \ - ${GOFFICE_CFLAGS} + ${GOFFICE_CFLAGS} \ + ${LIBGDA_CFLAGS} libgncmod_gnome_utils_la_SOURCES = \ QuickFill.c \ @@ -29,6 +30,7 @@ libgncmod_gnome_utils_la_SOURCES = \ dialog-account.c \ dialog-book-close.c \ dialog-commodity.c \ + dialog-database-connection.c \ dialog-options.c \ dialog-preferences.c \ dialog-query-list.c \ @@ -103,6 +105,7 @@ gncinclude_HEADERS = \ dialog-account.h \ dialog-book-close.h \ dialog-commodity.h \ + dialog-database-connection.h \ dialog-preferences.h \ dialog-options.h \ dialog-query-list.h \ @@ -178,7 +181,7 @@ libgncmod_gnome_utils_la_LIBADD = \ ${top_builddir}/src/core-utils/libgnc-core-utils.la \ ${top_builddir}/src/gnc-module/libgnc-module.la \ ${top_builddir}/src/engine/libgncmod-engine.la \ - ${top_builddir}/src/backend/file/libgnc-backend-file-utils.la \ + ${top_builddir}/src/backend/file/libgnc-backend-xml-utils.la \ ${top_builddir}/src/calculation/libgncmod-calculation.la \ ${top_builddir}/src/app-utils/libgncmod-app-utils.la \ $(top_builddir)/lib/libc/libc-missing.la \ diff --git a/src/gnome-utils/dialog-database-connection.c b/src/gnome-utils/dialog-database-connection.c new file mode 100644 index 0000000000..0f429a0ac6 --- /dev/null +++ b/src/gnome-utils/dialog-database-connection.c @@ -0,0 +1,155 @@ +/********************************************************************\ + * dialog-database-connection.c -- dialog for opening a connection * + * to a libgda database, either predefined * + * in ~/.libgda/config or explicit using * + * provider and database. * + * * + * Copyright (C) 2007-8 Phil Longstaff (plongstaff@rogers.com) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ + +#include "config.h" + +#include +#include +#include + +#include "gnc-ui.h" +#include "dialog-utils.h" +#include "dialog-database-connection.h" +#include "gnc-file.h" +#include "gnc-session.h" + +static QofLogModule log_module = GNC_MOD_GUI; + +void gnc_database_connection_response_cb( GtkDialog *, gint, GtkDialog * ); +#define PB_LOAD_RESPONSE 1000 +#define PB_SAVE_RESPONSE 1001 + +struct DatabaseConnectionWindow +{ + /* Parts of the dialog */ + GtkWidget* dialog; + GtkWidget* rb_mysql; + GtkWidget* rb_postgresql; + GtkWidget* tf_host; + GtkWidget* tf_database; + GtkWidget* tf_username; + GtkWidget* tf_password; +}; + +static gchar* +geturl( struct DatabaseConnectionWindow* dcw ) +{ + gchar* url; + const gchar* host; + const gchar* database; + const gchar* username; + const gchar* password; + const gchar* type; + + host = gtk_entry_get_text( GTK_ENTRY(dcw->tf_host) ); + database = gtk_entry_get_text( GTK_ENTRY(dcw->tf_database) ); + username = gtk_entry_get_text( GTK_ENTRY(dcw->tf_username) ); + password = gtk_entry_get_text( GTK_ENTRY(dcw->tf_password) ); + + if( gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(dcw->rb_mysql) ) ) { + type = "mysql"; + } else { + type = "postgres"; + } + url = g_strdup_printf( "%s://%s:%s:%s:%s", + type, host, database, username, password ); + + return url; +} + +void +gnc_database_connection_response_cb(GtkDialog *dialog, gint response, GtkDialog *unused) +{ + struct DatabaseConnectionWindow* dcw; + + g_return_if_fail( dialog != NULL ); + + dcw = g_object_get_data( G_OBJECT(dialog), "DatabaseConnectionWindow" ); + g_return_if_fail(dcw); + + switch( response ) { + case GTK_RESPONSE_HELP: + gnc_gnome_help( HF_HELP, HL_GLOBPREFS ); + break; + + case PB_LOAD_RESPONSE: + gnc_file_open_file( geturl( dcw ) ); + break; + + case PB_SAVE_RESPONSE: + gnc_file_do_save_as( geturl( dcw ) ); + break; + + case GTK_RESPONSE_CANCEL: + break; + + default: + PERR( "Invalid response" ); + break; + } + + if( response != GTK_RESPONSE_HELP ) { + gtk_widget_destroy( GTK_WIDGET(dialog) ); + } +} + +void gnc_ui_database_connection( void ) +{ + struct DatabaseConnectionWindow *dcw; + GladeXML* xml; + GtkWidget* box; + GList* ds_node; + + dcw = g_new0(struct DatabaseConnectionWindow, 1); + g_return_if_fail(dcw); + + /* Open the dialog */ + xml = gnc_glade_xml_new( "dialog-database-connection.glade", "Database Connection" ); + dcw->dialog = glade_xml_get_widget( xml, "Database Connection" ); + + dcw->rb_mysql = glade_xml_get_widget( xml, "rb_mysql" ); + dcw->rb_postgresql = glade_xml_get_widget( xml, "rb_postgresql" ); + dcw->tf_host = glade_xml_get_widget( xml, "tf_host" ); + gtk_entry_set_text( GTK_ENTRY(dcw->tf_host), "localhost" ); + dcw->tf_database = glade_xml_get_widget( xml, "tf_database" ); + gtk_entry_set_text( GTK_ENTRY(dcw->tf_database), "gnucash" ); + dcw->tf_username = glade_xml_get_widget( xml, "tf_username" ); + dcw->tf_password = glade_xml_get_widget( xml, "tf_password" ); + + /* Autoconnect signals */ + glade_xml_signal_autoconnect_full( xml, gnc_glade_autoconnect_full_func, + dcw->dialog ); + + /* Clean up the xml data structure when the dialog is destroyed */ + g_object_set_data_full( G_OBJECT(dcw->dialog), "dialog-database-connection.glade", + xml, g_object_unref ); + g_object_set_data_full( G_OBJECT(dcw->dialog), "DatabaseConnectionWindow", dcw, + g_free ); + + /* Run the dialog */ + gtk_widget_show_all( dcw->dialog ); +} + diff --git a/src/gnome-utils/dialog-database-connection.h b/src/gnome-utils/dialog-database-connection.h new file mode 100644 index 0000000000..0635d415d7 --- /dev/null +++ b/src/gnome-utils/dialog-database-connection.h @@ -0,0 +1,46 @@ +/********************************************************************\ + * dialog-database-connection.h -- dialog for opening a connection * + * to a libgda database, either predefined * + * in ~/.libgda/config or explicit using * + * provider and database. * + * * + * Copyright (C) 2007-8 Phil Longstaff (plongstaff@rogers.com) * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License as * + * published by the Free Software Foundation; either version 2 of * + * the License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License* + * along with this program; if not, contact: * + * * + * Free Software Foundation Voice: +1-617-542-5942 * + * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 * + * Boston, MA 02110-1301, USA gnu@gnu.org * +\********************************************************************/ + +#ifndef DIALOG_DATABASE_CONNECTION_H +#define DIALOG_DATABASE_CONNECTION_H + +/** @addtogroup GUI + @{ */ +/** @file dialog-database-connection.h + * + * This file contains the functions to present a GUI to select + * a database connection. + */ + +/** Create and run the dialog to close the book. + * + */ +void gnc_ui_database_connection( void ); + + +/** @} */ + +#endif /* DIALOG_DATABASE_CONNECTION_H */ diff --git a/src/gnome-utils/glade/Makefile.am b/src/gnome-utils/glade/Makefile.am index 7ee34b2c1b..1ff6f60385 100644 --- a/src/gnome-utils/glade/Makefile.am +++ b/src/gnome-utils/glade/Makefile.am @@ -2,6 +2,7 @@ gladedir = $(GNC_GLADE_DIR) glade_DATA = \ commodity.glade \ dialog-book-close.glade \ + dialog-database-connection.glade \ dialog-query-list.glade \ dialog-reset-warnings.glade \ druid-provider-multifile.glade \ diff --git a/src/gnome-utils/glade/dialog-database-connection.glade b/src/gnome-utils/glade/dialog-database-connection.glade new file mode 100644 index 0000000000..18efb4776b --- /dev/null +++ b/src/gnome-utils/glade/dialog-database-connection.glade @@ -0,0 +1,204 @@ + + + + + + True + Database Connection + GDK_WINDOW_TYPE_HINT_DIALOG + + + + True + + + True + + + True + + + True + True + MySQL + True + 0 + True + + + + + True + True + PostgreSQL + True + 0 + True + rb_mysql + + + 1 + + + + + + + True + 4 + 2 + + + True + True + + + 1 + 2 + 3 + 4 + + + + + True + True + + + 1 + 2 + 2 + 3 + + + + + True + True + + + 1 + 2 + 1 + 2 + + + + + True + True + + + 1 + 2 + + + + + True + password + + + 3 + 4 + + + + + True + username + + + 2 + 3 + + + + + True + database + + + 1 + 2 + + + + + True + host + + + + + 1 + + + + + 2 + + + + + True + GTK_BUTTONBOX_END + + + True + True + True + gtk-help + True + -11 + + + + + True + True + True + gtk-cancel + True + -6 + + + 1 + + + + + True + True + True + _Load + True + 1000 + + + 2 + + + + + True + True + True + _Save + True + 1001 + + + 3 + + + + + False + GTK_PACK_END + + + + + + diff --git a/src/gnome-utils/gnc-file.c b/src/gnome-utils/gnc-file.c index 754b553306..0427df6a79 100644 --- a/src/gnome-utils/gnc-file.c +++ b/src/gnome-utils/gnc-file.c @@ -1034,6 +1034,24 @@ gnc_file_save_as (void) g_free(default_dir); if (!filename) return; + gnc_file_do_save_as( filename ); + + LEAVE (" "); +} + +void +gnc_file_do_save_as (const char* filename) +{ + QofSession *new_session; + QofSession *session; + char *default_dir = NULL; /* Default to last open */ + char *last; + char *newfile; + const char *oldfile; + QofBackendError io_err = ERR_BACKEND_NO_ERR; + + ENTER(" "); + /* Check to see if the user specified the same file as the current * file. If so, then just do that, instead of the below, which * assumes a truly new name was given. */ diff --git a/src/gnome-utils/gnc-file.h b/src/gnome-utils/gnc-file.h index 3b7f0eaa3c..5399536b6e 100644 --- a/src/gnome-utils/gnc-file.h +++ b/src/gnome-utils/gnc-file.h @@ -127,6 +127,7 @@ void gnc_file_new (void); gboolean gnc_file_open (void); void gnc_file_save (void); void gnc_file_save_as (void); +void gnc_file_do_save_as(const char* filename); /** Tell the user about errors in the backends diff --git a/src/gnome-utils/gnc-main-window.c b/src/gnome-utils/gnc-main-window.c index f4bf6a336e..c7893b5d7c 100644 --- a/src/gnome-utils/gnc-main-window.c +++ b/src/gnome-utils/gnc-main-window.c @@ -1203,6 +1203,9 @@ gnc_main_window_event_handler (QofInstance *entity, QofEventId event_type, * window title and the title of the "Window" menu item associated * with the window. * + * As a side-effect, the save action is set sensitive iff the book + * is dirty. + * * @param window The window whose title should be generated. * * @return The title for the window. It is the callers @@ -1218,13 +1221,23 @@ gnc_main_window_generate_title (GncMainWindow *window) QofBook *book; const gchar *filename = NULL, *dirty = ""; gchar *title, *ptr; + GtkAction* action; + /* The save action is sensitive iff the book is dirty */ + action = gnc_main_window_find_action (window, "FileSaveAction"); + if (action != NULL) { + gtk_action_set_sensitive(action, FALSE); + } if (gnc_current_session_exist()) { filename = gnc_session_get_url (gnc_get_current_session ()); book = gnc_get_current_book(); - if (qof_instance_is_dirty(QOF_INSTANCE(book))) - dirty = "*"; - } + if (qof_instance_is_dirty(QOF_INSTANCE(book))) { + dirty = "*"; + if (action != NULL) { + gtk_action_set_sensitive(action, TRUE); + } + } + } if (!filename) filename = _(""); diff --git a/src/gnome-utils/test/Makefile.am b/src/gnome-utils/test/Makefile.am index 4fec0c79df..fb6c579a50 100644 --- a/src/gnome-utils/test/Makefile.am +++ b/src/gnome-utils/test/Makefile.am @@ -17,7 +17,7 @@ GNC_TEST_DEPS := @GNC_TEST_SRFI_LOAD_CMD@ \ --library-dir ${top_builddir}/src/core-utils \ --library-dir ${top_builddir}/src/gnc-module \ --library-dir ${top_builddir}/src/engine \ - --library-dir ${top_builddir}/src/backend/file \ + --library-dir ${top_builddir}/src/backend/xml \ --library-dir ${top_builddir}/src/calculation \ --library-dir ${top_builddir}/src/app-utils \ --library-dir ${top_builddir}/src/gnome-utils diff --git a/src/gnome/Makefile.am b/src/gnome/Makefile.am index e39897e5e6..92e48d5177 100644 --- a/src/gnome/Makefile.am +++ b/src/gnome/Makefile.am @@ -10,7 +10,7 @@ libgnc_gnome_la_LIBADD = \ ${top_builddir}/src/report/report-system/libgncmod-report-system.la \ ${top_builddir}/src/gnome-search/libgncmod-gnome-search.la \ ${top_builddir}/src/gnome-utils/libgncmod-gnome-utils.la \ - ${top_builddir}/src/backend/file/libgnc-backend-file-utils.la \ + ${top_builddir}/src/backend/file/libgnc-backend-xml-utils.la \ ${top_builddir}/src/app-utils/libgncmod-app-utils.la \ ${top_builddir}/src/engine/libgncmod-engine.la \ ${top_builddir}/src/calculation/libgncmod-calculation.la \ diff --git a/src/gnome/dialog-price-editor.c b/src/gnome/dialog-price-editor.c index a162856b38..fd3a1363d3 100644 --- a/src/gnome/dialog-price-editor.c +++ b/src/gnome/dialog-price-editor.c @@ -121,9 +121,9 @@ price_to_gui (PriceEditDialog *pedit_dialog) gnc_numeric value; Timespec date; - if (pedit_dialog->price) + commodity = gnc_price_get_commodity (pedit_dialog->price); + if (commodity) { - commodity = gnc_price_get_commodity (pedit_dialog->price); currency = gnc_price_get_currency (pedit_dialog->price); date = gnc_price_get_time (pedit_dialog->price); source = gnc_price_get_source (pedit_dialog->price); @@ -132,11 +132,10 @@ price_to_gui (PriceEditDialog *pedit_dialog) } else { - commodity = NULL; currency = gnc_default_currency (); date.tv_sec = time (NULL); date.tv_nsec = 0; - source = ""; + source = "user:price-editor"; type = ""; value = gnc_numeric_zero (); } @@ -383,22 +382,6 @@ gnc_price_pedit_dialog_create (GtkWidget *parent, } -static void -gnc_price_new_price_init (GNCPrice *price) -{ - Timespec date; - - gnc_price_begin_edit (price); - - gnc_price_set_source (price, "user:price-editor"); - date.tv_sec = time (NULL); - date.tv_nsec = 0; - gnc_price_set_time (price, date); - - gnc_price_commit_edit (price); - -} - static void close_handler (gpointer user_data) { @@ -436,7 +419,7 @@ show_handler (const char *class, gint component_id, * Args: parent - the parent of the window to be created * * Return: nothing * \********************************************************************/ -GNCPrice * +void gnc_price_edit_dialog (GtkWidget * parent, QofSession *session, GNCPrice * price, @@ -448,7 +431,7 @@ gnc_price_edit_dialog (GtkWidget * parent, if ((type == GNC_PRICE_EDIT) && (gnc_forall_gui_components (DIALOG_PRICE_EDIT_CM_CLASS, show_handler, price))) - return(price); + return; pedit_dialog = g_new0 (PriceEditDialog, 1); gnc_price_pedit_dialog_create (parent, pedit_dialog, session); @@ -461,7 +444,7 @@ gnc_price_edit_dialog (GtkWidget * parent, } else { price = gnc_price_create (pedit_dialog->book); } - gnc_price_new_price_init(price); + pedit_dialog->new = TRUE; /* New price will only have one ref, this dialog. */ break; @@ -473,16 +456,12 @@ gnc_price_edit_dialog (GtkWidget * parent, pedit_dialog->price = price; price_to_gui(pedit_dialog); - component_id = gnc_register_gui_component (DIALOG_PRICE_EDIT_CM_CLASS, refresh_handler, close_handler, pedit_dialog); gnc_gui_component_set_session (component_id, pedit_dialog->session); - gtk_widget_grab_focus (pedit_dialog->commodity_edit); - gtk_widget_show (pedit_dialog->dialog); - return(price); } /********************************************************************\ @@ -503,5 +482,6 @@ gnc_price_edit_by_guid (GtkWidget * parent, const GUID * guid) if (price == NULL) return(NULL); - return(gnc_price_edit_dialog(parent, session, price, GNC_PRICE_EDIT)); + gnc_price_edit_dialog(parent, session, price, GNC_PRICE_EDIT); + return price; } diff --git a/src/gnome/dialog-sx-editor.c b/src/gnome/dialog-sx-editor.c index 84b18e973a..378d1f2548 100644 --- a/src/gnome/dialog-sx-editor.c +++ b/src/gnome/dialog-sx-editor.c @@ -862,6 +862,8 @@ gnc_sxed_check_consistent( GncSxEditorDialog *sxed ) static void gnc_sxed_save_sx( GncSxEditorDialog *sxed ) { + gnc_sx_begin_edit( sxed->sx ); + /* name */ { char *name; @@ -963,6 +965,8 @@ gnc_sxed_save_sx( GncSxEditorDialog *sxed ) /* now that we have it, set the start date */ xaccSchedXactionSetStartDate( sxed->sx, &gdate ); } + + gnc_sx_commit_edit( sxed->sx ); } static void diff --git a/src/gnome/gnc-plugin-basic-commands.c b/src/gnome/gnc-plugin-basic-commands.c index b2d8a8f446..b76467c563 100644 --- a/src/gnome/gnc-plugin-basic-commands.c +++ b/src/gnome/gnc-plugin-basic-commands.c @@ -41,6 +41,7 @@ #include "dialog-book-close.h" #include "dialog-chart-export.h" +#include "dialog-database-connection.h" #include "dialog-fincalc.h" #include "dialog-find-transactions.h" #include "dialog-sx-since-last-run.h" @@ -67,6 +68,7 @@ static void gnc_plugin_basic_commands_finalize (GObject *object); /* Command callbacks */ static void gnc_main_window_cmd_file_new (GtkAction *action, GncMainWindowActionData *data); static void gnc_main_window_cmd_file_open (GtkAction *action, GncMainWindowActionData *data); +static void gnc_main_window_cmd_file_db_connection (GtkAction *action, GncMainWindowActionData *data); static void gnc_main_window_cmd_file_save (GtkAction *action, GncMainWindowActionData *data); static void gnc_main_window_cmd_file_save_as (GtkAction *action, GncMainWindowActionData *data); static void gnc_main_window_cmd_file_qsf_import (GtkAction *action, GncMainWindowActionData *data); @@ -101,6 +103,9 @@ static GtkActionEntry gnc_plugin_actions [] = { { "FileOpenAction", GTK_STOCK_OPEN, N_("_Open..."), NULL, N_("Open an existing GnuCash file"), G_CALLBACK (gnc_main_window_cmd_file_open) }, + { "FileDatabaseConnectionAction", NULL, N_("_Database Connection"), NULL, + N_("Connect to a database"), + G_CALLBACK (gnc_main_window_cmd_file_db_connection) }, { "FileSaveAction", GTK_STOCK_SAVE, N_("_Save"), "s", N_("Save the current file"), G_CALLBACK (gnc_main_window_cmd_file_save) }, @@ -332,6 +337,17 @@ gnc_main_window_cmd_file_open (GtkAction *action, GncMainWindowActionData *data) gnc_window_set_progressbar_window (NULL); } +static void +gnc_main_window_cmd_file_db_connection (GtkAction *action, GncMainWindowActionData *data) +{ + g_return_if_fail (data != NULL); + + if (!gnc_main_window_all_finish_pending()) + return; + + gnc_ui_database_connection(); +} + static void gnc_main_window_cmd_file_save (GtkAction *action, GncMainWindowActionData *data) { diff --git a/src/gnome/lot-viewer.c b/src/gnome/lot-viewer.c index 5a20d0c5e9..7eda2be3da 100644 --- a/src/gnome/lot-viewer.c +++ b/src/gnome/lot-viewer.c @@ -221,6 +221,8 @@ lv_save_current_row (GNCLotViewer *lv) if (lot) { + gnc_lot_begin_edit(lot); + /* Get the title, save_the_title */ str = gtk_entry_get_text (lv->title_entry); gnc_lot_set_title (lot, str); @@ -229,6 +231,8 @@ lv_save_current_row (GNCLotViewer *lv) notes = xxxgtk_textview_get_text (lv->lot_notes); gnc_lot_set_notes (lot, notes); g_free(notes); + + gnc_lot_commit_edit(lot); } } diff --git a/src/gnome/ui/gnc-plugin-basic-commands-ui.xml b/src/gnome/ui/gnc-plugin-basic-commands-ui.xml index 8a16b66867..175132651b 100644 --- a/src/gnome/ui/gnc-plugin-basic-commands-ui.xml +++ b/src/gnome/ui/gnc-plugin-basic-commands-ui.xml @@ -14,6 +14,7 @@ + diff --git a/src/import-export/qif-io-core/test/Makefile.am b/src/import-export/qif-io-core/test/Makefile.am index a681fd9db7..41c40541c1 100644 --- a/src/import-export/qif-io-core/test/Makefile.am +++ b/src/import-export/qif-io-core/test/Makefile.am @@ -10,7 +10,7 @@ GNC_TEST_DEPS := \ --gnc-module-dir ${top_builddir}/src/engine \ --gnc-module-dir ${top_builddir}/src/app-utils \ --gnc-module-dir ${top_builddir}/src/calculation \ - --gnc-module-dir ${top_builddir}/src/backend/file \ + --gnc-module-dir ${top_builddir}/src/backend/xml \ --gnc-module-dir ${top_builddir}/src/import-export/qif-io-core \ --guile-load-dir ${top_srcdir}/src/import-export/qif-io-core/test \ --guile-load-dir ${top_srcdir}/lib \ diff --git a/src/import-export/test/Makefile.am b/src/import-export/test/Makefile.am index b00bc2b10a..48086e14bc 100644 --- a/src/import-export/test/Makefile.am +++ b/src/import-export/test/Makefile.am @@ -29,7 +29,7 @@ GNC_TEST_DEPS := @GNC_TEST_SRFI_LOAD_CMD@ \ --library-dir ${top_builddir}/src/core-utils \ --library-dir ${top_builddir}/src/gnc-module \ --library-dir ${top_builddir}/src/engine \ - --library-dir ${top_builddir}/src/backend/file \ + --library-dir ${top_builddir}/src/backend/xml \ --library-dir ${top_builddir}/src/calculation \ --library-dir ${top_builddir}/src/app-utils \ --library-dir ${top_builddir}/src/gnome-utils \ diff --git a/src/report/report-gnome/test/Makefile.am b/src/report/report-gnome/test/Makefile.am index 91308cb386..03831aa184 100644 --- a/src/report/report-gnome/test/Makefile.am +++ b/src/report/report-gnome/test/Makefile.am @@ -12,7 +12,7 @@ GNC_TEST_DEPS := @GNC_TEST_SRFI_LOAD_CMD@ \ --library-dir ${top_builddir}/src/core-utils \ --library-dir ${top_builddir}/src/gnc-module \ --library-dir ${top_builddir}/src/engine \ - --library-dir ${top_builddir}/src/backend/file \ + --library-dir ${top_builddir}/src/backend/xml \ --library-dir ${top_builddir}/src/calculation \ --library-dir ${top_builddir}/src/app-utils \ --library-dir ${top_builddir}/src/gnome-utils \ diff --git a/src/report/stylesheets/test/Makefile.am b/src/report/stylesheets/test/Makefile.am index 848e2cf0c0..968be08dd7 100644 --- a/src/report/stylesheets/test/Makefile.am +++ b/src/report/stylesheets/test/Makefile.am @@ -10,7 +10,7 @@ GNC_TEST_DEPS := @GNC_TEST_SRFI_LOAD_CMD@ \ --library-dir ${top_builddir}/src/core-utils \ --library-dir ${top_builddir}/src/gnc-module \ --library-dir ${top_builddir}/src/engine \ - --library-dir ${top_builddir}/src/backend/file \ + --library-dir ${top_builddir}/src/backend/xml \ --library-dir ${top_builddir}/src/calculation \ --library-dir ${top_builddir}/src/app-utils \ --library-dir ${top_builddir}/src/gnome-utils \ diff --git a/src/valgrind-gdk.supp b/src/valgrind-gdk.supp index 2dd83da2a5..1b17b40904 100644 --- a/src/valgrind-gdk.supp +++ b/src/valgrind-gdk.supp @@ -1,3 +1,30 @@ +{ + qtcurve Cond + Memcheck:Cond + obj:/usr/lib/kde3/plugins/styles/qtcurve.so + fun:drawArrow + obj:/usr/lib/gtk-2.0/*/engines/libqtengine.so + fun:gtk_paint_arrow +} +{ + qtcurve Cond #2 + Memcheck:Cond + obj:/usr/lib/libkdefx.so* + obj:/usr/lib/kde3/plugins/styles/qtcurve.so + fun:drawArrow + obj:/usr/lib/gtk-2.0/*/engines/libqtengine.so + fun:gtk_paint_arrow +} +{ + qtcurve Cond #3 + Memcheck:Cond + obj:/usr/lib/libqt-mt.so* + obj:/usr/lib/libkdefx.so* + obj:/usr/lib/kde3/plugins/styles/qtcurve.so + fun:drawArrow + obj:/usr/lib/gtk-2.0/*/engines/libqtengine.so + fun:gtk_paint_arrow +} { Memcheck:Leak diff --git a/src/valgrind-glib.supp b/src/valgrind-glib.supp new file mode 100644 index 0000000000..48fc3191b6 --- /dev/null +++ b/src/valgrind-glib.supp @@ -0,0 +1,91 @@ + +# FILE: valgrind-gnucash.supp +# +# FUNCTION: This file is a valgrind 'suppression' file, used to +# get valgrind to not report certain errors that we aren't +# interested in. +# +# valgrind is a memory allocation debugging tool. See the +# file ./HACKING for instructions on how to run gnucash with +# valgrind +# +# HISTORY: created aug 2003 linas vepstas + + +##----------------------------------------------------------------------## + +# Format of this file is: +# { +# name_of_suppression +# skin_name:supp_kind +# (optional extra info for some suppression types) +# caller0 name, or /name/of/so/file.so +# caller1 name, or ditto +# (optionally: caller2 name) +# (optionally: caller3 name) +# } +# +# For memcheck, the supp_kinds are: +# +# Param Value1 Value2 Value4 Value8 Value16 +# Free Addr1 Addr2 Addr4 Addr8 Addr16 +# Cond (previously known as Value0) +# Leak +# +# and the optional extra info is: +# if Param: name of system call param +# if Free: name of free-ing fn) + +{ + g_thread_init Leak + Memcheck:Leak + fun:memalign + fun:posix_memalign + obj:/usr/lib/libglib-2.0.so.0.1600.3 + fun:g_slice_alloc + fun:g_array_sized_new + fun:g_array_new + fun:g_static_private_set + fun:g_get_filename_charsets +} +{ + g_get_tmp_dir leak + Memcheck:Leak + fun:malloc + obj:/lib/tls/i686/cmov/libc-2.7.so + fun:__nss_database_lookup + obj:* + obj:* + fun:getpwnam_r + obj:*libglib-2.0* + fun:g_get_tmp_dir +} +{ + g_option_context_parse leak + Memcheck:Leak + fun:calloc + fun:g_malloc0 + obj:*libgobject* + obj:*libgobject* + fun:g_type_init_with_debug_flags + fun:g_type_init + fun:gdk_pre_parse_libgtk_only + obj:*libgtk-x11-2.0* + fun:g_option_context_parse +} +{ + g_child_watch_add lean + Memcheck:Leak + fun:calloc (vg_replace_malloc.c:397) + obj:* + fun:_dl_allocate_tls + fun:pthread_create* + obj:*libgthread-2.0* + fun:g_thread_create_full + obj:*libglib-2.0* + fun:g_child_watch_source_new + fun:g_child_watch_add_full + fun:g_child_watch_add +} + +# ##----------------------------------------------------------------------## diff --git a/src/valgrind-gnucash.supp b/src/valgrind-gnucash.supp index 006db43862..5b63374bf7 100644 --- a/src/valgrind-gnucash.supp +++ b/src/valgrind-gnucash.supp @@ -37,17 +37,68 @@ # if Free: name of free-ing fn) { - libguile.so(Cond) - Memcheck:Cond - obj:*libguile.so.* + libld-2.7(Addr4) + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so } - { - libguile.so(Value4) - Memcheck:Value4 - obj:*libguile.so* + libld-2.7(Addr4 #2) + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libc-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libc-2.7.so +} +{ + libld-2.7(Addr4 #3) + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libc-2.7.so + fun:_dl_sym +} +{ + libld-2.7(Addr4 #4) + Memcheck:Addr4 + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libdl-2.7.so +} +{ + libld-2.7(Cond) + Memcheck:Cond + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libc-2.7.so + fun:_dl_sym +} +{ + libld-2.7(Cond #2) + Memcheck:Cond + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libc-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libc-2.7.so +} +{ + libld-2.7(Cond #3) + Memcheck:Cond + obj:/lib/ld-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libdl-2.7.so +} +{ + libld-2.7(Cond #4) + Memcheck:Cond + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libdl-2.7.so + obj:/lib/ld-2.7.so + obj:/lib/tls/i686/cmov/libdl-2.7.so } - { libglib-1.2.so.0(Value4) Memcheck:Value4 @@ -55,7 +106,6 @@ fun:g_hash_table_insert fun:g_hash_table_lookup_extended } - { write(buf) Param Memcheck:Param @@ -63,9 +113,8 @@ obj:/lib/libpthread-2.5.so fun:_X11TransWrite } - { - + writev(vector) ORBit Param #1 Memcheck:Param writev(vector[...]) fun:do_writev @@ -74,9 +123,8 @@ fun:link_connection_writev fun:giop_send_buffer_write } - { - + writev(vector) ORBit Param #2 Memcheck:Param writev(vector[...]) fun:do_writev @@ -84,18 +132,16 @@ fun:link_connection_writev fun:giop_send_buffer_write } - +#{ +# pthread_mutex_lock PThread +# core:PThread +# fun:pthread_mutex_lock +# fun:_IO_flockfile +# fun:pango_read_line +# fun:pango_find_map +#} { - - core:PThread - fun:pthread_mutex_lock - fun:_IO_flockfile - fun:pango_read_line - fun:pango_find_map -} - -{ - + writev(vector) X11 Param Memcheck:Param writev(vector[...]) fun:writev @@ -103,65 +149,32 @@ fun:_X11TransWritev fun:_XSend } - - { - - Memcheck:Leak - fun:malloc - obj:/usr/lib/libguile.so.12.3.0 - obj:/usr/lib/libguile.so.12.3.0 - obj:/usr/lib/libguile.so.12.3.0 -} -{ - - Memcheck:Leak - fun:realloc - fun:scm_make_port_type - fun:scm_init_vports - obj:/usr/lib/libguile.so.12.3.0 -} -{ - - Memcheck:Leak - fun:realloc - obj:/usr/lib/libguile.so.12.3.0 - fun:scm_gc_for_newcell - fun:scm_acons -} - -{ - + register_state Leak Memcheck:Leak fun:malloc fun:register_state fun:create_cd_newstate fun:re_compile_internal } - - - { - + poptGetContext Leak Memcheck:Leak fun:* fun:poptGetContext fun:gnome_program_preinit obj:/usr/lib/libgnome-2.so.0.800.0 } - - { - + IceAddConnectionWatch Leak Memcheck:Leak fun:malloc fun:IceAddConnectionWatch fun:gnome_ice_init obj:/usr/lib/libgnomeui-2.so.0.800.0 } - { - + CORBA_string_dup Leak Memcheck:Leak fun:malloc fun:g_malloc @@ -169,7 +182,7 @@ fun:CORBA_string_dup } { - + iconv_open Leak Memcheck:Leak fun:malloc fun:__gconv_open @@ -177,7 +190,7 @@ obj:/usr/lib/libglib-2.0.so.0.400.8 } { - + openaux Leak Memcheck:Leak fun:malloc fun:_dl_map_object_from_fd @@ -185,7 +198,7 @@ fun:openaux } { - + FcInitLoadConfigAndFonts Leak Memcheck:Leak fun:malloc fun:FcFontSetCreate @@ -193,23 +206,22 @@ fun:FcInitLoadConfigAndFonts } { - + libfontconfig.so Leak Memcheck:Leak fun:malloc fun:* obj:/usr/lib/libfontconfig.so.1.0.4 } { - + XftInitFtLibrary Leak Memcheck:Leak fun:malloc fun:FT_New_Memory fun:FT_Init_FreeType fun:XftInitFtLibrary } - { - + _XftDisplayInfoGet Leak Memcheck:Leak fun:malloc fun:XRenderFindDisplay @@ -217,32 +229,31 @@ fun:_XftDisplayInfoGet } { - + libXft Leak Memcheck:Leak fun:malloc obj:/usr/X11R6/lib/libXft.so.2.1.2 } - { - + /usr/bin/esd calloc Leak Memcheck:Leak fun:calloc obj:/usr/bin/esd } { - + /usr/bin/esd malloc Leak Memcheck:Leak fun:malloc obj:/usr/bin/esd } { - + /lib/libasound Leak Memcheck:Leak fun:malloc obj:/lib/libasound.so.2.0.0 } { - + esd_open_sound Leak Memcheck:Leak fun:malloc fun:esd_get_socket_dirname @@ -250,7 +261,7 @@ fun:esd_open_sound } { - + snd_pcm_open Leak Memcheck:Leak fun:calloc fun:snd_config_update_r @@ -258,7 +269,7 @@ fun:snd_pcm_open } { - + snd_output_stdio_attach Leak Memcheck:Leak fun:calloc fun:snd_output_stdio_attach @@ -266,20 +277,18 @@ obj:/usr/bin/esd } { - + esd_get_socket_name Leak Memcheck:Leak fun:malloc fun:esd_get_socket_name fun:esd_open_sound obj:/usr/lib/libgnome-2.so.0.800.0 } - { - + /lib/libcrypto realloc Leak Memcheck:Leak fun:realloc obj:/lib/libcrypto.so.0.9.7a } - # ##----------------------------------------------------------------------## diff --git a/src/valgrind-libfontconfig.supp b/src/valgrind-libfontconfig.supp new file mode 100644 index 0000000000..2cc6d9a68c --- /dev/null +++ b/src/valgrind-libfontconfig.supp @@ -0,0 +1,57 @@ +# FILE: valgrind-libfontconfig.supp +# +# FUNCTION: This file is a valgrind 'suppression' file, used to +# get valgrind to not report certain errors that we aren't +# interested in. +# +# valgrind is a memory allocation debugging tool. See the +# file ./HACKING for instructions on how to run gnucash with +# valgrind +# +# HISTORY: created aug 2003 linas vepstas + + +##----------------------------------------------------------------------## + +# Format of this file is: +# { +# name_of_suppression +# skin_name:supp_kind +# (optional extra info for some suppression types) +# caller0 name, or /name/of/so/file.so +# caller1 name, or ditto +# (optionally: caller2 name) +# (optionally: caller3 name) +# } +# +# For memcheck, the supp_kinds are: +# +# Param Value1 Value2 Value4 Value8 Value16 +# Free Addr1 Addr2 Addr4 Addr8 Addr16 +# Cond (previously known as Value0) +# Leak +# +# and the optional extra info is: +# if Param: name of system call param +# if Free: name of free-ing fn) + +{ + libfontconfig realloc Leak + Memcheck:Leak + fun:realloc + obj:/usr/lib/libfontconfig.so.1.3.0 +} +{ + libfontconfig malloc Leak #1 + Memcheck:Leak + fun:malloc + obj:/usr/lib/libfontconfig.so.1.3.0 +} +{ + libfontconfig malloc Leak #2 + fun:malloc + fun:FcStrCopy +} + + +# ##----------------------------------------------------------------------## diff --git a/src/valgrind-libgda.supp b/src/valgrind-libgda.supp new file mode 100644 index 0000000000..64d5f971de --- /dev/null +++ b/src/valgrind-libgda.supp @@ -0,0 +1,51 @@ + +# FILE: valgrind-gnucash.supp +# +# FUNCTION: This file is a valgrind 'suppression' file, used to +# get valgrind to not report certain errors that we aren't +# interested in. +# +# valgrind is a memory allocation debugging tool. See the +# file ./HACKING for instructions on how to run gnucash with +# valgrind +# +# HISTORY: created aug 2003 linas vepstas + + +##----------------------------------------------------------------------## + +# Format of this file is: +# { +# name_of_suppression +# skin_name:supp_kind +# (optional extra info for some suppression types) +# caller0 name, or /name/of/so/file.so +# caller1 name, or ditto +# (optionally: caller2 name) +# (optionally: caller3 name) +# } +# +# For memcheck, the supp_kinds are: +# +# Param Value1 Value2 Value4 Value8 Value16 +# Free Addr1 Addr2 Addr4 Addr8 Addr16 +# Cond (previously known as Value0) +# Leak +# +# and the optional extra info is: +# if Param: name of system call param +# if Free: name of free-ing fn) + +{ + libgda permanant memory for gda_paramlist_dtd + Memcheck:Leak + fun:malloc + fun:xmlStrndup + fun:xmlStrdup + fun:xmlNewDtd + fun:xmlSAXParseDTD + fun:xmlParseDTD + fun:gda_init +} + +# ##----------------------------------------------------------------------## diff --git a/src/valgrind-libguile.supp b/src/valgrind-libguile.supp new file mode 100644 index 0000000000..53bdd0de74 --- /dev/null +++ b/src/valgrind-libguile.supp @@ -0,0 +1,91 @@ + +# FILE: valgrind-gnucash.supp +# +# FUNCTION: This file is a valgrind 'suppression' file, used to +# get valgrind to not report certain errors that we aren't +# interested in. +# +# valgrind is a memory allocation debugging tool. See the +# file ./HACKING for instructions on how to run gnucash with +# valgrind +# +# HISTORY: created aug 2003 linas vepstas + + +##----------------------------------------------------------------------## + +# Format of this file is: +# { +# name_of_suppression +# skin_name:supp_kind +# (optional extra info for some suppression types) +# caller0 name, or /name/of/so/file.so +# caller1 name, or ditto +# (optionally: caller2 name) +# (optionally: caller3 name) +# } +# +# For memcheck, the supp_kinds are: +# +# Param Value1 Value2 Value4 Value8 Value16 +# Free Addr1 Addr2 Addr4 Addr8 Addr16 +# Cond (previously known as Value0) +# Leak +# +# and the optional extra info is: +# if Param: name of system call param +# if Free: name of free-ing fn) + +{ + libguile.so(Cond) + Memcheck:Cond + obj:*libguile.so.* +} +{ + libguile.so(Value4) + Memcheck:Value4 + obj:*libguile.so* +} +{ + libguile.so(Leak) + Memcheck:Leak + fun:malloc + obj:*libguile.so* + obj:*libguile.so* + obj:*libguile.so* +} +{ + scm_make_port_type Leak + Memcheck:Leak + fun:realloc + fun:scm_make_port_type + fun:scm_init_vports + obj:*libguile.so* +} +{ + scm_gc_for_newcell Leak + Memcheck:Leak + fun:malloc + obj:*libguile.so* + fun:scm_gc_for_newcell + fun:scm_acons +} +{ + scm_dynamic_link leak + Memcheck:Leak + fun:malloc + obj:*libguile-ltdl* + obj:*libguile-ltdl* + obj:*libguile-ltdl* + fun:scm_lt_dlopenext + fun:scm_dynamic_link +} +{ + scm_deval leak + Memcheck:Leak + fun:malloc + fun:_wrap_* + fun:scm_deval +} + +# ##----------------------------------------------------------------------##