211 const std::set<std::string>& aColumns )
218 nanodbc::catalog catalog( *
m_conn );
219 nanodbc::catalog::tables tables = catalog.find_tables(
fromUTF8( aTable ) );
223 wxLogTrace(
traceDatabase, wxT(
"CacheTableInfo: table '%s' not found in catalog" ),
228 std::string key =
toUTF8( tables.table_name() );
233 nanodbc::catalog::columns columns =
234 catalog.find_columns( NANODBC_TEXT(
"" ), tables.table_name() );
236 std::set<std::string> columnsInCatalog;
238 while( columns.next() )
240 std::string columnKey =
toUTF8( columns.column_name() );
241 std::string columnKeyLower = boost::to_lower_copy( columnKey );
243 if( aColumns.count( columnKeyLower ) )
246 columnsInCatalog.insert( columnKeyLower );
255 for(
const std::string& requestedCol : aColumns )
257 if( !columnsInCatalog.count( requestedCol ) && !requestedCol.empty() )
260 wxT(
"CacheTableInfo: column '%s' not found in catalog for table "
261 "'%s', adding anyway" ),
268 catch( nanodbc::database_error& e )
271 wxLogTrace(
traceDatabase, wxT(
"Exception while syncing columns for table '%s': %s" ),
276 catch( std::exception& e )
344 const std::pair<std::string, std::string>& aWhere,
349 wxLogTrace(
traceDatabase, wxT(
"Called SelectOne without valid connection!" ) );
353 auto tableMapIter =
m_tables.find( aTable );
355 if( tableMapIter ==
m_tables.end() )
357 wxLogTrace(
traceDatabase, wxT(
"SelectOne: requested table %s not found in cache" ),
362 const std::string& tableName = tableMapIter->first;
365 if(
m_cache->Get( tableName, cacheEntry ) )
367 if( cacheEntry.count( aWhere.second ) )
369 wxLogTrace(
traceDatabase, wxT(
"SelectOne: `%s` with parameter `%s` - cache hit" ),
370 tableName, aWhere.second );
371 aResult = cacheEntry.at( aWhere.second );
377 wxLogTrace(
traceDatabase, wxT(
"SelectOne: table `%s` not in row cache; will SelectAll" ),
378 tableName, aWhere.second );
382 if(
m_cache->Get( tableName, cacheEntry ) )
384 if( cacheEntry.count( aWhere.second ) )
386 wxLogTrace(
traceDatabase, wxT(
"SelectOne: `%s` with parameter `%s` - cache hit" ),
387 tableName, aWhere.second );
388 aResult = cacheEntry.at( aWhere.second );
396 wxLogTrace(
traceDatabase, wxT(
"SelectOne: requested table %s missing from column cache" ),
401 auto columnCacheIter =
m_columnCache.at( tableName ).find( aWhere.first );
405 wxLogTrace(
traceDatabase, wxT(
"SelectOne: requested column %s not found in cache for %s" ),
406 aWhere.first, tableName );
410 const std::string& columnName = columnCacheIter->first;
412 std::string cacheKey = fmt::format(
"{}{}{}", tableName, columnName, aWhere.second );
414 std::string queryStr = fmt::format(
"SELECT {} FROM {}{}{} WHERE {}{}{} = ?",
418 nanodbc::string query =
fromUTF8( queryStr );
421 nanodbc::statement statement;
425 statement.prepare( *
m_conn, query );
427 catch( std::exception& e )
430 wxLogTrace(
traceDatabase, wxT(
"Exception while preparing statement for SelectOne: %s" ),
443 statement.describe_parameters( { 0 }, { SQL_VARCHAR }, { 255 }, { 0 } );
444 statement.bind( 0, aWhere.second.c_str() );
446 catch( std::exception& e )
449 wxLogTrace(
traceDatabase, wxT(
"Exception while binding parameter for SelectOne: %s" ),
460 nanodbc::result results;
464 results = nanodbc::execute( statement );
466 catch( std::exception& e )
469 wxLogTrace(
traceDatabase, wxT(
"Exception while executing statement for SelectOne: %s" ),
481 if( !results.first() )
483 wxLogTrace(
traceDatabase, wxT(
"SelectOne: no results returned from query" ) );
487 wxLogTrace(
traceDatabase, wxT(
"SelectOne: %ld results returned from query in %0.1f ms" ),
488 results.rows(), timer.
msecs() );
494 for(
short i = 0; i < results.columns(); ++i )
496 std::string column =
toUTF8( results.column_name( i ) );
498 switch( results.column_datatype( i ) )
508 aResult[column] = fmt::format(
"{:G}", results.get<
double>( i ) );
510 catch( nanodbc::null_access_error& )
513 aResult[column] = std::string();
520 aResult[column] =
toUTF8( results.get<nanodbc::string>( i, NANODBC_TEXT(
"" ) ) );
524 catch( std::exception& e )
527 wxLogTrace(
traceDatabase, wxT(
"Exception while parsing results from SelectOne: %s" ),
540 nanodbc::statement statement( *
m_conn );
542 nanodbc::string query =
fromUTF8( fmt::format(
"SELECT {} FROM {}{}{}",
552 statement.prepare( query );
554 catch( std::exception& e )
558 wxT(
"Exception while preparing query for selectAllAndCache: %s" ),
567 nanodbc::result results;
571 results = nanodbc::execute( statement );
573 catch( std::exception& e )
577 wxT(
"Exception while executing query for selectAllAndCache: %s" ),
590 auto handleException =
591 [&]( std::runtime_error& aException,
const std::string& aExtraContext =
"" )
594 std::string extra = aExtraContext.empty() ?
"" :
": " + aExtraContext;
596 wxT(
"Exception while parsing result %d from selectAllAndCache: %s%s" ),
600 while( results.next() )
602 short columnCount = 0;
607 columnCount = results.columns();
609 catch( nanodbc::database_error& e )
611 handleException( e );
615 for(
short j = 0; j < columnCount; ++j )
618 std::string columnExtraDbgInfo;
619 int datatype = SQL_UNKNOWN_TYPE;
623 column =
toUTF8( results.column_name( j ) );
624 datatype = results.column_datatype( j );
625 columnExtraDbgInfo = fmt::format(
"column index {}, name '{}', type {}",
630 catch( nanodbc::index_range_error& e )
632 handleException( e, columnExtraDbgInfo );
645 result[column] = fmt::format(
"{:G}", results.get<
double>( j ) );
647 catch( nanodbc::null_access_error& )
650 result[column] = std::string();
652 catch( std::runtime_error& e )
654 handleException( e, columnExtraDbgInfo );
663 result[column] =
toUTF8( results.get<nanodbc::string>( j, NANODBC_TEXT(
"" ) ) );
665 catch( std::runtime_error& e )
667 handleException( e, columnExtraDbgInfo );
673 if( !
result.count( aKey ) )
676 wxT(
"selectAllAndCache: warning: key %s not found in result set" ), aKey );
680 std::string keyStr = std::any_cast<std::string>(
result.at( aKey ) );
681 cacheEntry[keyStr] =
result;
684 wxLogTrace(
traceDatabase, wxT(
"selectAllAndCache from %s completed in %0.1f ms" ), aTable,
687 m_cache->Put( aTable, cacheEntry );
690 catch( std::exception& e )
707 wxLogTrace(
traceDatabase, wxT(
"Called SelectAll without valid connection!" ) );
711 auto tableMapIter =
m_tables.find( aTable );
713 if( tableMapIter ==
m_tables.end() )
715 wxLogTrace(
traceDatabase, wxT(
"SelectAll: requested table %s not found in cache" ), aTable );
721 if( !
m_cache->Get( aTable, cacheEntry ) )
725 wxLogTrace(
traceDatabase, wxT(
"SelectAll: `%s` cache fill failed" ), aTable );
730 m_cache->Get( aTable, cacheEntry );
733 if( !
m_cache->Get( aTable, cacheEntry ) )
735 wxLogTrace(
traceDatabase, wxT(
"SelectAll: `%s` failed to get results from cache!" ), aTable );
739 wxLogTrace(
traceDatabase, wxT(
"SelectAll: `%s` - returning cached results" ), aTable );
741 aResults.reserve( cacheEntry.size() );
743 for(
auto &[ key, row ] : cacheEntry )
744 aResults.emplace_back( row );
bool SelectOne(const std::string &aTable, const std::pair< std::string, std::string > &aWhere, ROW &aResult)
Retrieves a single row from a database table.
wxString result
Test unit parsing edge cases and error handling.