# # patch "ChangeLog" # from [10f5571096a56bc32f62bc5e29a88e9181fc68d0] # to [3f9e3b3d3ba22996b606396005b20ec7cd21226c] # # patch "database.cc" # from [7e793889cee7cbbd201744d7d299e4aac84d42f4] # to [67dcca6acbccf335730ae029cadfde6adf7a67a5] # # patch "database.hh" # from [3e62dfc435b831ba05a3e7a478d74b73916bc92e] # to [c908a66fe8da7740c8fe9920cf5e95def4334c71] # ======================================================================== --- ChangeLog 10f5571096a56bc32f62bc5e29a88e9181fc68d0 +++ ChangeLog 3f9e3b3d3ba22996b606396005b20ec7cd21226c @@ -1,5 +1,11 @@ 2005-10-08 Nathaniel Smith + * database.{hh,cc}: Clean-up previous change -- switch to using a + cleanup_ptr to hold sqlite3_stmt handles, for proper exception + safety. + +2005-10-08 Nathaniel Smith + * database.cc (fetch): Do not insert prepared statements into the statement table until they are successfully created, to avoid segfaults on database teardown. ======================================================================== --- database.cc 7e793889cee7cbbd201744d7d299e4aac84d42f4 +++ database.cc 67dcca6acbccf335730ae029cadfde6adf7a67a5 @@ -531,10 +531,9 @@ for (map::const_iterator i = statement_cache.begin(); i != statement_cache.end(); ++i) - { - L(F("%d executions of %s\n") % i->second.count % i->first); - sqlite3_finalize(i->second.stmt); - } + L(F("%d executions of %s\n") % i->second.count % i->first); + // trigger destructors to finalize cached statements + statement_cache.clear(); if (__sql) { @@ -582,16 +581,12 @@ map::iterator i = statement_cache.find(query); if (i == statement_cache.end()) { - statement s; + statement_cache.insert(make_pair(query, statement())); + i = statement_cache.find(query); + I(i != statement_cache.end()); + const char * tail; - // this line can throw, which leaves our statement uninitialized. so we - // insert the statement into the table _after_ running it successfully. - sqlite3_prepare(sql(), query, -1, &s.stmt, &tail); - // note that this copies the statement, and thus the statement pointer. - // But we immediately tear down s, so we still end up with a single - // unique pointer to the statement, on which sqlite3_finalize will - // eventually be called by ~database. - statement_cache.insert(make_pair(query, s)); + sqlite3_prepare(sql(), query, -1, i->second.stmt.paddr(), &tail); assert_sqlite3_ok(sql()); L(F("prepared statement %s\n") % query); @@ -600,14 +595,14 @@ F("multiple statements in query: %s\n") % query); } - ncol = sqlite3_column_count(i->second.stmt); + ncol = sqlite3_column_count(i->second.stmt()); E(want_cols == any_cols || want_cols == ncol, F("wanted %d columns got %d in query: %s\n") % want_cols % ncol % query); // bind parameters for this execution - int params = sqlite3_bind_parameter_count(i->second.stmt); + int params = sqlite3_bind_parameter_count(i->second.stmt()); L(F("binding %d parameters for %s\n") % params % query); @@ -625,20 +620,20 @@ L(F("binding %d with value '%s'\n") % param % log); - sqlite3_bind_text(i->second.stmt, param, value, -1, SQLITE_TRANSIENT); + sqlite3_bind_text(i->second.stmt(), param, value, -1, SQLITE_TRANSIENT); assert_sqlite3_ok(sql()); } // execute and process results nrow = 0; - for (rescode = sqlite3_step(i->second.stmt); rescode == SQLITE_ROW; - rescode = sqlite3_step(i->second.stmt)) + for (rescode = sqlite3_step(i->second.stmt()); rescode == SQLITE_ROW; + rescode = sqlite3_step(i->second.stmt())) { vector row; for (int col = 0; col < ncol; col++) { - const char * value = sqlite3_column_text_s(i->second.stmt, col); + const char * value = sqlite3_column_text_s(i->second.stmt(), col); E(value, F("null result in query: %s\n") % query); row.push_back(value); //L(F("row %d col %d value='%s'\n") % nrow % col % value); @@ -649,7 +644,7 @@ if (rescode != SQLITE_DONE) assert_sqlite3_ok(sql()); - sqlite3_reset(i->second.stmt); + sqlite3_reset(i->second.stmt()); assert_sqlite3_ok(sql()); nrow = res.size(); ======================================================================== --- database.hh 3e62dfc435b831ba05a3e7a478d74b73916bc92e +++ database.hh c908a66fe8da7740c8fe9920cf5e95def4334c71 @@ -9,6 +9,7 @@ struct sqlite3; struct sqlite3_stmt; struct cert; +int sqlite3_finalize(sqlite3_stmt *); #include @@ -22,6 +23,7 @@ #include "numeric_vocab.hh" #include "vocab.hh" #include "paths.hh" +#include "cleanup.hh" struct revision_set; @@ -75,9 +77,9 @@ void check_schema(); struct statement { + statement() : count(0), stmt(0, sqlite3_finalize) {} int count; - statement() : count(0) {} - sqlite3_stmt *stmt; + cleanup_ptr stmt; }; std::map statement_cache;