Thunderbird 3.0 rc1 release branch only! Bug 529995 - startup crash [@ columnName] in sqlite. de-danger and fix-up everyone who depended on traditional semantics v2. r+sr=Standard8, a=Standard8 COMM1915_20091112_RELBRANCH SUNBIRD_1_0b1_BUILD4 SUNBIRD_1_0b1_BUILD5 SUNBIRD_1_0b1_BUILD6 SUNBIRD_1_0b1_RELEASE THUNDERBIRD_3_0_RELEASE THUNDERBIRD_3_0rc1_BUILD3 THUNDERBIRD_3_0rc1_RELEASE THUNDERBIRD_3_0rc2_BUILD1 THUNDERBIRD_3_0rc2_RELEASE THUNDERBIRD_3_0rc3_BUILD1 THUNDERBIRD_3_0rc3_RELEASE
authorAndrew Sutherland <asutherland@asutherland.org>
Fri, 20 Nov 2009 16:02:47 -0800
branchCOMM1915_20091112_RELBRANCH
changeset 26598 6dc036c103348ccc2dfd7eda29edea6e62be0cd0
parent 26597 8fea1fcb727a977cf37e99a357d33d979fa10947
child 26599 b88916ee9aba420b759bb43808fe01463fc53b7f
push id2137
push userbugmail@asutherland.org
push dateSat, 21 Nov 2009 00:03:33 +0000
reviewersStandard8
bugs529995
milestone1.9.1.5
Thunderbird 3.0 rc1 release branch only! Bug 529995 - startup crash [@ columnName] in sqlite. de-danger and fix-up everyone who depended on traditional semantics v2. r+sr=Standard8, a=Standard8
extensions/cookie/nsPermissionManager.cpp
netwerk/cookie/src/nsCookieService.cpp
storage/src/mozStorageStatementWrapper.cpp
toolkit/components/downloads/src/nsDownloadManager.cpp
toolkit/components/downloads/test/schema_migration/test_migration_to_2.js
toolkit/components/passwordmgr/src/storage-mozStorage.js
toolkit/components/places/src/nsNavHistory.cpp
toolkit/components/satchel/src/nsStorageFormHistory.cpp
--- a/extensions/cookie/nsPermissionManager.cpp
+++ b/extensions/cookie/nsPermissionManager.cpp
@@ -221,16 +221,20 @@ nsPermissionManager::InitDB(PRBool aRemo
     // a column is interpreted, make sure you also change its name so this
     // check will catch it.
     default:
       {
         // check if all the expected columns exist
         nsCOMPtr<mozIStorageStatement> stmt;
         rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
           "SELECT host, type, permission FROM moz_hosts"), getter_AddRefs(stmt));
+        PRInt32 state;
+        if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(stmt->GetState(&state)) &&
+            state == mozIStorageStatement::MOZ_STORAGE_STATEMENT_INVALID)
+          rv = NS_ERROR_UNEXPECTED;
         if (NS_SUCCEEDED(rv))
           break;
 
         // our columns aren't there - drop the table!
         rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DROP TABLE moz_hosts"));
         NS_ENSURE_SUCCESS(rv, rv);
 
         rv = CreateTable();
--- a/netwerk/cookie/src/nsCookieService.cpp
+++ b/netwerk/cookie/src/nsCookieService.cpp
@@ -551,16 +551,20 @@ nsCookieService::InitDB(PRBool aDeleteEx
     // check will catch it.
     default:
       {
         // check if all the expected columns exist
         nsCOMPtr<mozIStorageStatement> stmt;
         rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
           "SELECT id, name, value, host, path, expiry, isSecure, isHttpOnly "
           "FROM moz_cookies"), getter_AddRefs(stmt));
+        PRInt32 state;
+        if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(stmt->GetState(&state)) &&
+            state == mozIStorageStatement::MOZ_STORAGE_STATEMENT_INVALID)
+          rv = NS_ERROR_UNEXPECTED;
         if (NS_SUCCEEDED(rv))
           break;
 
         // our columns aren't there - drop the table!
         rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING("DROP TABLE moz_cookies"));
         NS_ENSURE_SUCCESS(rv, rv);
 
         rv = CreateTable();
