1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef SQL_STATEMENT_H_
#define SQL_STATEMENT_H_
#include <stdint.h>
#include <string>
#include <vector>
#include "base/component_export.h"
#include "base/containers/span.h"
#include "base/dcheck_is_on.h"
#include "base/memory/ref_counted.h"
#include "base/sequence_checker.h"
#include "base/strings/string_piece_forward.h"
#include "base/thread_annotations.h"
#include "base/time/time.h"
#include "sql/database.h"
namespace sql {
enum class SqliteResultCode : int;
// Possible return values from ColumnType in a statement. These should match
// the values in sqlite3.h.
enum class ColumnType {
kInteger = 1,
kFloat = 2,
kText = 3,
kBlob = 4,
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:
// sql::Statement s(connection_.GetUniqueStatement(...));
// s.BindInt(0, a);
// if (s.Step())
// return s.ColumnString(0);
//
// If there are errors getting the statement, the statement will be inert; no
// mutating or database-access methods will work. If you need to check for
// validity, use:
// if (!s.is_valid())
// return false;
//
// Step() and Run() just return true to signal success. If you want to handle
// specific errors such as database corruption, install an error handler in
// in the connection object using set_error_delegate().
class COMPONENT_EXPORT(SQL) Statement {
public:
// Creates an uninitialized statement. The statement will be invalid until
// you initialize it via Assign.
Statement();
explicit Statement(scoped_refptr<Database::StatementRef> ref);
Statement(const Statement&) = delete;
Statement& operator=(const Statement&) = delete;
Statement(Statement&&) = delete;
Statement& operator=(Statement&&) = delete;
~Statement();
// Initializes this object with the given statement, which may or may not
// be valid. Use is_valid() to check if it's OK.
void Assign(scoped_refptr<Database::StatementRef> ref);
// Resets the statement to an uninitialized state corresponding to
// the default constructor, releasing the StatementRef.
void Clear();
// Returns true if the statement can be executed. All functions can still
// be used if the statement is invalid, but they will return failure or some
// 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
// has to be reset.
bool is_valid() const {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
return ref_->is_valid();
}
// Running -------------------------------------------------------------------
// Executes the statement, returning true on success. This is like Step but
// for when there is no output, like an INSERT statement.
bool Run();
// Executes the statement, returning true if there is a row of data returned.
// You can keep calling Step() until it returns false to iterate through all
// the rows in your result set.
//
// When Step returns false, the result is either that there is no more data
// or there is an error. This makes it most convenient for loop usage. If you
// need to disambiguate these cases, use Succeeded().
//
// Typical example:
// while (s.Step()) {
// ...
// }
// return s.Succeeded();
bool Step();
// Resets the statement to its initial condition. This includes any current
// result row, and also the bound variables if the |clear_bound_vars| is true.
void Reset(bool clear_bound_vars);
// Returns true if the last executed thing in this statement succeeded. If
// there was no last executed thing or the statement is invalid, this will
// return false.
bool Succeeded() const;
// Binding -------------------------------------------------------------------
// These all take a 0-based parameter index and return true on success.
// strings there may be out of memory.
void BindNull(int param_index);
void BindBool(int param_index, bool val);
void BindInt(int param_index, int val);
void BindInt(int param_index,
int64_t val) = delete; // Call BindInt64() instead.
void BindInt64(int param_index, int64_t val);
void BindDouble(int param_index, double val);
void BindCString(int param_index, const char* val);
void BindString(int param_index, base::StringPiece val);
// If you need to store (potentially invalid) UTF-16 strings losslessly,
// store them as BLOBs instead. `BindBlob()` has an overload for this purpose.
void BindString16(int param_index, base::StringPiece16 value);
void BindBlob(int param_index, base::span<const uint8_t> value);
// Overload that makes it easy to pass in std::string values.
void BindBlob(int param_index, base::span<const char> value) {
BindBlob(param_index, base::as_bytes(base::make_span(value)));
}
// Overload that makes it easy to pass in std::u16string values.
void BindBlob(int param_index, base::span<const char16_t> value) {
BindBlob(param_index, base::as_bytes(base::make_span(value)));
}
// Conforms with base::Time serialization recommendations.
//
// This is equivalent to the following snippets, which should be replaced.
// * BindInt64(col, val.ToInternalValue())
// * BindInt64(col, val.ToDeltaSinceWindowsEpoch().InMicroseconds())
//
// Features that serialize base::Time in other ways, such as ToTimeT() or
// InMillisecondsSinceUnixEpoch(), will require a database migration to be
// converted to this (recommended) serialization method.
//
// TODO(crbug.com/1195962): Migrate all time serialization to this method, and
// then remove the migration details above.
void BindTime(int param_index, base::Time time);
// Conforms with base::TimeDelta serialization recommendations.
//
// This is equivalent to the following snippets, which should be replaced.
// * BindInt64(col, delta.ToInternalValue())
// * BindInt64(col, delta.InMicroseconds())
//
// TODO(crbug.com/1402777): Migrate all TimeDelta serialization to this method
// and remove the migration details above.
void BindTimeDelta(int param_index, base::TimeDelta delta);
// Retrieving ----------------------------------------------------------------
// Returns the number of output columns in the result.
int ColumnCount() const;
// Returns the type associated with the given column.
//
// Watch out: the type may be undefined if you've done something to cause a
// "type conversion." This means requesting the value of a column of a type
// where that type is not the native type. For safety, call ColumnType only
// on a column before getting the value out in any way.
ColumnType GetColumnType(int col);
// These all take a 0-based argument index.
bool ColumnBool(int column_index);
int ColumnInt(int column_index);
int64_t ColumnInt64(int column_index);
double ColumnDouble(int column_index);
std::string ColumnString(int column_index);
// If you need to store and retrieve (potentially invalid) UTF-16 strings
// losslessly, store them as BLOBs instead. They may be retrieved with
// `ColumnBlobAsString16()`.
std::u16string ColumnString16(int column_index);
// Conforms with base::Time serialization recommendations.
//
// This is equivalent to the following snippets, which should be replaced.
// * base::Time::FromInternalValue(ColumnInt64(col))
// * base::Time::FromDeltaSinceWindowsEpoch(
// base::Microseconds(ColumnInt64(col)))
//
// TODO(crbug.com/1195962): Migrate all time serialization to this method, and
// then remove the migration details above.
base::Time ColumnTime(int column_index);
// Conforms with base::TimeDelta deserialization recommendations.
//
// This is equivalent to the following snippets, which should be replaced.
// * base::TimeDelta::FromInternalValue(ColumnInt64(column_index))
//
// TODO(crbug.com/1402777): Migrate all TimeDelta serialization to this method
// and remove the migration details above.
base::TimeDelta ColumnTimeDelta(int column_index);
// Returns a span pointing to a buffer containing the blob data.
//
// The span's contents should be copied to a caller-owned buffer immediately.
// Any method call on the Statement may invalidate the span.
//
// The span will be empty (and may have a null data) if the underlying blob is
// empty. Code that needs to distinguish between empty blobs and NULL should
// call GetColumnType() before calling ColumnBlob().
base::span<const uint8_t> ColumnBlob(int column_index);
bool ColumnBlobAsString(int column_index, std::string* result);
bool ColumnBlobAsString16(int column_index, std::u16string* result);
bool ColumnBlobAsVector(int column_index, std::vector<char>* result);
bool ColumnBlobAsVector(int column_index, std::vector<uint8_t>* result);
// Diagnostics --------------------------------------------------------------
// Returns the original text of a SQL statement WITHOUT any bound values.
// Intended for logging in case of failures. Note that DOES NOT return any
// bound values, because that would cause a privacy / PII issue for logging.
std::string GetSQLStatement();
private:
friend class Database;
// Checks SQLite result codes and handles any errors.
//
// Returns `sqlite_result_code`. This gives callers the convenience of writing
// "return CheckSqliteResultCode(sqlite_result_code)" and gives the compiler
// the opportunity of doing tail call optimization (TCO) on the code above.
//
// This method reports error codes to the associated Database, and updates
// internal state to reflect whether the statement succeeded or not.
SqliteResultCode CheckSqliteResultCode(SqliteResultCode sqlite_result_code);
// Should be called by all mutating methods to check that the statement is
// valid. Returns true if the statement is valid. DCHECKS and returns false
// if it is not.
// The reason for this is to handle two specific cases in which a Statement
// may be invalid. The first case is that the programmer made an SQL error.
// Those cases need to be DCHECKed so that we are guaranteed to find them
// before release. The second case is that the computer has an error (probably
// out of disk space) which is prohibiting the correct operation of the
// database. Our testing apparatus should not exhibit this defect, but release
// situations may. Therefore, the code is handling disjoint situations in
// release and test. In test, we're ensuring correct SQL. In release, we're
// ensuring that contracts are honored in error edge cases.
bool CheckValid() const;
// Helper for Run() and Step(), calls sqlite3_step() and returns the checked
// value from it.
SqliteResultCode StepInternal();
// The actual sqlite statement. This may be unique to us, or it may be cached
// by the Database, which is why it's ref-counted. This pointer is
// guaranteed non-null.
scoped_refptr<Database::StatementRef> ref_
GUARDED_BY_CONTEXT(sequence_checker_);
// See Succeeded() for what this holds.
bool succeeded_ GUARDED_BY_CONTEXT(sequence_checker_) = false;
#if DCHECK_IS_ON()
// Used to DCHECK() that Bind*() is called before Step() or Run() are called.
bool step_called_ GUARDED_BY_CONTEXT(sequence_checker_) = false;
bool run_called_ GUARDED_BY_CONTEXT(sequence_checker_) = false;
#endif // DCHECK_IS_ON()
SEQUENCE_CHECKER(sequence_checker_);
};
} // namespace sql
#endif // SQL_STATEMENT_H_
|