AllPeers Cross Reference:
allpeers/ database/ src/ apDatabaseAutomaticUpdater.cpp
CVS Log
CVS Blame
Raw output
changes to
this file in
the last:
day
week
month
  1 /* ***** BEGIN LICENSE BLOCK *****
  2  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  3  *
  4  * The contents of this file are subject to the Mozilla Public License Version
  5  * 1.1 (the "License"); you may not use this file except in compliance with
  6  * the License. You may obtain a copy of the License at
  7  * http://www.mozilla.org/MPL/
  8  *
  9  * Software distributed under the License is distributed on an "AS IS" basis,
 10  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 11  * for the specific language governing rights and limitations under the
 12  * License.
 13  *
 14  * The Original Code is AllPeers Limited
 15  *
 16  * The Initial Developer of the Original Code is
 17  * AllPeers Limited.
 18  * Portions created by the Initial Developer are Copyright (C) 2007
 19  * the Initial Developer. All Rights Reserved.
 20  *
 21  * Contributor(s):
 22  *
 23  * Alternatively, the contents of this file may be used under the terms of
 24  * either of the GNU General Public License Version 2 or later (the "GPL"),
 25  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
 26  * in which case the provisions of the GPL or the LGPL are applicable instead
 27  * of those above. If you wish to allow use of your version of this file only
 28  * under the terms of either the GPL or the LGPL, and not to allow others to
 29  * use your version of this file under the terms of the MPL, indicate your
 30  * decision by deleting the provisions above and replace them with the notice
 31  * and other provisions required by the GPL or the LGPL. If you do not delete
 32  * the provisions above, a recipient may use your version of this file under
 33  * the terms of any one of the MPL, the GPL or the LGPL.
 34  *
 35  * ***** END LICENSE BLOCK ***** */
 36 
 37 // apDatabaseAutomaticUpdater.cpp: implementation of the apDatabaseAutomaticUpdater class.
 38 //
 39 // Project:    AllPeers
 40 // First author:    Jan Bambas
 41 // E-mail:          honzab@allpeers.com
 42 //
 43 // Creation date:
 44 // Modified by:
 45 //
 46 //////////////////////////////////////////////////////////////////////
 47 
 48 #include "apDatabaseAutomaticUpdater.h"
 49 
 50 #include "apIResourceDescManager.h"
 51 #include "apIDatabaseInfo.h"
 52 #include "apIDatabaseTableInfo.h"
 53 #include "apIDateTime.h"
 54 #include "apISplashScreen.h"
 55 #include "mozIStorageConnection.h"
 56 #include "mozIStorageService.h"
 57 #include "mozIStorageStatement.h"
 58 
 59 #include "apDatabaseService.h"
 60 #include "apDatabaseError.h"
 61 #include "nsComponentManagerUtils.h"
 62 #include "nsILocalFile.h"
 63 #include "nsIFileStreams.h"
 64 #include "nsIProxyObjectManager.h"
 65 #include "nsIStringBundle.h"
 66 #include "nsIZipWriter.h"
 67 #include "nsIEventQueue.h"
 68 #include "nsIEventQueueService.h"
 69 #include "nsServiceManagerUtils.h"
 70 #include "nsPrintfCString.h"
 71 #include "nsPromiseFlatString.h"
 72 #include "nsTArray.h"
 73 #include "apAutoFreePtr.h"
 74 #include "apDebug.h"
 75 #include "apCallbackEvent.h"
 76 
 77 #include "prio.h"
 78 
 79 #ifndef AP_USE_MYSQL
 80 #include "sqlite3.h"
 81 #endif
 82 
 83 #include "apILoggingService.h"
 84 #include "apLogging.h"
 85 #include "apProfiling.h"
 86 
 87 #define AP_LOG_MODULE_SCREENNAME "database"
 88 
 89 NS_IMPL_THREADSAFE_ISUPPORTS1(apDatabaseAutomaticUpdater, apIBackgroundProcess)
 90 
 91 // Common SQL statements
 92 //================================================================================
 93 
 94 #ifdef AP_USE_MYSQL
 95   #define SQL_SET_DB_VERSION    "UPDATE _Global_Variables SET ParamValue='%d' WHERE ParamName='user_version'"
 96   #define SQL_SELECT_TABLES_LIKE "SHOW TABLES LIKE '%s';"
 97 #else
 98   #define SQL_SET_DB_VERSION    "PRAGMA user_version=%d"
 99   #define SQL_SELECT_TABLES_LIKE "SELECT name FROM sqlite_master WHERE type='table' AND name LIKE '%s';"
