1 /* 2 * Copyright (C) 2006 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.compat.annotation.UnsupportedAppUsage; 20 import android.database.DatabaseUtils; 21 import android.os.Build; 22 import android.os.CancellationSignal; 23 24 import java.util.Arrays; 25 26 /** 27 * A base class for compiled SQLite programs. 28 * <p> 29 * This class is not thread-safe. 30 * </p> 31 */ 32 public abstract class SQLiteProgram extends SQLiteClosable { 33 private static final String[] EMPTY_STRING_ARRAY = new String[0]; 34 35 private final SQLiteDatabase mDatabase; 36 @UnsupportedAppUsage 37 private final String mSql; 38 private final boolean mReadOnly; 39 private final String[] mColumnNames; 40 private final int mNumParameters; 41 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) 42 private final Object[] mBindArgs; 43 SQLiteProgram(SQLiteDatabase db, String sql, Object[] bindArgs, CancellationSignal cancellationSignalForPrepare)44 SQLiteProgram(SQLiteDatabase db, String sql, Object[] bindArgs, 45 CancellationSignal cancellationSignalForPrepare) { 46 mDatabase = db; 47 mSql = sql.trim(); 48 49 int n = DatabaseUtils.getSqlStatementType(mSql); 50 switch (n) { 51 case DatabaseUtils.STATEMENT_BEGIN: 52 case DatabaseUtils.STATEMENT_COMMIT: 53 case DatabaseUtils.STATEMENT_ABORT: 54 mReadOnly = false; 55 mColumnNames = EMPTY_STRING_ARRAY; 56 mNumParameters = 0; 57 break; 58 59 default: 60 boolean assumeReadOnly = (n == DatabaseUtils.STATEMENT_SELECT); 61 SQLiteStatementInfo info = new SQLiteStatementInfo(); 62 db.getThreadSession().prepare(mSql, 63 db.getThreadDefaultConnectionFlags(assumeReadOnly), 64 cancellationSignalForPrepare, info); 65 mReadOnly = info.readOnly; 66 mColumnNames = info.columnNames; 67 mNumParameters = info.numParameters; 68 break; 69 } 70 71 if (bindArgs != null && bindArgs.length > mNumParameters) { 72 throw new IllegalArgumentException("Too many bind arguments. " 73 + bindArgs.length + " arguments were provided but the statement needs " 74 + mNumParameters + " arguments."); 75 } 76 77 if (mNumParameters != 0) { 78 mBindArgs = new Object[mNumParameters]; 79 if (bindArgs != null) { 80 System.arraycopy(bindArgs, 0, mBindArgs, 0, bindArgs.length); 81 } 82 } else { 83 mBindArgs = null; 84 } 85 } 86 getDatabase()87 final SQLiteDatabase getDatabase() { 88 return mDatabase; 89 } 90 getSql()91 final String getSql() { 92 return mSql; 93 } 94 getBindArgs()95 final Object[] getBindArgs() { 96 return mBindArgs; 97 } 98 getColumnNames()99 final String[] getColumnNames() { 100 return mColumnNames; 101 } 102 103 /** @hide */ getSession()104 protected final SQLiteSession getSession() { 105 return mDatabase.getThreadSession(); 106 } 107 108 /** @hide */ getConnectionFlags()109 protected final int getConnectionFlags() { 110 return mDatabase.getThreadDefaultConnectionFlags(mReadOnly); 111 } 112 113 /** @hide */ onCorruption()114 protected final void onCorruption() { 115 mDatabase.onCorruption(); 116 } 117 118 /** 119 * Unimplemented. 120 * @deprecated This method is deprecated and must not be used. 121 */ 122 @Deprecated getUniqueId()123 public final int getUniqueId() { 124 return -1; 125 } 126 127 /** 128 * Bind a NULL value to this statement. The value remains bound until 129 * {@link #clearBindings} is called. 130 * 131 * @param index The 1-based index to the parameter to bind null to 132 */ bindNull(int index)133 public void bindNull(int index) { 134 bind(index, null); 135 } 136 137 /** 138 * Bind a long value to this statement. The value remains bound until 139 * {@link #clearBindings} is called. 140 *addToBindArgs 141 * @param index The 1-based index to the parameter to bind 142 * @param value The value to bind 143 */ bindLong(int index, long value)144 public void bindLong(int index, long value) { 145 bind(index, value); 146 } 147 148 /** 149 * Bind a double value to this statement. The value remains bound until 150 * {@link #clearBindings} is called. 151 * 152 * @param index The 1-based index to the parameter to bind 153 * @param value The value to bind 154 */ bindDouble(int index, double value)155 public void bindDouble(int index, double value) { 156 bind(index, value); 157 } 158 159 /** 160 * Bind a String value to this statement. The value remains bound until 161 * {@link #clearBindings} is called. 162 * 163 * @param index The 1-based index to the parameter to bind 164 * @param value The value to bind, must not be null 165 */ bindString(int index, String value)166 public void bindString(int index, String value) { 167 if (value == null) { 168 throw new IllegalArgumentException("the bind value at index " + index + " is null"); 169 } 170 bind(index, value); 171 } 172 173 /** 174 * Bind a byte array value to this statement. The value remains bound until 175 * {@link #clearBindings} is called. 176 * 177 * @param index The 1-based index to the parameter to bind 178 * @param value The value to bind, must not be null 179 */ bindBlob(int index, byte[] value)180 public void bindBlob(int index, byte[] value) { 181 if (value == null) { 182 throw new IllegalArgumentException("the bind value at index " + index + " is null"); 183 } 184 bind(index, value); 185 } 186 187 /** 188 * Clears all existing bindings. Unset bindings are treated as NULL. 189 */ clearBindings()190 public void clearBindings() { 191 if (mBindArgs != null) { 192 Arrays.fill(mBindArgs, null); 193 } 194 } 195 196 /** 197 * Given an array of String bindArgs, this method binds all of them in one single call. 198 * 199 * @param bindArgs the String array of bind args, none of which must be null. 200 */ bindAllArgsAsStrings(String[] bindArgs)201 public void bindAllArgsAsStrings(String[] bindArgs) { 202 if (bindArgs != null) { 203 for (int i = bindArgs.length; i != 0; i--) { 204 bindString(i, bindArgs[i - 1]); 205 } 206 } 207 } 208 209 @Override onAllReferencesReleased()210 protected void onAllReferencesReleased() { 211 clearBindings(); 212 } 213 bind(int index, Object value)214 private void bind(int index, Object value) { 215 if (index < 1 || index > mNumParameters) { 216 throw new IllegalArgumentException("Cannot bind argument at index " 217 + index + " because the index is out of range. " 218 + "The statement has " + mNumParameters + " parameters."); 219 } 220 mBindArgs[index - 1] = value; 221 } 222 } 223