blob: 2bbc6d70c2a6fb1214bcbe3ca3b8a74cfcae2116 [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.
Jeff Brownd5064be2011-12-14 14:25:13 -080035 * @hide
Vasu Nori14b60e72010-03-01 14:47:47 -080036 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037 protected SQLiteDatabase mDatabase;
38
Vasu Nori5a03f362009-10-20 15:16:35 -070039 /** The SQL used to create this query */
40 /* package */ final String mSql;
41
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042 /**
43 * Native linkage, do not modify. This comes from the database and should not be modified
44 * in here or in the native code.
Jeff Brownd5064be2011-12-14 14:25:13 -080045 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046 */
Vasu Nori0732f792010-07-29 17:24:12 -070047 protected int nHandle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048
49 /**
Vasu Norie495d1f2010-01-06 16:34:19 -080050 * the SQLiteCompiledSql object for the given sql statement.
Vasu Nori5a03f362009-10-20 15:16:35 -070051 */
Vasu Nori422dad02010-09-03 16:03:08 -070052 /* package */ SQLiteCompiledSql mCompiledSql;
Vasu Nori5a03f362009-10-20 15:16:35 -070053
54 /**
Vasu Norie495d1f2010-01-06 16:34:19 -080055 * SQLiteCompiledSql statement id is populated with the corresponding object from the above
56 * member. This member is used by the native_bind_* methods
Jeff Brownd5064be2011-12-14 14:25:13 -080057 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058 */
Vasu Nori0732f792010-07-29 17:24:12 -070059 protected int nStatement;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060
Vasu Norie25539f2010-07-08 17:06:13 -070061 /**
62 * In the case of {@link SQLiteStatement}, this member stores the bindargs passed
63 * to the following methods, instead of actually doing the binding.
64 * <ul>
65 * <li>{@link #bindBlob(int, byte[])}</li>
66 * <li>{@link #bindDouble(int, double)}</li>
67 * <li>{@link #bindLong(int, long)}</li>
68 * <li>{@link #bindNull(int)}</li>
69 * <li>{@link #bindString(int, String)}</li>
70 * </ul>
71 * <p>
72 * Each entry in the array is a Pair of
73 * <ol>
74 * <li>bind arg position number</li>
75 * <li>the value to be bound to the bindarg</li>
76 * </ol>
77 * <p>
78 * It is lazily initialized in the above bind methods
79 * and it is cleared in {@link #clearBindings()} method.
80 * <p>
81 * It is protected (in multi-threaded environment) by {@link SQLiteProgram}.this
82 */
Vasu Nori0732f792010-07-29 17:24:12 -070083 /* package */ HashMap<Integer, Object> mBindArgs = null;
Vasu Norice38b982010-07-22 13:57:13 -070084 /* package */ final int mStatementType;
Vasu Nori4e874ed2010-09-15 18:40:49 -070085 /* package */ static final int STATEMENT_CACHEABLE = 16;
86 /* package */ static final int STATEMENT_DONT_PREPARE = 32;
87 /* package */ static final int STATEMENT_USE_POOLED_CONN = 64;
88 /* package */ static final int STATEMENT_TYPE_MASK = 0x0f;
Vasu Norie25539f2010-07-08 17:06:13 -070089
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090 /* package */ SQLiteProgram(SQLiteDatabase db, String sql) {
Vasu Nori0732f792010-07-29 17:24:12 -070091 this(db, sql, null, true);
Vasu Nori2827d6d2010-07-04 00:26:18 -070092 }
93
Vasu Nori0732f792010-07-29 17:24:12 -070094 /* package */ SQLiteProgram(SQLiteDatabase db, String sql, Object[] bindArgs,
95 boolean compileFlag) {
Vasu Norid606b4b2010-02-24 12:54:20 -080096 mSql = sql.trim();
Vasu Nori4e874ed2010-09-15 18:40:49 -070097 int n = DatabaseUtils.getSqlStatementType(mSql);
98 switch (n) {
99 case DatabaseUtils.STATEMENT_UPDATE:
100 mStatementType = n | STATEMENT_CACHEABLE;
101 break;
102 case DatabaseUtils.STATEMENT_SELECT:
103 mStatementType = n | STATEMENT_CACHEABLE | STATEMENT_USE_POOLED_CONN;
104 break;
Vasu Nori4e874ed2010-09-15 18:40:49 -0700105 case DatabaseUtils.STATEMENT_BEGIN:
106 case DatabaseUtils.STATEMENT_COMMIT:
107 case DatabaseUtils.STATEMENT_ABORT:
Vasu Nori4e874ed2010-09-15 18:40:49 -0700108 mStatementType = n | STATEMENT_DONT_PREPARE;
109 break;
110 default:
111 mStatementType = n;
112 }
Vasu Norie25539f2010-07-08 17:06:13 -0700113 db.acquireReference();
114 db.addSQLiteClosable(this);
115 mDatabase = db;
116 nHandle = db.mNativeHandle;
Vasu Nori0732f792010-07-29 17:24:12 -0700117 if (bindArgs != null) {
118 int size = bindArgs.length;
119 for (int i = 0; i < size; i++) {
120 this.addToBindArgs(i + 1, bindArgs[i]);
121 }
122 }
Vasu Nori2827d6d2010-07-04 00:26:18 -0700123 if (compileFlag) {
Vasu Nori0732f792010-07-29 17:24:12 -0700124 compileAndbindAllArgs();
Vasu Nori2827d6d2010-07-04 00:26:18 -0700125 }
Vasu Nori75010102010-07-01 16:23:06 -0700126 }
127
Vasu Norib729dcc2010-09-14 11:35:49 -0700128 private void compileSql() {
129 // only cache CRUD statements
Vasu Nori4e874ed2010-09-15 18:40:49 -0700130 if ((mStatementType & STATEMENT_CACHEABLE) == 0) {
Vasu Norib729dcc2010-09-14 11:35:49 -0700131 mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
132 nStatement = mCompiledSql.nStatement;
133 // since it is not in the cache, no need to acquire() it.
134 return;
135 }
136
137 mCompiledSql = mDatabase.getCompiledStatementForSql(mSql);
138 if (mCompiledSql == null) {
139 // create a new compiled-sql obj
140 mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
141
142 // add it to the cache of compiled-sqls
143 // but before adding it and thus making it available for anyone else to use it,
144 // make sure it is acquired by me.
145 mCompiledSql.acquire();
146 mDatabase.addToCompiledQueries(mSql, mCompiledSql);
147 } else {
148 // it is already in compiled-sql cache.
149 // try to acquire the object.
150 if (!mCompiledSql.acquire()) {
151 int last = mCompiledSql.nStatement;
152 // the SQLiteCompiledSql in cache is in use by some other SQLiteProgram object.
153 // we can't have two different SQLiteProgam objects can't share the same
154 // CompiledSql object. create a new one.
155 // finalize it when I am done with it in "this" object.
156 mCompiledSql = new SQLiteCompiledSql(mDatabase, mSql);
157 // since it is not in the cache, no need to acquire() it.
158 }
159 }
160 nStatement = mCompiledSql.nStatement;
161 }
162
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163 @Override
164 protected void onAllReferencesReleased() {
Vasu Noricc6f5492010-08-23 17:05:25 -0700165 release();
Vasu Norie25539f2010-07-08 17:06:13 -0700166 mDatabase.removeSQLiteClosable(this);
167 mDatabase.releaseReference();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800168 }
Vasu Nori5a03f362009-10-20 15:16:35 -0700169
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 @Override
Vasu Nori5a03f362009-10-20 15:16:35 -0700171 protected void onAllReferencesReleasedFromContainer() {
Vasu Noricc6f5492010-08-23 17:05:25 -0700172 release();
Vasu Nori5a03f362009-10-20 15:16:35 -0700173 mDatabase.releaseReference();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174 }
175
Vasu Nori24675612010-09-27 14:54:19 -0700176 /* package */ void release() {
Vasu Norie495d1f2010-01-06 16:34:19 -0800177 if (mCompiledSql == null) {
178 return;
179 }
Jesse Wilson9b5a9352011-02-10 11:19:09 -0800180 mDatabase.releaseCompiledSqlObj(mSql, mCompiledSql);
Vasu Nori75010102010-07-01 16:23:06 -0700181 mCompiledSql = null;
182 nStatement = 0;
Vasu Norie495d1f2010-01-06 16:34:19 -0800183 }
184
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800185 /**
186 * Returns a unique identifier for this program.
Vasu Nori5a03f362009-10-20 15:16:35 -0700187 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800188 * @return a unique identifier for this program
Vasu Nori59d60422010-07-03 15:37:21 -0700189 * @deprecated do not use this method. it is not guaranteed to be the same across executions of
190 * the SQL statement contained in this object.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800191 */
Vasu Nori59d60422010-07-03 15:37:21 -0700192 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800193 public final int getUniqueId() {
Vasu Nori59d60422010-07-03 15:37:21 -0700194 return -1;
195 }
196
197 /**
198 * used only for testing purposes
199 */
200 /* package */ int getSqlStatementId() {
201 synchronized(this) {
202 return (mCompiledSql == null) ? 0 : nStatement;
203 }
Vasu Nori5a03f362009-10-20 15:16:35 -0700204 }
205
206 /* package */ String getSqlString() {
207 return mSql;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800208 }
209
Vasu Nori0732f792010-07-29 17:24:12 -0700210 private void bind(int type, int index, Object value) {
Vasu Nori24675612010-09-27 14:54:19 -0700211 mDatabase.verifyDbIsOpen();
212 addToBindArgs(index, (type == Cursor.FIELD_TYPE_NULL) ? null : value);
213 if (nStatement > 0) {
214 // bind only if the SQL statement is compiled
215 acquireReference();
216 try {
217 switch (type) {
218 case Cursor.FIELD_TYPE_NULL:
219 native_bind_null(index);
220 break;
221 case Cursor.FIELD_TYPE_BLOB:
222 native_bind_blob(index, (byte[]) value);
223 break;
224 case Cursor.FIELD_TYPE_FLOAT:
225 native_bind_double(index, (Double) value);
226 break;
227 case Cursor.FIELD_TYPE_INTEGER:
228 native_bind_long(index, (Long) value);
229 break;
230 case Cursor.FIELD_TYPE_STRING:
231 default:
232 native_bind_string(index, (String) value);
233 break;
Vasu Nori0732f792010-07-29 17:24:12 -0700234 }
Vasu Nori24675612010-09-27 14:54:19 -0700235 } finally {
236 releaseReference();
Vasu Nori0732f792010-07-29 17:24:12 -0700237 }
238 }
239 }
240
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241 /**
242 * Bind a NULL value to this statement. The value remains bound until
243 * {@link #clearBindings} is called.
244 *
245 * @param index The 1-based index to the parameter to bind null to
246 */
247 public void bindNull(int index) {
Vasu Nori0732f792010-07-29 17:24:12 -0700248 bind(Cursor.FIELD_TYPE_NULL, index, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800249 }
250
251 /**
252 * Bind a long value to this statement. The value remains bound until
253 * {@link #clearBindings} is called.
Vasu Nori0732f792010-07-29 17:24:12 -0700254 *addToBindArgs
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800255 * @param index The 1-based index to the parameter to bind
256 * @param value The value to bind
257 */
258 public void bindLong(int index, long value) {
Vasu Nori0732f792010-07-29 17:24:12 -0700259 bind(Cursor.FIELD_TYPE_INTEGER, index, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800260 }
261
262 /**
263 * Bind a double value to this statement. The value remains bound until
264 * {@link #clearBindings} is called.
265 *
266 * @param index The 1-based index to the parameter to bind
267 * @param value The value to bind
268 */
269 public void bindDouble(int index, double value) {
Vasu Nori0732f792010-07-29 17:24:12 -0700270 bind(Cursor.FIELD_TYPE_FLOAT, index, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800271 }
272
273 /**
274 * Bind a String value to this statement. The value remains bound until
275 * {@link #clearBindings} is called.
276 *
277 * @param index The 1-based index to the parameter to bind
278 * @param value The value to bind
279 */
280 public void bindString(int index, String value) {
281 if (value == null) {
282 throw new IllegalArgumentException("the bind value at index " + index + " is null");
283 }
Vasu Nori0732f792010-07-29 17:24:12 -0700284 bind(Cursor.FIELD_TYPE_STRING, index, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800285 }
286
287 /**
288 * Bind a byte array value to this statement. The value remains bound until
289 * {@link #clearBindings} is called.
290 *
291 * @param index The 1-based index to the parameter to bind
292 * @param value The value to bind
293 */
294 public void bindBlob(int index, byte[] value) {
295 if (value == null) {
296 throw new IllegalArgumentException("the bind value at index " + index + " is null");
297 }
Vasu Nori0732f792010-07-29 17:24:12 -0700298 bind(Cursor.FIELD_TYPE_BLOB, index, value);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800299 }
300
301 /**
302 * Clears all existing bindings. Unset bindings are treated as NULL.
303 */
304 public void clearBindings() {
Vasu Nori24675612010-09-27 14:54:19 -0700305 mBindArgs = null;
306 if (this.nStatement == 0) {
307 return;
308 }
309 mDatabase.verifyDbIsOpen();
310 acquireReference();
311 try {
312 native_clear_bindings();
313 } finally {
314 releaseReference();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800315 }
316 }
317
318 /**
319 * Release this program's resources, making it invalid.
320 */
321 public void close() {
Vasu Nori24675612010-09-27 14:54:19 -0700322 mBindArgs = null;
323 if (nHandle == 0 || !mDatabase.isOpen()) {
324 return;
Vasu Noric8e1f232010-04-13 15:05:09 -0700325 }
Vasu Nori24675612010-09-27 14:54:19 -0700326 releaseReference();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800327 }
328
Vasu Nori24675612010-09-27 14:54:19 -0700329 private void addToBindArgs(int index, Object value) {
Vasu Norice38b982010-07-22 13:57:13 -0700330 if (mBindArgs == null) {
Vasu Nori0732f792010-07-29 17:24:12 -0700331 mBindArgs = new HashMap<Integer, Object>();
Vasu Norie25539f2010-07-08 17:06:13 -0700332 }
Vasu Nori0732f792010-07-29 17:24:12 -0700333 mBindArgs.put(index, value);
Vasu Norie25539f2010-07-08 17:06:13 -0700334 }
335
Vasu Nori24675612010-09-27 14:54:19 -0700336 /* package */ void compileAndbindAllArgs() {
Vasu Nori4e874ed2010-09-15 18:40:49 -0700337 if ((mStatementType & STATEMENT_DONT_PREPARE) > 0) {
Vasu Noricc1eaf62011-03-14 19:22:16 -0700338 if (mBindArgs != null) {
339 throw new IllegalArgumentException("Can't pass bindargs for this sql :" + mSql);
Vasu Nori4e874ed2010-09-15 18:40:49 -0700340 }
Vasu Noricc1eaf62011-03-14 19:22:16 -0700341 // no need to prepare this SQL statement
Vasu Nori4e874ed2010-09-15 18:40:49 -0700342 return;
343 }
Vasu Nori0732f792010-07-29 17:24:12 -0700344 if (nStatement == 0) {
345 // SQL statement is not compiled yet. compile it now.
Vasu Norib729dcc2010-09-14 11:35:49 -0700346 compileSql();
Vasu Nori0732f792010-07-29 17:24:12 -0700347 }
Vasu Norice38b982010-07-22 13:57:13 -0700348 if (mBindArgs == null) {
Vasu Norie25539f2010-07-08 17:06:13 -0700349 return;
350 }
Vasu Nori0732f792010-07-29 17:24:12 -0700351 for (int index : mBindArgs.keySet()) {
352 Object value = mBindArgs.get(index);
353 if (value == null) {
354 native_bind_null(index);
355 } else if (value instanceof Double || value instanceof Float) {
356 native_bind_double(index, ((Number) value).doubleValue());
357 } else if (value instanceof Number) {
358 native_bind_long(index, ((Number) value).longValue());
359 } else if (value instanceof Boolean) {
360 Boolean bool = (Boolean)value;
361 native_bind_long(index, (bool) ? 1 : 0);
362 if (bool) {
363 native_bind_long(index, 1);
364 } else {
365 native_bind_long(index, 0);
366 }
367 } else if (value instanceof byte[]){
368 native_bind_blob(index, (byte[]) value);
369 } else {
370 native_bind_string(index, value.toString());
371 }
372 }
373 }
374
375 /**
376 * Given an array of String bindArgs, this method binds all of them in one single call.
377 *
378 * @param bindArgs the String array of bind args.
379 */
380 public void bindAllArgsAsStrings(String[] bindArgs) {
381 if (bindArgs == null) {
382 return;
383 }
384 int size = bindArgs.length;
Vasu Nori24675612010-09-27 14:54:19 -0700385 for (int i = 0; i < size; i++) {
386 bindString(i + 1, bindArgs[i]);
Vasu Norie25539f2010-07-08 17:06:13 -0700387 }
388 }
389
Vasu Nori19666322010-09-10 14:09:00 -0700390 /* package */ synchronized final void setNativeHandle(int nHandle) {
391 this.nHandle = nHandle;
392 }
393
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394 /**
Jeff Brownd5064be2011-12-14 14:25:13 -0800395 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800396 * Compiles SQL into a SQLite program.
Vasu Nori5a03f362009-10-20 15:16:35 -0700397 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 * <P>The database lock must be held when calling this method.
399 * @param sql The SQL to compile.
400 */
401 protected final native void native_compile(String sql);
Vasu Nori8d45e4e2010-02-05 22:35:47 -0800402
403 /**
Jeff Brownd5064be2011-12-14 14:25:13 -0800404 * @hide
Vasu Nori8d45e4e2010-02-05 22:35:47 -0800405 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800406 protected final native void native_finalize();
407
Jeff Brownd5064be2011-12-14 14:25:13 -0800408 /** @hide */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800409 protected final native void native_bind_null(int index);
Jeff Brownd5064be2011-12-14 14:25:13 -0800410 /** @hide */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800411 protected final native void native_bind_long(int index, long value);
Jeff Brownd5064be2011-12-14 14:25:13 -0800412 /** @hide */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 protected final native void native_bind_double(int index, double value);
Jeff Brownd5064be2011-12-14 14:25:13 -0800414 /** @hide */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415 protected final native void native_bind_string(int index, String value);
Jeff Brownd5064be2011-12-14 14:25:13 -0800416 /** @hide */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417 protected final native void native_bind_blob(int index, byte[] value);
Vasu Nori0732f792010-07-29 17:24:12 -0700418 private final native void native_clear_bindings();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419}
420