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