--- a/storage/src/mozStorageStatementWrapper.cpp
+++ b/storage/src/mozStorageStatementWrapper.cpp
@@ -74,23 +74,31 @@ NS_IMPL_ISUPPORTS2(
 
 NS_IMETHODIMP
 StatementWrapper::Initialize(mozIStorageStatement *aStatement)
 {
   NS_ASSERTION(mStatement == nsnull, "StatementWrapper is already initialized");
   NS_ENSURE_ARG_POINTER(aStatement);
 
   mStatement = static_cast<Statement *>(aStatement);
+  // Get the raw sqlite3_stmt.  This will be NULL if the statement is invalid.
+  // In that case, we should fail to initialize and make sure that we restore
+  // our state to invalid (mStatement == NULL).
+  sqlite3_stmt *stmt = nativeStatement();
+  if (stmt == nsnull) {
+    mStatement = nsnull;
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
 
   // fetch various things we care about
   (void)mStatement->GetParameterCount(&mParamCount);
   (void)mStatement->GetColumnCount(&mResultColumnCount);
 
   for (unsigned int i = 0; i < mResultColumnCount; i++) {
-    const void *name = ::sqlite3_column_name16(nativeStatement(), i);
+    const void *name = ::sqlite3_column_name16(stmt, i);
     (void)mColumnNames.AppendElement(nsDependentString(static_cast<const PRUnichar*>(name)));
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 StatementWrapper::GetStatement(mozIStorageStatement **_statement)
--- a/toolkit/components/downloads/src/nsDownloadManager.cpp
+++ b/toolkit/components/downloads/src/nsDownloadManager.cpp
@@ -565,16 +565,20 @@ nsDownloadManager::InitFileDB(PRBool *aD
   default:
     {
       nsCOMPtr<mozIStorageStatement> stmt;
       rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
         "SELECT id, name, source, target, tempPath, startTime, endTime, state, "
                "referrer, entityID, currBytes, maxBytes, mimeType, "
                "preferredApplication, preferredAction, autoResume "
         "FROM moz_downloads"), getter_AddRefs(stmt));
+      PRInt32 state;
+      if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(stmt->GetState(&state)) &&
+          state == mozIStorageStatement::MOZ_STORAGE_STATEMENT_INVALID)
+        rv = NS_ERROR_UNEXPECTED;
       if (NS_SUCCEEDED(rv))
         break;
 
       // if the statement fails, that means all the columns were not there.
       // First we backup the database
       nsCOMPtr<mozIStorageService> storage =
         do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
       NS_ENSURE_TRUE(storage, NS_ERROR_NOT_AVAILABLE);
--- a/toolkit/components/downloads/test/schema_migration/test_migration_to_2.js
+++ b/toolkit/components/downloads/test/schema_migration/test_migration_to_2.js
@@ -50,16 +50,18 @@ function run_test()
 
   // check schema version
   do_check_true(dbConn.schemaVersion >= 2);
 
   // Check that the column no longer exists
   try {
     // throws when it doesn't exist
     stmt = dbConn.createStatement("SELECT iconURL FROM moz_downloads");
+    // force creation of the statement.
+    stmt.columnCount;
     do_throw("should not get here");
   } catch (e) {
     do_check_eq(Cr.NS_ERROR_FAILURE, e.result);
   }
 
   // now we check the entries
   stmt = dbConn.createStatement(
     "SELECT name, source, target, startTime, endTime, state " +
--- a/toolkit/components/passwordmgr/src/storage-mozStorage.js
+++ b/toolkit/components/passwordmgr/src/storage-mozStorage.js
@@ -1384,19 +1384,22 @@ LoginManagerStorage_mozStorage.prototype
     /*
      * _dbMigrateToVersion2
      *
      * Version 2 adds a GUID column. Existing logins are assigned a random GUID.
      */
     _dbMigrateToVersion2 : function () {
         // Check to see if GUID column already exists.
         let exists = true;
-        try { 
+        try {
             let stmt = this._dbConnection.createStatement(
                            "SELECT guid FROM moz_logins");
+            let wrapped = Cc["@mozilla.org/storage/statement-wrapper;1"].
+                          createInstance(Ci.mozIStorageStatementWrapper);
+            wrapped.initialize(stmt);
             // (no need to execute statement, if it compiled we're good)
             stmt.finalize();
         } catch (e) {
             exists = false;
         }
 
         // Add the new column and index only if needed.
         if (!exists) {
@@ -1449,23 +1452,27 @@ LoginManagerStorage_mozStorage.prototype
      *
      * Version 3 adds a encType column.
      */
     _dbMigrateToVersion3 : function () {
         // Check to see if encType column already exists.
         let exists = true;
         let query = "SELECT encType FROM moz_logins";
         let stmt;
-        try { 
+        try {
             stmt = this._dbConnection.createStatement(query);
+            let wrapped = Cc["@mozilla.org/storage/statement-wrapper;1"].
+                          createInstance(Ci.mozIStorageStatementWrapper);
+            wrapped.initialize(stmt);
             // (no need to execute statement, if it compiled we're good)
             stmt.finalize();
         } catch (e) {
             exists = false;
         }
+        stmt = null;
 
         // Add the new column and index only if needed.
         if (!exists) {
             query = "ALTER TABLE moz_logins ADD COLUMN encType INTEGER";
             this._dbConnection.executeSimpleSQL(query);
 
             query = "CREATE INDEX IF NOT EXISTS " +
                         "moz_logins_encType_index ON moz_logins (encType)";
@@ -1502,17 +1509,17 @@ LoginManagerStorage_mozStorage.prototype
                 stmt.execute();
             } catch (e) {
                 this.log("Failed setting encType: " + e);
                 throw e;
             } finally {
                 stmt.reset();
             }
         }
-        
+
     },
 
 
     /*
      * _dbAreExpectedColumnsPresent
      *
      * Sanity check to ensure that the columns this version of the code expects
      * are present in the DB we're using.
@@ -1525,30 +1532,36 @@ LoginManagerStorage_mozStorage.prototype
                        "formSubmitURL, " +
                        "usernameField, " +
                        "passwordField, " +
                        "encryptedUsername, " +
                        "encryptedPassword, " +
                        "guid, " +
                        "encType " +
                     "FROM moz_logins";
-        try { 
+        try {
             let stmt = this._dbConnection.createStatement(query);
+            let wrapped = Cc["@mozilla.org/storage/statement-wrapper;1"].
+                          createInstance(Ci.mozIStorageStatementWrapper);
+            wrapped.initialize(stmt);
             // (no need to execute statement, if it compiled we're good)
             stmt.finalize();
         } catch (e) {
             return false;
         }
 
         query = "SELECT " +
                    "id, " +
                    "hostname " +
                 "FROM moz_disabledHosts";
-        try { 
+        try {
             let stmt = this._dbConnection.createStatement(query);
+            let wrapped = Cc["@mozilla.org/storage/statement-wrapper;1"].
+                          createInstance(Ci.mozIStorageStatementWrapper);
+            wrapped.initialize(stmt);
             // (no need to execute statement, if it compiled we're good)
             stmt.finalize();
         } catch (e) {
             return false;
         }
 
         this.log("verified that expected columns are present in DB.");
         return true;
--- a/toolkit/components/places/src/nsNavHistory.cpp
+++ b/toolkit/components/places/src/nsNavHistory.cpp
@@ -1453,16 +1453,20 @@ nsresult
 nsNavHistory::MigrateV3Up(mozIStorageConnection* aDBConn) 
 {
   // if type col is already there, then a partial update occurred.
   // return, making no changes, and allowing db version to be updated.
   nsCOMPtr<mozIStorageStatement> statement;
   nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
       "SELECT type from moz_annos"),
     getter_AddRefs(statement));
+  PRInt32 state;
+  if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(statement->GetState(&state)) &&
+      state == mozIStorageStatement::MOZ_STORAGE_STATEMENT_INVALID)
+    rv = NS_ERROR_UNEXPECTED;
   if (NS_SUCCEEDED(rv))
     return NS_OK;
 
   // add type column to moz_annos
   rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
       "ALTER TABLE moz_annos ADD type INTEGER DEFAULT 0"));
   if (NS_FAILED(rv)) {
     // if the alteration failed, force-migrate
@@ -1483,31 +1487,38 @@ nsNavHistory::MigrateV6Up(mozIStorageCon
   mozStorageTransaction transaction(aDBConn, PR_FALSE);
 
   // if dateAdded & lastModified cols are already there, then a partial update occurred,
   // and so we should not attempt to add these cols.
   nsCOMPtr<mozIStorageStatement> statement;
   nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
       "SELECT a.dateAdded, a.lastModified FROM moz_annos a"), 
     getter_AddRefs(statement));
+  PRInt32 state;
+  if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(statement->GetState(&state)) &&
+      state == mozIStorageStatement::MOZ_STORAGE_STATEMENT_INVALID)
+    rv = NS_ERROR_UNEXPECTED;
   if (NS_FAILED(rv)) {
     // add dateAdded and lastModified columns to moz_annos
     rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
         "ALTER TABLE moz_annos ADD dateAdded INTEGER DEFAULT 0"));
     NS_ENSURE_SUCCESS(rv, rv);
     rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
         "ALTER TABLE moz_annos ADD lastModified INTEGER DEFAULT 0"));
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // if dateAdded & lastModified cols are already there, then a partial update occurred,
   // and so we should not attempt to add these cols.  see bug #408443 for details.
   rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
       "SELECT b.dateAdded, b.lastModified FROM moz_items_annos b"), 
     getter_AddRefs(statement));
+  if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(statement->GetState(&state)) &&
+      state == mozIStorageStatement::MOZ_STORAGE_STATEMENT_INVALID)
+    rv = NS_ERROR_UNEXPECTED;
   if (NS_FAILED(rv)) {
     // add dateAdded and lastModified columns to moz_items_annos
     rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
         "ALTER TABLE moz_items_annos ADD dateAdded INTEGER DEFAULT 0"));
     NS_ENSURE_SUCCESS(rv, rv);
     rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
         "ALTER TABLE moz_items_annos ADD lastModified INTEGER DEFAULT 0"));
     NS_ENSURE_SUCCESS(rv, rv);
@@ -1526,16 +1537,19 @@ nsNavHistory::MigrateV6Up(mozIStorageCon
 
 
   // bug #371800 - remove moz_places.user_title
   // test for moz_places.user_title
   nsCOMPtr<mozIStorageStatement> statement2;
   rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
       "SELECT user_title FROM moz_places"),
     getter_AddRefs(statement2));
+  if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(statement2->GetState(&state)) &&
+      state == mozIStorageStatement::MOZ_STORAGE_STATEMENT_INVALID)
+    rv = NS_ERROR_UNEXPECTED;
   if (NS_SUCCEEDED(rv)) {
     // 1. Indexes are moved along with the renamed table. Since we're dropping
     // that table, we're also dropping its indexes, and later re-creating them
     // for the new table.
     rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
         "DROP INDEX IF EXISTS moz_places_urlindex"));
     NS_ENSURE_SUCCESS(rv, rv);
     rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
@@ -1644,16 +1658,21 @@ nsNavHistory::MigrateV7Up(mozIStorageCon
     NS_ENSURE_SUCCESS(rv, rv);
   }
 
   // for existing profiles, we may not have a frecency column
   nsCOMPtr<mozIStorageStatement> hasFrecencyStatement;
   rv = aDBConn->CreateStatement(NS_LITERAL_CSTRING(
       "SELECT frecency FROM moz_places"),
     getter_AddRefs(hasFrecencyStatement));
+  PRInt32 state;
+  if (NS_SUCCEEDED(rv) &&
+      NS_SUCCEEDED(hasFrecencyStatement->GetState(&state)) &&
+      state == mozIStorageStatement::MOZ_STORAGE_STATEMENT_INVALID)
+    rv = NS_ERROR_UNEXPECTED;
 
   if (NS_FAILED(rv)) {
     // add frecency column to moz_places, default to -1
     // so that all the frecencies are invalid and we'll
     // recalculate them on idle.
     rv = aDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
         "ALTER TABLE moz_places ADD frecency INTEGER DEFAULT -1 NOT NULL"));
     NS_ENSURE_SUCCESS(rv, rv);
--- a/toolkit/components/satchel/src/nsStorageFormHistory.cpp
+++ b/toolkit/components/satchel/src/nsStorageFormHistory.cpp
@@ -760,16 +760,20 @@ nsresult
 nsFormHistory::MigrateToVersion1()
 {
   // Check to see if the new columns already exist (could be a v1 DB that
   // was downgraded to v0). If they exist, we don't need to add them.
   nsCOMPtr<mozIStorageStatement> stmt;
   nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
                   "SELECT timesUsed, firstUsed, lastUsed FROM moz_formhistory"),
                   getter_AddRefs(stmt));
+  PRInt32 state;
+  if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(stmt->GetState(&state)) &&
+      state == mozIStorageStatement::MOZ_STORAGE_STATEMENT_INVALID)
+    rv = NS_ERROR_UNEXPECTED;
 
   PRBool columnsExist = !!NS_SUCCEEDED(rv);
 
   if (!columnsExist) {
     rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
       "ALTER TABLE moz_formhistory ADD COLUMN timesUsed INTEGER"));
     NS_ENSURE_SUCCESS(rv, rv);
     rv = mDBConn->ExecuteSimpleSQL(NS_LITERAL_CSTRING(
@@ -872,16 +876,20 @@ nsFormHistory::dbCleanup()
 PRBool
 nsFormHistory::dbAreExpectedColumnsPresent()
 {
   // If the statement succeeds, all the columns are there.
   nsCOMPtr<mozIStorageStatement> stmt;
   nsresult rv = mDBConn->CreateStatement(NS_LITERAL_CSTRING(
                   "SELECT fieldname, value, timesUsed, firstUsed, lastUsed "
                   "FROM moz_formhistory"), getter_AddRefs(stmt));
+  PRInt32 state;
+  if (NS_SUCCEEDED(rv) && NS_SUCCEEDED(stmt->GetState(&state)) &&
+      state == mozIStorageStatement::MOZ_STORAGE_STATEMENT_INVALID)
+    rv = NS_ERROR_UNEXPECTED;
   return NS_SUCCEEDED(rv) ? PR_TRUE : PR_FALSE;
 }
 
 
 nsresult
 nsFormHistory::AutoCompleteSearch(const nsAString &aInputName,
                                   const nsAString &aInputValue,
                                   nsIAutoCompleteSimpleResult *aPrevResult,