Implement QofQuery for splits. At this point, the queries used by current reports work, but

more split fields could be implemented.  Implemented are: account.guid, reconcile-flag,
trans.desc and trans.date-posted.

Fixes Bug #585167


git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@18135 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
Phil Longstaff 2009-06-19 16:48:48 +00:00
parent 22a45587a9
commit 63ca792cfb
6 changed files with 623 additions and 104 deletions

View File

@ -26,7 +26,8 @@ libgnc_backend_sql_la_SOURCES = \
gnc-recurrence-sql.c \
gnc-schedxaction-sql.c \
gnc-slots-sql.c \
gnc-transaction-sql.c
gnc-transaction-sql.c \
escape.c
noinst_HEADERS = \
gnc-account-sql.h \
@ -39,7 +40,8 @@ noinst_HEADERS = \
gnc-recurrence-sql.h \
gnc-schedxaction-sql.h \
gnc-slots-sql.h \
gnc-transaction-sql.h
gnc-transaction-sql.h \
escape.h
libgnc_backend_sql_la_LIBADD = \
${GLIB_LIBS} \

150
src/backend/sql/escape.c Normal file
View File

@ -0,0 +1,150 @@
/********************************************************************\
* escape.c : escape SQL reserved characters *
* Copyright (C) 2001 Linas Vepstas <linas@linas.org> *
* *
* 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:
* esacpe.c
*
* FUNCTION:
* Escapes the ' and \ characters in a string
*/
#include "config.h"
#include <glib.h>
#include <string.h>
#include "gnc-engine.h"
#include "escape.h"
static QofLogModule log_module = GNC_MOD_BACKEND;
/* ================================================ */
struct _escape {
/* pointer to memory used for escaping arguments */
char * escape;
size_t esc_buflen;
};
/* ================================================ */
/* escape single-quote marks and backslashes so that the
* database SQL parser doesn't puke on the query string
*/
const char *
sqlEscapeString (sqlEscape *b, const char *str)
{
const char *p, *src_head;
char *dst_tail;
size_t len, slen;
ENTER("str = %s", str);
if (!b || !str) { LEAVE("(null) args"); return NULL; }
/* if a string is escaped twice, just return the first */
if (b->escape == str) {
LEAVE("%s: already escaped", str);
return str;
}
/* if nothing to escape, just return */
len = strlen (str);
slen = strcspn (str, "\\\'");
if (len == slen) {
LEAVE("nothing to escape");
return str;
}
/* count to see how much space we'll need */
p = str + slen + 1;
while (*p)
{
len ++;
p += 1 + strcspn (p, "\\\'");
}
/* get more space, if needed */
if (len >= b->esc_buflen)
{
b->escape = g_realloc(b->escape, len+100);
b->esc_buflen = len+100;
}
/* copy and escape */
src_head = (char *) str;
dst_tail = b->escape;
p = src_head + strcspn (src_head, "\\\'");
while (*p)
{
size_t cp_len = p - src_head;
strncpy (dst_tail, src_head, cp_len);
dst_tail += cp_len;
*dst_tail = '\\';
dst_tail ++;
*dst_tail = *p;
dst_tail ++;
src_head = p+1;
p = src_head + strcspn (src_head, "\\\'");
}
if (p != src_head)
{
size_t cp_len = p - src_head;
strncpy (dst_tail, src_head, cp_len);
dst_tail += cp_len;
}
*dst_tail = 0;
LEAVE("b->escape = %s", b->escape);
return b->escape;
}
/* ================================================ */
#define INITIAL_BUFSZ 2000
sqlEscape *
sqlEscape_new (void)
{
sqlEscape *b = g_new (sqlEscape, 1);
b->escape = g_malloc (INITIAL_BUFSZ);
b->esc_buflen = INITIAL_BUFSZ;
return (b);
}
/* ================================================ */
void
sqlEscape_destroy (sqlEscape *b)
{
ENTER(" ");
if (!b) { LEAVE("b is (null)"); return; }
g_free (b->escape); b->escape = NULL;
g_free (b);
LEAVE(" ");
}
/* ================ END OF FILE ==================== */

45
src/backend/sql/escape.h Normal file
View File

