Browse Source

Catch exceptions thrown from nanodbc::statement's d'tor.

Fixes https://gitlab.com/kicad/code/kicad/-/issues/21707
master
Jeff Young 1 month ago
parent
commit
260b0a9135
  1. 231
      common/database/database_connection.cpp

231
common/database/database_connection.cpp

@ -495,161 +495,172 @@ bool DATABASE_CONNECTION::SelectOne( const std::string& aTable,
bool DATABASE_CONNECTION::selectAllAndCache( const std::string& aTable, const std::string& aKey )
{
nanodbc::statement statement( *m_conn );
nanodbc::string query = fromUTF8( fmt::format( "SELECT {} FROM {}{}{}", columnsFor( aTable ),
m_quoteChar, aTable, m_quoteChar ) );
wxLogTrace( traceDatabase, wxT( "selectAllAndCache: `%s`" ), toUTF8( query ) );
PROF_TIMER timer;
try
{
statement.prepare( query );
}
catch( std::exception& e )
{
m_lastError = e.what();
wxLogTrace( traceDatabase,
wxT( "Exception while preparing query for selectAllAndCache: %s" ),
m_lastError );
nanodbc::statement statement( *m_conn );
// Exception may be due to a connection error; nanodbc won't auto-reconnect
Disconnect();
nanodbc::string query = fromUTF8( fmt::format( "SELECT {} FROM {}{}{}",
columnsFor( aTable ),
m_quoteChar, aTable, m_quoteChar ) );
return false;
}
wxLogTrace( traceDatabase, wxT( "selectAllAndCache: `%s`" ), toUTF8( query ) );
nanodbc::result results;
PROF_TIMER timer;
try
{
results = nanodbc::execute( statement );
}
catch( std::exception& e )
{
m_lastError = e.what();
wxLogTrace( traceDatabase,
wxT( "Exception while executing query for selectAllAndCache: %s" ),
m_lastError );
// Exception may be due to a connection error; nanodbc won't auto-reconnect
Disconnect();
return false;
}
try
{
statement.prepare( query );
}
catch( std::exception& e )
{
m_lastError = e.what();
wxLogTrace( traceDatabase,
wxT( "Exception while preparing query for selectAllAndCache: %s" ),
m_lastError );
timer.Stop();
// Exception may be due to a connection error; nanodbc won't auto-reconnect
Disconnect();
DB_CACHE_TYPE::CACHE_VALUE cacheEntry;
return false;
}
auto handleException =
[&]( std::runtime_error& aException, const std::string& aExtraContext = "" )
{
m_lastError = aException.what();
std::string extra = aExtraContext.empty() ? "" : ": " + aExtraContext;
wxLogTrace( traceDatabase,
wxT( "Exception while parsing result %d from selectAllAndCache: %s%s" ),
cacheEntry.size(), m_lastError, extra );
};
while( results.next() )
{
short columnCount = 0;
ROW result;
nanodbc::result results;
try
{
columnCount = results.columns();
results = nanodbc::execute( statement );
}
catch( nanodbc::database_error& e )
catch( std::exception& e )
{
handleException( e );
m_lastError = e.what();
wxLogTrace( traceDatabase,
wxT( "Exception while executing query for selectAllAndCache: %s" ),
m_lastError );
// Exception may be due to a connection error; nanodbc won't auto-reconnect
Disconnect();
return false;
}
for( short j = 0; j < columnCount; ++j )
timer.Stop();
DB_CACHE_TYPE::CACHE_VALUE cacheEntry;
auto handleException =
[&]( std::runtime_error& aException, const std::string& aExtraContext = "" )
{
m_lastError = aException.what();
std::string extra = aExtraContext.empty() ? "" : ": " + aExtraContext;
wxLogTrace( traceDatabase,
wxT( "Exception while parsing result %d from selectAllAndCache: %s%s" ),
cacheEntry.size(), m_lastError, extra );
};
while( results.next() )
{
std::string column;
std::string columnExtraDbgInfo;
int datatype = SQL_UNKNOWN_TYPE;
short columnCount = 0;
ROW result;
try
{
column = toUTF8( results.column_name( j ) );
datatype = results.column_datatype( j );
columnExtraDbgInfo = fmt::format( "column index {}, name '{}', type {}", j, column,
datatype );
columnCount = results.columns();
}
catch( nanodbc::index_range_error& e )
catch( nanodbc::database_error& e )
{
handleException( e, columnExtraDbgInfo );
handleException( e );
return false;
}
switch( datatype )
{
case SQL_DOUBLE:
case SQL_FLOAT:
case SQL_REAL:
case SQL_DECIMAL:
case SQL_NUMERIC:
for( short j = 0; j < columnCount; ++j )
{
std::string column;
std::string columnExtraDbgInfo;
int datatype = SQL_UNKNOWN_TYPE;
try
{
result[column] = fmt::format( "{:G}", results.get<double>( j ) );
}
catch( nanodbc::null_access_error& )
{
// Column was empty (null)
result[column] = std::string();
column = toUTF8( results.column_name( j ) );
datatype = results.column_datatype( j );
columnExtraDbgInfo = fmt::format( "column index {}, name '{}', type {}",
j,
column,
datatype );
}
catch( std::runtime_error& e )
catch( nanodbc::index_range_error& e )
{
handleException( e, columnExtraDbgInfo );
return false;
}
break;
}
default:
{
try
{
result[column] = toUTF8( results.get<nanodbc::string>( j,
NANODBC_TEXT( "" ) ) );
}
catch( std::runtime_error& e )
switch( datatype )
{
handleException( e, columnExtraDbgInfo );
return false;
case SQL_DOUBLE:
case SQL_FLOAT:
case SQL_REAL:
case SQL_DECIMAL:
case SQL_NUMERIC:
try
{
result[column] = fmt::format( "{:G}", results.get<double>( j ) );
}
catch( nanodbc::null_access_error& )
{
// Column was empty (null)
result[column] = std::string();
}
catch( std::runtime_error& e )
{
handleException( e, columnExtraDbgInfo );
return false;
}
break;
default:
try
{
result[column] = toUTF8( results.get<nanodbc::string>( j, NANODBC_TEXT( "" ) ) );
}
catch( std::runtime_error& e )
{
handleException( e, columnExtraDbgInfo );
return false;
}
}
}
if( !result.count( aKey ) )
{
wxLogTrace( traceDatabase,
wxT( "selectAllAndCache: warning: key %s not found in result set" ), aKey );
continue;
}
}
if( !result.count( aKey ) )
{
wxLogTrace( traceDatabase,
wxT( "selectAllAndCache: warning: key %s not found in result set" ), aKey );
continue;
std::string keyStr = std::any_cast<std::string>( result.at( aKey ) );
cacheEntry[keyStr] = result;
}
std::string keyStr = std::any_cast<std::string>( result.at( aKey ) );
cacheEntry[keyStr] = result;
wxLogTrace( traceDatabase, wxT( "selectAllAndCache from %s completed in %0.1f ms" ), aTable,
timer.msecs() );
m_cache->Put( aTable, cacheEntry );
return true;
}
catch( std::exception& e )
{
m_lastError = e.what();
wxLogTrace( traceDatabase, wxT( "Exception in selectAllAndCache: %s" ), m_lastError );
wxLogTrace( traceDatabase, wxT( "selectAllAndCache from %s completed in %0.1f ms" ), aTable,
timer.msecs() );
// Exception may be due to a connection error; nanodbc won't auto-reconnect
Disconnect();
m_cache->Put( aTable, cacheEntry );
return true;
return false;
}
}
bool DATABASE_CONNECTION::SelectAll( const std::string& aTable, const std::string& aKey,
std::vector<ROW>& aResults )
bool DATABASE_CONNECTION::SelectAll( const std::string& aTable, const std::string& aKey, std::vector<ROW>& aResults )
{
if( !m_conn )
{
@ -661,8 +672,7 @@ bool DATABASE_CONNECTION::SelectAll( const std::string& aTable, const std::strin
if( tableMapIter == m_tables.end() )
{
wxLogTrace( traceDatabase, wxT( "SelectAll: requested table %s not found in cache" ),
aTable );
wxLogTrace( traceDatabase, wxT( "SelectAll: requested table %s not found in cache" ), aTable );
return false;
}
@ -682,8 +692,7 @@ bool DATABASE_CONNECTION::SelectAll( const std::string& aTable, const std::strin
if( !m_cache->Get( aTable, cacheEntry ) )
{
wxLogTrace( traceDatabase, wxT( "SelectAll: `%s` failed to get results from cache!" ),
aTable );
wxLogTrace( traceDatabase, wxT( "SelectAll: `%s` failed to get results from cache!" ), aTable );
return false;
}

Loading…
Cancel
Save