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