Rosetta
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Pages
Schema.cc
Go to the documentation of this file.
1 // -*- mode:c++;tab-width:2;indent-tabs-mode:t;show-trailing-whitespace:t;rm-trailing-spaces:t -*-
2 // vi: set ts=2 noet:
3 //
4 // (c) Copyright Rosetta Commons Member Institutions.
5 // (c) This file is part of the Rosetta software suite and is made available under license.
6 // (c) The Rosetta software is developed by the contributing members of the Rosetta Commons.
7 // (c) For more information, see http://www.rosettacommons.org. Questions about this can be
8 // (c) addressed to University of Washington UW TechTransfer, email: license@u.washington.edu.
9 
10 /// @file basic/database/schema_generator/Schema.cc
11 ///
12 /// @brief Construct a database backend independant schema
13 /// @author Tim Jacobs
14 
15 //Unit
21 
23 
25 #include <basic/mpi/util.hh>
26 
27 // Basic Headers
28 #include <basic/Tracer.hh>
29 #include <platform/types.hh>
30 
31 // Utility Headers
32 #include <utility/exit.hh>
33 #include <utility/mpi_util.hh>
35 
36 #include <string>
37 #include <stdio.h>
38 #include <algorithm>
39 #include <cctype>
40 
41 // External
42 #include <cppdb/frontend.h>
43 
44 namespace basic {
45 namespace database {
46 namespace schema_generator {
47 
48 using platform::Size;
49 using std::string;
50 using std::endl;
51 using std::stringstream;
58 using utility::mpi_rank;
59 using cppdb::statement;
60 
61 static THREAD_LOCAL basic::Tracer TR( "basic.database.schema_generator.Schema" );
62 
63 
64 Schema::Schema(std::string table_name):
65  table_name_(table_name)
66 {
67  init();
68 }
69 
70 Schema::Schema(std::string table_name, PrimaryKey primary_key):
71  table_name_(table_name),
72  primary_key_(primary_key)
73 {
74  init();
75 }
76 
78  Schema const & src
79 ) :
80  primary_key_(src.primary_key_),
81  columns_(src.columns_),
82  foreign_keys_(src.foreign_keys_),
83  constraints_(src.constraints_),
84  indices_(src.indices_)
85 {}
86 
87 void
89  //Add primary key columns to schema list
90  Columns key_columns = primary_key_.columns();
91  this->columns_.insert( columns_.end(), key_columns.begin(), key_columns.end() );
92 
93  // Table names should all be lower case
94  std::transform(
95  table_name_.begin(), table_name_.end(), table_name_.begin(),
96  (int(*)(int)) std::tolower);
97 }
98 
99 void
102 ){
103  this->foreign_keys_.push_back(key);
104  //if the foreign key is also a primary key it will have already been added
105 
106  Columns key_cols = key.columns();
107 
108  for ( Size i=1; i <= key_cols.size(); ++i ) {
109  if ( !this->columns_.contains(key_cols[i]) ) {
110  this->columns_.push_back(key_cols[i]);
111  }
112  }
113 }
114 
115 void
117  Column column
118 ){
119  //Don't add a column more than once
120  if ( !this->columns_.contains(column) ) {
121  this->columns_.push_back(column);
122  }
123 }
124 
125 void
127  ConstraintOP constraint
128 ){
129  this->constraints_.push_back(constraint);
130 }
131 
132 void
134  Index index
135 ) {
136  indices_.push_back(index);
137 }
138 
139 std::string Schema::print( sessionOP db_session ) const
140 {
141  return table_schema_statements(db_session) + table_init_statements(db_session);
142 }
143 
144 std::string Schema::table_schema_statements( sessionOP db_session ) const
145 {
146  stringstream schema_string;
147  schema_string << "CREATE TABLE IF NOT EXISTS " << table_name_ << "(\n\t";
148 
149  for ( Columns::const_iterator it=columns_.begin(), end = columns_.end(); it != end; ++it ) {
150  if ( it!=columns_.begin() ) {
151  schema_string << ",\n\t";
152  }
153  schema_string << it->print(db_session);
154  }
155 
156  for ( size_t i=1; i<=foreign_keys_.size(); i++ ) {
157  schema_string << ",\n\t" << foreign_keys_[i].print(db_session);
158  }
159 
160  Columns const & keys(primary_key_.columns());
161 
162  if ( keys.size() > 0 ) {
163  switch(db_session->get_db_mode()) {
166  schema_string << ",\n\t" << primary_key_.print(db_session);
167  break;
169  //Prevent adding the primary key twice - this will happen if you have an autoincrementing primary key in sqlite3
170 
171  if ( !(keys.size()==1 && keys.begin()->auto_increment()) ) {
172  schema_string << ",\n\t" << primary_key_.print(db_session);
173  }
174  break;
175  default :
177  "Unrecognized database mode: '" + name_from_database_mode(db_session->get_db_mode()) + "'");
178  }
179  }
180 
181  for ( size_t i=1; i<=constraints_.size(); i++ ) {
182  schema_string << ",\n\t" << constraints_[i]->print(db_session);
183  }
184 
185  schema_string << ");\n";
186 
187  for ( Size i=1; i <= indices_.size(); ++i ) {
188  schema_string << indices_[i].print(table_name_, db_session);
189  schema_string << "\n";
190  }
191 
192  return schema_string.str();
193 }
194 
195 std::string Schema::table_init_statements( sessionOP db_session ) const
196 {
197  stringstream init_string;
198  for ( Columns::const_iterator it=columns_.begin(), end = columns_.end(); it != end; ++it ) {
199  if ( it->auto_increment() && it->auto_increment_base() != 0 ) {
200  switch(db_session->get_db_mode()){
202  // Postgresql creates an implicit sequence of the form <table_name>_<column_name>_seq to handle auto_increment values.
203  //
204  // http://www.postgresql.org/docs/9.2/static/datatype-numeric.html
205  // see 8.1.4 Serial Types
206  init_string << "ALTER SEQUENCE " << table_name_ << "_" << it->name() << "_seq" << " MINVALUE " << it->auto_increment_base() << ";\n";
207  break;
209  // http://dev.mysql.com/doc/refman/5.6/en/example-auto-increment.html
210  init_string << "ALTER TABLE " << table_name_ << " AUTO_INCREMENT = " << it->auto_increment_base() << ";\n";
211  break;
213  // Sqlite3 autoincrement is managed by the db table, sqlite_sequence. ROWID (and autoincrement value) are monotonically
214  // increased from from current value in sqlite_sequence. In the trivial case, the next value is sqlite_sequence + 1.
215  //
216  // http://www.sqlite.org/autoinc.html
217  init_string << "INSERT INTO sqlite_sequence SELECT \"" << table_name_ << "\", " << it->auto_increment_base() - 1 << " WHERE NOT EXISTS (SELECT 1 FROM sqlite_sequence WHERE NAME = \"" << table_name_ << "\");\n";
218  break;
219  default :
221  "Unrecognized database mode: '" + name_from_database_mode(db_session->get_db_mode()) + "'");
222  }
223  }
224  }
225 
226  return init_string.str();
227 }
228 
229 //Write this schema to the database
230 void Schema::write(sessionOP db_session)
231 {
232  std::string schema_statement = table_schema_statements(db_session);
233  std::string init_statements = table_init_statements(db_session);
234 
235  try
236 {
237  try
238 {
239  /*
240  * TODO alexford This abort logic still allows race conditions if writes are executed from multiple frontends
241  * This should be resolved by adding a meta-table to track schema initialization protected by transactions, or
242  * by running all table declations
243  */
244  check_table_and_perform_write(db_session, schema_statement, init_statements);
245  }
246 catch(cppdb::cppdb_error & except)
247 {
248  TR.Debug << "Error writing schema, retrying:\n" << except.what() << std::endl;
249  check_table_and_perform_write(db_session, schema_statement, init_statements);
250 }
251  }
252 catch(cppdb::cppdb_error & except)
253 {
254  TR.Error
255  << "ERROR writing schema after retry.\n"
256  << print(db_session) << std::endl;
257  TR.Error << except.what() << std::endl;
258  utility_exit();
259 }
260 }
261 
264  std::string schema_statement,
265  std::string init_statements) const
266 {
267  cppdb::transaction guard(*db_session);
268  statement stmt;
269 
270  if ( init_statements != "" && table_exists(db_session, table_name_) ) {
271  TR.Debug << "Table with init statement exists, skipping declaration: " << table_name_ << std::endl;
272  return;
273  }
274 
275  if ( !table_exists(db_session, table_name_) ) {
276  // kylebarlow - We don't need to run a "CREATE TABLE IF NOT EXISTS" query if the table already exists
277  // Running lots of those queries results in problems waiting for table metadata locks
278  // This problem remains unfixed in MySQL as of version 5.5.32
279  TR.Debug << "Writing schema for table: " << table_name_ << std::endl;
280  TR.Trace << schema_statement << std::endl;
281 
282  stmt = (*db_session) << schema_statement;
283  stmt.exec();
284  }
285 
286  if ( init_statements != "" ) {
287  TR.Debug << "Writing init for table: " << table_name_ << std::endl;
288  TR.Trace << init_statements << std::endl;
289 
290  stmt = (*db_session) << init_statements;
291  stmt.exec();
292  }
293 
294  guard.commit();
295 }
296 
297 } // schema_generator
298 } // namespace database
299 } // namespace utility
#define utility_exit_with_message(m)
Exit with file + line + message.
Definition: exit.hh:47
std::string print(utility::sql_database::sessionOP db_session) const
Definition: Schema.cc:139
utility::pointer::shared_ptr< Constraint > ConstraintOP
#define THREAD_LOCAL
bool contains(T const &t) const
Check if vector contains a given element.
Definition: vectorL.hh:324
utility::vector1< ForeignKey > foreign_keys_
Definition: Schema.hh:92
std::string table_init_statements(utility::sql_database::sessionOP db_session) const
Definition: Schema.cc:195
void add_constraint(ConstraintOP constraint)
Definition: Schema.cc:126
void write(utility::sql_database::sessionOP db_session)
Definition: Schema.cc:230
static THREAD_LOCAL basic::Tracer TR("basic.database.schema_generator.Schema")
utility::pointer::shared_ptr< MessageListener > MessageListenerOP
std::string name_from_database_mode(DatabaseMode::e database_mode)
Definition: types.cc:83
std::string print(utility::sql_database::sessionOP db_session) const
Definition: PrimaryKey.cc:43
utility::keys::lookup::end< KeyType > const end
void add_foreign_key(ForeignKey key)
Definition: Schema.cc:100
PrimaryKey class for the schema generator framework.
utility::vector1< ConstraintOP > constraints_
Definition: Schema.hh:93
Index class for the schema generator framework.
table definition for the schema generator framework
bool table_exists(sessionOP db_session, string const &table_name)
Definition: sql_utils.cc:473
Fstring::size_type index(Fstring const &s, Fstring const &ss)
First Index Position of a Substring in an Fstring.
Definition: Fstring.hh:2180
Schema(std::string table_name)
Definition: Schema.cc:64
void send_data_to_head_node(std::string const &MPI_ONLY(data))
Definition: util.cc:79
TracerProxy Trace
Definition: Tracer.hh:262
ForeignKey class for the schema generator framework.
tuple database
Program exit functions and macros.
string request_data_from_head_node(listener_tags MPI_ONLY(listener_tag), string const &MPI_ONLY(data))
used for message passing to the MPIWorkPoolJobDistributor. This function will ask the head node for d...
Definition: util.cc:48
utility::keys::lookup::key< KeyType > const key
Tracer IO system.
#define utility_exit()
Macro function wrappers for utility::exit.
Definition: exit.hh:41
TracerProxy Error
Definition: Tracer.hh:262
std::string table_schema_statements(utility::sql_database::sessionOP db_session) const
Definition: Schema.cc:144
super::const_iterator const_iterator
Definition: vector1.hh:62
void check_table_and_perform_write(utility::sql_database::sessionOP db_session, std::string schema_statement, std::string init_statements) const
Definition: Schema.cc:262
Column class for the schema generator framework.
utility::vector1< Index > indices_
Definition: Schema.hh:94
int mpi_rank()
Definition: mpi_util.cc:254
Class for handling user debug/warnings/errors. Use instance of this class instead of 'std::cout' for ...
Definition: Tracer.hh:134
std::size_t Size
Definition: types.hh:37
platform::Size Size
Definition: random.fwd.hh:30
pointer::shared_ptr< session > sessionOP
TracerProxy Debug
Definition: Tracer.hh:262