blob: 88246e8aee27dfb01bed5b05c38bb1f94ea16f5a [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;
108 case DatabaseUtils.STATEMENT_ATTACH:
109 case DatabaseUtils.STATEMENT_BEGIN:
110 case DatabaseUtils.STATEMENT_COMMIT:
111 case DatabaseUtils.STATEMENT_ABORT:
112 case DatabaseUtils.STATEMENT_DDL:
113 case DatabaseUtils.STATEMENT_UNPREPARED:
114 mStatementType = n | STATEMENT_DONT_PREPARE;
115 break;
116 default:
117 mStatementType = n;
118 }
Vasu Norie25539f2010-07-08 17:06:13 -0700119 db.acquireReference();
120 db.addSQLiteClosable(this);
121 mDatabase = db;
122 nHandle = db.mNativeHandle;
Vasu Nori0732f792010-07-29 17:24:12 -0700123 if (bindArgs != null) {
124 int size = bindArgs.length;
125 for (int i = 0; i < size; i++) {
126 this.addToBindArgs(i + 1, bindArgs[i]);
127 }
128 }
Vasu Nori2827d6d2010-07-04 00:26:18 -0700129 if (compileFlag) {
Vasu Nori0732f792010-07-29 17:24:12 -0700130 compileAndbindAllArgs();
Vasu Nori2827d6d2010-07-04 00:26:18 -0700131 }
Vasu Nori75010102010-07-01 16:23:06 -0700132 }
133
Vasu Norib729dcc2010-09-14 11:35:49 -0700134 private void compileSql() {
135 // only cache CRUD statements
Vasu Nori4e874ed2010-09-15 18:40:49 -0700136 if ((mStatementType & STATEMENT_CACHEABLE) == 0) {
Vasu Norib729dcc2010-09-14 11:35:49 -0700137 mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
138 nStatement = mCompiledSql.nStatement;
139 // since it is not in the cache, no need to acquire() it.
140 return;
141 }
142
143 mCompiledSql = mDatabase.getCompiledStatementForSql(mSql);
144 if (mCompiledSql == null) {
145 // create a new compiled-sql obj
146 mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
147
148 // add it to the cache of compiled-sqls
149 // but before adding it and thus making it available for anyone else to use it,
150 // make sure it is acquired by me.
151 mCompiledSql.acquire();
152 mDatabase.addToCompiledQueries(mSql, mCompiledSql);
153 } else {
154 // it is already in compiled-sql cache.
155 // try to acquire the object.
156 if (!mCompiledSql.acquire()) {
157 int last = mCompiledSql.nStatement;
158 // the SQLiteCompiledSql in cache is in use by some other SQLiteProgram object.
159 // we can't have two different SQLiteProgam objects can't share the same
160 // CompiledSql object. create a new one.
161 // finalize it when I am done with it in "this" object.
162 mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
163 // since it is not in the cache, no need to acquire() it.
164 }
165 }
166 nStatement = mCompiledSql.nStatement;
167 }
168
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 @Override
170 protected void onAllReferencesReleased() {
Vasu Noricc6f5492010-08-23 17:05:25 -0700171 release();
Vasu Norie25539f2010-07-08 17:06:13 -0700172 mDatabase.removeSQLiteClosable(this);
173 mDatabase.releaseReference();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174 }
Vasu Nori5a03f362009-10-20 15:16:35 -0700175
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800176 @Override
Vasu Nori5a03f362009-10-20 15:16:35 -0700177 protected void onAllReferencesReleasedFromContainer() {
Vasu Noricc6f5492010-08-23 17:05:25 -0700178 release();
Vasu Nori5a03f362009-10-20 15:16:35 -0700179 mDatabase.releaseReference();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800180 }
181
Vasu Nori24675612010-09-27 14:54:19 -0700182 /* package */ void release() {
Vasu Norie495d1f2010-01-06 16:34:19 -0800183 if (mCompiledSql == null) {
184 return;
185 }
Jesse Wilson9b5a9352011-02-10 11:19:09 -0800186 mDatabase.releaseCompiledSqlObj(mSql, mCompiledSql);
Vasu Nori75010102010-07-01 16:23:06 -0700187 mCompiledSql = null;
188 nStatement = 0;
Vasu Norie495d1f2010-01-06 16:34:19 -0800189 }
190
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 /**
192 * Returns a unique identifier for this program.
Vasu Nori5a03f362009-10-20 15:16:35 -0700193 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 * @return a unique identifier for this program
Vasu Nori59d60422010-07-03 15:37:21 -0700195 * @deprecated do not use this method. it is not guaranteed to be the same across executions of
196 * the SQL statement contained in this object.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197 */
Vasu Nori59d60422010-07-03 15:37:21 -0700198 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 public final int getUniqueId() {
Vasu Nori59d60422010-07-03 15:37:21 -0700200 return -1;
201 }
202
203 /**
204 * used only for testing purposes
205 */
206 /* package */ int getSqlStatementId() {
207 synchronized(this) {
208 return (mCompiledSql == null) ? 0 : nStatement;
209 }
Vasu Nori5a03f362009-10-20 15:16:35 -0700210 }
211
212 /* package */ String getSqlString() {
213 return mSql;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 }
215
216 /**
Vasu Nori8d45e4e2010-02-05 22:35:47 -0800217 * @deprecated This method is deprecated and must not be used.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 *
219 * @param sql the SQL string to compile
220 * @param forceCompilation forces the SQL to be recompiled in the event that there is an
221 * existing compiled SQL program already around
222 */
Vasu Nori5a03f362009-10-20 15:16:35 -0700223 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800224 protected void compile(String sql, boolean forceCompilation) {
Vasu Nori5a03f362009-10-20 15:16:35 -0700225 // TODO is there a need for this?
226 }
227
Vasu Nori0732f792010-07-29 17:24:12 -0700228 private void bind(int type, int index, Object value) {
Vasu Nori24675612010-09-27 14:54:19 -0700229 mDatabase.verifyDbIsOpen();
230 addToBindArgs(index, (type == Cursor.FIELD_TYPE_NULL) ? null : value);
231 if (nStatement > 0) {
232 // bind only if the SQL statement is compiled
233 acquireReference();
234 try {
235 switch (type) {
236 case Cursor.FIELD_TYPE_NULL:
237 native_bind_null(index);
238 break;
239 case Cursor.FIELD_TYPE_BLOB:
240 native_bind_blob(index, (byte[]) value);
241 break;
242 case Cursor.FIELD_TYPE_FLOAT:
243 native_bind_double(index, (Double) value);
244 break;
245 case Cursor.FIELD_TYPE_INTEGER:
246 native_bind_long(index, (Long) value);
247 break;
248 case Cursor.FIELD_TYPE_STRING:
249 default:
250 native_bind_string(index, (String) value);
251 break;
Vasu Nori0732f792010-07-29 17:24:12 -0700252 }
Vasu Nori24675612010-09-27 14:54:19 -0700253 } finally {
254 releaseReference();
Vasu Nori0732f792010-07-29 17:24:12 -0700255 }
256 }
257 }
258
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800259 /**
260 * Bind a NULL value to this statement. The value remains bound until
261 * {@link #clearBindings} is called.
262 *
263 * @param index The 1-based index to the parameter to bind null to
264 */
265 public void bindNull(int index) {
Vasu Nori0732f792010-07-29 17:24:12 -0700266 bind(Cursor.FIELD_TYPE_NULL, index, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800267 }
268
269 /**
270 * Bind a long value to this statement. The value remains bound until
271 * {@link #clearBindings} is called.
Vasu Nori0732f792010-07-29 17:24:12 -0700272 *addToBindArgs
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800273 * @param index The 1-based index to the parameter to bind
274 * @param value The value to bind
275 */
276 public void bindLong(int index, long value) {
Vasu Nori0732f792010-07-29 17:24:12 -0700277 bind(Cursor.FIELD_TYPE_INTEGER, index, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800278 }
279
280 /**
281 * Bind a double value to this statement. The value remains bound until
282 * {@link #clearBindings} is called.
283 *
284 * @param index The 1-based index to the parameter to bind
285 * @param value The value to bind
286 */
287 public void bindDouble(int index, double value) {
Vasu Nori0732f792010-07-29 17:24:12 -0700288 bind(Cursor.FIELD_TYPE_FLOAT, index, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289 }
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 }
Vasu Nori0732f792010-07-29 17:24:12 -0700302 bind(Cursor.FIELD_TYPE_STRING, index, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303 }
304
305 /**
306 * Bind a byte array value to this statement. The value remains bound until
307 * {@link #clearBindings} is called.
308 *
309 * @param index The 1-based index to the parameter to bind
310 * @param value The value to bind
311 */
312 public void bindBlob(int index, byte[] value) {
313 if (value == null) {
314 throw new IllegalArgumentException("the bind value at index " + index + " is null");
315 }
Vasu Nori0732f792010-07-29 17:24:12 -0700316 bind(Cursor.FIELD_TYPE_BLOB, index, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800317 }
318
319 /**
320 * Clears all existing bindings. Unset bindings are treated as NULL.
321 */
322 public void clearBindings() {
Vasu Nori24675612010-09-27 14:54:19 -0700323 mBindArgs = null;
324 if (this.nStatement == 0) {
325 return;
326 }
327 mDatabase.verifyDbIsOpen();
328 acquireReference();
329 try {
330 native_clear_bindings();
331 } finally {
332 releaseReference();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800333 }
334 }
335
336 /**
337 * Release this program's resources, making it invalid.
338 */
339 public void close() {
Vasu Nori24675612010-09-27 14:54:19 -0700340 mBindArgs = null;
341 if (nHandle == 0 || !mDatabase.isOpen()) {
342 return;
Vasu Noric8e1f232010-04-13 15:05:09 -0700343 }
Vasu Nori24675612010-09-27 14:54:19 -0700344 releaseReference();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345 }
346
Vasu Nori24675612010-09-27 14:54:19 -0700347 private void addToBindArgs(int index, Object value) {
Vasu Norice38b982010-07-22 13:57:13 -0700348 if (mBindArgs == null) {
Vasu Nori0732f792010-07-29 17:24:12 -0700349 mBindArgs = new HashMap<Integer, Object>();
Vasu Norie25539f2010-07-08 17:06:13 -0700350 }
Vasu Nori0732f792010-07-29 17:24:12 -0700351 mBindArgs.put(index, value);
Vasu Norie25539f2010-07-08 17:06:13 -0700352 }
353
Vasu Nori24675612010-09-27 14:54:19 -0700354 /* package */ void compileAndbindAllArgs() {
Vasu Nori4e874ed2010-09-15 18:40:49 -0700355 if ((mStatementType & STATEMENT_DONT_PREPARE) > 0) {
356 // no need to prepare this SQL statement
357 if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
358 if (mBindArgs != null) {
359 throw new IllegalArgumentException("no need to pass bindargs for this sql :" +
360 mSql);
361 }
362 }
363 return;
364 }
Vasu Nori0732f792010-07-29 17:24:12 -0700365 if (nStatement == 0) {
366 // SQL statement is not compiled yet. compile it now.
Vasu Norib729dcc2010-09-14 11:35:49 -0700367 compileSql();
Vasu Nori0732f792010-07-29 17:24:12 -0700368 }
Vasu Norice38b982010-07-22 13:57:13 -0700369 if (mBindArgs == null) {
Vasu Norie25539f2010-07-08 17:06:13 -0700370 return;
371 }
Vasu Nori0732f792010-07-29 17:24:12 -0700372 for (int index : mBindArgs.keySet()) {
373 Object value = mBindArgs.get(index);
374 if (value == null) {
375 native_bind_null(index);
376 } else if (value instanceof Double || value instanceof Float) {
377 native_bind_double(index, ((Number) value).doubleValue());
378 } else if (value instanceof Number) {
379 native_bind_long(index, ((Number) value).longValue());
380 } else if (value instanceof Boolean) {
381 Boolean bool = (Boolean)value;
382 native_bind_long(index, (bool) ? 1 : 0);
383 if (bool) {
384 native_bind_long(index, 1);
385 } else {
386 native_bind_long(index, 0);
387 }
388 } else if (value instanceof byte[]){
389 native_bind_blob(index, (byte[]) value);
390 } else {
391 native_bind_string(index, value.toString());
392 }
393 }
394 }
395
396 /**
397 * Given an array of String bindArgs, this method binds all of them in one single call.
398 *
399 * @param bindArgs the String array of bind args.
400 */
401 public void bindAllArgsAsStrings(String[] bindArgs) {
402 if (bindArgs == null) {
403 return;
404 }
405 int size = bindArgs.length;
Vasu Nori24675612010-09-27 14:54:19 -0700406 for (int i = 0; i < size; i++) {
407 bindString(i + 1, bindArgs[i]);
Vasu Norie25539f2010-07-08 17:06:13 -0700408 }
409 }
410
Vasu Nori19666322010-09-10 14:09:00 -0700411 /* package */ synchronized final void setNativeHandle(int nHandle) {
412 this.nHandle = nHandle;
413 }
414
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415 /**
Vasu Nori8d45e4e2010-02-05 22:35:47 -0800416 * @deprecated This method is deprecated and must not be used.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417 * Compiles SQL into a SQLite program.
Vasu Nori5a03f362009-10-20 15:16:35 -0700418 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 * <P>The database lock must be held when calling this method.
420 * @param sql The SQL to compile.
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_compile(String sql);
Vasu Nori8d45e4e2010-02-05 22:35:47 -0800424
425 /**
426 * @deprecated This method is deprecated and must not be used.
427 */
Vasu Nori5a03f362009-10-20 15:16:35 -0700428 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800429 protected final native void native_finalize();
430
431 protected final native void native_bind_null(int index);
432 protected final native void native_bind_long(int index, long value);
433 protected final native void native_bind_double(int index, double value);
434 protected final native void native_bind_string(int index, String value);
435 protected final native void native_bind_blob(int index, byte[] value);
Vasu Nori0732f792010-07-29 17:24:12 -0700436 private final native void native_clear_bindings();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800437}
438