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