diff options
author | Steve Block <steveblock@google.com> | 2011-05-13 06:44:40 -0700 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2011-05-13 06:44:40 -0700 |
commit | 08014c20784f3db5df3a89b73cce46037b77eb59 (patch) | |
tree | 47749210d31e19e6e2f64036fa8fae2ad693476f /Source/WebCore/platform/sql | |
parent | 860220379e56aeb66424861ad602b07ee22b4055 (diff) | |
parent | 4c3661f7918f8b3f139f824efb7855bedccb4c94 (diff) | |
download | external_webkit-08014c20784f3db5df3a89b73cce46037b77eb59.zip external_webkit-08014c20784f3db5df3a89b73cce46037b77eb59.tar.gz external_webkit-08014c20784f3db5df3a89b73cce46037b77eb59.tar.bz2 |
Merge changes Ide388898,Ic49f367c,I1158a808,Iacb6ca5d,I2100dd3a,I5c1abe54,Ib0ef9902,I31dbc523,I570314b3
* changes:
Merge WebKit at r75315: Update WebKit version
Merge WebKit at r75315: Add FrameLoaderClient PageCache stubs
Merge WebKit at r75315: Stub out AXObjectCache::remove()
Merge WebKit at r75315: Fix ImageBuffer
Merge WebKit at r75315: Fix PluginData::initPlugins()
Merge WebKit at r75315: Fix conflicts
Merge WebKit at r75315: Fix Makefiles
Merge WebKit at r75315: Move Android-specific WebCore files to Source
Merge WebKit at r75315: Initial merge by git.
Diffstat (limited to 'Source/WebCore/platform/sql')
-rw-r--r-- | Source/WebCore/platform/sql/SQLValue.cpp | 56 | ||||
-rw-r--r-- | Source/WebCore/platform/sql/SQLValue.h | 58 | ||||
-rw-r--r-- | Source/WebCore/platform/sql/SQLiteAuthorizer.cpp | 43 | ||||
-rw-r--r-- | Source/WebCore/platform/sql/SQLiteDatabase.cpp | 476 | ||||
-rw-r--r-- | Source/WebCore/platform/sql/SQLiteDatabase.h | 159 | ||||
-rw-r--r-- | Source/WebCore/platform/sql/SQLiteFileSystem.cpp | 128 | ||||
-rw-r--r-- | Source/WebCore/platform/sql/SQLiteFileSystem.h | 117 | ||||
-rw-r--r-- | Source/WebCore/platform/sql/SQLiteStatement.cpp | 502 | ||||
-rw-r--r-- | Source/WebCore/platform/sql/SQLiteStatement.h | 104 | ||||
-rw-r--r-- | Source/WebCore/platform/sql/SQLiteTransaction.cpp | 106 | ||||
-rw-r--r-- | Source/WebCore/platform/sql/SQLiteTransaction.h | 56 | ||||
-rw-r--r-- | Source/WebCore/platform/sql/chromium/SQLiteFileSystemChromium.cpp | 103 | ||||
-rw-r--r-- | Source/WebCore/platform/sql/chromium/SQLiteFileSystemChromiumPosix.cpp | 1188 | ||||
-rw-r--r-- | Source/WebCore/platform/sql/chromium/SQLiteFileSystemChromiumWin.cpp | 169 |
14 files changed, 3265 insertions, 0 deletions
diff --git a/Source/WebCore/platform/sql/SQLValue.cpp b/Source/WebCore/platform/sql/SQLValue.cpp new file mode 100644 index 0000000..0ad643e --- /dev/null +++ b/Source/WebCore/platform/sql/SQLValue.cpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following condition + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "SQLValue.h" + +namespace WebCore { + +SQLValue::SQLValue(const SQLValue& val) + : m_type(val.m_type) + , m_number(val.m_number) + , m_string(val.m_string.threadsafeCopy()) +{ +} + +String SQLValue::string() const +{ + ASSERT(m_type == StringValue); + + // Must return a copy since ref-shared Strings are not thread safe + return m_string.threadsafeCopy(); +} + +double SQLValue::number() const +{ + ASSERT(m_type == NumberValue); + + return m_number; +} + +} diff --git a/Source/WebCore/platform/sql/SQLValue.h b/Source/WebCore/platform/sql/SQLValue.h new file mode 100644 index 0000000..0f854fc --- /dev/null +++ b/Source/WebCore/platform/sql/SQLValue.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following condition + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLValue_h +#define SQLValue_h + +#include "PlatformString.h" +#include <wtf/Threading.h> + +namespace WebCore { + + class SQLValue { + public: + enum Type { NullValue, NumberValue, StringValue }; + + SQLValue() : m_type(NullValue), m_number(0.0) { } + SQLValue(double number) : m_type(NumberValue), m_number(number) { } + SQLValue(const String& s) : m_type(StringValue), m_number(0.0), m_string(s) { } + SQLValue(const SQLValue&); + + Type type() const { return m_type; } + + String string() const; + double number() const; + + private: + Type m_type; + double m_number; + String m_string; + }; +}; + +#endif diff --git a/Source/WebCore/platform/sql/SQLiteAuthorizer.cpp b/Source/WebCore/platform/sql/SQLiteAuthorizer.cpp new file mode 100644 index 0000000..6fbf79d --- /dev/null +++ b/Source/WebCore/platform/sql/SQLiteAuthorizer.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following condition + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of + * its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "DatabaseAuthorizer.h" + +#if ENABLE(DATABASE) +#include <sqlite3.h> + +namespace WebCore { + +const int SQLAuthAllow = SQLITE_OK; +const int SQLAuthIgnore = SQLITE_IGNORE; +const int SQLAuthDeny = SQLITE_DENY; + +} // namespace WebCore + +#endif // ENABLE(DATABASE) diff --git a/Source/WebCore/platform/sql/SQLiteDatabase.cpp b/Source/WebCore/platform/sql/SQLiteDatabase.cpp new file mode 100644 index 0000000..5bb56ab --- /dev/null +++ b/Source/WebCore/platform/sql/SQLiteDatabase.cpp @@ -0,0 +1,476 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "SQLiteDatabase.h" + +#if ENABLE(DATABASE) +#include "DatabaseAuthorizer.h" +#include "Logging.h" +#include "SQLiteFileSystem.h" +#include "SQLiteStatement.h" +#include <sqlite3.h> +#include <wtf/Threading.h> +#include <wtf/text/CString.h> +#include <wtf/text/StringConcatenate.h> + +namespace WebCore { + +const int SQLResultDone = SQLITE_DONE; +const int SQLResultError = SQLITE_ERROR; +const int SQLResultOk = SQLITE_OK; +const int SQLResultRow = SQLITE_ROW; +const int SQLResultSchema = SQLITE_SCHEMA; +const int SQLResultFull = SQLITE_FULL; +const int SQLResultInterrupt = SQLITE_INTERRUPT; + +SQLiteDatabase::SQLiteDatabase() + : m_db(0) + , m_pageSize(-1) + , m_transactionInProgress(false) + , m_sharable(false) + , m_openingThread(0) + , m_interrupted(false) +{ +} + +SQLiteDatabase::~SQLiteDatabase() +{ + close(); +} + +bool SQLiteDatabase::open(const String& filename, bool forWebSQLDatabase) +{ + close(); + + if (SQLiteFileSystem::openDatabase(filename, &m_db, forWebSQLDatabase) != SQLITE_OK) { + LOG_ERROR("SQLite database failed to load from %s\nCause - %s", filename.ascii().data(), + sqlite3_errmsg(m_db)); + sqlite3_close(m_db); + m_db = 0; + return false; + } + if (sqlite3_extended_result_codes(m_db, 1) != SQLITE_OK) { + LOG_ERROR("SQLite database error when enabling extended errors - %s", sqlite3_errmsg(m_db)); + sqlite3_close(m_db); + m_db = 0; + return false; + } + + if (isOpen()) + m_openingThread = currentThread(); + + if (!SQLiteStatement(*this, "PRAGMA temp_store = MEMORY;").executeCommand()) + LOG_ERROR("SQLite database could not set temp_store to memory"); + + return isOpen(); +} + +void SQLiteDatabase::close() +{ + if (m_db) { + // FIXME: This is being called on themain thread during JS GC. <rdar://problem/5739818> + // ASSERT(currentThread() == m_openingThread); + sqlite3* db = m_db; + { + MutexLocker locker(m_databaseClosingMutex); + m_db = 0; + } + sqlite3_close(db); + } + + m_openingThread = 0; +} + +void SQLiteDatabase::interrupt() +{ +#if !ENABLE(SINGLE_THREADED) + m_interrupted = true; + while (!m_lockingMutex.tryLock()) { + MutexLocker locker(m_databaseClosingMutex); + if (!m_db) + return; + sqlite3_interrupt(m_db); + yield(); + } + + m_lockingMutex.unlock(); +#endif +} + +bool SQLiteDatabase::isInterrupted() +{ + ASSERT(!m_lockingMutex.tryLock()); + return m_interrupted; +} + +void SQLiteDatabase::setFullsync(bool fsync) +{ + if (fsync) + executeCommand("PRAGMA fullfsync = 1;"); + else + executeCommand("PRAGMA fullfsync = 0;"); +} + +int64_t SQLiteDatabase::maximumSize() +{ + int64_t maxPageCount = 0; + + { + MutexLocker locker(m_authorizerLock); + enableAuthorizer(false); + SQLiteStatement statement(*this, "PRAGMA max_page_count"); + maxPageCount = statement.getColumnInt64(0); + enableAuthorizer(true); + } + + return maxPageCount * pageSize(); +} + +void SQLiteDatabase::setMaximumSize(int64_t size) +{ + if (size < 0) + size = 0; + + int currentPageSize = pageSize(); + + ASSERT(currentPageSize); + int64_t newMaxPageCount = currentPageSize ? size / currentPageSize : 0; + + MutexLocker locker(m_authorizerLock); + enableAuthorizer(false); + + SQLiteStatement statement(*this, "PRAGMA max_page_count = " + String::number(newMaxPageCount)); + statement.prepare(); + if (statement.step() != SQLResultRow) +#if OS(WINDOWS) + LOG_ERROR("Failed to set maximum size of database to %I64i bytes", static_cast<long long>(size)); +#else + LOG_ERROR("Failed to set maximum size of database to %lli bytes", static_cast<long long>(size)); +#endif + + enableAuthorizer(true); + +} + +int SQLiteDatabase::pageSize() +{ + // Since the page size of a database is locked in at creation and therefore cannot be dynamic, + // we can cache the value for future use + if (m_pageSize == -1) { + MutexLocker locker(m_authorizerLock); + enableAuthorizer(false); + + SQLiteStatement statement(*this, "PRAGMA page_size"); + m_pageSize = statement.getColumnInt(0); + + enableAuthorizer(true); + } + + return m_pageSize; +} + +int64_t SQLiteDatabase::freeSpaceSize() +{ + int64_t freelistCount = 0; + + { + MutexLocker locker(m_authorizerLock); + enableAuthorizer(false); + // Note: freelist_count was added in SQLite 3.4.1. + SQLiteStatement statement(*this, "PRAGMA freelist_count"); + freelistCount = statement.getColumnInt64(0); + enableAuthorizer(true); + } + + return freelistCount * pageSize(); +} + +int64_t SQLiteDatabase::totalSize() +{ + int64_t pageCount = 0; + + { + MutexLocker locker(m_authorizerLock); + enableAuthorizer(false); + SQLiteStatement statement(*this, "PRAGMA page_count"); + pageCount = statement.getColumnInt64(0); + enableAuthorizer(true); + } + + return pageCount * pageSize(); +} + +void SQLiteDatabase::setSynchronous(SynchronousPragma sync) +{ + executeCommand(makeString("PRAGMA synchronous = ", String::number(sync))); +} + +void SQLiteDatabase::setBusyTimeout(int ms) +{ + if (m_db) + sqlite3_busy_timeout(m_db, ms); + else + LOG(SQLDatabase, "BusyTimeout set on non-open database"); +} + +void SQLiteDatabase::setBusyHandler(int(*handler)(void*, int)) +{ + if (m_db) + sqlite3_busy_handler(m_db, handler, NULL); + else + LOG(SQLDatabase, "Busy handler set on non-open database"); +} + +bool SQLiteDatabase::executeCommand(const String& sql) +{ + return SQLiteStatement(*this, sql).executeCommand(); +} + +bool SQLiteDatabase::returnsAtLeastOneResult(const String& sql) +{ + return SQLiteStatement(*this, sql).returnsAtLeastOneResult(); +} + +bool SQLiteDatabase::tableExists(const String& tablename) +{ + if (!isOpen()) + return false; + + String statement = "SELECT name FROM sqlite_master WHERE type = 'table' AND name = '" + tablename + "';"; + + SQLiteStatement sql(*this, statement); + sql.prepare(); + return sql.step() == SQLITE_ROW; +} + +void SQLiteDatabase::clearAllTables() +{ + String query = "SELECT name FROM sqlite_master WHERE type='table';"; + Vector<String> tables; + if (!SQLiteStatement(*this, query).returnTextResults(0, tables)) { + LOG(SQLDatabase, "Unable to retrieve list of tables from database"); + return; + } + + for (Vector<String>::iterator table = tables.begin(); table != tables.end(); ++table ) { + if (*table == "sqlite_sequence") + continue; + if (!executeCommand("DROP TABLE " + *table)) + LOG(SQLDatabase, "Unable to drop table %s", (*table).ascii().data()); + } +} + +void SQLiteDatabase::runVacuumCommand() +{ + if (!executeCommand("VACUUM;")) + LOG(SQLDatabase, "Unable to vacuum database - %s", lastErrorMsg()); +} + +void SQLiteDatabase::runIncrementalVacuumCommand() +{ + MutexLocker locker(m_authorizerLock); + enableAuthorizer(false); + + if (!executeCommand("PRAGMA incremental_vacuum")) + LOG(SQLDatabase, "Unable to run incremental vacuum - %s", lastErrorMsg()); + + enableAuthorizer(true); +} + +int64_t SQLiteDatabase::lastInsertRowID() +{ + if (!m_db) + return 0; + return sqlite3_last_insert_rowid(m_db); +} + +int SQLiteDatabase::lastChanges() +{ + if (!m_db) + return 0; + return sqlite3_changes(m_db); +} + +int SQLiteDatabase::lastError() +{ + return m_db ? sqlite3_errcode(m_db) : SQLITE_ERROR; +} + +const char* SQLiteDatabase::lastErrorMsg() +{ + return sqlite3_errmsg(m_db); +} + +#ifndef NDEBUG +void SQLiteDatabase::disableThreadingChecks() +{ + // This doesn't guarantee that SQList was compiled with -DTHREADSAFE, or that you haven't turned off the mutexes. +#if SQLITE_VERSION_NUMBER >= 3003001 + m_sharable = true; +#else + ASSERT(0); // Your SQLite doesn't support sharing handles across threads. +#endif +} +#endif + +int SQLiteDatabase::authorizerFunction(void* userData, int actionCode, const char* parameter1, const char* parameter2, const char* /*databaseName*/, const char* /*trigger_or_view*/) +{ + DatabaseAuthorizer* auth = static_cast<DatabaseAuthorizer*>(userData); + ASSERT(auth); + + switch (actionCode) { + case SQLITE_CREATE_INDEX: + return auth->createIndex(parameter1, parameter2); + case SQLITE_CREATE_TABLE: + return auth->createTable(parameter1); + case SQLITE_CREATE_TEMP_INDEX: + return auth->createTempIndex(parameter1, parameter2); + case SQLITE_CREATE_TEMP_TABLE: + return auth->createTempTable(parameter1); + case SQLITE_CREATE_TEMP_TRIGGER: + return auth->createTempTrigger(parameter1, parameter2); + case SQLITE_CREATE_TEMP_VIEW: + return auth->createTempView(parameter1); + case SQLITE_CREATE_TRIGGER: + return auth->createTrigger(parameter1, parameter2); + case SQLITE_CREATE_VIEW: + return auth->createView(parameter1); + case SQLITE_DELETE: + return auth->allowDelete(parameter1); + case SQLITE_DROP_INDEX: + return auth->dropIndex(parameter1, parameter2); + case SQLITE_DROP_TABLE: + return auth->dropTable(parameter1); + case SQLITE_DROP_TEMP_INDEX: + return auth->dropTempIndex(parameter1, parameter2); + case SQLITE_DROP_TEMP_TABLE: + return auth->dropTempTable(parameter1); + case SQLITE_DROP_TEMP_TRIGGER: + return auth->dropTempTrigger(parameter1, parameter2); + case SQLITE_DROP_TEMP_VIEW: + return auth->dropTempView(parameter1); + case SQLITE_DROP_TRIGGER: + return auth->dropTrigger(parameter1, parameter2); + case SQLITE_DROP_VIEW: + return auth->dropView(parameter1); + case SQLITE_INSERT: + return auth->allowInsert(parameter1); + case SQLITE_PRAGMA: + return auth->allowPragma(parameter1, parameter2); + case SQLITE_READ: + return auth->allowRead(parameter1, parameter2); + case SQLITE_SELECT: + return auth->allowSelect(); + case SQLITE_TRANSACTION: + return auth->allowTransaction(); + case SQLITE_UPDATE: + return auth->allowUpdate(parameter1, parameter2); + case SQLITE_ATTACH: + return auth->allowAttach(parameter1); + case SQLITE_DETACH: + return auth->allowDetach(parameter1); + case SQLITE_ALTER_TABLE: + return auth->allowAlterTable(parameter1, parameter2); + case SQLITE_REINDEX: + return auth->allowReindex(parameter1); +#if SQLITE_VERSION_NUMBER >= 3003013 + case SQLITE_ANALYZE: + return auth->allowAnalyze(parameter1); + case SQLITE_CREATE_VTABLE: + return auth->createVTable(parameter1, parameter2); + case SQLITE_DROP_VTABLE: + return auth->dropVTable(parameter1, parameter2); + case SQLITE_FUNCTION: + return auth->allowFunction(parameter2); +#endif + default: + ASSERT_NOT_REACHED(); + return SQLAuthDeny; + } +} + +void SQLiteDatabase::setAuthorizer(PassRefPtr<DatabaseAuthorizer> auth) +{ + if (!m_db) { + LOG_ERROR("Attempt to set an authorizer on a non-open SQL database"); + ASSERT_NOT_REACHED(); + return; + } + + MutexLocker locker(m_authorizerLock); + + m_authorizer = auth; + + enableAuthorizer(true); +} + +void SQLiteDatabase::enableAuthorizer(bool enable) +{ + if (m_authorizer && enable) + sqlite3_set_authorizer(m_db, SQLiteDatabase::authorizerFunction, m_authorizer.get()); + else + sqlite3_set_authorizer(m_db, NULL, 0); +} + +bool SQLiteDatabase::isAutoCommitOn() const +{ + return sqlite3_get_autocommit(m_db); +} + +bool SQLiteDatabase::turnOnIncrementalAutoVacuum() +{ + SQLiteStatement statement(*this, "PRAGMA auto_vacuum"); + int autoVacuumMode = statement.getColumnInt(0); + int error = lastError(); + + // Check if we got an error while trying to get the value of the auto_vacuum flag. + // If we got a SQLITE_BUSY error, then there's probably another transaction in + // progress on this database. In this case, keep the current value of the + // auto_vacuum flag and try to set it to INCREMENTAL the next time we open this + // database. If the error is not SQLITE_BUSY, then we probably ran into a more + // serious problem and should return false (to log an error message). + if (error != SQLITE_ROW) + return false; + + switch (autoVacuumMode) { + case AutoVacuumIncremental: + return true; + case AutoVacuumFull: + return executeCommand("PRAGMA auto_vacuum = 2"); + case AutoVacuumNone: + default: + if (!executeCommand("PRAGMA auto_vacuum = 2")) + return false; + runVacuumCommand(); + error = lastError(); + return (error == SQLITE_OK); + } +} + +} // namespace WebCore + +#endif // ENABLE(DATABASE) diff --git a/Source/WebCore/platform/sql/SQLiteDatabase.h b/Source/WebCore/platform/sql/SQLiteDatabase.h new file mode 100644 index 0000000..c329273 --- /dev/null +++ b/Source/WebCore/platform/sql/SQLiteDatabase.h @@ -0,0 +1,159 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * Copyright (C) 2007 Justin Haygood (jhaygood@reaktix.com) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLiteDatabase_h +#define SQLiteDatabase_h + +#include "PlatformString.h" +#include <wtf/Threading.h> + +#if COMPILER(MSVC) +#pragma warning(disable: 4800) +#endif + +struct sqlite3; + +namespace WebCore { + +class DatabaseAuthorizer; +class SQLiteStatement; +class SQLiteTransaction; + +extern const int SQLResultDone; +extern const int SQLResultError; +extern const int SQLResultOk; +extern const int SQLResultRow; +extern const int SQLResultSchema; +extern const int SQLResultFull; +extern const int SQLResultInterrupt; + +class SQLiteDatabase : public Noncopyable { + friend class SQLiteTransaction; +public: + SQLiteDatabase(); + ~SQLiteDatabase(); + + bool open(const String& filename, bool forWebSQLDatabase = false); + bool isOpen() const { return m_db; } + void close(); + void interrupt(); + bool isInterrupted(); + + bool executeCommand(const String&); + bool returnsAtLeastOneResult(const String&); + + bool tableExists(const String&); + void clearAllTables(); + void runVacuumCommand(); + void runIncrementalVacuumCommand(); + + bool transactionInProgress() const { return m_transactionInProgress; } + + int64_t lastInsertRowID(); + int lastChanges(); + + void setBusyTimeout(int ms); + void setBusyHandler(int(*)(void*, int)); + + void setFullsync(bool); + + // Gets/sets the maximum size in bytes + // Depending on per-database attributes, the size will only be settable in units that are the page size of the database, which is established at creation + // These chunks will never be anything other than 512, 1024, 2048, 4096, 8192, 16384, or 32768 bytes in size. + // setMaximumSize() will round the size down to the next smallest chunk if the passed size doesn't align. + int64_t maximumSize(); + void setMaximumSize(int64_t); + + // Gets the number of unused bytes in the database file. + int64_t freeSpaceSize(); + int64_t totalSize(); + + // The SQLite SYNCHRONOUS pragma can be either FULL, NORMAL, or OFF + // FULL - Any writing calls to the DB block until the data is actually on the disk surface + // NORMAL - SQLite pauses at some critical moments when writing, but much less than FULL + // OFF - Calls return immediately after the data has been passed to disk + enum SynchronousPragma { SyncOff = 0, SyncNormal = 1, SyncFull = 2 }; + void setSynchronous(SynchronousPragma); + + int lastError(); + const char* lastErrorMsg(); + + sqlite3* sqlite3Handle() const { + ASSERT(m_sharable || currentThread() == m_openingThread); + return m_db; + } + + void setAuthorizer(PassRefPtr<DatabaseAuthorizer>); + + Mutex& databaseMutex() { return m_lockingMutex; } + bool isAutoCommitOn() const; + + // The SQLite AUTO_VACUUM pragma can be either NONE, FULL, or INCREMENTAL. + // NONE - SQLite does not do any vacuuming + // FULL - SQLite moves all empty pages to the end of the DB file and truncates + // the file to remove those pages after every transaction. This option + // requires SQLite to store additional information about each page in + // the database file. + // INCREMENTAL - SQLite stores extra information for each page in the database + // file, but removes the empty pages only when PRAGMA INCREMANTAL_VACUUM + // is called. + enum AutoVacuumPragma { AutoVacuumNone = 0, AutoVacuumFull = 1, AutoVacuumIncremental = 2 }; + bool turnOnIncrementalAutoVacuum(); + + // Set this flag to allow access from multiple threads. Not all multi-threaded accesses are safe! + // See http://www.sqlite.org/cvstrac/wiki?p=MultiThreading for more info. +#ifndef NDEBUG + void disableThreadingChecks(); +#else + void disableThreadingChecks() {} +#endif + +private: + static int authorizerFunction(void*, int, const char*, const char*, const char*, const char*); + + void enableAuthorizer(bool enable); + + int pageSize(); + + sqlite3* m_db; + int m_pageSize; + + bool m_transactionInProgress; + bool m_sharable; + + Mutex m_authorizerLock; + RefPtr<DatabaseAuthorizer> m_authorizer; + + Mutex m_lockingMutex; + ThreadIdentifier m_openingThread; + + Mutex m_databaseClosingMutex; + bool m_interrupted; +}; // class SQLiteDatabase + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/sql/SQLiteFileSystem.cpp b/Source/WebCore/platform/sql/SQLiteFileSystem.cpp new file mode 100644 index 0000000..362005c --- /dev/null +++ b/Source/WebCore/platform/sql/SQLiteFileSystem.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define __STDC_FORMAT_MACROS +#include "config.h" +#include "SQLiteFileSystem.h" + +#if ENABLE(DATABASE) + +#include "FileSystem.h" +#include "SQLiteDatabase.h" +#include "SQLiteStatement.h" +#include <inttypes.h> +#include <sqlite3.h> + +namespace WebCore { + +SQLiteFileSystem::SQLiteFileSystem() +{ +} + +void SQLiteFileSystem::registerSQLiteVFS() +{ +} + +int SQLiteFileSystem::openDatabase(const String& fileName, sqlite3** database, bool) +{ + // SQLite expects a null terminator on its UTF-16 strings. + String path = fileName; + return sqlite3_open16(path.charactersWithNullTermination(), database); +} + +String SQLiteFileSystem::getFileNameForNewDatabase(const String& dbDir, const String&, + const String&, SQLiteDatabase* db) +{ + // try to get the next sequence number from the given database + // if we can't get a number, return an empty string + SQLiteStatement sequenceStatement(*db, "SELECT seq FROM sqlite_sequence WHERE name='Databases';"); + if (sequenceStatement.prepare() != SQLResultOk) + return String(); + int result = sequenceStatement.step(); + int64_t seq = 0; + if (result == SQLResultRow) + seq = sequenceStatement.getColumnInt64(0); + else if (result != SQLResultDone) + return String(); + sequenceStatement.finalize(); + + // increment the number until we can use it to form a file name that doesn't exist + String fileName; + do { + ++seq; + fileName = pathByAppendingComponent(dbDir, String::format("%016"PRIx64".db", seq)); + } while (fileExists(fileName)); + + return String::format("%016"PRIx64".db", seq); +} + +String SQLiteFileSystem::appendDatabaseFileNameToPath(const String& path, const String& fileName) +{ + return pathByAppendingComponent(path, fileName); +} + +bool SQLiteFileSystem::ensureDatabaseDirectoryExists(const String& path) +{ + if (path.isEmpty()) + return false; + return makeAllDirectories(path); +} + +bool SQLiteFileSystem::ensureDatabaseFileExists(const String& fileName, bool checkPathOnly) +{ + if (fileName.isEmpty()) + return false; + + if (checkPathOnly) { + String dir = directoryName(fileName); + return ensureDatabaseDirectoryExists(dir); + } + + return fileExists(fileName); +} + +bool SQLiteFileSystem::deleteEmptyDatabaseDirectory(const String& path) +{ + return deleteEmptyDirectory(path); +} + +bool SQLiteFileSystem::deleteDatabaseFile(const String& fileName) +{ + return deleteFile(fileName); +} + +long long SQLiteFileSystem::getDatabaseFileSize(const String& fileName) +{ + long long size; + return getFileSize(fileName, size) ? size : 0; +} + +} // namespace WebCore +#endif // ENABLE(DATABASE) diff --git a/Source/WebCore/platform/sql/SQLiteFileSystem.h b/Source/WebCore/platform/sql/SQLiteFileSystem.h new file mode 100644 index 0000000..f25d01d --- /dev/null +++ b/Source/WebCore/platform/sql/SQLiteFileSystem.h @@ -0,0 +1,117 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLiteFileSystem_h +#define SQLiteFileSystem_h + +#include "PlatformString.h" +#include <wtf/Threading.h> + +struct sqlite3; + +namespace WebCore { + +class SQLiteDatabase; + +// A class that abstracts the file system related operations required +// by the WebKit database code. +class SQLiteFileSystem { +public: + // Registers a user-defined SQLite VFS. + static void registerSQLiteVFS(); + + // Opens a database file. + // + // fileName - The name of the database file. + // database - The SQLite structure that represents the database stored + // in the given file. + // forWebSQLDatabase - True, if and only if we're opening a Web SQL Database file. + // Used by Chromium to determine if the DB file needs to be opened + // using a custom VFS. + static int openDatabase(const String& fileName, sqlite3** database, bool forWebSQLDatabase); + + // Returns the file name for a database. + // + // dbDir - The directory where all databases are stored. + // dbName - The name of the database. + // originIdentifier - The origin that wants to use this database. + // db - A database with a number generator used to create unique file names. + static String getFileNameForNewDatabase(const String& dbDir, const String& dbName, + const String& originIdentifier, SQLiteDatabase* db); + + // Creates an absolute file path given a directory and a file name. + // + // path - The directory. + // fileName - The file name. + static String appendDatabaseFileNameToPath(const String& path, const String& fileName); + + // Makes sure the given directory exists, by creating all missing directories + // on the given path. + // + // path - The directory. + static bool ensureDatabaseDirectoryExists(const String& path); + + // If 'checkPathOnly' is false, then this method only checks if the given file exists. + // If 'checkPathOnly' is true, then this method makes sure all directories on the + // given path exist by creating the missing ones, and does not check if the file + // itself exists. + // + // Sometimes we expect a DB file to exist; other times, we're OK with creating a new + // DB file, but we want to make sure that the directory in which we want to put the + // new DB file exists. This method covers both cases. + // + // fileName - The file name. + // checkPathOnly - If true, we only make sure that the given directory exists. + // If false, we only check if the file exists. + static bool ensureDatabaseFileExists(const String& fileName, bool checkPathOnly); + + // Deletes an empty database directory. + // + // path - The directory. + static bool deleteEmptyDatabaseDirectory(const String& path); + + // Deletes a database file. + // + // fileName - The file name. + static bool deleteDatabaseFile(const String& fileName); + + // Returns the size of the database file. + // + // fileName - The file name. + static long long getDatabaseFileSize(const String& fileName); + +private: + // do not instantiate this class + SQLiteFileSystem(); +}; // class SQLiteFileSystem + +} // namespace WebCore + +#endif diff --git a/Source/WebCore/platform/sql/SQLiteStatement.cpp b/Source/WebCore/platform/sql/SQLiteStatement.cpp new file mode 100644 index 0000000..af9518a --- /dev/null +++ b/Source/WebCore/platform/sql/SQLiteStatement.cpp @@ -0,0 +1,502 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "SQLiteStatement.h" + +#if ENABLE(DATABASE) + +#include "Logging.h" +#include "SQLValue.h" +#include <sqlite3.h> +#include <wtf/Assertions.h> +#include <wtf/text/CString.h> + +namespace WebCore { + +#if SQLITE_VERSION_NUMBER < 3003009 + +// FIXME: This overload helps us compile with older versions of SQLite 3, but things like quotas will not work. +static inline int sqlite3_prepare16_v2(sqlite3* db, const void* zSql, int nBytes, sqlite3_stmt** ppStmt, const void** pzTail) +{ + return sqlite3_prepare16(db, zSql, nBytes, ppStmt, pzTail); +} + +#endif + +SQLiteStatement::SQLiteStatement(SQLiteDatabase& db, const String& sql) + : m_database(db) + , m_query(sql) + , m_statement(0) +#ifndef NDEBUG + , m_isPrepared(false) +#endif +{ +} + +SQLiteStatement::~SQLiteStatement() +{ + finalize(); +} + +int SQLiteStatement::prepare() +{ + ASSERT(!m_isPrepared); + + MutexLocker databaseLock(m_database.databaseMutex()); + if (m_database.isInterrupted()) + return SQLITE_INTERRUPT; + + const void* tail = 0; + LOG(SQLDatabase, "SQL - prepare - %s", m_query.ascii().data()); + String strippedQuery = m_query.stripWhiteSpace(); + int error = sqlite3_prepare16_v2(m_database.sqlite3Handle(), strippedQuery.charactersWithNullTermination(), -1, &m_statement, &tail); + + // Starting with version 3.6.16, sqlite has a patch (http://www.sqlite.org/src/ci/256ec3c6af) + // that should make sure sqlite3_prepare16_v2 doesn't return a SQLITE_SCHEMA error. + // If we're using an older sqlite version, try to emulate the patch. + if (error == SQLITE_SCHEMA) { + sqlite3_finalize(m_statement); + error = sqlite3_prepare16_v2(m_database.sqlite3Handle(), m_query.charactersWithNullTermination(), -1, &m_statement, &tail); + } + + if (error != SQLITE_OK) + LOG(SQLDatabase, "sqlite3_prepare16 failed (%i)\n%s\n%s", error, m_query.ascii().data(), sqlite3_errmsg(m_database.sqlite3Handle())); + const UChar* ch = static_cast<const UChar*>(tail); + if (ch && *ch) + error = SQLITE_ERROR; +#ifndef NDEBUG + m_isPrepared = error == SQLITE_OK; +#endif + return error; +} + +int SQLiteStatement::step() +{ + ASSERT(m_isPrepared); + + MutexLocker databaseLock(m_database.databaseMutex()); + if (m_database.isInterrupted()) + return SQLITE_INTERRUPT; + + if (!m_statement) + return SQLITE_OK; + LOG(SQLDatabase, "SQL - step - %s", m_query.ascii().data()); + int error = sqlite3_step(m_statement); + if (error != SQLITE_DONE && error != SQLITE_ROW) { + LOG(SQLDatabase, "sqlite3_step failed (%i)\nQuery - %s\nError - %s", + error, m_query.ascii().data(), sqlite3_errmsg(m_database.sqlite3Handle())); + } + + return error; +} + +int SQLiteStatement::finalize() +{ +#ifndef NDEBUG + m_isPrepared = false; +#endif + if (!m_statement) + return SQLITE_OK; + LOG(SQLDatabase, "SQL - finalize - %s", m_query.ascii().data()); + int result = sqlite3_finalize(m_statement); + m_statement = 0; + return result; +} + +int SQLiteStatement::reset() +{ + ASSERT(m_isPrepared); + if (!m_statement) + return SQLITE_OK; + LOG(SQLDatabase, "SQL - reset - %s", m_query.ascii().data()); + return sqlite3_reset(m_statement); +} + +bool SQLiteStatement::executeCommand() +{ + if (!m_statement && prepare() != SQLITE_OK) + return false; + ASSERT(m_isPrepared); + if (step() != SQLITE_DONE) { + finalize(); + return false; + } + finalize(); + return true; +} + +bool SQLiteStatement::returnsAtLeastOneResult() +{ + if (!m_statement && prepare() != SQLITE_OK) + return false; + ASSERT(m_isPrepared); + if (step() != SQLITE_ROW) { + finalize(); + return false; + } + finalize(); + return true; + +} + +int SQLiteStatement::bindBlob(int index, const void* blob, int size) +{ + ASSERT(m_isPrepared); + ASSERT(index > 0); + ASSERT(static_cast<unsigned>(index) <= bindParameterCount()); + ASSERT(blob); + ASSERT(size >= 0); + + if (!m_statement) + return SQLITE_ERROR; + + return sqlite3_bind_blob(m_statement, index, blob, size, SQLITE_TRANSIENT); +} + +int SQLiteStatement::bindText(int index, const String& text) +{ + ASSERT(m_isPrepared); + ASSERT(index > 0); + ASSERT(static_cast<unsigned>(index) <= bindParameterCount()); + + // String::characters() returns 0 for the empty string, which SQLite + // treats as a null, so we supply a non-null pointer for that case. + UChar anyCharacter = 0; + const UChar* characters; + if (text.isEmpty() && !text.isNull()) + characters = &anyCharacter; + else + characters = text.characters(); + + return sqlite3_bind_text16(m_statement, index, characters, sizeof(UChar) * text.length(), SQLITE_TRANSIENT); +} + +int SQLiteStatement::bindInt(int index, int integer) +{ + ASSERT(m_isPrepared); + ASSERT(index > 0); + ASSERT(static_cast<unsigned>(index) <= bindParameterCount()); + + return sqlite3_bind_int(m_statement, index, integer); +} + +int SQLiteStatement::bindInt64(int index, int64_t integer) +{ + ASSERT(m_isPrepared); + ASSERT(index > 0); + ASSERT(static_cast<unsigned>(index) <= bindParameterCount()); + + return sqlite3_bind_int64(m_statement, index, integer); +} + +int SQLiteStatement::bindDouble(int index, double number) +{ + ASSERT(m_isPrepared); + ASSERT(index > 0); + ASSERT(static_cast<unsigned>(index) <= bindParameterCount()); + + return sqlite3_bind_double(m_statement, index, number); +} + +int SQLiteStatement::bindNull(int index) +{ + ASSERT(m_isPrepared); + ASSERT(index > 0); + ASSERT(static_cast<unsigned>(index) <= bindParameterCount()); + + return sqlite3_bind_null(m_statement, index); +} + +int SQLiteStatement::bindValue(int index, const SQLValue& value) +{ + switch (value.type()) { + case SQLValue::StringValue: + return bindText(index, value.string()); + case SQLValue::NumberValue: + return bindDouble(index, value.number()); + case SQLValue::NullValue: + return bindNull(index); + } + + ASSERT_NOT_REACHED(); + return SQLITE_ERROR; +} + +unsigned SQLiteStatement::bindParameterCount() const +{ + ASSERT(m_isPrepared); + if (!m_statement) + return 0; + return sqlite3_bind_parameter_count(m_statement); +} + +int SQLiteStatement::columnCount() +{ + ASSERT(m_isPrepared); + if (!m_statement) + return 0; + return sqlite3_data_count(m_statement); +} + +bool SQLiteStatement::isColumnNull(int col) +{ + ASSERT(col >= 0); + if (!m_statement) + if (prepareAndStep() != SQLITE_ROW) + return false; + if (columnCount() <= col) + return false; + + return sqlite3_column_type(m_statement, col) == SQLITE_NULL; +} + +String SQLiteStatement::getColumnName(int col) +{ + ASSERT(col >= 0); + if (!m_statement) + if (prepareAndStep() != SQLITE_ROW) + return String(); + if (columnCount() <= col) + return String(); + return String(reinterpret_cast<const UChar*>(sqlite3_column_name16(m_statement, col))); +} + +SQLValue SQLiteStatement::getColumnValue(int col) +{ + ASSERT(col >= 0); + if (!m_statement) + if (prepareAndStep() != SQLITE_ROW) + return SQLValue(); + if (columnCount() <= col) + return SQLValue(); + + // SQLite is typed per value. optional column types are + // "(mostly) ignored" + sqlite3_value* value = sqlite3_column_value(m_statement, col); + switch (sqlite3_value_type(value)) { + case SQLITE_INTEGER: // SQLValue and JS don't represent integers, so use FLOAT -case + case SQLITE_FLOAT: + return SQLValue(sqlite3_value_double(value)); + case SQLITE_BLOB: // SQLValue and JS don't represent blobs, so use TEXT -case + case SQLITE_TEXT: + return SQLValue(String(reinterpret_cast<const UChar*>(sqlite3_value_text16(value)))); + case SQLITE_NULL: + return SQLValue(); + default: + break; + } + ASSERT_NOT_REACHED(); + return SQLValue(); +} + +String SQLiteStatement::getColumnText(int col) +{ + ASSERT(col >= 0); + if (!m_statement) + if (prepareAndStep() != SQLITE_ROW) + return String(); + if (columnCount() <= col) + return String(); + return String(reinterpret_cast<const UChar*>(sqlite3_column_text16(m_statement, col))); +} + +double SQLiteStatement::getColumnDouble(int col) +{ + ASSERT(col >= 0); + if (!m_statement) + if (prepareAndStep() != SQLITE_ROW) + return 0.0; + if (columnCount() <= col) + return 0.0; + return sqlite3_column_double(m_statement, col); +} + +int SQLiteStatement::getColumnInt(int col) +{ + ASSERT(col >= 0); + if (!m_statement) + if (prepareAndStep() != SQLITE_ROW) + return 0; + if (columnCount() <= col) + return 0; + return sqlite3_column_int(m_statement, col); +} + +int64_t SQLiteStatement::getColumnInt64(int col) +{ + ASSERT(col >= 0); + if (!m_statement) + if (prepareAndStep() != SQLITE_ROW) + return 0; + if (columnCount() <= col) + return 0; + return sqlite3_column_int64(m_statement, col); +} + +void SQLiteStatement::getColumnBlobAsVector(int col, Vector<char>& result) +{ + ASSERT(col >= 0); + + if (!m_statement && prepareAndStep() != SQLITE_ROW) { + result.clear(); + return; + } + + if (columnCount() <= col) { + result.clear(); + return; + } + + const void* blob = sqlite3_column_blob(m_statement, col); + if (!blob) { + result.clear(); + return; + } + + int size = sqlite3_column_bytes(m_statement, col); + result.resize((size_t)size); + for (int i = 0; i < size; ++i) + result[i] = ((const unsigned char*)blob)[i]; +} + +const void* SQLiteStatement::getColumnBlob(int col, int& size) +{ + ASSERT(col >= 0); + + size = 0; + + if (finalize() != SQLITE_OK) + LOG(SQLDatabase, "Finalize failed"); + if (prepare() != SQLITE_OK) { + LOG(SQLDatabase, "Prepare failed"); + return 0; + } + if (step() != SQLITE_ROW) { + LOG(SQLDatabase, "Step wasn't a row"); + return 0; + } + + if (columnCount() <= col) + return 0; + + const void* blob = sqlite3_column_blob(m_statement, col); + if (!blob) + return 0; + + size = sqlite3_column_bytes(m_statement, col); + return blob; +} + +bool SQLiteStatement::returnTextResults(int col, Vector<String>& v) +{ + ASSERT(col >= 0); + + v.clear(); + + if (m_statement) + finalize(); + if (prepare() != SQLITE_OK) + return false; + + while (step() == SQLITE_ROW) + v.append(getColumnText(col)); + bool result = true; + if (m_database.lastError() != SQLITE_DONE) { + result = false; + LOG(SQLDatabase, "Error reading results from database query %s", m_query.ascii().data()); + } + finalize(); + return result; +} + +bool SQLiteStatement::returnIntResults(int col, Vector<int>& v) +{ + v.clear(); + + if (m_statement) + finalize(); + if (prepare() != SQLITE_OK) + return false; + + while (step() == SQLITE_ROW) + v.append(getColumnInt(col)); + bool result = true; + if (m_database.lastError() != SQLITE_DONE) { + result = false; + LOG(SQLDatabase, "Error reading results from database query %s", m_query.ascii().data()); + } + finalize(); + return result; +} + +bool SQLiteStatement::returnInt64Results(int col, Vector<int64_t>& v) +{ + v.clear(); + + if (m_statement) + finalize(); + if (prepare() != SQLITE_OK) + return false; + + while (step() == SQLITE_ROW) + v.append(getColumnInt64(col)); + bool result = true; + if (m_database.lastError() != SQLITE_DONE) { + result = false; + LOG(SQLDatabase, "Error reading results from database query %s", m_query.ascii().data()); + } + finalize(); + return result; +} + +bool SQLiteStatement::returnDoubleResults(int col, Vector<double>& v) +{ + v.clear(); + + if (m_statement) + finalize(); + if (prepare() != SQLITE_OK) + return false; + + while (step() == SQLITE_ROW) + v.append(getColumnDouble(col)); + bool result = true; + if (m_database.lastError() != SQLITE_DONE) { + result = false; + LOG(SQLDatabase, "Error reading results from database query %s", m_query.ascii().data()); + } + finalize(); + return result; +} + +bool SQLiteStatement::isExpired() +{ + return !m_statement || sqlite3_expired(m_statement); +} + +} // namespace WebCore + +#endif // ENABLE(DATABASE) diff --git a/Source/WebCore/platform/sql/SQLiteStatement.h b/Source/WebCore/platform/sql/SQLiteStatement.h new file mode 100644 index 0000000..1444f0e --- /dev/null +++ b/Source/WebCore/platform/sql/SQLiteStatement.h @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2006, 2007, 2008 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLiteStatement_h +#define SQLiteStatement_h + +#include "SQLiteDatabase.h" + +struct sqlite3_stmt; + +namespace WebCore { + +class SQLValue; + +class SQLiteStatement : public Noncopyable { +public: + SQLiteStatement(SQLiteDatabase&, const String&); + ~SQLiteStatement(); + + int prepare(); + int bindBlob(int index, const void* blob, int size); + int bindText(int index, const String&); + int bindInt(int index, int); + int bindInt64(int index, int64_t); + int bindDouble(int index, double); + int bindNull(int index); + int bindValue(int index, const SQLValue&); + unsigned bindParameterCount() const; + + int step(); + int finalize(); + int reset(); + + int prepareAndStep() { if (int error = prepare()) return error; return step(); } + + // prepares, steps, and finalizes the query. + // returns true if all 3 steps succeed with step() returning SQLITE_DONE + // returns false otherwise + bool executeCommand(); + + // prepares, steps, and finalizes. + // returns true is step() returns SQLITE_ROW + // returns false otherwise + bool returnsAtLeastOneResult(); + + bool isExpired(); + + // Returns -1 on last-step failing. Otherwise, returns number of rows + // returned in the last step() + int columnCount(); + + bool isColumnNull(int col); + String getColumnName(int col); + SQLValue getColumnValue(int col); + String getColumnText(int col); + double getColumnDouble(int col); + int getColumnInt(int col); + int64_t getColumnInt64(int col); + const void* getColumnBlob(int col, int& size); + void getColumnBlobAsVector(int col, Vector<char>&); + + bool returnTextResults(int col, Vector<String>&); + bool returnIntResults(int col, Vector<int>&); + bool returnInt64Results(int col, Vector<int64_t>&); + bool returnDoubleResults(int col, Vector<double>&); + + SQLiteDatabase* database() { return &m_database; } + + const String& query() const { return m_query; } + +private: + SQLiteDatabase& m_database; + String m_query; + sqlite3_stmt* m_statement; +#ifndef NDEBUG + bool m_isPrepared; +#endif +}; + +} // namespace WebCore + +#endif // SQLiteStatement_h diff --git a/Source/WebCore/platform/sql/SQLiteTransaction.cpp b/Source/WebCore/platform/sql/SQLiteTransaction.cpp new file mode 100644 index 0000000..63b3e7a --- /dev/null +++ b/Source/WebCore/platform/sql/SQLiteTransaction.cpp @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "SQLiteTransaction.h" + +#if ENABLE(DATABASE) + +#include "SQLiteDatabase.h" + +namespace WebCore { + +SQLiteTransaction::SQLiteTransaction(SQLiteDatabase& db, bool readOnly) + : m_db(db) + , m_inProgress(false) + , m_readOnly(readOnly) +{ +} + +SQLiteTransaction::~SQLiteTransaction() +{ + if (m_inProgress) + rollback(); +} + +void SQLiteTransaction::begin() +{ + if (!m_inProgress) { + ASSERT(!m_db.m_transactionInProgress); + // Call BEGIN IMMEDIATE for a write transaction to acquire + // a RESERVED lock on the DB file. Otherwise, another write + // transaction (on another connection) could make changes + // to the same DB file before this transaction gets to execute + // any statements. If that happens, this transaction will fail. + // http://www.sqlite.org/lang_transaction.html + // http://www.sqlite.org/lockingv3.html#locking + if (m_readOnly) + m_inProgress = m_db.executeCommand("BEGIN"); + else + m_inProgress = m_db.executeCommand("BEGIN IMMEDIATE"); + m_db.m_transactionInProgress = m_inProgress; + } +} + +void SQLiteTransaction::commit() +{ + if (m_inProgress) { + ASSERT(m_db.m_transactionInProgress); + m_inProgress = !m_db.executeCommand("COMMIT"); + m_db.m_transactionInProgress = m_inProgress; + } +} + +void SQLiteTransaction::rollback() +{ + // We do not use the 'm_inProgress = m_db.executeCommand("ROLLBACK")' construct here, + // because m_inProgress should always be set to false after a ROLLBACK, and + // m_db.executeCommand("ROLLBACK") can sometimes harmlessly fail, thus returning + // a non-zero/true result (http://www.sqlite.org/lang_transaction.html). + if (m_inProgress) { + ASSERT(m_db.m_transactionInProgress); + m_db.executeCommand("ROLLBACK"); + m_inProgress = false; + m_db.m_transactionInProgress = false; + } +} + +void SQLiteTransaction::stop() +{ + if (m_inProgress) { + m_inProgress = false; + m_db.m_transactionInProgress = false; + } +} + +bool SQLiteTransaction::wasRolledBackBySqlite() const +{ + // According to http://www.sqlite.org/c3ref/get_autocommit.html, + // the auto-commit flag should be off in the middle of a transaction + return m_inProgress && m_db.isAutoCommitOn(); +} + +} // namespace WebCore +#endif // ENABLE(DATABASE) diff --git a/Source/WebCore/platform/sql/SQLiteTransaction.h b/Source/WebCore/platform/sql/SQLiteTransaction.h new file mode 100644 index 0000000..924241f --- /dev/null +++ b/Source/WebCore/platform/sql/SQLiteTransaction.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2006 Apple Computer, Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY + * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef SQLiteTransaction_h +#define SQLiteTransaction_h + +#include <wtf/Noncopyable.h> + +namespace WebCore { + +class SQLiteDatabase; + +class SQLiteTransaction : public Noncopyable +{ +public: + SQLiteTransaction(SQLiteDatabase& db, bool readOnly = false); + ~SQLiteTransaction(); + + void begin(); + void commit(); + void rollback(); + void stop(); + + bool inProgress() const { return m_inProgress; } + bool wasRolledBackBySqlite() const; +private: + SQLiteDatabase& m_db; + bool m_inProgress; + bool m_readOnly; +}; + +} // namespace WebCore + +#endif // SQLiteTransation_H diff --git a/Source/WebCore/platform/sql/chromium/SQLiteFileSystemChromium.cpp b/Source/WebCore/platform/sql/chromium/SQLiteFileSystemChromium.cpp new file mode 100644 index 0000000..0a09888 --- /dev/null +++ b/Source/WebCore/platform/sql/chromium/SQLiteFileSystemChromium.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "SQLiteFileSystem.h" + +#include "ChromiumBridge.h" +#include "SQLiteDatabase.h" +#include <sqlite3.h> +#include <wtf/text/CString.h> + +// SQLiteFileSystem::registerSQLiteVFS() is implemented in the +// platform-specific files SQLiteFileSystemChromium{Win|Posix}.cpp +namespace WebCore { + +SQLiteFileSystem::SQLiteFileSystem() +{ +} + +int SQLiteFileSystem::openDatabase(const String& fileName, sqlite3** database, bool forWebSQLDatabase) +{ + if (!forWebSQLDatabase) { + String path = fileName; + return sqlite3_open16(path.charactersWithNullTermination(), database); + } + + return sqlite3_open_v2(fileName.utf8().data(), database, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, "chromium_vfs"); +} + +String SQLiteFileSystem::getFileNameForNewDatabase( + const String&, const String& dbName, const String &originIdentifier, SQLiteDatabase*) +{ + // Not used by Chromium's DatabaseTracker implementation + ASSERT_NOT_REACHED(); + return String(); +} + +String SQLiteFileSystem::appendDatabaseFileNameToPath(const String&, const String& fileName) +{ + // Not used by Chromium's DatabaseTracker implementation + ASSERT_NOT_REACHED(); + return String(); +} + +bool SQLiteFileSystem::ensureDatabaseDirectoryExists(const String&) +{ + // Not used by Chromium's DatabaseTracker implementation + ASSERT_NOT_REACHED(); + return false; +} + +bool SQLiteFileSystem::ensureDatabaseFileExists(const String&, bool) +{ + // Not used by Chromium's DatabaseTracker implementation + ASSERT_NOT_REACHED(); + return false; +} + +bool SQLiteFileSystem::deleteEmptyDatabaseDirectory(const String&) +{ + // Not used by Chromium's DatabaseTracker implementation + ASSERT_NOT_REACHED(); + return false; +} + +bool SQLiteFileSystem::deleteDatabaseFile(const String& fileName) +{ + return (ChromiumBridge::databaseDeleteFile(fileName) == SQLITE_OK); +} + +long long SQLiteFileSystem::getDatabaseFileSize(const String& fileName) +{ + return ChromiumBridge::databaseGetFileSize(fileName); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/sql/chromium/SQLiteFileSystemChromiumPosix.cpp b/Source/WebCore/platform/sql/chromium/SQLiteFileSystemChromiumPosix.cpp new file mode 100644 index 0000000..1102df5 --- /dev/null +++ b/Source/WebCore/platform/sql/chromium/SQLiteFileSystemChromiumPosix.cpp @@ -0,0 +1,1188 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "SQLiteFileSystem.h" + +#include "ChromiumBridge.h" +#include <sqlite3.h> + +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> +#include <unistd.h> + +using namespace WebCore; + +// Chromium's Posix implementation of SQLite VFS. +// This is heavily based on SQLite's os_unix.c, +// without parts we don't need. + +// Identifies a file by its device number and inode. +struct ChromiumFileId { + dev_t dev; // Device number. + ino_t ino; // Inode number. +}; + +// Information about file locks (one per open inode). Note that many open +// file descriptors may refer to the same inode. +struct ChromiumLockInfo { + ChromiumFileId lockKey; // File identifier. + int cnt; // Number of shared locks held. + int locktype; // Type of the lock. + int nRef; // Reference count. + + // Double-linked list pointers. + ChromiumLockInfo* pNext; + ChromiumLockInfo* pPrev; +}; + +// Information about a file descriptor that cannot be closed immediately. +struct ChromiumUnusedFd { + int fd; // File descriptor. + int flags; // Flags this file descriptor was opened with. + ChromiumUnusedFd* pNext; // Next unused file descriptor on the same file. +}; + +// Information about an open inode. When we want to close an inode +// that still has locks, we defer the close until all locks are cleared. +struct ChromiumOpenInfo { + ChromiumFileId fileId; // The lookup key. + int nRef; // Reference count. + int nLock; // Number of outstanding locks. + ChromiumUnusedFd* pUnused; // List of file descriptors to close. + + // Double-linked list pointers. + ChromiumOpenInfo* pNext; + ChromiumOpenInfo* pPrev; +}; + +// Keep track of locks and inodes in double-linked lists. +static struct ChromiumLockInfo* lockList = 0; +static struct ChromiumOpenInfo* openList = 0; + +// Extension of sqlite3_file specific to the chromium VFS. +struct ChromiumFile { + sqlite3_io_methods const* pMethod; // Implementation of sqlite3_file. + ChromiumOpenInfo* pOpen; // Information about all open file descriptors for this file. + ChromiumLockInfo* pLock; // Information about all locks for this file. + int h; // File descriptor. + int dirfd; // File descriptor for the file directory. + unsigned char locktype; // Type of the lock used for this file. + int lastErrno; // Value of errno for last operation on this file. + ChromiumUnusedFd* pUnused; // Information about unused file descriptors for this file. +}; + +// The following constants specify the range of bytes used for locking. +// SQLiteSharedSize is the number of bytes available in the pool from which +// a random byte is selected for a shared lock. The pool of bytes for +// shared locks begins at SQLiteSharedFirstByte. +// The values are the same as used by SQLite for compatibility. +static const off_t SQLitePendingByte = 0x40000000; +static const off_t SQLiteReservedByte = SQLitePendingByte + 1; +static const off_t SQLiteSharedFirstByte = SQLitePendingByte + 2; +static const off_t SQLiteSharedSize = 510; + +// Maps a POSIX error code to an SQLite error code. +static int sqliteErrorFromPosixError(int posixError, int sqliteIOErr) +{ + switch (posixError) { + case 0: + return SQLITE_OK; + case EAGAIN: + case ETIMEDOUT: + case EBUSY: + case EINTR: + case ENOLCK: + return SQLITE_BUSY; + case EACCES: + // EACCES is like EAGAIN during locking operations. + if ((sqliteIOErr == SQLITE_IOERR_LOCK) || + (sqliteIOErr == SQLITE_IOERR_UNLOCK) || + (sqliteIOErr == SQLITE_IOERR_RDLOCK) || + (sqliteIOErr == SQLITE_IOERR_CHECKRESERVEDLOCK)) + return SQLITE_BUSY; + return SQLITE_PERM; + case EPERM: + return SQLITE_PERM; + case EDEADLK: + return SQLITE_IOERR_BLOCKED; + default: + return sqliteIOErr; + } +} + +// Releases a ChromiumLockInfo structure previously allocated by findLockInfo(). +static void releaseLockInfo(ChromiumLockInfo* pLock) +{ + if (!pLock) + return; + + pLock->nRef--; + if (pLock->nRef > 0) + return; + + if (pLock->pPrev) { + ASSERT(pLock->pPrev->pNext == pLock); + pLock->pPrev->pNext = pLock->pNext; + } else { + ASSERT(lockList == pLock); + lockList = pLock->pNext; + } + if (pLock->pNext) { + ASSERT(pLock->pNext->pPrev == pLock); + pLock->pNext->pPrev = pLock->pPrev; + } + + sqlite3_free(pLock); +} + +// Releases a ChromiumOpenInfo structure previously allocated by findLockInfo(). +static void releaseOpenInfo(ChromiumOpenInfo* pOpen) +{ + if (!pOpen) + return; + + pOpen->nRef--; + if (pOpen->nRef > 0) + return; + + if (pOpen->pPrev) { + ASSERT(pOpen->pPrev->pNext == pOpen); + pOpen->pPrev->pNext = pOpen->pNext; + } else { + ASSERT(openList == pOpen); + openList = pOpen->pNext; + } + if (pOpen->pNext) { + ASSERT(pOpen->pNext->pPrev == pOpen); + pOpen->pNext->pPrev = pOpen->pPrev; + } + + ASSERT(!pOpen->pUnused); // Make sure we're not leaking memory and file descriptors. + + sqlite3_free(pOpen); +} + +// Locates ChromiumLockInfo and ChromiumOpenInfo for given file descriptor (creating new ones if needed). +// Returns a SQLite error code. +static int findLockInfo(ChromiumFile* pFile, ChromiumLockInfo** ppLock, ChromiumOpenInfo** ppOpen) +{ + int fd = pFile->h; + struct stat statbuf; + int rc = fstat(fd, &statbuf); + if (rc) { + pFile->lastErrno = errno; +#ifdef EOVERFLOW + if (pFile->lastErrno == EOVERFLOW) + return SQLITE_NOLFS; +#endif + return SQLITE_IOERR; + } + +#if OS(DARWIN) + // On OS X on an msdos/fat filesystems, the inode number is reported + // incorrectly for zero-size files. See http://www.sqlite.org/cvstrac/tktview?tn=3260. + // To work around this problem we always increase the file size to 1 by writing a single byte + // prior to accessing the inode number. The one byte written is an ASCII 'S' character which + // also happens to be the first byte in the header of every SQLite database. In this way, + // if there is a race condition such that another thread has already populated the first page + // of the database, no damage is done. + if (!statbuf.st_size) { + rc = write(fd, "S", 1); + if (rc != 1) + return SQLITE_IOERR; + rc = fstat(fd, &statbuf); + if (rc) { + pFile->lastErrno = errno; + return SQLITE_IOERR; + } + } +#endif + + ChromiumFileId fileId; + memset(&fileId, 0, sizeof(fileId)); + fileId.dev = statbuf.st_dev; + fileId.ino = statbuf.st_ino; + + ChromiumLockInfo* pLock = 0; + + if (ppLock) { + pLock = lockList; + while (pLock && memcmp(&fileId, &pLock->lockKey, sizeof(fileId))) + pLock = pLock->pNext; + if (pLock) + pLock->nRef++; + else { + pLock = static_cast<ChromiumLockInfo*>(sqlite3_malloc(sizeof(*pLock))); + if (!pLock) + return SQLITE_NOMEM; + pLock->lockKey = fileId; + pLock->nRef = 1; + pLock->cnt = 0; + pLock->locktype = 0; + pLock->pNext = lockList; + pLock->pPrev = 0; + if (lockList) + lockList->pPrev = pLock; + lockList = pLock; + } + *ppLock = pLock; + } + + if (ppOpen) { + ChromiumOpenInfo* pOpen = openList; + while (pOpen && memcmp(&fileId, &pOpen->fileId, sizeof(fileId))) + pOpen = pOpen->pNext; + if (pOpen) + pOpen->nRef++; + else { + pOpen = static_cast<ChromiumOpenInfo*>(sqlite3_malloc(sizeof(*pOpen))); + if (!pOpen) { + releaseLockInfo(pLock); + return SQLITE_NOMEM; + } + memset(pOpen, 0, sizeof(*pOpen)); + pOpen->fileId = fileId; + pOpen->nRef = 1; + pOpen->pNext = openList; + if (openList) + openList->pPrev = pOpen; + openList = pOpen; + } + *ppOpen = pOpen; + } + + return rc; +} + +// Checks if there is a RESERVED lock held on the specified file by this or any other process. +// If the lock is held, sets pResOut to a non-zero value. Returns a SQLite error code. +static int chromiumCheckReservedLock(sqlite3_file* id, int* pResOut) +{ + ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); + ASSERT(pFile); + + // Look for locks held by this process. + int reserved = 0; + if (pFile->pLock->locktype > SQLITE_LOCK_SHARED) + reserved = 1; + + // Look for locks held by other processes. + int rc = SQLITE_OK; + if (!reserved) { + struct flock lock; + lock.l_whence = SEEK_SET; + lock.l_start = SQLiteReservedByte; + lock.l_len = 1; + lock.l_type = F_WRLCK; + if (-1 == fcntl(pFile->h, F_GETLK, &lock)) { + int tErrno = errno; + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_CHECKRESERVEDLOCK); + pFile->lastErrno = tErrno; + } else if (lock.l_type != F_UNLCK) + reserved = 1; + } + + *pResOut = reserved; + return rc; +} + +// Performs a file locking operation on a range of bytes in a file. +// The |op| parameter should be one of F_RFLCK, F_WRLCK or F_UNLCK. +// Returns a Unix error code, and also writes it to pErrcode. +static int rangeLock(ChromiumFile* pFile, int op, int* pErrcode) +{ + struct flock lock; + lock.l_type = op; + lock.l_start = SQLiteSharedFirstByte; + lock.l_whence = SEEK_SET; + lock.l_len = SQLiteSharedSize; + int rc = fcntl(pFile->h, F_SETLK, &lock); + *pErrcode = errno; + return rc; +} + +// Locks the file with the lock specified by parameter locktype - one +// of the following: +// +// (1) SQLITE_LOCK_SHARED +// (2) SQLITE_LOCK_RESERVED +// (3) SQLITE_LOCK_PENDING +// (4) SQLITE_LOCK_EXCLUSIVE +// +// Sometimes when requesting one lock state, additional lock states +// are inserted in between. The locking might fail on one of the later +// transitions leaving the lock state different from what it started but +// still short of its goal. The following chart shows the allowed +// transitions and the inserted intermediate states: +// +// UNLOCKED -> SHARED +// SHARED -> RESERVED +// SHARED -> (PENDING) -> EXCLUSIVE +// RESERVED -> (PENDING) -> EXCLUSIVE +// PENDING -> EXCLUSIVE +static int chromiumLock(sqlite3_file* id, int locktype) +{ + // To obtain a SHARED lock, a read-lock is obtained on the 'pending + // byte'. If this is successful, a random byte from the 'shared byte + // range' is read-locked and the lock on the 'pending byte' released. + // + // A process may only obtain a RESERVED lock after it has a SHARED lock. + // A RESERVED lock is implemented by grabbing a write-lock on the + // 'reserved byte'. + // + // A process may only obtain a PENDING lock after it has obtained a + // SHARED lock. A PENDING lock is implemented by obtaining a write-lock + // on the 'pending byte'. This ensures that no new SHARED locks can be + // obtained, but existing SHARED locks are allowed to persist. A process + // does not have to obtain a RESERVED lock on the way to a PENDING lock. + // This property is used by the algorithm for rolling back a journal file + // after a crash. + // + // An EXCLUSIVE lock, obtained after a PENDING lock is held, is + // implemented by obtaining a write-lock on the entire 'shared byte + // range'. Since all other locks require a read-lock on one of the bytes + // within this range, this ensures that no other locks are held on the + // database. + + int rc = SQLITE_OK; + struct flock lock; + int s = 0; + int tErrno; + + ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); + ASSERT(pFile); + + ChromiumLockInfo* pLock = pFile->pLock; + + // If there is already a lock of this type or more restrictive, do nothing. + if (pFile->locktype >= locktype) + return SQLITE_OK; + + // Make sure we never move from unlocked to anything higher than shared lock. + ASSERT(pFile->locktype != SQLITE_LOCK_NONE || locktype == SQLITE_LOCK_SHARED); + + // Make sure we never request a pending lock. + ASSERT(locktype != SQLITE_LOCK_PENDING); + + // Make sure a shared lock is always held when a RESERVED lock is requested. + ASSERT(locktype != SQLITE_LOCK_RESERVED || pFile->locktype == SQLITE_LOCK_SHARED); + + // If some thread using this PID has a lock via a different ChromiumFile + // handle that precludes the requested lock, return BUSY. + if (pFile->locktype != pLock->locktype && + (pLock->locktype >= SQLITE_LOCK_PENDING || locktype > SQLITE_LOCK_SHARED)) + return SQLITE_BUSY; + + // If a SHARED lock is requested, and some thread using this PID already + // has a SHARED or RESERVED lock, then just increment reference counts. + if (locktype == SQLITE_LOCK_SHARED && + (pLock->locktype == SQLITE_LOCK_SHARED || pLock->locktype == SQLITE_LOCK_RESERVED)) { + ASSERT(!pFile->locktype); + ASSERT(pLock->cnt > 0); + pFile->locktype = SQLITE_LOCK_SHARED; + pLock->cnt++; + pFile->pOpen->nLock++; + return SQLITE_OK; + } + + // A PENDING lock is needed before acquiring a SHARED lock and before + // acquiring an EXCLUSIVE lock. For the SHARED lock, the PENDING will + // be released. + lock.l_len = 1; + lock.l_whence = SEEK_SET; + if (locktype == SQLITE_LOCK_SHARED || + (locktype == SQLITE_LOCK_EXCLUSIVE && pFile->locktype < SQLITE_LOCK_PENDING)) { + lock.l_type = (locktype == SQLITE_LOCK_SHARED ? F_RDLCK : F_WRLCK); + lock.l_start = SQLitePendingByte; + s = fcntl(pFile->h, F_SETLK, &lock); + if (s == -1) { + tErrno = errno; + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); + if ((rc != SQLITE_OK) && (rc != SQLITE_BUSY)) + pFile->lastErrno = tErrno; + return rc; + } + } + + if (locktype == SQLITE_LOCK_SHARED) { + ASSERT(!pLock->cnt); + ASSERT(!pLock->locktype); + + s = rangeLock(pFile, F_RDLCK, &tErrno); + + // Drop the temporary PENDING lock. + lock.l_start = SQLitePendingByte; + lock.l_len = 1; + lock.l_type = F_UNLCK; + if (fcntl(pFile->h, F_SETLK, &lock)) { + if (s != -1) { + tErrno = errno; + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); + if ((rc != SQLITE_OK) && (rc != SQLITE_BUSY)) + pFile->lastErrno = tErrno; + return rc; + } + } + if (s == -1) { + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); + if ((rc != SQLITE_OK) && (rc != SQLITE_BUSY)) + pFile->lastErrno = tErrno; + } else { + pFile->locktype = SQLITE_LOCK_SHARED; + pFile->pOpen->nLock++; + pLock->cnt = 1; + } + } else if (locktype == SQLITE_LOCK_EXCLUSIVE && pLock->cnt > 1) { + // We are trying for an exclusive lock but another thread in the + // same process is still holding a shared lock. + rc = SQLITE_BUSY; + } else { + // The request was for a RESERVED or EXCLUSIVE lock. It is + // assumed that there is a SHARED or greater lock on the file + // already. + ASSERT(pFile->locktype); + lock.l_type = F_WRLCK; + switch (locktype) { + case SQLITE_LOCK_RESERVED: + lock.l_start = SQLiteReservedByte; + s = fcntl(pFile->h, F_SETLK, &lock); + tErrno = errno; + break; + case SQLITE_LOCK_EXCLUSIVE: + s = rangeLock(pFile, F_WRLCK, &tErrno); + break; + default: + ASSERT_NOT_REACHED(); + } + if (s == -1) { + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_LOCK); + if ((rc != SQLITE_OK) && (rc != SQLITE_BUSY)) + pFile->lastErrno = tErrno; + } + } + + if (rc == SQLITE_OK) { + pFile->locktype = locktype; + pLock->locktype = locktype; + } else if (locktype == SQLITE_LOCK_EXCLUSIVE) { + pFile->locktype = SQLITE_LOCK_PENDING; + pLock->locktype = SQLITE_LOCK_PENDING; + } + + return rc; +} + +// Closes all file descriptors for given ChromiumFile for which the close has been deferred. +// Returns a SQLite error code. +static int closePendingFds(ChromiumFile* pFile) +{ + int rc = SQLITE_OK; + ChromiumOpenInfo* pOpen = pFile->pOpen; + ChromiumUnusedFd* pError = 0; + ChromiumUnusedFd* pNext; + for (ChromiumUnusedFd* p = pOpen->pUnused; p; p = pNext) { + pNext = p->pNext; + if (close(p->fd)) { + pFile->lastErrno = errno; + rc = SQLITE_IOERR_CLOSE; + p->pNext = pError; + pError = p; + } else + sqlite3_free(p); + } + pOpen->pUnused = pError; + return rc; +} + +// Lowers the locking level on file descriptor. +// locktype must be either SQLITE_LOCK_NONE or SQLITE_LOCK_SHARED. +static int chromiumUnlock(sqlite3_file* id, int locktype) +{ + ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); + ASSERT(pFile); + ASSERT(locktype <= SQLITE_LOCK_SHARED); + + if (pFile->locktype <= locktype) + return SQLITE_OK; + + ChromiumLockInfo* pLock = pFile->pLock; + ASSERT(pLock->cnt); + + struct flock lock; + int rc = SQLITE_OK; + int h = pFile->h; + int tErrno; + + if (pFile->locktype > SQLITE_LOCK_SHARED) { + ASSERT(pLock->locktype == pFile->locktype); + + if (locktype == SQLITE_LOCK_SHARED && rangeLock(pFile, F_RDLCK, &tErrno) == -1) { + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_RDLOCK); + if ((rc != SQLITE_OK) && (rc != SQLITE_BUSY)) + pFile->lastErrno = tErrno; + if (rc == SQLITE_OK) + pFile->locktype = locktype; + return rc; + } + lock.l_type = F_UNLCK; + lock.l_whence = SEEK_SET; + lock.l_start = SQLitePendingByte; + lock.l_len = 2; + if (fcntl(h, F_SETLK, &lock) != -1) + pLock->locktype = SQLITE_LOCK_SHARED; + else { + tErrno = errno; + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); + if ((rc != SQLITE_OK) && (rc != SQLITE_BUSY)) + pFile->lastErrno = tErrno; + if (rc == SQLITE_OK) + pFile->locktype = locktype; + return rc; + } + } + if (locktype == SQLITE_LOCK_NONE) { + struct ChromiumOpenInfo *pOpen; + + pLock->cnt--; + + // Release the lock using an OS call only when all threads in this same process have released the lock. + if (!pLock->cnt) { + lock.l_type = F_UNLCK; + lock.l_whence = SEEK_SET; + lock.l_start = lock.l_len = 0L; + if (fcntl(h, F_SETLK, &lock) != -1) + pLock->locktype = SQLITE_LOCK_NONE; + else { + tErrno = errno; + rc = sqliteErrorFromPosixError(tErrno, SQLITE_IOERR_UNLOCK); + if ((rc != SQLITE_OK) && (rc != SQLITE_BUSY)) + pFile->lastErrno = tErrno; + pLock->locktype = SQLITE_LOCK_NONE; + pFile->locktype = SQLITE_LOCK_NONE; + } + } + + pOpen = pFile->pOpen; + pOpen->nLock--; + ASSERT(pOpen->nLock >= 0); + if (!pOpen->nLock) { + int rc2 = closePendingFds(pFile); + if (rc == SQLITE_OK) + rc = rc2; + } + } + + if (rc == SQLITE_OK) + pFile->locktype = locktype; + return rc; +} + +// Closes all file handles for given ChromiumFile and sets all its fields to 0. +// Returns a SQLite error code. +static int chromiumCloseNoLock(sqlite3_file* id) +{ + ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); + if (!pFile) + return SQLITE_OK; + if (pFile->dirfd >= 0) { + if (close(pFile->dirfd)) { + pFile->lastErrno = errno; + return SQLITE_IOERR_DIR_CLOSE; + } + pFile->dirfd = -1; + } + if (pFile->h >= 0 && close(pFile->h)) { + pFile->lastErrno = errno; + return SQLITE_IOERR_CLOSE; + } + sqlite3_free(pFile->pUnused); + memset(pFile, 0, sizeof(ChromiumFile)); + return SQLITE_OK; +} + +// Closes a ChromiumFile, including locking operations. Returns a SQLite error code. +static int chromiumClose(sqlite3_file* id) +{ + if (!id) + return SQLITE_OK; + + ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); + chromiumUnlock(id, SQLITE_LOCK_NONE); + if (pFile->pOpen && pFile->pOpen->nLock) { + // If there are outstanding locks, do not actually close the file just + // yet because that would clear those locks. + ChromiumOpenInfo* pOpen = pFile->pOpen; + ChromiumUnusedFd* p = pFile->pUnused; + p->pNext = pOpen->pUnused; + pOpen->pUnused = p; + pFile->h = -1; + pFile->pUnused = 0; + } + releaseLockInfo(pFile->pLock); + releaseOpenInfo(pFile->pOpen); + return chromiumCloseNoLock(id); +} + +static int chromiumCheckReservedLockNoop(sqlite3_file*, int* pResOut) +{ + *pResOut = 0; + return SQLITE_OK; +} + +static int chromiumLockNoop(sqlite3_file*, int) +{ + return SQLITE_OK; +} + +static int chromiumUnlockNoop(sqlite3_file*, int) +{ + return SQLITE_OK; +} + +// Seeks to the requested offset and reads up to |cnt| bytes into |pBuf|. Returns number of bytes actually read. +static int seekAndRead(ChromiumFile* id, sqlite3_int64 offset, void* pBuf, int cnt) +{ + sqlite_int64 newOffset = lseek(id->h, offset, SEEK_SET); + if (newOffset != offset) { + id->lastErrno = (newOffset == -1) ? errno : 0; + return -1; + } + int got = read(id->h, pBuf, cnt); + if (got < 0) + id->lastErrno = errno; + return got; +} + +// Reads data from file into a buffer. Returns a SQLite error code. +static int chromiumRead(sqlite3_file* id, void* pBuf, int amt, sqlite3_int64 offset) +{ + ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); + ASSERT(pFile); + + // The bytes in the locking range should never be read. + ASSERT(!pFile->pUnused || offset >= SQLitePendingByte + 512 || offset + amt <= SQLitePendingByte); + + int got = seekAndRead(pFile, offset, pBuf, amt); + if (got == amt) + return SQLITE_OK; + + if (got < 0) + return SQLITE_IOERR_READ; + + // Unread parts of the buffer must be zero-filled. + memset(&(reinterpret_cast<char*>(pBuf))[got], 0, amt - got); + pFile->lastErrno = 0; + return SQLITE_IOERR_SHORT_READ; +} + +// Seeks to the requested offset and writes up to |cnt| bytes. Returns number of bytes actually written. +static int seekAndWrite(ChromiumFile* id, sqlite_int64 offset, const void* pBuf, int cnt) +{ + sqlite_int64 newOffset = lseek(id->h, offset, SEEK_SET); + if (newOffset != offset) { + id->lastErrno = (newOffset == -1) ? errno : 0; + return -1; + } + int got = write(id->h, pBuf, cnt); + if (got < 0) + id->lastErrno = errno; + return got; +} + +// Writes data from buffer into a file. Returns a SQLite error code. +static int chromiumWrite(sqlite3_file* id, const void* pBuf, int amt, sqlite3_int64 offset) +{ + ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); + ASSERT(pFile); + ASSERT(amt > 0); + + // The bytes in the locking range should never be written. + ASSERT(!pFile->pUnused || offset >= SQLitePendingByte + 512 || offset + amt <= SQLitePendingByte); + + int wrote = 0; + while (amt > 0 && (wrote = seekAndWrite(pFile, offset, pBuf, amt)) > 0) { + amt -= wrote; + offset += wrote; + pBuf = &(reinterpret_cast<const char*>(pBuf))[wrote]; + } + if (amt > 0) { + if (wrote < 0) + return SQLITE_IOERR_WRITE; + pFile->lastErrno = 0; + return SQLITE_FULL; + } + return SQLITE_OK; +} + +static bool syncWrapper(int fd, bool fullSync) +{ +#if OS(DARWIN) + bool success = false; + if (fullSync) + success = !fcntl(fd, F_FULLFSYNC, 0); + if (!success) + success = !fsync(fd); + return success; +#else + return !fdatasync(fd); +#endif +} + +// Makes sure all writes to a particular file are committed to disk. Returns a SQLite error code. +static int chromiumSync(sqlite3_file* id, int flags) +{ + ASSERT((flags & 0x0F) == SQLITE_SYNC_NORMAL || (flags & 0x0F) == SQLITE_SYNC_FULL); + + ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); + ASSERT(pFile); + + bool isFullSync = ((flags & 0x0F) == SQLITE_SYNC_FULL); + + if (!syncWrapper(pFile->h, isFullSync)) { + pFile->lastErrno = errno; + return SQLITE_IOERR_FSYNC; + } + + if (pFile->dirfd >= 0) { +#if !OS(DARWIN) + if (!isFullSync) { + // Ignore directory sync failures, see http://www.sqlite.org/cvstrac/tktview?tn=1657. + syncWrapper(pFile->dirfd, false); + } +#endif + if (!close(pFile->dirfd)) + pFile->dirfd = -1; + else { + pFile->lastErrno = errno; + return SQLITE_IOERR_DIR_CLOSE; + } + } + + return SQLITE_OK; +} + +// Truncates an open file to the specified size. Returns a SQLite error code. +static int chromiumTruncate(sqlite3_file* id, sqlite_int64 nByte) +{ + ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); + ASSERT(pFile); + + if (ftruncate(pFile->h, nByte)) { + pFile->lastErrno = errno; + return SQLITE_IOERR_TRUNCATE; + } + + return SQLITE_OK; +} + +// Determines the size of a file in bytes. Returns a SQLite error code. +static int chromiumFileSize(sqlite3_file* id, sqlite_int64* pSize) +{ + ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); + ASSERT(pFile); + + struct stat buf; + if (fstat(pFile->h, &buf)) { + pFile->lastErrno = errno; + return SQLITE_IOERR_FSTAT; + } + *pSize = buf.st_size; + + // When opening a zero-size database, findLockInfo writes a single byte into that file + // in order to work around a bug in the OS X msdos filesystem. In order to avoid problems + // with upper layers, we need to report this file size as zero even though it is really 1. + // See http://www.sqlite.org/cvstrac/tktview?tn=3260. + if (*pSize == 1) + *pSize = 0; + + return SQLITE_OK; +} + +static int chromiumFileControl(sqlite3_file* id, int op, void* pArg) +{ + ChromiumFile* pFile = reinterpret_cast<ChromiumFile*>(id); + ASSERT(pFile); + + switch (op) { + case SQLITE_FCNTL_LOCKSTATE: + *reinterpret_cast<int*>(pArg) = pFile->locktype; + return SQLITE_OK; + case SQLITE_LAST_ERRNO: + *reinterpret_cast<int*>(pArg) = pFile->lastErrno; + return SQLITE_OK; + } + return SQLITE_ERROR; +} + +// Same as SQLITE_DEFAULT_SECTOR_SIZE from sqlite's os.h. +static const int SQLiteDefaultSectorSize = 512; + +static int chromiumSectorSize(sqlite3_file*) +{ + return SQLiteDefaultSectorSize; +} + +static int chromiumDeviceCharacteristics(sqlite3_file*) +{ + return 0; +} + +static const sqlite3_io_methods posixIoMethods = { + 1, + chromiumClose, + chromiumRead, + chromiumWrite, + chromiumTruncate, + chromiumSync, + chromiumFileSize, + chromiumLock, + chromiumUnlock, + chromiumCheckReservedLock, + chromiumFileControl, + chromiumSectorSize, + chromiumDeviceCharacteristics +}; + +static const sqlite3_io_methods nolockIoMethods = { + 1, + chromiumCloseNoLock, + chromiumRead, + chromiumWrite, + chromiumTruncate, + chromiumSync, + chromiumFileSize, + chromiumLockNoop, + chromiumUnlockNoop, + chromiumCheckReservedLockNoop, + chromiumFileControl, + chromiumSectorSize, + chromiumDeviceCharacteristics +}; + +// Initializes a ChromiumFile. Returns a SQLite error code. +static int fillInChromiumFile(sqlite3_vfs* pVfs, int h, int dirfd, sqlite3_file* pId, const char* zFilename, int noLock) +{ + ChromiumFile* pNew = reinterpret_cast<ChromiumFile*>(pId); + + ASSERT(!pNew->pLock); + ASSERT(!pNew->pOpen); + + pNew->h = h; + pNew->dirfd = dirfd; + + int rc = SQLITE_OK; + const sqlite3_io_methods* pLockingStyle; + if (noLock) + pLockingStyle = &nolockIoMethods; + else { + pLockingStyle = &posixIoMethods; + rc = findLockInfo(pNew, &pNew->pLock, &pNew->pOpen); + if (rc != SQLITE_OK) { + // If an error occured in findLockInfo(), close the file descriptor + // immediately. This can happen in two scenarios: + // + // (a) A call to fstat() failed. + // (b) A malloc failed. + // + // Scenario (b) may only occur if the process is holding no other + // file descriptors open on the same file. If there were other file + // descriptors on this file, then no malloc would be required by + // findLockInfo(). If this is the case, it is quite safe to close + // handle h - as it is guaranteed that no posix locks will be released + // by doing so. + // + // If scenario (a) caused the error then things are not so safe. The + // implicit assumption here is that if fstat() fails, things are in + // such bad shape that dropping a lock or two doesn't matter much. + close(h); + h = -1; + } + } + + pNew->lastErrno = 0; + if (rc != SQLITE_OK) { + if (dirfd >= 0) + close(dirfd); + if (h >= 0) + close(h); + } else + pNew->pMethod = pLockingStyle; + return rc; +} + +// Searches for an unused file descriptor that was opened on the database +// file identified by zPath with matching flags. Returns 0 if not found. +static ChromiumUnusedFd* findReusableFd(const char* zPath, int flags) +{ + ChromiumUnusedFd* pUnused = 0; + + struct stat sStat; + if (!stat(zPath, &sStat)) { + ChromiumFileId id; + id.dev = sStat.st_dev; + id.ino = sStat.st_ino; + + ChromiumOpenInfo* pO = 0; + for (pO = openList; pO && memcmp(&id, &pO->fileId, sizeof(id)); pO = pO->pNext) { } + if (pO) { + ChromiumUnusedFd** pp; + for (pp = &pO->pUnused; *pp && (*pp)->flags != flags; pp = &((*pp)->pNext)) { } + pUnused = *pp; + if (pUnused) + *pp = pUnused->pNext; + } + } + return pUnused; +} + +// Opens a file. +// +// vfs - pointer to the sqlite3_vfs object. +// fileName - the name of the file. +// id - the structure that will manipulate the newly opened file. +// desiredFlags - the desired open mode flags. +// usedFlags - the actual open mode flags that were used. +static int chromiumOpen(sqlite3_vfs* vfs, const char* fileName, + sqlite3_file* id, int desiredFlags, int* usedFlags) +{ + // The mask 0x00007F00 gives us the 7 bits that determine the type of the file SQLite is trying to open. + int fileType = desiredFlags & 0x00007F00; + + memset(id, 0, sizeof(ChromiumFile)); + ChromiumFile* chromiumFile = reinterpret_cast<ChromiumFile*>(id); + int fd = -1; + if (fileType == SQLITE_OPEN_MAIN_DB) { + ChromiumUnusedFd* unusedFd = findReusableFd(fileName, desiredFlags); + if (unusedFd) + fd = unusedFd->fd; + else { + unusedFd = static_cast<ChromiumUnusedFd*>(sqlite3_malloc(sizeof(*unusedFd))); + if (!unusedFd) + return SQLITE_NOMEM; + } + chromiumFile->pUnused = unusedFd; + } + + if (fd < 0) { + fd = ChromiumBridge::databaseOpenFile(fileName, desiredFlags); + if ((fd < 0) && (desiredFlags & SQLITE_OPEN_READWRITE)) { + int newFlags = (desiredFlags & ~(SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE)) | SQLITE_OPEN_READONLY; + fd = ChromiumBridge::databaseOpenFile(fileName, newFlags); + } + } + if (fd < 0) { + sqlite3_free(chromiumFile->pUnused); + return SQLITE_CANTOPEN; + } + + if (usedFlags) + *usedFlags = desiredFlags; + if (chromiumFile->pUnused) { + chromiumFile->pUnused->fd = fd; + chromiumFile->pUnused->flags = desiredFlags; + } + + fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); + + int noLock = (fileType != SQLITE_OPEN_MAIN_DB); + int rc = fillInChromiumFile(vfs, fd, -1, id, fileName, noLock); + if (rc != SQLITE_OK) + sqlite3_free(chromiumFile->pUnused); + return rc; +} + +// Deletes the given file. +// +// vfs - pointer to the sqlite3_vfs object. +// fileName - the name of the file. +// syncDir - determines if the directory to which this file belongs +// should be synched after the file is deleted. +static int chromiumDelete(sqlite3_vfs*, const char* fileName, int syncDir) +{ + return ChromiumBridge::databaseDeleteFile(fileName, syncDir); +} + +// Check the existance and status of the given file. +// +// vfs - pointer to the sqlite3_vfs object. +// fileName - the name of the file. +// flag - the type of test to make on this file. +// res - the result. +static int chromiumAccess(sqlite3_vfs*, const char* fileName, int flag, int* res) +{ + int attr = static_cast<int>(ChromiumBridge::databaseGetFileAttributes(fileName)); + if (attr < 0) { + *res = 0; + return SQLITE_OK; + } + + switch (flag) { + case SQLITE_ACCESS_EXISTS: + *res = 1; // if the file doesn't exist, attr < 0 + break; + case SQLITE_ACCESS_READWRITE: + *res = (attr & W_OK) && (attr & R_OK); + break; + case SQLITE_ACCESS_READ: + *res = (attr & R_OK); + break; + default: + return SQLITE_ERROR; + } + + return SQLITE_OK; +} + +// Turns a relative pathname into a full pathname. +// +// vfs - pointer to the sqlite3_vfs object. +// relativePath - the relative path. +// bufSize - the size of the output buffer in bytes. +// absolutePath - the output buffer where the absolute path will be stored. +static int chromiumFullPathname(sqlite3_vfs* vfs, const char* relativePath, + int, char* absolutePath) +{ + // The renderer process doesn't need to know the absolute path of the file + sqlite3_snprintf(vfs->mxPathname, absolutePath, "%s", relativePath); + return SQLITE_OK; +} + +#ifndef SQLITE_OMIT_LOAD_EXTENSION +// We disallow loading DSOs inside the renderer process, so the following procedures are no-op. +static void* chromiumDlOpen(sqlite3_vfs*, const char*) +{ + return 0; +} + +static void chromiumDlError(sqlite3_vfs*, int, char*) +{ +} + +static void (*chromiumDlSym(sqlite3_vfs*, void*, const char*))() +{ + return 0; +} + +static void chromiumDlClose(sqlite3_vfs*, void*) +{ +} +#else +#define chromiumDlOpen 0 +#define chromiumDlError 0 +#define chromiumDlSym 0 +#define chromiumDlClose 0 +#endif // SQLITE_OMIT_LOAD_EXTENSION + +// Generates a seed for SQLite's PRNG. +static int chromiumRandomness(sqlite3_vfs*, int nBuf, char *zBuf) +{ + ASSERT(static_cast<size_t>(nBuf) >= (sizeof(time_t) + sizeof(int))); + + memset(zBuf, 0, nBuf); + int fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) { + time_t t; + time(&t); + memcpy(zBuf, &t, sizeof(t)); + int pid = getpid(); + memcpy(&zBuf[sizeof(t)], &pid, sizeof(pid)); + return sizeof(t) + sizeof(pid); + } + nBuf = read(fd, zBuf, nBuf); + close(fd); + return nBuf; +} + +// Sleeps for at least |microseconds|, and returns the actual +// amount of time spent sleeping (in microseconds). +static int chromiumSleep(sqlite3_vfs*, int microseconds) +{ +#if OS(DARWIN) + usleep(microseconds); + return microseconds; +#else + // Round to the nearest second. + int seconds = (microseconds + 999999) / 1000000; + sleep(seconds); + return seconds * 1000000; +#endif +} + +// Retrieves the current system time (UTC). +static int chromiumCurrentTime(sqlite3_vfs*, double* now) +{ + struct timeval timeval; + gettimeofday(&timeval, 0); + *now = 2440587.5 + timeval.tv_sec / 86400.0 + timeval.tv_usec / 86400000000.0; + return 0; +} + +// This is not yet implemented in SQLite core. +static int chromiumGetLastError(sqlite3_vfs*, int, char*) +{ + return 0; +} + +// Same as MAX_PATHNAME from sqlite's os_unix.c. +static const int chromiumMaxPathname = 512; + +namespace WebCore { + +void SQLiteFileSystem::registerSQLiteVFS() +{ + static sqlite3_vfs chromium_vfs = { + 1, + sizeof(ChromiumFile), + chromiumMaxPathname, + 0, + "chromium_vfs", + 0, + chromiumOpen, + chromiumDelete, + chromiumAccess, + chromiumFullPathname, + chromiumDlOpen, + chromiumDlError, + chromiumDlSym, + chromiumDlClose, + chromiumRandomness, + chromiumSleep, + chromiumCurrentTime, + chromiumGetLastError + }; + sqlite3_vfs_register(&chromium_vfs, 0); +} + +} // namespace WebCore diff --git a/Source/WebCore/platform/sql/chromium/SQLiteFileSystemChromiumWin.cpp b/Source/WebCore/platform/sql/chromium/SQLiteFileSystemChromiumWin.cpp new file mode 100644 index 0000000..d846af7 --- /dev/null +++ b/Source/WebCore/platform/sql/chromium/SQLiteFileSystemChromiumWin.cpp @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2009 Google Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google Inc. nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "config.h" +#include "SQLiteFileSystem.h" + +#include "ChromiumBridge.h" +#include <sqlite3.h> +#include <windows.h> + +using namespace WebCore; + +// Defined in Chromium's codebase in third_party/sqlite/src/os_win.c +extern "C" { +int chromium_sqlite3_initialize_win_sqlite3_file(sqlite3_file* file, HANDLE handle); +} + +// Chromium's Windows implementation of SQLite VFS +namespace { + +// Opens a file. +// +// vfs - pointer to the sqlite3_vfs object. +// fileName - the name of the file. +// id - the structure that will manipulate the newly opened file. +// desiredFlags - the desired open mode flags. +// usedFlags - the actual open mode flags that were used. +int chromiumOpen(sqlite3_vfs*, const char* fileName, + sqlite3_file* id, int desiredFlags, int* usedFlags) +{ + HANDLE h = ChromiumBridge::databaseOpenFile(fileName, desiredFlags); + if (h == INVALID_HANDLE_VALUE) { + if (desiredFlags & SQLITE_OPEN_READWRITE) { + int newFlags = (desiredFlags | SQLITE_OPEN_READONLY) & ~SQLITE_OPEN_READWRITE; + return chromiumOpen(0, fileName, id, newFlags, usedFlags); + } else + return SQLITE_CANTOPEN; + } + if (usedFlags) { + if (desiredFlags & SQLITE_OPEN_READWRITE) + *usedFlags = SQLITE_OPEN_READWRITE; + else + *usedFlags = SQLITE_OPEN_READONLY; + } + + chromium_sqlite3_initialize_win_sqlite3_file(id, h); + return SQLITE_OK; +} + +// Deletes the given file. +// +// vfs - pointer to the sqlite3_vfs object. +// fileName - the name of the file. +// syncDir - determines if the directory to which this file belongs +// should be synched after the file is deleted. +int chromiumDelete(sqlite3_vfs*, const char* fileName, int) +{ + return ChromiumBridge::databaseDeleteFile(fileName); +} + +// Check the existance and status of the given file. +// +// vfs - pointer to the sqlite3_vfs object. +// fileName - the name of the file. +// flag - the type of test to make on this file. +// res - the result. +int chromiumAccess(sqlite3_vfs*, const char* fileName, int flag, int* res) +{ + DWORD attr = ChromiumBridge::databaseGetFileAttributes(fileName); + switch (flag) { + case SQLITE_ACCESS_READ: + case SQLITE_ACCESS_EXISTS: + *res = (attr != INVALID_FILE_ATTRIBUTES); + break; + case SQLITE_ACCESS_READWRITE: + *res = ((attr & FILE_ATTRIBUTE_READONLY) == 0); + break; + default: + return SQLITE_ERROR; + } + + return SQLITE_OK; +} + +// Turns a relative pathname into a full pathname. +// +// vfs - pointer to the sqlite3_vfs object. +// relativePath - the relative path. +// bufSize - the size of the output buffer in bytes. +// absolutePath - the output buffer where the absolute path will be stored. +int chromiumFullPathname(sqlite3_vfs* vfs, const char* relativePath, + int, char* absolutePath) +{ + // The renderer process doesn't need to know the absolute path of the file + sqlite3_snprintf(vfs->mxPathname, absolutePath, "%s", relativePath); + return SQLITE_OK; +} + +#ifndef SQLITE_OMIT_LOAD_EXTENSION +// Returns NULL, thus disallowing loading libraries in the renderer process. +// +// vfs - pointer to the sqlite3_vfs object. +// fileName - the name of the shared library file. +void* chromiumDlOpen(sqlite3_vfs*, const char*) +{ + return 0; +} +#else +#define chromiumDlOpen 0 +#endif // SQLITE_OMIT_LOAD_EXTENSION + +} // namespace + +namespace WebCore { + +void SQLiteFileSystem::registerSQLiteVFS() +{ + sqlite3_vfs* win32_vfs = sqlite3_vfs_find("win32"); + static sqlite3_vfs chromium_vfs = { + 1, + win32_vfs->szOsFile, + win32_vfs->mxPathname, + 0, + "chromium_vfs", + win32_vfs->pAppData, + chromiumOpen, + chromiumDelete, + chromiumAccess, + chromiumFullPathname, + chromiumDlOpen, + win32_vfs->xDlError, + win32_vfs->xDlSym, + win32_vfs->xDlClose, + win32_vfs->xRandomness, + win32_vfs->xSleep, + win32_vfs->xCurrentTime, + win32_vfs->xGetLastError + }; + sqlite3_vfs_register(&chromium_vfs, 0); +} + +} // namespace WebCore |