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