summaryrefslogtreecommitdiffstats
path: root/Source/WebCore/platform/sql
diff options
context:
space:
mode:
authorSteve Block <steveblock@google.com>2011-05-13 06:44:40 -0700
committerAndroid (Google) Code Review <android-gerrit@google.com>2011-05-13 06:44:40 -0700
commit08014c20784f3db5df3a89b73cce46037b77eb59 (patch)
tree47749210d31e19e6e2f64036fa8fae2ad693476f /Source/WebCore/platform/sql
parent860220379e56aeb66424861ad602b07ee22b4055 (diff)
parent4c3661f7918f8b3f139f824efb7855bedccb4c94 (diff)
downloadexternal_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.cpp56
-rw-r--r--Source/WebCore/platform/sql/SQLValue.h58
-rw-r--r--Source/WebCore/platform/sql/SQLiteAuthorizer.cpp43
-rw-r--r--Source/WebCore/platform/sql/SQLiteDatabase.cpp476
-rw-r--r--Source/WebCore/platform/sql/SQLiteDatabase.h159
-rw-r--r--Source/WebCore/platform/sql/SQLiteFileSystem.cpp128
-rw-r--r--Source/WebCore/platform/sql/SQLiteFileSystem.h117
-rw-r--r--Source/WebCore/platform/sql/SQLiteStatement.cpp502
-rw-r--r--Source/WebCore/platform/sql/SQLiteStatement.h104
-rw-r--r--Source/WebCore/platform/sql/SQLiteTransaction.cpp106
-rw-r--r--Source/WebCore/platform/sql/SQLiteTransaction.h56
-rw-r--r--Source/WebCore/platform/sql/chromium/SQLiteFileSystemChromium.cpp103
-rw-r--r--Source/WebCore/platform/sql/chromium/SQLiteFileSystemChromiumPosix.cpp1188
-rw-r--r--Source/WebCore/platform/sql/chromium/SQLiteFileSystemChromiumWin.cpp169
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