blob: 89552dc2291e4152a894a9f46f67c56a783e4ac0 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
Vasu Norice38b982010-07-22 13:57:13 -070019import android.database.DatabaseUtils;
Vasu Nori0732f792010-07-29 17:24:12 -070020import android.database.Cursor;
Vasu Norie25539f2010-07-08 17:06:13 -070021
Vasu Nori0732f792010-07-29 17:24:12 -070022import java.util.HashMap;
Vasu Nori36957092010-03-11 14:57:53 -080023
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024/**
25 * A base class for compiled SQLite programs.
Vasu Noriccd95442010-05-28 17:04:16 -070026 *<p>
Vasu Nori24675612010-09-27 14:54:19 -070027 * SQLiteProgram is NOT internally synchronized so code using a SQLiteProgram from multiple
Jeff Hamiltonf3ca9a52010-05-12 15:04:33 -070028 * threads should perform its own synchronization when using the SQLiteProgram.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029 */
30public abstract class SQLiteProgram extends SQLiteClosable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031
Vasu Nori36957092010-03-11 14:57:53 -080032 private static final String TAG = "SQLiteProgram";
33
Vasu Nori14b60e72010-03-01 14:47:47 -080034 /** The database this program is compiled against.
35 * @deprecated do not use this
36 */
37 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038 protected SQLiteDatabase mDatabase;
39
Vasu Nori5a03f362009-10-20 15:16:35 -070040 /** The SQL used to create this query */
41 /* package */ final String mSql;
42
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043 /**
44 * Native linkage, do not modify. This comes from the database and should not be modified
45 * in here or in the native code.
Vasu Nori14b60e72010-03-01 14:47:47 -080046 * @deprecated do not use this
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047 */
Vasu Nori14b60e72010-03-01 14:47:47 -080048 @Deprecated
Vasu Nori0732f792010-07-29 17:24:12 -070049 protected int nHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050
51 /**
Vasu Norie495d1f2010-01-06 16:34:19 -080052 * the SQLiteCompiledSql object for the given sql statement.
Vasu Nori5a03f362009-10-20 15:16:35 -070053 */
Vasu Nori422dad02010-09-03 16:03:08 -070054 /* package */ SQLiteCompiledSql mCompiledSql;
Vasu Nori5a03f362009-10-20 15:16:35 -070055
56 /**
Vasu Norie495d1f2010-01-06 16:34:19 -080057 * SQLiteCompiledSql statement id is populated with the corresponding object from the above
58 * member. This member is used by the native_bind_* methods
Vasu Nori14b60e72010-03-01 14:47:47 -080059 * @deprecated do not use this
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060 */
Vasu Nori14b60e72010-03-01 14:47:47 -080061 @Deprecated
Vasu Nori0732f792010-07-29 17:24:12 -070062 protected int nStatement;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063
Vasu Norie25539f2010-07-08 17:06:13 -070064 /**
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 */
Vasu Nori0732f792010-07-29 17:24:12 -070086 /* package */ HashMap<Integer, Object> mBindArgs = null;
Vasu Norice38b982010-07-22 13:57:13 -070087 /* package */ final int mStatementType;
Vasu Nori4e874ed2010-09-15 18:40:49 -070088 /* 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;
Vasu Norie25539f2010-07-08 17:06:13 -070092
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093 /* package */ SQLiteProgram(SQLiteDatabase db, String sql) {
Vasu Nori0732f792010-07-29 17:24:12 -070094 this(db, sql, null, true);
Vasu Nori2827d6d2010-07-04 00:26:18 -070095 }
96
Vasu Nori0732f792010-07-29 17:24:12 -070097 /* package */ SQLiteProgram(SQLiteDatabase db, String sql, Object[] bindArgs,
98 boolean compileFlag) {
Vasu Norid606b4b2010-02-24 12:54:20 -080099 mSql = sql.trim();
Vasu Nori4e874ed2010-09-15 18:40:49 -0700100 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;
Vasu Nori4e874ed2010-09-15 18:40:49 -0700108 case DatabaseUtils.STATEMENT_BEGIN:
109 case DatabaseUtils.STATEMENT_COMMIT:
110 case DatabaseUtils.STATEMENT_ABORT:
Vasu Nori4e874ed2010-09-15 18:40:49 -0700111 mStatementType = n | STATEMENT_DONT_PREPARE;
112 break;
113 default:
114 mStatementType = n;
115 }
Vasu Norie25539f2010-07-08 17:06:13 -0700116 db.acquireReference();
117 db.addSQLiteClosable(this);
118 mDatabase = db;
119 nHandle = db.mNativeHandle;
Vasu Nori0732f792010-07-29 17:24:12 -0700120 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 }
Vasu Nori2827d6d2010-07-04 00:26:18 -0700126 if (compileFlag) {
Vasu Nori0732f792010-07-29 17:24:12 -0700127 compileAndbindAllArgs();
Vasu Nori2827d6d2010-07-04 00:26:18 -0700128 }
Vasu Nori75010102010-07-01 16:23:06 -0700129 }
130
Vasu Norib729dcc2010-09-14 11:35:49 -0700131 private void compileSql() {
132 // only cache CRUD statements
Vasu Nori4e874ed2010-09-15 18:40:49 -0700133 if ((mStatementType & STATEMENT_CACHEABLE) == 0) {
Vasu Norib729dcc2010-09-14 11:35:49 -0700134 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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 @Override
167 protected void onAllReferencesReleased() {
Vasu Noricc6f5492010-08-23 17:05:25 -0700168 release();
Vasu Norie25539f2010-07-08 17:06:13 -0700169 mDatabase.removeSQLiteClosable(this);
170 mDatabase.releaseReference();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800171 }
Vasu Nori5a03f362009-10-20 15:16:35 -0700172
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800173 @Override
Vasu Nori5a03f362009-10-20 15:16:35 -0700174 protected void onAllReferencesReleasedFromContainer() {
Vasu Noricc6f5492010-08-23 17:05:25 -0700175 release();
Vasu Nori5a03f362009-10-20 15:16:35 -0700176 mDatabase.releaseReference();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800177 }
178
Vasu Nori24675612010-09-27 14:54:19 -0700179 /* package */ void release() {
Vasu Norie495d1f2010-01-06 16:34:19 -0800180 if (mCompiledSql == null) {
181 return;
182 }
Jesse Wilson9b5a9352011-02-10 11:19:09 -0800183 mDatabase.releaseCompiledSqlObj(mSql, mCompiledSql);
Vasu Nori75010102010-07-01 16:23:06 -0700184 mCompiledSql = null;
185 nStatement = 0;
Vasu Norie495d1f2010-01-06 16:34:19 -0800186 }
187
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 /**
189 * Returns a unique identifier for this program.
Vasu Nori5a03f362009-10-20 15:16:35 -0700190 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 * @return a unique identifier for this program
Vasu Nori59d60422010-07-03 15:37:21 -0700192 * @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.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 */
Vasu Nori59d60422010-07-03 15:37:21 -0700195 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 public final int getUniqueId() {
Vasu Nori59d60422010-07-03 15:37:21 -0700197 return -1;
198 }
199
200 /**
201 * used only for testing purposes
202 */
203 /* package */ int getSqlStatementId() {
204 synchronized(this) {
205 return (mCompiledSql == null) ? 0 : nStatement;
206 }
Vasu Nori5a03f362009-10-20 15:16:35 -0700207 }
208
209 /* package */ String getSqlString() {
210 return mSql;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 }
212
213 /**
Vasu Nori8d45e4e2010-02-05 22:35:47 -0800214 * @deprecated This method is deprecated and must not be used.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 *
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 */
Vasu Nori5a03f362009-10-20 15:16:35 -0700220 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221 protected void compile(String sql, boolean forceCompilation) {
Vasu Nori5a03f362009-10-20 15:16:35 -0700222 // TODO is there a need for this?
223 }
224
Vasu Nori0732f792010-07-29 17:24:12 -0700225 private void bind(int type, int index, Object value) {
Vasu Nori24675612010-09-27 14:54:19 -0700226 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;
Vasu Nori0732f792010-07-29 17:24:12 -0700249 }
Vasu Nori24675612010-09-27 14:54:19 -0700250 } finally {
251 releaseReference();
Vasu Nori0732f792010-07-29 17:24:12 -0700252 }
253 }
254 }
255
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800256 /**
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 */
262 public void bindNull(int index) {
Vasu Nori0732f792010-07-29 17:24:12 -0700263 bind(Cursor.FIELD_TYPE_NULL, index, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800264 }
265
266 /**
267 * Bind a long value to this statement. The value remains bound until
268 * {@link #clearBindings} is called.
Vasu Nori0732f792010-07-29 17:24:12 -0700269 *addToBindArgs
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270 * @param index The 1-based index to the parameter to bind
271 * @param value The value to bind
272 */
273 public void bindLong(int index, long value) {
Vasu Nori0732f792010-07-29 17:24:12 -0700274 bind(Cursor.FIELD_TYPE_INTEGER, index, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275 }
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 */
284 public void bindDouble(int index, double value) {
Vasu Nori0732f792010-07-29 17:24:12 -0700285 bind(Cursor.FIELD_TYPE_FLOAT, index, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 }
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 */
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 }
Vasu Nori0732f792010-07-29 17:24:12 -0700299 bind(Cursor.FIELD_TYPE_STRING, index, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800300 }
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 */
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 }
Vasu Nori0732f792010-07-29 17:24:12 -0700313 bind(Cursor.FIELD_TYPE_BLOB, index, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 }
315
316 /**
317 * Clears all existing bindings. Unset bindings are treated as NULL.
318 */
319 public void clearBindings() {
Vasu Nori24675612010-09-27 14:54:19 -0700320 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();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 }
331 }
332
333 /**
334 * Release this program's resources, making it invalid.
335 */
336 public void close() {
Vasu Nori24675612010-09-27 14:54:19 -0700337 mBindArgs = null;
338 if (nHandle == 0 || !mDatabase.isOpen()) {
339 return;
Vasu Noric8e1f232010-04-13 15:05:09 -0700340 }
Vasu Nori24675612010-09-27 14:54:19 -0700341 releaseReference();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800342 }
343
Vasu Nori24675612010-09-27 14:54:19 -0700344 private void addToBindArgs(int index, Object value) {
Vasu Norice38b982010-07-22 13:57:13 -0700345 if (mBindArgs == null) {
Vasu Nori0732f792010-07-29 17:24:12 -0700346 mBindArgs = new HashMap<Integer, Object>();
Vasu Norie25539f2010-07-08 17:06:13 -0700347 }
Vasu Nori0732f792010-07-29 17:24:12 -0700348 mBindArgs.put(index, value);
Vasu Norie25539f2010-07-08 17:06:13 -0700349 }
350
Vasu Nori24675612010-09-27 14:54:19 -0700351 /* package */ void compileAndbindAllArgs() {
Vasu Nori4e874ed2010-09-15 18:40:49 -0700352 if ((mStatementType & STATEMENT_DONT_PREPARE) > 0) {
Vasu Noricc1eaf62011-03-14 19:22:16 -0700353 if (mBindArgs != null) {
354 throw new IllegalArgumentException("Can't pass bindargs for this sql :" + mSql);
Vasu Nori4e874ed2010-09-15 18:40:49 -0700355 }
Vasu Noricc1eaf62011-03-14 19:22:16 -0700356 // no need to prepare this SQL statement
Vasu Nori4e874ed2010-09-15 18:40:49 -0700357 return;
358 }
Vasu Nori0732f792010-07-29 17:24:12 -0700359 if (nStatement == 0) {
360 // SQL statement is not compiled yet. compile it now.
Vasu Norib729dcc2010-09-14 11:35:49 -0700361 compileSql();
Vasu Nori0732f792010-07-29 17:24:12 -0700362 }
Vasu Norice38b982010-07-22 13:57:13 -0700363 if (mBindArgs == null) {
Vasu Norie25539f2010-07-08 17:06:13 -0700364 return;
365 }
Vasu Nori0732f792010-07-29 17:24:12 -0700366 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 */
395 public void bindAllArgsAsStrings(String[] bindArgs) {
396 if (bindArgs == null) {
397 return;
398 }
399 int size = bindArgs.length;
Vasu Nori24675612010-09-27 14:54:19 -0700400 for (int i = 0; i < size; i++) {
401 bindString(i + 1, bindArgs[i]);
Vasu Norie25539f2010-07-08 17:06:13 -0700402 }
403 }
404
Vasu Nori19666322010-09-10 14:09:00 -0700405 /* package */ synchronized final void setNativeHandle(int nHandle) {
406 this.nHandle = nHandle;
407 }
408
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800409 /**
Vasu Nori8d45e4e2010-02-05 22:35:47 -0800410 * @deprecated This method is deprecated and must not be used.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800411 * Compiles SQL into a SQLite program.
Vasu Nori5a03f362009-10-20 15:16:35 -0700412 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 * <P>The database lock must be held when calling this method.
414 * @param sql The SQL to compile.
415 */
Vasu Nori5a03f362009-10-20 15:16:35 -0700416 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417 protected final native void native_compile(String sql);
Vasu Nori8d45e4e2010-02-05 22:35:47 -0800418
419 /**
420 * @deprecated This method is deprecated and must not be used.
421 */
Vasu Nori5a03f362009-10-20 15:16:35 -0700422 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800423 protected final native void native_finalize();
424
425 protected final native void native_bind_null(int index);
426 protected final native void native_bind_long(int index, long value);
427 protected final native void native_bind_double(int index, double value);
428 protected final native void native_bind_string(int index, String value);
429 protected final native void native_bind_blob(int index, byte[] value);
Vasu Nori0732f792010-07-29 17:24:12 -0700430 private final native void native_clear_bindings();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800431}
432