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