mirror of
https://github.com/Gnucash/gnucash.git
synced 2025-02-25 18:55:30 -06:00
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:
parent
22a45587a9
commit
63ca792cfb
@ -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
150
src/backend/sql/escape.c
Normal 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
45
src/backend/sql/escape.h
Normal 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 */
|
||||
|
@ -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 );
|
||||
|
@ -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,
|
||||
|
@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user