sql: Use SQLite API instead of PRAGMA to check for column existence.
sql::Database::DoesColumnExist currently compiles and runs a "PRAGMA
table_info" [1] SQL query and iterates over the result looking for
the desired column name. This is more brittle and expensive than using
the the sqlite3_table_column_metadata() [2] API, which pulls schema data
directly, bypassing query compilation and execution.
This CL introduces a behavior change -- the previous PRAGMA table_info
implementation works for views [3, 4], whereas
sqlite3_table_column_metadata() only works for tables. The behavior
change is not visible, because Chrome currently uses views in a single
feature [5], namely extensions [6].
DoesColumnExist should only be used for migrations, which should operate
on concrete tables. In steady-state, each feature should have the
database schema migrated to the current version, so column existence is
known statically. So, removing support for views in DoesColumnExist() is
acceptable.
[1] https://www.sqlite.org/pragma.html#pragma_table_info
[2] https://www.sqlite.org/c3ref/table_column_metadata.html
[3] https://www.sqlite.org/matrix/pragma.html
[4] https://www.sqlite.org/matrix/matrix_dpragma.html#R-35224-32827-55933-44478-17553-06933-55583-57822
[5] https://cs.chromium.org/search/?q=file:%5C.cc+sql::+VIEW+case:yes
[6] https://cs.chromium.org/chromium/src/chrome/browser/extensions/activity_log/counting_policy.cc?q=VIEW+case:yes
Bug: 910955
Change-Id: I44474585e6bb2523b5cd91c76c3444058822e601
Reviewed-on: https://chromium-review.googlesource.com/c/1357825
Reviewed-by: Chris Mumford <[email protected]>
Commit-Queue: Victor Costan <[email protected]>
Cr-Commit-Position: refs/heads/master@{#614663}
diff --git a/sql/database.cc b/sql/database.cc
index 96689675..adf3b2c 100644
--- a/sql/database.cc
+++ b/sql/database.cc
@@ -1490,23 +1490,15 @@
bool Database::DoesColumnExist(const char* table_name,
const char* column_name) const {
- std::string sql("PRAGMA TABLE_INFO(");
- sql.append(table_name);
- sql.append(")");
-
- Statement statement(GetUntrackedStatement(sql.c_str()));
-
- // This can happen if the database is corrupt and the error is a test
- // expectation.
- if (!statement.is_valid())
- return false;
-
- while (statement.Step()) {
- if (base::EqualsCaseInsensitiveASCII(statement.ColumnString(1),
- column_name))
- return true;
- }
- return false;
+ // sqlite3_table_column_metadata uses out-params to return column definition
+ // details, such as the column type and whether it allows NULL values. These
+ // aren't needed to compute the current method's result, so we pass in nullptr
+ // for all the out-params.
+ int error = sqlite3_table_column_metadata(
+ db_, "main", table_name, column_name, /* pzDataType= */ nullptr,
+ /* pzCollSeq= */ nullptr, /* pNotNull= */ nullptr,
+ /* pPrimaryKey= */ nullptr, /* pAutoinc= */ nullptr);
+ return error == SQLITE_OK;
}
int64_t Database::GetLastInsertRowId() const {