• 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 SQLResultError = SQLITE_ERROR;
40 const int SQLResultOk = SQLITE_OK;
41 const int SQLResultRow = SQLITE_ROW;
42 const int SQLResultSchema = SQLITE_SCHEMA;
43 const int SQLResultFull = SQLITE_FULL;
44 const int SQLResultInterrupt = SQLITE_INTERRUPT;
45 const int SQLResultConstraint = SQLITE_CONSTRAINT;
46 
47 static const char notOpenErrorMessage[] = "database is not open";
48 
SQLiteDatabase()49 SQLiteDatabase::SQLiteDatabase()
50     : m_db(0)
51     , m_pageSize(-1)
52     , m_transactionInProgress(false)
53     , m_sharable(false)
54     , m_openingThread(0)
55     , m_interrupted(false)
56     , m_openError(SQLITE_ERROR)
57     , m_openErrorMessage()
58     , m_lastChangesCount(0)
59 {
60 }
61 
~SQLiteDatabase()62 SQLiteDatabase::~SQLiteDatabase()
63 {
64     close();
65 }
66 
open(const String & filename,bool forWebSQLDatabase)67 bool SQLiteDatabase::open(const String& filename, bool forWebSQLDatabase)
68 {
69     close();
70 
71     m_openError = SQLiteFileSystem::openDatabase(filename, &m_db, forWebSQLDatabase);
72     if (m_openError != SQLITE_OK) {
73         m_openErrorMessage = m_db ? sqlite3_errmsg(m_db) : "sqlite_open returned null";
74         WTF_LOG_ERROR("SQLite database failed to load from %s\nCause - %s", filename.ascii().data(),
75             m_openErrorMessage.data());
76         sqlite3_close(m_db);
77         m_db = 0;
78         return false;
79     }
80 
81     m_openError = sqlite3_extended_result_codes(m_db, 1);
82     if (m_openError != SQLITE_OK) {
83         m_openErrorMessage = sqlite3_errmsg(m_db);
84         WTF_LOG_ERROR("SQLite database error when enabling extended errors - %s", m_openErrorMessage.data());
85         sqlite3_close(m_db);
86         m_db = 0;
87         return false;
88     }
89 
90     if (isOpen())
91         m_openingThread = currentThread();
92     else
93         m_openErrorMessage = "sqlite_open returned null";
94 
95     if (!SQLiteStatement(*this, "PRAGMA temp_store = MEMORY;").executeCommand())
96         WTF_LOG_ERROR("SQLite database could not set temp_store to memory");
97 
98     return isOpen();
99 }
100 
close()101 void SQLiteDatabase::close()
102 {
103     if (m_db) {
104         // FIXME: This is being called on the main thread during JS GC. <rdar://problem/5739818>
105         // ASSERT(currentThread() == m_openingThread);
106         sqlite3* db = m_db;
107         {
108             MutexLocker locker(m_databaseClosingMutex);
109             m_db = 0;
110         }
111         sqlite3_close(db);
112     }
113 
114     m_openingThread = 0;
115     m_openError = SQLITE_ERROR;
116     m_openErrorMessage = CString();
117 }
118 
interrupt()119 void SQLiteDatabase::interrupt()
120 {
121     m_interrupted = true;
122     while (!m_lockingMutex.tryLock()) {
123         MutexLocker locker(m_databaseClosingMutex);
124         if (!m_db)
125             return;
126         sqlite3_interrupt(m_db);
127         yield();
128     }
129 
130     m_lockingMutex.unlock();
131 }
132 
isInterrupted()133 bool SQLiteDatabase::isInterrupted()
134 {
135     ASSERT(!m_lockingMutex.tryLock());
136     return m_interrupted;
137 }
138 
setFullsync(bool fsync)139 void SQLiteDatabase::setFullsync(bool fsync)
140 {
141     if (fsync)
142         executeCommand("PRAGMA fullfsync = 1;");
143     else
144         executeCommand("PRAGMA fullfsync = 0;");
145 }
146 
maximumSize()147 int64_t SQLiteDatabase::maximumSize()
148 {
149     int64_t maxPageCount = 0;
150 
151     {
152         MutexLocker locker(m_authorizerLock);
153         enableAuthorizer(false);
154         SQLiteStatement statement(*this, "PRAGMA max_page_count");
155         maxPageCount = statement.getColumnInt64(0);
156         enableAuthorizer(true);
157     }
158 
159     return maxPageCount * pageSize();
160 }
161 
setMaximumSize(int64_t size)162 void SQLiteDatabase::setMaximumSize(int64_t size)
163 {
164     if (size < 0)
165         size = 0;
166 
167     int currentPageSize = pageSize();
168 
169     ASSERT(currentPageSize || !m_db);
170     int64_t newMaxPageCount = currentPageSize ? size / currentPageSize : 0;
171 
172     MutexLocker locker(m_authorizerLock);
173     enableAuthorizer(false);
174 
175     SQLiteStatement statement(*this, "PRAGMA max_page_count = " + String::number(newMaxPageCount));
176     statement.prepare();
177     if (statement.step() != SQLResultRow)
178 #if OS(WIN)
179         WTF_LOG_ERROR("Failed to set maximum size of database to %I64i bytes", static_cast<long long>(size));
180 #else
181         WTF_LOG_ERROR("Failed to set maximum size of database to %lli bytes", static_cast<long long>(size));
182 #endif
183 
184     enableAuthorizer(true);
185 
186 }
187 
pageSize()188 int SQLiteDatabase::pageSize()
189 {
190     // Since the page size of a database is locked in at creation and therefore cannot be dynamic,
191     // we can cache the value for future use
192     if (m_pageSize == -1) {
193         MutexLocker locker(m_authorizerLock);
194         enableAuthorizer(false);
195 
196         SQLiteStatement statement(*this, "PRAGMA page_size");
197         m_pageSize = statement.getColumnInt(0);
198 
199         enableAuthorizer(true);
200     }
201 
202     return m_pageSize;
203 }
204 
freeSpaceSize()205 int64_t SQLiteDatabase::freeSpaceSize()
206 {
207     int64_t freelistCount = 0;
208 
209     {
210         MutexLocker locker(m_authorizerLock);
211         enableAuthorizer(false);
212         // Note: freelist_count was added in SQLite 3.4.1.
213         SQLiteStatement statement(*this, "PRAGMA freelist_count");
214         freelistCount = statement.getColumnInt64(0);
215         enableAuthorizer(true);
216     }
217 
218     return freelistCount * pageSize();
219 }
220 
totalSize()221 int64_t SQLiteDatabase::totalSize()
222 {
223     int64_t pageCount = 0;
224 
225     {
226         MutexLocker locker(m_authorizerLock);
227         enableAuthorizer(false);
228         SQLiteStatement statement(*this, "PRAGMA page_count");
229         pageCount = statement.getColumnInt64(0);
230         enableAuthorizer(true);
231     }
232 
233     return pageCount * pageSize();
234 }
235 
setSynchronous(SynchronousPragma sync)236 void SQLiteDatabase::setSynchronous(SynchronousPragma sync)
237 {
238     executeCommand("PRAGMA synchronous = " + String::number(sync));
239 }
240 
setBusyTimeout(int ms)241 void SQLiteDatabase::setBusyTimeout(int ms)
242 {
243     if (m_db)
244         sqlite3_busy_timeout(m_db, ms);
245     else
246         WTF_LOG(SQLDatabase, "BusyTimeout set on non-open database");
247 }
248 
setBusyHandler(int (* handler)(void *,int))249 void SQLiteDatabase::setBusyHandler(int(*handler)(void*, int))
250 {
251     if (m_db)
252         sqlite3_busy_handler(m_db, handler, NULL);
253     else
254         WTF_LOG(SQLDatabase, "Busy handler set on non-open database");
255 }
256 
executeCommand(const String & sql)257 bool SQLiteDatabase::executeCommand(const String& sql)
258 {
259     return SQLiteStatement(*this, sql).executeCommand();
260 }
261 
returnsAtLeastOneResult(const String & sql)262 bool SQLiteDatabase::returnsAtLeastOneResult(const String& sql)
263 {
264     return SQLiteStatement(*this, sql).returnsAtLeastOneResult();
265 }
266 
tableExists(const String & tablename)267 bool SQLiteDatabase::tableExists(const String& tablename)
268 {
269     if (!isOpen())
270         return false;
271 
272     String statement = "SELECT name FROM sqlite_master WHERE type = 'table' AND name = '" + tablename + "';";
273 
274     SQLiteStatement sql(*this, statement);
275     sql.prepare();
276     return sql.step() == SQLITE_ROW;
277 }
278 
clearAllTables()279 void SQLiteDatabase::clearAllTables()
280 {
281     String query = "SELECT name FROM sqlite_master WHERE type='table';";
282     Vector<String> tables;
283     if (!SQLiteStatement(*this, query).returnTextResults(0, tables)) {
284         WTF_LOG(SQLDatabase, "Unable to retrieve list of tables from database");
285         return;
286     }
287 
288     for (Vector<String>::iterator table = tables.begin(); table != tables.end(); ++table ) {
289         if (*table == "sqlite_sequence")
290             continue;
291         if (!executeCommand("DROP TABLE " + *table))
292             WTF_LOG(SQLDatabase, "Unable to drop table %s", (*table).ascii().data());
293     }
294 }
295 
runVacuumCommand()296 int SQLiteDatabase::runVacuumCommand()
297 {
298     if (!executeCommand("VACUUM;"))
299         WTF_LOG(SQLDatabase, "Unable to vacuum database - %s", lastErrorMsg());
300     return lastError();
301 }
302 
runIncrementalVacuumCommand()303 int SQLiteDatabase::runIncrementalVacuumCommand()
304 {
305     MutexLocker locker(m_authorizerLock);
306     enableAuthorizer(false);
307 
308     if (!executeCommand("PRAGMA incremental_vacuum"))
309         WTF_LOG(SQLDatabase, "Unable to run incremental vacuum - %s", lastErrorMsg());
310 
311     enableAuthorizer(true);
312     return lastError();
313 }
314 
lastInsertRowID()315 int64_t SQLiteDatabase::lastInsertRowID()
316 {
317     if (!m_db)
318         return 0;
319     return sqlite3_last_insert_rowid(m_db);
320 }
321 
updateLastChangesCount()322 void SQLiteDatabase::updateLastChangesCount()
323 {
324     if (!m_db)
325         return;
326 
327     m_lastChangesCount = sqlite3_total_changes(m_db);
328 }
329 
lastChanges()330 int SQLiteDatabase::lastChanges()
331 {
332     if (!m_db)
333         return 0;
334 
335     return sqlite3_total_changes(m_db) - m_lastChangesCount;
336 }
337 
lastError()338 int SQLiteDatabase::lastError()
339 {
340     return m_db ? sqlite3_errcode(m_db) : m_openError;
341 }
342 
lastErrorMsg()343 const char* SQLiteDatabase::lastErrorMsg()
344 {
345     if (m_db)
346         return sqlite3_errmsg(m_db);
347     return m_openErrorMessage.isNull() ? notOpenErrorMessage : m_openErrorMessage.data();
348 }
349 
350 #ifndef NDEBUG
disableThreadingChecks()351 void SQLiteDatabase::disableThreadingChecks()
352 {
353     // This doesn't guarantee that SQList was compiled with -DTHREADSAFE, or that you haven't turned off the mutexes.
354 #if SQLITE_VERSION_NUMBER >= 3003001
355     m_sharable = true;
356 #else
357     ASSERT(0); // Your SQLite doesn't support sharing handles across threads.
358 #endif
359 }
360 #endif
361 
authorizerFunction(void * userData,int actionCode,const char * parameter1,const char * parameter2,const char *,const char *)362 int SQLiteDatabase::authorizerFunction(void* userData, int actionCode, const char* parameter1, const char* parameter2, const char* /*databaseName*/, const char* /*trigger_or_view*/)
363 {
364     DatabaseAuthorizer* auth = static_cast<DatabaseAuthorizer*>(userData);
365     ASSERT(auth);
366 
367     switch (actionCode) {
368         case SQLITE_CREATE_INDEX:
369             return auth->createIndex(parameter1, parameter2);
370         case SQLITE_CREATE_TABLE:
371             return auth->createTable(parameter1);
372         case SQLITE_CREATE_TEMP_INDEX:
373             return auth->createTempIndex(parameter1, parameter2);
374         case SQLITE_CREATE_TEMP_TABLE:
375             return auth->createTempTable(parameter1);
376         case SQLITE_CREATE_TEMP_TRIGGER:
377             return auth->createTempTrigger(parameter1, parameter2);
378         case SQLITE_CREATE_TEMP_VIEW:
379             return auth->createTempView(parameter1);
380         case SQLITE_CREATE_TRIGGER:
381             return auth->createTrigger(parameter1, parameter2);
382         case SQLITE_CREATE_VIEW:
383             return auth->createView(parameter1);
384         case SQLITE_DELETE:
385             return auth->allowDelete(parameter1);
386         case SQLITE_DROP_INDEX:
387             return auth->dropIndex(parameter1, parameter2);
388         case SQLITE_DROP_TABLE:
389             return auth->dropTable(parameter1);
390         case SQLITE_DROP_TEMP_INDEX:
391             return auth->dropTempIndex(parameter1, parameter2);
392         case SQLITE_DROP_TEMP_TABLE:
393             return auth->dropTempTable(parameter1);
394         case SQLITE_DROP_TEMP_TRIGGER:
395             return auth->dropTempTrigger(parameter1, parameter2);
396         case SQLITE_DROP_TEMP_VIEW:
397             return auth->dropTempView(parameter1);
398         case SQLITE_DROP_TRIGGER:
399             return auth->dropTrigger(parameter1, parameter2);
400         case SQLITE_DROP_VIEW:
401             return auth->dropView(parameter1);
402         case SQLITE_INSERT:
403             return auth->allowInsert(parameter1);
404         case SQLITE_PRAGMA:
405             return auth->allowPragma(parameter1, parameter2);
406         case SQLITE_READ:
407             return auth->allowRead(parameter1, parameter2);
408         case SQLITE_SELECT:
409             return auth->allowSelect();
410         case SQLITE_TRANSACTION:
411             return auth->allowTransaction();
412         case SQLITE_UPDATE:
413             return auth->allowUpdate(parameter1, parameter2);
414         case SQLITE_ATTACH:
415             return auth->allowAttach(parameter1);
416         case SQLITE_DETACH:
417             return auth->allowDetach(parameter1);
418         case SQLITE_ALTER_TABLE:
419             return auth->allowAlterTable(parameter1, parameter2);
420         case SQLITE_REINDEX:
421             return auth->allowReindex(parameter1);
422 #if SQLITE_VERSION_NUMBER >= 3003013
423         case SQLITE_ANALYZE:
424             return auth->allowAnalyze(parameter1);
425         case SQLITE_CREATE_VTABLE:
426             return auth->createVTable(parameter1, parameter2);
427         case SQLITE_DROP_VTABLE:
428             return auth->dropVTable(parameter1, parameter2);
429         case SQLITE_FUNCTION:
430             return auth->allowFunction(parameter2);
431 #endif
432         default:
433             ASSERT_NOT_REACHED();
434             return SQLAuthDeny;
435     }
436 }
437 
setAuthorizer(PassRefPtr<DatabaseAuthorizer> auth)438 void SQLiteDatabase::setAuthorizer(PassRefPtr<DatabaseAuthorizer> auth)
439 {
440     if (!m_db) {
441         WTF_LOG_ERROR("Attempt to set an authorizer on a non-open SQL database");
442         ASSERT_NOT_REACHED();
443         return;
444     }
445 
446     MutexLocker locker(m_authorizerLock);
447 
448     m_authorizer = auth;
449 
450     enableAuthorizer(true);
451 }
452 
enableAuthorizer(bool enable)453 void SQLiteDatabase::enableAuthorizer(bool enable)
454 {
455     if (m_authorizer && enable)
456         sqlite3_set_authorizer(m_db, SQLiteDatabase::authorizerFunction, m_authorizer.get());
457     else
458         sqlite3_set_authorizer(m_db, NULL, 0);
459 }
460 
isAutoCommitOn() const461 bool SQLiteDatabase::isAutoCommitOn() const
462 {
463     return sqlite3_get_autocommit(m_db);
464 }
465 
turnOnIncrementalAutoVacuum()466 bool SQLiteDatabase::turnOnIncrementalAutoVacuum()
467 {
468     SQLiteStatement statement(*this, "PRAGMA auto_vacuum");
469     int autoVacuumMode = statement.getColumnInt(0);
470     int error = lastError();
471 
472     // Check if we got an error while trying to get the value of the auto_vacuum flag.
473     // If we got a SQLITE_BUSY error, then there's probably another transaction in
474     // progress on this database. In this case, keep the current value of the
475     // auto_vacuum flag and try to set it to INCREMENTAL the next time we open this
476     // database. If the error is not SQLITE_BUSY, then we probably ran into a more
477     // serious problem and should return false (to log an error message).
478     if (error != SQLITE_ROW)
479         return false;
480 
481     switch (autoVacuumMode) {
482     case AutoVacuumIncremental:
483         return true;
484     case AutoVacuumFull:
485         return executeCommand("PRAGMA auto_vacuum = 2");
486     case AutoVacuumNone:
487     default:
488         if (!executeCommand("PRAGMA auto_vacuum = 2"))
489             return false;
490         runVacuumCommand();
491         error = lastError();
492         return (error == SQLITE_OK);
493     }
494 }
495 
496 } // namespace WebCore
497