• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.database.DatabaseUtils;
20 import android.database.Cursor;
21 
22 import java.util.HashMap;
23 
24 /**
25  * A base class for compiled SQLite programs.
26  *<p>
27  * SQLiteProgram is NOT internally synchronized so code using a SQLiteProgram from multiple
28  * threads should perform its own synchronization when using the SQLiteProgram.
29  */
30 public abstract class SQLiteProgram extends SQLiteClosable {
31 
32     private static final String TAG = "SQLiteProgram";
33 
34     /** The database this program is compiled against.
35      * @deprecated do not use this
36      */
37     @Deprecated
38     protected SQLiteDatabase mDatabase;
39 
40     /** The SQL used to create this query */
41     /* package */ final String mSql;
42 
43     /**
44      * Native linkage, do not modify. This comes from the database and should not be modified
45      * in here or in the native code.
46      * @deprecated do not use this
47      */
48     @Deprecated
49     protected int nHandle;
50 
51     /**
52      * the SQLiteCompiledSql object for the given sql statement.
53      */
54     /* package */ SQLiteCompiledSql mCompiledSql;
55 
56     /**
57      * SQLiteCompiledSql statement id is populated with the corresponding object from the above
58      * member. This member is used by the native_bind_* methods
59      * @deprecated do not use this
60      */
61     @Deprecated
62     protected int nStatement;
63 
64     /**
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      */
86     /* package */ HashMap<Integer, Object> mBindArgs = null;
87     /* package */ final int mStatementType;
88     /* 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;
92 
SQLiteProgram(SQLiteDatabase db, String sql)93     /* package */ SQLiteProgram(SQLiteDatabase db, String sql) {
94         this(db, sql, null, true);
95     }
96 
SQLiteProgram(SQLiteDatabase db, String sql, Object[] bindArgs, boolean compileFlag)97     /* package */ SQLiteProgram(SQLiteDatabase db, String sql, Object[] bindArgs,
98             boolean compileFlag) {
99         mSql = sql.trim();
100         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_BEGIN:
109             case DatabaseUtils.STATEMENT_COMMIT:
110             case DatabaseUtils.STATEMENT_ABORT:
111                 mStatementType = n | STATEMENT_DONT_PREPARE;
112                 break;
113             default:
114                 mStatementType = n;
115         }
116         db.acquireReference();
117         db.addSQLiteClosable(this);
118         mDatabase = db;
119         nHandle = db.mNativeHandle;
120         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         }
126         if (compileFlag) {
127             compileAndbindAllArgs();
128         }
129     }
130 
compileSql()131     private void compileSql() {
132         // only cache CRUD statements
133         if ((mStatementType & STATEMENT_CACHEABLE) == 0) {
134             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 
166     @Override
onAllReferencesReleased()167     protected void onAllReferencesReleased() {
168         release();
169         mDatabase.removeSQLiteClosable(this);
170         mDatabase.releaseReference();
171     }
172 
173     @Override
onAllReferencesReleasedFromContainer()174     protected void onAllReferencesReleasedFromContainer() {
175         release();
176         mDatabase.releaseReference();
177     }
178 
release()179     /* package */ void release() {
180         if (mCompiledSql == null) {
181             return;
182         }
183         mDatabase.releaseCompiledSqlObj(mSql, mCompiledSql);
184         mCompiledSql = null;
185         nStatement = 0;
186     }
187 
188     /**
189      * Returns a unique identifier for this program.
190      *
191      * @return a unique identifier for this program
192      * @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.
194      */
195     @Deprecated
getUniqueId()196     public final int getUniqueId() {
197       return -1;
198     }
199 
200     /**
201      * used only for testing purposes
202      */
getSqlStatementId()203     /* package */ int getSqlStatementId() {
204       synchronized(this) {
205         return (mCompiledSql == null) ? 0 : nStatement;
206       }
207     }
208 
getSqlString()209     /* package */ String getSqlString() {
210         return mSql;
211     }
212 
213     /**
214      * @deprecated This method is deprecated and must not be used.
215      *
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      */
220     @Deprecated
compile(String sql, boolean forceCompilation)221     protected void compile(String sql, boolean forceCompilation) {
222         // TODO is there a need for this?
223     }
224 
bind(int type, int index, Object value)225     private void bind(int type, int index, Object value) {
226         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;
249                 }
250             } finally {
251                 releaseReference();
252             }
253         }
254     }
255 
256     /**
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      */
bindNull(int index)262     public void bindNull(int index) {
263         bind(Cursor.FIELD_TYPE_NULL, index, null);
264     }
265 
266     /**
267      * Bind a long value to this statement. The value remains bound until
268      * {@link #clearBindings} is called.
269      *addToBindArgs
270      * @param index The 1-based index to the parameter to bind
271      * @param value The value to bind
272      */
bindLong(int index, long value)273     public void bindLong(int index, long value) {
274         bind(Cursor.FIELD_TYPE_INTEGER, index, value);
275     }
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      */
bindDouble(int index, double value)284     public void bindDouble(int index, double value) {
285         bind(Cursor.FIELD_TYPE_FLOAT, index, value);
286     }
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      */
bindString(int index, String value)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         }
299         bind(Cursor.FIELD_TYPE_STRING, index, value);
300     }
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      */
bindBlob(int index, byte[] value)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         }
313         bind(Cursor.FIELD_TYPE_BLOB, index, value);
314     }
315 
316     /**
317      * Clears all existing bindings. Unset bindings are treated as NULL.
318      */
clearBindings()319     public void clearBindings() {
320         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();
330         }
331     }
332 
333     /**
334      * Release this program's resources, making it invalid.
335      */
close()336     public void close() {
337         mBindArgs = null;
338         if (nHandle == 0 || !mDatabase.isOpen()) {
339             return;
340         }
341         releaseReference();
342     }
343 
addToBindArgs(int index, Object value)344     private void addToBindArgs(int index, Object value) {
345         if (mBindArgs == null) {
346             mBindArgs = new HashMap<Integer, Object>();
347         }
348         mBindArgs.put(index, value);
349     }
350 
compileAndbindAllArgs()351     /* package */ void compileAndbindAllArgs() {
352         if ((mStatementType & STATEMENT_DONT_PREPARE) > 0) {
353             if (mBindArgs != null) {
354                 throw new IllegalArgumentException("Can't pass bindargs for this sql :" + mSql);
355             }
356             // no need to prepare this SQL statement
357             return;
358         }
359         if (nStatement == 0) {
360             // SQL statement is not compiled yet. compile it now.
361             compileSql();
362         }
363         if (mBindArgs == null) {
364             return;
365         }
366         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      */
bindAllArgsAsStrings(String[] bindArgs)395     public void bindAllArgsAsStrings(String[] bindArgs) {
396         if (bindArgs == null) {
397             return;
398         }
399         int size = bindArgs.length;
400         for (int i = 0; i < size; i++) {
401             bindString(i + 1, bindArgs[i]);
402         }
403     }
404 
setNativeHandle(int nHandle)405     /* package */ synchronized final void setNativeHandle(int nHandle) {
406         this.nHandle = nHandle;
407     }
408 
409     /**
410      * @deprecated This method is deprecated and must not be used.
411      * Compiles SQL into a SQLite program.
412      *
413      * <P>The database lock must be held when calling this method.
414      * @param sql The SQL to compile.
415      */
416     @Deprecated
native_compile(String sql)417     protected final native void native_compile(String sql);
418 
419     /**
420      * @deprecated This method is deprecated and must not be used.
421      */
422     @Deprecated
native_finalize()423     protected final native void native_finalize();
424 
native_bind_null(int index)425     protected final native void native_bind_null(int index);
native_bind_long(int index, long value)426     protected final native void native_bind_long(int index, long value);
native_bind_double(int index, double value)427     protected final native void native_bind_double(int index, double value);
native_bind_string(int index, String value)428     protected final native void native_bind_string(int index, String value);
native_bind_blob(int index, byte[] value)429     protected final native void native_bind_blob(int index, byte[] value);
native_clear_bindings()430     private final native void native_clear_bindings();
431 }
432 
433