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 blink {
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_openError(SQLITE_ERROR)
54 , m_openErrorMessage()
55 , m_lastChangesCount(0)
56 {
57 }
58
~SQLiteDatabase()59 SQLiteDatabase::~SQLiteDatabase()
60 {
61 close();
62 }
63
open(const String & filename,bool forWebSQLDatabase)64 bool SQLiteDatabase::open(const String& filename, bool forWebSQLDatabase)
65 {
66 close();
67
68 m_openError = SQLiteFileSystem::openDatabase(filename, &m_db, forWebSQLDatabase);
69 if (m_openError != SQLITE_OK) {
70 m_openErrorMessage = m_db ? sqlite3_errmsg(m_db) : "sqlite_open returned null";
71 WTF_LOG_ERROR("SQLite database failed to load from %s\nCause - %s", filename.ascii().data(),
72 m_openErrorMessage.data());
73 sqlite3_close(m_db);
74 m_db = 0;
75 return false;
76 }
77
78 m_openError = sqlite3_extended_result_codes(m_db, 1);
79 if (m_openError != SQLITE_OK) {
80 m_openErrorMessage = sqlite3_errmsg(m_db);
81 WTF_LOG_ERROR("SQLite database error when enabling extended errors - %s", m_openErrorMessage.data());
82 sqlite3_close(m_db);
83 m_db = 0;
84 return false;
85 }
86
87 if (isOpen())
88 m_openingThread = currentThread();
89 else
90 m_openErrorMessage = "sqlite_open returned null";
91
92 if (!SQLiteStatement(*this, "PRAGMA temp_store = MEMORY;").executeCommand())
93 WTF_LOG_ERROR("SQLite database could not set temp_store to memory");
94
95 return isOpen();
96 }
97
close()98 void SQLiteDatabase::close()
99 {
100 if (m_db) {
101 // FIXME: This is being called on the main thread during JS GC. <rdar://problem/5739818>
102 // ASSERT(currentThread() == m_openingThread);
103 sqlite3* db = m_db;
104 {
105 MutexLocker locker(m_databaseClosingMutex);
106 m_db = 0;
107 }
108 sqlite3_close(db);
109 }
110
111 m_openingThread = 0;
112 m_openError = SQLITE_ERROR;
113 m_openErrorMessage = CString();
114 }
115
setMaximumSize(int64_t size)116 void SQLiteDatabase::setMaximumSize(int64_t size)
117 {
118 if (size < 0)
119 size = 0;
120
121 int currentPageSize = pageSize();
122
123 ASSERT(currentPageSize || !m_db);
124 int64_t newMaxPageCount = currentPageSize ? size / currentPageSize : 0;
125
126 MutexLocker locker(m_authorizerLock);
127 enableAuthorizer(false);
128
129 SQLiteStatement statement(*this, "PRAGMA max_page_count = " + String::number(newMaxPageCount));
130 statement.prepare();
131 if (statement.step() != SQLResultRow)
132 #if OS(WIN)
133 WTF_LOG_ERROR("Failed to set maximum size of database to %I64i bytes", static_cast<long long>(size));
134 #else
135 WTF_LOG_ERROR("Failed to set maximum size of database to %lli bytes", static_cast<long long>(size));
136 #endif
137
138 enableAuthorizer(true);
139
140 }
141
pageSize()142 int SQLiteDatabase::pageSize()
143 {
144 // Since the page size of a database is locked in at creation and therefore cannot be dynamic,
145 // we can cache the value for future use
146 if (m_pageSize == -1) {
147 MutexLocker locker(m_authorizerLock);
148 enableAuthorizer(false);
149
150 SQLiteStatement statement(*this, "PRAGMA page_size");
151 m_pageSize = statement.getColumnInt(0);
152
153 enableAuthorizer(true);
154 }
155
156 return m_pageSize;
157 }
158
freeSpaceSize()159 int64_t SQLiteDatabase::freeSpaceSize()
160 {
161 int64_t freelistCount = 0;
162
163 {
164 MutexLocker locker(m_authorizerLock);
165 enableAuthorizer(false);
166 // Note: freelist_count was added in SQLite 3.4.1.
167 SQLiteStatement statement(*this, "PRAGMA freelist_count");
168 freelistCount = statement.getColumnInt64(0);
169 enableAuthorizer(true);
170 }
171
172 return freelistCount * pageSize();
173 }
174
totalSize()175 int64_t SQLiteDatabase::totalSize()
176 {
177 int64_t pageCount = 0;
178
179 {
180 MutexLocker locker(m_authorizerLock);
181 enableAuthorizer(false);
182 SQLiteStatement statement(*this, "PRAGMA page_count");
183 pageCount = statement.getColumnInt64(0);
184 enableAuthorizer(true);
185 }
186
187 return pageCount * pageSize();
188 }
189
setBusyTimeout(int ms)190 void SQLiteDatabase::setBusyTimeout(int ms)
191 {
192 if (m_db)
193 sqlite3_busy_timeout(m_db, ms);
194 else
195 WTF_LOG(SQLDatabase, "BusyTimeout set on non-open database");
196 }
197
executeCommand(const String & sql)198 bool SQLiteDatabase::executeCommand(const String& sql)
199 {
200 return SQLiteStatement(*this, sql).executeCommand();
201 }
202
tableExists(const String & tablename)203 bool SQLiteDatabase::tableExists(const String& tablename)
204 {
205 if (!isOpen())
206 return false;
207
208 String statement = "SELECT name FROM sqlite_master WHERE type = 'table' AND name = '" + tablename + "';";
209
210 SQLiteStatement sql(*this, statement);
211 sql.prepare();
212 return sql.step() == SQLITE_ROW;
213 }
214
runVacuumCommand()215 int SQLiteDatabase::runVacuumCommand()
216 {
217 if (!executeCommand("VACUUM;"))
218 WTF_LOG(SQLDatabase, "Unable to vacuum database - %s", lastErrorMsg());
219 return lastError();
220 }
221
runIncrementalVacuumCommand()222 int SQLiteDatabase::runIncrementalVacuumCommand()
223 {
224 MutexLocker locker(m_authorizerLock);
225 enableAuthorizer(false);
226
227 if (!executeCommand("PRAGMA incremental_vacuum"))
228 WTF_LOG(SQLDatabase, "Unable to run incremental vacuum - %s", lastErrorMsg());
229
230 enableAuthorizer(true);
231 return lastError();
232 }
233
lastInsertRowID()234 int64_t SQLiteDatabase::lastInsertRowID()
235 {
236 if (!m_db)
237 return 0;
238 return sqlite3_last_insert_rowid(m_db);
239 }
240
updateLastChangesCount()241 void SQLiteDatabase::updateLastChangesCount()
242 {
243 if (!m_db)
244 return;
245
246 m_lastChangesCount = sqlite3_total_changes(m_db);
247 }
248
lastChanges()249 int SQLiteDatabase::lastChanges()
250 {
251 if (!m_db)
252 return 0;
253
254 return sqlite3_total_changes(m_db) - m_lastChangesCount;
255 }
256
lastError()257 int SQLiteDatabase::lastError()
258 {
259 return m_db ? sqlite3_errcode(m_db) : m_openError;
260 }
261
lastErrorMsg()262 const char* SQLiteDatabase::lastErrorMsg()
263 {
264 if (m_db)
265 return sqlite3_errmsg(m_db);
266 return m_openErrorMessage.isNull() ? notOpenErrorMessage : m_openErrorMessage.data();
267 }
268
authorizerFunction(void * userData,int actionCode,const char * parameter1,const char * parameter2,const char *,const char *)269 int SQLiteDatabase::authorizerFunction(void* userData, int actionCode, const char* parameter1, const char* parameter2, const char* /*databaseName*/, const char* /*trigger_or_view*/)
270 {
271 DatabaseAuthorizer* auth = static_cast<DatabaseAuthorizer*>(userData);
272 ASSERT(auth);
273
274 switch (actionCode) {
275 case SQLITE_CREATE_INDEX:
276 return auth->createIndex(parameter1, parameter2);
277 case SQLITE_CREATE_TABLE:
278 return auth->createTable(parameter1);
279 case SQLITE_CREATE_TEMP_INDEX:
280 return auth->createTempIndex(parameter1, parameter2);
281 case SQLITE_CREATE_TEMP_TABLE:
282 return auth->createTempTable(parameter1);
283 case SQLITE_CREATE_TEMP_TRIGGER:
284 return auth->createTempTrigger(parameter1, parameter2);
285 case SQLITE_CREATE_TEMP_VIEW:
286 return auth->createTempView(parameter1);
287 case SQLITE_CREATE_TRIGGER:
288 return auth->createTrigger(parameter1, parameter2);
289 case SQLITE_CREATE_VIEW:
290 return auth->createView(parameter1);
291 case SQLITE_DELETE:
292 return auth->allowDelete(parameter1);
293 case SQLITE_DROP_INDEX:
294 return auth->dropIndex(parameter1, parameter2);
295 case SQLITE_DROP_TABLE:
296 return auth->dropTable(parameter1);
297 case SQLITE_DROP_TEMP_INDEX:
298 return auth->dropTempIndex(parameter1, parameter2);
299 case SQLITE_DROP_TEMP_TABLE:
300 return auth->dropTempTable(parameter1);
301 case SQLITE_DROP_TEMP_TRIGGER:
302 return auth->dropTempTrigger(parameter1, parameter2);
303 case SQLITE_DROP_TEMP_VIEW:
304 return auth->dropTempView(parameter1);
305 case SQLITE_DROP_TRIGGER:
306 return auth->dropTrigger(parameter1, parameter2);
307 case SQLITE_DROP_VIEW:
308 return auth->dropView(parameter1);
309 case SQLITE_INSERT:
310 return auth->allowInsert(parameter1);
311 case SQLITE_PRAGMA:
312 return auth->allowPragma(parameter1, parameter2);
313 case SQLITE_READ:
314 return auth->allowRead(parameter1, parameter2);
315 case SQLITE_SELECT:
316 return auth->allowSelect();
317 case SQLITE_TRANSACTION:
318 return auth->allowTransaction();
319 case SQLITE_UPDATE:
320 return auth->allowUpdate(parameter1, parameter2);
321 case SQLITE_ATTACH:
322 return auth->allowAttach(parameter1);
323 case SQLITE_DETACH:
324 return auth->allowDetach(parameter1);
325 case SQLITE_ALTER_TABLE:
326 return auth->allowAlterTable(parameter1, parameter2);
327 case SQLITE_REINDEX:
328 return auth->allowReindex(parameter1);
329 #if SQLITE_VERSION_NUMBER >= 3003013
330 case SQLITE_ANALYZE:
331 return auth->allowAnalyze(parameter1);
332 case SQLITE_CREATE_VTABLE:
333 return auth->createVTable(parameter1, parameter2);
334 case SQLITE_DROP_VTABLE:
335 return auth->dropVTable(parameter1, parameter2);
336 case SQLITE_FUNCTION:
337 return auth->allowFunction(parameter2);
338 #endif
339 default:
340 ASSERT_NOT_REACHED();
341 return SQLAuthDeny;
342 }
343 }
344
setAuthorizer(DatabaseAuthorizer * auth)345 void SQLiteDatabase::setAuthorizer(DatabaseAuthorizer* auth)
346 {
347 if (!m_db) {
348 WTF_LOG_ERROR("Attempt to set an authorizer on a non-open SQL database");
349 ASSERT_NOT_REACHED();
350 return;
351 }
352
353 MutexLocker locker(m_authorizerLock);
354
355 m_authorizer = auth;
356
357 enableAuthorizer(true);
358 }
359
enableAuthorizer(bool enable)360 void SQLiteDatabase::enableAuthorizer(bool enable)
361 {
362 if (m_authorizer && enable)
363 sqlite3_set_authorizer(m_db, SQLiteDatabase::authorizerFunction, m_authorizer.get());
364 else
365 sqlite3_set_authorizer(m_db, NULL, 0);
366 }
367
isAutoCommitOn() const368 bool SQLiteDatabase::isAutoCommitOn() const
369 {
370 return sqlite3_get_autocommit(m_db);
371 }
372
turnOnIncrementalAutoVacuum()373 bool SQLiteDatabase::turnOnIncrementalAutoVacuum()
374 {
375 SQLiteStatement statement(*this, "PRAGMA auto_vacuum");
376 int autoVacuumMode = statement.getColumnInt(0);
377 int error = lastError();
378
379 // Check if we got an error while trying to get the value of the auto_vacuum flag.
380 // If we got a SQLITE_BUSY error, then there's probably another transaction in
381 // progress on this database. In this case, keep the current value of the
382 // auto_vacuum flag and try to set it to INCREMENTAL the next time we open this
383 // database. If the error is not SQLITE_BUSY, then we probably ran into a more
384 // serious problem and should return false (to log an error message).
385 if (error != SQLITE_ROW)
386 return false;
387
388 switch (autoVacuumMode) {
389 case AutoVacuumIncremental:
390 return true;
391 case AutoVacuumFull:
392 return executeCommand("PRAGMA auto_vacuum = 2");
393 case AutoVacuumNone:
394 default:
395 if (!executeCommand("PRAGMA auto_vacuum = 2"))
396 return false;
397 runVacuumCommand();
398 error = lastError();
399 return (error == SQLITE_OK);
400 }
401 }
402
trace(Visitor * visitor)403 void SQLiteDatabase::trace(Visitor* visitor)
404 {
405 visitor->trace(m_authorizer);
406 }
407
408 } // namespace blink
409