• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved.
3  * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com)
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25  */
26 
27 #include "config.h"
28 #include "modules/webdatabase/sqlite/SQLiteDatabase.h"
29 
30 #include <sqlite3.h>
31 #include "platform/Logging.h"
32 #include "modules/webdatabase/sqlite/SQLiteFileSystem.h"
33 #include "modules/webdatabase/sqlite/SQLiteStatement.h"
34 #include "modules/webdatabase/DatabaseAuthorizer.h"
35 
36 namespace WebCore {
37 
38 const int SQLResultDone = SQLITE_DONE;
39 const int SQLResultOk = SQLITE_OK;
40 const int SQLResultRow = SQLITE_ROW;
41 const int SQLResultFull = SQLITE_FULL;
42 const int SQLResultInterrupt = SQLITE_INTERRUPT;
43 const int SQLResultConstraint = SQLITE_CONSTRAINT;
44 
45 static const char notOpenErrorMessage[] = "database is not open";
46 
SQLiteDatabase()47 SQLiteDatabase::SQLiteDatabase()
48     : m_db(0)
49     , m_pageSize(-1)
50     , m_transactionInProgress(false)
51     , m_sharable(false)
52     , m_openingThread(0)
53     , m_interrupted(false)
54     , m_openError(SQLITE_ERROR)
55     , m_openErrorMessage()
56     , m_lastChangesCount(0)
57 {
58 }
59 
~SQLiteDatabase()60 SQLiteDatabase::~SQLiteDatabase()
61 {
62     close();
63 }
64 
open(const String & filename,bool forWebSQLDatabase)65 bool SQLiteDatabase::open(const String& filename, bool forWebSQLDatabase)
66 {
67     close();
68 
69     m_openError = SQLiteFileSystem::openDatabase(filename, &m_db, forWebSQLDatabase);
70     if (m_openError != SQLITE_OK) {
71         m_openErrorMessage = m_db ? sqlite3_errmsg(m_db) : "sqlite_open returned null";
72         WTF_LOG_ERROR("SQLite database failed to load from %s\nCause - %s", filename.ascii().data(),
73             m_openErrorMessage.data());
74         sqlite3_close(m_db);
75         m_db = 0;
76         return false;
77     }
78 
79     m_openError = sqlite3_extended_result_codes(m_db, 1);
80     if (m_openError != SQLITE_OK) {
81         m_openErrorMessage = sqlite3_errmsg(m_db);
82         WTF_LOG_ERROR("SQLite database error when enabling extended errors - %s", m_openErrorMessage.data());
83         sqlite3_close(m_db);
84         m_db = 0;
85         return false;
86     }
87 
88     if (isOpen())
89         m_openingThread = currentThread();
90     else
91         m_openErrorMessage = "sqlite_open returned null";
92 
93     if (!SQLiteStatement(*this, "PRAGMA temp_store = MEMORY;").executeCommand())
94         WTF_LOG_ERROR("SQLite database could not set temp_store to memory");
95 
96     return isOpen();
97 }
98 
close()99 void SQLiteDatabase::close()
100 {
101     if (m_db) {
102         // FIXME: This is being called on the main thread during JS GC. <rdar://problem/5739818>
103         // ASSERT(currentThread() == m_openingThread);
104         sqlite3* db = m_db;
105         {
106             MutexLocker locker(m_databaseClosingMutex);
107             m_db = 0;
108         }
109         sqlite3_close(db);
110     }
111 
112     m_openingThread = 0;
113     m_openError = SQLITE_ERROR;
114     m_openErrorMessage = CString();
115 }
116 
interrupt()117 void SQLiteDatabase::interrupt()
118 {
119     m_interrupted = true;
120     while (!m_lockingMutex.tryLock()) {
121         MutexLocker locker(m_databaseClosingMutex);
122         if (!m_db)
123             return;
124         sqlite3_interrupt(m_db);
125         yield();
126     }
127 
128     m_lockingMutex.unlock();
129 }
130 
isInterrupted()131 bool SQLiteDatabase::isInterrupted()
132 {
133     ASSERT(!m_lockingMutex.tryLock());
134     return m_interrupted;
135 }
136 
setMaximumSize(int64_t size)137 void SQLiteDatabase::setMaximumSize(int64_t size)
138 {
139     if (size < 0)
140         size = 0;
141 
142     int currentPageSize = pageSize();
143 
144     ASSERT(currentPageSize || !m_db);
145     int64_t newMaxPageCount = currentPageSize ? size / currentPageSize : 0;
146 
147     MutexLocker locker(m_authorizerLock);
148     enableAuthorizer(false);
149 
150     SQLiteStatement statement(*this, "PRAGMA max_page_count = " + String::number(newMaxPageCount));
151     statement.prepare();
152     if (statement.step() != SQLResultRow)
153 #if OS(WIN)
154         WTF_LOG_ERROR("Failed to set maximum size of database to %I64i bytes", static_cast<long long>(size));
155 #else
156         WTF_LOG_ERROR("Failed to set maximum size of database to %lli bytes", static_cast<long long>(size));
157 #endif
158 
159     enableAuthorizer(true);
160 
161 }
162 
pageSize()163 int SQLiteDatabase::pageSize()
164 {
165     // Since the page size of a database is locked in at creation and therefore cannot be dynamic,
166     // we can cache the value for future use
167     if (m_pageSize == -1) {
168         MutexLocker locker(m_authorizerLock);
169         enableAuthorizer(false);
170 
171         SQLiteStatement statement(*this, "PRAGMA page_size");
172         m_pageSize = statement.getColumnInt(0);
173 
174         enableAuthorizer(true);
175     }
176 
177     return m_pageSize;
178 }
179 
freeSpaceSize()180 int64_t SQLiteDatabase::freeSpaceSize()
181 {
182     int64_t freelistCount = 0;
183 
184     {
185         MutexLocker locker(m_authorizerLock);
186         enableAuthorizer(false);
187         // Note: freelist_count was added in SQLite 3.4.1.
188         SQLiteStatement statement(*this, "PRAGMA freelist_count");
189         freelistCount = statement.getColumnInt64(0);
190         enableAuthorizer(true);
191     }
192 
193     return freelistCount * pageSize();
194 }
195 
totalSize()196 int64_t SQLiteDatabase::totalSize()
197 {
198     int64_t pageCount = 0;
199 
200     {
201         MutexLocker locker(m_authorizerLock);
202         enableAuthorizer(false);
203         SQLiteStatement statement(*this, "PRAGMA page_count");
204         pageCount = statement.getColumnInt64(0);
205         enableAuthorizer(true);
206     }
207 
208     return pageCount * pageSize();
209 }
210 
setBusyTimeout(int ms)211 void SQLiteDatabase::setBusyTimeout(int ms)
212 {
213     if (m_db)
214         sqlite3_busy_timeout(m_db, ms);
215     else
216         WTF_LOG(SQLDatabase, "BusyTimeout set on non-open database");
217 }
218 
executeCommand(const String & sql)219 bool SQLiteDatabase::executeCommand(const String& sql)
220 {
221     return SQLiteStatement(*this, sql).executeCommand();
222 }
223 
tableExists(const String & tablename)224 bool SQLiteDatabase::tableExists(const String& tablename)
225 {
226     if (!isOpen())
227         return false;
228 
229     String statement = "SELECT name FROM sqlite_master WHERE type = 'table' AND name = '" + tablename + "';";
230 
231     SQLiteStatement sql(*this, statement);
232     sql.prepare();
233     return sql.step() == SQLITE_ROW;
234 }
235 
runVacuumCommand()236 int SQLiteDatabase::runVacuumCommand()
237 {
238     if (!executeCommand("VACUUM;"))
239         WTF_LOG(SQLDatabase, "Unable to vacuum database - %s", lastErrorMsg());
240     return lastError();
241 }
242 
runIncrementalVacuumCommand()243 int SQLiteDatabase::runIncrementalVacuumCommand()
244 {
245     MutexLocker locker(m_authorizerLock);
246     enableAuthorizer(false);
247 
248     if (!executeCommand("PRAGMA incremental_vacuum"))
249         WTF_LOG(SQLDatabase, "Unable to run incremental vacuum - %s", lastErrorMsg());
250 
251     enableAuthorizer(true);
252     return lastError();
253 }
254 
lastInsertRowID()255 int64_t SQLiteDatabase::lastInsertRowID()
256 {
257     if (!m_db)
258         return 0;
259     return sqlite3_last_insert_rowid(m_db);
260 }
261 
updateLastChangesCount()262 void SQLiteDatabase::updateLastChangesCount()
263 {
264     if (!m_db)
265         return;
266 
267     m_lastChangesCount = sqlite3_total_changes(m_db);
268 }
269 
lastChanges()270 int SQLiteDatabase::lastChanges()
271 {
272     if (!m_db)
273         return 0;
274 
275     return sqlite3_total_changes(m_db) - m_lastChangesCount;
276 }
277 
lastError()278 int SQLiteDatabase::lastError()
279 {
280     return m_db ? sqlite3_errcode(m_db) : m_openError;
281 }
282 
lastErrorMsg()283 const char* SQLiteDatabase::lastErrorMsg()
284 {
285     if (m_db)
286         return sqlite3_errmsg(m_db);
287     return m_openErrorMessage.isNull() ? notOpenErrorMessage : m_openErrorMessage.data();
288 }
289 
authorizerFunction(void * userData,int actionCode,const char * parameter1,const char * parameter2,const char *,const char *)290 int SQLiteDatabase::authorizerFunction(void* userData, int actionCode, const char* parameter1, const char* parameter2, const char* /*databaseName*/, const char* /*trigger_or_view*/)
291 {
292     DatabaseAuthorizer* auth = static_cast<DatabaseAuthorizer*>(userData);
293     ASSERT(auth);
294 
295     switch (actionCode) {
296         case SQLITE_CREATE_INDEX:
297             return auth->createIndex(parameter1, parameter2);
298         case SQLITE_CREATE_TABLE:
299             return auth->createTable(parameter1);
300         case SQLITE_CREATE_TEMP_INDEX:
301             return auth->createTempIndex(parameter1, parameter2);
302         case SQLITE_CREATE_TEMP_TABLE:
303             return auth->createTempTable(parameter1);
304         case SQLITE_CREATE_TEMP_TRIGGER:
305             return auth->createTempTrigger(parameter1, parameter2);
306         case SQLITE_CREATE_TEMP_VIEW:
307             return auth->createTempView(parameter1);
308         case SQLITE_CREATE_TRIGGER:
309             return auth->createTrigger(parameter1, parameter2);
310         case SQLITE_CREATE_VIEW:
311             return auth->createView(parameter1);
312         case SQLITE_DELETE:
313             return auth->allowDelete(parameter1);
314         case SQLITE_DROP_INDEX:
315             return auth->dropIndex(parameter1, parameter2);
316         case SQLITE_DROP_TABLE:
317             return auth->dropTable(parameter1);
318         case SQLITE_DROP_TEMP_INDEX:
319             return auth->dropTempIndex(parameter1, parameter2);
320         case SQLITE_DROP_TEMP_TABLE:
321             return auth->dropTempTable(parameter1);
322         case SQLITE_DROP_TEMP_TRIGGER:
323             return auth->dropTempTrigger(parameter1, parameter2);
324         case SQLITE_DROP_TEMP_VIEW:
325             return auth->dropTempView(parameter1);
326         case SQLITE_DROP_TRIGGER:
327             return auth->dropTrigger(parameter1, parameter2);
328         case SQLITE_DROP_VIEW:
329             return auth->dropView(parameter1);
330         case SQLITE_INSERT:
331             return auth->allowInsert(parameter1);
332         case SQLITE_PRAGMA:
333             return auth->allowPragma(parameter1, parameter2);
334         case SQLITE_READ:
335             return auth->allowRead(parameter1, parameter2);
336         case SQLITE_SELECT:
337             return auth->allowSelect();
338         case SQLITE_TRANSACTION:
339             return auth->allowTransaction();
340         case SQLITE_UPDATE:
341             return auth->allowUpdate(parameter1, parameter2);
342         case SQLITE_ATTACH:
343             return auth->allowAttach(parameter1);
344         case SQLITE_DETACH:
345             return auth->allowDetach(parameter1);
346         case SQLITE_ALTER_TABLE:
347             return auth->allowAlterTable(parameter1, parameter2);
348         case SQLITE_REINDEX:
349             return auth->allowReindex(parameter1);
350 #if SQLITE_VERSION_NUMBER >= 3003013
351         case SQLITE_ANALYZE:
352             return auth->allowAnalyze(parameter1);
353         case SQLITE_CREATE_VTABLE:
354             return auth->createVTable(parameter1, parameter2);
355         case SQLITE_DROP_VTABLE:
356             return auth->dropVTable(parameter1, parameter2);
357         case SQLITE_FUNCTION:
358             return auth->allowFunction(parameter2);
359 #endif
360         default:
361             ASSERT_NOT_REACHED();
362             return SQLAuthDeny;
363     }
364 }
365 
setAuthorizer(DatabaseAuthorizer * auth)366 void SQLiteDatabase::setAuthorizer(DatabaseAuthorizer* auth)
367 {
368     if (!m_db) {
369         WTF_LOG_ERROR("Attempt to set an authorizer on a non-open SQL database");
370         ASSERT_NOT_REACHED();
371         return;
372     }
373 
374     MutexLocker locker(m_authorizerLock);
375 
376     m_authorizer = auth;
377 
378     enableAuthorizer(true);
379 }
380 
enableAuthorizer(bool enable)381 void SQLiteDatabase::enableAuthorizer(bool enable)
382 {
383     if (m_authorizer && enable)
384         sqlite3_set_authorizer(m_db, SQLiteDatabase::authorizerFunction, m_authorizer.get());
385     else
386         sqlite3_set_authorizer(m_db, NULL, 0);
387 }
388 
isAutoCommitOn() const389 bool SQLiteDatabase::isAutoCommitOn() const
390 {
391     return sqlite3_get_autocommit(m_db);
392 }
393 
turnOnIncrementalAutoVacuum()394 bool SQLiteDatabase::turnOnIncrementalAutoVacuum()
395 {
396     SQLiteStatement statement(*this, "PRAGMA auto_vacuum");
397     int autoVacuumMode = statement.getColumnInt(0);
398     int error = lastError();
399 
400     // Check if we got an error while trying to get the value of the auto_vacuum flag.
401     // If we got a SQLITE_BUSY error, then there's probably another transaction in
402     // progress on this database. In this case, keep the current value of the
403     // auto_vacuum flag and try to set it to INCREMENTAL the next time we open this
404     // database. If the error is not SQLITE_BUSY, then we probably ran into a more
405     // serious problem and should return false (to log an error message).
406     if (error != SQLITE_ROW)
407         return false;
408 
409     switch (autoVacuumMode) {
410     case AutoVacuumIncremental:
411         return true;
412     case AutoVacuumFull:
413         return executeCommand("PRAGMA auto_vacuum = 2");
414     case AutoVacuumNone:
415     default:
416         if (!executeCommand("PRAGMA auto_vacuum = 2"))
417             return false;
418         runVacuumCommand();
419         error = lastError();
420         return (error == SQLITE_OK);
421     }
422 }
423 
trace(Visitor * visitor)424 void SQLiteDatabase::trace(Visitor* visitor)
425 {
426     visitor->trace(m_authorizer);
427 }
428 
429 } // namespace WebCore
430