0

Add sql::Statement::ColumnStringView method

Analogous to sql::Statement::ColumnBlob returning a base::span, this
allows callers to avoid allocating a temporary std::string when the
value is immediately passed to a function accepting a string view.

For example, this allows sql::Statement::ColumnString16 to be
implemented more efficiently.

Change-Id: I7538d25aacf14849ddfc75b781b3e82f2d315483
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5854261
Commit-Queue: Andrew Paseltiner <apaseltiner@chromium.org>
Reviewed-by: Evan Stade <estade@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1354102}
This commit is contained in:
Andrew Paseltiner
2024-09-11 18:52:18 +00:00
committed by Chromium LUCI CQ
parent c36b30a5e6
commit 84724d909b
3 changed files with 39 additions and 26 deletions

@ -533,7 +533,7 @@ base::TimeDelta Statement::ColumnTimeDelta(int column_index) {
return base::Microseconds(int_value);
}
std::string Statement::ColumnString(int column_index) {
std::string_view Statement::ColumnStringView(int column_index) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#if DCHECK_IS_ON()
@ -542,7 +542,7 @@ std::string Statement::ColumnString(int column_index) {
#endif // DCHECK_IS_ON()
if (!CheckValid())
return std::string();
return std::string_view();
DCHECK_GE(column_index, 0);
DCHECK_LT(column_index, sqlite3_data_count(ref_->stmt()))
<< "Invalid column index";
@ -550,29 +550,18 @@ std::string Statement::ColumnString(int column_index) {
const char* string_buffer = reinterpret_cast<const char*>(
sqlite3_column_text(ref_->stmt(), column_index));
int size = sqlite3_column_bytes(ref_->stmt(), column_index);
DCHECK(size == 0 || string_buffer != nullptr)
<< "sqlite3_column_text() returned a null buffer for a non-empty string";
std::string result;
if (string_buffer && size > 0)
result.assign(string_buffer, size);
return result;
return std::string_view(string_buffer, base::checked_cast<size_t>(size));
}
std::string Statement::ColumnString(int column_index) {
return std::string(ColumnStringView(column_index));
}
std::u16string Statement::ColumnString16(int column_index) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
#if DCHECK_IS_ON()
DCHECK(!run_called_) << __func__ << " can be used after Step(), not Run()";
DCHECK(step_called_) << __func__ << " can only be used after Step()";
#endif // DCHECK_IS_ON()
if (!CheckValid())
return std::u16string();
DCHECK_GE(column_index, 0);
DCHECK_LT(column_index, sqlite3_data_count(ref_->stmt()))
<< "Invalid column index";
std::string string = ColumnString(column_index);
return string.empty() ? std::u16string() : base::UTF8ToUTF16(string);
return base::UTF8ToUTF16(ColumnStringView(column_index));
}
base::span<const uint8_t> Statement::ColumnBlob(int column_index) {

@ -203,6 +203,19 @@ class COMPONENT_EXPORT(SQL) Statement {
// `ColumnBlobAsString16()`.
std::u16string ColumnString16(int column_index);
// Returns a string view pointing to a buffer containing the string data.
//
// This can be used to avoid allocating a temporary string when the value is
// immediately passed to a function accepting a string view. Otherwise, the
// string view's contents should be copied to a caller-owned buffer
// immediately. Any method call on the `Statement` may invalidate the string
// view.
//
// The string view will be empty (and may have a null data) if the underlying
// string is empty. Code that needs to distinguish between empty strings and
// NULL should call `GetColumnType()` before calling `ColumnStringView()`.
std::string_view ColumnStringView(int column_index);
// Conforms with base::Time serialization recommendations.
//
// This is equivalent to the following snippets, which should be replaced.

@ -320,12 +320,23 @@ TEST_F(StatementTest, BindString) {
insert.Reset(/*clear_bound_vars=*/true);
}
Statement select(db_.GetUniqueStatement("SELECT t FROM texts ORDER BY id"));
for (const std::string& value : values) {
ASSERT_TRUE(select.Step());
EXPECT_EQ(value, select.ColumnString(0));
{
Statement select(db_.GetUniqueStatement("SELECT t FROM texts ORDER BY id"));
for (const std::string& value : values) {
ASSERT_TRUE(select.Step());
EXPECT_EQ(value, select.ColumnString(0));
}
EXPECT_FALSE(select.Step());
}
{
Statement select(db_.GetUniqueStatement("SELECT t FROM texts ORDER BY id"));
for (const std::string& value : values) {
ASSERT_TRUE(select.Step());
EXPECT_EQ(value, select.ColumnStringView(0));
}
EXPECT_FALSE(select.Step());
}
EXPECT_FALSE(select.Step());
}
TEST_F(StatementTest, BindString_NullData) {