benm@chromium.org | 98b6f8b1 | 2012-02-10 13:31:59 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
brettw@chromium.org | e5ffd0e4 | 2009-09-11 21:30:56 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
tfarina@chromium.org | f0a54b2 | 2011-07-19 18:40:21 | [diff] [blame] | 5 | #ifndef SQL_STATEMENT_H_ |
| 6 | #define SQL_STATEMENT_H_ |
brettw@chromium.org | e5ffd0e4 | 2009-09-11 21:30:56 | [diff] [blame] | 7 | |
tfarina | 720d4f3 | 2015-05-11 22:31:26 | [diff] [blame] | 8 | #include <stdint.h> |
Victor Costan | f40a8757 | 2021-01-07 20:22:15 | [diff] [blame] | 9 | |
brettw@chromium.org | e5ffd0e4 | 2009-09-11 21:30:56 | [diff] [blame] | 10 | #include <string> |
| 11 | #include <vector> |
| 12 | |
Victor Costan | e56cc68 | 2018-12-27 01:53:46 | [diff] [blame] | 13 | #include "base/component_export.h" |
Victor Costan | 698ae0450 | 2021-07-08 07:31:09 | [diff] [blame] | 14 | #include "base/containers/span.h" |
Victor Costan | c27863df | 2021-07-14 22:17:52 | [diff] [blame] | 15 | #include "base/dcheck_is_on.h" |
levin@chromium.org | 3b63f8f4 | 2011-03-28 01:54:15 | [diff] [blame] | 16 | #include "base/memory/ref_counted.h" |
Victor Costan | 3a325b81 | 2018-07-23 22:16:18 | [diff] [blame] | 17 | #include "base/sequence_checker.h" |
Jan Wilken Dörrie | 9720dce | 2020-07-21 17:14:23 | [diff] [blame] | 18 | #include "base/strings/string_piece_forward.h" |
Victor Costan | b3793d6 | 2021-07-15 20:21:28 | [diff] [blame] | 19 | #include "base/thread_annotations.h" |
Victor Costan | ad6b011 | 2021-04-06 00:53:48 | [diff] [blame] | 20 | #include "base/time/time.h" |
Victor Costan | cfbfa60 | 2018-08-01 23:24:46 | [diff] [blame] | 21 | #include "sql/database.h" |
brettw@chromium.org | e5ffd0e4 | 2009-09-11 21:30:56 | [diff] [blame] | 22 | |
| 23 | namespace sql { |
| 24 | |
brettw@chromium.org | 765b4450 | 2009-10-02 05:01:42 | [diff] [blame] | 25 | // Possible return values from ColumnType in a statement. These should match |
| 26 | // the values in sqlite3.h. |
Victor Costan | 57aecd23 | 2019-04-04 09:09:57 | [diff] [blame] | 27 | enum class ColumnType { |
| 28 | kInteger = 1, |
| 29 | kFloat = 2, |
| 30 | kText = 3, |
| 31 | kBlob = 4, |
| 32 | kNull = 5, |
brettw@chromium.org | 765b4450 | 2009-10-02 05:01:42 | [diff] [blame] | 33 | }; |
| 34 | |
Victor Costan | f40a8757 | 2021-01-07 20:22:15 | [diff] [blame] | 35 | // Compiles and executes SQL statements. |
| 36 | // |
| 37 | // This class is not thread-safe. An instance must be accessed from a single |
| 38 | // sequence. This is enforced in DCHECK-enabled builds. |
| 39 | // |
brettw@chromium.org | e5ffd0e4 | 2009-09-11 21:30:56 | [diff] [blame] | 40 | // Normal usage: |
nick@chromium.org | 3273dce | 2010-01-27 16:08:08 | [diff] [blame] | 41 | // sql::Statement s(connection_.GetUniqueStatement(...)); |
brettw@chromium.org | e5ffd0e4 | 2009-09-11 21:30:56 | [diff] [blame] | 42 | // s.BindInt(0, a); |
| 43 | // if (s.Step()) |
| 44 | // return s.ColumnString(0); |
cpu@chromium.org | faa604e | 2009-09-25 22:38:59 | [diff] [blame] | 45 | // |
shess@chromium.org | eff1fa52 | 2011-12-12 23:50:59 | [diff] [blame] | 46 | // If there are errors getting the statement, the statement will be inert; no |
| 47 | // mutating or database-access methods will work. If you need to check for |
| 48 | // validity, use: |
| 49 | // if (!s.is_valid()) |
| 50 | // return false; |
| 51 | // |
cpu@chromium.org | faa604e | 2009-09-25 22:38:59 | [diff] [blame] | 52 | // Step() and Run() just return true to signal success. If you want to handle |
| 53 | // specific errors such as database corruption, install an error handler in |
| 54 | // in the connection object using set_error_delegate(). |
Victor Costan | e56cc68 | 2018-12-27 01:53:46 | [diff] [blame] | 55 | class COMPONENT_EXPORT(SQL) Statement { |
brettw@chromium.org | e5ffd0e4 | 2009-09-11 21:30:56 | [diff] [blame] | 56 | public: |
| 57 | // Creates an uninitialized statement. The statement will be invalid until |
| 58 | // you initialize it via Assign. |
| 59 | Statement(); |
| 60 | |
Victor Costan | cfbfa60 | 2018-08-01 23:24:46 | [diff] [blame] | 61 | explicit Statement(scoped_refptr<Database::StatementRef> ref); |
Victor Costan | 00c7643 | 2021-07-07 16:55:58 | [diff] [blame] | 62 | |
| 63 | Statement(const Statement&) = delete; |
| 64 | Statement& operator=(const Statement&) = delete; |
| 65 | |
brettw@chromium.org | e5ffd0e4 | 2009-09-11 21:30:56 | [diff] [blame] | 66 | ~Statement(); |
| 67 | |
| 68 | // Initializes this object with the given statement, which may or may not |
| 69 | // be valid. Use is_valid() to check if it's OK. |
Victor Costan | cfbfa60 | 2018-08-01 23:24:46 | [diff] [blame] | 70 | void Assign(scoped_refptr<Database::StatementRef> ref); |
brettw@chromium.org | e5ffd0e4 | 2009-09-11 21:30:56 | [diff] [blame] | 71 | |
Robert Ogden | 7b0e3630 | 2019-10-17 17:12:30 | [diff] [blame] | 72 | // Resets the statement to an uninitialized state corresponding to |
shess@chromium.org | 85fc27b0 | 2012-02-17 02:15:09 | [diff] [blame] | 73 | // the default constructor, releasing the StatementRef. |
| 74 | void Clear(); |
| 75 | |
brettw@chromium.org | e5ffd0e4 | 2009-09-11 21:30:56 | [diff] [blame] | 76 | // Returns true if the statement can be executed. All functions can still |
| 77 | // be used if the statement is invalid, but they will return failure or some |
| 78 | // default value. This is because the statement can become invalid in the |
gbillock@chromium.org | bed29d94 | 2011-12-22 19:25:51 | [diff] [blame] | 79 | // middle of executing a command if there is a serious error and the database |
brettw@chromium.org | e5ffd0e4 | 2009-09-11 21:30:56 | [diff] [blame] | 80 | // has to be reset. |
Victor Costan | f40a8757 | 2021-01-07 20:22:15 | [diff] [blame] | 81 | bool is_valid() const { |
Victor Costan | f40a8757 | 2021-01-07 20:22:15 | [diff] [blame] | 82 | DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
Victor Costan | f40a8757 | 2021-01-07 20:22:15 | [diff] [blame] | 83 | |
| 84 | return ref_->is_valid(); |
| 85 | } |
brettw@chromium.org | e5ffd0e4 | 2009-09-11 21:30:56 | [diff] [blame] | 86 | |
brettw@chromium.org | e5ffd0e4 | 2009-09-11 21:30:56 | [diff] [blame] | 87 | // Running ------------------------------------------------------------------- |
| 88 | |
| 89 | // Executes the statement, returning true on success. This is like Step but |
| 90 | // for when there is no output, like an INSERT statement. |
| 91 | bool Run(); |
| 92 | |
| 93 | // Executes the statement, returning true if there is a row of data returned. |
| 94 | // You can keep calling Step() until it returns false to iterate through all |
| 95 | // the rows in your result set. |
| 96 | // |
| 97 | // When Step returns false, the result is either that there is no more data |
| 98 | // or there is an error. This makes it most convenient for loop usage. If you |
| 99 | // need to disambiguate these cases, use Succeeded(). |
| 100 | // |
| 101 | // Typical example: |
| 102 | // while (s.Step()) { |
| 103 | // ... |
| 104 | // } |
| 105 | // return s.Succeeded(); |
| 106 | bool Step(); |
| 107 | |
michaelbai@chromium.org | 389e0a4 | 2012-04-25 21:36:41 | [diff] [blame] | 108 | // Resets the statement to its initial condition. This includes any current |
| 109 | // result row, and also the bound variables if the |clear_bound_vars| is true. |
| 110 | void Reset(bool clear_bound_vars); |
brettw@chromium.org | e5ffd0e4 | 2009-09-11 21:30:56 | [diff] [blame] | 111 | |
| 112 | // Returns true if the last executed thing in this statement succeeded. If |
| 113 | // there was no last executed thing or the statement is invalid, this will |
| 114 | // return false. |
| 115 | bool Succeeded() const; |
| 116 | |
| 117 | // Binding ------------------------------------------------------------------- |
| 118 | |
Victor Costan | 11e241f | 2021-07-14 04:31:18 | [diff] [blame] | 119 | // These all take a 0-based parameter index and return true on success. |
brettw@chromium.org | e5ffd0e4 | 2009-09-11 21:30:56 | [diff] [blame] | 120 | // strings there may be out of memory. |
Victor Costan | 11e241f | 2021-07-14 04:31:18 | [diff] [blame] | 121 | void BindNull(int param_index); |
| 122 | void BindBool(int param_index, bool val); |
| 123 | void BindInt(int param_index, int val); |
| 124 | void BindInt(int param_index, |
Victor Costan | 58980e05 | 2021-07-13 03:14:15 | [diff] [blame] | 125 | int64_t val) = delete; // Call BindInt64() instead. |
Victor Costan | 11e241f | 2021-07-14 04:31:18 | [diff] [blame] | 126 | void BindInt64(int param_index, int64_t val); |
| 127 | void BindDouble(int param_index, double val); |
| 128 | void BindCString(int param_index, const char* val); |
| 129 | void BindString(int param_index, base::StringPiece val); |
| 130 | void BindString16(int param_index, base::StringPiece16 value); |
| 131 | void BindBlob(int param_index, base::span<const uint8_t> value); |
Victor Costan | 698ae0450 | 2021-07-08 07:31:09 | [diff] [blame] | 132 | |
| 133 | // Overload that makes it easy to pass in std::string values. |
Victor Costan | 11e241f | 2021-07-14 04:31:18 | [diff] [blame] | 134 | void BindBlob(int param_index, base::span<const char> value) { |
| 135 | BindBlob(param_index, base::as_bytes(base::make_span(value))); |
Victor Costan | 698ae0450 | 2021-07-08 07:31:09 | [diff] [blame] | 136 | } |
brettw@chromium.org | e5ffd0e4 | 2009-09-11 21:30:56 | [diff] [blame] | 137 | |
Victor Costan | ad6b011 | 2021-04-06 00:53:48 | [diff] [blame] | 138 | // Conforms with base::Time serialization recommendations. |
| 139 | // |
| 140 | // This is equivalent to the following snippets, which should be replaced. |
| 141 | // * BindInt64(col, val.ToInternalValue()) |
| 142 | // * BindInt64(col, val.ToDeltaSinceWindowsEpoch().InMicroseconds()) |
| 143 | // |
| 144 | // Features that serialize base::Time in other ways, such as ToTimeT() or |
| 145 | // ToJavaTime(), will require a database migration to be converted to this |
| 146 | // (recommended) serialization method. |
| 147 | // |
| 148 | // TODO(crbug.com/1195962): Migrate all time serialization to this method, and |
| 149 | // then remove the migration details above. |
Victor Costan | 11e241f | 2021-07-14 04:31:18 | [diff] [blame] | 150 | void BindTime(int param_index, base::Time time); |
Victor Costan | ad6b011 | 2021-04-06 00:53:48 | [diff] [blame] | 151 | |
brettw@chromium.org | e5ffd0e4 | 2009-09-11 21:30:56 | [diff] [blame] | 152 | // Retrieving ---------------------------------------------------------------- |
| 153 | |
| 154 | // Returns the number of output columns in the result. |
| 155 | int ColumnCount() const; |
| 156 | |
brettw@chromium.org | 765b4450 | 2009-10-02 05:01:42 | [diff] [blame] | 157 | // Returns the type associated with the given column. |
| 158 | // |
| 159 | // Watch out: the type may be undefined if you've done something to cause a |
| 160 | // "type conversion." This means requesting the value of a column of a type |
| 161 | // where that type is not the native type. For safety, call ColumnType only |
| 162 | // on a column before getting the value out in any way. |
Victor Costan | 081d534 | 2021-07-15 14:23:59 | [diff] [blame] | 163 | ColumnType GetColumnType(int col); |
brettw@chromium.org | 765b4450 | 2009-10-02 05:01:42 | [diff] [blame] | 164 | |
brettw@chromium.org | e5ffd0e4 | 2009-09-11 21:30:56 | [diff] [blame] | 165 | // These all take a 0-based argument index. |
Victor Costan | 081d534 | 2021-07-15 14:23:59 | [diff] [blame] | 166 | bool ColumnBool(int column_index); |
| 167 | int ColumnInt(int column_index); |
| 168 | int64_t ColumnInt64(int column_index); |
| 169 | double ColumnDouble(int column_index); |
| 170 | std::string ColumnString(int column_index); |
| 171 | std::u16string ColumnString16(int column_index); |
brettw@chromium.org | e5ffd0e4 | 2009-09-11 21:30:56 | [diff] [blame] | 172 | |
Victor Costan | ad6b011 | 2021-04-06 00:53:48 | [diff] [blame] | 173 | // Conforms with base::Time serialization recommendations. |
| 174 | // |
| 175 | // This is equivalent to the following snippets, which should be replaced. |
| 176 | // * base::Time::FromInternalValue(ColumnInt64(col)) |
| 177 | // * base::Time::FromDeltaSinceWindowsEpoch( |
Peter Kasting | e5a38ed | 2021-10-02 03:06:35 | [diff] [blame] | 178 | // base::Microseconds(ColumnInt64(col))) |
Victor Costan | ad6b011 | 2021-04-06 00:53:48 | [diff] [blame] | 179 | // |
| 180 | // TODO(crbug.com/1195962): Migrate all time serialization to this method, and |
| 181 | // then remove the migration details above. |
Victor Costan | 081d534 | 2021-07-15 14:23:59 | [diff] [blame] | 182 | base::Time ColumnTime(int column_index); |
Victor Costan | ad6b011 | 2021-04-06 00:53:48 | [diff] [blame] | 183 | |
Victor Costan | 1268a999 | 2021-07-16 17:16:39 | [diff] [blame] | 184 | // Returns a span pointing to a buffer containing the blob data. |
| 185 | // |
| 186 | // The span's contents should be copied to a caller-owned buffer immediately. |
| 187 | // Any method call on the Statement may invalidate the span. |
| 188 | // |
| 189 | // The span will be empty (and may have a null data) if the underlying blob is |
| 190 | // empty. Code that needs to distinguish between empty blobs and NULL should |
| 191 | // call GetColumnType() before calling ColumnBlob(). |
| 192 | base::span<const uint8_t> ColumnBlob(int column_index); |
| 193 | |
Victor Costan | 081d534 | 2021-07-15 14:23:59 | [diff] [blame] | 194 | bool ColumnBlobAsString(int column_index, std::string* result); |
| 195 | bool ColumnBlobAsVector(int column_index, std::vector<char>* result); |
| 196 | bool ColumnBlobAsVector(int column_index, std::vector<uint8_t>* result); |
brettw@chromium.org | e5ffd0e4 | 2009-09-11 21:30:56 | [diff] [blame] | 197 | |
cpu@chromium.org | faa604e | 2009-09-25 22:38:59 | [diff] [blame] | 198 | // Diagnostics -------------------------------------------------------------- |
| 199 | |
| 200 | // Returns the original text of sql statement. Do not keep a pointer to it. |
| 201 | const char* GetSQLStatement(); |
| 202 | |
brettw@chromium.org | e5ffd0e4 | 2009-09-11 21:30:56 | [diff] [blame] | 203 | private: |
Victor Costan | cfbfa60 | 2018-08-01 23:24:46 | [diff] [blame] | 204 | friend class Database; |
shess | 58b8df8 | 2015-06-03 00:19:32 | [diff] [blame] | 205 | |
brettw@chromium.org | e5ffd0e4 | 2009-09-11 21:30:56 | [diff] [blame] | 206 | // This is intended to check for serious errors and report them to the |
Victor Costan | cfbfa60 | 2018-08-01 23:24:46 | [diff] [blame] | 207 | // Database object. It takes a sqlite error code, and returns the same |
brettw@chromium.org | e5ffd0e4 | 2009-09-11 21:30:56 | [diff] [blame] | 208 | // code. Currently this function just updates the succeeded flag, but will be |
| 209 | // enhanced in the future to do the notification. |
| 210 | int CheckError(int err); |
| 211 | |
shess@chromium.org | eff1fa52 | 2011-12-12 23:50:59 | [diff] [blame] | 212 | // Should be called by all mutating methods to check that the statement is |
| 213 | // valid. Returns true if the statement is valid. DCHECKS and returns false |
| 214 | // if it is not. |
| 215 | // The reason for this is to handle two specific cases in which a Statement |
| 216 | // may be invalid. The first case is that the programmer made an SQL error. |
| 217 | // Those cases need to be DCHECKed so that we are guaranteed to find them |
| 218 | // before release. The second case is that the computer has an error (probably |
| 219 | // out of disk space) which is prohibiting the correct operation of the |
| 220 | // database. Our testing apparatus should not exhibit this defect, but release |
| 221 | // situations may. Therefore, the code is handling disjoint situations in |
| 222 | // release and test. In test, we're ensuring correct SQL. In release, we're |
| 223 | // ensuring that contracts are honored in error edge cases. |
| 224 | bool CheckValid() const; |
| 225 | |
Victor Costan | 5e785e3 | 2019-02-26 20:39:31 | [diff] [blame] | 226 | // Helper for Run() and Step(), calls sqlite3_step() and returns the checked |
| 227 | // value from it. |
| 228 | int StepInternal(); |
shess | 58b8df8 | 2015-06-03 00:19:32 | [diff] [blame] | 229 | |
brettw@chromium.org | e5ffd0e4 | 2009-09-11 21:30:56 | [diff] [blame] | 230 | // The actual sqlite statement. This may be unique to us, or it may be cached |
Victor Costan | cfbfa60 | 2018-08-01 23:24:46 | [diff] [blame] | 231 | // by the Database, which is why it's ref-counted. This pointer is |
Victor Costan | bd62311 | 2018-07-18 04:17:27 | [diff] [blame] | 232 | // guaranteed non-null. |
Victor Costan | b3793d6 | 2021-07-15 20:21:28 | [diff] [blame] | 233 | scoped_refptr<Database::StatementRef> ref_ |
| 234 | GUARDED_BY_CONTEXT(sequence_checker_); |
brettw@chromium.org | e5ffd0e4 | 2009-09-11 21:30:56 | [diff] [blame] | 235 | |
| 236 | // See Succeeded() for what this holds. |
Victor Costan | b3793d6 | 2021-07-15 20:21:28 | [diff] [blame] | 237 | bool succeeded_ GUARDED_BY_CONTEXT(sequence_checker_) = false; |
brettw@chromium.org | e5ffd0e4 | 2009-09-11 21:30:56 | [diff] [blame] | 238 | |
Victor Costan | c27863df | 2021-07-14 22:17:52 | [diff] [blame] | 239 | #if DCHECK_IS_ON() |
| 240 | // Used to DCHECK() that Bind*() is called before Step() or Run() are called. |
Victor Costan | b3793d6 | 2021-07-15 20:21:28 | [diff] [blame] | 241 | bool step_called_ GUARDED_BY_CONTEXT(sequence_checker_) = false; |
| 242 | bool run_called_ GUARDED_BY_CONTEXT(sequence_checker_) = false; |
Victor Costan | c27863df | 2021-07-14 22:17:52 | [diff] [blame] | 243 | #endif // DCHECK_IS_ON() |
| 244 | |
Victor Costan | f40a8757 | 2021-01-07 20:22:15 | [diff] [blame] | 245 | SEQUENCE_CHECKER(sequence_checker_); |
brettw@chromium.org | e5ffd0e4 | 2009-09-11 21:30:56 | [diff] [blame] | 246 | }; |
| 247 | |
| 248 | } // namespace sql |
| 249 | |
tfarina@chromium.org | f0a54b2 | 2011-07-19 18:40:21 | [diff] [blame] | 250 | #endif // SQL_STATEMENT_H_ |