0

[sql] Change DoesStuffExist() to work with ScopedErrorIgnorer.

This not working was preventing certain corruption-testing tests from
being written.

Also modified Does{Table,Index,Column}Exist() to reflect that these
names are _not_ case sensitive.  It is not possible to have distinct
tables [Foo] and [foo].

BUG=none

Review URL: https://codereview.chromium.org/1069313004

Cr-Commit-Position: refs/heads/master@{#324337}
This commit is contained in:
shess
2015-04-08 18:59:47 -07:00
committed by Commit bot
parent 9afe19122a
commit 92a2ab1f1d
3 changed files with 58 additions and 14 deletions

@ -725,7 +725,8 @@ scoped_refptr<Connection::StatementRef> Connection::GetUniqueStatement(
int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
// This is evidence of a syntax error in the incoming SQL.
DLOG(FATAL) << "SQL compile error " << GetErrorMessage();
DLOG_IF(FATAL, !ShouldIgnoreSqliteError(rc))
<< "SQL compile error " << GetErrorMessage();
// It could also be database corruption.
OnSqliteError(rc, NULL, sql);
@ -744,7 +745,8 @@ scoped_refptr<Connection::StatementRef> Connection::GetUntrackedStatement(
int rc = sqlite3_prepare_v2(db_, sql, -1, &stmt, NULL);
if (rc != SQLITE_OK) {
// This is evidence of a syntax error in the incoming SQL.
DLOG(FATAL) << "SQL compile error " << GetErrorMessage();
DLOG_IF(FATAL, !ShouldIgnoreSqliteError(rc))
<< "SQL compile error " << GetErrorMessage();
return new StatementRef(NULL, NULL, false);
}
return new StatementRef(NULL, stmt, true);
@ -798,8 +800,15 @@ bool Connection::DoesIndexExist(const char* index_name) const {
bool Connection::DoesTableOrIndexExist(
const char* name, const char* type) const {
const char* kSql = "SELECT name FROM sqlite_master WHERE type=? AND name=?";
const char* kSql =
"SELECT name FROM sqlite_master WHERE type=? AND name=? COLLATE NOCASE";
Statement statement(GetUntrackedStatement(kSql));
// This can happen if the database is corrupt and the error is being ignored
// for testing purposes.
if (!statement.is_valid())
return false;
statement.BindString(0, type);
statement.BindString(1, name);
@ -813,8 +822,14 @@ bool Connection::DoesColumnExist(const char* table_name,
sql.append(")");
Statement statement(GetUntrackedStatement(sql.c_str()));
// This can happen if the database is corrupt and the error is being ignored
// for testing purposes.
if (!statement.is_valid())
return false;
while (statement.Step()) {
if (!statement.ColumnString(1).compare(column_name))
if (!base::strcasecmp(statement.ColumnString(1).c_str(), column_name))
return true;
}
return false;

@ -361,10 +361,10 @@ class SQL_EXPORT Connection {
// Info querying -------------------------------------------------------------
// Returns true if the given table exists.
// Returns true if the given table (or index) exists. Instead of
// test-then-create, callers should almost always prefer "CREATE TABLE IF NOT
// EXISTS" or "CREATE INDEX IF NOT EXISTS".
bool DoesTableExist(const char* table_name) const;
// Returns true if the given index exists.
bool DoesIndexExist(const char* index_name) const;
// Returns true if a column with the given name exists in the given table.

@ -176,10 +176,9 @@ TEST_F(SQLConnectionTest, DoesStuffExist) {
// Test DoesTableExist.
EXPECT_FALSE(db().DoesTableExist("foo"));
ASSERT_TRUE(db().Execute("CREATE TABLE foo (a, b)"));
ASSERT_TRUE(db().Execute("CREATE INDEX foo_a ON foo (a)"));
EXPECT_TRUE(db().DoesTableExist("foo"));
// Should be case sensitive.
EXPECT_FALSE(db().DoesTableExist("FOO"));
EXPECT_TRUE(db().DoesIndexExist("foo_a"));
// Test DoesColumnExist.
EXPECT_FALSE(db().DoesColumnExist("foo", "bar"));
@ -187,6 +186,10 @@ TEST_F(SQLConnectionTest, DoesStuffExist) {
// Testing for a column on a nonexistent table.
EXPECT_FALSE(db().DoesColumnExist("bar", "b"));
// Names are not case sensitive.
EXPECT_TRUE(db().DoesTableExist("FOO"));
EXPECT_TRUE(db().DoesColumnExist("FOO", "A"));
}
TEST_F(SQLConnectionTest, GetLastInsertRowId) {
@ -221,10 +224,36 @@ TEST_F(SQLConnectionTest, ScopedIgnoreError) {
ASSERT_TRUE(db().Execute(kCreateSql));
ASSERT_TRUE(db().Execute("INSERT INTO foo (id) VALUES (12)"));
sql::ScopedErrorIgnorer ignore_errors;
ignore_errors.IgnoreError(SQLITE_CONSTRAINT);
ASSERT_FALSE(db().Execute("INSERT INTO foo (id) VALUES (12)"));
ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
{
sql::ScopedErrorIgnorer ignore_errors;
ignore_errors.IgnoreError(SQLITE_CONSTRAINT);
ASSERT_FALSE(db().Execute("INSERT INTO foo (id) VALUES (12)"));
ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
}
}
// Test that clients of GetUntrackedStatement() can test corruption-handling
// with ScopedErrorIgnorer.
TEST_F(SQLConnectionTest, ScopedIgnoreUntracked) {
const char* kCreateSql = "CREATE TABLE foo (id INTEGER UNIQUE)";
ASSERT_TRUE(db().Execute(kCreateSql));
ASSERT_FALSE(db().DoesTableExist("bar"));
ASSERT_TRUE(db().DoesTableExist("foo"));
ASSERT_TRUE(db().DoesColumnExist("foo", "id"));
db().Close();
// Corrupt the database so that nothing works, including PRAGMAs.
ASSERT_TRUE(sql::test::CorruptSizeInHeader(db_path()));
{
sql::ScopedErrorIgnorer ignore_errors;
ignore_errors.IgnoreError(SQLITE_CORRUPT);
ASSERT_TRUE(db().Open(db_path()));
ASSERT_FALSE(db().DoesTableExist("bar"));
ASSERT_FALSE(db().DoesTableExist("foo"));
ASSERT_FALSE(db().DoesColumnExist("foo", "id"));
ASSERT_TRUE(ignore_errors.CheckIgnoredErrors());
}
}
TEST_F(SQLConnectionTest, ErrorCallback) {