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