1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.database.sqlite; 18 19 import android.annotation.FlaggedApi; 20 import android.annotation.IntDef; 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 24 import com.android.internal.annotations.VisibleForTesting; 25 26 import dalvik.annotation.optimization.FastNative; 27 28 import java.io.Closeable; 29 import java.lang.annotation.Retention; 30 import java.lang.annotation.RetentionPolicy; 31 import java.lang.ref.Reference; 32 import java.util.Objects; 33 34 /** 35 * A {@link SQLiteRawStatement} represents a SQLite prepared statement. The methods correspond very 36 * closely to SQLite APIs that operate on a sqlite_stmt object. In general, each API in this class 37 * corresponds to a single SQLite API. 38 39 * <p> 40 * A {@link SQLiteRawStatement} must be created through a database, and there must be a 41 * transaction open at the time. Statements are implicitly closed when the outermost transaction 42 * ends, or if the current transaction is marked successful. Statements may be explicitly 43 * closed at any time with {@link #close}. The {@link #close} operation is idempotent and may be 44 * called multiple times without harm. 45 * <p> 46 * Multiple {@link SQLiteRawStatement}s may be open simultaneously. They are independent of each 47 * other. Closing one statement does not affect any other statement nor does it have any effect 48 * on the enclosing transaction. 49 * <p> 50 * Once a {@link SQLiteRawStatement} has been closed, no further database operations are 51 * permitted on that statement. An {@link IllegalStateException} will be thrown if a database 52 * operation is attempted on a closed statement. 53 * <p> 54 * All operations on a {@link SQLiteRawStatement} must be invoked from the thread that created 55 * it. A {@link IllegalStateException} will be thrown if cross-thread use is detected. 56 * <p> 57 * A common pattern for statements is try-with-resources. 58 * <code><pre> 59 * // Begin a transaction. 60 * database.beginTransaction(); 61 * try (SQLiteRawStatement statement = database.createRawStatement("SELECT * FROM ...")) { 62 * while (statement.step()) { 63 * // Fetch columns from the result rows. 64 * } 65 * database.setTransactionSuccessful(); 66 * } finally { 67 * database.endTransaction(); 68 * } 69 * </pre></code> 70 * Note that {@link SQLiteRawStatement} is unrelated to {@link SQLiteStatement}. 71 * 72 * @see <a href="http://sqlite.org/c3ref/stmt.html">sqlite3_stmt</a> 73 */ 74 @FlaggedApi(Flags.FLAG_SQLITE_APIS_35) 75 public final class SQLiteRawStatement implements Closeable { 76 77 private static final String TAG = "SQLiteRawStatement"; 78 79 /** 80 * The database for this object. 81 */ 82 private final SQLiteDatabase mDatabase; 83 84 /** 85 * The session for this object. 86 */ 87 private final SQLiteSession mSession; 88 89 /** 90 * The PreparedStatement associated with this object. This is returned to 91 * {@link SQLiteSession} when the object is closed. This also retains immutable attributes of 92 * the statement, like the parameter count. 93 */ 94 private SQLiteConnection.PreparedStatement mPreparedStatement; 95 96 /** 97 * The native statement associated with this object. This is pulled from the 98 * PreparedStatement for faster access. 99 */ 100 private final long mStatement; 101 102 /** 103 * The SQL string, for logging. 104 */ 105 private final String mSql; 106 107 /** 108 * The thread that created this object. The object is tied to a connection, which is tied to 109 * its session, which is tied to the thread. (The lifetime of this object is bounded by the 110 * lifetime of the enclosing transaction, so there are more rules than just the relationships 111 * in the second sentence.) This variable is set to null when the statement is closed. 112 */ 113 private Thread mThread; 114 115 /** 116 * The field types for SQLite columns. 117 * @hide 118 */ 119 @Retention(RetentionPolicy.SOURCE) 120 @IntDef(value = { 121 SQLITE_DATA_TYPE_INTEGER, 122 SQLITE_DATA_TYPE_FLOAT, 123 SQLITE_DATA_TYPE_TEXT, 124 SQLITE_DATA_TYPE_BLOB, 125 SQLITE_DATA_TYPE_NULL}) 126 public @interface SQLiteDataType {} 127 128 /** 129 * The constant returned by {@link #getColumnType} when the column value is SQLITE_INTEGER. 130 */ 131 public static final int SQLITE_DATA_TYPE_INTEGER = 1; 132 133 /** 134 * The constant returned by {@link #getColumnType} when the column value is SQLITE_FLOAT. 135 */ 136 public static final int SQLITE_DATA_TYPE_FLOAT = 2; 137 138 /** 139 * The constant returned by {@link #getColumnType} when the column value is SQLITE_TEXT. 140 */ 141 public static final int SQLITE_DATA_TYPE_TEXT = 3; 142 143 /** 144 * The constant returned by {@link #getColumnType} when the column value is SQLITE_BLOB. 145 */ 146 public static final int SQLITE_DATA_TYPE_BLOB = 4; 147 148 /** 149 * The constant returned by {@link #getColumnType} when the column value is SQLITE_NULL. 150 */ 151 public static final int SQLITE_DATA_TYPE_NULL = 5; 152 153 /** 154 * SQLite error codes that are used by this class. 155 */ 156 private static final int SQLITE_BUSY = 5; 157 private static final int SQLITE_LOCKED = 6; 158 private static final int SQLITE_ROW = 100; 159 private static final int SQLITE_DONE = 101; 160 161 /** 162 * Create the statement with empty bindings. The construtor will throw 163 * {@link IllegalStateException} if a transaction is not in progress. Clients should call 164 * {@link SQLiteDatabase.createRawStatement} to create a new instance. 165 */ SQLiteRawStatement(@onNull SQLiteDatabase db, @NonNull String sql)166 SQLiteRawStatement(@NonNull SQLiteDatabase db, @NonNull String sql) { 167 mThread = Thread.currentThread(); 168 mDatabase = db; 169 mSession = mDatabase.getThreadSession(); 170 mSession.throwIfNoTransaction(); 171 mSql = sql; 172 // Acquire a connection and prepare the statement. 173 mPreparedStatement = mSession.acquirePersistentStatement(mSql, this); 174 mStatement = mPreparedStatement.mStatementPtr; 175 } 176 177 /** 178 * Throw if the current session is not the session under which the object was created. Throw 179 * if the object has been closed. The actual check is that the current thread is not equal to 180 * the creation thread. 181 */ throwIfInvalid()182 private void throwIfInvalid() { 183 if (mThread != Thread.currentThread()) { 184 // Disambiguate the reasons for a mismatch. 185 if (mThread == null) { 186 throw new IllegalStateException("method called on a closed statement"); 187 } else { 188 throw new IllegalStateException("method called on a foreign thread: " + mThread); 189 } 190 } 191 } 192 193 /** 194 * Throw {@link IllegalArgumentException} if the length + offset are invalid with respect to 195 * the array length. 196 */ throwIfInvalidBounds(int arrayLength, int offset, int length)197 private void throwIfInvalidBounds(int arrayLength, int offset, int length) { 198 if (arrayLength < 0) { 199 throw new IllegalArgumentException("invalid array length " + arrayLength); 200 } 201 if (offset < 0 || offset >= arrayLength) { 202 throw new IllegalArgumentException("invalid offset " + offset 203 + " for array length " + arrayLength); 204 } 205 if (length <= 0 || ((arrayLength - offset) < length)) { 206 throw new IllegalArgumentException("invalid offset " + offset 207 + " and length " + length 208 + " for array length " + arrayLength); 209 } 210 } 211 212 /** 213 * Close the object and release any native resources. It is not an error to call this on an 214 * already-closed object. 215 */ 216 @Override close()217 public void close() { 218 if (mThread != null) { 219 // The object is known not to be closed, so this only throws if the caller is not in 220 // the creation thread. 221 throwIfInvalid(); 222 mSession.releasePersistentStatement(mPreparedStatement, this); 223 mThread = null; 224 } 225 } 226 227 /** 228 * Return true if the statement is still open and false otherwise. 229 * 230 * @return True if the statement is open. 231 */ isOpen()232 public boolean isOpen() { 233 return mThread != null; 234 } 235 236 /** 237 * Step to the next result row. This returns true if the statement stepped to a new row, and 238 * false if the statement is done. The method throws on any other result, including a busy or 239 * locked database. If WAL is enabled then the database should never be locked or busy. 240 * 241 * @see <a href="http://sqlite.org/c3ref/step.html">sqlite3_step</a> 242 * 243 * @return True if a row is available and false otherwise. 244 * @throws IllegalStateException if the statement is closed or this is a foreign thread. 245 * @throws SQLiteDatabaseLockedException if the database is locked or busy. 246 * @throws SQLiteException if a native error occurs. 247 */ step()248 public boolean step() { 249 throwIfInvalid(); 250 try { 251 int err = nativeStep(mStatement, true); 252 switch (err) { 253 case SQLITE_ROW: 254 return true; 255 case SQLITE_DONE: 256 return false; 257 case SQLITE_BUSY: 258 throw new SQLiteDatabaseLockedException("database " + mDatabase + " busy"); 259 case SQLITE_LOCKED: 260 throw new SQLiteDatabaseLockedException("database " + mDatabase + " locked"); 261 } 262 // This line of code should never be reached, because the native method should already 263 // have thrown an exception. 264 throw new SQLiteException("unknown error " + err); 265 } finally { 266 Reference.reachabilityFence(this); 267 } 268 } 269 270 /** 271 * Step to the next result. This returns the raw result code code from the native method. The 272 * expected values are SQLITE_ROW and SQLITE_DONE. For other return values, clients must 273 * decode the error and handle it themselves. http://sqlite.org/rescode.html for the current 274 * list of result codes. 275 * 276 * @return The native result code from the sqlite3_step() operation. 277 * @throws IllegalStateException if the statement is closed or this is a foreign thread. 278 * @hide 279 */ stepNoThrow()280 public int stepNoThrow() { 281 throwIfInvalid(); 282 try { 283 return nativeStep(mStatement, false); 284 } finally { 285 Reference.reachabilityFence(this); 286 } 287 } 288 289 /** 290 * Reset the statement. 291 * 292 * @see <a href="http://sqlite.org/c3ref/reset.html">sqlite3_reset</a> 293 * 294 * @throws IllegalStateException if the statement is closed or this is a foreign thread. 295 * @throws SQLiteException if a native error occurs. 296 */ reset()297 public void reset() { 298 throwIfInvalid(); 299 try { 300 nativeReset(mStatement, false); 301 } finally { 302 Reference.reachabilityFence(this); 303 } 304 } 305 306 /** 307 * Clear all parameter bindings. 308 * 309 * @see <a href="http://sqlite.org/c3ref/clear_bindings.html">sqlite3_clear_bindings</a> 310 * 311 * @throws IllegalStateException if the statement is closed or this is a foreign thread. 312 * @throws SQLiteException if a native error occurs. 313 */ clearBindings()314 public void clearBindings() { 315 throwIfInvalid(); 316 try { 317 nativeClearBindings(mStatement); 318 } finally { 319 Reference.reachabilityFence(this); 320 } 321 } 322 323 /** 324 * Return the number of parameters in the statement. 325 * 326 * @see 327 * <a href="http://sqlite.org/c3ref/bind_parameter_count.html">sqlite3_bind_parameter_count</a> 328 * 329 * @return The number of parameters in the statement. 330 * @throws IllegalStateException if the statement is closed or this is a foreign thread. 331 */ getParameterCount()332 public int getParameterCount() { 333 throwIfInvalid(); 334 try { 335 return nativeBindParameterCount(mStatement); 336 } finally { 337 Reference.reachabilityFence(this); 338 } 339 } 340 341 /** 342 * Return the index of the parameter with specified name. If the name does not match any 343 * parameter, 0 is returned. 344 * 345 * @see 346 * <a href="http://sqlite.org/c3ref/bind_parameter_index.html">sqlite3_bind_parameter_index</a> 347 * 348 * @param name The name of a parameter. 349 * @return The index of the parameter or 0 if the name does not identify a parameter. 350 * @throws IllegalStateException if the statement is closed or this is a foreign thread. 351 */ getParameterIndex(@onNull String name)352 public int getParameterIndex(@NonNull String name) { 353 Objects.requireNonNull(name); 354 throwIfInvalid(); 355 try { 356 return nativeBindParameterIndex(mStatement, name); 357 } finally { 358 Reference.reachabilityFence(this); 359 } 360 } 361 362 /** 363 * Return the name of the parameter at the specified index. Null is returned if there is no 364 * such parameter or if the parameter does not have a name. 365 * 366 * @see 367 * <a href="http://sqlite.org/c3ref/bind_parameter_name.html">sqlite3_bind_parameter_name</a> 368 * 369 * @param parameterIndex The index of the parameter. 370 * @return The name of the parameter. 371 * @throws IllegalStateException if the statement is closed or this is a foreign thread. 372 */ 373 @Nullable getParameterName(int parameterIndex)374 public String getParameterName(int parameterIndex) { 375 throwIfInvalid(); 376 try { 377 return nativeBindParameterName(mStatement, parameterIndex); 378 } finally { 379 Reference.reachabilityFence(this); 380 } 381 } 382 383 /** 384 * Bind a blob to a parameter. Parameter indices start at 1. The function throws if the 385 * parameter index is out of bounds. 386 * 387 * @see <a href="http://sqlite.org/c3ref/bind_blob.html">sqlite3_bind_blob</a> 388 * 389 * @param parameterIndex The index of the parameter in the query. It is one-based. 390 * @param value The value to be bound to the parameter. 391 * @throws IllegalStateException if the statement is closed or this is a foreign thread. 392 * @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range. 393 * @throws SQLiteException if a native error occurs. 394 */ bindBlob(int parameterIndex, @NonNull byte[] value)395 public void bindBlob(int parameterIndex, @NonNull byte[] value) { 396 Objects.requireNonNull(value); 397 throwIfInvalid(); 398 try { 399 nativeBindBlob(mStatement, parameterIndex, value, 0, value.length); 400 } finally { 401 Reference.reachabilityFence(this); 402 } 403 } 404 405 /** 406 * Bind a blob to a parameter. Parameter indices start at 1. The function throws if the 407 * parameter index is out of bounds. The sub-array value[offset] to value[offset+length-1] is 408 * bound. 409 * 410 * @see <a href="http://sqlite.org/c3ref/bind_blob.html">sqlite3_bind_blob</a> 411 * 412 * @param parameterIndex The index of the parameter in the query. It is one-based. 413 * @param value The value to be bound to the parameter. 414 * @param offset An offset into the value array 415 * @param length The number of bytes to bind from the value array. 416 * @throws IllegalStateException if the statement is closed or this is a foreign thread. 417 * @throws IllegalArgumentException if the sub-array exceeds the bounds of the value array. 418 * @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range. 419 * @throws SQLiteException if a native error occurs. 420 */ bindBlob(int parameterIndex, @NonNull byte[] value, int offset, int length)421 public void bindBlob(int parameterIndex, @NonNull byte[] value, int offset, int length) { 422 Objects.requireNonNull(value); 423 throwIfInvalid(); 424 throwIfInvalidBounds(value.length, offset, length); 425 try { 426 nativeBindBlob(mStatement, parameterIndex, value, offset, length); 427 } finally { 428 Reference.reachabilityFence(this); 429 } 430 } 431 432 /** 433 * Bind a double to a parameter. Parameter indices start at 1. The function throws if the 434 * parameter index is out of bounds. 435 * 436 * @see <a href="http://sqlite.org/c3ref/bind_blob.html">sqlite3_bind_double</a> 437 * 438 * @param parameterIndex The index of the parameter in the query. It is one-based. 439 * @param value The value to be bound to the parameter. 440 * @throws IllegalStateException if the statement is closed or this is a foreign thread. 441 * @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range. 442 * @throws SQLiteException if a native error occurs. 443 */ bindDouble(int parameterIndex, double value)444 public void bindDouble(int parameterIndex, double value) { 445 throwIfInvalid(); 446 try { 447 nativeBindDouble(mStatement, parameterIndex, value); 448 } finally { 449 Reference.reachabilityFence(this); 450 } 451 } 452 453 /** 454 * Bind an int to a parameter. Parameter indices start at 1. The function throws if the 455 * parameter index is out of bounds. 456 * 457 * @see <a href="http://sqlite.org/c3ref/bind_blob.html">sqlite3_bind_int</a> 458 * 459 * @param parameterIndex The index of the parameter in the query. It is one-based. 460 * @throws IllegalStateException if the statement is closed or this is a foreign thread. 461 * @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range. 462 * @throws SQLiteException if a native error occurs. 463 */ bindInt(int parameterIndex, int value)464 public void bindInt(int parameterIndex, int value) { 465 throwIfInvalid(); 466 try { 467 nativeBindInt(mStatement, parameterIndex, value); 468 } finally { 469 Reference.reachabilityFence(this); 470 } 471 } 472 473 /** 474 * Bind a long to the parameter. Parameter indices start at 1. The function throws if the 475 * parameter index is out of bounds. 476 * 477 * @see <a href="http://sqlite.org/c3ref/bind_blob.html">sqlite3_bind_int64</a> 478 * 479 * @param value The value to be bound to the parameter. 480 * @throws IllegalStateException if the statement is closed or this is a foreign thread. 481 * @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range. 482 * @throws SQLiteException if a native error occurs. 483 */ bindLong(int parameterIndex, long value)484 public void bindLong(int parameterIndex, long value) { 485 throwIfInvalid(); 486 try { 487 nativeBindLong(mStatement, parameterIndex, value); 488 } finally { 489 Reference.reachabilityFence(this); 490 } 491 } 492 493 /** 494 * Bind a null to the parameter. Parameter indices start at 1. The function throws if the 495 * parameter index is out of bounds. 496 * 497 * @see <a href="http://sqlite.org/c3ref/bind_blob.html">sqlite3_bind_null</a> 498 * 499 * @param parameterIndex The index of the parameter in the query. It is one-based. 500 * @throws IllegalStateException if the statement is closed or this is a foreign thread. 501 * @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range. 502 * @throws SQLiteException if a native error occurs. 503 */ bindNull(int parameterIndex)504 public void bindNull(int parameterIndex) { 505 throwIfInvalid(); 506 try { 507 nativeBindNull(mStatement, parameterIndex); 508 } finally { 509 Reference.reachabilityFence(this); 510 } 511 } 512 513 /** 514 * Bind a string to the parameter. Parameter indices start at 1. The function throws if the 515 * parameter index is out of bounds. The string may not be null. 516 * 517 * @see <a href="http://sqlite.org/c3ref/bind_blob.html">sqlite3_bind_text16</a> 518 * 519 * @param parameterIndex The index of the parameter in the query. It is one-based. 520 * @param value The value to be bound to the parameter. 521 * @throws IllegalStateException if the statement is closed or this is a foreign thread. 522 * @throws SQLiteBindOrColumnIndexOutOfRangeException if the parameter is out of range. 523 * @throws SQLiteException if a native error occurs. 524 */ bindText(int parameterIndex, @NonNull String value)525 public void bindText(int parameterIndex, @NonNull String value) { 526 Objects.requireNonNull(value); 527 throwIfInvalid(); 528 try { 529 nativeBindText(mStatement, parameterIndex, value); 530 } finally { 531 Reference.reachabilityFence(this); 532 } 533 } 534 535 /** 536 * Return the number of columns in the current result row. 537 * 538 * @see <a href="http://sqlite.org/c3ref/column_count.html">sqlite3_column_count</a> 539 * 540 * @return The number of columns in the result row. 541 * @throws IllegalStateException if the statement is closed or this is a foreign thread. 542 */ getResultColumnCount()543 public int getResultColumnCount() { 544 throwIfInvalid(); 545 try { 546 return nativeColumnCount(mStatement); 547 } finally { 548 Reference.reachabilityFence(this); 549 } 550 } 551 552 /** 553 * Return the type of the column in the result row. Column indices start at 0. 554 * 555 * @see <a href="http://sqlite.org/c3ref/column_blob.html">sqlite3_column_type</a> 556 * 557 * @param columnIndex The index of a column in the result row. It is zero-based. 558 * @return The type of the value in the column of the result row. 559 * @throws IllegalStateException if the statement is closed or this is a foreign thread. 560 * @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range. 561 * @throws SQLiteException if a native error occurs. 562 */ 563 @SQLiteDataType getColumnType(int columnIndex)564 public int getColumnType(int columnIndex) { 565 throwIfInvalid(); 566 try { 567 return nativeColumnType(mStatement, columnIndex); 568 } finally { 569 Reference.reachabilityFence(this); 570 } 571 } 572 573 /** 574 * Return the name of the column in the result row. Column indices start at 0. This throws 575 * an exception if column is not in the result. 576 * 577 * @see <a href="http://sqlite.org/c3ref/column_name.html">sqlite3_column_name</a> 578 * 579 * @param columnIndex The index of a column in the result row. It is zero-based. 580 * @return The name of the column in the result row. 581 * @throws IllegalStateException if the statement is closed or this is a foreign thread. 582 * @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range. 583 * @throws SQLiteOutOfMemoryException if the database cannot allocate memory for the name. 584 */ 585 @NonNull getColumnName(int columnIndex)586 public String getColumnName(int columnIndex) { 587 throwIfInvalid(); 588 try { 589 return nativeColumnName(mStatement, columnIndex); 590 } finally { 591 Reference.reachabilityFence(this); 592 } 593 } 594 595 /** 596 * Return the length of the column value in the result row. Column indices start at 0. This 597 * returns 0 for a null and number of bytes for text or blob. Numeric values are converted to a 598 * string and the length of the string is returned. See the sqlite documentation for 599 * details. Note that this cannot be used to distinguish a null value from an empty text or 600 * blob. Note that this returns the number of bytes in the text value, not the number of 601 * characters. 602 * 603 * @see <a href="http://sqlite.org/c3ref/column_blob.html">sqlite3_column_bytes</a> 604 * 605 * @param columnIndex The index of a column in the result row. It is zero-based. 606 * @return The length, in bytes, of the value in the column. 607 * @throws IllegalStateException if the statement is closed or this is a foreign thread. 608 * @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range. 609 * @throws SQLiteException if a native error occurs. 610 */ getColumnLength(int columnIndex)611 public int getColumnLength(int columnIndex) { 612 throwIfInvalid(); 613 try { 614 return nativeColumnBytes(mStatement, columnIndex); 615 } finally { 616 Reference.reachabilityFence(this); 617 } 618 } 619 620 /** 621 * Return the column value of the result row as a blob. Column indices start at 0. This 622 * throws an exception if column is not in the result. This returns null if the column value 623 * is null. 624 * 625 * The column value will be converted if it is not of type {@link #SQLITE_DATA_TYPE_BLOB}; see 626 * the sqlite documentation for details. 627 * 628 * @see <a href="http://sqlite.org/c3ref/column_blob.html">sqlite3_column_blob</a> 629 * 630 * @param columnIndex The index of a column in the result row. It is zero-based. 631 * @return The value of the column as a blob, or null if the column is NULL. 632 * @throws IllegalStateException if the statement is closed or this is a foreign thread. 633 * @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range. 634 * @throws SQLiteException if a native error occurs. 635 */ 636 @Nullable getColumnBlob(int columnIndex)637 public byte[] getColumnBlob(int columnIndex) { 638 throwIfInvalid(); 639 try { 640 return nativeColumnBlob(mStatement, columnIndex); 641 } finally { 642 Reference.reachabilityFence(this); 643 } 644 } 645 646 /** 647 * Copy the column value of the result row, interpreted as a blob, into the buffer. Column 648 * indices start at 0. This throws an exception if column is not in the result row. Bytes are 649 * copied into the buffer starting at the offset. Bytes are copied from the blob starting at 650 * srcOffset. Length bytes are copied unless the column value has fewer bytes available. The 651 * function returns the number of bytes copied. 652 * 653 * The column value will be converted if it is not of type {@link #SQLITE_DATA_TYPE_BLOB}; see 654 * the sqlite documentation for details. 655 * 656 * @see <a href="http://sqlite.org/c3ref/column_blob.html">sqlite3_column_blob</a> 657 * 658 * @param columnIndex The index of a column in the result row. It is zero-based. 659 * @param buffer A pre-allocated array to be filled with the value of the column. 660 * @param offset An offset into the buffer: copying starts here. 661 * @param length The number of bytes to copy. 662 * @param srcOffset The offset into the blob from which to start copying. 663 * @return the number of bytes that were copied. 664 * @throws IllegalStateException if the statement is closed or this is a foreign thread. 665 * @throws IllegalArgumentException if the buffer is too small for offset+length. 666 * @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range. 667 * @throws SQLiteException if a native error occurs. 668 */ readColumnBlob(int columnIndex, @NonNull byte[] buffer, int offset, int length, int srcOffset)669 public int readColumnBlob(int columnIndex, @NonNull byte[] buffer, int offset, 670 int length, int srcOffset) { 671 Objects.requireNonNull(buffer); 672 throwIfInvalid(); 673 throwIfInvalidBounds(buffer.length, offset, length); 674 try { 675 return nativeColumnBuffer(mStatement, columnIndex, buffer, offset, length, srcOffset); 676 } finally { 677 Reference.reachabilityFence(this); 678 } 679 } 680 681 /** 682 * Return the column value as a double. Column indices start at 0. This throws an exception 683 * if column is not in the result. 684 * 685 * The column value will be converted if it is not of type {@link #SQLITE_DATA_TYPE_FLOAT}; see 686 * the sqlite documentation for details. 687 * 688 * @see <a href="http://sqlite.org/c3ref/column_blob.html">sqlite3_column_double</a> 689 * 690 * @param columnIndex The index of a column in the result row. It is zero-based. 691 * @return The value of a column as a double. 692 * @throws IllegalStateException if the statement is closed or this is a foreign thread. 693 * @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range. 694 * @throws SQLiteException if a native error occurs. 695 */ getColumnDouble(int columnIndex)696 public double getColumnDouble(int columnIndex) { 697 throwIfInvalid(); 698 try { 699 return nativeColumnDouble(mStatement, columnIndex); 700 } finally { 701 Reference.reachabilityFence(this); 702 } 703 } 704 705 /** 706 * Return the column value as a int. Column indices start at 0. This throws an exception if 707 * column is not in the result. 708 * 709 * The column value will be converted if it is not of type {@link #SQLITE_DATA_TYPE_INTEGER}; 710 * see the sqlite documentation for details. 711 * 712 * @see <a href="http://sqlite.org/c3ref/column_blob.html">sqlite3_column_int</a> 713 * 714 * @param columnIndex The index of a column in the result row. It is zero-based. 715 * @return The value of the column as an int. 716 * @throws IllegalStateException if the statement is closed or this is a foreign thread. 717 * @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range. 718 * @throws SQLiteException if a native error occurs. 719 */ getColumnInt(int columnIndex)720 public int getColumnInt(int columnIndex) { 721 throwIfInvalid(); 722 try { 723 return nativeColumnInt(mStatement, columnIndex); 724 } finally { 725 Reference.reachabilityFence(this); 726 } 727 } 728 729 /** 730 * Return the column value as a long. Column indices start at 0. This throws an exception if 731 * column is not in the result. 732 * 733 * The column value will be converted if it is not of type {@link #SQLITE_DATA_TYPE_INTEGER}; 734 * see the sqlite documentation for details. 735 * 736 * @see <a href="http://sqlite.org/c3ref/column_blob.html">sqlite3_column_long</a> 737 * 738 * @param columnIndex The index of a column in the result row. It is zero-based. 739 * @return The value of the column as an long. 740 * @throws IllegalStateException if the statement is closed or this is a foreign thread. 741 * @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range. 742 * @throws SQLiteException if a native error occurs. 743 */ getColumnLong(int columnIndex)744 public long getColumnLong(int columnIndex) { 745 throwIfInvalid(); 746 try { 747 return nativeColumnLong(mStatement, columnIndex); 748 } finally { 749 Reference.reachabilityFence(this); 750 } 751 } 752 753 /** 754 * Return the column value as a text. Column indices start at 0. This throws an exception if 755 * column is not in the result. 756 * 757 * The column value will be converted if it is not of type {@link #SQLITE_DATA_TYPE_TEXT}; see 758 * the sqlite documentation for details. 759 * 760 * @see <a href="http://sqlite.org/c3ref/column_blob.html">sqlite3_column_text16</a> 761 * 762 * @param columnIndex The index of a column in the result row. It is zero-based. 763 * @return The value of the column as a string. 764 * @throws IllegalStateException if the statement is closed or this is a foreign thread. 765 * @throws SQLiteBindOrColumnIndexOutOfRangeException if the column is out of range. 766 * @throws SQLiteException if a native error occurs. 767 */ 768 @NonNull getColumnText(int columnIndex)769 public String getColumnText(int columnIndex) { 770 throwIfInvalid(); 771 try { 772 return nativeColumnText(mStatement, columnIndex); 773 } finally { 774 Reference.reachabilityFence(this); 775 } 776 } 777 778 @Override toString()779 public String toString() { 780 if (isOpen()) { 781 return "SQLiteRawStatement: " + mSql; 782 } else { 783 return "SQLiteRawStatement: (closed) " + mSql; 784 } 785 } 786 787 /** 788 * Native methods that only require a statement. 789 */ 790 791 /** 792 * Metadata about the prepared statement. The results are a property of the statement itself 793 * and not of any data in the database. 794 */ 795 @FastNative nativeBindParameterCount(long stmt)796 private static native int nativeBindParameterCount(long stmt); 797 @FastNative nativeBindParameterIndex(long stmt, String name)798 private static native int nativeBindParameterIndex(long stmt, String name); 799 @FastNative nativeBindParameterName(long stmt, int param)800 private static native String nativeBindParameterName(long stmt, int param); 801 802 @FastNative nativeColumnCount(long stmt)803 private static native int nativeColumnCount(long stmt); 804 805 /** 806 * Operations on the statement 807 */ nativeStep(long stmt, boolean throwOnError)808 private static native int nativeStep(long stmt, boolean throwOnError); nativeReset(long stmt, boolean clear)809 private static native void nativeReset(long stmt, boolean clear); 810 @FastNative nativeClearBindings(long stmt)811 private static native void nativeClearBindings(long stmt); 812 813 /** 814 * Methods that bind values to parameters. 815 */ 816 @FastNative nativeBindBlob(long stmt, int param, byte[] val, int off, int len)817 private static native void nativeBindBlob(long stmt, int param, byte[] val, int off, int len); 818 @FastNative nativeBindDouble(long stmt, int param, double val)819 private static native void nativeBindDouble(long stmt, int param, double val); 820 @FastNative nativeBindInt(long stmt, int param, int val)821 private static native void nativeBindInt(long stmt, int param, int val); 822 @FastNative nativeBindLong(long stmt, int param, long val)823 private static native void nativeBindLong(long stmt, int param, long val); 824 @FastNative nativeBindNull(long stmt, int param)825 private static native void nativeBindNull(long stmt, int param); 826 @FastNative nativeBindText(long stmt, int param, String val)827 private static native void nativeBindText(long stmt, int param, String val); 828 829 /** 830 * Methods that return information about the columns int the current result row. 831 */ 832 @FastNative nativeColumnType(long stmt, int col)833 private static native int nativeColumnType(long stmt, int col); 834 @FastNative nativeColumnName(long stmt, int col)835 private static native String nativeColumnName(long stmt, int col); 836 837 /** 838 * Methods that return information about the value columns in the current result row. 839 */ 840 @FastNative nativeColumnBytes(long stmt, int col)841 private static native int nativeColumnBytes(long stmt, int col); 842 843 @FastNative nativeColumnBlob(long stmt, int col)844 private static native byte[] nativeColumnBlob(long stmt, int col); 845 @FastNative nativeColumnBuffer(long stmt, int col, byte[] val, int off, int len, int srcOffset)846 private static native int nativeColumnBuffer(long stmt, int col, 847 byte[] val, int off, int len, int srcOffset); 848 @FastNative nativeColumnDouble(long stmt, int col)849 private static native double nativeColumnDouble(long stmt, int col); 850 @FastNative nativeColumnInt(long stmt, int col)851 private static native int nativeColumnInt(long stmt, int col); 852 @FastNative nativeColumnLong(long stmt, int col)853 private static native long nativeColumnLong(long stmt, int col); 854 @FastNative nativeColumnText(long stmt, int col)855 private static native String nativeColumnText(long stmt, int col); 856 } 857