From c8e30f6b6b172d4c4a8ec6aefa9378b619f7cb83 Mon Sep 17 00:00:00 2001 From: Phil Longstaff Date: Sat, 20 Dec 2008 03:06:17 +0000 Subject: [PATCH] =?UTF-8?q?Fix=20Bug=20559772=20=E2=80=93=20SQL=20backend?= =?UTF-8?q?=20not=20non-ascii-safe?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Sqlite3 uses utf8 encoding for all char fields, so it is non-ascii-safe. For postgresql, the default encoding can be set on a per-db basis. Since the database is not created by gnucash (the tables are, but not the database), it is for the user to set utf8 encoding when the database is created. For mysql, a default encoding can be set on a per-db, per-table or per-field basis. Since there are char fields which do not need to be utf8 (e.g. guids), encoding is set on a per-field basis. git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@17780 57a11ea4-9604-0410-9ed3-97b8803252fd --- src/backend/dbi/gnc-backend-dbi.c | 221 ++++++++++++++---- src/backend/sql/gnc-backend-sql.c | 42 ++-- src/backend/sql/gnc-backend-sql.h | 11 +- .../business-core/sql/gnc-address-sql.c | 6 +- .../business-core/sql/gnc-owner-sql.c | 8 +- 5 files changed, 206 insertions(+), 82 deletions(-) diff --git a/src/backend/dbi/gnc-backend-dbi.c b/src/backend/dbi/gnc-backend-dbi.c index 36d0e9a8ae..cce05bad11 100644 --- a/src/backend/dbi/gnc-backend-dbi.c +++ b/src/backend/dbi/gnc-backend-dbi.c @@ -54,11 +54,43 @@ static QofLogModule log_module = G_LOG_DOMAIN; -static GncSqlConnection* create_dbi_connection( gint provider, dbi_conn conn ); +typedef gchar* (*CREATE_TABLE_DDL_FN)( GncSqlConnection* conn, + const gchar* table_name, + const GList* col_info_list ); +typedef struct { + CREATE_TABLE_DDL_FN create_table_ddl; +} provider_functions_t; -#define GNC_DBI_PROVIDER_SQLITE 0 -#define GNC_DBI_PROVIDER_MYSQL 1 -#define GNC_DBI_PROVIDER_PGSQL 2 +static gchar* conn_create_table_ddl_sqlite3( GncSqlConnection* conn, + const gchar* table_name, + const GList* col_info_list ); + +static provider_functions_t provider_sqlite3 = +{ + conn_create_table_ddl_sqlite3 +}; + +static gchar* conn_create_table_ddl_mysql( GncSqlConnection* conn, + const gchar* table_name, + const GList* col_info_list ); +static provider_functions_t provider_mysql = +{ + conn_create_table_ddl_mysql +}; + +static gchar* conn_create_table_ddl_pgsql( GncSqlConnection* conn, + const gchar* table_name, + const GList* col_info_list ); +static provider_functions_t provider_pgsql = +{ + conn_create_table_ddl_pgsql +}; + +static GncSqlConnection* create_dbi_connection( provider_functions_t* provider, dbi_conn conn ); + +#define GNC_DBI_PROVIDER_SQLITE (&provider_sqlite3) +#define GNC_DBI_PROVIDER_MYSQL (&provider_mysql) +#define GNC_DBI_PROVIDER_PGSQL (&provider_pgsql) struct GncDbiBackend_struct { @@ -825,7 +857,7 @@ typedef struct GncSqlConnection base; dbi_conn conn; - gint provider; + provider_functions_t* provider; } GncDbiSqlConnection; static void @@ -992,46 +1024,24 @@ conn_get_column_type_name( GncSqlConnection* conn, GType type, gint size ) return ""; } } else { - PERR( "Unknown provider: %d\n", dbi_conn->provider ); + PERR( "Unknown provider type\n" ); return ""; } } -static void -add_table_column( GString* ddl, const GncSqlColumnInfo* info ) +static gchar* +conn_create_table_ddl_sqlite3( GncSqlConnection* conn, + const gchar* table_name, + const GList* col_info_list ) { - 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 gboolean -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; + gchar* ddl_result; - g_return_val_if_fail( conn != NULL, FALSE ); - g_return_val_if_fail( table_name != NULL, FALSE ); - g_return_val_if_fail( col_info_list != NULL, FALSE ); + g_return_val_if_fail( conn != NULL, NULL ); + g_return_val_if_fail( table_name != NULL, NULL ); + g_return_val_if_fail( col_info_list != NULL, NULL ); ddl = g_string_new( "" ); g_string_printf( ddl, "CREATE TABLE %s (", table_name ); @@ -1042,14 +1052,141 @@ conn_create_table( GncSqlConnection* conn, const gchar* table_name, if( col_num != 0 ) { g_string_append( ddl, ", " ); } - add_table_column( ddl, info ); + g_string_append_printf( ddl, "%s %s", info->name, + gnc_sql_connection_get_column_type_name( conn, info->type, info->size ) ); + 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" ); + } } 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 ); + ddl_result = ddl->str; + g_string_free( ddl, FALSE ); + + return ddl_result; +} + +static gchar* +conn_create_table_ddl_mysql( GncSqlConnection* conn, const gchar* table_name, + const GList* col_info_list ) +{ + GString* ddl; + const GList* list_node; + guint col_num; + gchar* ddl_result; + + g_return_val_if_fail( conn != NULL, NULL ); + g_return_val_if_fail( table_name != NULL, NULL ); + g_return_val_if_fail( col_info_list != NULL, 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, ", " ); + } + g_string_append_printf( ddl, "%s %s", info->name, + gnc_sql_connection_get_column_type_name( conn, info->type, info->size ) ); + if( info->size != 0 ) { + g_string_append_printf( ddl, "(%d)", info->size ); + } + if( info->is_unicode ) { + g_string_append( ddl, " CHARACTER SET utf8" ); + } + if( info->is_primary_key ) { + g_string_append( ddl, " PRIMARY KEY" ); + } + if( !info->null_allowed ) { + g_string_append( ddl, " NOT NULL" ); + } + } + g_string_append( ddl, ")" ); + + ddl_result = ddl->str; + g_string_free( ddl, FALSE ); + + return ddl_result; +} + +static gchar* +conn_create_table_ddl_pgsql( GncSqlConnection* conn, const gchar* table_name, + const GList* col_info_list ) +{ + GString* ddl; + const GList* list_node; + guint col_num; + gchar* ddl_result; + gboolean is_unicode = FALSE; + + g_return_val_if_fail( conn != NULL, NULL ); + g_return_val_if_fail( table_name != NULL, NULL ); + g_return_val_if_fail( col_info_list != NULL, 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, ", " ); + } + g_string_append_printf( ddl, "%s %s", info->name, + gnc_sql_connection_get_column_type_name( conn, info->type, info->size ) ); + 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" ); + } + is_unicode = is_unicode || info->is_unicode; + } + g_string_append( ddl, ")" ); + if( is_unicode ) { + } + + ddl_result = ddl->str; + g_string_free( ddl, FALSE ); + + return ddl_result; +} + +static gboolean +conn_create_table( GncSqlConnection* conn, const gchar* table_name, + const GList* col_info_list ) +{ + GncDbiSqlConnection* dbi_conn = (GncDbiSqlConnection*)conn; + gchar* ddl; + const GList* list_node; + guint col_num; + dbi_result result; + + g_return_val_if_fail( conn != NULL, FALSE ); + g_return_val_if_fail( table_name != NULL, FALSE ); + g_return_val_if_fail( col_info_list != NULL, FALSE ); + + + ddl = dbi_conn->provider->create_table_ddl( conn, table_name, + col_info_list ); + if( ddl != NULL ) { + DEBUG( "SQL: %s\n", ddl ); + result = dbi_conn_query( dbi_conn->conn, ddl ); + dbi_result_free( result ); + } else { + return FALSE; + } return TRUE; } @@ -1156,7 +1293,7 @@ conn_quote_string( const GncSqlConnection* conn, gchar* unquoted_str ) } static GncSqlConnection* -create_dbi_connection( gint provider, dbi_conn conn ) +create_dbi_connection( provider_functions_t* provider, dbi_conn conn ) { GncDbiSqlConnection* dbi_conn; diff --git a/src/backend/sql/gnc-backend-sql.c b/src/backend/sql/gnc-backend-sql.c index 0c7d28b306..efeac84dc6 100644 --- a/src/backend/sql/gnc-backend-sql.c +++ b/src/backend/sql/gnc-backend-sql.c @@ -941,17 +941,18 @@ gnc_sql_add_subtable_colnames_to_list( const GncSqlColumnTableEntry* table_row, } static GncSqlColumnInfo* -create_column_info( const GncSqlColumnTableEntry* table_row, const gchar* type, - gint size ) +create_column_info( const GncSqlColumnTableEntry* table_row, GType type, + gint size, gboolean is_unicode ) { GncSqlColumnInfo* info; info = g_new0( GncSqlColumnInfo, 1 ); info->name = table_row->col_name; - info->type_name = type; + info->type = 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; + info->is_unicode = is_unicode; return info; } @@ -993,10 +994,7 @@ add_string_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEnt 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 ); + info = create_column_info( table_row, G_TYPE_STRING, table_row->size, TRUE ); *pList = g_list_append( *pList, info ); } @@ -1078,10 +1076,7 @@ add_int_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry* 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 ); + info = create_column_info( table_row, G_TYPE_INT, 0, FALSE ); *pList = g_list_append( *pList, info ); } @@ -1161,10 +1156,7 @@ add_boolean_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEn 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 ); + info = create_column_info( table_row, G_TYPE_INT, 0, FALSE ); *pList = g_list_append( *pList, info ); } @@ -1238,10 +1230,7 @@ add_int64_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntr 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 ); + info = create_column_info( table_row, G_TYPE_INT64, 0, FALSE ); *pList = g_list_append( *pList, info ); } @@ -1316,10 +1305,7 @@ add_double_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEnt 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 ); + info = create_column_info( table_row, G_TYPE_DOUBLE, 0, FALSE ); *pList = g_list_append( *pList, info ); } @@ -1398,7 +1384,7 @@ add_guid_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry g_return_if_fail( table_row != NULL ); g_return_if_fail( pList != NULL ); - info = create_column_info( table_row, "CHAR", GUID_ENCODING_LENGTH ); + info = create_column_info( table_row, G_TYPE_STRING, GUID_ENCODING_LENGTH, FALSE ); *pList = g_list_append( *pList, info ); } @@ -1539,7 +1525,7 @@ add_timespec_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableE g_return_if_fail( table_row != NULL ); g_return_if_fail( pList != NULL ); - info = create_column_info( table_row, "CHAR", TIMESPEC_COL_SIZE ); + info = create_column_info( table_row, G_TYPE_STRING, TIMESPEC_COL_SIZE, FALSE ); *pList = g_list_append( *pList, info ); } @@ -1642,7 +1628,7 @@ add_date_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntry g_return_if_fail( table_row != NULL ); g_return_if_fail( pList != NULL ); - info = create_column_info( table_row, "CHAR", DATE_COL_SIZE ); + info = create_column_info( table_row, G_TYPE_STRING, DATE_COL_SIZE, FALSE ); *pList = g_list_append( *pList, info ); } @@ -1747,10 +1733,10 @@ add_numeric_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEn 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->type = G_TYPE_INT64; info->is_primary_key = (table_row->flags & COL_PKEY) ? TRUE : FALSE; info->null_allowed = (table_row->flags & COL_NNUL) ? FALSE : TRUE; + info->is_unicode = FALSE; *pList = g_list_append( *pList, info ); } } diff --git a/src/backend/sql/gnc-backend-sql.h b/src/backend/sql/gnc-backend-sql.h index e122cb85a0..04be657840 100644 --- a/src/backend/sql/gnc-backend-sql.h +++ b/src/backend/sql/gnc-backend-sql.h @@ -280,11 +280,12 @@ typedef struct * a column in a table. */ 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; + const gchar* name; /**< Column name */ + GType type; /**< Column basic type */ + gint size; /**< Column size (string types) */ + gboolean is_unicode; /**< Column is unicode (string types) */ + gboolean is_primary_key; /**< Column is the primary key */ + gboolean null_allowed; /**< Column allows NULL values */ } GncSqlColumnInfo; // Type for conversion of db row to object. diff --git a/src/business/business-core/sql/gnc-address-sql.c b/src/business/business-core/sql/gnc-address-sql.c index e8fd3165d7..c0100b27b2 100644 --- a/src/business/business-core/sql/gnc-address-sql.c +++ b/src/business/business-core/sql/gnc-address-sql.c @@ -121,11 +121,11 @@ add_address_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEn 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->type = G_TYPE_STRING; info->size = subtable_row->size; + info->is_primary_key = (table_row->flags & COL_PKEY) ? TRUE : FALSE; info->null_allowed = (table_row->flags & COL_NNUL) ? FALSE : TRUE; + info->is_unicode = TRUE; *pList = g_list_append( *pList, info ); } } diff --git a/src/business/business-core/sql/gnc-owner-sql.c b/src/business/business-core/sql/gnc-owner-sql.c index bf1e54a3dd..7c6d7cda77 100644 --- a/src/business/business-core/sql/gnc-owner-sql.c +++ b/src/business/business-core/sql/gnc-owner-sql.c @@ -166,21 +166,21 @@ add_owner_col_info_to_list( const GncSqlBackend* be, const GncSqlColumnTableEntr 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->type = G_TYPE_INT; 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; + info->is_unicode = FALSE; *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->type = G_TYPE_STRING; 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; + info->is_unicode = FALSE; *pList = g_list_append( *pList, info ); }