@ -0,0 +1,45 @@
/********************************************************************\
* escape.h : Escape SQL reserved characters in strings *
* Copyright (C) 2001 Linas Vepstas <linas@linas.org> *
* *
* 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:
* escape.h
*
* FUNCTION:
* Escape SQL reserved characters \ and ' from strings
*
* HISTORY:
* Linas Vepstas January 2001
*/
#ifndef SQL_ESCAPE_H
#define SQL_ESCAPE_H
typedef struct _escape sqlEscape;
sqlEscape * sqlEscape_new(void);
void sqlEscape_destroy (sqlEscape *);
const char * sqlEscapeString (sqlEscape *, const char *orig_string);
#endif /* SQL_ESCAPE_H */

View File

@ -562,15 +562,8 @@ gnc_sql_commit_edit( GncSqlBackend *be, QofInstance *inst )
/* ---------------------------------------------------------------------- */
/* Query processing */
#if 0
static const gchar*
convert_search_obj( QofIdType objType )
{
return (gchar*)objType;
}
static void
handle_and_term( QofQueryTerm* pTerm, gchar* sql )
handle_and_term( QofQueryTerm* pTerm, GString* sql )
{
GSList* pParamPath;
QofQueryPredData* pPredData;
@ -585,81 +578,100 @@ handle_and_term( QofQueryTerm* pTerm, gchar* sql )
pPredData = qof_query_term_get_pred_data( pTerm );
isInverted = qof_query_term_is_inverted( pTerm );
strcat( sql, "(" );
if( strcmp( pPredData->type_name, QOF_TYPE_GUID ) == 0 ) {
query_guid_t guid_data = (query_guid_t)pPredData;
GList* guid_entry;
for( name = pParamPath; name != NULL; name = name->next ) {
if( name != pParamPath ) g_string_append( sql, "." );
g_string_append( sql, name->data );
}
if( guid_data->options == QOF_GUID_MATCH_ANY ) {
if( isInverted ) g_string_append( sql, " NOT " );
g_string_append( sql, " IN (" );
}
for( guid_entry = guid_data->guids; guid_entry != NULL; guid_entry = guid_entry->next ) {
if( guid_entry != guid_data->guids ) g_string_append( sql, "." );
(void)guid_to_string_buff( guid_entry->data, val );
g_string_append( sql, "'" );
g_string_append( sql, val );
g_string_append( sql, "'" );
}
if( guid_data->options == QOF_GUID_MATCH_ANY ) {
g_string_append( sql, ")" );
}
}
g_string_append( sql, "(" );
if( isInverted ) {
strcat( sql, "!" );
g_string_append( sql, "!" );
}
for( name = pParamPath; name != NULL; name = name->next ) {
if( name != pParamPath ) strcat( sql, "." );
strcat( sql, name->data );
if( name != pParamPath ) g_string_append( sql, "." );
g_string_append( sql, name->data );
}
if( pPredData->how == QOF_COMPARE_LT ) {
strcat( sql, "<" );
g_string_append( sql, "<" );
} else if( pPredData->how == QOF_COMPARE_LTE ) {
strcat( sql, "<=" );
g_string_append( sql, "<=" );
} else if( pPredData->how == QOF_COMPARE_EQUAL ) {
strcat( sql, "=" );
g_string_append( sql, "=" );
} else if( pPredData->how == QOF_COMPARE_GT ) {
strcat( sql, ">" );
g_string_append( sql, ">" );
} else if( pPredData->how == QOF_COMPARE_GTE ) {
strcat( sql, ">=" );
g_string_append( sql, ">=" );
} else if( pPredData->how == QOF_COMPARE_NEQ ) {
strcat( sql, "~=" );
g_string_append( sql, "~=" );
} else {
strcat( sql, "??" );
g_string_append( sql, "??" );
}
if( strcmp( pPredData->type_name, "string" ) == 0 ) {
query_string_t pData = (query_string_t)pPredData;
strcat( sql, "'" );
strcat( sql, pData->matchstring );
strcat( sql, "'" );
g_string_append( sql, "'" );
g_string_append( sql, pData->matchstring );
g_string_append( 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, "'" );
g_string_append( sql, "'" );
//g_string_append( sql, val, 4+1+2+1+2 );
g_string_append( 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, "'" );
g_string_append( sql, "numeric" );
} else if( strcmp( pPredData->type_name, QOF_TYPE_GUID ) == 0 ) {
} else if( strcmp( pPredData->type_name, "gint32" ) == 0 ) {
query_int32_t pData = (query_int32_t)pPredData;
sprintf( val, "%d", pData->val );
strcat( sql, val );
g_string_append( 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 );
g_string_append( 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 );
g_string_append( 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 );
g_string_append( sql, val );
} else {
g_assert( FALSE );
}
strcat( sql, ")" );
g_string_append( sql, ")" );
}
#endif
static void
compile_query_cb( const gchar* type, gpointer data_p, gpointer be_data_p )
@ -682,6 +694,8 @@ compile_query_cb( const gchar* type, gpointer data_p, gpointer be_data_p )
}
}
gchar* gnc_sql_compile_query_to_sql( GncSqlBackend* be, QofQuery* query );
/*@ null @*/ gpointer
gnc_sql_compile_query( QofBackend* pBEnd, QofQuery* pQuery )
{
@ -695,6 +709,7 @@ gnc_sql_compile_query( QofBackend* pBEnd, QofQuery* pQuery )
ENTER( " " );
//gnc_sql_compile_query_to_sql( be, pQuery );
searchObj = qof_query_get_search_for( pQuery );
pQueryInfo = g_malloc( (gsize)sizeof( gnc_sql_query_info ) );
@ -714,42 +729,60 @@ gnc_sql_compile_query( QofBackend* pBEnd, QofQuery* pQuery )
return be_data.pQueryInfo;
}
#if 0
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 );
#endif
LEAVE( "" );
return pQueryInfo;
}
static const gchar*
convert_search_obj( QofIdType objType )
{
return (gchar*)objType;
}
gchar*
gnc_sql_compile_query_to_sql( GncSqlBackend* be, QofQuery* query )
{
QofIdType searchObj;
GList* bookList;
GString* sql;
g_return_val_if_fail( be != NULL, NULL );
g_return_val_if_fail( query != NULL, NULL );
searchObj = qof_query_get_search_for( query );
bookList = qof_query_get_books( query );
/* Convert search object type to table name */
sql = g_string_new( "" );
g_string_append( sql, "SELECT * FROM " );
g_string_append( sql, convert_search_obj( searchObj ) );
if( !qof_query_has_terms( query ) ) {
g_string_append( sql, ";" );
} else {
GList* orterms = qof_query_get_terms( query );
GList* orTerm;
g_string_append( sql, " WHERE " );
for( orTerm = orterms; orTerm != NULL; orTerm = orTerm->next ) {
GList* andterms = (GList*)orTerm->data;
GList* andTerm;
if( orTerm != orterms ) g_string_append( sql, " OR " );
g_string_append( sql, "(" );
for( andTerm = andterms; andTerm != NULL; andTerm = andTerm->next ) {
if( andTerm != andterms ) g_string_append( sql, " AND " );
handle_and_term( (QofQueryTerm*)andTerm->data, sql );
}
g_string_append( sql, ")" );
}
}
DEBUG( "Compiled: %s\n", sql->str );
return g_string_free( sql, FALSE );
}
static void
free_query_cb( const gchar* type, gpointer data_p, gpointer be_data_p )
{
@ -1520,6 +1553,25 @@ 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)
gchar*
gnc_sql_convert_timespec_to_string( Timespec ts )
{
time_t time;
struct tm* tm;
gint year;
gchar* datebuf;
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 );
return datebuf;
}
static void
load_timespec( const GncSqlBackend* be, GncSqlRow* row,
/*@ null @*/ QofSetterFunc setter, gpointer pObject,
@ -1582,9 +1634,6 @@ add_gvalue_timespec_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name,
TimespecAccessFunc ts_getter;
Timespec ts;
gchar* datebuf;
time_t time;
struct tm* tm;
gint year;
GValue* value;
g_return_if_fail( be != NULL );
@ -1597,14 +1646,7 @@ add_gvalue_timespec_to_slist( const GncSqlBackend* be, QofIdTypeConst obj_name,
g_return_if_fail( ts_getter != NULL );
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 );
datebuf = gnc_sql_convert_timespec_to_string( ts );
value = g_new0( GValue, 1 );
g_assert( value != NULL );
(void)g_value_init( value, G_TYPE_STRING );

View File

@ -678,6 +678,14 @@ gboolean gnc_sql_commit_standard_item( GncSqlBackend* be, QofInstance* inst, con
*/
gint64 gnc_sql_get_integer_value( const GValue* value );
/**
* Converts a Timespec value to a string value for the database.
*
* @param ts Timespec to be converted
* @return String representation of the Timespec
*/
gchar* gnc_sql_convert_timespec_to_string( Timespec ts );
/**
* Upgrades a table to a new structure. The upgrade is done by creating a new table with
* the new structure, SELECTing the old data into the new table, deleting the old table,

View File

@ -47,6 +47,8 @@
#include "gnc-engine.h"
#include "escape.h"
#ifdef S_SPLINT_S
#include "splint-defs.h"
#endif
@ -722,42 +724,316 @@ void gnc_sql_transaction_load_all_tx( GncSqlBackend* be )
}
}
static void
convert_query_comparison_to_sql( QofQueryPredData* pPredData, gboolean isInverted, GString* sql )
{
if( pPredData->how == QOF_COMPARE_LT
|| ( isInverted && pPredData->how == QOF_COMPARE_GTE ) ) {
g_string_append( sql, "<" );
} else if( pPredData->how == QOF_COMPARE_LTE
|| ( isInverted && pPredData->how == QOF_COMPARE_GT ) ) {
g_string_append( sql, "<=" );
} else if( pPredData->how == QOF_COMPARE_EQUAL
|| ( isInverted && pPredData->how == QOF_COMPARE_NEQ ) ) {
g_string_append( sql, "=" );
} else if( pPredData->how == QOF_COMPARE_GT
|| ( isInverted && pPredData->how == QOF_COMPARE_LTE ) ) {
g_string_append( sql, ">" );
} else if( pPredData->how == QOF_COMPARE_GTE
|| ( isInverted && pPredData->how == QOF_COMPARE_LT ) ) {
g_string_append( sql, ">=" );
} else if( pPredData->how == QOF_COMPARE_NEQ
|| ( isInverted && pPredData->how == QOF_COMPARE_EQUAL ) ) {
g_string_append( sql, "~=" );
} else {
PERR( "Unknown comparison type\n" );
g_string_append( sql, "??" );
}
}
static void
convert_query_term_to_sql( const gchar* fieldName, QofQueryTerm* pTerm, GString* 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 );
if( safe_strcmp( pPredData->type_name, QOF_TYPE_GUID ) == 0 ) {
query_guid_t guid_data = (query_guid_t)pPredData;
GList* guid_entry;
g_string_append( sql, "(" );
g_string_append( sql, fieldName );
switch( guid_data->options ) {
case QOF_GUID_MATCH_ANY:
if( isInverted ) g_string_append( sql, " NOT IN (" );
else g_string_append( sql, " IN (" );
break;
case QOF_GUID_MATCH_NONE:
if( isInverted ) g_string_append( sql, " IN (" );
else g_string_append( sql, " NOT IN (" );
break;
default:
PERR( "Unexpected GUID match type: %d\n", guid_data->options );
}
for( guid_entry = guid_data->guids; guid_entry != NULL; guid_entry = guid_entry->next ) {
if( guid_entry != guid_data->guids ) g_string_append( sql, "," );
(void)guid_to_string_buff( guid_entry->data, val );
g_string_append( sql, "'" );
g_string_append( sql, val );
g_string_append( sql, "'" );
break;
}
g_string_append( sql, ")" );
g_string_append( sql, ")" );
return;
} else if( safe_strcmp( pPredData->type_name, QOF_TYPE_CHAR ) == 0 ) {
query_char_t char_data = (query_char_t)pPredData;
int i;
if( isInverted ) {
g_string_append( sql, "NOT(" );
}
if( char_data->options == QOF_CHAR_MATCH_NONE ) {
g_string_append( sql, "NOT " );
}
g_string_append( sql, "(" );
for( i = 0; char_data->char_list[i] != '\0'; i++ ) {
if( i != 0 ) {
g_string_append( sql, " OR " );
}
g_string_append( sql, fieldName );
g_string_append( sql, " = '" );
g_string_append_c( sql, char_data->char_list[i] );
g_string_append( sql, "'" );
}
g_string_append( sql, ") " );
if( isInverted ) {
g_string_append( sql, ") " );
}
return;
} else if( safe_strcmp( pPredData->type_name, QOF_TYPE_DATE ) == 0 ) {
query_date_t date_data = (query_date_t)pPredData;
gchar* datebuf;
g_string_append( sql, "(" );
g_string_append( sql, fieldName );
convert_query_comparison_to_sql( pPredData, isInverted, sql );
datebuf = gnc_sql_convert_timespec_to_string( date_data->date );
g_string_append( sql, "'" );
g_string_append( sql, datebuf );
g_string_append( sql, "')" );
return;
} else if( safe_strcmp( pPredData->type_name, QOF_TYPE_STRING ) == 0 ) {
query_string_t string_data = (query_string_t)pPredData;
sqlEscape* escape = sqlEscape_new();
if( isInverted ) {
g_string_append( sql, "NOT(" );
}
if( pPredData->how == QOF_COMPARE_NEQ ) {
g_string_append( sql, "NOT(" );
}
g_string_append( sql, fieldName );
if( string_data->is_regex || string_data->options == QOF_STRING_MATCH_CASEINSENSITIVE ) {
PWARN( "String is_regex || option = QOF_STRING_MATCH_INSENSITIVE\n" );
}
// g_string_append( sql, " ~" );
// } else {
g_string_append( sql, " =" );
// }
// if( string_data->options == QOF_STRING_MATCH_CASEINSENSITIVE ) {
// g_string_append( sql, "*" );
// }
g_string_append( sql, "'" );
g_string_append( sql, sqlEscapeString( escape, string_data->matchstring ) );
g_string_append( sql, "'" );
if( pPredData->how == QOF_COMPARE_NEQ ) {
g_string_append( sql, ")" );
}
if( isInverted ) {
g_string_append( sql, ")" );
}
sqlEscape_destroy( escape );
return;
}
g_string_append( sql, "(" );
g_string_append( sql, fieldName );
convert_query_comparison_to_sql( pPredData, isInverted, sql );
if( strcmp( pPredData->type_name, "numeric" ) == 0 ) {
query_numeric_t pData = (query_numeric_t)pPredData;
g_string_append( sql, "numeric" );
} else if( strcmp( pPredData->type_name, "gint32" ) == 0 ) {
query_int32_t pData = (query_int32_t)pPredData;
sprintf( val, "%d", pData->val );
g_string_append( 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 );
g_string_append( sql, val );
} else if( strcmp( pPredData->type_name, "double" ) == 0 ) {
query_double_t pData = (query_double_t)pPredData;
sprintf( val, "%f", pData->val );
g_string_append( sql, val );
} else if( strcmp( pPredData->type_name, "boolean" ) == 0 ) {
query_boolean_t pData = (query_boolean_t)pPredData;
sprintf( val, "%d", pData->val );
g_string_append( sql, val );
} else {
PERR( "Unknown query predicate type: %s\n", pPredData->type_name );
}
g_string_append( sql, ")" );
}
typedef struct {
GncSqlStatement* stmt;
Account* acct;
gboolean has_been_run;
} split_query_info_t;
static /*@ null @*/ gpointer
compile_split_query( GncSqlBackend* be, QofQuery* pQuery )
compile_split_query( GncSqlBackend* be, QofQuery* query )
{
const GUID* acct_guid;
gchar guid_buf[GUID_ENCODING_LENGTH+1];
split_query_info_t* query_info = NULL;
gchar* subquery_sql;
gchar* query_sql;
GString* subquery_tables;
gboolean has_transactions_table = FALSE;
g_return_val_if_fail( be != NULL, NULL );
g_return_val_if_fail( pQuery != NULL, NULL );
g_return_val_if_fail( query != NULL, NULL );
acct_guid = get_guid_from_query( pQuery );
if( acct_guid != NULL ) {
(void)guid_to_string_buff( acct_guid, guid_buf );
subquery_sql = g_strdup_printf( "SELECT DISTINCT tx_guid FROM %s WHERE account_guid='%s'", SPLIT_TABLE, guid_buf );
query_sql = g_strdup_printf( "SELECT * FROM %s WHERE guid IN (%s)", TRANSACTION_TABLE, subquery_sql );
query_info = g_malloc( (gsize)sizeof(split_query_info_t) );
g_assert( query_info != NULL );
query_info->has_been_run = FALSE;
query_info = g_malloc( (gsize)sizeof(split_query_info_t) );
g_assert( query_info != NULL );
subquery_tables = g_string_new( SPLIT_TABLE );
if( qof_query_has_terms( query ) ) {
GList* orterms = qof_query_get_terms( query );
GList* orTerm;
GString* sql = g_string_new( "" );
gboolean need_OR = FALSE;
for( orTerm = orterms; orTerm != NULL; orTerm = orTerm->next ) {
GList* andterms = (GList*)orTerm->data;
GList* andTerm;
gboolean need_AND = FALSE;
gboolean has_tx_guid_check = FALSE;
if( need_OR ) {
g_string_append( sql, " OR " );
}
g_string_append( sql, "(" );
for( andTerm = andterms; andTerm != NULL; andTerm = andTerm->next ) {
QofQueryTerm* term;
GSList* paramPath;
gboolean unknownPath = FALSE;
term = (QofQueryTerm*)andTerm->data;
paramPath = qof_query_term_get_param_path( term );
if( strcmp( paramPath->data, QOF_PARAM_BOOK ) == 0 ) continue;
if( need_AND ) g_string_append( sql, " AND " );
if( strcmp( paramPath->data, SPLIT_ACCOUNT ) == 0
&& strcmp( paramPath->next->data, QOF_PARAM_GUID ) == 0 ) {
convert_query_term_to_sql( "account_guid", term, sql );
} else if( strcmp( paramPath->data, SPLIT_RECONCILE ) == 0 ) {
convert_query_term_to_sql( "reconcile_state", term, sql );
} else if( strcmp( paramPath->data, SPLIT_TRANS ) == 0 ) {
if( !has_transactions_table ) {
g_string_append( subquery_tables, ", " );
g_string_append( subquery_tables, TRANSACTION_TABLE );
has_transactions_table = TRUE;
}
if( !has_tx_guid_check ) {
g_string_append( sql, "(splits.tx_guid = transactions.guid) AND " );
has_tx_guid_check = TRUE;
}
if( strcmp( paramPath->next->data, TRANS_DATE_POSTED ) == 0 ) {
convert_query_term_to_sql( "transactions.post_date", term, sql );
} else if( strcmp( paramPath->next->data, TRANS_DESCRIPTION ) == 0 ) {
convert_query_term_to_sql( "transactions.description", term, sql );
} else {
unknownPath = TRUE;
}
} else {
unknownPath = TRUE;
}
if( unknownPath ) {
GString* name = g_string_new( (gchar*)paramPath->data );
while( paramPath->next != NULL ) {
g_string_append( name, "." );
g_string_append( name, paramPath->next->data );
paramPath = paramPath->next;
}
PERR( "Unknown SPLIT query field: %s\n", name->str );
g_string_free( name, TRUE );
}
need_AND = TRUE;
}
/* If the last char in the string is a '(', then for some reason, there were
no terms added to the SQL. If so, remove it and ignore the OR term. */
if( sql->str[sql->len-1] == '(' ) {
g_string_truncate( sql, sql->len-1 );
need_OR = FALSE;
} else {
g_string_append( sql, ")" );
need_OR = TRUE;
}
}
if( sql->len != 0 ) {
subquery_sql = g_strdup_printf( "SELECT DISTINCT tx_guid FROM %s WHERE %s",
g_string_free( subquery_tables, FALSE ),
sql->str );
query_sql = g_strdup_printf( "SELECT * FROM %s WHERE guid IN (%s)",
TRANSACTION_TABLE, subquery_sql );
g_free( subquery_sql );
} else {
query_sql = g_strdup_printf( "SELECT * FROM %s", TRANSACTION_TABLE );
}
query_info->stmt = gnc_sql_create_statement_from_sql( be, query_sql );
g_string_free( sql, TRUE );
g_free( query_sql );
} else {
query_sql = g_strdup_printf( "SELECT * FROM %s", TRANSACTION_TABLE );
query_info->stmt = gnc_sql_create_statement_from_sql( be, query_sql );
g_free( query_sql );
query_info->has_been_run = FALSE;
query_info->acct = xaccAccountLookup( acct_guid, be->primary_book );
if( query_info->acct == NULL ) {
PWARN( "Unable to find account with guid='%s'\n", guid_buf );
}
g_free( subquery_sql );
}
return query_info;
@ -770,16 +1046,12 @@ run_split_query( GncSqlBackend* be, gpointer pQuery )
g_return_if_fail( be != NULL );
g_return_if_fail( pQuery != NULL );
g_return_if_fail( query_info->acct != NULL );
// When the query to load all splits for the account has been run, set the
// mark so that this account's query is not reexecuted.
if( !query_info->has_been_run && xaccAccountGetMark( query_info->acct ) == 0 ) {
if( !query_info->has_been_run ) {
query_transactions( be, query_info->stmt );
query_info->has_been_run = TRUE;
gnc_sql_statement_dispose( query_info->stmt );
query_info->stmt = NULL;
xaccAccountSetMark( query_info->acct, 1 );
}
}