 |
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.