diff options
Diffstat (limited to 'chromium/sql')
-rw-r--r-- | chromium/sql/BUILD.gn | 14 | ||||
-rw-r--r-- | chromium/sql/connection.cc | 15 | ||||
-rw-r--r-- | chromium/sql/connection_unittest.cc | 17 | ||||
-rw-r--r-- | chromium/sql/mojo/BUILD.gn | 62 | ||||
-rw-r--r-- | chromium/sql/mojo/DEPS | 6 | ||||
-rw-r--r-- | chromium/sql/mojo/mojo_vfs.cc | 455 | ||||
-rw-r--r-- | chromium/sql/mojo/mojo_vfs.h | 45 | ||||
-rw-r--r-- | chromium/sql/mojo/sql_test_base.cc | 166 | ||||
-rw-r--r-- | chromium/sql/mojo/sql_test_base.h | 92 | ||||
-rw-r--r-- | chromium/sql/mojo/vfs_unittest.cc | 342 | ||||
-rw-r--r-- | chromium/sql/recovery.cc | 54 | ||||
-rw-r--r-- | chromium/sql/recovery.h | 21 | ||||
-rw-r--r-- | chromium/sql/recovery_unittest.cc | 144 | ||||
-rw-r--r-- | chromium/sql/sql.gyp | 21 | ||||
-rw-r--r-- | chromium/sql/sql_unittests_apk.isolate | 1 |
15 files changed, 167 insertions, 1288 deletions
diff --git a/chromium/sql/BUILD.gn b/chromium/sql/BUILD.gn index b79eca87874..b2ca63849f9 100644 --- a/chromium/sql/BUILD.gn +++ b/chromium/sql/BUILD.gn @@ -69,12 +69,14 @@ source_set("redirection_header") { ] } -# TODO(GYP): Delete this after we've converted everything to GN. -# The _run targets exist only for compatibility w/ GYP. -group("sql_unittests_run") { +bundle_data("sql_unittests_bundle_data") { testonly = true - deps = [ - ":sql_unittests", + sources = [ + "test/data", + ] + outputs = [ + "{{bundle_resources_dir}}/" + + "{{source_root_relative_dir}}/{{source_file_part}}", ] } @@ -106,8 +108,8 @@ test("sql_unittests") { deps = [ ":redirection_header", ":sql", + ":sql_unittests_bundle_data", ":test_support", - "//base/allocator", "//base/test:test_support", "//testing/gtest", "//third_party/sqlite", diff --git a/chromium/sql/connection.cc b/chromium/sql/connection.cc index f08560c5135..7787adfe283 100644 --- a/chromium/sql/connection.cc +++ b/chromium/sql/connection.cc @@ -268,7 +268,15 @@ void Connection::ReportDiagnosticInfo(int extended_error, Statement* stmt) { std::string debug_info; const int error = (extended_error & 0xFF); if (error == SQLITE_CORRUPT) { + // CollectCorruptionInfo() is implemented in terms of sql::Connection, + // prevent reentrant calls to the error callback. + // TODO(shess): Rewrite IntegrityCheckHelper() in terms of raw SQLite. + ErrorCallback original_callback = std::move(error_callback_); + reset_error_callback(); + debug_info = CollectCorruptionInfo(); + + error_callback_ = std::move(original_callback); } else { debug_info = CollectErrorInfo(extended_error, stmt); } @@ -587,7 +595,12 @@ void Connection::Preload() { // work. There could be two prepared statements, one for cache_size=1 one for // cache_size=goal. void Connection::ReleaseCacheMemoryIfNeeded(bool implicit_change_performed) { - DCHECK(is_open()); + // The database could have been closed during a transaction as part of error + // recovery. + if (!db_) { + DLOG_IF(FATAL, !poisoned_) << "Illegal use of connection without a db"; + return; + } // If memory-mapping is not enabled, the page cache helps performance. if (!mmap_enabled_) diff --git a/chromium/sql/connection_unittest.cc b/chromium/sql/connection_unittest.cc index c6f80d84cb6..d9dd9d9f460 100644 --- a/chromium/sql/connection_unittest.cc +++ b/chromium/sql/connection_unittest.cc @@ -247,7 +247,9 @@ class SQLConnectionTest : public sql::SQLTestBase { // Handle errors by blowing away the database. void RazeErrorCallback(int expected_error, int error, sql::Statement* stmt) { - EXPECT_EQ(expected_error, error); + // Nothing here needs extended errors at this time. + EXPECT_EQ(expected_error, expected_error&0xff); + EXPECT_EQ(expected_error, error&0xff); db().RazeAndClose(); } }; @@ -928,6 +930,19 @@ TEST_F(SQLConnectionTest, Poison) { // The existing statement has become invalid. ASSERT_FALSE(valid_statement.is_valid()); ASSERT_FALSE(valid_statement.Step()); + + // Test that poisoning the database during a transaction works (with errors). + // RazeErrorCallback() poisons the database, the extra COMMIT causes + // CommitTransaction() to throw an error while commiting. + db().set_error_callback(base::Bind(&SQLConnectionTest::RazeErrorCallback, + base::Unretained(this), + SQLITE_ERROR)); + db().Close(); + ASSERT_TRUE(db().Open(db_path())); + EXPECT_TRUE(db().BeginTransaction()); + EXPECT_TRUE(db().Execute("INSERT INTO x VALUES ('x')")); + EXPECT_TRUE(db().Execute("COMMIT")); + EXPECT_FALSE(db().CommitTransaction()); } // Test attaching and detaching databases from the connection. diff --git a/chromium/sql/mojo/BUILD.gn b/chromium/sql/mojo/BUILD.gn deleted file mode 100644 index b7a3793297b..00000000000 --- a/chromium/sql/mojo/BUILD.gn +++ /dev/null @@ -1,62 +0,0 @@ -# Copyright 2015 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import("//mojo/public/mojo_application.gni") - -source_set("mojo") { - sources = [ - "mojo_vfs.cc", - "mojo_vfs.h", - ] - - # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. - configs += [ "//build/config/compiler:no_size_t_to_int_warning" ] - - defines = [ "SQL_IMPLEMENTATION" ] - - deps = [ - "//base", - "//base/third_party/dynamic_annotations", - "//components/filesystem/public/interfaces", - "//mojo/common", - "//mojo/platform_handle", - "//mojo/shell/public/cpp", - "//third_party/sqlite", - ] -} - -mojo_native_application("apptests") { - output_name = "sql_apptests" - - testonly = true - - # Instead of using the code in //sql/test/sql_test_base.h, we should use the - # mojo test base class. - defines = [ "MOJO_APPTEST_IMPL" ] - - sources = [ - "../connection_unittest.cc", - "../statement_unittest.cc", - "../test/paths.cc", - "../test/paths.h", - "../transaction_unittest.cc", - "sql_test_base.cc", - "sql_test_base.h", - "vfs_unittest.cc", - ] - - deps = [ - ":mojo", - "//base", - "//base/test:test_support", - "//components/filesystem/public/interfaces", - "//mojo/public/cpp/bindings", - "//mojo/shell/public/cpp:sources", - "//mojo/shell/public/cpp:test_support", - "//sql", - "//sql:redirection_header", - "//sql:test_support", - "//testing/gtest:gtest", - ] -} diff --git a/chromium/sql/mojo/DEPS b/chromium/sql/mojo/DEPS deleted file mode 100644 index 9dd7c0295fc..00000000000 --- a/chromium/sql/mojo/DEPS +++ /dev/null @@ -1,6 +0,0 @@ -include_rules = [ - "+components/filesystem", - "+mojo/shell", - "+mojo/public", - "+mojo/util", -] diff --git a/chromium/sql/mojo/mojo_vfs.cc b/chromium/sql/mojo/mojo_vfs.cc deleted file mode 100644 index 5c427fb8b69..00000000000 --- a/chromium/sql/mojo/mojo_vfs.cc +++ /dev/null @@ -1,455 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sql/mojo/mojo_vfs.h" - -#include <stddef.h> -#include <stdint.h> -#include <utility> - -#include "base/logging.h" -#include "base/rand_util.h" -#include "base/strings/stringprintf.h" -#include "base/trace_event/trace_event.h" -#include "components/filesystem/public/interfaces/file.mojom.h" -#include "components/filesystem/public/interfaces/file_system.mojom.h" -#include "components/filesystem/public/interfaces/types.mojom.h" -#include "mojo/public/cpp/bindings/lib/template_util.h" -#include "mojo/util/capture_util.h" -#include "third_party/sqlite/sqlite3.h" - -using mojo::Capture; - -namespace sql { - -sqlite3_vfs* GetParentVFS(sqlite3_vfs* mojo_vfs) { - return static_cast<ScopedMojoFilesystemVFS*>(mojo_vfs->pAppData)->parent_; -} - -filesystem::DirectoryPtr& GetRootDirectory(sqlite3_vfs* mojo_vfs) { - return static_cast<ScopedMojoFilesystemVFS*>(mojo_vfs->pAppData)-> - root_directory_; -} - -namespace { - -// Implementation of the sqlite3 Mojo proxying vfs. -// -// This is a bunch of C callback objects which transparently proxy sqlite3's -// filesystem reads/writes over the mojo:filesystem service. The main -// entrypoint is sqlite3_mojovfs(), which proxies all the file open/delete/etc -// operations. mojo:filesystem has support for passing a raw file descriptor -// over the IPC barrier, and most of the implementation of sqlite3_io_methods -// is derived from the default sqlite3 unix VFS and operates on the raw file -// descriptors. - -const int kMaxPathName = 512; - -// A struct which extends the base sqlite3_file to also hold on to a file -// pipe. We reinterpret_cast our sqlite3_file structs to this struct -// instead. This is "safe" because this struct is really just a slab of -// malloced memory, of which we tell sqlite how large we want with szOsFile. -struct MojoVFSFile { - // The "vtable" of our sqlite3_file "subclass". - sqlite3_file base; - - // We keep an open pipe to the File object to keep it from cleaning itself - // up. - filesystem::FilePtr file_ptr; -}; - -filesystem::FilePtr& GetFSFile(sqlite3_file* vfs_file) { - return reinterpret_cast<MojoVFSFile*>(vfs_file)->file_ptr; -} - -int MojoVFSClose(sqlite3_file* file) { - DVLOG(1) << "MojoVFSClose(*)"; - TRACE_EVENT0("sql", "MojoVFSClose"); - using filesystem::FilePtr; - filesystem::FileError error = filesystem::FILE_ERROR_FAILED; - // Must call File::Close explicitly instead of just deleting the file, since - // otherwise we wouldn't have an object to wait on. - GetFSFile(file)->Close(mojo::Capture(&error)); - GetFSFile(file).WaitForIncomingResponse(); - GetFSFile(file).~FilePtr(); - return SQLITE_OK; -} - -int MojoVFSRead(sqlite3_file* sql_file, - void* buffer, - int size, - sqlite3_int64 offset) { - DVLOG(1) << "MojoVFSRead (" << size << " @ " << offset << ")"; - TRACE_EVENT0("sql", "MojoVFSRead"); - filesystem::FileError error = filesystem::FILE_ERROR_FAILED; - mojo::Array<uint8_t> mojo_data; - GetFSFile(sql_file)->Read(size, offset, filesystem::WHENCE_FROM_BEGIN, - Capture(&error, &mojo_data)); - GetFSFile(sql_file).WaitForIncomingResponse(); - - if (error != filesystem::FILE_ERROR_OK) { - // TODO(erg): Better implementation here. - NOTIMPLEMENTED(); - return SQLITE_IOERR_READ; - } - - if (mojo_data.size()) - memcpy(buffer, &mojo_data.front(), mojo_data.size()); - if (mojo_data.size() == static_cast<size_t>(size)) - return SQLITE_OK; - - // We didn't read the entire buffer. Fill the rest of the buffer with zeros. - memset(reinterpret_cast<char*>(buffer) + mojo_data.size(), 0, - size - mojo_data.size()); - - return SQLITE_IOERR_SHORT_READ; -} - -int MojoVFSWrite(sqlite3_file* sql_file, - const void* buffer, - int size, - sqlite_int64 offset) { - DVLOG(1) << "MojoVFSWrite(*, " << size << ", " << offset << ")"; - TRACE_EVENT0("sql", "MojoVFSWrite"); - mojo::Array<uint8_t> mojo_data(size); - memcpy(&mojo_data.front(), buffer, size); - - filesystem::FileError error = filesystem::FILE_ERROR_FAILED; - uint32_t num_bytes_written = 0; - GetFSFile(sql_file)->Write(std::move(mojo_data), offset, - filesystem::WHENCE_FROM_BEGIN, - Capture(&error, &num_bytes_written)); - GetFSFile(sql_file).WaitForIncomingResponse(); - if (error != filesystem::FILE_ERROR_OK) { - // TODO(erg): Better implementation here. - NOTIMPLEMENTED(); - return SQLITE_IOERR_WRITE; - } - if (num_bytes_written != static_cast<uint32_t>(size)) { - NOTIMPLEMENTED(); - return SQLITE_IOERR_WRITE; - } - - return SQLITE_OK; -} - -int MojoVFSTruncate(sqlite3_file* sql_file, sqlite_int64 size) { - DVLOG(1) << "MojoVFSTruncate(*, " << size << ")"; - TRACE_EVENT0("sql", "MojoVFSTruncate"); - filesystem::FileError error = filesystem::FILE_ERROR_FAILED; - GetFSFile(sql_file)->Truncate(size, Capture(&error)); - GetFSFile(sql_file).WaitForIncomingResponse(); - if (error != filesystem::FILE_ERROR_OK) { - // TODO(erg): Better implementation here. - NOTIMPLEMENTED(); - return SQLITE_IOERR_TRUNCATE; - } - - return SQLITE_OK; -} - -int MojoVFSSync(sqlite3_file* sql_file, int flags) { - DVLOG(1) << "MojoVFSSync(*, " << flags << ")"; - TRACE_EVENT0("sql", "MojoVFSSync"); - filesystem::FileError error = filesystem::FILE_ERROR_FAILED; - GetFSFile(sql_file)->Flush(Capture(&error)); - GetFSFile(sql_file).WaitForIncomingResponse(); - if (error != filesystem::FILE_ERROR_OK) { - // TODO(erg): Better implementation here. - NOTIMPLEMENTED(); - return SQLITE_IOERR_FSYNC; - } - - return SQLITE_OK; -} - -int MojoVFSFileSize(sqlite3_file* sql_file, sqlite_int64* size) { - DVLOG(1) << "MojoVFSFileSize(*)"; - TRACE_EVENT0("sql", "MojoVFSFileSize"); - - filesystem::FileError err = filesystem::FILE_ERROR_FAILED; - filesystem::FileInformationPtr file_info; - GetFSFile(sql_file)->Stat(Capture(&err, &file_info)); - GetFSFile(sql_file).WaitForIncomingResponse(); - - if (err != filesystem::FILE_ERROR_OK) { - // TODO(erg): Better implementation here. - NOTIMPLEMENTED(); - return SQLITE_IOERR_FSTAT; - } - - *size = file_info->size; - return SQLITE_OK; -} - -// TODO(erg): The current base::File interface isn't sufficient to handle -// sqlite's locking primitives, which are done on byte ranges in the file. (See -// "File Locking Notes" in sqlite3.c.) -int MojoVFSLock(sqlite3_file* pFile, int eLock) { - DVLOG(1) << "MojoVFSLock(*, " << eLock << ")"; - return SQLITE_OK; -} -int MojoVFSUnlock(sqlite3_file* pFile, int eLock) { - DVLOG(1) << "MojoVFSUnlock(*, " << eLock << ")"; - return SQLITE_OK; -} -int MojoVFSCheckReservedLock(sqlite3_file* pFile, int* pResOut) { - DVLOG(1) << "MojoVFSCheckReservedLock(*)"; - *pResOut = 0; - return SQLITE_OK; -} - -// TODO(erg): This is the minimal implementation to get a few tests passing; -// lots more needs to be done here. -int MojoVFSFileControl(sqlite3_file* pFile, int op, void* pArg) { - DVLOG(1) << "MojoVFSFileControl(*, " << op << ", *)"; - if (op == SQLITE_FCNTL_PRAGMA) { - // Returning NOTFOUND tells sqlite that we aren't doing any processing. - return SQLITE_NOTFOUND; - } - - return SQLITE_OK; -} - -int MojoVFSSectorSize(sqlite3_file* pFile) { - DVLOG(1) << "MojoVFSSectorSize(*)"; - // Use the default sector size. - return 0; -} - -int MojoVFSDeviceCharacteristics(sqlite3_file* pFile) { - DVLOG(1) << "MojoVFSDeviceCharacteristics(*)"; - // TODO(erg): Figure out what to return here. (This function is super spammy, - // so not leaving a NOTIMPLEMENTED().) - return 0; -} - -static sqlite3_io_methods mojo_vfs_io_methods = { - 1, /* iVersion */ - MojoVFSClose, /* xClose */ - MojoVFSRead, /* xRead */ - MojoVFSWrite, /* xWrite */ - MojoVFSTruncate, /* xTruncate */ - MojoVFSSync, /* xSync */ - MojoVFSFileSize, /* xFileSize */ - MojoVFSLock, /* xLock */ - MojoVFSUnlock, /* xUnlock */ - MojoVFSCheckReservedLock, /* xCheckReservedLock */ - MojoVFSFileControl, /* xFileControl */ - MojoVFSSectorSize, /* xSectorSize */ - MojoVFSDeviceCharacteristics, /* xDeviceCharacteristics */ -}; - -int MojoVFSOpen(sqlite3_vfs* mojo_vfs, - const char* name, - sqlite3_file* file, - int flags, - int* pOutFlags) { - DVLOG(1) << "MojoVFSOpen(*, " << name << ", *, " << flags << ")"; - TRACE_EVENT2("sql", "MojoVFSOpen", - "name", name, - "flags", flags); - int open_flags = 0; - if (flags & SQLITE_OPEN_EXCLUSIVE) { - DCHECK(flags & SQLITE_OPEN_CREATE); - open_flags = filesystem::kFlagCreate; - } else if (flags & SQLITE_OPEN_CREATE) { - DCHECK(flags & SQLITE_OPEN_READWRITE); - open_flags = filesystem::kFlagOpenAlways; - } else { - open_flags = filesystem::kFlagOpen; - } - open_flags |= filesystem::kFlagRead; - if (flags & SQLITE_OPEN_READWRITE) - open_flags |= filesystem::kFlagWrite; - if (flags & SQLITE_OPEN_DELETEONCLOSE) - open_flags |= filesystem::kDeleteOnClose; - - mojo::String mojo_name; - if (name) { - // Don't let callers open the pattern of our temporary databases. When we - // open with a null name and SQLITE_OPEN_DELETEONCLOSE, we unlink the - // database after we open it. If we create a database here, close it - // normally, and then open the same file through the other path, we could - // delete the database. - CHECK(strncmp("Temp_", name, 5) != 0); - mojo_name = name; - } else { - DCHECK(flags & SQLITE_OPEN_DELETEONCLOSE); - static int temp_number = 0; - mojo_name = base::StringPrintf("Temp_%d.db", temp_number++); - } - - // Grab the incoming file - filesystem::FilePtr file_ptr; - filesystem::FileError error = filesystem::FILE_ERROR_FAILED; - GetRootDirectory(mojo_vfs)->OpenFile(mojo_name, GetProxy(&file_ptr), - open_flags, Capture(&error)); - GetRootDirectory(mojo_vfs).WaitForIncomingResponse(); - if (error != filesystem::FILE_ERROR_OK) { - // TODO(erg): Translate more of the mojo error codes into sqlite error - // codes. - return SQLITE_CANTOPEN; - } - - // Set the method table so we can be closed (and run the manual dtor call to - // match the following placement news). - file->pMethods = &mojo_vfs_io_methods; - - // |file| is actually a malloced buffer of size szOsFile. This means that we - // need to manually use placement new to construct the C++ object which owns - // the pipe to our file. - new (&GetFSFile(file)) filesystem::FilePtr(std::move(file_ptr)); - - return SQLITE_OK; -} - -int MojoVFSDelete(sqlite3_vfs* mojo_vfs, const char* filename, int sync_dir) { - DVLOG(1) << "MojoVFSDelete(*, " << filename << ", " << sync_dir << ")"; - TRACE_EVENT2("sql", "MojoVFSDelete", - "name", filename, - "sync_dir", sync_dir); - // TODO(erg): The default windows sqlite VFS has retry code to work around - // antivirus software keeping files open. We'll probably have to do something - // like that in the far future if we ever support Windows. - filesystem::FileError error = filesystem::FILE_ERROR_FAILED; - GetRootDirectory(mojo_vfs)->Delete(filename, 0, Capture(&error)); - GetRootDirectory(mojo_vfs).WaitForIncomingResponse(); - - if (error == filesystem::FILE_ERROR_OK && sync_dir) { - GetRootDirectory(mojo_vfs)->Flush(Capture(&error)); - GetRootDirectory(mojo_vfs).WaitForIncomingResponse(); - } - - return error == filesystem::FILE_ERROR_OK ? SQLITE_OK : SQLITE_IOERR_DELETE; -} - -int MojoVFSAccess(sqlite3_vfs* mojo_vfs, - const char* filename, - int flags, - int* result) { - DVLOG(1) << "MojoVFSAccess(*, " << filename << ", " << flags << ", *)"; - TRACE_EVENT2("sql", "MojoVFSAccess", - "name", filename, - "flags", flags); - filesystem::FileError error = filesystem::FILE_ERROR_FAILED; - - if (flags == SQLITE_ACCESS_READWRITE || flags == SQLITE_ACCESS_READ) { - bool is_writable = false; - GetRootDirectory(mojo_vfs) - ->IsWritable(filename, Capture(&error, &is_writable)); - GetRootDirectory(mojo_vfs).WaitForIncomingResponse(); - *result = is_writable; - return SQLITE_OK; - } - - if (flags == SQLITE_ACCESS_EXISTS) { - bool exists = false; - GetRootDirectory(mojo_vfs)->Exists(filename, Capture(&error, &exists)); - GetRootDirectory(mojo_vfs).WaitForIncomingResponse(); - *result = exists; - return SQLITE_OK; - } - - return SQLITE_IOERR; -} - -int MojoVFSFullPathname(sqlite3_vfs* mojo_vfs, - const char* relative_path, - int absolute_path_size, - char* absolute_path) { - // The sandboxed process doesn't need to know the absolute path of the file. - sqlite3_snprintf(absolute_path_size, absolute_path, "%s", relative_path); - return SQLITE_OK; -} - -// Don't let SQLite dynamically load things. (If we are using the -// mojo:filesystem proxying VFS, then it's highly likely that we are sandboxed -// and that any attempt to dlopen() a shared object is folly.) -void* MojoVFSDlOpen(sqlite3_vfs*, const char*) { - return 0; -} - -void MojoVFSDlError(sqlite3_vfs*, int buf_size, char* error_msg) { - sqlite3_snprintf(buf_size, error_msg, "Dynamic loading not supported"); -} - -void (*MojoVFSDlSym(sqlite3_vfs*, void*, const char*))(void) { - return 0; -} - -void MojoVFSDlClose(sqlite3_vfs*, void*) { - return; -} - -int MojoVFSRandomness(sqlite3_vfs* mojo_vfs, int size, char* out) { - base::RandBytes(out, size); - return size; -} - -// Proxy the rest of the calls down to the OS specific handler. -int MojoVFSSleep(sqlite3_vfs* mojo_vfs, int micro) { - return GetParentVFS(mojo_vfs)->xSleep(GetParentVFS(mojo_vfs), micro); -} - -int MojoVFSCurrentTime(sqlite3_vfs* mojo_vfs, double* time) { - return GetParentVFS(mojo_vfs)->xCurrentTime(GetParentVFS(mojo_vfs), time); -} - -int MojoVFSGetLastError(sqlite3_vfs* mojo_vfs, int a, char* b) { - return 0; -} - -int MojoVFSCurrentTimeInt64(sqlite3_vfs* mojo_vfs, sqlite3_int64* out) { - return GetParentVFS(mojo_vfs)->xCurrentTimeInt64(GetParentVFS(mojo_vfs), out); -} - -static sqlite3_vfs mojo_vfs = { - 1, /* iVersion */ - sizeof(MojoVFSFile), /* szOsFile */ - kMaxPathName, /* mxPathname */ - 0, /* pNext */ - "mojo", /* zName */ - 0, /* pAppData */ - MojoVFSOpen, /* xOpen */ - MojoVFSDelete, /* xDelete */ - MojoVFSAccess, /* xAccess */ - MojoVFSFullPathname, /* xFullPathname */ - MojoVFSDlOpen, /* xDlOpen */ - MojoVFSDlError, /* xDlError */ - MojoVFSDlSym, /* xDlSym */ - MojoVFSDlClose, /* xDlClose */ - MojoVFSRandomness, /* xRandomness */ - MojoVFSSleep, /* xSleep */ - MojoVFSCurrentTime, /* xCurrentTime */ - MojoVFSGetLastError, /* xGetLastError */ - MojoVFSCurrentTimeInt64 /* xCurrentTimeInt64 */ -}; - -} // namespace - -ScopedMojoFilesystemVFS::ScopedMojoFilesystemVFS( - filesystem::DirectoryPtr root_directory) - : parent_(sqlite3_vfs_find(NULL)), - root_directory_(std::move(root_directory)) { - CHECK(!mojo_vfs.pAppData); - mojo_vfs.pAppData = this; - mojo_vfs.mxPathname = parent_->mxPathname; - - CHECK(sqlite3_vfs_register(&mojo_vfs, 1) == SQLITE_OK); -} - -ScopedMojoFilesystemVFS::~ScopedMojoFilesystemVFS() { - CHECK(mojo_vfs.pAppData); - mojo_vfs.pAppData = nullptr; - - CHECK(sqlite3_vfs_register(parent_, 1) == SQLITE_OK); - CHECK(sqlite3_vfs_unregister(&mojo_vfs) == SQLITE_OK); -} - -filesystem::DirectoryPtr& ScopedMojoFilesystemVFS::GetDirectory() { - return root_directory_; -} - -} // namespace sql diff --git a/chromium/sql/mojo/mojo_vfs.h b/chromium/sql/mojo/mojo_vfs.h deleted file mode 100644 index dc835938eff..00000000000 --- a/chromium/sql/mojo/mojo_vfs.h +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SQL_MOJO_MOJO_VFS_H_ -#define SQL_MOJO_MOJO_VFS_H_ - -#include "base/macros.h" -#include "components/filesystem/public/interfaces/directory.mojom.h" - -typedef struct sqlite3_vfs sqlite3_vfs; - -namespace sql { - -// Changes the default sqlite3 vfs to a vfs that uses proxies calls to the -// mojo:filesystem service. Instantiating this object transparently changes how -// the entire //sql/ subsystem works in the process of the caller; all paths -// are treated as relative to |directory|. -class ScopedMojoFilesystemVFS { - public: - explicit ScopedMojoFilesystemVFS(filesystem::DirectoryPtr directory); - ~ScopedMojoFilesystemVFS(); - - // Returns the directory of the current VFS. - filesystem::DirectoryPtr& GetDirectory(); - - private: - friend sqlite3_vfs* GetParentVFS(sqlite3_vfs* mojo_vfs); - friend filesystem::DirectoryPtr& GetRootDirectory(sqlite3_vfs* mojo_vfs); - - // The default vfs at the time MojoVFS was installed. We use the to pass - // through things like randomness requests and per-platform sleep calls. - sqlite3_vfs* parent_; - - // When we initialize the subsystem, we are given a filesystem::Directory - // object, which is the root directory of a mojo:filesystem. All access to - // various files are specified from this root directory. - filesystem::DirectoryPtr root_directory_; - - DISALLOW_COPY_AND_ASSIGN(ScopedMojoFilesystemVFS); -}; - -} // namespace sql - -#endif // SQL_MOJO_MOJO_VFS_H_ diff --git a/chromium/sql/mojo/sql_test_base.cc b/chromium/sql/mojo/sql_test_base.cc deleted file mode 100644 index 72aaa64f964..00000000000 --- a/chromium/sql/mojo/sql_test_base.cc +++ /dev/null @@ -1,166 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "sql/mojo/sql_test_base.h" - -#include <stddef.h> -#include <stdint.h> -#include <utility> - -#include "mojo/shell/public/cpp/application_impl.h" -#include "mojo/util/capture_util.h" -#include "sql/mojo/mojo_vfs.h" -#include "sql/test/test_helpers.h" - -using mojo::Capture; - -namespace sql { - -SQLTestBase::SQLTestBase() - : binding_(this) { -} - -SQLTestBase::~SQLTestBase() { -} - -base::FilePath SQLTestBase::db_path() { - return base::FilePath(FILE_PATH_LITERAL("SQLTest.db")); -} - -sql::Connection& SQLTestBase::db() { - return db_; -} - -bool SQLTestBase::Reopen() { - db_.Close(); - return db_.Open(db_path()); -} - -bool SQLTestBase::GetPathExists(const base::FilePath& path) { - filesystem::FileError error = filesystem::FILE_ERROR_FAILED; - bool exists = false; - vfs_->GetDirectory()->Exists(path.AsUTF8Unsafe(), Capture(&error, &exists)); - vfs_->GetDirectory().WaitForIncomingResponse(); - if (error != filesystem::FILE_ERROR_OK) - return false; - return exists; -} - -bool SQLTestBase::CorruptSizeInHeaderOfDB() { - // See http://www.sqlite.org/fileformat.html#database_header - const size_t kHeaderSize = 100; - - mojo::Array<uint8_t> header; - - filesystem::FileError error = filesystem::FILE_ERROR_FAILED; - filesystem::FilePtr file_ptr; - vfs_->GetDirectory()->OpenFile( - mojo::String(db_path().AsUTF8Unsafe()), GetProxy(&file_ptr), - filesystem::kFlagRead | filesystem::kFlagWrite | - filesystem::kFlagOpenAlways, - Capture(&error)); - vfs_->GetDirectory().WaitForIncomingResponse(); - if (error != filesystem::FILE_ERROR_OK) - return false; - - file_ptr->Read(kHeaderSize, 0, filesystem::WHENCE_FROM_BEGIN, - Capture(&error, &header)); - file_ptr.WaitForIncomingResponse(); - if (error != filesystem::FILE_ERROR_OK) - return false; - - filesystem::FileInformationPtr info; - file_ptr->Stat(Capture(&error, &info)); - file_ptr.WaitForIncomingResponse(); - if (error != filesystem::FILE_ERROR_OK) - return false; - int64_t db_size = info->size; - - test::CorruptSizeInHeaderMemory(&header.front(), db_size); - - uint32_t num_bytes_written = 0; - file_ptr->Write(std::move(header), 0, filesystem::WHENCE_FROM_BEGIN, - Capture(&error, &num_bytes_written)); - file_ptr.WaitForIncomingResponse(); - if (error != filesystem::FILE_ERROR_OK) - return false; - if (num_bytes_written != kHeaderSize) - return false; - - return true; -} - -void SQLTestBase::WriteJunkToDatabase(WriteJunkType type) { - uint32_t flags = 0; - if (type == TYPE_OVERWRITE_AND_TRUNCATE) - flags = filesystem::kFlagWrite | filesystem::kFlagCreate; - else - flags = filesystem::kFlagWrite | filesystem::kFlagOpen; - - filesystem::FileError error = filesystem::FILE_ERROR_FAILED; - filesystem::FilePtr file_ptr; - vfs_->GetDirectory()->OpenFile( - mojo::String(db_path().AsUTF8Unsafe()), GetProxy(&file_ptr), - flags, - Capture(&error)); - vfs_->GetDirectory().WaitForIncomingResponse(); - if (error != filesystem::FILE_ERROR_OK) - return; - - const char* kJunk = "Now is the winter of our discontent."; - mojo::Array<uint8_t> data(strlen(kJunk)); - memcpy(&data.front(), kJunk, strlen(kJunk)); - - uint32_t num_bytes_written = 0; - file_ptr->Write(std::move(data), 0, filesystem::WHENCE_FROM_BEGIN, - Capture(&error, &num_bytes_written)); - file_ptr.WaitForIncomingResponse(); -} - -void SQLTestBase::TruncateDatabase() { - filesystem::FileError error = filesystem::FILE_ERROR_FAILED; - filesystem::FilePtr file_ptr; - vfs_->GetDirectory()->OpenFile( - mojo::String(db_path().AsUTF8Unsafe()), GetProxy(&file_ptr), - filesystem::kFlagWrite | filesystem::kFlagOpen, - Capture(&error)); - vfs_->GetDirectory().WaitForIncomingResponse(); - if (error != filesystem::FILE_ERROR_OK) - return; - - file_ptr->Truncate(0, Capture(&error)); - file_ptr.WaitForIncomingResponse(); - ASSERT_EQ(filesystem::FILE_ERROR_OK, error); -} - -void SQLTestBase::SetUp() { - ApplicationTestBase::SetUp(); - - application_impl()->ConnectToService("mojo:filesystem", &files_); - - filesystem::FileSystemClientPtr client; - binding_.Bind(GetProxy(&client)); - - filesystem::FileError error = filesystem::FILE_ERROR_FAILED; - filesystem::DirectoryPtr directory; - files()->OpenFileSystem("temp", GetProxy(&directory), std::move(client), - Capture(&error)); - ASSERT_TRUE(files().WaitForIncomingResponse()); - ASSERT_EQ(filesystem::FILE_ERROR_OK, error); - - vfs_.reset(new ScopedMojoFilesystemVFS(std::move(directory))); - ASSERT_TRUE(db_.Open(db_path())); -} - -void SQLTestBase::TearDown() { - db_.Close(); - vfs_.reset(); - - ApplicationTestBase::TearDown(); -} - -void SQLTestBase::OnFileSystemShutdown() { -} - -} // namespace sql diff --git a/chromium/sql/mojo/sql_test_base.h b/chromium/sql/mojo/sql_test_base.h deleted file mode 100644 index 2fba5aa7bc5..00000000000 --- a/chromium/sql/mojo/sql_test_base.h +++ /dev/null @@ -1,92 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef SQL_MOJO_SQL_TEST_BASE_H_ -#define SQL_MOJO_SQL_TEST_BASE_H_ - -#include "base/files/file_path.h" -#include "base/macros.h" -#include "base/memory/scoped_ptr.h" -#include "components/filesystem/public/interfaces/file_system.mojom.h" -#include "mojo/public/cpp/bindings/binding.h" -#include "mojo/shell/public/cpp/application_test_base.h" -#include "sql/connection.h" -#include "testing/gtest/include/gtest/gtest.h" - -namespace sql { - -class Connection; -class ScopedMojoFilesystemVFS; - -// Base class for SQL tests. -// -// WARNING: We want to run the same gtest based unit test code both against -// chromium (which uses this implementation here), and the mojo code (which -// uses a different class named SQLTestBase). These two classes need to have -// the same interface because we compile time switch them based on a -// #define. We need to have two different implementations because the mojo -// version derives from mojo::test::ApplicationTestBase instead of -// testing::Test. -class SQLTestBase : public mojo::test::ApplicationTestBase, - public filesystem::FileSystemClient { - public: - SQLTestBase(); - ~SQLTestBase() override; - - enum WriteJunkType { - TYPE_OVERWRITE_AND_TRUNCATE, - TYPE_OVERWRITE - }; - - // Returns the path to the database. - base::FilePath db_path(); - - // Returns a connection to the database at db_path(). - sql::Connection& db(); - - // Closes the current connection to the database and reopens it. - bool Reopen(); - - // Proxying method around base::PathExists. - bool GetPathExists(const base::FilePath& path); - - // SQLite stores the database size in the header, and if the actual - // OS-derived size is smaller, the database is considered corrupt. - // [This case is actually a common form of corruption in the wild.] - // This helper sets the in-header size to one page larger than the - // actual file size. The resulting file will return SQLITE_CORRUPT - // for most operations unless PRAGMA writable_schema is turned ON. - // - // Returns false if any error occurs accessing the file. - bool CorruptSizeInHeaderOfDB(); - - // Writes junk to the start of the file. - void WriteJunkToDatabase(WriteJunkType type); - - // Sets the database file size to 0. - void TruncateDatabase(); - - // Overridden from testing::Test: - void SetUp() override; - void TearDown() override; - - // Overridden from FileSystemClient: - void OnFileSystemShutdown() override; - - protected: - filesystem::FileSystemPtr& files() { return files_; } - - private: - filesystem::FileSystemPtr files_; - - scoped_ptr<ScopedMojoFilesystemVFS> vfs_; - mojo::Binding<filesystem::FileSystemClient> binding_; - sql::Connection db_; - - DISALLOW_COPY_AND_ASSIGN(SQLTestBase); -}; - -} // namespace sql - -#endif // SQL_MOJO_SQL_TEST_BASE_H_ diff --git a/chromium/sql/mojo/vfs_unittest.cc b/chromium/sql/mojo/vfs_unittest.cc deleted file mode 100644 index e51f1bd1e11..00000000000 --- a/chromium/sql/mojo/vfs_unittest.cc +++ /dev/null @@ -1,342 +0,0 @@ -// Copyright 2015 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include <stdint.h> -#include <memory> -#include <utility> - -#include "base/macros.h" -#include "components/filesystem/public/interfaces/file_system.mojom.h" -#include "mojo/shell/public/cpp/application_impl.h" -#include "mojo/shell/public/cpp/application_test_base.h" -#include "mojo/util/capture_util.h" -#include "sql/mojo/mojo_vfs.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "third_party/sqlite/sqlite3.h" - -namespace std { - -// This deleter lets us be safe with sqlite3 objects, which aren't really the -// structs, but slabs of new uint8_t[size]. -template <> -struct default_delete<sqlite3_file> { - inline void operator()(sqlite3_file* ptr) const { - // Why don't we call file->pMethods->xClose() here? Because it's not - // guaranteed to be valid. sqlite3_file "objects" can be in partially - // initialized states. - delete [] reinterpret_cast<uint8_t*>(ptr); - } -}; - -} // namespace std - -namespace sql { - -const char kFileName[] = "TestingDatabase.db"; - -class VFSTest : public mojo::test::ApplicationTestBase, - public filesystem::FileSystemClient { - public: - VFSTest() : binding_(this) {} - ~VFSTest() override {} - - sqlite3_vfs* vfs() { - return sqlite3_vfs_find(NULL); - } - - scoped_ptr<sqlite3_file> MakeFile() { - return scoped_ptr<sqlite3_file>(reinterpret_cast<sqlite3_file*>( - new uint8_t[vfs()->szOsFile])); - } - - void SetUp() override { - mojo::test::ApplicationTestBase::SetUp(); - - application_impl()->ConnectToService("mojo:filesystem", &files_); - - filesystem::FileSystemClientPtr client; - binding_.Bind(GetProxy(&client)); - - filesystem::FileError error = filesystem::FILE_ERROR_FAILED; - filesystem::DirectoryPtr directory; - files_->OpenFileSystem("temp", GetProxy(&directory), std::move(client), - mojo::Capture(&error)); - ASSERT_TRUE(files_.WaitForIncomingResponse()); - ASSERT_EQ(filesystem::FILE_ERROR_OK, error); - - vfs_.reset(new ScopedMojoFilesystemVFS(std::move(directory))); - } - - void TearDown() override { - vfs_.reset(); - mojo::test::ApplicationTestBase::TearDown(); - } - - void OnFileSystemShutdown() override { - } - - private: - filesystem::FileSystemPtr files_; - scoped_ptr<ScopedMojoFilesystemVFS> vfs_; - mojo::Binding<filesystem::FileSystemClient> binding_; - - DISALLOW_COPY_AND_ASSIGN(VFSTest); -}; - -TEST_F(VFSTest, TestInstalled) { - EXPECT_EQ("mojo", std::string(vfs()->zName)); -} - -TEST_F(VFSTest, ExclusiveOpen) { - // Opening a non-existent file exclusively should work. - scoped_ptr<sqlite3_file> file(MakeFile()); - int out_flags; - int rc = vfs()->xOpen(vfs(), kFileName, file.get(), - SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_CREATE, - &out_flags); - EXPECT_EQ(SQLITE_OK, rc); - - // Opening it a second time exclusively shouldn't. - scoped_ptr<sqlite3_file> file2(MakeFile()); - rc = vfs()->xOpen(vfs(), kFileName, file2.get(), - SQLITE_OPEN_EXCLUSIVE | SQLITE_OPEN_CREATE, - &out_flags); - EXPECT_NE(SQLITE_OK, rc); - - file->pMethods->xClose(file.get()); -} - -TEST_F(VFSTest, NonexclusiveOpen) { - // Opening a non-existent file should work. - scoped_ptr<sqlite3_file> file(MakeFile()); - int out_flags; - int rc = vfs()->xOpen(vfs(), kFileName, file.get(), - SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, - &out_flags); - EXPECT_EQ(SQLITE_OK, rc); - - // Opening it a second time should work. - scoped_ptr<sqlite3_file> file2(MakeFile()); - rc = vfs()->xOpen(vfs(), kFileName, file2.get(), - SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, - &out_flags); - EXPECT_EQ(SQLITE_OK, rc); - - file->pMethods->xClose(file.get()); - file->pMethods->xClose(file2.get()); -} - -TEST_F(VFSTest, NullFilenameOpen) { - // Opening a file with a null filename should return a valid file object. - scoped_ptr<sqlite3_file> file(MakeFile()); - int out_flags; - int rc = vfs()->xOpen( - vfs(), nullptr, file.get(), - SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, - &out_flags); - EXPECT_EQ(SQLITE_OK, rc); - - file->pMethods->xClose(file.get()); -} - -TEST_F(VFSTest, DeleteOnClose) { - { - scoped_ptr<sqlite3_file> file(MakeFile()); - int out_flags; - int rc = vfs()->xOpen( - vfs(), kFileName, file.get(), - SQLITE_OPEN_DELETEONCLOSE | SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, - &out_flags); - EXPECT_EQ(SQLITE_OK, rc); - file->pMethods->xClose(file.get()); - } - - // The file shouldn't exist now. - int result = 0; - vfs()->xAccess(vfs(), kFileName, SQLITE_ACCESS_EXISTS, &result); - EXPECT_FALSE(result); -} - -TEST_F(VFSTest, TestNonExistence) { - // We shouldn't have a file exist yet in a fresh directory. - int result = 0; - vfs()->xAccess(vfs(), kFileName, SQLITE_ACCESS_EXISTS, &result); - EXPECT_FALSE(result); -} - -TEST_F(VFSTest, TestExistence) { - { - scoped_ptr<sqlite3_file> file(MakeFile()); - int out_flags; - int rc = vfs()->xOpen(vfs(), kFileName, file.get(), - SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, - &out_flags); - EXPECT_EQ(SQLITE_OK, rc); - - file->pMethods->xClose(file.get()); - } - - int result = 0; - vfs()->xAccess(vfs(), kFileName, SQLITE_ACCESS_EXISTS, &result); - EXPECT_TRUE(result); -} - -TEST_F(VFSTest, TestDelete) { - { - scoped_ptr<sqlite3_file> file(MakeFile()); - int out_flags; - int rc = vfs()->xOpen(vfs(), kFileName, file.get(), - SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, - &out_flags); - EXPECT_EQ(SQLITE_OK, rc); - - file->pMethods->xClose(file.get()); - } - - int result = 0; - vfs()->xAccess(vfs(), kFileName, SQLITE_ACCESS_EXISTS, &result); - EXPECT_TRUE(result); - - vfs()->xDelete(vfs(), kFileName, 0); - - vfs()->xAccess(vfs(), kFileName, SQLITE_ACCESS_EXISTS, &result); - EXPECT_FALSE(result); -} - -TEST_F(VFSTest, TestWriteAndRead) { - const char kBuffer[] = "One Two Three Four Five Six Seven"; - const int kBufferSize = arraysize(kBuffer); - - { - scoped_ptr<sqlite3_file> file(MakeFile()); - int out_flags; - int rc = vfs()->xOpen(vfs(), kFileName, file.get(), - SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, - &out_flags); - EXPECT_EQ(SQLITE_OK, rc); - - for (int i = 0; i < 10; ++i) { - rc = file->pMethods->xWrite(file.get(), kBuffer, kBufferSize, - i * kBufferSize); - EXPECT_EQ(SQLITE_OK, rc); - } - - file->pMethods->xClose(file.get()); - } - - // Expect that the size of the file is 10 * arraysize(kBuffer); - { - scoped_ptr<sqlite3_file> file(MakeFile()); - int out_flags; - int rc = vfs()->xOpen(vfs(), kFileName, file.get(), - SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, - &out_flags); - EXPECT_EQ(SQLITE_OK, rc); - - sqlite_int64 size; - rc = file->pMethods->xFileSize(file.get(), &size); - EXPECT_EQ(SQLITE_OK, rc); - EXPECT_EQ(10 * kBufferSize, size); - - file->pMethods->xClose(file.get()); - } - - // We should be able to read things back. - { - scoped_ptr<sqlite3_file> file(MakeFile()); - int out_flags; - int rc = vfs()->xOpen(vfs(), kFileName, file.get(), - SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, - &out_flags); - EXPECT_EQ(SQLITE_OK, rc); - - char data_buffer[kBufferSize]; - memset(data_buffer, '8', kBufferSize); - for (int i = 0; i < 10; ++i) { - rc = file->pMethods->xRead(file.get(), data_buffer, kBufferSize, - i * kBufferSize); - EXPECT_EQ(SQLITE_OK, rc); - EXPECT_TRUE(strncmp(kBuffer, &data_buffer[0], kBufferSize) == 0); - } - - file->pMethods->xClose(file.get()); - } -} - -TEST_F(VFSTest, PartialRead) { - const char kBuffer[] = "One Two Three Four Five Six Seven"; - const int kBufferSize = arraysize(kBuffer); - - // Write the data once. - { - scoped_ptr<sqlite3_file> file(MakeFile()); - int out_flags; - int rc = vfs()->xOpen(vfs(), kFileName, file.get(), - SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, - &out_flags); - EXPECT_EQ(SQLITE_OK, rc); - - rc = file->pMethods->xWrite(file.get(), kBuffer, kBufferSize, 0); - EXPECT_EQ(SQLITE_OK, rc); - - file->pMethods->xClose(file.get()); - } - - // Now attempt to read kBufferSize + 5 from a file sized to kBufferSize. - { - scoped_ptr<sqlite3_file> file(MakeFile()); - int out_flags; - int rc = vfs()->xOpen(vfs(), kFileName, file.get(), - SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, - &out_flags); - EXPECT_EQ(SQLITE_OK, rc); - - const char kBufferWithFiveNulls[] = - "One Two Three Four Five Six Seven\0\0\0\0\0"; - const int kBufferWithFiveNullsSize = arraysize(kBufferWithFiveNulls); - - char data_buffer[kBufferWithFiveNullsSize]; - memset(data_buffer, '8', kBufferWithFiveNullsSize); - rc = file->pMethods->xRead(file.get(), data_buffer, - kBufferWithFiveNullsSize, 0); - EXPECT_EQ(SQLITE_IOERR_SHORT_READ, rc); - - EXPECT_TRUE(strncmp(kBufferWithFiveNulls, &data_buffer[0], - kBufferWithFiveNullsSize) == 0); - - file->pMethods->xClose(file.get()); - } -} - -TEST_F(VFSTest, Truncate) { - const char kBuffer[] = "One Two Three Four Five Six Seven"; - const int kBufferSize = arraysize(kBuffer); - const int kCharsToThree = 13; - - scoped_ptr<sqlite3_file> file(MakeFile()); - int out_flags; - int rc = vfs()->xOpen(vfs(), kFileName, file.get(), - SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, - &out_flags); - EXPECT_EQ(SQLITE_OK, rc); - - rc = file->pMethods->xWrite(file.get(), kBuffer, kBufferSize, 0); - EXPECT_EQ(SQLITE_OK, rc); - - sqlite_int64 size; - rc = file->pMethods->xFileSize(file.get(), &size); - EXPECT_EQ(SQLITE_OK, rc); - EXPECT_EQ(kBufferSize, size); - - rc = file->pMethods->xTruncate(file.get(), kCharsToThree); - EXPECT_EQ(SQLITE_OK, rc); - - rc = file->pMethods->xFileSize(file.get(), &size); - EXPECT_EQ(SQLITE_OK, rc); - EXPECT_EQ(kCharsToThree, size); - - file->pMethods->xClose(file.get()); -} - -} // namespace sql diff --git a/chromium/sql/recovery.cc b/chromium/sql/recovery.cc index 377aafbf1fb..7b9ca9f6fee 100644 --- a/chromium/sql/recovery.cc +++ b/chromium/sql/recovery.cc @@ -16,6 +16,7 @@ #include "sql/connection.h" #include "sql/statement.h" #include "third_party/sqlite/sqlite3.h" +#include "third_party/sqlite/src/src/recover.h" namespace sql { @@ -97,17 +98,23 @@ void RecordRecoveryEvent(RecoveryEventType recovery_event) { // static bool Recovery::FullRecoverySupported() { // TODO(shess): See comment in Init(). -#if defined(USE_SYSTEM_SQLITE) - return false; -#else return true; -#endif } // static scoped_ptr<Recovery> Recovery::Begin( Connection* connection, const base::FilePath& db_path) { + // Recovery is likely to be used in error handling. Since recovery changes + // the state of the handle, protect against multiple layers attempting the + // same recovery. + if (!connection->is_open()) { + // Warn about API mis-use. + DLOG_IF(FATAL, !connection->poisoned_) + << "Illegal to recover with closed database"; + return scoped_ptr<Recovery>(); + } + scoped_ptr<Recovery> r(new Recovery(connection)); if (!r->Init(db_path)) { // TODO(shess): Should Init() failure result in Raze()? @@ -192,16 +199,7 @@ bool Recovery::Init(const base::FilePath& db_path) { return false; } - // TODO(shess): Figure out a story for USE_SYSTEM_SQLITE. The - // virtual table implementation relies on SQLite internals for some - // types and functions, which could be copied inline to make it - // standalone. Or an alternate implementation could try to read - // through errors entirely at the SQLite level. - // - // For now, defer to the caller. The setup will succeed, but the - // later CREATE VIRTUAL TABLE call will fail, at which point the - // caller can fire Unrecoverable(). -#if !defined(USE_SYSTEM_SQLITE) + // Enable the recover virtual table for this connection. int rc = recoverVtableInit(recover_db_.db_); if (rc != SQLITE_OK) { RecordRecoveryEvent(RECOVERY_FAILED_VIRTUAL_TABLE_INIT); @@ -209,10 +207,6 @@ bool Recovery::Init(const base::FilePath& db_path) { << recover_db_.GetErrorMessage(); return false; } -#else - // If this is infrequent enough, just wire it to Raze(). - RecordRecoveryEvent(RECOVERY_FAILED_VIRTUAL_TABLE_SYSTEM_SQLITE); -#endif // Turn on |SQLITE_RecoveryMode| for the handle, which allows // reading certain broken databases. @@ -339,7 +333,6 @@ void Recovery::Shutdown(Recovery::Disposition raze) { } bool Recovery::AutoRecoverTable(const char* table_name, - size_t extend_columns, size_t* rows_recovered) { // Query the info for the recovered table in database [main]. std::string query( @@ -366,7 +359,6 @@ bool Recovery::AutoRecoverTable(const char* table_name, while (s.Step()) { const std::string column_name(s.ColumnString(1)); const std::string column_type(s.ColumnString(2)); - const bool not_null = s.ColumnBool(3); const int default_type = s.ColumnType(4); const bool default_is_null = (default_type == COLUMN_TYPE_NULL); const int pk_column = s.ColumnInt(5); @@ -413,16 +405,6 @@ bool Recovery::AutoRecoverTable(const char* table_name, return false; } - // If column has constraint "NOT NULL", then inserting NULL into - // that column will fail. If the column has a non-NULL DEFAULT - // specified, the INSERT will handle it (see below). If the - // DEFAULT is also NULL, the row must be filtered out. - // TODO(shess): The above scenario applies to INSERT OR REPLACE, - // whereas INSERT OR IGNORE drops such rows. - // http://www.sqlite.org/lang_conflict.html - if (not_null && default_is_null) - column_decl += " NOT NULL"; - create_column_decls.push_back(column_decl); // Per the NOTE in the header file, convert NULL values to the @@ -449,22 +431,16 @@ bool Recovery::AutoRecoverTable(const char* table_name, if (pk_column_count == 1 && !rowid_decl.empty()) create_column_decls[rowid_ofs] = rowid_decl; - // Additional columns accept anything. - // TODO(shess): ignoreN isn't well namespaced. But it will fail to - // execute in case of conflicts. - for (size_t i = 0; i < extend_columns; ++i) { - create_column_decls.push_back( - base::StringPrintf("ignore%" PRIuS " ANY", i)); - } - std::string recover_create(base::StringPrintf( "CREATE VIRTUAL TABLE temp.recover_%s USING recover(corrupt.%s, %s)", table_name, table_name, base::JoinString(create_column_decls, ",").c_str())); + // INSERT OR IGNORE means that it will drop rows resulting from constraint + // violations. INSERT OR REPLACE only handles UNIQUE constraint violations. std::string recover_insert(base::StringPrintf( - "INSERT OR REPLACE INTO main.%s SELECT %s FROM temp.recover_%s", + "INSERT OR IGNORE INTO main.%s SELECT %s FROM temp.recover_%s", table_name, base::JoinString(insert_columns, ",").c_str(), table_name)); diff --git a/chromium/sql/recovery.h b/chromium/sql/recovery.h index c03ebb2b391..af991081929 100644 --- a/chromium/sql/recovery.h +++ b/chromium/sql/recovery.h @@ -31,25 +31,25 @@ namespace sql { // // Create the schema to recover to. On failure, clear the // // database. // if (!r.db()->Execute(kCreateSchemaSql)) { -// sql::Recovery::Unrecoverable(r.Pass()); +// sql::Recovery::Unrecoverable(std::move(r)); // return; // } // // // Recover data in "mytable". // size_t rows_recovered = 0; // if (!r.AutoRecoverTable("mytable", 0, &rows_recovered)) { -// sql::Recovery::Unrecoverable(r.Pass()); +// sql::Recovery::Unrecoverable(std::move(r)); // return; // } // // // Manually cleanup additional constraints. // if (!r.db()->Execute(kCleanupSql)) { -// sql::Recovery::Unrecoverable(r.Pass()); +// sql::Recovery::Unrecoverable(std::move(r)); // return; // } // // // Commit the recovered data to the original database file. -// sql::Recovery::Recovered(r.Pass()); +// sql::Recovery::Recovered(std::move(r)); // } // } // @@ -115,12 +115,11 @@ class SQL_EXPORT Recovery { // Attempt to recover the named table from the corrupt database into // the recovery database using a temporary recover virtual table. // The virtual table schema is derived from the named table's schema - // in database [main]. Data is copied using INSERT OR REPLACE, so - // duplicates overwrite each other. + // in database [main]. Data is copied using INSERT OR IGNORE, so + // duplicates are dropped. // - // |extend_columns| allows recovering tables which have excess - // columns relative to the target schema. The recover virtual table - // treats more data than specified as a sign of corruption. + // If the source table has fewer columns than the target, the target + // DEFAULT value will be used for those columns. // // Returns true if all operations succeeded, with the number of rows // recovered in |*rows_recovered|. @@ -134,9 +133,7 @@ class SQL_EXPORT Recovery { // // TODO(shess): Flag for INSERT OR REPLACE vs IGNORE. // TODO(shess): Handle extended table names. - bool AutoRecoverTable(const char* table_name, - size_t extend_columns, - size_t* rows_recovered); + bool AutoRecoverTable(const char* table_name, size_t* rows_recovered); // Setup a recover virtual table at temp.recover_meta, reading from // corrupt.meta. Returns true if created. diff --git a/chromium/sql/recovery_unittest.cc b/chromium/sql/recovery_unittest.cc index f964eb9c051..d3b04b1eb90 100644 --- a/chromium/sql/recovery_unittest.cc +++ b/chromium/sql/recovery_unittest.cc @@ -67,9 +67,12 @@ std::string GetSchema(sql::Connection* db) { using SQLRecoveryTest = sql::SQLTestBase; +// Baseline sql::Recovery test covering the different ways to dispose of the +// scoped pointer received from sql::Recovery::Begin(). TEST_F(SQLRecoveryTest, RecoverBasic) { const char kCreateSql[] = "CREATE TABLE x (t TEXT)"; const char kInsertSql[] = "INSERT INTO x VALUES ('This is a test')"; + const char kAltInsertSql[] = "INSERT INTO x VALUES ('That was a test')"; ASSERT_TRUE(db().Execute(kCreateSql)); ASSERT_TRUE(db().Execute(kInsertSql)); ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); @@ -103,11 +106,26 @@ TEST_F(SQLRecoveryTest, RecoverBasic) { EXPECT_TRUE(db().is_open()); ASSERT_EQ("", GetSchema(&db())); + // Attempting to recover a previously-recovered handle fails early. + { + scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); + ASSERT_TRUE(recovery.get()); + recovery.reset(); + + recovery = sql::Recovery::Begin(&db(), db_path()); + ASSERT_FALSE(recovery.get()); + } + ASSERT_TRUE(Reopen()); + // Recreate the database. ASSERT_TRUE(db().Execute(kCreateSql)); ASSERT_TRUE(db().Execute(kInsertSql)); ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); + // Unrecovered table to distinguish from recovered database. + ASSERT_TRUE(db().Execute("CREATE TABLE y (c INTEGER)")); + ASSERT_NE("CREATE TABLE x (t TEXT)", GetSchema(&db())); + // Recovered() replaces the original with the "recovered" version. { scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); @@ -117,7 +135,6 @@ TEST_F(SQLRecoveryTest, RecoverBasic) { ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); // Insert different data to distinguish from original database. - const char kAltInsertSql[] = "INSERT INTO x VALUES ('That was a test')"; ASSERT_TRUE(recovery->db()->Execute(kAltInsertSql)); // Successfully recovered. @@ -131,12 +148,31 @@ TEST_F(SQLRecoveryTest, RecoverBasic) { const char* kXSql = "SELECT * FROM x ORDER BY 1"; ASSERT_EQ("That was a test", ExecuteWithResults(&db(), kXSql, "|", "\n")); -} -// The recovery virtual table is only supported for Chromium's SQLite. -#if !defined(USE_SYSTEM_SQLITE) + // Reset the database contents. + ASSERT_TRUE(db().Execute("DELETE FROM x")); + ASSERT_TRUE(db().Execute(kInsertSql)); + + // Rollback() discards recovery progress and leaves the database as it was. + { + scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); + ASSERT_TRUE(recovery.get()); + + ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); + ASSERT_TRUE(recovery->db()->Execute(kAltInsertSql)); + + sql::Recovery::Rollback(std::move(recovery)); + } + EXPECT_FALSE(db().is_open()); + ASSERT_TRUE(Reopen()); + EXPECT_TRUE(db().is_open()); + ASSERT_EQ("CREATE TABLE x (t TEXT)", GetSchema(&db())); -// Run recovery through its paces on a valid database. + ASSERT_EQ("This is a test", + ExecuteWithResults(&db(), kXSql, "|", "\n")); +} + +// Test operation of the virtual table used by sql::Recovery. TEST_F(SQLRecoveryTest, VirtualTable) { const char kCreateSql[] = "CREATE TABLE x (t TEXT)"; ASSERT_TRUE(db().Execute(kCreateSql)); @@ -178,6 +214,7 @@ TEST_F(SQLRecoveryTest, VirtualTable) { } void RecoveryCallback(sql::Connection* db, const base::FilePath& db_path, + const char* create_table, const char* create_index, int* record_error, int error, sql::Statement* stmt) { *record_error = error; @@ -187,23 +224,11 @@ void RecoveryCallback(sql::Connection* db, const base::FilePath& db_path, scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(db, db_path); ASSERT_TRUE(recovery.get()); - const char kRecoveryCreateSql[] = - "CREATE VIRTUAL TABLE temp.recover_x using recover(" - " corrupt.x," - " id INTEGER STRICT," - " v INTEGER STRICT" - ")"; - const char kCreateTable[] = "CREATE TABLE x (id INTEGER, v INTEGER)"; - const char kCreateIndex[] = "CREATE UNIQUE INDEX x_id ON x (id)"; - - // Replicate data over. - const char kRecoveryCopySql[] = - "INSERT OR REPLACE INTO x SELECT id, v FROM recover_x"; + ASSERT_TRUE(recovery->db()->Execute(create_table)); + ASSERT_TRUE(recovery->db()->Execute(create_index)); - ASSERT_TRUE(recovery->db()->Execute(kRecoveryCreateSql)); - ASSERT_TRUE(recovery->db()->Execute(kCreateTable)); - ASSERT_TRUE(recovery->db()->Execute(kCreateIndex)); - ASSERT_TRUE(recovery->db()->Execute(kRecoveryCopySql)); + size_t rows = 0; + ASSERT_TRUE(recovery->AutoRecoverTable("x", &rows)); ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); } @@ -242,8 +267,8 @@ TEST_F(SQLRecoveryTest, RecoverCorruptIndex) { ASSERT_TRUE(Reopen()); int error = SQLITE_OK; - db().set_error_callback(base::Bind(&RecoveryCallback, - &db(), db_path(), &error)); + db().set_error_callback(base::Bind(&RecoveryCallback, &db(), db_path(), + kCreateTable, kCreateIndex, &error)); // This works before the callback is called. const char kTrivialSql[] = "SELECT COUNT(*) FROM sqlite_master"; @@ -298,11 +323,12 @@ TEST_F(SQLRecoveryTest, RecoverCorruptTable) { const char kDeleteSql[] = "DELETE FROM x WHERE id = 0"; ASSERT_TRUE(sql::test::CorruptTableOrIndex(db_path(), "x", kDeleteSql)); - // TODO(shess): Figure out a query which causes SQLite to notice - // this organically. Meanwhile, just handle it manually. - ASSERT_TRUE(Reopen()); + int error = SQLITE_OK; + db().set_error_callback(base::Bind(&RecoveryCallback, &db(), db_path(), + kCreateTable, kCreateIndex, &error)); + // Index shows one less than originally inserted. const char kCountSql[] = "SELECT COUNT (*) FROM x"; EXPECT_EQ("9", ExecuteWithResults(&db(), kCountSql, "|", ",")); @@ -325,10 +351,13 @@ TEST_F(SQLRecoveryTest, RecoverCorruptTable) { const char kTrivialSql[] = "SELECT COUNT(*) FROM sqlite_master"; EXPECT_TRUE(db().IsSQLValid(kTrivialSql)); - // Call the recovery callback manually. - int error = SQLITE_OK; - RecoveryCallback(&db(), db_path(), &error, SQLITE_CORRUPT, NULL); - EXPECT_EQ(SQLITE_CORRUPT, error); + // TODO(shess): Figure out a statement which causes SQLite to notice the + // corruption. SELECT doesn't see errors because missing index values aren't + // visible. UPDATE or DELETE against v=0 don't see errors, even though the + // index item is missing. I suspect SQLite only deletes the key in these + // cases, but doesn't verify that one or more keys were deleted. + ASSERT_FALSE(db().Execute("INSERT INTO x (id, v) VALUES (0, 101)")); + EXPECT_EQ(SQLITE_CONSTRAINT_UNIQUE, error); // Database handle has been poisoned. EXPECT_FALSE(db().IsSQLValid(kTrivialSql)); @@ -339,9 +368,10 @@ TEST_F(SQLRecoveryTest, RecoverCorruptTable) { EXPECT_EQ("10", ExecuteWithResults(&db(), kCountSql, "|", ",")); EXPECT_EQ("10", ExecuteWithResults(&db(), kDistinctSql, "|", ",")); - // The expected value was retained. + // Only one of the values is retained. const char kSelectSql[] = "SELECT v FROM x WHERE id = 0"; - EXPECT_EQ("100", ExecuteWithResults(&db(), kSelectSql, "|", ",")); + const std::string results = ExecuteWithResults(&db(), kSelectSql, "|", ","); + EXPECT_TRUE(results=="100" || results=="0") << "Actual results: " << results; } TEST_F(SQLRecoveryTest, Meta) { @@ -418,7 +448,7 @@ TEST_F(SQLRecoveryTest, AutoRecoverTable) { ExecuteWithResults(recovery->db(), kTempSchemaSql, "|", "\n")); size_t rows = 0; - EXPECT_TRUE(recovery->AutoRecoverTable("x", 0, &rows)); + EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); EXPECT_EQ(2u, rows); // Test that any additional temp tables were cleaned up. @@ -441,7 +471,7 @@ TEST_F(SQLRecoveryTest, AutoRecoverTable) { // TODO(shess): Should this failure implicitly lead to Raze()? size_t rows = 0; - EXPECT_FALSE(recovery->AutoRecoverTable("y", 0, &rows)); + EXPECT_FALSE(recovery->AutoRecoverTable("y", &rows)); sql::Recovery::Unrecoverable(std::move(recovery)); } @@ -498,7 +528,7 @@ TEST_F(SQLRecoveryTest, AutoRecoverTableWithDefault) { ASSERT_TRUE(recovery->db()->Execute(final_schema.c_str())); size_t rows = 0; - EXPECT_TRUE(recovery->AutoRecoverTable("x", 0, &rows)); + EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); EXPECT_EQ(4u, rows); ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); @@ -534,7 +564,7 @@ TEST_F(SQLRecoveryTest, AutoRecoverTableNullFilter) { ASSERT_TRUE(recovery->db()->Execute(kFinalSchema)); size_t rows = 0; - EXPECT_TRUE(recovery->AutoRecoverTable("x", 0, &rows)); + EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); EXPECT_EQ(1u, rows); ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); @@ -573,7 +603,7 @@ TEST_F(SQLRecoveryTest, AutoRecoverTableWithRowid) { ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); size_t rows = 0; - EXPECT_TRUE(recovery->AutoRecoverTable("x", 0, &rows)); + EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); EXPECT_EQ(2u, rows); ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); @@ -618,7 +648,7 @@ TEST_F(SQLRecoveryTest, AutoRecoverTableWithCompoundKey) { ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); size_t rows = 0; - EXPECT_TRUE(recovery->AutoRecoverTable("x", 0, &rows)); + EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); EXPECT_EQ(3u, rows); ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); @@ -631,29 +661,40 @@ TEST_F(SQLRecoveryTest, AutoRecoverTableWithCompoundKey) { ASSERT_EQ(orig_data, ExecuteWithResults(&db(), kXSql, "|", "\n")); } -// Test |extend_columns| support. -TEST_F(SQLRecoveryTest, AutoRecoverTableExtendColumns) { +// Test recovering from a table with fewer columns than the target. +TEST_F(SQLRecoveryTest, AutoRecoverTableMissingColumns) { const char kCreateSql[] = "CREATE TABLE x (id INTEGER PRIMARY KEY, t0 TEXT)"; + const char kAlterSql[] = "ALTER TABLE x ADD COLUMN t1 TEXT DEFAULT 't'"; ASSERT_TRUE(db().Execute(kCreateSql)); ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (1, 'This is')")); ASSERT_TRUE(db().Execute("INSERT INTO x VALUES (2, 'That was')")); - // Save aside a copy of the original schema and data. + // Generate the expected info by faking a table to match what recovery will + // create. const std::string orig_schema(GetSchema(&db())); const char kXSql[] = "SELECT * FROM x ORDER BY 1"; - const std::string orig_data(ExecuteWithResults(&db(), kXSql, "|", "\n")); + std::string expected_schema; + std::string expected_data; + { + ASSERT_TRUE(db().BeginTransaction()); + ASSERT_TRUE(db().Execute(kAlterSql)); - // Modify the table to add a column, and add data to that column. - ASSERT_TRUE(db().Execute("ALTER TABLE x ADD COLUMN t1 TEXT")); - ASSERT_TRUE(db().Execute("UPDATE x SET t1 = 'a test'")); - ASSERT_NE(orig_schema, GetSchema(&db())); - ASSERT_NE(orig_data, ExecuteWithResults(&db(), kXSql, "|", "\n")); + expected_schema = GetSchema(&db()); + expected_data = ExecuteWithResults(&db(), kXSql, "|", "\n"); + db().RollbackTransaction(); + } + + // Following tests are pointless if the rollback didn't work. + ASSERT_EQ(orig_schema, GetSchema(&db())); + + // Recover the previous version of the table into the altered version. { scoped_ptr<sql::Recovery> recovery = sql::Recovery::Begin(&db(), db_path()); ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); + ASSERT_TRUE(recovery->db()->Execute(kAlterSql)); size_t rows = 0; - EXPECT_TRUE(recovery->AutoRecoverTable("x", 1, &rows)); + EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); EXPECT_EQ(2u, rows); ASSERT_TRUE(sql::Recovery::Recovered(std::move(recovery))); } @@ -661,8 +702,8 @@ TEST_F(SQLRecoveryTest, AutoRecoverTableExtendColumns) { // Since the database was not corrupt, the entire schema and all // data should be recovered. ASSERT_TRUE(Reopen()); - ASSERT_EQ(orig_schema, GetSchema(&db())); - ASSERT_EQ(orig_data, ExecuteWithResults(&db(), kXSql, "|", "\n")); + ASSERT_EQ(expected_schema, GetSchema(&db())); + ASSERT_EQ(expected_data, ExecuteWithResults(&db(), kXSql, "|", "\n")); } // Recover a golden file where an interior page has been manually modified so @@ -686,14 +727,13 @@ TEST_F(SQLRecoveryTest, Bug387868) { ASSERT_TRUE(recovery->db()->Execute(kCreateSql)); size_t rows = 0; - EXPECT_TRUE(recovery->AutoRecoverTable("x", 0, &rows)); + EXPECT_TRUE(recovery->AutoRecoverTable("x", &rows)); EXPECT_EQ(43u, rows); // Successfully recovered. EXPECT_TRUE(sql::Recovery::Recovered(std::move(recovery))); } } -#endif // !defined(USE_SYSTEM_SQLITE) // Memory-mapped I/O interacts poorly with I/O errors. Make sure the recovery // database doesn't accidentally enable it. diff --git a/chromium/sql/sql.gyp b/chromium/sql/sql.gyp index 2e02427d5cd..cde30576cb9 100644 --- a/chromium/sql/sql.gyp +++ b/chromium/sql/sql.gyp @@ -109,20 +109,23 @@ '..', ], 'conditions': [ - ['os_posix==1 and OS!="mac" and OS!="ios"', { - 'conditions': [ - ['use_allocator!="none"', { - 'dependencies': [ - '../base/allocator/allocator.gyp:allocator', - ], - }], - ], - }], ['OS == "android"', { 'dependencies': [ '../testing/android/native_test.gyp:native_test_native_code', ], }], + ['OS == "ios"', { + 'actions': [{ + 'action_name': 'copy_test_data', + 'variables': { + 'test_data_files': [ + 'test/data', + ], + 'test_data_prefix' : 'sql', + }, + 'includes': [ '../build/copy_test_data_ios.gypi' ], + }], + }], ], # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. 'msvs_disabled_warnings': [4267, ], diff --git a/chromium/sql/sql_unittests_apk.isolate b/chromium/sql/sql_unittests_apk.isolate index 7788b5c5500..412e3b8edd6 100644 --- a/chromium/sql/sql_unittests_apk.isolate +++ b/chromium/sql/sql_unittests_apk.isolate @@ -9,6 +9,7 @@ 'variables': { 'command': [ '<(PRODUCT_DIR)/bin/run_sql_unittests', + '--logcat-output-dir', '${ISOLATED_OUTDIR}/logcats', ], 'files': [ '../base/base.isolate', |