summaryrefslogtreecommitdiff
path: root/chromium/sql
diff options
context:
space:
mode:
authorAllan Sandfeld Jensen <allan.jensen@theqtcompany.com>2016-05-09 14:22:11 +0200
committerAllan Sandfeld Jensen <allan.jensen@qt.io>2016-05-09 15:11:45 +0000
commit2ddb2d3e14eef3de7dbd0cef553d669b9ac2361c (patch)
treee75f511546c5fd1a173e87c1f9fb11d7ac8d1af3 /chromium/sql
parenta4f3d46271c57e8155ba912df46a05559d14726e (diff)
downloadqtwebengine-chromium-2ddb2d3e14eef3de7dbd0cef553d669b9ac2361c.tar.gz
BASELINE: Update Chromium to 51.0.2704.41
Also adds in all smaller components by reversing logic for exclusion. Change-Id: Ibf90b506e7da088ea2f65dcf23f2b0992c504422 Reviewed-by: Joerg Bornemann <joerg.bornemann@theqtcompany.com>
Diffstat (limited to 'chromium/sql')
-rw-r--r--chromium/sql/BUILD.gn14
-rw-r--r--chromium/sql/connection.cc15
-rw-r--r--chromium/sql/connection_unittest.cc17
-rw-r--r--chromium/sql/mojo/BUILD.gn62
-rw-r--r--chromium/sql/mojo/DEPS6
-rw-r--r--chromium/sql/mojo/mojo_vfs.cc455
-rw-r--r--chromium/sql/mojo/mojo_vfs.h45
-rw-r--r--chromium/sql/mojo/sql_test_base.cc166
-rw-r--r--chromium/sql/mojo/sql_test_base.h92
-rw-r--r--chromium/sql/mojo/vfs_unittest.cc342
-rw-r--r--chromium/sql/recovery.cc54
-rw-r--r--chromium/sql/recovery.h21
-rw-r--r--chromium/sql/recovery_unittest.cc144
-rw-r--r--chromium/sql/sql.gyp21
-rw-r--r--chromium/sql/sql_unittests_apk.isolate1
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',