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.util.Log; 20 21 /** 22 * A base class for compiled SQLite programs. 23 * 24 * SQLiteProgram is not internally synchronized so code using a SQLiteProgram from multiple 25 * threads should perform its own synchronization when using the SQLiteProgram. 26 */ 27 public abstract class SQLiteProgram extends SQLiteClosable { 28 29 private static final String TAG = "SQLiteProgram"; 30 31 /** The database this program is compiled against. 32 * @deprecated do not use this 33 */ 34 @Deprecated 35 protected SQLiteDatabase mDatabase; 36 37 /** The SQL used to create this query */ 38 /* package */ final String mSql; 39 40 /** 41 * Native linkage, do not modify. This comes from the database and should not be modified 42 * in here or in the native code. 43 * @deprecated do not use this 44 */ 45 @Deprecated 46 protected int nHandle = 0; 47 48 /** 49 * the SQLiteCompiledSql object for the given sql statement. 50 */ 51 private SQLiteCompiledSql mCompiledSql; 52 53 /** 54 * SQLiteCompiledSql statement id is populated with the corresponding object from the above 55 * member. This member is used by the native_bind_* methods 56 * @deprecated do not use this 57 */ 58 @Deprecated 59 protected int nStatement = 0; 60 61 /* package */ SQLiteProgram(SQLiteDatabase db, String sql) { 62 mDatabase = db; 63 mSql = sql.trim(); 64 db.acquireReference(); 65 db.addSQLiteClosable(this); 66 this.nHandle = db.mNativeHandle; 67 68 // only cache CRUD statements 69 String prefixSql = mSql.substring(0, 6); 70 if (!prefixSql.equalsIgnoreCase("INSERT") && !prefixSql.equalsIgnoreCase("UPDATE") && 71 !prefixSql.equalsIgnoreCase("REPLAC") && 72 !prefixSql.equalsIgnoreCase("DELETE") && !prefixSql.equalsIgnoreCase("SELECT")) { 73 mCompiledSql = new SQLiteCompiledSql(db, sql); 74 nStatement = mCompiledSql.nStatement; 75 // since it is not in the cache, no need to acquire() it. 76 return; 77 } 78 79 // it is not pragma 80 mCompiledSql = db.getCompiledStatementForSql(sql); 81 if (mCompiledSql == null) { 82 // create a new compiled-sql obj 83 mCompiledSql = new SQLiteCompiledSql(db, sql); 84 85 // add it to the cache of compiled-sqls 86 // but before adding it and thus making it available for anyone else to use it, 87 // make sure it is acquired by me. 88 mCompiledSql.acquire(); 89 db.addToCompiledQueries(sql, mCompiledSql); 90 if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) { 91 Log.v(TAG, "Created DbObj (id#" + mCompiledSql.nStatement + 92 ") for sql: " + sql); 93 } 94 } else { 95 // it is already in compiled-sql cache. 96 // try to acquire the object. 97 if (!mCompiledSql.acquire()) { 98 int last = mCompiledSql.nStatement; 99 // the SQLiteCompiledSql in cache is in use by some other SQLiteProgram object. 100 // we can't have two different SQLiteProgam objects can't share the same 101 // CompiledSql object. create a new one. 102 // finalize it when I am done with it in "this" object. 103 mCompiledSql = new SQLiteCompiledSql(db, sql); 104 if (SQLiteDebug.DEBUG_ACTIVE_CURSOR_FINALIZATION) { 105 Log.v(TAG, "** possible bug ** Created NEW DbObj (id#" + 106 mCompiledSql.nStatement + 107 ") because the previously created DbObj (id#" + last + 108 ") was not released for sql:" + sql); 109 } 110 // since it is not in the cache, no need to acquire() it. 111 } 112 } 113 nStatement = mCompiledSql.nStatement; 114 } 115 116 @Override 117 protected void onAllReferencesReleased() { 118 releaseCompiledSqlIfNotInCache(); 119 mDatabase.releaseReference(); 120 mDatabase.removeSQLiteClosable(this); 121 } 122 123 @Override 124 protected void onAllReferencesReleasedFromContainer() { 125 releaseCompiledSqlIfNotInCache(); 126 mDatabase.releaseReference(); 127 } 128 129 private void releaseCompiledSqlIfNotInCache() { 130 if (mCompiledSql == null) { 131 return; 132 } 133 synchronized(mDatabase.mCompiledQueries) { 134 if (!mDatabase.mCompiledQueries.containsValue(mCompiledSql)) { 135 // it is NOT in compiled-sql cache. i.e., responsibility of 136 // releasing this statement is on me. 137 mCompiledSql.releaseSqlStatement(); 138 mCompiledSql = null; 139 nStatement = 0; 140 } else { 141 // it is in compiled-sql cache. reset its CompiledSql#mInUse flag 142 mCompiledSql.release(); 143 } 144 } 145 } 146 147 /** 148 * Returns a unique identifier for this program. 149 * 150 * @return a unique identifier for this program 151 */ 152 public final int getUniqueId() { 153 return nStatement; 154 } 155 156 /* package */ String getSqlString() { 157 return mSql; 158 } 159 160 /** 161 * @deprecated This method is deprecated and must not be used. 162 * 163 * @param sql the SQL string to compile 164 * @param forceCompilation forces the SQL to be recompiled in the event that there is an 165 * existing compiled SQL program already around 166 */ 167 @Deprecated 168 protected void compile(String sql, boolean forceCompilation) { 169 // TODO is there a need for this? 170 } 171 172 /** 173 * Bind a NULL 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 null to 177 */ 178 public void bindNull(int index) { 179 if (!mDatabase.isOpen()) { 180 throw new IllegalStateException("database " + mDatabase.getPath() + " already closed"); 181 } 182 acquireReference(); 183 try { 184 native_bind_null(index); 185 } finally { 186 releaseReference(); 187 } 188 } 189 190 /** 191 * Bind a long value to this statement. The value remains bound until 192 * {@link #clearBindings} is called. 193 * 194 * @param index The 1-based index to the parameter to bind 195 * @param value The value to bind 196 */ 197 public void bindLong(int index, long value) { 198 if (!mDatabase.isOpen()) { 199 throw new IllegalStateException("database " + mDatabase.getPath() + " already closed"); 200 } 201 acquireReference(); 202 try { 203 native_bind_long(index, value); 204 } finally { 205 releaseReference(); 206 } 207 } 208 209 /** 210 * Bind a double value to this statement. The value remains bound until 211 * {@link #clearBindings} is called. 212 * 213 * @param index The 1-based index to the parameter to bind 214 * @param value The value to bind 215 */ 216 public void bindDouble(int index, double value) { 217 if (!mDatabase.isOpen()) { 218 throw new IllegalStateException("database " + mDatabase.getPath() + " already closed"); 219 } 220 acquireReference(); 221 try { 222 native_bind_double(index, value); 223 } finally { 224 releaseReference(); 225 } 226 } 227 228 /** 229 * Bind a String value to this statement. The value remains bound until 230 * {@link #clearBindings} is called. 231 * 232 * @param index The 1-based index to the parameter to bind 233 * @param value The value to bind 234 */ 235 public void bindString(int index, String value) { 236 if (value == null) { 237 throw new IllegalArgumentException("the bind value at index " + index + " is null"); 238 } 239 if (!mDatabase.isOpen()) { 240 throw new IllegalStateException("database " + mDatabase.getPath() + " already closed"); 241 } 242 acquireReference(); 243 try { 244 native_bind_string(index, value); 245 } finally { 246 releaseReference(); 247 } 248 } 249 250 /** 251 * Bind a byte array value to this statement. The value remains bound until 252 * {@link #clearBindings} is called. 253 * 254 * @param index The 1-based index to the parameter to bind 255 * @param value The value to bind 256 */ 257 public void bindBlob(int index, byte[] value) { 258 if (value == null) { 259 throw new IllegalArgumentException("the bind value at index " + index + " is null"); 260 } 261 if (!mDatabase.isOpen()) { 262 throw new IllegalStateException("database " + mDatabase.getPath() + " already closed"); 263 } 264 acquireReference(); 265 try { 266 native_bind_blob(index, value); 267 } finally { 268 releaseReference(); 269 } 270 } 271 272 /** 273 * Clears all existing bindings. Unset bindings are treated as NULL. 274 */ 275 public void clearBindings() { 276 if (!mDatabase.isOpen()) { 277 throw new IllegalStateException("database " + mDatabase.getPath() + " already closed"); 278 } 279 acquireReference(); 280 try { 281 native_clear_bindings(); 282 } finally { 283 releaseReference(); 284 } 285 } 286 287 /** 288 * Release this program's resources, making it invalid. 289 */ 290 public void close() { 291 if (!mDatabase.isOpen()) { 292 return; 293 } 294 mDatabase.lock(); 295 try { 296 releaseReference(); 297 } finally { 298 mDatabase.unlock(); 299 } 300 } 301 302 /** 303 * @deprecated This method is deprecated and must not be used. 304 * Compiles SQL into a SQLite program. 305 * 306 * <P>The database lock must be held when calling this method. 307 * @param sql The SQL to compile. 308 */ 309 @Deprecated 310 protected final native void native_compile(String sql); 311 312 /** 313 * @deprecated This method is deprecated and must not be used. 314 */ 315 @Deprecated 316 protected final native void native_finalize(); 317 318 protected final native void native_bind_null(int index); 319 protected final native void native_bind_long(int index, long value); 320 protected final native void native_bind_double(int index, double value); 321 protected final native void native_bind_string(int index, String value); 322 protected final native void native_bind_blob(int index, byte[] value); 323 private final native void native_clear_bindings(); 324 } 325 326