aboutsummaryrefslogtreecommitdiffhomepage
path: root/tensorflow/core/lib/db
diff options
context:
space:
mode:
authorGravatar Justine Tunney <jart@google.com>2017-10-28 23:45:55 -0700
committerGravatar TensorFlower Gardener <gardener@tensorflow.org>2017-10-28 23:49:36 -0700
commit325c8e5efa003bdc53f9605eb0b272075abc3565 (patch)
tree3a7bbf03a5d606b6c977c71904ff79458254100d /tensorflow/core/lib/db
parent0eba15fe6349ae2bd50b14496a1f283f462b0c66 (diff)
Improve C++ SQLite veneer
- Use shared_ptr for Sqlite - Don't need unique_ptr on SqliteStatement - Don't need db namespace - Include SQL in error statuses PiperOrigin-RevId: 173802267
Diffstat (limited to 'tensorflow/core/lib/db')
-rw-r--r--tensorflow/core/lib/db/BUILD1
-rw-r--r--tensorflow/core/lib/db/sqlite.cc50
-rw-r--r--tensorflow/core/lib/db/sqlite.h61
-rw-r--r--tensorflow/core/lib/db/sqlite_test.cc209
4 files changed, 210 insertions, 111 deletions
diff --git a/tensorflow/core/lib/db/BUILD b/tensorflow/core/lib/db/BUILD
index 367686c16a..41b7af1b69 100644
--- a/tensorflow/core/lib/db/BUILD
+++ b/tensorflow/core/lib/db/BUILD
@@ -12,6 +12,7 @@ cc_library(
srcs = ["sqlite.cc"],
hdrs = ["sqlite.h"],
deps = [
+ "//tensorflow/compiler/xla:statusor",
"//tensorflow/core:lib",
"@sqlite_archive//:sqlite",
],
diff --git a/tensorflow/core/lib/db/sqlite.cc b/tensorflow/core/lib/db/sqlite.cc
index 108be452a2..701655f622 100644
--- a/tensorflow/core/lib/db/sqlite.cc
+++ b/tensorflow/core/lib/db/sqlite.cc
@@ -18,14 +18,13 @@ limitations under the License.
#include "tensorflow/core/platform/logging.h"
namespace tensorflow {
-namespace db {
/* static */
-Status Sqlite::Open(const string& uri, std::unique_ptr<Sqlite>* db) {
+xla::StatusOr<std::shared_ptr<Sqlite>> Sqlite::Open(const string& uri) {
sqlite3* sqlite = nullptr;
Status s = MakeStatus(sqlite3_open(uri.c_str(), &sqlite));
if (s.ok()) {
- *db = std::unique_ptr<Sqlite>(new Sqlite(sqlite));
+ return std::shared_ptr<Sqlite>(new Sqlite(sqlite));
}
return s;
}
@@ -87,6 +86,9 @@ Sqlite::~Sqlite() {
}
Status Sqlite::Close() {
+ if (db_ == nullptr) {
+ return Status::OK();
+ }
// If Close is explicitly called, ordering must be correct.
Status s = MakeStatus(sqlite3_close(db_));
if (s.ok()) {
@@ -95,23 +97,42 @@ Status Sqlite::Close() {
return s;
}
-std::unique_ptr<SqliteStatement> Sqlite::Prepare(const string& sql) {
+SqliteStatement Sqlite::Prepare(const string& sql) {
sqlite3_stmt* stmt = nullptr;
int rc = sqlite3_prepare_v2(db_, sql.c_str(), sql.size() + 1, &stmt, nullptr);
- return std::unique_ptr<SqliteStatement>(new SqliteStatement(stmt, rc));
+ if (rc == SQLITE_OK) {
+ return {stmt, SQLITE_OK, std::unique_ptr<string>(nullptr)};
+ } else {
+ return {nullptr, rc, std::unique_ptr<string>(new string(sql))};
+ }
}
-SqliteStatement::SqliteStatement(sqlite3_stmt* stmt, int error)
- : stmt_(stmt), error_(error) {}
+Status SqliteStatement::status() const {
+ Status s = Sqlite::MakeStatus(error_);
+ if (!s.ok()) {
+ if (stmt_ != nullptr) {
+ errors::AppendToMessage(&s, sqlite3_sql(stmt_));
+ } else {
+ errors::AppendToMessage(&s, *prepare_error_sql_);
+ }
+ }
+ return s;
+}
-SqliteStatement::~SqliteStatement() {
- int rc = sqlite3_finalize(stmt_);
- if (rc != SQLITE_OK) {
- LOG(ERROR) << "destruct sqlite3_stmt: " << Sqlite::MakeStatus(rc);
+void SqliteStatement::CloseOrLog() {
+ if (stmt_ != nullptr) {
+ int rc = sqlite3_finalize(stmt_);
+ if (rc != SQLITE_OK) {
+ LOG(ERROR) << "destruct sqlite3_stmt: " << Sqlite::MakeStatus(rc);
+ }
+ stmt_ = nullptr;
}
}
Status SqliteStatement::Close() {
+ if (stmt_ == nullptr) {
+ return Status::OK();
+ }
int rc = sqlite3_finalize(stmt_);
if (rc == SQLITE_OK) {
stmt_ = nullptr;
@@ -121,8 +142,10 @@ Status SqliteStatement::Close() {
}
void SqliteStatement::Reset() {
- sqlite3_reset(stmt_);
- sqlite3_clear_bindings(stmt_);
+ if (TF_PREDICT_TRUE(stmt_ != nullptr)) {
+ sqlite3_reset(stmt_);
+ sqlite3_clear_bindings(stmt_); // not nullptr friendly
+ }
error_ = SQLITE_OK;
}
@@ -163,5 +186,4 @@ Status SqliteStatement::StepAndReset() {
return s;
}
-} // namespace db
} // namespace tensorflow
diff --git a/tensorflow/core/lib/db/sqlite.h b/tensorflow/core/lib/db/sqlite.h
index 316e938f1b..774852efea 100644
--- a/tensorflow/core/lib/db/sqlite.h
+++ b/tensorflow/core/lib/db/sqlite.h
@@ -17,15 +17,16 @@ limitations under the License.
#include <stddef.h>
#include <memory>
+#include <utility>
#include "sqlite3.h"
+#include "tensorflow/compiler/xla/statusor.h"
#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/core/status.h"
#include "tensorflow/core/platform/macros.h"
#include "tensorflow/core/platform/types.h"
namespace tensorflow {
-namespace db {
class SqliteStatement;
@@ -46,7 +47,7 @@ class Sqlite {
/// `file::memory:` for testing.
///
/// See https://sqlite.org/c3ref/open.html
- static Status Open(const string& uri, std::unique_ptr<Sqlite>* db);
+ static xla::StatusOr<std::shared_ptr<Sqlite>> Open(const string& uri);
/// \brief Makes tensorflow::Status for SQLite result code.
///
@@ -65,7 +66,7 @@ class Sqlite {
/// \brief Frees underlying SQLite object.
///
/// Unlike the destructor, all SqliteStatement objects must be closed
- /// beforehand.
+ /// beforehand. This is a no-op if already closed
Status Close();
/// \brief Creates SQLite statement.
@@ -74,7 +75,7 @@ class Sqlite {
/// failed. It is also possible to punt the error checking to after
/// the values have been binded and Step() or ExecuteWriteQuery() is
/// called.
- std::unique_ptr<SqliteStatement> Prepare(const string& sql);
+ SqliteStatement Prepare(const string& sql);
private:
explicit Sqlite(sqlite3* db);
@@ -89,21 +90,34 @@ class Sqlite {
/// Instances of this class are not thread safe.
class SqliteStatement {
public:
- /// \brief Destroys object and finalizes statement if needed.
- ~SqliteStatement();
+ /// \brief Constructs empty statement that should be assigned later.
+ SqliteStatement() : stmt_(nullptr), error_(SQLITE_OK) {}
+
+ /// \brief Empties object and finalizes statement if needed.
+ ~SqliteStatement() { CloseOrLog(); }
+
+ /// \brief Move constructor, after which <other> should not be used.
+ SqliteStatement(SqliteStatement&& other);
+
+ /// \brief Move assignment, after which <other> should not be used.
+ SqliteStatement& operator=(SqliteStatement&& other);
+
+ /// \brief Returns true if statement is not empty.
+ operator bool() const { return stmt_ != nullptr; }
/// \brief Returns SQLite result code state.
///
/// This will be SQLITE_OK unless an error happened. If multiple
/// errors happened, only the first error code will be returned.
- int error() { return error_; }
+ int error() const { return error_; }
/// \brief Returns error() as a tensorflow::Status.
- Status status() { return Sqlite::MakeStatus(error_); }
+ Status status() const;
/// \brief Finalize statement object.
///
- /// Please note that the destructor can also do this.
+ /// Please note that the destructor can also do this. This method is
+ /// a no-op if already closed.
Status Close();
/// \brief Executes query and/or fetches next row.
@@ -247,7 +261,12 @@ class SqliteStatement {
private:
friend Sqlite;
- SqliteStatement(sqlite3_stmt* stmt, int error); // takes ownership
+ SqliteStatement(sqlite3_stmt* stmt, int error,
+ std::unique_ptr<string> prepare_error_sql)
+ : stmt_(stmt),
+ error_(error),
+ prepare_error_sql_(std::move(prepare_error_sql)) {}
+ void CloseOrLog();
void Update(int rc) {
if (TF_PREDICT_FALSE(rc != SQLITE_OK)) {
@@ -268,11 +287,31 @@ class SqliteStatement {
sqlite3_stmt* stmt_;
int error_;
+ std::unique_ptr<string> prepare_error_sql_;
TF_DISALLOW_COPY_AND_ASSIGN(SqliteStatement);
};
-} // namespace db
+inline SqliteStatement::SqliteStatement(SqliteStatement&& other)
+ : stmt_(other.stmt_),
+ error_(other.error_),
+ prepare_error_sql_(std::move(other.prepare_error_sql_)) {
+ other.stmt_ = nullptr;
+ other.error_ = SQLITE_OK;
+}
+
+inline SqliteStatement& SqliteStatement::operator=(SqliteStatement&& other) {
+ if (&other != this) {
+ CloseOrLog();
+ stmt_ = other.stmt_;
+ error_ = other.error_;
+ prepare_error_sql_ = std::move(other.prepare_error_sql_);
+ other.stmt_ = nullptr;
+ other.error_ = SQLITE_OK;
+ }
+ return *this;
+}
+
} // namespace tensorflow
#endif // TENSORFLOW_CORE_LIB_DB_SQLITE_H_
diff --git a/tensorflow/core/lib/db/sqlite_test.cc b/tensorflow/core/lib/db/sqlite_test.cc
index ce22379d97..ba045274ad 100644
--- a/tensorflow/core/lib/db/sqlite_test.cc
+++ b/tensorflow/core/lib/db/sqlite_test.cc
@@ -24,97 +24,96 @@ limitations under the License.
#include "tensorflow/core/platform/test.h"
namespace tensorflow {
-namespace db {
namespace {
class SqliteTest : public ::testing::Test {
protected:
void SetUp() override {
- TF_ASSERT_OK(Sqlite::Open(":memory:", &db_));
+ db_ = Sqlite::Open(":memory:").ValueOrDie();
auto stmt = db_->Prepare("CREATE TABLE T (a BLOB, b BLOB)");
- TF_ASSERT_OK(stmt->StepAndReset());
+ TF_ASSERT_OK(stmt.StepAndReset());
}
- std::unique_ptr<Sqlite> db_;
+ std::shared_ptr<Sqlite> db_;
bool is_done_;
};
TEST_F(SqliteTest, InsertAndSelectInt) {
auto stmt = db_->Prepare("INSERT INTO T (a, b) VALUES (?, ?)");
- stmt->BindInt(1, 3);
- stmt->BindInt(2, -7);
- TF_ASSERT_OK(stmt->StepAndReset());
- stmt->BindInt(1, 123);
- stmt->BindInt(2, -123);
- TF_ASSERT_OK(stmt->StepAndReset());
+ stmt.BindInt(1, 3);
+ stmt.BindInt(2, -7);
+ TF_ASSERT_OK(stmt.StepAndReset());
+ stmt.BindInt(1, 123);
+ stmt.BindInt(2, -123);
+ TF_ASSERT_OK(stmt.StepAndReset());
stmt = db_->Prepare("SELECT a, b FROM T ORDER BY b");
- TF_ASSERT_OK(stmt->Step(&is_done_));
+ TF_ASSERT_OK(stmt.Step(&is_done_));
ASSERT_FALSE(is_done_);
- EXPECT_EQ(123, stmt->ColumnInt(0));
- EXPECT_EQ(-123, stmt->ColumnInt(1));
- TF_ASSERT_OK(stmt->Step(&is_done_));
+ EXPECT_EQ(123, stmt.ColumnInt(0));
+ EXPECT_EQ(-123, stmt.ColumnInt(1));
+ TF_ASSERT_OK(stmt.Step(&is_done_));
ASSERT_FALSE(is_done_);
- EXPECT_EQ(3, stmt->ColumnInt(0));
- EXPECT_EQ(-7, stmt->ColumnInt(1));
- TF_ASSERT_OK(stmt->Step(&is_done_));
+ EXPECT_EQ(3, stmt.ColumnInt(0));
+ EXPECT_EQ(-7, stmt.ColumnInt(1));
+ TF_ASSERT_OK(stmt.Step(&is_done_));
ASSERT_TRUE(is_done_);
}
TEST_F(SqliteTest, InsertAndSelectDouble) {
auto stmt = db_->Prepare("INSERT INTO T (a, b) VALUES (?, ?)");
- stmt->BindDouble(1, 6.28318530);
- stmt->BindDouble(2, 1.61803399);
- TF_ASSERT_OK(stmt->StepAndReset());
+ stmt.BindDouble(1, 6.28318530);
+ stmt.BindDouble(2, 1.61803399);
+ TF_ASSERT_OK(stmt.StepAndReset());
stmt = db_->Prepare("SELECT a, b FROM T");
- TF_ASSERT_OK(stmt->Step(&is_done_));
- EXPECT_EQ(6.28318530, stmt->ColumnDouble(0));
- EXPECT_EQ(1.61803399, stmt->ColumnDouble(1));
- EXPECT_EQ(6, stmt->ColumnInt(0));
- EXPECT_EQ(1, stmt->ColumnInt(1));
+ TF_ASSERT_OK(stmt.Step(&is_done_));
+ EXPECT_EQ(6.28318530, stmt.ColumnDouble(0));
+ EXPECT_EQ(1.61803399, stmt.ColumnDouble(1));
+ EXPECT_EQ(6, stmt.ColumnInt(0));
+ EXPECT_EQ(1, stmt.ColumnInt(1));
}
TEST_F(SqliteTest, NulCharsInString) {
string s; // XXX: Want to write {2, '\0'} but not sure why not.
s.append(static_cast<size_t>(2), '\0');
auto stmt = db_->Prepare("INSERT INTO T (a, b) VALUES (?, ?)");
- stmt->BindBlob(1, s);
- stmt->BindText(2, s);
- TF_ASSERT_OK(stmt->StepAndReset());
+ stmt.BindBlob(1, s);
+ stmt.BindText(2, s);
+ TF_ASSERT_OK(stmt.StepAndReset());
stmt = db_->Prepare("SELECT a, b FROM T");
- TF_ASSERT_OK(stmt->Step(&is_done_));
- EXPECT_EQ(2, stmt->ColumnSize(0));
- EXPECT_EQ(2, stmt->ColumnString(0).size());
- EXPECT_EQ('\0', stmt->ColumnString(0).at(0));
- EXPECT_EQ('\0', stmt->ColumnString(0).at(1));
- EXPECT_EQ(2, stmt->ColumnSize(1));
- EXPECT_EQ(2, stmt->ColumnString(1).size());
- EXPECT_EQ('\0', stmt->ColumnString(1).at(0));
- EXPECT_EQ('\0', stmt->ColumnString(1).at(1));
+ TF_ASSERT_OK(stmt.Step(&is_done_));
+ EXPECT_EQ(2, stmt.ColumnSize(0));
+ EXPECT_EQ(2, stmt.ColumnString(0).size());
+ EXPECT_EQ('\0', stmt.ColumnString(0).at(0));
+ EXPECT_EQ('\0', stmt.ColumnString(0).at(1));
+ EXPECT_EQ(2, stmt.ColumnSize(1));
+ EXPECT_EQ(2, stmt.ColumnString(1).size());
+ EXPECT_EQ('\0', stmt.ColumnString(1).at(0));
+ EXPECT_EQ('\0', stmt.ColumnString(1).at(1));
}
TEST_F(SqliteTest, Unicode) {
string s = "要依法治国是赞美那些谁是公义的和惩罚恶人。 - 韩非";
auto stmt = db_->Prepare("INSERT INTO T (a, b) VALUES (?, ?)");
- stmt->BindBlob(1, s);
- stmt->BindText(2, s);
- TF_ASSERT_OK(stmt->StepAndReset());
+ stmt.BindBlob(1, s);
+ stmt.BindText(2, s);
+ TF_ASSERT_OK(stmt.StepAndReset());
stmt = db_->Prepare("SELECT a, b FROM T");
- TF_ASSERT_OK(stmt->Step(&is_done_));
- EXPECT_EQ(s, stmt->ColumnString(0));
- EXPECT_EQ(s, stmt->ColumnString(1));
+ TF_ASSERT_OK(stmt.Step(&is_done_));
+ EXPECT_EQ(s, stmt.ColumnString(0));
+ EXPECT_EQ(s, stmt.ColumnString(1));
}
TEST_F(SqliteTest, StepAndResetClearsBindings) {
auto stmt = db_->Prepare("INSERT INTO T (a, b) VALUES (?, ?)");
- stmt->BindInt(1, 1);
- stmt->BindInt(2, 123);
- TF_ASSERT_OK(stmt->StepAndReset());
- stmt->BindInt(1, 2);
- TF_ASSERT_OK(stmt->StepAndReset());
+ stmt.BindInt(1, 1);
+ stmt.BindInt(2, 123);
+ TF_ASSERT_OK(stmt.StepAndReset());
+ stmt.BindInt(1, 2);
+ TF_ASSERT_OK(stmt.StepAndReset());
stmt = db_->Prepare("SELECT b FROM T ORDER BY a");
- TF_ASSERT_OK(stmt->Step(&is_done_));
- EXPECT_EQ(123, stmt->ColumnInt(0));
- TF_ASSERT_OK(stmt->Step(&is_done_));
- EXPECT_EQ(SQLITE_NULL, stmt->ColumnType(0));
+ TF_ASSERT_OK(stmt.Step(&is_done_));
+ EXPECT_EQ(123, stmt.ColumnInt(0));
+ TF_ASSERT_OK(stmt.Step(&is_done_));
+ EXPECT_EQ(SQLITE_NULL, stmt.ColumnType(0));
}
TEST_F(SqliteTest, CloseBeforeFinalizeFails) {
@@ -128,71 +127,109 @@ TEST_F(SqliteTest, CloseBeforeFinalizeFails) {
// is designed to carry the first error state forward to Step().
TEST_F(SqliteTest, ErrorPuntingDoesNotReportLibraryAbuse) {
auto stmt = db_->Prepare("lol cat");
- EXPECT_FALSE(stmt->status().ok());
- EXPECT_EQ(SQLITE_ERROR, stmt->error());
- stmt->BindInt(1, 1);
- stmt->BindInt(2, 2);
- Status s = stmt->Step(&is_done_);
- EXPECT_EQ(SQLITE_ERROR, stmt->error()); // first error of several
+ EXPECT_FALSE(stmt.status().ok());
+ EXPECT_EQ(SQLITE_ERROR, stmt.error());
+ stmt.BindInt(1, 1);
+ stmt.BindInt(2, 2);
+ Status s = stmt.Step(&is_done_);
+ EXPECT_EQ(SQLITE_ERROR, stmt.error()); // first error of several
EXPECT_FALSE(s.ok());
}
TEST_F(SqliteTest, SafeBind) {
string s = "hello";
auto stmt = db_->Prepare("INSERT INTO T (a, b) VALUES (?, ?)");
- stmt->BindBlob(1, s);
- stmt->BindText(2, s);
+ stmt.BindBlob(1, s);
+ stmt.BindText(2, s);
s.at(0) = 'y';
- TF_ASSERT_OK(stmt->StepAndReset());
+ TF_ASSERT_OK(stmt.StepAndReset());
stmt = db_->Prepare("SELECT a, b FROM T");
- TF_ASSERT_OK(stmt->Step(&is_done_));
- EXPECT_EQ("hello", stmt->ColumnString(0));
- EXPECT_EQ("hello", stmt->ColumnString(1));
+ TF_ASSERT_OK(stmt.Step(&is_done_));
+ EXPECT_EQ("hello", stmt.ColumnString(0));
+ EXPECT_EQ("hello", stmt.ColumnString(1));
}
TEST_F(SqliteTest, UnsafeBind) {
string s = "hello";
auto stmt = db_->Prepare("INSERT INTO T (a, b) VALUES (?, ?)");
- stmt->BindBlobUnsafe(1, s);
- stmt->BindTextUnsafe(2, s);
+ stmt.BindBlobUnsafe(1, s);
+ stmt.BindTextUnsafe(2, s);
s.at(0) = 'y';
- TF_ASSERT_OK(stmt->StepAndReset());
+ TF_ASSERT_OK(stmt.StepAndReset());
stmt = db_->Prepare("SELECT a, b FROM T");
- TF_ASSERT_OK(stmt->Step(&is_done_));
- EXPECT_EQ("yello", stmt->ColumnString(0));
- EXPECT_EQ("yello", stmt->ColumnString(1));
+ TF_ASSERT_OK(stmt.Step(&is_done_));
+ EXPECT_EQ("yello", stmt.ColumnString(0));
+ EXPECT_EQ("yello", stmt.ColumnString(1));
}
TEST_F(SqliteTest, UnsafeColumn) {
auto stmt = db_->Prepare("INSERT INTO T (a, b) VALUES (?, ?)");
- stmt->BindInt(1, 1);
- stmt->BindText(2, "hello");
- TF_ASSERT_OK(stmt->StepAndReset());
- stmt->BindInt(1, 2);
- stmt->BindText(2, "there");
- TF_ASSERT_OK(stmt->StepAndReset());
+ stmt.BindInt(1, 1);
+ stmt.BindText(2, "hello");
+ TF_ASSERT_OK(stmt.StepAndReset());
+ stmt.BindInt(1, 2);
+ stmt.BindText(2, "there");
+ TF_ASSERT_OK(stmt.StepAndReset());
stmt = db_->Prepare("SELECT b FROM T ORDER BY a");
- TF_ASSERT_OK(stmt->Step(&is_done_));
- const char* p = stmt->ColumnStringUnsafe(0);
+ TF_ASSERT_OK(stmt.Step(&is_done_));
+ const char* p = stmt.ColumnStringUnsafe(0);
EXPECT_EQ('h', *p);
- TF_ASSERT_OK(stmt->Step(&is_done_));
+ TF_ASSERT_OK(stmt.Step(&is_done_));
// This will actually happen, but it's not safe to test this behavior.
// EXPECT_EQ('t', *p);
}
TEST_F(SqliteTest, NamedParameterBind) {
auto stmt = db_->Prepare("INSERT INTO T (a) VALUES (:a)");
- stmt->BindText(":a", "lol");
- TF_ASSERT_OK(stmt->StepAndReset());
+ stmt.BindText(":a", "lol");
+ TF_ASSERT_OK(stmt.StepAndReset());
stmt = db_->Prepare("SELECT COUNT(*) FROM T");
- TF_ASSERT_OK(stmt->Step(&is_done_));
- EXPECT_EQ(1, stmt->ColumnInt(0));
+ TF_ASSERT_OK(stmt.Step(&is_done_));
+ EXPECT_EQ(1, stmt.ColumnInt(0));
stmt = db_->Prepare("SELECT a FROM T");
- TF_ASSERT_OK(stmt->Step(&is_done_));
+ TF_ASSERT_OK(stmt.Step(&is_done_));
EXPECT_FALSE(is_done_);
- EXPECT_EQ("lol", stmt->ColumnString(0));
+ EXPECT_EQ("lol", stmt.ColumnString(0));
+}
+
+TEST_F(SqliteTest, Statement_DefaultConstructor) {
+ SqliteStatement stmt;
+ EXPECT_FALSE(stmt);
+ EXPECT_FALSE(stmt.StepAndReset().ok());
+ stmt = db_->Prepare("INSERT INTO T (a) VALUES (1)");
+ EXPECT_TRUE(stmt);
+ EXPECT_TRUE(stmt.StepAndReset().ok());
+}
+
+TEST_F(SqliteTest, Statement_MoveConstructor) {
+ SqliteStatement stmt{db_->Prepare("INSERT INTO T (a) VALUES (1)")};
+ EXPECT_TRUE(stmt.StepAndReset().ok());
+}
+
+TEST_F(SqliteTest, Statement_MoveAssignment) {
+ SqliteStatement stmt1 = db_->Prepare("INSERT INTO T (a) VALUES (1)");
+ SqliteStatement stmt2;
+ EXPECT_TRUE(stmt1.StepAndReset().ok());
+ EXPECT_FALSE(stmt2.StepAndReset().ok());
+ stmt2 = std::move(stmt1);
+ EXPECT_TRUE(stmt2.StepAndReset().ok());
+}
+
+TEST_F(SqliteTest, PrepareFailed) {
+ SqliteStatement s = db_->Prepare("SELECT");
+ EXPECT_FALSE(s.status().ok());
+ EXPECT_NE(string::npos, s.status().error_message().find("SELECT"));
+}
+
+TEST_F(SqliteTest, BindFailed) {
+ SqliteStatement s = db_->Prepare("INSERT INTO T (a) VALUES (123)");
+ EXPECT_TRUE(s.status().ok());
+ EXPECT_EQ("", s.status().error_message());
+ s.BindInt(1, 123);
+ EXPECT_FALSE(s.status().ok());
+ EXPECT_NE(string::npos,
+ s.status().error_message().find("INSERT INTO T (a) VALUES (123)"));
}
} // namespace
-} // namespace db
} // namespace tensorflow