Put debugging assertions into sql::Statement.

Pulls out the core of gbillock's http://codereview.chromium.org/8283002/
- Move NOTREACHED and similar checks into the sql:: implementation code.
- Add malformed SQL checks to Connection::Execute.
- Add SQL-checking convenience methods to Connection.

The general idea is that the sql:: framework assumes valid statements,
rather than having client code contain scattered ad-hoc (and thus
inconsistent) checks.

This version puts back Statement operator overloading and loosy-goosy
Execute() calls to allow other code to be updated in small batches.

[email protected],[email protected],[email protected] 
BUG=none
TEST=sql_unittests,unit_tests:*Table*.*


Review URL: http://codereview.chromium.org/8899012

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@114118 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/sql/connection.cc b/sql/connection.cc
index f7fef12..26e71fd 100644
--- a/sql/connection.cc
+++ b/sql/connection.cc
@@ -132,7 +132,7 @@
 
 void Connection::Preload() {
   if (!db_) {
-    NOTREACHED();
+    DLOG(FATAL) << "Cannot preload null db";
     return;
   }
 
@@ -142,7 +142,7 @@
   if (!DoesTableExist("meta"))
     return;
   Statement dummy(GetUniqueStatement("SELECT * FROM meta"));
-  if (!dummy || !dummy.Step())
+  if (!dummy.Step())
     return;
 
 #if !defined(USE_SYSTEM_SQLITE)
@@ -166,7 +166,7 @@
     needs_rollback_ = false;
 
     Statement begin(GetCachedStatement(SQL_FROM_HERE, "BEGIN TRANSACTION"));
-    if (!begin || !begin.Run())
+    if (!begin.Run())
       return false;
   }
   transaction_nesting_++;
@@ -175,7 +175,7 @@
 
 void Connection::RollbackTransaction() {
   if (!transaction_nesting_) {
-    NOTREACHED() << "Rolling back a nonexistent transaction";
+    DLOG(FATAL) << "Rolling back a nonexistent transaction";
     return;
   }
 
@@ -192,7 +192,7 @@
 
 bool Connection::CommitTransaction() {
   if (!transaction_nesting_) {
-    NOTREACHED() << "Rolling back a nonexistent transaction";
+    DLOG(FATAL) << "Rolling back a nonexistent transaction";
     return false;
   }
   transaction_nesting_--;
@@ -208,15 +208,22 @@
   }
 
   Statement commit(GetCachedStatement(SQL_FROM_HERE, "COMMIT"));
-  if (!commit)
-    return false;
   return commit.Run();
 }
 
