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