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