-bool Connection::Execute(const char* sql) {
+int Connection::ExecuteAndReturnErrorCode(const char* sql) {
   if (!db_)
     return false;
-  return sqlite3_exec(db_, sql, NULL, NULL, NULL) == SQLITE_OK;
+  return sqlite3_exec(db_, sql, NULL, NULL, NULL);
+}
+
+bool Connection::Execute(const char* sql) {
+  int error = ExecuteAndReturnErrorCode(sql);
+  // TODO(shess,gbillock): DLOG(FATAL) once Execute() clients are
+  // converted.
+  if (error == SQLITE_ERROR)
+    DLOG(ERROR) << "SQL Error in " << sql << ", " << GetErrorMessage();
+  return error == SQLITE_OK;
 }
 
 bool Connection::ExecuteWithTimeout(const char* sql, base::TimeDelta timeout) {
@@ -225,7 +232,7 @@
 
   ScopedBusyTimeout busy_timeout(db_);
   busy_timeout.SetTimeout(timeout);
-  return sqlite3_exec(db_, sql, NULL, NULL, NULL) == SQLITE_OK;
+  return Execute(sql);
 }
 
 bool Connection::HasCachedStatement(const StatementID& id) const {
@@ -259,22 +266,28 @@
 
   sqlite3_stmt* stmt = NULL;
   if (sqlite3_prepare_v2(db_, sql, -1, &stmt, NULL) != SQLITE_OK) {
-    // Treat this as non-fatal, it can occur in a number of valid cases, and
-    // callers should be doing their own error handling.
-    DLOG(WARNING) << "SQL compile error " << GetErrorMessage();
+    // This is evidence of a syntax error in the incoming SQL.
+    DLOG(FATAL) << "SQL compile error " << GetErrorMessage();
     return new StatementRef(this, NULL);
   }
   return new StatementRef(this, stmt);
 }
 
+bool Connection::IsSQLValid(const char* sql) {
+  sqlite3_stmt* stmt = NULL;
+  if (sqlite3_prepare_v2(db_, sql, -1, &stmt, NULL) != SQLITE_OK)
+    return false;
+
+  sqlite3_finalize(stmt);
+  return true;
+}
+
 bool Connection::DoesTableExist(const char* table_name) const {
   // GetUniqueStatement can't be const since statements may modify the
   // database, but we know ours doesn't modify it, so the cast is safe.
   Statement statement(const_cast<Connection*>(this)->GetUniqueStatement(
       "SELECT name FROM sqlite_master "
       "WHERE type='table' AND name=?"));
-  if (!statement)
-    return false;
   statement.BindString(0, table_name);
   return statement.Step();  // Table exists if any row was returned.
 }
@@ -288,8 +301,6 @@
   // Our SQL is non-mutating, so this cast is OK.
   Statement statement(const_cast<Connection*>(this)->GetUniqueStatement(
       sql.c_str()));
-  if (!statement)
-    return false;
 
   while (statement.Step()) {
     if (!statement.ColumnString(1).compare(column_name))
@@ -300,7 +311,7 @@
 
 int64 Connection::GetLastInsertRowId() const {
   if (!db_) {
-    NOTREACHED();
+    DLOG(FATAL) << "Illegal use of connection without a db";
     return 0;
   }
   return sqlite3_last_insert_rowid(db_);
@@ -308,7 +319,7 @@
 
 int Connection::GetLastChangeCount() const {
   if (!db_) {
-    NOTREACHED();
+    DLOG(FATAL) << "Illegal use of connection without a db";
     return 0;
   }
   return sqlite3_changes(db_);
@@ -339,7 +350,7 @@
 
 bool Connection::OpenInternal(const std::string& file_name) {
   if (db_) {
-    NOTREACHED() << "sql::Connection is already open.";
+    DLOG(FATAL) << "sql::Connection is already open.";
     return false;
   }
 
@@ -370,7 +381,7 @@
     // which requests exclusive locking but doesn't get it is almost
     // certain to be ill-tested.
     if (!Execute("PRAGMA locking_mode=EXCLUSIVE"))
-      NOTREACHED() << "Could not set locking mode: " << GetErrorMessage();
+      DLOG(FATAL) << "Could not set locking mode: " << GetErrorMessage();
   }
 
   const base::TimeDelta kBusyTimeout =
@@ -384,17 +395,17 @@
     DCHECK_LE(page_size_, kSqliteMaxPageSize);
     const std::string sql = StringPrintf("PRAGMA page_size=%d", page_size_);
     if (!ExecuteWithTimeout(sql.c_str(), kBusyTimeout))
-      NOTREACHED() << "Could not set page size: " << GetErrorMessage();
+      DLOG(FATAL) << "Could not set page size: " << GetErrorMessage();
   }
 
   if (cache_size_ != 0) {
     const std::string sql = StringPrintf("PRAGMA cache_size=%d", cache_size_);
     if (!ExecuteWithTimeout(sql.c_str(), kBusyTimeout))
-      NOTREACHED() << "Could not set cache size: " << GetErrorMessage();
+      DLOG(FATAL) << "Could not set cache size: " << GetErrorMessage();
   }
 
   if (!ExecuteWithTimeout("PRAGMA secure_delete=ON", kBusyTimeout)) {
-    NOTREACHED() << "Could not enable secure_delete: " << GetErrorMessage();
+    DLOG(FATAL) << "Could not enable secure_delete: " << GetErrorMessage();
     Close();
     return false;
   }
@@ -404,8 +415,7 @@
 
 void Connection::DoRollback() {
   Statement rollback(GetCachedStatement(SQL_FROM_HERE, "ROLLBACK"));
-  if (rollback)
-    rollback.Run();
+  rollback.Run();
 }
 
 void Connection::StatementRefCreated(StatementRef* ref) {
@@ -416,7 +426,7 @@
 void Connection::StatementRefDeleted(StatementRef* ref) {
   StatementRefSet::iterator i = open_statements_.find(ref);
   if (i == open_statements_.end())
-    NOTREACHED();
+    DLOG(FATAL) << "Could not find statement";
   else
     open_statements_.erase(i);
 }
@@ -436,7 +446,7 @@
   if (error_delegate_.get())
     return error_delegate_->OnError(err, this, stmt);
   // The default handling is to assert on debug and to ignore on release.
-  NOTREACHED() << GetErrorMessage();
+  DLOG(FATAL) << GetErrorMessage();
   return err;
 }