100 #endif // AP_USE_MYSQL
101 
102 // Construction
103 //================================================================================
104 
105 apDatabaseAutomaticUpdater::apDatabaseAutomaticUpdater(apDatabaseService* databaseService,
106   PRUint32 localVersion, PRUint32 newVersion, apIBackgroundProcessRunner* runner,
107   apISplashScreen* splashScreen, nsIEventQueue* eventQueue)
108 {
109   mDatabaseService = databaseService;
110   mLocalVersion = localVersion;
111   mNewVersion = newVersion;
112   mRunner = runner;
113   mSplashScreen = splashScreen;
114   mState = INIT;
115   mUIEventQueue = eventQueue;
116 }
117 
118 //--------------------------------------------------------------------------------
119 
120 apDatabaseAutomaticUpdater::~apDatabaseAutomaticUpdater()
121 {
122 }
123 // Methods
124 //================================================================================
125 
126 nsresult apDatabaseAutomaticUpdater::UpdateTables()
127 {
128   NS_ENSURE_STATE(mDatabaseService);
129 
130   AP_PROF_METHOD("apDatabaseAutomaticUpdater::UpdateTables", mDatabaseService, NULL, 1);
131 
132   nsresult rv;
133 
134   // Instantiate the resource desc manager to make sure that the schemas are loaded
135   nsCOMPtr<apIResourceDescManager>
136       descManager(do_GetService("@allpeers.com/resource/desc-manager;1", &rv));
137   NS_ENSURE_SUCCESS(rv, rv);
138 
139   // @007 This is a hack until we have proper namespace handling in the database
140   rv = mDatabaseService->UpdateTablesForNamespace(NS_LITERAL_CSTRING("http://www.allpeers.com/schemas/resource"));
141   NS_ENSURE_SUCCESS(rv, rv);
142 
143   rv = mDatabaseService->UpdateTablesForNamespace(NS_LITERAL_CSTRING("http://www.allpeers.com/schemas/resource/metadata"));
144   NS_ENSURE_SUCCESS(rv, rv);
145 
146   return NS_OK;
147 }
148 
149 //--------------------------------------------------------------------------------
150 
151 nsresult apDatabaseAutomaticUpdater::UpdateVersion(PRInt32 newVersion)
152 {
153   AP_PROF_METHOD("apDatabaseAutomaticUpdater::UpdateVersion", mDatabaseService, NULL, 1);
154 
155   nsresult rv;
156 
157   nsPrintfCString sqlUpdate(128, SQL_SET_DB_VERSION, newVersion);
158 
159 #ifdef AP_MANUAL_CONVERSION
160     AP_LOGERROR1("Perform manual conversion [%s]", sqlUpdate.get());
161     return NS_ERROR_ABORT;
162 #endif
163 
164   rv = ExecuteSql(sqlUpdate);
165   NS_ENSURE_SUCCESS(rv, rv);
166 
167   return NS_OK;
168 }
169 
170 //--------------------------------------------------------------------------------
171 
172 #undef AP_LOG_THIS_KEY
173 #define AP_LOG_THIS_KEY(result) (result)=databaseService
174 
175 nsresult apDatabaseAutomaticUpdater::Update(apDatabaseService* databaseService,
176   PRInt32 localVersion, PRInt32 newVersion)
177 {
178   nsresult rv;
179   if (localVersion == newVersion)
180     return NS_OK;
181 
182   if (localVersion > newVersion)
183   {
184     AP_LOGERROR0("FATAL: We do not support return to older version of the product!");
185     return AP_ERROR_DATABASE_UPDATE_NO_ROLLBACK;
186   }
187 
188   nsCOMPtr<apISplashScreen>
189     splashScreen(do_CreateInstance("@allpeers.com/workbench/splash-screen;1", &rv));
190   NS_ENSURE_SUCCESS(rv, rv);
191 
192   nsCOMPtr<nsIEventQueueService> eqService
193       (do_GetService("@mozilla.org/event-queue-service;1", &rv));
194   NS_ENSURE_SUCCESS(rv, rv);
195 
196   nsCOMPtr<nsIEventQueue> eventQueue;
197   rv = eqService->ResolveEventQueue(NS_UI_THREAD_EVENTQ, getter_AddRefs(eventQueue));
198   NS_ENSURE_SUCCESS(rv, rv);
199 
200   nsCOMPtr<apIBackgroundProcessRunner>
201     runner(do_CreateInstance("@allpeers.com/core/process-runner;1", &rv));
202   NS_ENSURE_SUCCESS(rv, rv);
203 
204   nsRefPtr<apDatabaseAutomaticUpdater> updater = new apDatabaseAutomaticUpdater(databaseService,
205     localVersion, newVersion, runner, splashScreen, eventQueue);
206 
207   rv = updater->Init();
208   NS_ENSURE_SUCCESS(rv, rv);
209 
210   PRBool needsUpdate = !databaseService->IsNewDatabase();
211 
212   if (needsUpdate)
213   {
214     rv = splashScreen->Show(NS_LITERAL_CSTRING("chrome://allpeers/content/database/apDatabaseUpdate.xul"),
215       runner);
216     NS_ENSURE_SUCCESS(rv, rv);
217 
218     splashScreen->GetResult(&rv);
219   }
220   else
221   {
222     // If the database is new, it must have proper structure and data already.
223     rv = updater->UpdateTables();
224     if (NS_SUCCEEDED(rv))
225     {
226       rv = updater->UpdateVersion(newVersion);
227     }
228   }
229 
230   if (NS_FAILED(rv))
231   {
232     AP_LOGERROR0("FATAL: Something wrong happened during database update!");
233     return AP_ERROR_DATABASE_UPDATE_FAILED;
234   }
235 
236   return NS_OK;
237 }
238   
239 //--------------------------------------------------------------------------------
240 
241 #undef AP_LOG_THIS_KEY
242 #define AP_LOG_THIS_KEY(result) (result)=this
243 
244 nsresult apDatabaseAutomaticUpdater::PostSplashScreenSetText(const nsACString& text)
245 {
246   nsAutoPtr<nsCString> asyncText(new nsCString(text));
247   NS_ENSURE_TRUE(asyncText, NS_ERROR_OUT_OF_MEMORY);
248 
249   AP_LOG1("Posting splash screen SetText(\"%s\")", (*asyncText).get());
250 
251   apCallbackEvent<apDatabaseAutomaticUpdater> *event = new 
252     apCallbackEvent<apDatabaseAutomaticUpdater>(nsnull, this, 
253       &apDatabaseAutomaticUpdater::HandleSplashScreenSetText, (void*)asyncText.get(), nsnull);
254   NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
255 
256   nsresult rv;
257 
258   rv = mUIEventQueue->PostEvent(event);
259   if (NS_FAILED(rv)) PL_DestroyEvent(event);
260   NS_ENSURE_SUCCESS(rv, rv);
261 
262   asyncText.forget();
263   return NS_OK;
264 }
265 
266 //--------------------------------------------------------------------------------
267 
268 nsresult apDatabaseAutomaticUpdater::PostSplashScreenSetResultAndClose(nsresult result)
269 {
270   nsAutoPtr<nsresult> asyncResult(new nsresult);
271   *asyncResult = result;
272   NS_ENSURE_TRUE(asyncResult, NS_ERROR_OUT_OF_MEMORY);
273 
274   AP_LOG1("Posting splash screen SetResult(0x%08x) and Close()", *asyncResult);
275 
276   apCallbackEvent<apDatabaseAutomaticUpdater> *event = new 
277     apCallbackEvent<apDatabaseAutomaticUpdater>(nsnull, this, 
278       &apDatabaseAutomaticUpdater::HandleSplashScreenSetResultAndClose, nsnull, asyncResult);
279   NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
280 
281   nsresult rv;
282 
283   rv = mUIEventQueue->PostEvent(event);
284   if (NS_FAILED(rv)) PL_DestroyEvent(event);
285   NS_ENSURE_SUCCESS(rv, rv);
286 
287   asyncResult.forget();
288   return NS_OK;
289 }
290 
291 //--------------------------------------------------------------------------------
292 
293 void apDatabaseAutomaticUpdater::HandleSplashScreenSetText(void* param, nsresult* result)
294 {
295   nsAutoPtr<nsCString> asyncText((nsCString*)param);
296 
297   AP_LOG1("Handling splash screen SetText(\"%s\")", (*asyncText).get());
298 
299   nsresult rv;
300   rv = mSplashScreen->SetText(*asyncText);
301   if (NS_FAILED(rv))
302     AP_LOGWARN0("Could not set text of the splash screen");
303 }
304 
305 //--------------------------------------------------------------------------------
306 
307 void apDatabaseAutomaticUpdater::HandleSplashScreenSetResultAndClose(void* param, nsresult* result)
308 {
309   AP_LOG1("Handling splash screen SetResult(0x%08x) and Close()", *result);
310 
311   nsAutoPtr<nsresult> asyncResult(result);
312 
313   nsresult rv;
314   rv = mSplashScreen->SetResult(*result);
315   if (NS_FAILED(rv))
316     AP_LOGWARN0("Could not set result of the splash screen");
317 
318   rv = mSplashScreen->Close();
319   if (NS_FAILED(rv))
320     AP_LOGERROR0("Could not close the update database splash screen");
321 }
322 
323 //--------------------------------------------------------------------------------
324 
325 nsresult apDatabaseAutomaticUpdater::ExecuteSql(const nsACString& sql)
326 {
327   nsresult rv;
328   rv = mDatabaseService->DoExecuteSqlWithParams(sql, nsnull, nsnull);
329   if (NS_SUCCEEDED(rv))
330   {
331     AP_LOG1("Database updated with SQL: %s", nsPromiseFlatCString(sql).get());
332   }
333   else
334   {
335     AP_LOGERROR1("SQL execution failed during database update: %s",
336       nsPromiseFlatCString(sql).get());
337   }
338   return rv;
339 }
340 
341 //--------------------------------------------------------------------------------
342 
343 nsCString apDatabaseAutomaticUpdater::GetBundleText(const char* name)
344 {
345   nsIStringBundle* bundle = GetDatabaseBundle();
346 
347   nsAutoString unicodeName = NS_ConvertUTF8toUTF16(nsDependentCString(name));
348 
349   nsXPIDLString str;
350   bundle->GetStringFromName(unicodeName.get(), getter_Copies(str));
351 
352   return NS_ConvertUTF16toUTF8(str);
353 }
354 
355 // Implementation - apIBackgroundProcess
356 //================================================================================
357 
358 NS_IMETHODIMP apDatabaseAutomaticUpdater::Initiate()
359 {
360   nsresult rv;
361   nsCOMPtr<apILoggingService>
362     logService(do_GetService("@allpeers/logging-service;1", &rv));
363   NS_ENSURE_SUCCESS(rv, rv);
364 
365   rv = logService->GetLogSeverity(&mOldSeverity);
366   NS_ENSURE_SUCCESS(rv, rv);
367 
368   rv = logService->SetLogSeverity(apILoggingService::SEVERITY_BONE);
369   NS_ENSURE_SUCCESS(rv, rv);
370   
371   return NS_OK;
372 }
373 
374 //--------------------------------------------------------------------------------
375 
376 NS_IMETHODIMP apDatabaseAutomaticUpdater::GetLoopCondition(PRBool *aLoopCondition)
377 {
378   *aLoopCondition = (mState != DONE);
379   return NS_OK;
380 }
381 
382 //--------------------------------------------------------------------------------
383 
384 NS_IMETHODIMP apDatabaseAutomaticUpdater::LoopBody(nsISupports **object)
385 {
386   nsresult rv;
387 
388   mState++;
389 
390   switch (mState)
391   {
392     case BACKINGUP:
393       {
394 #ifndef AP_SERVER
395         rv = PostSplashScreenSetText(GetBundleText("DATABASE_UPDATE_BACKINGUP"));
396         NS_ENSURE_SUCCESS(rv, rv);
397 
398         // Drop any temporary tables just in case the last update try failed
399         rv = DropTemporaryTables();
400         NS_ENSURE_SUCCESS(rv, rv);
401 
402         rv = ZipAndBackupDatabase();
403         NS_ENSURE_SUCCESS(rv, rv);
404 #endif
405         break;
406       }
407     case PREPARING:
408       {
409         rv = PostSplashScreenSetText(GetBundleText("DATABASE_UPDATE_PREPARING"));
410         NS_ENSURE_SUCCESS(rv, rv);
411 
412         rv = UpdateConditional_Caller(711, &apDatabaseAutomaticUpdater::UpdateTo_711);
413         NS_ENSURE_SUCCESS(rv, rv);
414 
415 #ifndef AP_SERVER
416         // We intentionally ignore the error code, some older clients may have
417         // database schema 0, but if they did not register, the tables may
418         // not exist as they were created on demand.
419         UpdateConditional_Caller(715, &apDatabaseAutomaticUpdater::UpdateTo_715);
420 #endif
421 
422         // @333 - DO NOT USE UpdateTo_Caller HERE!
423         break;
424       }
425     case TABLES:
426       {
427         rv = PostSplashScreenSetText(GetBundleText("DATABASE_UPDATE_TABLES"));
428         NS_ENSURE_SUCCESS(rv, rv);
429 
430         // Verify all schemas, create new if does no exist.
431         rv = UpdateTables();
432         NS_ENSURE_SUCCESS(rv, rv);
433 
434         break;
435       }
436     case UPGRADING:
437       {
438         rv = PostSplashScreenSetText(GetBundleText("DATABASE_UPDATE_UPGRADING"));
439         NS_ENSURE_SUCCESS(rv, rv);
440 
441         // Please note that we update the database number for each version to be
442         // able to tell, when it failed.
443         // @333 - We should be doing this in one transaction, so that database
444         //        does not stay somewhere in the middle. This is left for later.
445         rv = UpdateTo_Caller(546, &apDatabaseAutomaticUpdater::UpdateTo_546);
446         NS_ENSURE_SUCCESS(rv, rv);
447         rv = UpdateTo_Caller(547, &apDatabaseAutomaticUpdater::UpdateTo_547);
448         NS_ENSURE_SUCCESS(rv, rv);
449         rv = UpdateTo_Caller(601, &apDatabaseAutomaticUpdater::UpdateTo_601);
450         NS_ENSURE_SUCCESS(rv, rv);
451         rv = UpdateTo_Caller(602, &apDatabaseAutomaticUpdater::UpdateTo_602);
452         NS_ENSURE_SUCCESS(rv, rv);
453         rv = UpdateTo_Caller(603, &apDatabaseAutomaticUpdater::UpdateTo_603);
454         NS_ENSURE_SUCCESS(rv, rv);
455         rv = UpdateTo_Caller(604, &apDatabaseAutomaticUpdater::UpdateTo_604);
456         NS_ENSURE_SUCCESS(rv, rv);
457 #ifndef AP_SERVER
458         // On servers we do not use peer status
459         rv = UpdateTo_Caller(606, &apDatabaseAutomaticUpdater::UpdateTo_606);
460         NS_ENSURE_SUCCESS(rv, rv);
461 #endif
462 #ifdef AP_SERVER
463         // This is only needed on offline server, we execute it on all servers
464         rv = UpdateTo_Caller(607, &apDatabaseAutomaticUpdater::UpdateTo_607);
465         NS_ENSURE_SUCCESS(rv, rv);
466 #endif
467         rv = UpdateTo_Caller(608, &apDatabaseAutomaticUpdater::UpdateTo_608);
468         NS_ENSURE_SUCCESS(rv, rv);
469 #ifndef AP_SERVER
470         // This could not have happended on servers
471         if (mLocalVersion >= 601)
472         {
473           rv = UpdateTo_Caller(609, &apDatabaseAutomaticUpdater::UpdateTo_609);
474           NS_ENSURE_SUCCESS(rv, rv);
475         }
476         // This has already been updated on registrar
477         rv = UpdateTo_Caller(610, &apDatabaseAutomaticUpdater::UpdateTo_610);
478         NS_ENSURE_SUCCESS(rv, rv);
479 #endif
480         rv = UpdateTo_Caller(614, &apDatabaseAutomaticUpdater::UpdateTo_614);
481         NS_ENSURE_SUCCESS(rv, rv);
482 #ifndef AP_SERVER
483         // This is not needed on servers
484         rv = UpdateTo_Caller(615, &apDatabaseAutomaticUpdater::UpdateTo_615);
485         NS_ENSURE_SUCCESS(rv, rv);
486         rv = UpdateTo_Caller(650, &apDatabaseAutomaticUpdater::UpdateTo_650);
487         NS_ENSURE_SUCCESS(rv, rv);
488         rv = UpdateTo_Caller(651, &apDatabaseAutomaticUpdater::UpdateTo_651);
489         NS_ENSURE_SUCCESS(rv, rv);
490 #endif
491         rv = UpdateTo_Caller(714, &apDatabaseAutomaticUpdater::UpdateTo_714);
492         NS_ENSURE_SUCCESS(rv, rv);
493 #ifndef AP_SERVER
494         // This is not needed on servers
495         rv = UpdateTo_Caller(717, &apDatabaseAutomaticUpdater::UpdateTo_717);
496         NS_ENSURE_SUCCESS(rv, rv);
497         rv = UpdateTo_Caller(718, &apDatabaseAutomaticUpdater::UpdateTo_718);
498         NS_ENSURE_SUCCESS(rv, rv);
499         rv = UpdateTo_Caller(720, &apDatabaseAutomaticUpdater::UpdateTo_720);
500         NS_ENSURE_SUCCESS(rv, rv);
501         if (mLocalVersion >= 711) // 711 removed _Torrent table
502         {
503           rv = UpdateTo_Caller(721, &apDatabaseAutomaticUpdater::UpdateTo_721);
504           NS_ENSURE_SUCCESS(rv, rv);
505         }
506 
507         rv = UpdateTo_Caller(753, &apDatabaseAutomaticUpdater::UpdateTo_722);
508         NS_ENSURE_SUCCESS(rv, rv);
509 #endif
510 
511         if (mLocalVersion >= 719) // there were some problems with update so we delete for more version
512         {
513           rv = UpdateTo_Caller(755, &apDatabaseAutomaticUpdater::UpdateTo_755);
514           NS_ENSURE_SUCCESS(rv, rv);
515         }
516 
517 #ifndef AP_SERVER
518         rv = UpdateTo_Caller(757, &apDatabaseAutomaticUpdater::UpdateTo_757);
519         NS_ENSURE_SUCCESS(rv, rv);
520 
521         rv = UpdateTo_Caller(758, &apDatabaseAutomaticUpdater::UpdateTo_758);
522         NS_ENSURE_SUCCESS(rv, rv);
523 #endif
524         rv = UpdateTo_Caller(761, &apDatabaseAutomaticUpdater::UpdateTo_761);
525         NS_ENSURE_SUCCESS(rv, rv);
526 
527         rv = UpdateVersion(mNewVersion);
528         NS_ENSURE_SUCCESS(rv, rv);
529 
530         break;
531       }
532     case DROPPING:
533       {
534         rv = PostSplashScreenSetText(GetBundleText("DATABASE_UPDATE_DROPPING"));
535         NS_ENSURE_SUCCESS(rv, rv);
536 
537         rv = DropTemporaryTables();
538         NS_ENSURE_SUCCESS(rv, rv);
539       }
540   }
541 
542   return NS_OK;
543 }
544 
545 //--------------------------------------------------------------------------------
546 
547 NS_IMETHODIMP apDatabaseAutomaticUpdater::GetProgressMax(PRInt64* aProgressMax)
548 {
549   *aProgressMax = -1;
550   return NS_OK;
551 }
552 
553 //--------------------------------------------------------------------------------
554 
555 NS_IMETHODIMP apDatabaseAutomaticUpdater::Finished(nsresult result, nsISupports** object)
556 {
557   nsresult rv;
558 
559   rv = PostSplashScreenSetResultAndClose(result);
560   NS_ENSURE_SUCCESS(rv, rv);
561 
562   // Complete update is done so reset logging level
563   nsCOMPtr<apILoggingService>
564     logService(do_GetService("@allpeers/logging-service;1", &rv));
565   NS_ENSURE_SUCCESS(rv, rv);
566 
567   rv = logService->SetLogSeverity(mOldSeverity);
568   NS_ENSURE_SUCCESS(rv, rv);
569 
570   return NS_OK;
571 }
572 
573 // Methods - Initialization
574 //================================================================================
575 
576 nsresult apDatabaseAutomaticUpdater::Init()
577 {
578   NS_ENSURE_STATE(mRunner);
579 
580   return mRunner->SetupWithProcess(this, PR_FALSE);
581 }
582 
583 // Custom update methods
584 //================================================================================
585 
586 #ifndef AP_USE_MYSQL
587 void GetOwnerId(sqlite3_context* context, int argc, sqlite3_value** argv)
588 {
589   const unsigned char* src = sqlite3_value_text(argv[0]);
590   const char * colon = strchr((const char *) src+10, ':');
591   if (colon)
592     sqlite3_result_text(context, (char*) src, colon-(char *) src, SQLITE_TRANSIENT);
593   else
594     sqlite3_result_text(context, "", 0, SQLITE_TRANSIENT);
595 }
596 #endif
597 
598 //--------------------------------------------------------------------------------
599 
600 nsresult apDatabaseAutomaticUpdater::UpdateTo_Caller(PRUint32 updateToVersion, 
601   VersionUpdateFunction updater)
602 {
603   nsresult rv;
604 
605   if (mLocalVersion < updateToVersion && updateToVersion <= mNewVersion)
606   {
607     rv = ((*this).*updater)();
608     NS_ENSURE_SUCCESS(rv, rv);
609 
610     rv = UpdateVersion(updateToVersion);
611     NS_ENSURE_SUCCESS(rv, rv);
612   }
613   return NS_OK;
614 }
615 
616 //--------------------------------------------------------------------------------
617 
618 nsresult apDatabaseAutomaticUpdater::UpdateConditional_Caller(PRUint32 updateToVersion, 
619   VersionUpdateFunction updater)
620 {
621   nsresult rv;
622 
623   if (mLocalVersion < updateToVersion && updateToVersion <= mNewVersion)
624   {
625     rv = ((*this).*updater)();
626     NS_ENSURE_SUCCESS(rv, rv);
627   }
628 
629   return NS_OK;
630 }
631 
632 //--------------------------------------------------------------------------------
633 
634 nsresult apDatabaseAutomaticUpdater::CreateIndex(const nsACString& tableName,
635   const nsACString& indexName, const nsACString& columns)
636 {
637   nsCAutoString sql;
638 
639   sql = NS_LITERAL_CSTRING("CREATE INDEX ");
640 #ifndef AP_USE_MYSQL
641   sql += NS_LITERAL_CSTRING("IF NOT EXISTS ");
642 #endif
643   sql += indexName;
644   sql += NS_LITERAL_CSTRING(" ON ") + tableName + NS_LITERAL_CSTRING(" (") + columns + NS_LITERAL_CSTRING(")");
645 
646   nsresult rv;
647   rv = ExecuteSql(sql);
648   // @333 With mysql we do not have IF EXIST clause, and have to ignore potential error
649 #ifndef AP_USE_MYSQL
650   NS_ENSURE_SUCCESS(rv, rv);
651 #endif
652 
653   return NS_OK;
654 }
655 
656 //--------------------------------------------------------------------------------
657 
658 nsresult apDatabaseAutomaticUpdater::DropTable(const nsACString& tableName)
659 {
660   nsresult rv;
661   nsCAutoString sql;
662   sql = NS_LITERAL_CSTRING("DROP TABLE IF EXISTS ") + tableName;
663   rv = ExecuteSql(sql);
664   NS_ENSURE_SUCCESS(rv, rv);
665 
666   if (mState == PREPARING)
667   {
668     // The database info already knows about those tables, if we keep the info
669     // it will try to upgrade the table instead of creating. So if we run in
670     // preparing mode, we remove the table info here.
671 
672     nsCOMPtr<apIDatabaseInfo> databaseInfo;
673     rv = mDatabaseService->GetDatabaseInfo(getter_AddRefs(databaseInfo));
674     NS_ENSURE_SUCCESS(rv, rv);
675     rv = databaseInfo->RemoveTable(tableName);
676     NS_ENSURE_SUCCESS(rv, rv);
677   }
678 
679   return NS_OK;
680 }
681 
682 //--------------------------------------------------------------------------------
683 
684 nsresult apDatabaseAutomaticUpdater::DropIndex(const nsACString& tableName, const nsACString& indexName)
685 {
686   nsCAutoString sql;
687 
688   sql = NS_LITERAL_CSTRING("DROP INDEX ");
689 #ifndef AP_USE_MYSQL
690   sql += NS_LITERAL_CSTRING(" IF EXISTS ");
691 #endif
692   sql += indexName;
693 #ifdef AP_USE_MYSQL
694   sql += NS_LITERAL_CSTRING(" ON ") + tableName;
695 #endif
696 
697   nsresult rv;
698   rv = ExecuteSql(sql);
699   // @333 With mysql we do not have IF EXIST clause, and have to ignore potential error
700 #ifndef AP_USE_MYSQL
701   NS_ENSURE_SUCCESS(rv, rv);
702 #endif
703 
704   return NS_OK;
705 }
706 
707 //--------------------------------------------------------------------------------
708 
709 nsresult apDatabaseAutomaticUpdater::EliminateDuplicates(const nsACString& tableName)
710 {
711   nsresult rv;
712 
713   nsCAutoString tempTable(tableName + NS_LITERAL_CSTRING("_TMP"));
714 
715   rv = ExecuteSql(NS_LITERAL_CSTRING("CREATE TABLE ") + tempTable +
716     NS_LITERAL_CSTRING(" (ResourceId TEXT, Value TEXT)"));
717   NS_ENSURE_SUCCESS(rv, rv);
718 
719   rv = ExecuteSql(NS_LITERAL_CSTRING("INSERT INTO ") + tempTable +
720     NS_LITERAL_CSTRING(" SELECT DISTINCT ResourceId, Value FROM ") + tableName);
721   NS_ENSURE_SUCCESS(rv, rv);
722 
723   rv = ExecuteSql(NS_LITERAL_CSTRING("DELETE FROM ") + tableName);
724   NS_ENSURE_SUCCESS(rv, rv);
725 
726   rv = ExecuteSql(NS_LITERAL_CSTRING("INSERT INTO ") + tableName +
727     NS_LITERAL_CSTRING(" SELECT * FROM ") + tempTable);
728   NS_ENSURE_SUCCESS(rv, rv);
729 
730   rv = ExecuteSql(NS_LITERAL_CSTRING("DROP TABLE ") + tempTable);
731   NS_ENSURE_SUCCESS(rv, rv);
732 
733   return NS_OK;
734 }
735 
736 //--------------------------------------------------------------------------------
737 
738 nsresult apDatabaseAutomaticUpdater::DropTemporaryTables()
739 {
740   nsresult rv;
741   nsCOMPtr<mozIStorageStatement> statement;
742   rv = mDatabaseService->mConnection->CreateStatement(nsPrintfCString(256, SQL_SELECT_TABLES_LIKE,
743     "APTMP_%"), getter_AddRefs(statement));
744   NS_ENSURE_SUCCESS(rv, rv);
745 
746   nsCAutoString sql;
747   
748   PRBool more;
749   while (NS_SUCCEEDED(statement->ExecuteStep(&more)) && more)
750   {
751     nsCAutoString tableName;
752     rv = statement->GetUTF8String(0, tableName);
753     NS_ENSURE_SUCCESS(rv, rv);
754 
755     sql += NS_LITERAL_CSTRING("DROP TABLE ") + tableName + NS_LITERAL_CSTRING(";");
756   }
757 
758   rv = ExecuteSql(sql);
759   NS_ENSURE_SUCCESS(rv, rv);
760 
761   return NS_OK;
762 }
763 
764 //--------------------------------------------------------------------------------
765 
766 nsresult apDatabaseAutomaticUpdater::ZipAndBackupDatabase()
767 {
768   nsresult rv;
769   nsCOMPtr<nsIFile> dbFile;
770   rv = mDatabaseService->GetDbFile(getter_AddRefs(dbFile));
771   NS_ENSURE_SUCCESS(rv, rv);
772 
773   // Get version of last backup
774   nsAutoString versionFilePath;
775   rv = dbFile->GetPath(versionFilePath);
776   NS_ENSURE_SUCCESS(rv, rv);
777 
778   versionFilePath += NS_LITERAL_STRING(".version");
779 
780   nsCOMPtr<nsILocalFile> versionFile(do_CreateInstance("@mozilla.org/file/local;1", &rv));
781   NS_ENSURE_SUCCESS(rv, rv);
782 
783   rv = versionFile->InitWithPath(versionFilePath);
784   NS_ENSURE_SUCCESS(rv, rv);
785 
786   PRBool versionFileExists;
787   rv = versionFile->Exists(&versionFileExists);
788   NS_ENSURE_SUCCESS(rv, rv);
789 
790   nsCAutoString newVersionString;
791   newVersionString.Assign( nsPrintfCString(256, "%u\n", mNewVersion) );
792 
793   /**
794   * Before we try to convert database, we make backup of the database and mark to which version
795   * we were upgrading. If upgrade fails, and we will be upgrading multiple times to the same
796   * version, no backup will be made and we will have the same database.
797   *
798   * A problem would be if someone will not be able to update, but he will take newer version,
799   * this would overwrite potentially corrupted database. But this is not expected scenario.
800   */  
801   if (versionFileExists)
802   {
803     nsCOMPtr<nsIFileInputStream> inFileStream( do_CreateInstance( "@mozilla.org/network/file-input-stream;1", &rv) );
804     NS_ENSURE_SUCCESS(rv, rv);
805 
806     rv = inFileStream->Init( versionFile, PR_RDONLY, 0666, nsIFileInputStream::CLOSE_ON_EOF );
807     NS_ENSURE_SUCCESS(rv, rv);
808 
809     nsCOMPtr<nsIInputStream> inStream = do_QueryInterface(inFileStream, &rv);
810     NS_ENSURE_SUCCESS(rv, rv);
811 
812     PRUint32 bufSize;
813     rv = inStream->Available(&bufSize);
814     NS_ENSURE_SUCCESS(rv, rv);
815 
816     nsAutoArrayPtr<char> data;
817     data = new char[bufSize+1];
818     NS_ENSURE_TRUE(data, NS_ERROR_OUT_OF_MEMORY);
819 
820     PRUint32 numProcessed;
821     rv = inStream->Read(data.get(), bufSize, &numProcessed);
822     data[numProcessed] = 0;
823 
824     NS_ENSURE_SUCCESS(rv, rv);
825     NS_ENSURE_TRUE((numProcessed == bufSize), NS_ERROR_UNEXPECTED);
826 
827     rv = inStream->Close();
828     NS_ENSURE_SUCCESS(rv, rv);
829 
830     // This version has already been backed up
831     if (newVersionString.Equals(data))
832     {
833       // If same as now, then we are upgrading to the same version, and we do not want to backup again
834       return NS_OK;
835     }
836 
837     // Remove old version file, we need new one
838     rv = versionFile->Remove(PR_FALSE);
839     NS_ENSURE_SUCCESS(rv, rv);
840   }
841 
842   // Write current version file
843   {
844     nsCOMPtr<nsIFileOutputStream> outStream( do_CreateInstance( "@mozilla.org/network/file-output-stream;1", &rv) );
845     NS_ENSURE_SUCCESS(rv, rv);
846 
847     outStream->Init( versionFile, PR_RDWR | PR_CREATE_FILE, PR_IRUSR | PR_IWUSR, 0 );
848 
849     outStream->Write( newVersionString.get() , newVersionString.Length(), &rv);
850     NS_ENSURE_SUCCESS(rv, rv);
851 
852     rv = outStream->Flush();
853     NS_ENSURE_SUCCESS(rv, rv);
854 
855     rv = outStream->Close();
856     NS_ENSURE_SUCCESS(rv, rv);
857   } 
858 
859   nsAutoString zipFilePath;
860   rv = dbFile->GetPath(zipFilePath);
861   NS_ENSURE_SUCCESS(rv, rv);
862 
863   zipFilePath += NS_LITERAL_STRING(".zip");
864 
865   nsCOMPtr<nsILocalFile> zipFile(do_CreateInstance("@mozilla.