LCOV - code coverage report
Current view: directory - storage/src - mozStorageConnection.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 629 521 82.8 %
Date: 2012-06-02 Functions: 80 67 83.8 %

       1                 : /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
       2                 :  * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ :
       3                 :  * ***** BEGIN LICENSE BLOCK *****
       4                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       5                 :  *
       6                 :  * The contents of this file are subject to the Mozilla Public License Version
       7                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       8                 :  * the License. You may obtain a copy of the License at
       9                 :  * http://www.mozilla.org/MPL/
      10                 :  *
      11                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      12                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      13                 :  * for the specific language governing rights and limitations under the
      14                 :  * License.
      15                 :  *
      16                 :  * The Original Code is Oracle Corporation code.
      17                 :  *
      18                 :  * The Initial Developer of the Original Code is
      19                 :  *  Oracle Corporation
      20                 :  * Portions created by the Initial Developer are Copyright (C) 2004
      21                 :  * the Initial Developer. All Rights Reserved.
      22                 :  *
      23                 :  * Contributor(s):
      24                 :  *   Vladimir Vukicevic <vladimir.vukicevic@oracle.com>
      25                 :  *   Brett Wilson <brettw@gmail.com>
      26                 :  *   Shawn Wilsher <me@shawnwilsher.com>
      27                 :  *   Lev Serebryakov <lev@serebryakov.spb.ru>
      28                 :  *   Drew Willcoxon <adw@mozilla.com>
      29                 :  *
      30                 :  * Alternatively, the contents of this file may be used under the terms of
      31                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      32                 :  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
      33                 :  * in which case the provisions of the GPL or the LGPL are applicable instead
      34                 :  * of those above. If you wish to allow use of your version of this file only
      35                 :  * under the terms of either the GPL or the LGPL, and not to allow others to
      36                 :  * use your version of this file under the terms of the MPL, indicate your
      37                 :  * decision by deleting the provisions above and replace them with the notice
      38                 :  * and other provisions required by the GPL or the LGPL. If you do not delete
      39                 :  * the provisions above, a recipient may use your version of this file under
      40                 :  * the terms of any one of the MPL, the GPL or the LGPL.
      41                 :  *
      42                 :  * ***** END LICENSE BLOCK ***** */
      43                 : 
      44                 : #include <stdio.h>
      45                 : 
      46                 : #include "nsError.h"
      47                 : #include "nsIMutableArray.h"
      48                 : #include "nsAutoPtr.h"
      49                 : #include "nsIMemoryReporter.h"
      50                 : #include "nsThreadUtils.h"
      51                 : #include "nsILocalFile.h"
      52                 : #include "mozilla/Telemetry.h"
      53                 : #include "mozilla/Mutex.h"
      54                 : #include "mozilla/CondVar.h"
      55                 : 
      56                 : #include "mozIStorageAggregateFunction.h"
      57                 : #include "mozIStorageCompletionCallback.h"
      58                 : #include "mozIStorageFunction.h"
      59                 : 
      60                 : #include "mozStorageAsyncStatementExecution.h"
      61                 : #include "mozStorageSQLFunctions.h"
      62                 : #include "mozStorageConnection.h"
      63                 : #include "mozStorageService.h"
      64                 : #include "mozStorageStatement.h"
      65                 : #include "mozStorageAsyncStatement.h"
      66                 : #include "mozStorageArgValueArray.h"
      67                 : #include "mozStoragePrivateHelpers.h"
      68                 : #include "mozStorageStatementData.h"
      69                 : #include "StorageBaseStatementInternal.h"
      70                 : #include "SQLCollations.h"
      71                 : #include "FileSystemModule.h"
      72                 : #include "mozStorageHelper.h"
      73                 : #include "sampler.h"
      74                 : 
      75                 : #include "prlog.h"
      76                 : #include "prprf.h"
      77                 : 
      78                 : #define MIN_AVAILABLE_BYTES_PER_CHUNKED_GROWTH 524288000 // 500 MiB
      79                 : 
      80                 : // Maximum size of the pages cache per connection.  If the default cache_size
      81                 : // value evaluates to a larger size, it will be reduced to save memory.
      82                 : #define MAX_CACHE_SIZE_BYTES 4194304 // 4 MiB
      83                 : 
      84                 : // Default maximum number of pages to allow in the connection pages cache.
      85                 : #define DEFAULT_CACHE_SIZE_PAGES 2000
      86                 : 
      87                 : #ifdef PR_LOGGING
      88                 : PRLogModuleInfo* gStorageLog = nsnull;
      89                 : #endif
      90                 : 
      91                 : namespace mozilla {
      92                 : namespace storage {
      93                 : 
      94                 : namespace {
      95                 : 
      96                 : ////////////////////////////////////////////////////////////////////////////////
      97                 : //// Variant Specialization Functions (variantToSQLiteT)
      98                 : 
      99                 : int
     100              11 : sqlite3_T_int(sqlite3_context *aCtx,
     101                 :               int aValue)
     102                 : {
     103              11 :   ::sqlite3_result_int(aCtx, aValue);
     104              11 :   return SQLITE_OK;
     105                 : }
     106                 : 
     107                 : int
     108            6976 : sqlite3_T_int64(sqlite3_context *aCtx,
     109                 :                 sqlite3_int64 aValue)
     110                 : {
     111            6976 :   ::sqlite3_result_int64(aCtx, aValue);
     112            6976 :   return SQLITE_OK;
     113                 : }
     114                 : 
     115                 : int
     116               0 : sqlite3_T_double(sqlite3_context *aCtx,
     117                 :                  double aValue)
     118                 : {
     119               0 :   ::sqlite3_result_double(aCtx, aValue);
     120               0 :   return SQLITE_OK;
     121                 : }
     122                 : 
     123                 : int
     124            5124 : sqlite3_T_text(sqlite3_context *aCtx,
     125                 :                const nsCString &aValue)
     126                 : {
     127                 :   ::sqlite3_result_text(aCtx,
     128                 :                         aValue.get(),
     129            5124 :                         aValue.Length(),
     130            5124 :                         SQLITE_TRANSIENT);
     131            5124 :   return SQLITE_OK;
     132                 : }
     133                 : 
     134                 : int
     135           33201 : sqlite3_T_text16(sqlite3_context *aCtx,
     136                 :                  const nsString &aValue)
     137                 : {
     138                 :   ::sqlite3_result_text16(aCtx,
     139           33201 :                           aValue.get(),
     140           33201 :                           aValue.Length() * 2, // Number of bytes.
     141           66402 :                           SQLITE_TRANSIENT);
     142           33201 :   return SQLITE_OK;
     143                 : }
     144                 : 
     145                 : int
     146               5 : sqlite3_T_null(sqlite3_context *aCtx)
     147                 : {
     148               5 :   ::sqlite3_result_null(aCtx);
     149               5 :   return SQLITE_OK;
     150                 : }
     151                 : 
     152                 : int
     153               0 : sqlite3_T_blob(sqlite3_context *aCtx,
     154                 :                const void *aData,
     155                 :                int aSize)
     156                 : {
     157               0 :   ::sqlite3_result_blob(aCtx, aData, aSize, NS_Free);
     158               0 :   return SQLITE_OK;
     159                 : }
     160                 : 
     161                 : #include "variantToSQLiteT_impl.h"
     162                 : 
     163                 : ////////////////////////////////////////////////////////////////////////////////
     164                 : //// Modules
     165                 : 
     166                 : struct Module
     167                 : {
     168                 :   const char* name;
     169                 :   int (*registerFunc)(sqlite3*, const char*);
     170                 : };
     171                 : 
     172                 : Module gModules[] = {
     173                 :   { "filesystem", RegisterFileSystemModule }
     174                 : };
     175                 : 
     176                 : ////////////////////////////////////////////////////////////////////////////////
     177                 : //// Local Functions
     178                 : 
     179                 : #ifdef PR_LOGGING
     180          204609 : void tracefunc (void *aClosure, const char *aStmt)
     181                 : {
     182          204609 :   PR_LOG(gStorageLog, PR_LOG_DEBUG, ("sqlite3_trace on %p for '%s'", aClosure,
     183                 :                                      aStmt));
     184          204609 : }
     185                 : #endif
     186                 : 
     187                 : struct FFEArguments
     188                 : {
     189                 :     nsISupports *target;
     190                 :     bool found;
     191                 : };
     192                 : PLDHashOperator
     193               1 : findFunctionEnumerator(const nsACString &aKey,
     194                 :                        Connection::FunctionInfo aData,
     195                 :                        void *aUserArg)
     196                 : {
     197               1 :   FFEArguments *args = static_cast<FFEArguments *>(aUserArg);
     198               1 :   if (aData.function == args->target) {
     199               1 :     args->found = true;
     200               1 :     return PL_DHASH_STOP;
     201                 :   }
     202               0 :   return PL_DHASH_NEXT;
     203                 : }
     204                 : 
     205                 : PLDHashOperator
     206             166 : copyFunctionEnumerator(const nsACString &aKey,
     207                 :                        Connection::FunctionInfo aData,
     208                 :                        void *aUserArg)
     209                 : {
     210             166 :   NS_PRECONDITION(aData.type == Connection::FunctionInfo::SIMPLE ||
     211                 :                   aData.type == Connection::FunctionInfo::AGGREGATE,
     212                 :                   "Invalid function type!");
     213                 : 
     214             166 :   Connection *connection = static_cast<Connection *>(aUserArg);
     215             166 :   if (aData.type == Connection::FunctionInfo::SIMPLE) {
     216                 :     mozIStorageFunction *function =
     217             158 :       static_cast<mozIStorageFunction *>(aData.function.get());
     218             158 :     (void)connection->CreateFunction(aKey, aData.numArgs, function);
     219                 :   }
     220                 :   else {
     221                 :     mozIStorageAggregateFunction *function =
     222               8 :       static_cast<mozIStorageAggregateFunction *>(aData.function.get());
     223               8 :     (void)connection->CreateAggregateFunction(aKey, aData.numArgs, function);
     224                 :   }
     225                 : 
     226             166 :   return PL_DHASH_NEXT;
     227                 : }
     228                 : 
     229                 : void
     230           45311 : basicFunctionHelper(sqlite3_context *aCtx,
     231                 :                     int aArgc,
     232                 :                     sqlite3_value **aArgv)
     233                 : {
     234           45311 :   void *userData = ::sqlite3_user_data(aCtx);
     235                 : 
     236           45311 :   mozIStorageFunction *func = static_cast<mozIStorageFunction *>(userData);
     237                 : 
     238           90622 :   nsRefPtr<ArgValueArray> arguments(new ArgValueArray(aArgc, aArgv));
     239           45311 :   if (!arguments)
     240                 :       return;
     241                 : 
     242           90622 :   nsCOMPtr<nsIVariant> result;
     243           45311 :   if (NS_FAILED(func->OnFunctionCall(arguments, getter_AddRefs(result)))) {
     244               0 :     NS_WARNING("User function returned error code!");
     245                 :     ::sqlite3_result_error(aCtx,
     246                 :                            "User function returned error code",
     247               0 :                            -1);
     248                 :     return;
     249                 :   }
     250           45311 :   if (variantToSQLiteT(aCtx, result) != SQLITE_OK) {
     251               0 :     NS_WARNING("User function returned invalid data type!");
     252                 :     ::sqlite3_result_error(aCtx,
     253                 :                            "User function returned invalid data type",
     254               0 :                            -1);
     255                 :   }
     256                 : }
     257                 : 
     258                 : void
     259              16 : aggregateFunctionStepHelper(sqlite3_context *aCtx,
     260                 :                             int aArgc,
     261                 :                             sqlite3_value **aArgv)
     262                 : {
     263              16 :   void *userData = ::sqlite3_user_data(aCtx);
     264                 :   mozIStorageAggregateFunction *func =
     265              16 :     static_cast<mozIStorageAggregateFunction *>(userData);
     266                 : 
     267              32 :   nsRefPtr<ArgValueArray> arguments(new ArgValueArray(aArgc, aArgv));
     268              16 :   if (!arguments)
     269                 :     return;
     270                 : 
     271              16 :   if (NS_FAILED(func->OnStep(arguments)))
     272               0 :     NS_WARNING("User aggregate step function returned error code!");
     273                 : }
     274                 : 
     275                 : void
     276               6 : aggregateFunctionFinalHelper(sqlite3_context *aCtx)
     277                 : {
     278               6 :   void *userData = ::sqlite3_user_data(aCtx);
     279                 :   mozIStorageAggregateFunction *func =
     280               6 :     static_cast<mozIStorageAggregateFunction *>(userData);
     281                 : 
     282              12 :   nsRefPtr<nsIVariant> result;
     283               6 :   if (NS_FAILED(func->OnFinal(getter_AddRefs(result)))) {
     284               0 :     NS_WARNING("User aggregate final function returned error code!");
     285                 :     ::sqlite3_result_error(aCtx,
     286                 :                            "User aggregate final function returned error code",
     287               0 :                            -1);
     288                 :     return;
     289                 :   }
     290                 : 
     291               6 :   if (variantToSQLiteT(aCtx, result) != SQLITE_OK) {
     292               0 :     NS_WARNING("User aggregate final function returned invalid data type!");
     293                 :     ::sqlite3_result_error(aCtx,
     294                 :                            "User aggregate final function returned invalid data type",
     295               0 :                            -1);
     296                 :   }
     297                 : }
     298                 : 
     299                 : /**
     300                 :  * This code is heavily based on the sample at:
     301                 :  *   http://www.sqlite.org/unlock_notify.html
     302                 :  */
     303                 : class UnlockNotification
     304             297 : {
     305                 : public:
     306             297 :   UnlockNotification()
     307                 :   : mMutex("UnlockNotification mMutex")
     308                 :   , mCondVar(mMutex, "UnlockNotification condVar")
     309             297 :   , mSignaled(false)
     310                 :   {
     311             297 :   }
     312                 : 
     313             297 :   void Wait()
     314                 :   {
     315             594 :     MutexAutoLock lock(mMutex);
     316             867 :     while (!mSignaled) {
     317             273 :       (void)mCondVar.Wait();
     318                 :     }
     319             297 :   }
     320                 : 
     321             297 :   void Signal()
     322                 :   {
     323             594 :     MutexAutoLock lock(mMutex);
     324             297 :     mSignaled = true;
     325             297 :     (void)mCondVar.Notify();
     326             297 :   }
     327                 : 
     328                 : private:
     329                 :   Mutex mMutex;
     330                 :   CondVar mCondVar;
     331                 :   bool mSignaled;
     332                 : };
     333                 : 
     334                 : void
     335             297 : UnlockNotifyCallback(void **aArgs,
     336                 :                      int aArgsSize)
     337                 : {
     338             594 :   for (int i = 0; i < aArgsSize; i++) {
     339                 :     UnlockNotification *notification =
     340             297 :       static_cast<UnlockNotification *>(aArgs[i]);
     341             297 :     notification->Signal();
     342                 :   }
     343             297 : }
     344                 : 
     345                 : int
     346             297 : WaitForUnlockNotify(sqlite3* aDatabase)
     347                 : {
     348             594 :   UnlockNotification notification;
     349                 :   int srv = ::sqlite3_unlock_notify(aDatabase, UnlockNotifyCallback,
     350             297 :                                     &notification);
     351             297 :   MOZ_ASSERT(srv == SQLITE_LOCKED || srv == SQLITE_OK);
     352             297 :   if (srv == SQLITE_OK) {
     353             297 :     notification.Wait();
     354                 :   }
     355                 : 
     356             297 :   return srv;
     357                 : }
     358                 : 
     359                 : } // anonymous namespace
     360                 : 
     361                 : ////////////////////////////////////////////////////////////////////////////////
     362                 : //// Local Classes
     363                 : 
     364                 : namespace {
     365                 : 
     366                 : class AsyncCloseConnection : public nsRunnable
     367            6616 : {
     368                 : public:
     369            1654 :   AsyncCloseConnection(Connection *aConnection,
     370                 :                        nsIEventTarget *aCallingThread,
     371                 :                        nsIRunnable *aCallbackEvent)
     372                 :   : mConnection(aConnection)
     373                 :   , mCallingThread(aCallingThread)
     374            1654 :   , mCallbackEvent(aCallbackEvent)
     375                 :   {
     376            1654 :   }
     377                 : 
     378            3308 :   NS_METHOD Run()
     379                 :   {
     380                 :     // This event is first dispatched to the background thread to ensure that
     381                 :     // all pending asynchronous events are completed, and then back to the
     382                 :     // calling thread to actually close and notify.
     383            3308 :     bool onCallingThread = false;
     384            3308 :     (void)mCallingThread->IsOnCurrentThread(&onCallingThread);
     385            3308 :     if (!onCallingThread) {
     386            1654 :       (void)mCallingThread->Dispatch(this, NS_DISPATCH_NORMAL);
     387            1654 :       return NS_OK;
     388                 :     }
     389                 : 
     390            1654 :     (void)mConnection->internalClose();
     391            1654 :     if (mCallbackEvent)
     392            1599 :       (void)mCallingThread->Dispatch(mCallbackEvent, NS_DISPATCH_NORMAL);
     393                 : 
     394                 :     // Because we have no guarantee that the invocation of this method on the
     395                 :     // asynchronous thread has fully completed (including the Release of the
     396                 :     // reference to this object held by that event loop), we need to explicitly
     397                 :     // null out our pointers here.  It is possible this object will be destroyed
     398                 :     // on the asynchronous thread and if the references are still alive we will
     399                 :     // release them on that thread. We definitely do not want that for
     400                 :     // mConnection and it's nice to avoid for mCallbackEvent too.  We do not
     401                 :     // null out mCallingThread because it is conceivable the async thread might
     402                 :     // still be 'in' the object.
     403            1654 :     mConnection = nsnull;
     404            1654 :     mCallbackEvent = nsnull;
     405                 : 
     406            1654 :     return NS_OK;
     407                 :   }
     408                 : private:
     409                 :   nsRefPtr<Connection> mConnection;
     410                 :   nsCOMPtr<nsIEventTarget> mCallingThread;
     411                 :   nsCOMPtr<nsIRunnable> mCallbackEvent;
     412                 : };
     413                 : 
     414                 : } // anonymous namespace
     415                 : 
     416                 : ////////////////////////////////////////////////////////////////////////////////
     417                 : //// Memory Reporting
     418                 : 
     419                 : class StorageMemoryReporter : public nsIMemoryReporter
     420               0 : {
     421                 : public:
     422                 :   NS_DECL_ISUPPORTS
     423                 : 
     424                 :   enum ReporterType {
     425                 :     Cache_Used,
     426                 :     Schema_Used,
     427                 :     Stmt_Used
     428                 :   };
     429                 : 
     430                 :   StorageMemoryReporter(sqlite3 *aDBConn,
     431                 :                         const nsCString &aFileName,
     432                 :                         ReporterType aType)
     433                 :   : mDBConn(aDBConn)
     434                 :   , mFileName(aFileName)
     435                 :   , mType(aType)
     436                 :   , mHasLeaked(false)
     437                 :   {
     438                 :   }
     439                 : 
     440               0 :   NS_IMETHOD GetProcess(nsACString &process)
     441                 :   {
     442               0 :     process.Truncate();
     443               0 :     return NS_OK;
     444                 :   }
     445                 : 
     446               0 :   NS_IMETHOD GetPath(nsACString &path)
     447                 :   {
     448               0 :     path.AssignLiteral("explicit/storage/sqlite/");
     449               0 :     path.Append(mFileName);
     450               0 :     if (mHasLeaked) {
     451               0 :       path.AppendLiteral("-LEAKED");
     452                 :     }
     453                 : 
     454               0 :     if (mType == Cache_Used) {
     455               0 :       path.AppendLiteral("/cache-used");
     456                 :     }
     457               0 :     else if (mType == Schema_Used) {
     458               0 :       path.AppendLiteral("/schema-used");
     459                 :     }
     460               0 :     else if (mType == Stmt_Used) {
     461               0 :       path.AppendLiteral("/stmt-used");
     462                 :     }
     463               0 :     return NS_OK;
     464                 :   }
     465                 : 
     466               0 :   NS_IMETHOD GetKind(PRInt32 *kind)
     467                 :   {
     468               0 :     *kind = KIND_HEAP;
     469               0 :     return NS_OK;
     470                 :   }
     471                 : 
     472               0 :   NS_IMETHOD GetUnits(PRInt32 *units)
     473                 :   {
     474               0 :     *units = UNITS_BYTES;
     475               0 :     return NS_OK;
     476                 :   }
     477                 : 
     478               0 :   NS_IMETHOD GetAmount(PRInt64 *amount)
     479                 :   {
     480               0 :     int type = 0;
     481               0 :     if (mType == Cache_Used) {
     482               0 :       type = SQLITE_DBSTATUS_CACHE_USED;
     483                 :     }
     484               0 :     else if (mType == Schema_Used) {
     485               0 :       type = SQLITE_DBSTATUS_SCHEMA_USED;
     486                 :     }
     487               0 :     else if (mType == Stmt_Used) {
     488               0 :       type = SQLITE_DBSTATUS_STMT_USED;
     489                 :     }
     490                 : 
     491               0 :     int cur=0, max=0;
     492               0 :     int rc = ::sqlite3_db_status(mDBConn, type, &cur, &max, 0);
     493               0 :     *amount = cur;
     494               0 :     return convertResultCode(rc);
     495                 :   }
     496                 : 
     497               0 :   NS_IMETHOD GetDescription(nsACString &desc)
     498                 :   {
     499               0 :     if (mType == Cache_Used) {
     500                 :       desc.AssignLiteral("Memory (approximate) used by all pager caches used "
     501               0 :                          "by connections to this database.");
     502                 :     }
     503               0 :     else if (mType == Schema_Used) {
     504                 :       desc.AssignLiteral("Memory (approximate) used to store the schema "
     505                 :                          "for all databases associated with connections to "
     506               0 :                          "this database.");
     507                 :     }
     508               0 :     else if (mType == Stmt_Used) {
     509                 :       desc.AssignLiteral("Memory (approximate) used by all prepared statements "
     510               0 :                          "used by connections to this database.");
     511                 :     }
     512               0 :     return NS_OK;
     513                 :   }
     514                 : 
     515                 :   // We call this when we know we've leaked a connection.
     516                 :   void markAsLeaked()
     517                 :   {
     518                 :     mHasLeaked = true;
     519                 :   }
     520                 : 
     521                 : private:
     522                 :   sqlite3 *mDBConn;
     523                 :   nsCString mFileName;
     524                 :   ReporterType mType;
     525                 :   bool mHasLeaked;
     526                 : };
     527               0 : NS_IMPL_THREADSAFE_ISUPPORTS1(
     528                 :   StorageMemoryReporter
     529                 : , nsIMemoryReporter
     530                 : )
     531                 : 
     532                 : ////////////////////////////////////////////////////////////////////////////////
     533                 : //// Connection
     534                 : 
     535            3281 : Connection::Connection(Service *aService,
     536                 :                        int aFlags)
     537                 : : sharedAsyncExecutionMutex("Connection::sharedAsyncExecutionMutex")
     538                 : , sharedDBMutex("Connection::sharedDBMutex")
     539                 : , threadOpenedOn(do_GetCurrentThread())
     540                 : , mDBConn(nsnull)
     541                 : , mAsyncExecutionThreadShuttingDown(false)
     542                 : , mTransactionInProgress(false)
     543                 : , mProgressHandler(nsnull)
     544                 : , mFlags(aFlags)
     545            3281 : , mStorageService(aService)
     546                 : {
     547            3281 :   mFunctions.Init();
     548            3281 :   mStorageService->registerConnection(this);
     549            3281 : }
     550                 : 
     551            6548 : Connection::~Connection()
     552                 : {
     553            3274 :   (void)Close();
     554            3274 : }
     555                 : 
     556          154483 : NS_IMPL_THREADSAFE_ADDREF(Connection)
     557           43586 : NS_IMPL_THREADSAFE_QUERY_INTERFACE2(
     558                 :   Connection,
     559                 :   mozIStorageConnection,
     560                 :   nsIInterfaceRequestor
     561                 : )
     562                 : 
     563                 : // This is identical to what NS_IMPL_THREADSAFE_RELEASE provides, but with the
     564                 : // extra |1 == count| case.
     565          154455 : NS_IMETHODIMP_(nsrefcnt) Connection::Release(void)
     566                 : {
     567          154455 :   NS_PRECONDITION(0 != mRefCnt, "dup release");
     568          154455 :   nsrefcnt count = NS_AtomicDecrementRefcnt(mRefCnt);
     569          154455 :   NS_LOG_RELEASE(this, count, "Connection");
     570          154455 :   if (1 == count) {
     571                 :     // If the refcount is 1, the single reference must be from
     572                 :     // gService->mConnections (in class |Service|).  Which means we can
     573                 :     // unregister it safely.
     574            3274 :     mStorageService->unregisterConnection(this);
     575          151181 :   } else if (0 == count) {
     576            3274 :     mRefCnt = 1; /* stabilize */
     577                 :     /* enable this to find non-threadsafe destructors: */
     578                 :     /* NS_ASSERT_OWNINGTHREAD(Connection); */
     579            3274 :     delete (this);
     580            3274 :     return 0;
     581                 :   }
     582          151181 :   return count;
     583                 : }
     584                 : 
     585                 : nsIEventTarget *
     586          109151 : Connection::getAsyncExecutionTarget()
     587                 : {
     588          218302 :   MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
     589                 : 
     590                 :   // If we are shutting down the asynchronous thread, don't hand out any more
     591                 :   // references to the thread.
     592          109151 :   if (mAsyncExecutionThreadShuttingDown)
     593             267 :     return nsnull;
     594                 : 
     595          108884 :   if (!mAsyncExecutionThread) {
     596            1654 :     nsresult rv = ::NS_NewThread(getter_AddRefs(mAsyncExecutionThread));
     597            1654 :     if (NS_FAILED(rv)) {
     598               0 :       NS_WARNING("Failed to create async thread.");
     599               0 :       return nsnull;
     600                 :     }
     601                 :   }
     602                 : 
     603          108884 :   return mAsyncExecutionThread;
     604                 : }
     605                 : 
     606                 : nsresult
     607            3281 : Connection::initialize(nsIFile *aDatabaseFile,
     608                 :                        const char* aVFSName)
     609                 : {
     610            3281 :   NS_ASSERTION (!mDBConn, "Initialize called on already opened database!");
     611            6562 :   SAMPLE_LABEL("storage", "Connection::initialize");
     612                 : 
     613                 :   int srv;
     614                 :   nsresult rv;
     615                 : 
     616            3281 :   mDatabaseFile = aDatabaseFile;
     617                 : 
     618            3281 :   if (aDatabaseFile) {
     619            6464 :     nsAutoString path;
     620            3232 :     rv = aDatabaseFile->GetPath(path);
     621            3232 :     NS_ENSURE_SUCCESS(rv, rv);
     622                 : 
     623            6464 :     srv = ::sqlite3_open_v2(NS_ConvertUTF16toUTF8(path).get(), &mDBConn, mFlags,
     624            3232 :                             aVFSName);
     625                 :   }
     626                 :   else {
     627                 :     // in memory database requested, sqlite uses a magic file name
     628              49 :     srv = ::sqlite3_open_v2(":memory:", &mDBConn, mFlags, aVFSName);
     629                 :   }
     630            3281 :   if (srv != SQLITE_OK) {
     631               4 :     mDBConn = nsnull;
     632               4 :     return convertResultCode(srv);
     633                 :   }
     634                 : 
     635                 :   // Properly wrap the database handle's mutex.
     636            3277 :   sharedDBMutex.initWithMutex(sqlite3_db_mutex(mDBConn));
     637                 : 
     638                 : #ifdef PR_LOGGING
     639            3277 :   if (!gStorageLog)
     640             697 :     gStorageLog = ::PR_NewLogModule("mozStorage");
     641                 : 
     642            3277 :   ::sqlite3_trace(mDBConn, tracefunc, this);
     643                 : 
     644            6554 :   nsCAutoString leafName(":memory");
     645            3277 :   if (aDatabaseFile)
     646            3228 :     (void)aDatabaseFile->GetNativeLeafName(leafName);
     647            3277 :   PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Opening connection to '%s' (%p)",
     648                 :                                       leafName.get(), this));
     649                 : #endif
     650                 : 
     651                 :   // Set page_size to the preferred default value.  This is effective only if
     652                 :   // the database has just been created, otherwise, if the database does not
     653                 :   // use WAL journal mode, a VACUUM operation will updated its page_size.
     654            3277 :   PRInt64 pageSize = DEFAULT_PAGE_SIZE;
     655                 :   nsCAutoString pageSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
     656            6554 :                               "PRAGMA page_size = ");
     657            3277 :   pageSizeQuery.AppendInt(pageSize);
     658            3277 :   rv = ExecuteSimpleSQL(pageSizeQuery);
     659            3277 :   NS_ENSURE_SUCCESS(rv, rv);
     660                 : 
     661                 :   // Get the current page_size, since it may differ from the specified value.
     662                 :   sqlite3_stmt *stmt;
     663            6554 :   NS_NAMED_LITERAL_CSTRING(pragma_page_size,
     664                 :                            MOZ_STORAGE_UNIQUIFY_QUERY_STR "PRAGMA page_size");
     665            3277 :   srv = prepareStatement(pragma_page_size, &stmt);
     666            3277 :   if (srv == SQLITE_OK) {
     667            3277 :     if (SQLITE_ROW == stepStatement(stmt)) {
     668            3277 :       pageSize = ::sqlite3_column_int64(stmt, 0);
     669                 :     }
     670            3277 :     (void)::sqlite3_finalize(stmt);
     671                 :   }
     672                 : 
     673                 :   // Setting the cache_size forces the database open, verifying if it is valid
     674                 :   // or corrupt.  So this is executed regardless it being actually needed.
     675                 :   // The cache_size is calculated from the actual page_size, to save memory.
     676                 :   nsCAutoString cacheSizeQuery(MOZ_STORAGE_UNIQUIFY_QUERY_STR
     677            6554 :                                "PRAGMA cache_size = ");
     678                 :   cacheSizeQuery.AppendInt(NS_MIN(DEFAULT_CACHE_SIZE_PAGES,
     679            3277 :                                   PRInt32(MAX_CACHE_SIZE_BYTES / pageSize)));
     680            3277 :   srv = ::sqlite3_exec(mDBConn, cacheSizeQuery.get(), NULL, NULL, NULL);
     681            3277 :   if (srv != SQLITE_OK) {
     682              10 :     ::sqlite3_close(mDBConn);
     683              10 :     mDBConn = nsnull;
     684              10 :     return convertResultCode(srv);
     685                 :   }
     686                 : 
     687                 :   // Register our built-in SQL functions.
     688            3267 :   srv = registerFunctions(mDBConn);
     689            3267 :   if (srv != SQLITE_OK) {
     690               0 :     ::sqlite3_close(mDBConn);
     691               0 :     mDBConn = nsnull;
     692               0 :     return convertResultCode(srv);
     693                 :   }
     694                 : 
     695                 :   // Register our built-in SQL collating sequences.
     696            3267 :   srv = registerCollations(mDBConn, mStorageService);
     697            3267 :   if (srv != SQLITE_OK) {
     698               0 :     ::sqlite3_close(mDBConn);
     699               0 :     mDBConn = nsnull;
     700               0 :     return convertResultCode(srv);
     701                 :   }
     702                 : 
     703                 :   // Set the synchronous PRAGMA, according to the preference.
     704            3267 :   switch (Service::getSynchronousPref()) {
     705                 :     case 2:
     706               0 :       (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     707               0 :           "PRAGMA synchronous = FULL;"));
     708               0 :       break;
     709                 :     case 0:
     710               0 :       (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     711               0 :           "PRAGMA synchronous = OFF;"));
     712               0 :       break;
     713                 :     case 1:
     714                 :     default:
     715            3267 :       (void)ExecuteSimpleSQL(NS_LITERAL_CSTRING(
     716            3267 :           "PRAGMA synchronous = NORMAL;"));
     717            3267 :       break;
     718                 :   }
     719                 : 
     720            3267 :   return NS_OK;
     721                 : }
     722                 : 
     723                 : nsresult
     724             850 : Connection::databaseElementExists(enum DatabaseElementType aElementType,
     725                 :                                   const nsACString &aElementName,
     726                 :                                   bool *_exists)
     727                 : {
     728             850 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
     729                 : 
     730            1700 :   nsCAutoString query("SELECT name FROM sqlite_master WHERE type = '");
     731             850 :   switch (aElementType) {
     732                 :     case INDEX:
     733              32 :       query.Append("index");
     734              32 :       break;
     735                 :     case TABLE:
     736             818 :       query.Append("table");
     737             818 :       break;
     738                 :   }
     739             850 :   query.Append("' AND name ='");
     740             850 :   query.Append(aElementName);
     741             850 :   query.Append("'");
     742                 : 
     743                 :   sqlite3_stmt *stmt;
     744             850 :   int srv = prepareStatement(query, &stmt);
     745             850 :   if (srv != SQLITE_OK)
     746               0 :     return convertResultCode(srv);
     747                 : 
     748             850 :   srv = stepStatement(stmt);
     749                 :   // we just care about the return value from step
     750             850 :   (void)::sqlite3_finalize(stmt);
     751                 : 
     752             850 :   if (srv == SQLITE_ROW) {
     753             122 :     *_exists = true;
     754             122 :     return NS_OK;
     755                 :   }
     756             728 :   if (srv == SQLITE_DONE) {
     757             728 :     *_exists = false;
     758             728 :     return NS_OK;
     759                 :   }
     760                 : 
     761               0 :   return convertResultCode(srv);
     762                 : }
     763                 : 
     764                 : bool
     765              19 : Connection::findFunctionByInstance(nsISupports *aInstance)
     766                 : {
     767              19 :   sharedDBMutex.assertCurrentThreadOwns();
     768              19 :   FFEArguments args = { aInstance, false };
     769              19 :   (void)mFunctions.EnumerateRead(findFunctionEnumerator, &args);
     770              19 :   return args.found;
     771                 : }
     772                 : 
     773                 : /* static */ int
     774            1084 : Connection::sProgressHelper(void *aArg)
     775                 : {
     776            1084 :   Connection *_this = static_cast<Connection *>(aArg);
     777            1084 :   return _this->progressHandler();
     778                 : }
     779                 : 
     780                 : int
     781            1084 : Connection::progressHandler()
     782                 : {
     783            1084 :   sharedDBMutex.assertCurrentThreadOwns();
     784            1084 :   if (mProgressHandler) {
     785                 :     bool result;
     786            1084 :     nsresult rv = mProgressHandler->OnProgress(this, &result);
     787            1084 :     if (NS_FAILED(rv)) return 0; // Don't break request
     788            1084 :     return result ? 1 : 0;
     789                 :   }
     790               0 :   return 0;
     791                 : }
     792                 : 
     793                 : nsresult
     794            3267 : Connection::setClosedState()
     795                 : {
     796                 :   // Ensure that we are on the correct thread to close the database.
     797                 :   bool onOpenedThread;
     798            3267 :   nsresult rv = threadOpenedOn->IsOnCurrentThread(&onOpenedThread);
     799            3267 :   NS_ENSURE_SUCCESS(rv, rv);
     800            3267 :   if (!onOpenedThread) {
     801               0 :     NS_ERROR("Must close the database on the thread that you opened it with!");
     802               0 :     return NS_ERROR_UNEXPECTED;
     803                 :   }
     804                 : 
     805                 :   // Flag that we are shutting down the async thread, so that
     806                 :   // getAsyncExecutionTarget knows not to expose/create the async thread.
     807                 :   {
     808            6534 :     MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
     809            3267 :     NS_ENSURE_FALSE(mAsyncExecutionThreadShuttingDown, NS_ERROR_UNEXPECTED);
     810            6534 :     mAsyncExecutionThreadShuttingDown = true;
     811                 :   }
     812                 : 
     813            3267 :   return NS_OK;
     814                 : }
     815                 : 
     816                 : nsresult
     817            3267 : Connection::internalClose()
     818                 : {
     819                 : #ifdef DEBUG
     820                 :   // Sanity checks to make sure we are in the proper state before calling this.
     821            3267 :   NS_ASSERTION(mDBConn, "Database connection is already null!");
     822                 : 
     823                 :   { // Make sure we have marked our async thread as shutting down.
     824            6534 :     MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
     825            3267 :     NS_ASSERTION(mAsyncExecutionThreadShuttingDown,
     826                 :                  "Did not call setClosedState!");
     827                 :   }
     828                 : 
     829                 :   { // Ensure that we are being called on the thread we were opened with.
     830            3267 :     bool onOpenedThread = false;
     831            3267 :     (void)threadOpenedOn->IsOnCurrentThread(&onOpenedThread);
     832            3267 :     NS_ASSERTION(onOpenedThread,
     833                 :                  "Not called on the thread the database was opened on!");
     834                 :   }
     835                 : #endif
     836                 : 
     837                 : #ifdef PR_LOGGING
     838            6534 :   nsCAutoString leafName(":memory");
     839            3267 :   if (mDatabaseFile)
     840            3218 :       (void)mDatabaseFile->GetNativeLeafName(leafName);
     841            3267 :   PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Closing connection to '%s'",
     842                 :                                       leafName.get()));
     843                 : #endif
     844                 : 
     845                 : #ifdef DEBUG
     846                 :   // Notify about any non-finalized statements.
     847            3267 :   sqlite3_stmt *stmt = NULL;
     848            6540 :   while ((stmt = ::sqlite3_next_stmt(mDBConn, stmt))) {
     849                 :     char *msg = ::PR_smprintf("SQL statement '%s' was not finalized",
     850               6 :                               ::sqlite3_sql(stmt));
     851               6 :     NS_WARNING(msg);
     852               6 :     ::PR_smprintf_free(msg);
     853                 :   }
     854                 : #endif
     855                 : 
     856            3267 :   int srv = ::sqlite3_close(mDBConn);
     857            3267 :   NS_ASSERTION(srv == SQLITE_OK,
     858                 :                "sqlite3_close failed. There are probably outstanding statements that are listed above!");
     859                 : 
     860            3267 :   mDBConn = NULL;
     861            3267 :   return convertResultCode(srv);
     862                 : }
     863                 : 
     864                 : nsCString
     865              22 : Connection::getFilename()
     866                 : {
     867              22 :   nsCString leafname(":memory:");
     868              22 :   if (mDatabaseFile) {
     869              22 :     (void)mDatabaseFile->GetNativeLeafName(leafname);
     870                 :   }
     871                 :   return leafname;
     872                 : }
     873                 : 
     874                 : int
     875          220552 : Connection::stepStatement(sqlite3_stmt *aStatement)
     876                 : {
     877          220552 :   bool checkedMainThread = false;
     878          220552 :   TimeStamp startTime = TimeStamp::Now();
     879                 : 
     880                 :   // mDBConn may be null if the executing statement has been created and cached
     881                 :   // after a call to asyncClose() but before the connection has been nullified
     882                 :   // by internalClose().  In such a case closing the connection fails due to
     883                 :   // the existence of prepared statements, but mDBConn is set to null
     884                 :   // regardless. This usually happens when other tasks using cached statements
     885                 :   // are asynchronously scheduled for execution and any of them ends up after
     886                 :   // asyncClose. See bug 728653 for details.
     887          220553 :   if (!mDBConn)
     888               0 :     return SQLITE_MISUSE;
     889                 : 
     890          220553 :   (void)::sqlite3_extended_result_codes(mDBConn, 1);
     891                 : 
     892                 :   int srv;
     893          441403 :   while ((srv = ::sqlite3_step(aStatement)) == SQLITE_LOCKED_SHAREDCACHE) {
     894             298 :     if (!checkedMainThread) {
     895             298 :       checkedMainThread = true;
     896             298 :       if (::NS_IsMainThread()) {
     897               1 :         NS_WARNING("We won't allow blocking on the main thread!");
     898               1 :         break;
     899                 :       }
     900                 :     }
     901                 : 
     902             297 :     srv = WaitForUnlockNotify(mDBConn);
     903             297 :     if (srv != SQLITE_OK) {
     904               0 :       break;
     905                 :     }
     906                 : 
     907             297 :     ::sqlite3_reset(aStatement);
     908                 :   }
     909                 : 
     910                 :   // Report very slow SQL statements to Telemetry
     911          220552 :   TimeDuration duration = TimeStamp::Now() - startTime;
     912          220553 :   if (duration.ToMilliseconds() >= Telemetry::kSlowStatementThreshold) {
     913              26 :     nsDependentCString statementString(::sqlite3_sql(aStatement));
     914              13 :     Telemetry::RecordSlowSQLStatement(statementString, getFilename(),
     915              13 :                                       duration.ToMilliseconds());
     916                 :   }
     917                 : 
     918          220553 :   (void)::sqlite3_extended_result_codes(mDBConn, 0);
     919                 :   // Drop off the extended result bits of the result code.
     920          220553 :   return srv & 0xFF;
     921                 : }
     922                 : 
     923                 : int
     924           50895 : Connection::prepareStatement(const nsCString &aSQL,
     925                 :                              sqlite3_stmt **_stmt)
     926                 : {
     927           50895 :   bool checkedMainThread = false;
     928                 : 
     929           50895 :   (void)::sqlite3_extended_result_codes(mDBConn, 1);
     930                 : 
     931                 :   int srv;
     932           50895 :   while((srv = ::sqlite3_prepare_v2(mDBConn, aSQL.get(), -1, _stmt, NULL)) ==
     933                 :         SQLITE_LOCKED_SHAREDCACHE) {
     934               0 :     if (!checkedMainThread) {
     935               0 :       checkedMainThread = true;
     936               0 :       if (::NS_IsMainThread()) {
     937               0 :         NS_WARNING("We won't allow blocking on the main thread!");
     938               0 :         break;
     939                 :       }
     940                 :     }
     941                 : 
     942               0 :     srv = WaitForUnlockNotify(mDBConn);
     943               0 :     if (srv != SQLITE_OK) {
     944               0 :       break;
     945                 :     }
     946                 :   }
     947                 : 
     948           50895 :   if (srv != SQLITE_OK) {
     949             114 :     nsCString warnMsg;
     950              57 :     warnMsg.AppendLiteral("The SQL statement '");
     951              57 :     warnMsg.Append(aSQL);
     952              57 :     warnMsg.AppendLiteral("' could not be compiled due to an error: ");
     953              57 :     warnMsg.Append(::sqlite3_errmsg(mDBConn));
     954                 : 
     955                 : #ifdef DEBUG
     956              57 :     NS_WARNING(warnMsg.get());
     957                 : #endif
     958                 : #ifdef PR_LOGGING
     959              57 :     PR_LOG(gStorageLog, PR_LOG_ERROR, ("%s", warnMsg.get()));
     960                 : #endif
     961                 :   }
     962                 : 
     963           50895 :   (void)::sqlite3_extended_result_codes(mDBConn, 0);
     964                 :   // Drop off the extended result bits of the result code.
     965           50895 :   return srv & 0xFF;
     966                 : }
     967                 : 
     968                 : ////////////////////////////////////////////////////////////////////////////////
     969                 : //// nsIInterfaceRequestor
     970                 : 
     971                 : NS_IMETHODIMP
     972             622 : Connection::GetInterface(const nsIID &aIID,
     973                 :                          void **_result)
     974                 : {
     975             622 :   if (aIID.Equals(NS_GET_IID(nsIEventTarget))) {
     976             622 :     nsIEventTarget *background = getAsyncExecutionTarget();
     977             622 :     NS_IF_ADDREF(background);
     978             622 :     *_result = background;
     979             622 :     return NS_OK;
     980                 :   }
     981               0 :   return NS_ERROR_NO_INTERFACE;
     982                 : }
     983                 : 
     984                 : ////////////////////////////////////////////////////////////////////////////////
     985                 : //// mozIStorageConnection
     986                 : 
     987                 : NS_IMETHODIMP
     988            4549 : Connection::Close()
     989                 : {
     990            4549 :   if (!mDBConn)
     991            2935 :     return NS_ERROR_NOT_INITIALIZED;
     992                 : 
     993                 :   { // Make sure we have not executed any asynchronous statements.
     994                 :     // If this fails, the mDBConn will be left open, resulting in a leak.
     995                 :     // Ideally we'd schedule some code to destroy the mDBConn once all its
     996                 :     // async statements have finished executing;  see bug 704030.
     997            3228 :     MutexAutoLock lockedScope(sharedAsyncExecutionMutex);
     998            1614 :     bool asyncCloseWasCalled = !mAsyncExecutionThread;
     999            1614 :     NS_ENSURE_TRUE(asyncCloseWasCalled, NS_ERROR_UNEXPECTED);
    1000                 :   }
    1001                 : 
    1002            1613 :   nsresult rv = setClosedState();
    1003            1613 :   NS_ENSURE_SUCCESS(rv, rv);
    1004                 : 
    1005            1613 :   return internalClose();
    1006                 : }
    1007                 : 
    1008                 : NS_IMETHODIMP
    1009            1661 : Connection::AsyncClose(mozIStorageCompletionCallback *aCallback)
    1010                 : {
    1011            1661 :   if (!mDBConn)
    1012               6 :     return NS_ERROR_NOT_INITIALIZED;
    1013                 : 
    1014            1655 :   nsIEventTarget *asyncThread = getAsyncExecutionTarget();
    1015            1655 :   NS_ENSURE_TRUE(asyncThread, NS_ERROR_UNEXPECTED);
    1016                 : 
    1017            1654 :   nsresult rv = setClosedState();
    1018            1654 :   NS_ENSURE_SUCCESS(rv, rv);
    1019                 : 
    1020                 :   // Create our callback event if we were given a callback.
    1021            3308 :   nsCOMPtr<nsIRunnable> completeEvent;
    1022            1654 :   if (aCallback) {
    1023            1599 :     completeEvent = newCompletionEvent(aCallback);
    1024            1599 :     NS_ENSURE_TRUE(completeEvent, NS_ERROR_OUT_OF_MEMORY);
    1025                 :   }
    1026                 : 
    1027                 :   // Create and dispatch our close event to the background thread.
    1028                 :   nsCOMPtr<nsIRunnable> closeEvent =
    1029            4962 :     new AsyncCloseConnection(this, NS_GetCurrentThread(), completeEvent);
    1030            1654 :   NS_ENSURE_TRUE(closeEvent, NS_ERROR_OUT_OF_MEMORY);
    1031                 : 
    1032            1654 :   rv = asyncThread->Dispatch(closeEvent, NS_DISPATCH_NORMAL);
    1033            1654 :   NS_ENSURE_SUCCESS(rv, rv);
    1034                 : 
    1035            1654 :   return NS_OK;
    1036                 : }
    1037                 : 
    1038                 : NS_IMETHODIMP
    1039              52 : Connection::Clone(bool aReadOnly,
    1040                 :                   mozIStorageConnection **_connection)
    1041                 : {
    1042             104 :   SAMPLE_LABEL("storage", "Connection::Clone");
    1043              52 :   if (!mDBConn)
    1044               2 :     return NS_ERROR_NOT_INITIALIZED;
    1045              50 :   if (!mDatabaseFile)
    1046               0 :     return NS_ERROR_UNEXPECTED;
    1047                 : 
    1048              50 :   int flags = mFlags;
    1049              50 :   if (aReadOnly) {
    1050                 :     // Turn off SQLITE_OPEN_READWRITE, and set SQLITE_OPEN_READONLY.
    1051              40 :     flags = (~SQLITE_OPEN_READWRITE & flags) | SQLITE_OPEN_READONLY;
    1052                 :     // Turn off SQLITE_OPEN_CREATE.
    1053              40 :     flags = (~SQLITE_OPEN_CREATE & flags);
    1054                 :   }
    1055             150 :   nsRefPtr<Connection> clone = new Connection(mStorageService, flags);
    1056              50 :   NS_ENSURE_TRUE(clone, NS_ERROR_OUT_OF_MEMORY);
    1057                 : 
    1058              50 :   nsresult rv = clone->initialize(mDatabaseFile);
    1059              50 :   NS_ENSURE_SUCCESS(rv, rv);
    1060                 : 
    1061                 :   // Copy over pragmas from the original connection.
    1062                 :   static const char * pragmas[] = {
    1063                 :     "cache_size",
    1064                 :     "temp_store",
    1065                 :     "foreign_keys",
    1066                 :     "journal_size_limit",
    1067                 :     "synchronous",
    1068                 :     "wal_autocheckpoint",
    1069                 :   };
    1070             350 :   for (PRUint32 i = 0; i < ArrayLength(pragmas); ++i) {
    1071                 :     // Read-only connections just need cache_size and temp_store pragmas.
    1072             500 :     if (aReadOnly && ::strcmp(pragmas[i], "cache_size") != 0 &&
    1073             200 :                      ::strcmp(pragmas[i], "temp_store") != 0) {
    1074             160 :       continue;
    1075                 :     }
    1076                 : 
    1077             280 :     nsCAutoString pragmaQuery("PRAGMA ");
    1078             140 :     pragmaQuery.Append(pragmas[i]);
    1079             280 :     nsCOMPtr<mozIStorageStatement> stmt;
    1080             140 :     rv = CreateStatement(pragmaQuery, getter_AddRefs(stmt));
    1081             140 :     MOZ_ASSERT(NS_SUCCEEDED(rv));
    1082             140 :     bool hasResult = false;
    1083             140 :     if (stmt && NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult) {
    1084             140 :       pragmaQuery.AppendLiteral(" = ");
    1085             140 :       pragmaQuery.AppendInt(stmt->AsInt32(0));
    1086             140 :       rv = clone->ExecuteSimpleSQL(pragmaQuery);
    1087             140 :       MOZ_ASSERT(NS_SUCCEEDED(rv));
    1088                 :     }
    1089                 :   }
    1090                 : 
    1091                 :   // Copy any functions that have been added to this connection.
    1092              50 :   (void)mFunctions.EnumerateRead(copyFunctionEnumerator, clone);
    1093                 : 
    1094              50 :   NS_ADDREF(*_connection = clone);
    1095              50 :   return NS_OK;
    1096                 : }
    1097                 : 
    1098                 : NS_IMETHODIMP
    1099            1107 : Connection::GetConnectionReady(bool *_ready)
    1100                 : {
    1101            1107 :   *_ready = (mDBConn != nsnull);
    1102            1107 :   return NS_OK;
    1103                 : }
    1104                 : 
    1105                 : NS_IMETHODIMP
    1106            1162 : Connection::GetDatabaseFile(nsIFile **_dbFile)
    1107                 : {
    1108            1162 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1109                 : 
    1110            1162 :   NS_IF_ADDREF(*_dbFile = mDatabaseFile);
    1111                 : 
    1112            1162 :   return NS_OK;
    1113                 : }
    1114                 : 
    1115                 : NS_IMETHODIMP
    1116            5717 : Connection::GetLastInsertRowID(PRInt64 *_id)
    1117                 : {
    1118            5717 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1119                 : 
    1120            5717 :   sqlite_int64 id = ::sqlite3_last_insert_rowid(mDBConn);
    1121            5717 :   *_id = id;
    1122                 : 
    1123            5717 :   return NS_OK;
    1124                 : }
    1125                 : 
    1126                 : NS_IMETHODIMP
    1127               0 : Connection::GetAffectedRows(PRInt32 *_rows)
    1128                 : {
    1129               0 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1130                 : 
    1131               0 :   *_rows = ::sqlite3_changes(mDBConn);
    1132                 : 
    1133               0 :   return NS_OK;
    1134                 : }
    1135                 : 
    1136                 : NS_IMETHODIMP
    1137              90 : Connection::GetLastError(PRInt32 *_error)
    1138                 : {
    1139              90 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1140                 : 
    1141              90 :   *_error = ::sqlite3_errcode(mDBConn);
    1142                 : 
    1143              90 :   return NS_OK;
    1144                 : }
    1145                 : 
    1146                 : NS_IMETHODIMP
    1147              52 : Connection::GetLastErrorString(nsACString &_errorString)
    1148                 : {
    1149              52 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1150                 : 
    1151              52 :   const char *serr = ::sqlite3_errmsg(mDBConn);
    1152              52 :   _errorString.Assign(serr);
    1153                 : 
    1154              52 :   return NS_OK;
    1155                 : }
    1156                 : 
    1157                 : NS_IMETHODIMP
    1158            1798 : Connection::GetSchemaVersion(PRInt32 *_version)
    1159                 : {
    1160            1798 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1161                 : 
    1162            3596 :   nsCOMPtr<mozIStorageStatement> stmt;
    1163            1798 :   (void)CreateStatement(NS_LITERAL_CSTRING("PRAGMA user_version"),
    1164            3596 :                         getter_AddRefs(stmt));
    1165            1798 :   NS_ENSURE_TRUE(stmt, NS_ERROR_OUT_OF_MEMORY);
    1166                 : 
    1167            1798 :   *_version = 0;
    1168                 :   bool hasResult;
    1169            1798 :   if (NS_SUCCEEDED(stmt->ExecuteStep(&hasResult)) && hasResult)
    1170            1798 :     *_version = stmt->AsInt32(0);
    1171                 : 
    1172            1798 :   return NS_OK;
    1173                 : }
    1174                 : 
    1175                 : NS_IMETHODIMP
    1176            1317 : Connection::SetSchemaVersion(PRInt32 aVersion)
    1177                 : {
    1178            1317 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1179                 : 
    1180            2634 :   nsCAutoString stmt(NS_LITERAL_CSTRING("PRAGMA user_version = "));
    1181            1317 :   stmt.AppendInt(aVersion);
    1182                 : 
    1183            1317 :   return ExecuteSimpleSQL(stmt);
    1184                 : }
    1185                 : 
    1186                 : NS_IMETHODIMP
    1187           36811 : Connection::CreateStatement(const nsACString &aSQLStatement,
    1188                 :                             mozIStorageStatement **_stmt)
    1189                 : {
    1190           36811 :   NS_ENSURE_ARG_POINTER(_stmt);
    1191           36811 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1192                 : 
    1193           73622 :   nsRefPtr<Statement> statement(new Statement());
    1194           36811 :   NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
    1195                 : 
    1196           36811 :   nsresult rv = statement->initialize(this, aSQLStatement);
    1197           36811 :   NS_ENSURE_SUCCESS(rv, rv);
    1198                 : 
    1199                 :   Statement *rawPtr;
    1200           36762 :   statement.forget(&rawPtr);
    1201           36762 :   *_stmt = rawPtr;
    1202           36762 :   return NS_OK;
    1203                 : }
    1204                 : 
    1205                 : NS_IMETHODIMP
    1206            5918 : Connection::CreateAsyncStatement(const nsACString &aSQLStatement,
    1207                 :                                  mozIStorageAsyncStatement **_stmt)
    1208                 : {
    1209            5918 :   NS_ENSURE_ARG_POINTER(_stmt);
    1210            5918 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1211                 : 
    1212           11836 :   nsRefPtr<AsyncStatement> statement(new AsyncStatement());
    1213            5918 :   NS_ENSURE_TRUE(statement, NS_ERROR_OUT_OF_MEMORY);
    1214                 : 
    1215            5918 :   nsresult rv = statement->initialize(this, aSQLStatement);
    1216            5918 :   NS_ENSURE_SUCCESS(rv, rv);
    1217                 : 
    1218                 :   AsyncStatement *rawPtr;
    1219            5918 :   statement.forget(&rawPtr);
    1220            5918 :   *_stmt = rawPtr;
    1221            5918 :   return NS_OK;
    1222                 : }
    1223                 : 
    1224                 : NS_IMETHODIMP
    1225           44242 : Connection::ExecuteSimpleSQL(const nsACString &aSQLStatement)
    1226                 : {
    1227           44242 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1228                 : 
    1229           44242 :   int srv = ::sqlite3_exec(mDBConn, PromiseFlatCString(aSQLStatement).get(),
    1230           44242 :                            NULL, NULL, NULL);
    1231           44242 :   return convertResultCode(srv);
    1232                 : }
    1233                 : 
    1234                 : NS_IMETHODIMP
    1235            5534 : Connection::ExecuteAsync(mozIStorageBaseStatement **aStatements,
    1236                 :                          PRUint32 aNumStatements,
    1237                 :                          mozIStorageStatementCallback *aCallback,
    1238                 :                          mozIStoragePendingStatement **_handle)
    1239                 : {
    1240           11068 :   nsTArray<StatementData> stmts(aNumStatements);
    1241           22316 :   for (PRUint32 i = 0; i < aNumStatements; i++) {
    1242                 :     nsCOMPtr<StorageBaseStatementInternal> stmt = 
    1243           33564 :       do_QueryInterface(aStatements[i]);
    1244                 : 
    1245                 :     // Obtain our StatementData.
    1246           33564 :     StatementData data;
    1247           16782 :     nsresult rv = stmt->getAsynchronousStatementData(data);
    1248           16782 :     NS_ENSURE_SUCCESS(rv, rv);
    1249                 : 
    1250           16782 :     NS_ASSERTION(stmt->getOwner() == this,
    1251                 :                  "Statement must be from this database connection!");
    1252                 : 
    1253                 :     // Now append it to our array.
    1254           16782 :     NS_ENSURE_TRUE(stmts.AppendElement(data), NS_ERROR_OUT_OF_MEMORY);
    1255                 :   }
    1256                 : 
    1257                 :   // Dispatch to the background
    1258            5534 :   return AsyncExecuteStatements::execute(stmts, this, aCallback, _handle);
    1259                 : }
    1260                 : 
    1261                 : NS_IMETHODIMP
    1262             818 : Connection::TableExists(const nsACString &aTableName,
    1263                 :                         bool *_exists)
    1264                 : {
    1265             818 :     return databaseElementExists(TABLE, aTableName, _exists);
    1266                 : }
    1267                 : 
    1268                 : NS_IMETHODIMP
    1269              32 : Connection::IndexExists(const nsACString &aIndexName,
    1270                 :                         bool* _exists)
    1271                 : {
    1272              32 :     return databaseElementExists(INDEX, aIndexName, _exists);
    1273                 : }
    1274                 : 
    1275                 : NS_IMETHODIMP
    1276             646 : Connection::GetTransactionInProgress(bool *_inProgress)
    1277                 : {
    1278             646 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1279                 : 
    1280            1292 :   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
    1281             646 :   *_inProgress = mTransactionInProgress;
    1282             646 :   return NS_OK;
    1283                 : }
    1284                 : 
    1285                 : NS_IMETHODIMP
    1286             439 : Connection::BeginTransaction()
    1287                 : {
    1288             439 :   return BeginTransactionAs(mozIStorageConnection::TRANSACTION_DEFERRED);
    1289                 : }
    1290                 : 
    1291                 : NS_IMETHODIMP
    1292           12841 : Connection::BeginTransactionAs(PRInt32 aTransactionType)
    1293                 : {
    1294           12841 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1295                 : 
    1296           25682 :   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
    1297           12841 :   if (mTransactionInProgress)
    1298            5244 :     return NS_ERROR_FAILURE;
    1299                 :   nsresult rv;
    1300            7597 :   switch(aTransactionType) {
    1301                 :     case TRANSACTION_DEFERRED:
    1302            4620 :       rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN DEFERRED"));
    1303            4620 :       break;
    1304                 :     case TRANSACTION_IMMEDIATE:
    1305            2977 :       rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN IMMEDIATE"));
    1306            2977 :       break;
    1307                 :     case TRANSACTION_EXCLUSIVE:
    1308               0 :       rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN EXCLUSIVE"));
    1309               0 :       break;
    1310                 :     default:
    1311               0 :       return NS_ERROR_ILLEGAL_VALUE;
    1312                 :   }
    1313            7597 :   if (NS_SUCCEEDED(rv))
    1314            7596 :     mTransactionInProgress = true;
    1315            7597 :   return rv;
    1316                 : }
    1317                 : 
    1318                 : NS_IMETHODIMP
    1319            7560 : Connection::CommitTransaction()
    1320                 : {
    1321            7560 :   if (!mDBConn)
    1322               0 :     return NS_ERROR_NOT_INITIALIZED;
    1323                 : 
    1324           15120 :   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
    1325            7560 :   if (!mTransactionInProgress)
    1326               1 :     return NS_ERROR_UNEXPECTED;
    1327                 : 
    1328            7559 :   nsresult rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("COMMIT TRANSACTION"));
    1329            7559 :   if (NS_SUCCEEDED(rv))
    1330            7559 :     mTransactionInProgress = false;
    1331            7559 :   return rv;
    1332                 : }
    1333                 : 
    1334                 : NS_IMETHODIMP
    1335              36 : Connection::RollbackTransaction()
    1336                 : {
    1337              36 :   if (!mDBConn)
    1338               0 :     return NS_ERROR_NOT_INITIALIZED;
    1339                 : 
    1340              72 :   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
    1341              36 :   if (!mTransactionInProgress)
    1342               1 :     return NS_ERROR_UNEXPECTED;
    1343                 : 
    1344              35 :   nsresult rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("ROLLBACK TRANSACTION"));
    1345              35 :   if (NS_SUCCEEDED(rv))
    1346              35 :     mTransactionInProgress = false;
    1347              35 :   return rv;
    1348                 : }
    1349                 : 
    1350                 : NS_IMETHODIMP
    1351            1617 : Connection::CreateTable(const char *aTableName,
    1352                 :                         const char *aTableSchema)
    1353                 : {
    1354            1617 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1355                 : 
    1356            1617 :   char *buf = ::PR_smprintf("CREATE TABLE %s (%s)", aTableName, aTableSchema);
    1357            1617 :   if (!buf)
    1358               0 :     return NS_ERROR_OUT_OF_MEMORY;
    1359                 : 
    1360            1617 :   int srv = ::sqlite3_exec(mDBConn, buf, NULL, NULL, NULL);
    1361            1617 :   ::PR_smprintf_free(buf);
    1362                 : 
    1363            1617 :   return convertResultCode(srv);
    1364                 : }
    1365                 : 
    1366                 : NS_IMETHODIMP
    1367            2043 : Connection::CreateFunction(const nsACString &aFunctionName,
    1368                 :                            PRInt32 aNumArguments,
    1369                 :                            mozIStorageFunction *aFunction)
    1370                 : {
    1371            2043 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1372                 : 
    1373                 :   // Check to see if this function is already defined.  We only check the name
    1374                 :   // because a function can be defined with the same body but different names.
    1375            4086 :   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
    1376            2043 :   NS_ENSURE_FALSE(mFunctions.Get(aFunctionName, NULL), NS_ERROR_FAILURE);
    1377                 : 
    1378                 :   int srv = ::sqlite3_create_function(mDBConn,
    1379            2042 :                                       nsPromiseFlatCString(aFunctionName).get(),
    1380                 :                                       aNumArguments,
    1381                 :                                       SQLITE_ANY,
    1382                 :                                       aFunction,
    1383                 :                                       basicFunctionHelper,
    1384                 :                                       NULL,
    1385            2042 :                                       NULL);
    1386            2042 :   if (srv != SQLITE_OK)
    1387               0 :     return convertResultCode(srv);
    1388                 : 
    1389                 :   FunctionInfo info = { aFunction,
    1390                 :                         Connection::FunctionInfo::SIMPLE,
    1391            4084 :                         aNumArguments };
    1392            2042 :   NS_ENSURE_TRUE(mFunctions.Put(aFunctionName, info),
    1393                 :                  NS_ERROR_OUT_OF_MEMORY);
    1394                 : 
    1395            2042 :   return NS_OK;
    1396                 : }
    1397                 : 
    1398                 : NS_IMETHODIMP
    1399              20 : Connection::CreateAggregateFunction(const nsACString &aFunctionName,
    1400                 :                                     PRInt32 aNumArguments,
    1401                 :                                     mozIStorageAggregateFunction *aFunction)
    1402                 : {
    1403              20 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1404                 : 
    1405                 :   // Check to see if this function name is already defined.
    1406              40 :   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
    1407              20 :   NS_ENSURE_FALSE(mFunctions.Get(aFunctionName, NULL), NS_ERROR_FAILURE);
    1408                 : 
    1409                 :   // Because aggregate functions depend on state across calls, you cannot have
    1410                 :   // the same instance use the same name.  We want to enumerate all functions
    1411                 :   // and make sure this instance is not already registered.
    1412              19 :   NS_ENSURE_FALSE(findFunctionByInstance(aFunction), NS_ERROR_FAILURE);
    1413                 : 
    1414                 :   int srv = ::sqlite3_create_function(mDBConn,
    1415              18 :                                       nsPromiseFlatCString(aFunctionName).get(),
    1416                 :                                       aNumArguments,
    1417                 :                                       SQLITE_ANY,
    1418                 :                                       aFunction,
    1419                 :                                       NULL,
    1420                 :                                       aggregateFunctionStepHelper,
    1421              18 :                                       aggregateFunctionFinalHelper);
    1422              18 :   if (srv != SQLITE_OK)
    1423               0 :     return convertResultCode(srv);
    1424                 : 
    1425                 :   FunctionInfo info = { aFunction,
    1426                 :                         Connection::FunctionInfo::AGGREGATE,
    1427              36 :                         aNumArguments };
    1428              18 :   NS_ENSURE_TRUE(mFunctions.Put(aFunctionName, info),
    1429                 :                  NS_ERROR_OUT_OF_MEMORY);
    1430                 : 
    1431              18 :   return NS_OK;
    1432                 : }
    1433                 : 
    1434                 : NS_IMETHODIMP
    1435             369 : Connection::RemoveFunction(const nsACString &aFunctionName)
    1436                 : {
    1437             369 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1438                 : 
    1439             738 :   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
    1440             369 :   NS_ENSURE_TRUE(mFunctions.Get(aFunctionName, NULL), NS_ERROR_FAILURE);
    1441                 : 
    1442                 :   int srv = ::sqlite3_create_function(mDBConn,
    1443             369 :                                       nsPromiseFlatCString(aFunctionName).get(),
    1444                 :                                       0,
    1445                 :                                       SQLITE_ANY,
    1446                 :                                       NULL,
    1447                 :                                       NULL,
    1448                 :                                       NULL,
    1449             369 :                                       NULL);
    1450             369 :   if (srv != SQLITE_OK)
    1451               0 :     return convertResultCode(srv);
    1452                 : 
    1453             369 :   mFunctions.Remove(aFunctionName);
    1454                 : 
    1455             369 :   return NS_OK;
    1456                 : }
    1457                 : 
    1458                 : NS_IMETHODIMP
    1459            4674 : Connection::SetProgressHandler(PRInt32 aGranularity,
    1460                 :                                mozIStorageProgressHandler *aHandler,
    1461                 :                                mozIStorageProgressHandler **_oldHandler)
    1462                 : {
    1463            4674 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1464                 : 
    1465                 :   // Return previous one
    1466            9348 :   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
    1467            4674 :   NS_IF_ADDREF(*_oldHandler = mProgressHandler);
    1468                 : 
    1469            4674 :   if (!aHandler || aGranularity <= 0) {
    1470               0 :     aHandler = nsnull;
    1471               0 :     aGranularity = 0;
    1472                 :   }
    1473            4674 :   mProgressHandler = aHandler;
    1474            4674 :   ::sqlite3_progress_handler(mDBConn, aGranularity, sProgressHelper, this);
    1475                 : 
    1476            4674 :   return NS_OK;
    1477                 : }
    1478                 : 
    1479                 : NS_IMETHODIMP
    1480            4672 : Connection::RemoveProgressHandler(mozIStorageProgressHandler **_oldHandler)
    1481                 : {
    1482            4672 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1483                 : 
    1484                 :   // Return previous one
    1485            9344 :   SQLiteMutexAutoLock lockedScope(sharedDBMutex);
    1486            4672 :   NS_IF_ADDREF(*_oldHandler = mProgressHandler);
    1487                 : 
    1488            4672 :   mProgressHandler = nsnull;
    1489            4672 :   ::sqlite3_progress_handler(mDBConn, 0, NULL, NULL);
    1490                 : 
    1491            4672 :   return NS_OK;
    1492                 : }
    1493                 : 
    1494                 : NS_IMETHODIMP
    1495             659 : Connection::SetGrowthIncrement(PRInt32 aChunkSize, const nsACString &aDatabaseName)
    1496                 : {
    1497                 :   // Bug 597215: Disk space is extremely limited on Android
    1498                 :   // so don't preallocate space. This is also not effective
    1499                 :   // on log structured file systems used by Android devices
    1500                 : #if !defined(ANDROID) && !defined(MOZ_PLATFORM_MAEMO)
    1501                 :   // Don't preallocate if less than 500MiB is available.
    1502            1318 :   nsCOMPtr<nsILocalFile> localFile = do_QueryInterface(mDatabaseFile);
    1503             659 :   NS_ENSURE_STATE(localFile);
    1504                 :   PRInt64 bytesAvailable;
    1505             659 :   nsresult rv = localFile->GetDiskSpaceAvailable(&bytesAvailable);
    1506             659 :   NS_ENSURE_SUCCESS(rv, rv);
    1507             659 :   if (bytesAvailable < MIN_AVAILABLE_BYTES_PER_CHUNKED_GROWTH) {
    1508               0 :     return NS_ERROR_FILE_TOO_BIG;
    1509                 :   }
    1510                 : 
    1511                 :   (void)::sqlite3_file_control(mDBConn,
    1512            1318 :                                aDatabaseName.Length() ? nsPromiseFlatCString(aDatabaseName).get() : NULL,
    1513                 :                                SQLITE_FCNTL_CHUNK_SIZE,
    1514             659 :                                &aChunkSize);
    1515                 : #endif
    1516             659 :   return NS_OK;
    1517                 : }
    1518                 : 
    1519                 : NS_IMETHODIMP
    1520              76 : Connection::EnableModule(const nsACString& aModuleName)
    1521                 : {
    1522              76 :   if (!mDBConn) return NS_ERROR_NOT_INITIALIZED;
    1523                 : 
    1524              76 :   for (size_t i = 0; i < ArrayLength(gModules); i++) {
    1525              76 :     struct Module* m = &gModules[i];
    1526              76 :     if (aModuleName.Equals(m->name)) {
    1527              76 :       int srv = m->registerFunc(mDBConn, m->name);
    1528              76 :       if (srv != SQLITE_OK)
    1529               0 :         return convertResultCode(srv);
    1530                 : 
    1531              76 :       return NS_OK;
    1532                 :     }
    1533                 :   }
    1534                 : 
    1535               0 :   return NS_ERROR_FAILURE;
    1536                 : }
    1537                 : 
    1538                 : } // namespace storage
    1539                 : } // namespace mozilla

Generated by: LCOV version 1.7