Commit f40a8757 authored by Victor Costan's avatar Victor Costan Committed by Chromium LUCI CQ

sql: Add sequence checks to sql::Statement.

sql::Statement is not thread-safe. Add checks documenting this.

The checks are currently excluded on Android, because the Android
implementation of History uses sql::Statement in a thread-unsafe way --
see https://crbug.com/866218. Landing the checks on all other platforms
now is a bit ugly, but has the benefit of preventing new errors from
creeping into the codebase while the Android issues are figured out.

Bug: 863724
Change-Id: Ia0d087f40bd892c2ed084e3acfc3cf1ecf12e6af
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1137851
Commit-Queue: Victor Costan <pwnall@chromium.org>
Commit-Queue: Darwin Huang <huangdarwin@chromium.org>
Reviewed-by: default avatarDarwin Huang <huangdarwin@chromium.org>
Auto-Submit: Victor Costan <pwnall@chromium.org>
Cr-Commit-Position: refs/heads/master@{#841165}
parent 909a9407
...@@ -9,9 +9,11 @@ ...@@ -9,9 +9,11 @@
#include "base/logging.h" #include "base/logging.h"
#include "base/numerics/safe_conversions.h" #include "base/numerics/safe_conversions.h"
#include "base/sequence_checker.h"
#include "base/strings/string_piece_forward.h" #include "base/strings/string_piece_forward.h"
#include "base/strings/string_util.h" #include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h" #include "base/strings/utf_string_conversions.h"
#include "build/build_config.h" // TODO(crbug.com/866218): Remove this include.
#include "third_party/sqlite/sqlite3.h" #include "third_party/sqlite/sqlite3.h"
namespace sql { namespace sql {
...@@ -28,6 +30,10 @@ Statement::Statement(scoped_refptr<Database::StatementRef> ref) ...@@ -28,6 +30,10 @@ Statement::Statement(scoped_refptr<Database::StatementRef> ref)
: ref_(std::move(ref)) {} : ref_(std::move(ref)) {}
Statement::~Statement() { Statement::~Statement() {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // !defined(OS_ANDROID)
// Free the resources associated with this statement. We assume there's only // Free the resources associated with this statement. We assume there's only
// one statement active for a given sqlite3_stmt at any time, so this won't // one statement active for a given sqlite3_stmt at any time, so this won't
// mess with anything. // mess with anything.
...@@ -35,16 +41,28 @@ Statement::~Statement() { ...@@ -35,16 +41,28 @@ Statement::~Statement() {
} }
void Statement::Assign(scoped_refptr<Database::StatementRef> ref) { void Statement::Assign(scoped_refptr<Database::StatementRef> ref) {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // !defined(OS_ANDROID)
Reset(true); Reset(true);
ref_ = std::move(ref); ref_ = std::move(ref);
} }
void Statement::Clear() { void Statement::Clear() {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // !defined(OS_ANDROID)
Assign(base::MakeRefCounted<Database::StatementRef>(nullptr, nullptr, false)); Assign(base::MakeRefCounted<Database::StatementRef>(nullptr, nullptr, false));
succeeded_ = false; succeeded_ = false;
} }
bool Statement::CheckValid() const { bool Statement::CheckValid() const {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // !defined(OS_ANDROID)
// Allow operations to fail silently if a statement was invalidated // Allow operations to fail silently if a statement was invalidated
// because the database was closed by an error handler. // because the database was closed by an error handler.
DLOG_IF(FATAL, !ref_->was_valid()) DLOG_IF(FATAL, !ref_->was_valid())
...@@ -53,6 +71,10 @@ bool Statement::CheckValid() const { ...@@ -53,6 +71,10 @@ bool Statement::CheckValid() const {
} }
int Statement::StepInternal() { int Statement::StepInternal() {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
if (!CheckValid()) if (!CheckValid())
return SQLITE_ERROR; return SQLITE_ERROR;
...@@ -65,15 +87,27 @@ int Statement::StepInternal() { ...@@ -65,15 +87,27 @@ int Statement::StepInternal() {
} }
bool Statement::Run() { bool Statement::Run() {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
DCHECK(!stepped_); DCHECK(!stepped_);
return StepInternal() == SQLITE_DONE; return StepInternal() == SQLITE_DONE;
} }
bool Statement::Step() { bool Statement::Step() {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
return StepInternal() == SQLITE_ROW; return StepInternal() == SQLITE_ROW;
} }
void Statement::Reset(bool clear_bound_vars) { void Statement::Reset(bool clear_bound_vars) {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
base::Optional<base::ScopedBlockingCall> scoped_blocking_call; base::Optional<base::ScopedBlockingCall> scoped_blocking_call;
ref_->InitScopedBlockingCall(FROM_HERE, &scoped_blocking_call); ref_->InitScopedBlockingCall(FROM_HERE, &scoped_blocking_call);
if (is_valid()) { if (is_valid()) {
...@@ -97,38 +131,61 @@ void Statement::Reset(bool clear_bound_vars) { ...@@ -97,38 +131,61 @@ void Statement::Reset(bool clear_bound_vars) {
} }
bool Statement::Succeeded() const { bool Statement::Succeeded() const {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
return is_valid() && succeeded_; return is_valid() && succeeded_;
} }
bool Statement::BindNull(int col) { bool Statement::BindNull(int col) {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
DCHECK(!stepped_); DCHECK(!stepped_);
return is_valid() && CheckOk(sqlite3_bind_null(ref_->stmt(), col + 1)); return is_valid() && CheckOk(sqlite3_bind_null(ref_->stmt(), col + 1));
} }
bool Statement::BindBool(int col, bool val) { bool Statement::BindBool(int col, bool val) {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
return BindInt(col, val ? 1 : 0); return BindInt(col, val ? 1 : 0);
} }
bool Statement::BindInt(int col, int val) { bool Statement::BindInt(int col, int val) {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
DCHECK(!stepped_); DCHECK(!stepped_);
return is_valid() && CheckOk(sqlite3_bind_int(ref_->stmt(), col + 1, val)); return is_valid() && CheckOk(sqlite3_bind_int(ref_->stmt(), col + 1, val));
} }
bool Statement::BindInt64(int col, int64_t val) { bool Statement::BindInt64(int col, int64_t val) {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
DCHECK(!stepped_); DCHECK(!stepped_);
return is_valid() && CheckOk(sqlite3_bind_int64(ref_->stmt(), col + 1, val)); return is_valid() && CheckOk(sqlite3_bind_int64(ref_->stmt(), col + 1, val));
} }
bool Statement::BindDouble(int col, double val) { bool Statement::BindDouble(int col, double val) {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
DCHECK(!stepped_); DCHECK(!stepped_);
return is_valid() && CheckOk(sqlite3_bind_double(ref_->stmt(), col + 1, val)); return is_valid() && CheckOk(sqlite3_bind_double(ref_->stmt(), col + 1, val));
} }
bool Statement::BindCString(int col, const char* val) { bool Statement::BindCString(int col, const char* val) {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
DCHECK(!stepped_); DCHECK(!stepped_);
return is_valid() && CheckOk(sqlite3_bind_text(ref_->stmt(), col + 1, val, -1, return is_valid() && CheckOk(sqlite3_bind_text(ref_->stmt(), col + 1, val, -1,
...@@ -136,6 +193,9 @@ bool Statement::BindCString(int col, const char* val) { ...@@ -136,6 +193,9 @@ bool Statement::BindCString(int col, const char* val) {
} }
bool Statement::BindString(int col, const std::string& val) { bool Statement::BindString(int col, const std::string& val) {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
DCHECK(!stepped_); DCHECK(!stepped_);
return is_valid() && return is_valid() &&
...@@ -145,10 +205,17 @@ bool Statement::BindString(int col, const std::string& val) { ...@@ -145,10 +205,17 @@ bool Statement::BindString(int col, const std::string& val) {
} }
bool Statement::BindString16(int col, base::StringPiece16 value) { bool Statement::BindString16(int col, base::StringPiece16 value) {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
return BindString(col, base::UTF16ToUTF8(value)); return BindString(col, base::UTF16ToUTF8(value));
} }
bool Statement::BindBlob(int col, const void* val, int val_len) { bool Statement::BindBlob(int col, const void* val, int val_len) {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
DCHECK(!stepped_); DCHECK(!stepped_);
return is_valid() && CheckOk(sqlite3_bind_blob(ref_->stmt(), col + 1, val, return is_valid() && CheckOk(sqlite3_bind_blob(ref_->stmt(), col + 1, val,
...@@ -156,6 +223,10 @@ bool Statement::BindBlob(int col, const void* val, int val_len) { ...@@ -156,6 +223,10 @@ bool Statement::BindBlob(int col, const void* val, int val_len) {
} }
int Statement::ColumnCount() const { int Statement::ColumnCount() const {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
if (!is_valid()) if (!is_valid())
return 0; return 0;
return sqlite3_column_count(ref_->stmt()); return sqlite3_column_count(ref_->stmt());
...@@ -174,32 +245,56 @@ static_assert(static_cast<int>(ColumnType::kNull) == SQLITE_NULL, ...@@ -174,32 +245,56 @@ static_assert(static_cast<int>(ColumnType::kNull) == SQLITE_NULL,
"NULL mismatch"); "NULL mismatch");
ColumnType Statement::GetColumnType(int col) const { ColumnType Statement::GetColumnType(int col) const {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
return static_cast<enum ColumnType>(sqlite3_column_type(ref_->stmt(), col)); return static_cast<enum ColumnType>(sqlite3_column_type(ref_->stmt(), col));
} }
bool Statement::ColumnBool(int col) const { bool Statement::ColumnBool(int col) const {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
return static_cast<bool>(ColumnInt(col)); return static_cast<bool>(ColumnInt(col));
} }
int Statement::ColumnInt(int col) const { int Statement::ColumnInt(int col) const {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
if (!CheckValid()) if (!CheckValid())
return 0; return 0;
return sqlite3_column_int(ref_->stmt(), col); return sqlite3_column_int(ref_->stmt(), col);
} }
int64_t Statement::ColumnInt64(int col) const { int64_t Statement::ColumnInt64(int col) const {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
if (!CheckValid()) if (!CheckValid())
return 0; return 0;
return sqlite3_column_int64(ref_->stmt(), col); return sqlite3_column_int64(ref_->stmt(), col);
} }
double Statement::ColumnDouble(int col) const { double Statement::ColumnDouble(int col) const {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
if (!CheckValid()) if (!CheckValid())
return 0; return 0;
return sqlite3_column_double(ref_->stmt(), col); return sqlite3_column_double(ref_->stmt(), col);
} }
std::string Statement::ColumnString(int col) const { std::string Statement::ColumnString(int col) const {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
if (!CheckValid()) if (!CheckValid())
return std::string(); return std::string();
...@@ -214,6 +309,10 @@ std::string Statement::ColumnString(int col) const { ...@@ -214,6 +309,10 @@ std::string Statement::ColumnString(int col) const {
} }
base::string16 Statement::ColumnString16(int col) const { base::string16 Statement::ColumnString16(int col) const {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
if (!CheckValid()) if (!CheckValid())
return base::string16(); return base::string16();
...@@ -222,12 +321,20 @@ base::string16 Statement::ColumnString16(int col) const { ...@@ -222,12 +321,20 @@ base::string16 Statement::ColumnString16(int col) const {
} }
int Statement::ColumnByteLength(int col) const { int Statement::ColumnByteLength(int col) const {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
if (!CheckValid()) if (!CheckValid())
return 0; return 0;
return sqlite3_column_bytes(ref_->stmt(), col); return sqlite3_column_bytes(ref_->stmt(), col);
} }
const void* Statement::ColumnBlob(int col) const { const void* Statement::ColumnBlob(int col) const {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
if (!CheckValid()) if (!CheckValid())
return nullptr; return nullptr;
...@@ -235,6 +342,10 @@ const void* Statement::ColumnBlob(int col) const { ...@@ -235,6 +342,10 @@ const void* Statement::ColumnBlob(int col) const {
} }
bool Statement::ColumnBlobAsString(int col, std::string* blob) const { bool Statement::ColumnBlobAsString(int col, std::string* blob) const {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
if (!CheckValid()) if (!CheckValid())
return false; return false;
...@@ -249,6 +360,10 @@ bool Statement::ColumnBlobAsString(int col, std::string* blob) const { ...@@ -249,6 +360,10 @@ bool Statement::ColumnBlobAsString(int col, std::string* blob) const {
} }
bool Statement::ColumnBlobAsString16(int col, base::string16* val) const { bool Statement::ColumnBlobAsString16(int col, base::string16* val) const {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
if (!CheckValid()) if (!CheckValid())
return false; return false;
...@@ -262,6 +377,10 @@ bool Statement::ColumnBlobAsString16(int col, base::string16* val) const { ...@@ -262,6 +377,10 @@ bool Statement::ColumnBlobAsString16(int col, base::string16* val) const {
} }
bool Statement::ColumnBlobAsVector(int col, std::vector<char>* val) const { bool Statement::ColumnBlobAsVector(int col, std::vector<char>* val) const {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
val->clear(); val->clear();
if (!CheckValid()) if (!CheckValid())
...@@ -279,14 +398,26 @@ bool Statement::ColumnBlobAsVector(int col, std::vector<char>* val) const { ...@@ -279,14 +398,26 @@ bool Statement::ColumnBlobAsVector(int col, std::vector<char>* val) const {
bool Statement::ColumnBlobAsVector( bool Statement::ColumnBlobAsVector(
int col, int col,
std::vector<unsigned char>* val) const { std::vector<unsigned char>* val) const {
return ColumnBlobAsVector(col, reinterpret_cast< std::vector<char>* >(val)); #if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
return ColumnBlobAsVector(col, reinterpret_cast<std::vector<char>*>(val));
} }
const char* Statement::GetSQLStatement() { const char* Statement::GetSQLStatement() {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
return sqlite3_sql(ref_->stmt()); return sqlite3_sql(ref_->stmt());
} }
bool Statement::CheckOk(int err) const { bool Statement::CheckOk(int err) const {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
// Binding to a non-existent variable is evidence of a serious error. // Binding to a non-existent variable is evidence of a serious error.
// TODO(gbillock,shess): make this invalidate the statement so it // TODO(gbillock,shess): make this invalidate the statement so it
// can't wreak havoc. // can't wreak havoc.
...@@ -295,6 +426,10 @@ bool Statement::CheckOk(int err) const { ...@@ -295,6 +426,10 @@ bool Statement::CheckOk(int err) const {
} }
int Statement::CheckError(int err) { int Statement::CheckError(int err) {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // OS_ANDROID
// Please don't add DCHECKs here, OnSqliteError() already has them. // Please don't add DCHECKs here, OnSqliteError() already has them.
succeeded_ = (err == SQLITE_OK || err == SQLITE_ROW || err == SQLITE_DONE); succeeded_ = (err == SQLITE_OK || err == SQLITE_ROW || err == SQLITE_DONE);
if (!succeeded_ && ref_.get() && ref_->database()) if (!succeeded_ && ref_.get() && ref_->database())
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#define SQL_STATEMENT_H_ #define SQL_STATEMENT_H_
#include <stdint.h> #include <stdint.h>
#include <string> #include <string>
#include <vector> #include <vector>
...@@ -15,6 +16,7 @@ ...@@ -15,6 +16,7 @@
#include "base/sequence_checker.h" #include "base/sequence_checker.h"
#include "base/strings/string16.h" #include "base/strings/string16.h"
#include "base/strings/string_piece_forward.h" #include "base/strings/string_piece_forward.h"
#include "build/build_config.h" // TODO(crbug.com/866218): Remove this include.
#include "sql/database.h" #include "sql/database.h"
namespace sql { namespace sql {
...@@ -29,6 +31,11 @@ enum class ColumnType { ...@@ -29,6 +31,11 @@ enum class ColumnType {
kNull = 5, kNull = 5,
}; };
// Compiles and executes SQL statements.
//
// This class is not thread-safe. An instance must be accessed from a single
// sequence. This is enforced in DCHECK-enabled builds.
//
// Normal usage: // Normal usage:
// sql::Statement s(connection_.GetUniqueStatement(...)); // sql::Statement s(connection_.GetUniqueStatement(...));
// s.BindInt(0, a); // s.BindInt(0, a);
...@@ -66,7 +73,13 @@ class COMPONENT_EXPORT(SQL) Statement { ...@@ -66,7 +73,13 @@ class COMPONENT_EXPORT(SQL) Statement {
// default value. This is because the statement can become invalid in the // default value. This is because the statement can become invalid in the
// middle of executing a command if there is a serious error and the database // middle of executing a command if there is a serious error and the database
// has to be reset. // has to be reset.
bool is_valid() const { return ref_->is_valid(); } bool is_valid() const {
#if !defined(OS_ANDROID) // TODO(crbug.com/866218): Remove this conditional
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#endif // !defined(OS_ANDROID)
return ref_->is_valid();
}
// Running ------------------------------------------------------------------- // Running -------------------------------------------------------------------
...@@ -195,6 +208,8 @@ class COMPONENT_EXPORT(SQL) Statement { ...@@ -195,6 +208,8 @@ class COMPONENT_EXPORT(SQL) Statement {
// See Succeeded() for what this holds. // See Succeeded() for what this holds.
bool succeeded_ = false; bool succeeded_ = false;
SEQUENCE_CHECKER(sequence_checker_);
DISALLOW_COPY_AND_ASSIGN(Statement); DISALLOW_COPY_AND_ASSIGN(Statement);
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment