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.database.Cursor; 21 22 import java.util.HashMap; 23 24 /** 25 * A base class for compiled SQLite programs. 26 *<p> 27 * SQLiteProgram is NOT internally synchronized so code using a SQLiteProgram from multiple 28 * threads should perform its own synchronization when using the SQLiteProgram. 29 */ 30 public abstract class SQLiteProgram extends SQLiteClosable { 31 32 private static final String TAG = "SQLiteProgram"; 33 34 /** The database this program is compiled against. 35 * @deprecated do not use this 36 */ 37 @Deprecated 38 protected SQLiteDatabase mDatabase; 39 40 /** The SQL used to create this query */ 41 /* package */ final String mSql; 42 43 /** 44 * Native linkage, do not modify. This comes from the database and should not be modified 45 * in here or in the native code. 46 * @deprecated do not use this 47 */ 48 @Deprecated 49 protected int nHandle; 50 51 /** 52 * the SQLiteCompiledSql object for the given sql statement. 53 */ 54 /* package */ SQLiteCompiledSql mCompiledSql; 55 56 /** 57 * SQLiteCompiledSql statement id is populated with the corresponding object from the above 58 * member. This member is used by the native_bind_* methods 59 * @deprecated do not use this 60 */ 61 @Deprecated 62 protected int nStatement; 63 64 /** 65 * In the case of {@link SQLiteStatement}, this member stores the bindargs passed 66 * to the following methods, instead of actually doing the binding. 67 * <ul> 68 * <li>{@link #bindBlob(int, byte[])}</li> 69 * <li>{@link #bindDouble(int, double)}</li> 70 * <li>{@link #bindLong(int, long)}</li> 71 * <li>{@link #bindNull(int)}</li> 72 * <li>{@link #bindString(int, String)}</li> 73 * </ul> 74 * <p> 75 * Each entry in the array is a Pair of 76 * <ol> 77 * <li>bind arg position number</li> 78 * <li>the value to be bound to the bindarg</li> 79 * </ol> 80 * <p> 81 * It is lazily initialized in the above bind methods 82 * and it is cleared in {@link #clearBindings()} method. 83 * <p> 84 * It is protected (in multi-threaded environment) by {@link SQLiteProgram}.this 85 */ 86 /* package */ HashMap<Integer, Object> mBindArgs = null; 87 /* package */ final int mStatementType; 88 /* package */ static final int STATEMENT_CACHEABLE = 16; 89 /* package */ static final int STATEMENT_DONT_PREPARE = 32; 90 /* package */ static final int STATEMENT_USE_POOLED_CONN = 64; 91 /* package */ static final int STATEMENT_TYPE_MASK = 0x0f; 92 SQLiteProgram(SQLiteDatabase db, String sql)93 /* package */ SQLiteProgram(SQLiteDatabase db, String sql) { 94 this(db, sql, null, true); 95 } 96 SQLiteProgram(SQLiteDatabase db, String sql, Object[] bindArgs, boolean compileFlag)97 /* package */ SQLiteProgram(SQLiteDatabase db, String sql, Object[] bindArgs, 98 boolean compileFlag) { 99 mSql = sql.trim(); 100 int n = DatabaseUtils.getSqlStatementType(mSql); 101 switch (n) { 102 case DatabaseUtils.STATEMENT_UPDATE: 103 mStatementType = n | STATEMENT_CACHEABLE; 104 break; 105 case DatabaseUtils.STATEMENT_SELECT: 106 mStatementType = n | STATEMENT_CACHEABLE | STATEMENT_USE_POOLED_CONN; 107 break; 108 case DatabaseUtils.STATEMENT_BEGIN: 109 case DatabaseUtils.STATEMENT_COMMIT: 110 case DatabaseUtils.STATEMENT_ABORT: 111 mStatementType = n | STATEMENT_DONT_PREPARE; 112 break; 113 default: 114 mStatementType = n; 115 } 116 db.acquireReference(); 117 db.addSQLiteClosable(this); 118 mDatabase = db; 119 nHandle = db.mNativeHandle; 120 if (bindArgs != null) { 121 int size = bindArgs.length; 122 for (int i = 0; i < size; i++) { 123 this.addToBindArgs(i + 1, bindArgs[i]); 124 } 125 } 126 if (compileFlag) { 127 compileAndbindAllArgs(); 128 } 129 } 130 compileSql()131 private void compileSql() { 132 // only cache CRUD statements 133 if ((mStatementType & STATEMENT_CACHEABLE) == 0) { 134 mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql); 135 nStatement = mCompiledSql.nStatement; 136 // since it is not in the cache, no need to acquire() it. 137 return; 138 } 139 140 mCompiledSql = mDatabase.getCompiledStatementForSql(mSql); 141 if (mCompiledSql == null) { 142 // create a new compiled-sql obj 143 mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql); 144 145 // add it to the cache of compiled-sqls 146 // but before adding it and thus making it available for anyone else to use it, 147 // make sure it is acquired by me. 148 mCompiledSql.acquire(); 149 mDatabase.addToCompiledQueries(mSql, mCompiledSql); 150 } else { 151 // it is already in compiled-sql cache. 152 // try to acquire the object. 153 if (!mCompiledSql.acquire()) { 154 int last = mCompiledSql.nStatement; 155 // the SQLiteCompiledSql in cache is in use by some other SQLiteProgram object. 156 // we can't have two different SQLiteProgam objects can't share the same 157 // CompiledSql object. create a new one. 158 // finalize it when I am done with it in "this" object. 159 mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql); 160 // since it is not in the cache, no need to acquire() it. 161 } 162 } 163 nStatement = mCompiledSql.nStatement; 164 } 165 166 @Override onAllReferencesReleased()167 protected void onAllReferencesReleased() { 168 release(); 169 mDatabase.removeSQLiteClosable(this); 170 mDatabase.releaseReference(); 171 } 172 173 @Override onAllReferencesReleasedFromContainer()174 protected void onAllReferencesReleasedFromContainer() { 175 release(); 176 mDatabase.releaseReference(); 177 } 178 release()179 /* package */ void release() { 180 if (mCompiledSql == null) { 181 return; 182 } 183 mDatabase.releaseCompiledSqlObj(mSql, mCompiledSql); 184 mCompiledSql = null; 185 nStatement = 0; 186 } 187 188 /** 189 * Returns a unique identifier for this program. 190 * 191 * @return a unique identifier for this program 192 * @deprecated do not use this method. it is not guaranteed to be the same across executions of 193 * the SQL statement contained in this object. 194 */ 195 @Deprecated getUniqueId()196 public final int getUniqueId() { 197 return -1; 198 } 199 200 /** 201 * used only for testing purposes 202 */ getSqlStatementId()203 /* package */ int getSqlStatementId() { 204 synchronized(this) { 205 return (mCompiledSql == null) ? 0 : nStatement; 206 } 207 } 208 getSqlString()209 /* package */ String getSqlString() { 210 return mSql; 211 } 212 213 /** 214 * @deprecated This method is deprecated and must not be used. 215 * 216 * @param sql the SQL string to compile 217 * @param forceCompilation forces the SQL to be recompiled in the event that there is an 218 * existing compiled SQL program already around 219 */ 220 @Deprecated compile(String sql, boolean forceCompilation)221 protected void compile(String sql, boolean forceCompilation) { 222 // TODO is there a need for this? 223 } 224 bind(int type, int index, Object value)225 private void bind(int type, int index, Object value) { 226 mDatabase.verifyDbIsOpen(); 227 addToBindArgs(index, (type == Cursor.FIELD_TYPE_NULL) ? null : value); 228 if (nStatement > 0) { 229 // bind only if the SQL statement is compiled 230 acquireReference(); 231 try { 232 switch (type) { 233 case Cursor.FIELD_TYPE_NULL: 234 native_bind_null(index); 235 break; 236 case Cursor.FIELD_TYPE_BLOB: 237 native_bind_blob(index, (byte[]) value); 238 break; 239 case Cursor.FIELD_TYPE_FLOAT: 240 native_bind_double(index, (Double) value); 241 break; 242 case Cursor.FIELD_TYPE_INTEGER: 243 native_bind_long(index, (Long) value); 244 break; 245 case Cursor.FIELD_TYPE_STRING: 246 default: 247 native_bind_string(index, (String) value); 248 break; 249 } 250 } finally { 251 releaseReference(); 252 } 253 } 254 } 255 256 /** 257 * Bind a NULL value to this statement. The value remains bound until 258 * {@link #clearBindings} is called. 259 * 260 * @param index The 1-based index to the parameter to bind null to 261 */ bindNull(int index)262 public void bindNull(int index) { 263 bind(Cursor.FIELD_TYPE_NULL, index, null); 264 } 265 266 /** 267 * Bind a long value to this statement. The value remains bound until 268 * {@link #clearBindings} is called. 269 *addToBindArgs 270 * @param index The 1-based index to the parameter to bind 271 * @param value The value to bind 272 */ bindLong(int index, long value)273 public void bindLong(int index, long value) { 274 bind(Cursor.FIELD_TYPE_INTEGER, index, value); 275 } 276 277 /** 278 * Bind a double value to this statement. The value remains bound until 279 * {@link #clearBindings} is called. 280 * 281 * @param index The 1-based index to the parameter to bind 282 * @param value The value to bind 283 */ bindDouble(int index, double value)284 public void bindDouble(int index, double value) { 285 bind(Cursor.FIELD_TYPE_FLOAT, index, value); 286 } 287 288 /** 289 * Bind a String value to this statement. The value remains bound until 290 * {@link #clearBindings} is called. 291 * 292 * @param index The 1-based index to the parameter to bind 293 * @param value The value to bind 294 */ bindString(int index, String value)295 public void bindString(int index, String value) { 296 if (value == null) { 297 throw new IllegalArgumentException("the bind value at index " + index + " is null"); 298 } 299 bind(Cursor.FIELD_TYPE_STRING, index, value); 300 } 301 302 /** 303 * Bind a byte array value to this statement. The value remains bound until 304 * {@link #clearBindings} is called. 305 * 306 * @param index The 1-based index to the parameter to bind 307 * @param value The value to bind 308 */ bindBlob(int index, byte[] value)309 public void bindBlob(int index, byte[] value) { 310 if (value == null) { 311 throw new IllegalArgumentException("the bind value at index " + index + " is null"); 312 } 313 bind(Cursor.FIELD_TYPE_BLOB, index, value); 314 } 315 316 /** 317 * Clears all existing bindings. Unset bindings are treated as NULL. 318 */ clearBindings()319 public void clearBindings() { 320 mBindArgs = null; 321 if (this.nStatement == 0) { 322 return; 323 } 324 mDatabase.verifyDbIsOpen(); 325 acquireReference(); 326 try { 327 native_clear_bindings(); 328 } finally { 329 releaseReference(); 330 } 331 } 332 333 /** 334 * Release this program's resources, making it invalid. 335 */ close()336 public void close() { 337 mBindArgs = null; 338 if (nHandle == 0 || !mDatabase.isOpen()) { 339 return; 340 } 341 releaseReference(); 342 } 343 addToBindArgs(int index, Object value)344 private void addToBindArgs(int index, Object value) { 345 if (mBindArgs == null) { 346 mBindArgs = new HashMap<Integer, Object>(); 347 } 348 mBindArgs.put(index, value); 349 } 350 compileAndbindAllArgs()351 /* package */ void compileAndbindAllArgs() { 352 if ((mStatementType & STATEMENT_DONT_PREPARE) > 0) { 353 if (mBindArgs != null) { 354 throw new IllegalArgumentException("Can't pass bindargs for this sql :" + mSql); 355 } 356 // no need to prepare this SQL statement 357 return; 358 } 359 if (nStatement == 0) { 360 // SQL statement is not compiled yet. compile it now. 361 compileSql(); 362 } 363 if (mBindArgs == null) { 364 return; 365 } 366 for (int index : mBindArgs.keySet()) { 367 Object value = mBindArgs.get(index); 368 if (value == null) { 369 native_bind_null(index); 370 } else if (value instanceof Double || value instanceof Float) { 371 native_bind_double(index, ((Number) value).doubleValue()); 372 } else if (value instanceof Number) { 373 native_bind_long(index, ((Number) value).longValue()); 374 } else if (value instanceof Boolean) { 375 Boolean bool = (Boolean)value; 376 native_bind_long(index, (bool) ? 1 : 0); 377 if (bool) { 378 native_bind_long(index, 1); 379 } else { 380 native_bind_long(index, 0); 381 } 382 } else if (value instanceof byte[]){ 383 native_bind_blob(index, (byte[]) value); 384 } else { 385 native_bind_string(index, value.toString()); 386 } 387 } 388 } 389 390 /** 391 * Given an array of String bindArgs, this method binds all of them in one single call. 392 * 393 * @param bindArgs the String array of bind args. 394 */ bindAllArgsAsStrings(String[] bindArgs)395 public void bindAllArgsAsStrings(String[] bindArgs) { 396 if (bindArgs == null) { 397 return; 398 } 399 int size = bindArgs.length; 400 for (int i = 0; i < size; i++) { 401 bindString(i + 1, bindArgs[i]); 402 } 403 } 404 setNativeHandle(int nHandle)405 /* package */ synchronized final void setNativeHandle(int nHandle) { 406 this.nHandle = nHandle; 407 } 408 409 /** 410 * @deprecated This method is deprecated and must not be used. 411 * Compiles SQL into a SQLite program. 412 * 413 * <P>The database lock must be held when calling this method. 414 * @param sql The SQL to compile. 415 */ 416 @Deprecated native_compile(String sql)417 protected final native void native_compile(String sql); 418 419 /** 420 * @deprecated This method is deprecated and must not be used. 421 */ 422 @Deprecated native_finalize()423 protected final native void native_finalize(); 424 native_bind_null(int index)425 protected final native void native_bind_null(int index); native_bind_long(int index, long value)426 protected final native void native_bind_long(int index, long value); native_bind_double(int index, double value)427 protected final native void native_bind_double(int index, double value); native_bind_string(int index, String value)428 protected final native void native_bind_string(int index, String value); native_bind_blob(int index, byte[] value)429 protected final native void native_bind_blob(int index, byte[] value); native_clear_bindings()430 private final native void native_clear_bindings(); 431 } 432 433