22 #include <basic/options/keys/inout.OptionKeys.gen.hh>
23 #include <basic/options/keys/out.OptionKeys.gen.hh>
28 #include <cppdb/errors.h>
29 #include <cppdb/frontend.h>
31 #include <boost/algorithm/string/predicate.hpp>
32 #include <boost/foreach.hpp>
33 #include <boost/iterator/iterator_facade.hpp>
34 #include <boost/lexical_cast.hpp>
35 #include <boost/mpl/bool.hpp>
36 #include <boost/mpl/bool_fwd.hpp>
37 #include <boost/token_functions.hpp>
38 #include <boost/tokenizer.hpp>
40 #include <platform/types.hh>
72 using std::stringstream;
75 using cppdb::statement;
76 using cppdb::cppdb_error;
79 using namespace utility::sql_database;
90 using namespace basic::options::OptionKeys;
93 option[inout::dbms::database_name],
96 option[inout::dbms::pq_schema]);
102 string const & db_name,
103 string const & pq_schema
106 using namespace basic::options::OptionKeys;
118 string const & db_name,
121 string const & pq_schema
124 using namespace basic::options::OptionKeys;
137 std::string
const & db_name,
138 std::string
const & pq_schema){
153 string const & db_name,
154 string const & pq_schema
157 using namespace basic::options::OptionKeys;
169 "You have specified options for a client-server database "
170 "but the database mode is sqlite3. "
171 "Please specify -inout:dbms:mode <db_mode>.");
174 if ( pq_schema.compare(
"") ) {
176 <<
"You have specified a postgres schema but using a sqlite3 database. "
177 <<
"To use postgres, please specify -inout:dbms:mode postgres"
185 option[inout::dbms::readonly],
190 if (
option[inout::dbms::readonly] ) {
192 "Restricting access to a mysql database is done at the user level "
193 "rather that the connection level. "
194 "So requesting a readonly connection cannot fullfilled.");
197 if ( pq_schema.compare(
"") ) {
199 <<
"You have specified a postgres schema but using a mysql database. "
200 <<
"To use postgres, please specify -inout:dbms:mode postgres"
210 "To connect to a mysql database you must specify "
211 "-inout:dbms:host -inout:dbms:user -inout:dbms:password and "
222 option[inout::dbms::host],
224 option[inout::dbms::password],
225 option[inout::dbms::port]);
230 if (
option[inout::dbms::readonly] ) {
232 "Restricting access to a postgres database is done at the user level "
233 "rather that the connection level. So requesting a readonly connection "
234 "cannot fullfilled.");
244 "To connect to a postgres database you must specify "
245 "-inout:dbms:host -inout:dbms:user -inout:dbms:password "
246 "and -inout:dbms:port");
257 option[inout::dbms::host],
258 option[inout::dbms::user],
259 option[inout::dbms::password],
260 option[inout::dbms::port]);
272 using namespace basic::options::OptionKeys;
274 if (
option[inout::dbms::separate_db_per_mpi_process] &&
option[inout::dbms::database_partition].
user() ) {
276 "The -inout:dbms:separate_db_per_mpi_process and -inout::dbms::database_partition options are mutually exclusive.");
277 }
else if (
option[inout::dbms::database_partition].
user() ) {
278 if (
option[inout::dbms::database_partition] < 0 ) {
280 error <<
"Invalid -inout::dbms::database_partition specified, must be positive integer value: " <<
option[inout::dbms::database_partition] << std::endl;
289 if ( !
option[inout::dbms::database_partition].
user() ) {
291 option[inout::dbms::separate_db_per_mpi_process]);
294 option[inout::dbms::separate_db_per_mpi_process],
295 option[inout::dbms::database_partition]);
302 if (
option[inout::dbms::separate_db_per_mpi_process] ||
option[inout::dbms::database_partition].
user() ) {
304 "The -inout:dbms:separate_db_per_mpi_process and -inout::dbms::database_partition flags only apply to "
305 "sqlite3 databases.");
315 bool partition_by_mpi_process,
318 if ( partition_by_mpi_process ) {
321 MPI_Comm_rank(MPI_COMM_WORLD, &mpi_rank);
326 if ( manual_partition >= 0 ) {
327 return manual_partition;
334 string const & statement_string,
340 stmt = db_session->prepare(statement_string);
342 }
catch(cppdb_error
const & error)
344 TR.
Error <<
" Failed to safely prepare the following statement: " << std::endl;
345 TR.
Error << statement_string << std::endl;
346 TR.
Error << error.what() << std::endl;
355 statement & statement
365 }
catch(cppdb::bad_value_cast & except)
369 }
catch(cppdb::empty_row_access & except)
372 }
catch(cppdb::invalid_column & except)
375 }
catch(cppdb::invalid_placeholder & except)
378 }
catch(cppdb::multiple_rows_query & except)
381 }
catch(cppdb::not_supported_by_backend & except)
384 }
catch(cppdb::null_value_fetch & except)
387 }
catch(cppdb::cppdb_error & except)
390 if( std::string(except.what()) ==
"database is locked"){
391 stringstream err_msg;
393 <<
"database is locked" << std::endl
395 <<
"PSA: If this is an sqlite3 session, running under MPI, and you are using the database primarily for writing output," << std::endl
396 <<
"consider using the 'separate_db_per_mpi_process' option if you're not already using it." << std::endl
397 <<
"To do this add separate_db_per_mpi_process=1 to a RosettaScripts/resource_definitions xml tag that takes database options" << std::endl
398 <<
"or add -inout:dbms:separate_db_per_mpi_process to the command line or flags file" << std::endl
399 <<
"This option will append '_<mpi_rank>' to the database filename for each mpi process. Once the run has finished," << std::endl
400 <<
"these databases can be merged together using " << std::endl
402 <<
" bash /path/to/rosetta_tests/features/sample_sources/merge.sh <output_db> <input_db_part_1> [<input_db_part_2> [ ... ] ]" << std::endl
404 <<
"For more information see the database connection options page in the Rosetta Wiki: RosettaScripts_database_connection_options" << std::endl
405 << except.what() << std::endl;
419 statement & statement
422 using namespace basic::options::OptionKeys;
429 return statement.query();
430 }
catch(cppdb::bad_value_cast & except)
434 }
catch(cppdb::empty_row_access & except)
437 }
catch(cppdb::invalid_column & except)
440 }
catch(cppdb::invalid_placeholder & except)
443 }
catch(cppdb::multiple_rows_query & except)
446 }
catch(cppdb::not_supported_by_backend & except)
449 }
catch(cppdb::null_value_fetch & except)
452 }
catch(cppdb::cppdb_error & except)
454 if (
option[inout::dbms::retry_failed_reads] && cycle <= retry_limit ) {
455 TR <<
"Caught an exception on db read: " << except.what() <<
"\n" <<
456 "pausing before another attempted read. This is attempt " << cycle <<
" of " << retry_limit <<std::endl;
475 string const & table_name
482 string statement_string;
485 switch(db_session->get_db_mode()){
487 statement_string =
"SELECT name FROM sqlite_master WHERE name=?;";
491 statement_string =
"SHOW TABLES WHERE Tables_in_"+db_session->get_db_name()+
" = ?;";
495 if ( db_session->get_pq_schema() ==
"" ) {
497 "SELECT tablename \n"
498 "FROM pg_catalog.pg_tables \n"
499 "WHERE tablename = ?;";
503 "SELECT tablename FROM pg_catalog.pg_tables \n"
504 "WHERE schemaname = ? AND tablename = ?;";
506 stmt.bind(i, db_session->get_pq_schema());
514 stmt.bind(i, table_name);
515 result
res(stmt.query());
525 if ( !boost::istarts_with(sql,
"SELECT") ) {
529 int semicolon_count=0;
530 for (
size_t i = 0; i < sql.size(); i++ ) {
531 if ( sql[i] ==
';' ) {
535 if ( semicolon_count > 1 ) {
544 std::vector<string> column_names,
545 std::vector<string> values,
549 string statement_string=
"";
550 switch(db_session->get_db_mode()){
552 statement_string =
"INSERT IGNORE into " + table_name +
" (";
553 for (
size_t i=0; i<column_names.size(); i++ ) {
554 statement_string += column_names[i];
555 if ( i != column_names.size()-1 ) {
556 statement_string +=
",";
560 statement_string+=
") VALUES(";
561 for (
size_t i=0; i<values.size(); i++ ) {
562 statement_string +=
"?";
563 if ( i != column_names.size()-1 ) {
564 statement_string +=
",";
568 statement_string +=
");";
571 for (
size_t i=0; i<values.size(); i++ ) {
572 stmt.bind(i+1, values[i]);
580 string select_statement_string =
"SELECT * FROM " + table_name +
" WHERE ";
581 for (
size_t i=0; i<column_names.size(); i++ ) {
582 select_statement_string += column_names[i] +
"=?";
583 if ( i != column_names.size()-1 ) {
584 select_statement_string +=
" AND ";
587 select_statement_string+=
";";
590 select_statement_string, db_session
593 for (
size_t i=0; i<values.size(); i++ ) {
594 select_stmt.bind(i+1, values[i]);
600 statement_string +=
"INSERT into " + table_name +
"(";
601 for (
size_t i=0; i<column_names.size(); i++ ) {
602 statement_string += column_names[i];
603 if ( i != column_names.size()-1 ) {
604 statement_string +=
",";
608 statement_string +=
") VALUES(";
609 for (
size_t i=0; i<values.size(); i++ ) {
610 statement_string +=
"?";
611 if ( i != column_names.size()-1 ) {
612 statement_string +=
",";
616 statement_string +=
");";
619 statement_string, db_session
622 for (
size_t i=0; i<values.size(); i++ ) {
623 stmt.bind(i+1, values[i]);
631 statement_string =
"INSERT OR IGNORE into "+table_name+
"(";
632 for (
size_t i=0; i<column_names.size(); i++ ) {
633 statement_string+=column_names[i];
634 if ( i != column_names.size()-1 ) {
635 statement_string+=
",";
639 statement_string+=
") VALUES(";
640 for (
size_t i=0; i<values.size(); i++ ) {
641 statement_string +=
"?";
642 if ( i != column_names.size()-1 ) {
643 statement_string+=
",";
646 statement_string+=
");";
649 for (
size_t i=0; i<values.size(); i++ ) {
650 stmt.bind(i+1, values[i]);
658 "Unrecognized database mode: '" +
667 boost::char_separator< char > sep(
";");
668 boost::tokenizer< boost::char_separator< char > > tokens( schema_str, sep );
669 BOOST_FOREACH ( std::string
const & stmt_str, tokens ) {
671 if ( trimmed_stmt_str.size() ) {
673 statement stmt = (*db_session) << trimmed_stmt_str +
";";
675 }
catch (cppdb_error
const & e) {
677 <<
"ERROR reading schema \n"
678 << trimmed_stmt_str << std::endl;
679 TR.
Error << e.what() << std::endl;
694 stringstream stmt_ss;
695 stmt_ss <<
"PRAGMA cache_size = " << cache_size <<
";";
700 <<
"WARNING: Attempting to set database cache size "
701 <<
"for a database type for which this is currently not supported: "
707 std::string
const & table_name,
708 std::vector<std::string>
const & column_names,
711 std::string table_definition = table_name +
" (" +
utility::join(column_names,
",") +
")";
712 std::string value_list;
715 std::string row_block=
"(?";
721 value_list += row_block;
722 if ( i != row_count-1 ) {
727 return "INSERT INTO "+table_definition+
" VALUES " + value_list +
";";
738 using namespace basic::options::OptionKeys::inout;
739 using namespace basic::options::OptionKeys;
741 using namespace basic::resource_manager;
743 bool separate_database =
option[ inout::dbms::separate_db_per_mpi_process ]();
744 int database_partition =
option[ inout::dbms::database_partition]();
746 if ( tag->hasOption(
"database_resource") ) {
747 std::string database_resource = tag->getOption<
string>(
"database_resource");
748 if ( ! ResourceManager::get_instance()->has_resource_with_description( database_resource ) ) {
750 (
"You specified a database_resource of '" + database_resource +
751 "', but the ResourceManager doesn't have a resource with that description." );
753 return get_resource< utility::sql_database::session >( database_resource );
756 if ( tag->hasOption(
"database_resource_tag") ) {
757 std::string database_resource_tag = tag->getOption<
string>(
758 "database_resource_tag");
759 if ( ! ResourceManager::get_instance()->has_resource(
760 database_resource_tag ) ) {
762 (
"You specified a database_resource_tag of '" + database_resource_tag +
763 "', but the ResourceManager doesn't have a resource with that tag." );
766 ResourceManager::get_instance()->find_resource(database_resource_tag)));
768 stringstream err_msg;
770 <<
"You specified a database_resource_tag of '" + database_resource_tag +
"', while the ResourceManager does have a resource with that tag, it couldn't cast into a database session.";
777 tag->getOption<
string>(
"transaction_mode",
"standard"));
780 switch(transaction_mode){
782 if ( tag->hasOption(
"chunk_size") ) {
783 TR <<
"WARNING: You must specify 'transaction_mode=chunk' ";
784 TR <<
"to use the 'chunk size' tag." << endl;
789 if ( tag->hasOption(
"chunk_size") ) {
790 TR <<
"WARNING: You must specify 'transaction_mode=chunk' ";
791 TR <<
"to use the 'chunk size' tag." << endl;
796 if ( !tag->hasOption(
"chunk_size") ) {
798 "Must specify chunk_size if using the chunk transaction mode");
801 tag->getOption<
Size>(
"chunk_size");
805 "Unrecognized transaction mode: '" +
811 if ( tag->hasOption(
"database_mode") ) {
813 tag->getOption<
string>(
"database_mode"));
819 std::string database_name;
820 if ( tag->hasOption(
"database_name") ) {
821 database_name = tag->getOption<
string>(
"database_name");
823 database_name =
option[dbms::database_name];
826 std::string database_dir;
828 database_dir =
option[ out::path::db ]().
path();
836 std::string database_path = database_dir + database_name;
840 TR <<
"WARNING: You must specify 'database_mode=postgres' ";
841 TR <<
"to use the 'database_pq_schema' tag." << endl;
844 std::string database_pq_schema;
845 if ( tag->hasOption(
"database_pq_schema") ) {
846 database_pq_schema = tag->getOption<
string>(
"database_pq_schema");
848 database_pq_schema =
option[dbms::pq_schema];
854 if ( tag->hasOption(
"database_separate_db_per_mpi_process") ) {
855 TR <<
"WARNING: You must specify 'database_mode=sqlite3' ";
856 TR <<
"to use the 'database_separate_db_per_mpi_process' tag." << endl;
859 if ( tag->hasOption(
"database_partition") ) {
860 TR <<
"WARNING: You must specify 'database_mode=sqlite3' ";
861 TR <<
"to use the 'database_partition' tag." << endl;
864 if ( tag->hasOption(
"database_read_only") ) {
865 TR <<
"WARNING: You must specify 'database_mode=sqlite3' ";
866 TR <<
"to use the 'database_read_only tag." << endl;
871 if ( tag->hasOption(
"database_separate_db_per_mpi_process") && tag->hasOption(
"database_partition") ) {
872 TR <<
"WARNING: 'database_separate_db_per_mpi_process' and 'database_partition' tags are mutually exclusive, using 'database_separate_db_per_mpi_process'." << endl;
875 if ( tag->hasOption(
"database_host") ) {
876 TR <<
"WARNING: You must specify either 'database_mode=mysql' ";
877 TR <<
"or database_mode=postgres' to use the 'database_host' tag." << endl;
880 if ( tag->hasOption(
"database_user") ) {
881 TR <<
"WARNING: You must specify either 'database_mode=mysql' ";
882 TR <<
"or database_mode=postgres' to use the 'database_user' tag." << endl;
885 if ( tag->hasOption(
"database_password") ) {
886 TR <<
"WARNING: You must specify either 'database_mode=mysql' ";
887 TR <<
"or database_mode=postgres' to use the 'database_password' tag." << endl;
890 if ( tag->hasOption(
"database_port") ) {
891 TR <<
"WARNING: You must specify either 'database_mode=mysql' ";
892 TR <<
"or database_mode=postgres' to use the 'database_port' tag." << endl;
896 switch(database_mode){
900 database_mode, transaction_mode, chunk_size,
901 database_path,
"",
"",
"",
"", 0,
902 tag->getOption(
"database_read_only",
false),
904 tag->getOption(
"database_separate_db_per_mpi_process", separate_database),
905 tag->getOption(
"database_partition", database_partition)));
910 std::string database_host;
911 if ( !tag->hasOption(
"database_host") ) {
914 "WARNING: To connect to a postgres or mysql database you must set"
915 " the database_host tag or specify -dbms:host on the command line.");
917 database_host=
option[dbms::host];
920 database_host=tag->getOption<
string>(
"database_host");
923 std::string database_user;
924 if ( !tag->hasOption(
"database_user") ) {
927 "WARNING: To connect to a postgres or mysql database you must set"
928 "the database_user tag or specify -dbms:user on the command line.");
933 database_user=tag->getOption<
string>(
"database_user");
936 std::string database_password;
937 if ( !tag->hasOption(
"database_password") ) {
940 "WARNING: To connect to a postgres or mysql database you must set"
941 "the database_password tag or specify -dbms:password on the command line.");
943 database_password=
option[dbms::password];
946 database_password=tag->getOption<
string>(
"database_password");
950 if ( !tag->hasOption(
"database_port") ) {
953 "WARNING: To connect to a postgres or mysql database you must set"
954 "the database_port tag or specify -dbms:port on the command line.");
956 database_port=
option[dbms::port];
959 database_port=tag->getOption<
Size>(
"database_port");
964 database_mode, transaction_mode, chunk_size,
974 "Unrecognized database mode: '" +
#define utility_exit_with_message(m)
Exit with file + line + message.
Program path option class.
static THREAD_LOCAL basic::Tracer TR("basic.database.sql_utils")
std::string make_compound_statement(std::string const &table_name, std::vector< std::string > const &column_names, platform::Size const &row_count)
RandomGenerator & rg()
Return the one-per-thread "singleton" random generator.
std::string name_from_database_mode(DatabaseMode::e database_mode)
sessionOP get_db_session(DatabaseMode::e db_mode, TransactionMode::e transaction_mode, Size chunk_size, string const &db_name, string const &pq_schema)
BooleanOptionKey const user("options:user")
Program string option class.
void trim(std::string &s, const std::string &drop)
statement safely_prepare_statement(string const &statement_string, sessionOP db_session)
utility::sql_database::sessionOP parse_database_connection(utility::tag::TagCOP tag)
DatabaseMode::e database_mode_from_name(std::string database_mode)
Program boolean option class.
Program scalar-valued option abstract base class.
Random number generator system.
common derived classes for thrown exceptions
bool table_exists(sessionOP db_session, string const &table_name)
void check_statement_sanity(string sql)
result safely_read_from_database(statement &statement)
sessionOP get_session_postgres(std::string const &database, TransactionMode::e transaction_mode, platform::Size chunk_size, std::string const &pq_schema, std::string const &host, std::string const &user, std::string const &password, platform::Size port)
Acquire a postgres database session.
platform::SSize db_partition_from_options(DatabaseMode::e db_mode)
@
std::string join(utility::vector1< std::string > const &s, std::string const &connector)
combine strings with anything
void write_schema_to_database(string schema_str, sessionOP db_session)
sessionOP get_db_session(DatabaseMode::e db_mode, TransactionMode::e transaction_mode, platform::Size chunk_size, std::string const &db_name, std::string const &pq_schema, std::string const &host, std::string const &user, std::string const &password, platform::Size port, bool readonly=false, platform::SSize db_partition=-1)
Acquire a database session.
Program exit functions and macros.
std::string name_from_transaction_mode(TransactionMode::e transaction_mode)
void set_cache_size(sessionOP db_session, Size cache_size)
#define utility_exit()
Macro function wrappers for utility::exit.
std::vector with 1-based indexing
void insert_or_ignore(string table_name, std::vector< string > column_names, std::vector< string > values, sessionOP db_session)
rule< Scanner, options_closure::context_t > options
BooleanOptionKey const all("options:all")
void safely_write_to_database(statement &statement)
static DatabaseSessionManager * get_instance()
return singleton instance of session manager
Program integer option class.
Path name class supporting Windows and UN*X/Linux format names.
vector1: std::vector with 1-based indexing
Class for handling user debug/warnings/errors. Use instance of this class instead of 'std::cout' for ...
sessionOP get_session_sqlite3(std::string const &database, TransactionMode::e transaction_mode=TransactionMode::standard, platform::Size chunk_size=0, bool const readonly=false, platform::SSize db_partition=-1)
Acquire a sqlite3 database session.
utility::pointer::shared_ptr< Tag const > TagCOP
double uniform()
Generate a random number between 0 and 1. Threadsafe since each thread uses its own random generator...
An object for reading/writing a simple xml-like language.
sessionOP get_session_mysql(std::string const &database, TransactionMode::e transaction_mode, platform::Size chunk_size, std::string const &host, std::string const &user, std::string const &password, platform::Size port)
Acquire a mysql database session.
Some std::string helper functions.
Program options global and initialization function.
rule< Scanner, tag_closure::context_t > tag
TransactionMode::e transaction_mode_from_name(std::string transaction_mode)
pointer::shared_ptr< session > sessionOP
platform::SSize resolve_db_partition(bool partition_by_mpi_process, platform::SSize manual_partition)
Returns partition identifer from mpi rank if in partitioned database mode, or valid manual partition...
rule< Scanner, option_closure::context_t > option