blob: a0e9d0248769ae9e0442da8e5c2dbfd52e45a0b9 [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 Nori5a03f362009-10-20 15:16:35 -070019import com.google.android.collect.Maps;
20
Dan Egnor12311952009-11-23 14:47:45 -080021import android.app.ActivityThread;
Dianne Hackborn01e4cfc2010-06-24 15:07:24 -070022import android.app.AppGlobals;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.content.ContentValues;
24import android.database.Cursor;
25import android.database.DatabaseUtils;
26import android.database.SQLException;
Vasu Noric3849202010-03-09 10:47:25 -080027import android.database.sqlite.SQLiteDebug.DbStats;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.os.Debug;
29import android.os.SystemClock;
Dmitri Plotnikov90142c92009-09-15 10:52:17 -070030import android.os.SystemProperties;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.text.TextUtils;
32import android.util.Config;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.util.EventLog;
Dmitri Plotnikov90142c92009-09-15 10:52:17 -070034import android.util.Log;
Vasu Noric3849202010-03-09 10:47:25 -080035import android.util.Pair;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036
Brad Fitzpatrickcfda9f32010-06-03 12:52:54 -070037import dalvik.system.BlockGuard;
38
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import java.io.File;
Vasu Noric3849202010-03-09 10:47:25 -080040import java.lang.ref.WeakReference;
Vasu Nori5a03f362009-10-20 15:16:35 -070041import java.text.SimpleDateFormat;
Vasu Noric3849202010-03-09 10:47:25 -080042import java.util.ArrayList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import java.util.HashMap;
Vasu Noric3849202010-03-09 10:47:25 -080044import java.util.HashSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import java.util.Iterator;
46import java.util.Locale;
47import java.util.Map;
Dan Egnor12311952009-11-23 14:47:45 -080048import java.util.Random;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import java.util.Set;
50import java.util.WeakHashMap;
51import java.util.concurrent.locks.ReentrantLock;
Brad Fitzpatrickd8330232010-02-19 10:59:01 -080052import java.util.regex.Pattern;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053
54/**
55 * Exposes methods to manage a SQLite database.
56 * <p>SQLiteDatabase has methods to create, delete, execute SQL commands, and
57 * perform other common database management tasks.
58 * <p>See the Notepad sample application in the SDK for an example of creating
59 * and managing a database.
60 * <p> Database names must be unique within an application, not across all
61 * applications.
62 *
63 * <h3>Localized Collation - ORDER BY</h3>
64 * <p>In addition to SQLite's default <code>BINARY</code> collator, Android supplies
65 * two more, <code>LOCALIZED</code>, which changes with the system's current locale
66 * if you wire it up correctly (XXX a link needed!), and <code>UNICODE</code>, which
67 * is the Unicode Collation Algorithm and not tailored to the current locale.
68 */
69public class SQLiteDatabase extends SQLiteClosable {
70 private static final String TAG = "Database";
Jeff Hamilton082c2af2009-09-29 11:49:51 -070071 private static final int EVENT_DB_OPERATION = 52000;
72 private static final int EVENT_DB_CORRUPT = 75004;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073
74 /**
75 * Algorithms used in ON CONFLICT clause
76 * http://www.sqlite.org/lang_conflict.html
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077 */
Vasu Nori8d45e4e2010-02-05 22:35:47 -080078 /**
79 * When a constraint violation occurs, an immediate ROLLBACK occurs,
80 * thus ending the current transaction, and the command aborts with a
81 * return code of SQLITE_CONSTRAINT. If no transaction is active
82 * (other than the implied transaction that is created on every command)
83 * then this algorithm works the same as ABORT.
84 */
85 public static final int CONFLICT_ROLLBACK = 1;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -070086
Vasu Nori8d45e4e2010-02-05 22:35:47 -080087 /**
88 * When a constraint violation occurs,no ROLLBACK is executed
89 * so changes from prior commands within the same transaction
90 * are preserved. This is the default behavior.
91 */
92 public static final int CONFLICT_ABORT = 2;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -070093
Vasu Nori8d45e4e2010-02-05 22:35:47 -080094 /**
95 * When a constraint violation occurs, the command aborts with a return
96 * code SQLITE_CONSTRAINT. But any changes to the database that
97 * the command made prior to encountering the constraint violation
98 * are preserved and are not backed out.
99 */
100 public static final int CONFLICT_FAIL = 3;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700101
Vasu Nori8d45e4e2010-02-05 22:35:47 -0800102 /**
103 * When a constraint violation occurs, the one row that contains
104 * the constraint violation is not inserted or changed.
105 * But the command continues executing normally. Other rows before and
106 * after the row that contained the constraint violation continue to be
107 * inserted or updated normally. No error is returned.
108 */
109 public static final int CONFLICT_IGNORE = 4;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700110
Vasu Nori8d45e4e2010-02-05 22:35:47 -0800111 /**
112 * When a UNIQUE constraint violation occurs, the pre-existing rows that
113 * are causing the constraint violation are removed prior to inserting
114 * or updating the current row. Thus the insert or update always occurs.
115 * The command continues executing normally. No error is returned.
116 * If a NOT NULL constraint violation occurs, the NULL value is replaced
117 * by the default value for that column. If the column has no default
118 * value, then the ABORT algorithm is used. If a CHECK constraint
119 * violation occurs then the IGNORE algorithm is used. When this conflict
120 * resolution strategy deletes rows in order to satisfy a constraint,
121 * it does not invoke delete triggers on those rows.
122 * This behavior might change in a future release.
123 */
124 public static final int CONFLICT_REPLACE = 5;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700125
Vasu Nori8d45e4e2010-02-05 22:35:47 -0800126 /**
127 * use the following when no conflict action is specified.
128 */
129 public static final int CONFLICT_NONE = 0;
130 private static final String[] CONFLICT_VALUES = new String[]
131 {"", " OR ROLLBACK ", " OR ABORT ", " OR FAIL ", " OR IGNORE ", " OR REPLACE "};
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700132
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 /**
134 * Maximum Length Of A LIKE Or GLOB Pattern
135 * The pattern matching algorithm used in the default LIKE and GLOB implementation
136 * of SQLite can exhibit O(N^2) performance (where N is the number of characters in
137 * the pattern) for certain pathological cases. To avoid denial-of-service attacks
138 * the length of the LIKE or GLOB pattern is limited to SQLITE_MAX_LIKE_PATTERN_LENGTH bytes.
139 * The default value of this limit is 50000. A modern workstation can evaluate
140 * even a pathological LIKE or GLOB pattern of 50000 bytes relatively quickly.
141 * The denial of service problem only comes into play when the pattern length gets
142 * into millions of bytes. Nevertheless, since most useful LIKE or GLOB patterns
143 * are at most a few dozen bytes in length, paranoid application developers may
144 * want to reduce this parameter to something in the range of a few hundred
145 * if they know that external users are able to generate arbitrary patterns.
146 */
147 public static final int SQLITE_MAX_LIKE_PATTERN_LENGTH = 50000;
148
149 /**
150 * Flag for {@link #openDatabase} to open the database for reading and writing.
151 * If the disk is full, this may fail even before you actually write anything.
152 *
153 * {@more} Note that the value of this flag is 0, so it is the default.
154 */
155 public static final int OPEN_READWRITE = 0x00000000; // update native code if changing
156
157 /**
158 * Flag for {@link #openDatabase} to open the database for reading only.
159 * This is the only reliable way to open a database if the disk may be full.
160 */
161 public static final int OPEN_READONLY = 0x00000001; // update native code if changing
162
163 private static final int OPEN_READ_MASK = 0x00000001; // update native code if changing
164
165 /**
166 * Flag for {@link #openDatabase} to open the database without support for localized collators.
167 *
168 * {@more} This causes the collator <code>LOCALIZED</code> not to be created.
169 * You must be consistent when using this flag to use the setting the database was
170 * created with. If this is set, {@link #setLocale} will do nothing.
171 */
172 public static final int NO_LOCALIZED_COLLATORS = 0x00000010; // update native code if changing
173
174 /**
175 * Flag for {@link #openDatabase} to create the database file if it does not already exist.
176 */
177 public static final int CREATE_IF_NECESSARY = 0x10000000; // update native code if changing
178
179 /**
180 * Indicates whether the most-recently started transaction has been marked as successful.
181 */
182 private boolean mInnerTransactionIsSuccessful;
183
184 /**
185 * Valid during the life of a transaction, and indicates whether the entire transaction (the
186 * outer one and all of the inner ones) so far has been successful.
187 */
188 private boolean mTransactionIsSuccessful;
189
Fred Quintanac4516a72009-09-03 12:14:06 -0700190 /**
191 * Valid during the life of a transaction.
192 */
193 private SQLiteTransactionListener mTransactionListener;
194
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800195 /** Synchronize on this when accessing the database */
196 private final ReentrantLock mLock = new ReentrantLock(true);
197
198 private long mLockAcquiredWallTime = 0L;
199 private long mLockAcquiredThreadTime = 0L;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700200
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201 // limit the frequency of complaints about each database to one within 20 sec
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700202 // unless run command adb shell setprop log.tag.Database VERBOSE
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 private static final int LOCK_WARNING_WINDOW_IN_MS = 20000;
204 /** If the lock is held this long then a warning will be printed when it is released. */
205 private static final int LOCK_ACQUIRED_WARNING_TIME_IN_MS = 300;
206 private static final int LOCK_ACQUIRED_WARNING_THREAD_TIME_IN_MS = 100;
207 private static final int LOCK_ACQUIRED_WARNING_TIME_IN_MS_ALWAYS_PRINT = 2000;
208
Dmitri Plotnikovb43b58d2009-09-09 18:10:42 -0700209 private static final int SLEEP_AFTER_YIELD_QUANTUM = 1000;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700210
Brad Fitzpatrickd8330232010-02-19 10:59:01 -0800211 // The pattern we remove from database filenames before
212 // potentially logging them.
213 private static final Pattern EMAIL_IN_DB_PATTERN = Pattern.compile("[\\w\\.\\-]+@[\\w\\.\\-]+");
214
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800215 private long mLastLockMessageTime = 0L;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700216
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -0800217 // Things related to query logging/sampling for debugging
218 // slow/frequent queries during development. Always log queries
Brad Fitzpatrick722802e2010-03-23 22:22:16 -0700219 // which take (by default) 500ms+; shorter queries are sampled
220 // accordingly. Commit statements, which are typically slow, are
221 // logged together with the most recently executed SQL statement,
222 // for disambiguation. The 500ms value is configurable via a
223 // SystemProperty, but developers actively debugging database I/O
224 // should probably use the regular log tunable,
225 // LOG_SLOW_QUERIES_PROPERTY, defined below.
226 private static int sQueryLogTimeInMillis = 0; // lazily initialized
Dan Egnor12311952009-11-23 14:47:45 -0800227 private static final int QUERY_LOG_SQL_LENGTH = 64;
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -0800228 private static final String COMMIT_SQL = "COMMIT;";
Dan Egnor12311952009-11-23 14:47:45 -0800229 private final Random mRandom = new Random();
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -0800230 private String mLastSqlStatement = null;
Dan Egnor12311952009-11-23 14:47:45 -0800231
Brad Fitzpatrick722802e2010-03-23 22:22:16 -0700232 // String prefix for slow database query EventLog records that show
233 // lock acquistions of the database.
234 /* package */ static final String GET_LOCK_LOG_PREFIX = "GETLOCK:";
235
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800236 /** Used by native code, do not rename */
237 /* package */ int mNativeHandle = 0;
238
239 /** Used to make temp table names unique */
240 /* package */ int mTempTableSequence = 0;
241
242 /** The path for the database file */
243 private String mPath;
244
Brad Fitzpatrickd8330232010-02-19 10:59:01 -0800245 /** The anonymized path for the database file for logging purposes */
246 private String mPathForLogs = null; // lazily populated
247
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800248 /** The flags passed to open/create */
249 private int mFlags;
250
251 /** The optional factory to use when creating new Cursors */
252 private CursorFactory mFactory;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700253
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254 private WeakHashMap<SQLiteClosable, Object> mPrograms;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700255
Vasu Nori5a03f362009-10-20 15:16:35 -0700256 /**
257 * for each instance of this class, a cache is maintained to store
258 * the compiled query statement ids returned by sqlite database.
259 * key = sql statement with "?" for bind args
260 * value = {@link SQLiteCompiledSql}
261 * If an application opens the database and keeps it open during its entire life, then
262 * there will not be an overhead of compilation of sql statements by sqlite.
263 *
264 * why is this cache NOT static? because sqlite attaches compiledsql statements to the
265 * struct created when {@link SQLiteDatabase#openDatabase(String, CursorFactory, int)} is
266 * invoked.
267 *
268 * this cache has an upper limit of mMaxSqlCacheSize (settable by calling the method
269 * (@link setMaxCacheSize(int)}). its default is 0 - i.e., no caching by default because
270 * most of the apps don't use "?" syntax in their sql, caching is not useful for them.
271 */
Vasu Norie495d1f2010-01-06 16:34:19 -0800272 /* package */ Map<String, SQLiteCompiledSql> mCompiledQueries = Maps.newHashMap();
273 /**
274 * @hide
275 */
276 public static final int MAX_SQL_CACHE_SIZE = 250;
277 private int mMaxSqlCacheSize = MAX_SQL_CACHE_SIZE; // max cache size per Database instance
Vasu Norie9d92102010-01-20 15:07:26 -0800278 private int mCacheFullWarnings;
Vasu Nori49d02ac2010-03-05 21:49:30 -0800279 private static final int MAX_WARNINGS_ON_CACHESIZE_CONDITION = 1;
Vasu Nori5a03f362009-10-20 15:16:35 -0700280
281 /** maintain stats about number of cache hits and misses */
282 private int mNumCacheHits;
283 private int mNumCacheMisses;
284
285 /** the following 2 members maintain the time when a database is opened and closed */
286 private String mTimeOpened = null;
287 private String mTimeClosed = null;
288
Vasu Norid606b4b2010-02-24 12:54:20 -0800289 /** Used to find out where this object was created in case it never got closed. */
290 private Throwable mStackTrace = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800291
Dmitri Plotnikov90142c92009-09-15 10:52:17 -0700292 // System property that enables logging of slow queries. Specify the threshold in ms.
293 private static final String LOG_SLOW_QUERIES_PROPERTY = "db.log.slow_query_threshold";
294 private final int mSlowQueryThreshold;
295
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296 /**
297 * @param closable
298 */
299 void addSQLiteClosable(SQLiteClosable closable) {
300 lock();
301 try {
302 mPrograms.put(closable, null);
303 } finally {
304 unlock();
305 }
306 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700307
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800308 void removeSQLiteClosable(SQLiteClosable closable) {
309 lock();
310 try {
311 mPrograms.remove(closable);
312 } finally {
313 unlock();
314 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700315 }
316
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800317 @Override
318 protected void onAllReferencesReleased() {
319 if (isOpen()) {
Vasu Nori5a03f362009-10-20 15:16:35 -0700320 if (SQLiteDebug.DEBUG_SQL_CACHE) {
321 mTimeClosed = getTime();
322 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323 dbclose();
324 }
325 }
326
327 /**
328 * Attempts to release memory that SQLite holds but does not require to
329 * operate properly. Typically this memory will come from the page cache.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700330 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800331 * @return the number of bytes actually released
332 */
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700333 static public native int releaseMemory();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334
335 /**
336 * Control whether or not the SQLiteDatabase is made thread-safe by using locks
337 * around critical sections. This is pretty expensive, so if you know that your
338 * DB will only be used by a single thread then you should set this to false.
339 * The default is true.
340 * @param lockingEnabled set to true to enable locks, false otherwise
341 */
342 public void setLockingEnabled(boolean lockingEnabled) {
343 mLockingEnabled = lockingEnabled;
344 }
345
346 /**
347 * If set then the SQLiteDatabase is made thread-safe by using locks
348 * around critical sections
349 */
350 private boolean mLockingEnabled = true;
351
352 /* package */ void onCorruption() {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700353 Log.e(TAG, "Removing corrupt database: " + mPath);
354 EventLog.writeEvent(EVENT_DB_CORRUPT, mPath);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800355 try {
356 // Close the database (if we can), which will cause subsequent operations to fail.
357 close();
358 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 // Delete the corrupt file. Don't re-create it now -- that would just confuse people
360 // -- but the next time someone tries to open it, they can set it up from scratch.
Vasu Norif3cf8a42010-03-23 11:41:44 -0700361 if (!mPath.equalsIgnoreCase(":memory")) {
362 // delete is only for non-memory database files
363 new File(mPath).delete();
364 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800365 }
366 }
367
368 /**
369 * Locks the database for exclusive access. The database lock must be held when
370 * touch the native sqlite3* object since it is single threaded and uses
371 * a polling lock contention algorithm. The lock is recursive, and may be acquired
372 * multiple times by the same thread. This is a no-op if mLockingEnabled is false.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700373 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 * @see #unlock()
375 */
376 /* package */ void lock() {
377 if (!mLockingEnabled) return;
378 mLock.lock();
379 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
380 if (mLock.getHoldCount() == 1) {
381 // Use elapsed real-time since the CPU may sleep when waiting for IO
382 mLockAcquiredWallTime = SystemClock.elapsedRealtime();
383 mLockAcquiredThreadTime = Debug.threadCpuTimeNanos();
384 }
385 }
386 }
387
388 /**
389 * Locks the database for exclusive access. The database lock must be held when
390 * touch the native sqlite3* object since it is single threaded and uses
391 * a polling lock contention algorithm. The lock is recursive, and may be acquired
392 * multiple times by the same thread.
393 *
394 * @see #unlockForced()
395 */
396 private void lockForced() {
397 mLock.lock();
398 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
399 if (mLock.getHoldCount() == 1) {
400 // Use elapsed real-time since the CPU may sleep when waiting for IO
401 mLockAcquiredWallTime = SystemClock.elapsedRealtime();
402 mLockAcquiredThreadTime = Debug.threadCpuTimeNanos();
403 }
404 }
405 }
406
407 /**
408 * Releases the database lock. This is a no-op if mLockingEnabled is false.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700409 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800410 * @see #unlock()
411 */
412 /* package */ void unlock() {
413 if (!mLockingEnabled) return;
414 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
415 if (mLock.getHoldCount() == 1) {
416 checkLockHoldTime();
417 }
418 }
419 mLock.unlock();
420 }
421
422 /**
423 * Releases the database lock.
424 *
425 * @see #unlockForced()
426 */
427 private void unlockForced() {
428 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
429 if (mLock.getHoldCount() == 1) {
430 checkLockHoldTime();
431 }
432 }
433 mLock.unlock();
434 }
435
436 private void checkLockHoldTime() {
437 // Use elapsed real-time since the CPU may sleep when waiting for IO
438 long elapsedTime = SystemClock.elapsedRealtime();
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700439 long lockedTime = elapsedTime - mLockAcquiredWallTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800440 if (lockedTime < LOCK_ACQUIRED_WARNING_TIME_IN_MS_ALWAYS_PRINT &&
441 !Log.isLoggable(TAG, Log.VERBOSE) &&
442 (elapsedTime - mLastLockMessageTime) < LOCK_WARNING_WINDOW_IN_MS) {
443 return;
444 }
445 if (lockedTime > LOCK_ACQUIRED_WARNING_TIME_IN_MS) {
446 int threadTime = (int)
447 ((Debug.threadCpuTimeNanos() - mLockAcquiredThreadTime) / 1000000);
448 if (threadTime > LOCK_ACQUIRED_WARNING_THREAD_TIME_IN_MS ||
449 lockedTime > LOCK_ACQUIRED_WARNING_TIME_IN_MS_ALWAYS_PRINT) {
450 mLastLockMessageTime = elapsedTime;
451 String msg = "lock held on " + mPath + " for " + lockedTime + "ms. Thread time was "
452 + threadTime + "ms";
453 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING_STACK_TRACE) {
454 Log.d(TAG, msg, new Exception());
455 } else {
456 Log.d(TAG, msg);
457 }
458 }
459 }
460 }
461
462 /**
463 * Begins a transaction. Transactions can be nested. When the outer transaction is ended all of
464 * the work done in that transaction and all of the nested transactions will be committed or
465 * rolled back. The changes will be rolled back if any transaction is ended without being
466 * marked as clean (by calling setTransactionSuccessful). Otherwise they will be committed.
467 *
468 * <p>Here is the standard idiom for transactions:
469 *
470 * <pre>
471 * db.beginTransaction();
472 * try {
473 * ...
474 * db.setTransactionSuccessful();
475 * } finally {
476 * db.endTransaction();
477 * }
478 * </pre>
479 */
480 public void beginTransaction() {
Fred Quintanac4516a72009-09-03 12:14:06 -0700481 beginTransactionWithListener(null /* transactionStatusCallback */);
482 }
483
484 /**
485 * Begins a transaction. Transactions can be nested. When the outer transaction is ended all of
486 * the work done in that transaction and all of the nested transactions will be committed or
487 * rolled back. The changes will be rolled back if any transaction is ended without being
488 * marked as clean (by calling setTransactionSuccessful). Otherwise they will be committed.
489 *
490 * <p>Here is the standard idiom for transactions:
491 *
492 * <pre>
493 * db.beginTransactionWithListener(listener);
494 * try {
495 * ...
496 * db.setTransactionSuccessful();
497 * } finally {
498 * db.endTransaction();
499 * }
500 * </pre>
501 * @param transactionListener listener that should be notified when the transaction begins,
502 * commits, or is rolled back, either explicitly or by a call to
503 * {@link #yieldIfContendedSafely}.
504 */
505 public void beginTransactionWithListener(SQLiteTransactionListener transactionListener) {
Vasu Noric8e1f232010-04-13 15:05:09 -0700506 lockForced();
Vasu Norif3cf8a42010-03-23 11:41:44 -0700507 if (!isOpen()) {
508 throw new IllegalStateException("database not open");
509 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800510 boolean ok = false;
511 try {
512 // If this thread already had the lock then get out
513 if (mLock.getHoldCount() > 1) {
514 if (mInnerTransactionIsSuccessful) {
515 String msg = "Cannot call beginTransaction between "
516 + "calling setTransactionSuccessful and endTransaction";
517 IllegalStateException e = new IllegalStateException(msg);
518 Log.e(TAG, "beginTransaction() failed", e);
519 throw e;
520 }
521 ok = true;
522 return;
523 }
524
525 // This thread didn't already have the lock, so begin a database
526 // transaction now.
527 execSQL("BEGIN EXCLUSIVE;");
Fred Quintanac4516a72009-09-03 12:14:06 -0700528 mTransactionListener = transactionListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 mTransactionIsSuccessful = true;
530 mInnerTransactionIsSuccessful = false;
Fred Quintanac4516a72009-09-03 12:14:06 -0700531 if (transactionListener != null) {
532 try {
533 transactionListener.onBegin();
534 } catch (RuntimeException e) {
535 execSQL("ROLLBACK;");
536 throw e;
537 }
538 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 ok = true;
540 } finally {
541 if (!ok) {
542 // beginTransaction is called before the try block so we must release the lock in
543 // the case of failure.
544 unlockForced();
545 }
546 }
547 }
548
549 /**
550 * End a transaction. See beginTransaction for notes about how to use this and when transactions
551 * are committed and rolled back.
552 */
553 public void endTransaction() {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700554 if (!isOpen()) {
555 throw new IllegalStateException("database not open");
556 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800557 if (!mLock.isHeldByCurrentThread()) {
558 throw new IllegalStateException("no transaction pending");
559 }
560 try {
561 if (mInnerTransactionIsSuccessful) {
562 mInnerTransactionIsSuccessful = false;
563 } else {
564 mTransactionIsSuccessful = false;
565 }
566 if (mLock.getHoldCount() != 1) {
567 return;
568 }
Fred Quintanac4516a72009-09-03 12:14:06 -0700569 RuntimeException savedException = null;
570 if (mTransactionListener != null) {
571 try {
572 if (mTransactionIsSuccessful) {
573 mTransactionListener.onCommit();
574 } else {
575 mTransactionListener.onRollback();
576 }
577 } catch (RuntimeException e) {
578 savedException = e;
579 mTransactionIsSuccessful = false;
580 }
581 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800582 if (mTransactionIsSuccessful) {
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -0800583 execSQL(COMMIT_SQL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800584 } else {
585 try {
586 execSQL("ROLLBACK;");
Fred Quintanac4516a72009-09-03 12:14:06 -0700587 if (savedException != null) {
588 throw savedException;
589 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800590 } catch (SQLException e) {
591 if (Config.LOGD) {
592 Log.d(TAG, "exception during rollback, maybe the DB previously "
593 + "performed an auto-rollback");
594 }
595 }
596 }
597 } finally {
Fred Quintanac4516a72009-09-03 12:14:06 -0700598 mTransactionListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800599 unlockForced();
600 if (Config.LOGV) {
601 Log.v(TAG, "unlocked " + Thread.currentThread()
602 + ", holdCount is " + mLock.getHoldCount());
603 }
604 }
605 }
606
607 /**
608 * Marks the current transaction as successful. Do not do any more database work between
609 * calling this and calling endTransaction. Do as little non-database work as possible in that
610 * situation too. If any errors are encountered between this and endTransaction the transaction
611 * will still be committed.
612 *
613 * @throws IllegalStateException if the current thread is not in a transaction or the
614 * transaction is already marked as successful.
615 */
616 public void setTransactionSuccessful() {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700617 if (!isOpen()) {
618 throw new IllegalStateException("database not open");
619 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800620 if (!mLock.isHeldByCurrentThread()) {
621 throw new IllegalStateException("no transaction pending");
622 }
623 if (mInnerTransactionIsSuccessful) {
624 throw new IllegalStateException(
625 "setTransactionSuccessful may only be called once per call to beginTransaction");
626 }
627 mInnerTransactionIsSuccessful = true;
628 }
629
630 /**
631 * return true if there is a transaction pending
632 */
633 public boolean inTransaction() {
634 return mLock.getHoldCount() > 0;
635 }
636
637 /**
638 * Checks if the database lock is held by this thread.
639 *
640 * @return true, if this thread is holding the database lock.
641 */
642 public boolean isDbLockedByCurrentThread() {
643 return mLock.isHeldByCurrentThread();
644 }
645
646 /**
647 * Checks if the database is locked by another thread. This is
648 * just an estimate, since this status can change at any time,
649 * including after the call is made but before the result has
650 * been acted upon.
651 *
652 * @return true, if the database is locked by another thread
653 */
654 public boolean isDbLockedByOtherThreads() {
655 return !mLock.isHeldByCurrentThread() && mLock.isLocked();
656 }
657
658 /**
659 * Temporarily end the transaction to let other threads run. The transaction is assumed to be
660 * successful so far. Do not call setTransactionSuccessful before calling this. When this
661 * returns a new transaction will have been created but not marked as successful.
662 * @return true if the transaction was yielded
663 * @deprecated if the db is locked more than once (becuase of nested transactions) then the lock
664 * will not be yielded. Use yieldIfContendedSafely instead.
665 */
Dianne Hackborn4a51c202009-08-21 15:14:02 -0700666 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800667 public boolean yieldIfContended() {
Fred Quintana5c7aede2009-08-27 21:41:27 -0700668 return yieldIfContendedHelper(false /* do not check yielding */,
669 -1 /* sleepAfterYieldDelay */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800670 }
671
672 /**
673 * Temporarily end the transaction to let other threads run. The transaction is assumed to be
674 * successful so far. Do not call setTransactionSuccessful before calling this. When this
675 * returns a new transaction will have been created but not marked as successful. This assumes
676 * that there are no nested transactions (beginTransaction has only been called once) and will
Fred Quintana5c7aede2009-08-27 21:41:27 -0700677 * throw an exception if that is not the case.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800678 * @return true if the transaction was yielded
679 */
680 public boolean yieldIfContendedSafely() {
Fred Quintana5c7aede2009-08-27 21:41:27 -0700681 return yieldIfContendedHelper(true /* check yielding */, -1 /* sleepAfterYieldDelay*/);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800682 }
683
Fred Quintana5c7aede2009-08-27 21:41:27 -0700684 /**
685 * Temporarily end the transaction to let other threads run. The transaction is assumed to be
686 * successful so far. Do not call setTransactionSuccessful before calling this. When this
687 * returns a new transaction will have been created but not marked as successful. This assumes
688 * that there are no nested transactions (beginTransaction has only been called once) and will
689 * throw an exception if that is not the case.
690 * @param sleepAfterYieldDelay if > 0, sleep this long before starting a new transaction if
691 * the lock was actually yielded. This will allow other background threads to make some
692 * more progress than they would if we started the transaction immediately.
693 * @return true if the transaction was yielded
694 */
695 public boolean yieldIfContendedSafely(long sleepAfterYieldDelay) {
696 return yieldIfContendedHelper(true /* check yielding */, sleepAfterYieldDelay);
697 }
698
699 private boolean yieldIfContendedHelper(boolean checkFullyYielded, long sleepAfterYieldDelay) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800700 if (mLock.getQueueLength() == 0) {
701 // Reset the lock acquire time since we know that the thread was willing to yield
702 // the lock at this time.
703 mLockAcquiredWallTime = SystemClock.elapsedRealtime();
704 mLockAcquiredThreadTime = Debug.threadCpuTimeNanos();
705 return false;
706 }
707 setTransactionSuccessful();
Fred Quintanac4516a72009-09-03 12:14:06 -0700708 SQLiteTransactionListener transactionListener = mTransactionListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800709 endTransaction();
710 if (checkFullyYielded) {
711 if (this.isDbLockedByCurrentThread()) {
712 throw new IllegalStateException(
713 "Db locked more than once. yielfIfContended cannot yield");
714 }
715 }
Fred Quintana5c7aede2009-08-27 21:41:27 -0700716 if (sleepAfterYieldDelay > 0) {
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700717 // Sleep for up to sleepAfterYieldDelay milliseconds, waking up periodically to
718 // check if anyone is using the database. If the database is not contended,
719 // retake the lock and return.
720 long remainingDelay = sleepAfterYieldDelay;
721 while (remainingDelay > 0) {
722 try {
723 Thread.sleep(remainingDelay < SLEEP_AFTER_YIELD_QUANTUM ?
724 remainingDelay : SLEEP_AFTER_YIELD_QUANTUM);
725 } catch (InterruptedException e) {
726 Thread.interrupted();
727 }
728 remainingDelay -= SLEEP_AFTER_YIELD_QUANTUM;
729 if (mLock.getQueueLength() == 0) {
730 break;
731 }
Fred Quintana5c7aede2009-08-27 21:41:27 -0700732 }
733 }
Fred Quintanac4516a72009-09-03 12:14:06 -0700734 beginTransactionWithListener(transactionListener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800735 return true;
736 }
737
738 /** Maps table names to info about what to which _sync_time column to set
739 * to NULL on an update. This is used to support syncing. */
740 private final Map<String, SyncUpdateInfo> mSyncUpdateInfo =
741 new HashMap<String, SyncUpdateInfo>();
742
743 public Map<String, String> getSyncedTables() {
744 synchronized(mSyncUpdateInfo) {
745 HashMap<String, String> tables = new HashMap<String, String>();
746 for (String table : mSyncUpdateInfo.keySet()) {
747 SyncUpdateInfo info = mSyncUpdateInfo.get(table);
748 if (info.deletedTable != null) {
749 tables.put(table, info.deletedTable);
750 }
751 }
752 return tables;
753 }
754 }
755
756 /**
757 * Internal class used to keep track what needs to be marked as changed
758 * when an update occurs. This is used for syncing, so the sync engine
759 * knows what data has been updated locally.
760 */
761 static private class SyncUpdateInfo {
762 /**
763 * Creates the SyncUpdateInfo class.
764 *
765 * @param masterTable The table to set _sync_time to NULL in
766 * @param deletedTable The deleted table that corresponds to the
767 * master table
768 * @param foreignKey The key that refers to the primary key in table
769 */
770 SyncUpdateInfo(String masterTable, String deletedTable,
771 String foreignKey) {
772 this.masterTable = masterTable;
773 this.deletedTable = deletedTable;
774 this.foreignKey = foreignKey;
775 }
776
777 /** The table containing the _sync_time column */
778 String masterTable;
779
780 /** The deleted table that corresponds to the master table */
781 String deletedTable;
782
783 /** The key in the local table the row in table. It may be _id, if table
784 * is the local table. */
785 String foreignKey;
786 }
787
788 /**
789 * Used to allow returning sub-classes of {@link Cursor} when calling query.
790 */
791 public interface CursorFactory {
792 /**
793 * See
794 * {@link SQLiteCursor#SQLiteCursor(SQLiteDatabase, SQLiteCursorDriver,
795 * String, SQLiteQuery)}.
796 */
797 public Cursor newCursor(SQLiteDatabase db,
798 SQLiteCursorDriver masterQuery, String editTable,
799 SQLiteQuery query);
800 }
801
802 /**
803 * Open the database according to the flags {@link #OPEN_READWRITE}
804 * {@link #OPEN_READONLY} {@link #CREATE_IF_NECESSARY} and/or {@link #NO_LOCALIZED_COLLATORS}.
805 *
806 * <p>Sets the locale of the database to the the system's current locale.
807 * Call {@link #setLocale} if you would like something else.</p>
808 *
809 * @param path to database file to open and/or create
810 * @param factory an optional factory class that is called to instantiate a
811 * cursor when query is called, or null for default
812 * @param flags to control database access mode
813 * @return the newly opened database
814 * @throws SQLiteException if the database cannot be opened
815 */
816 public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags) {
Vasu Noric3849202010-03-09 10:47:25 -0800817 SQLiteDatabase sqliteDatabase = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800818 try {
819 // Open the database.
Vasu Noric3849202010-03-09 10:47:25 -0800820 sqliteDatabase = new SQLiteDatabase(path, factory, flags);
Vasu Nori3ef94e22010-02-05 14:49:04 -0800821 if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
822 sqliteDatabase.enableSqlTracing(path);
823 }
824 if (SQLiteDebug.DEBUG_SQL_TIME) {
825 sqliteDatabase.enableSqlProfiling(path);
826 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800827 } catch (SQLiteDatabaseCorruptException e) {
828 // Try to recover from this, if we can.
829 // TODO: should we do this for other open failures?
830 Log.e(TAG, "Deleting and re-creating corrupt database " + path, e);
Jeff Hamilton082c2af2009-09-29 11:49:51 -0700831 EventLog.writeEvent(EVENT_DB_CORRUPT, path);
Vasu Norif3cf8a42010-03-23 11:41:44 -0700832 if (!path.equalsIgnoreCase(":memory")) {
833 // delete is only for non-memory database files
834 new File(path).delete();
835 }
Vasu Noric3849202010-03-09 10:47:25 -0800836 sqliteDatabase = new SQLiteDatabase(path, factory, flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800837 }
Vasu Noric3849202010-03-09 10:47:25 -0800838 ActiveDatabases.getInstance().mActiveDatabases.add(
839 new WeakReference<SQLiteDatabase>(sqliteDatabase));
840 return sqliteDatabase;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800841 }
842
843 /**
844 * Equivalent to openDatabase(file.getPath(), factory, CREATE_IF_NECESSARY).
845 */
846 public static SQLiteDatabase openOrCreateDatabase(File file, CursorFactory factory) {
847 return openOrCreateDatabase(file.getPath(), factory);
848 }
849
850 /**
851 * Equivalent to openDatabase(path, factory, CREATE_IF_NECESSARY).
852 */
853 public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory) {
854 return openDatabase(path, factory, CREATE_IF_NECESSARY);
855 }
856
857 /**
858 * Create a memory backed SQLite database. Its contents will be destroyed
859 * when the database is closed.
860 *
861 * <p>Sets the locale of the database to the the system's current locale.
862 * Call {@link #setLocale} if you would like something else.</p>
863 *
864 * @param factory an optional factory class that is called to instantiate a
865 * cursor when query is called
866 * @return a SQLiteDatabase object, or null if the database can't be created
867 */
868 public static SQLiteDatabase create(CursorFactory factory) {
869 // This is a magic string with special meaning for SQLite.
870 return openDatabase(":memory:", factory, CREATE_IF_NECESSARY);
871 }
872
873 /**
874 * Close the database.
875 */
876 public void close() {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700877 if (!isOpen()) {
878 return; // already closed
879 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800880 lock();
881 try {
882 closeClosable();
Vasu Norif6373e92010-03-16 10:21:00 -0700883 // close this database instance - regardless of its reference count value
884 onAllReferencesReleased();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800885 } finally {
886 unlock();
887 }
888 }
889
890 private void closeClosable() {
Vasu Norie495d1f2010-01-06 16:34:19 -0800891 /* deallocate all compiled sql statement objects from mCompiledQueries cache.
892 * this should be done before de-referencing all {@link SQLiteClosable} objects
893 * from this database object because calling
894 * {@link SQLiteClosable#onAllReferencesReleasedFromContainer()} could cause the database
895 * to be closed. sqlite doesn't let a database close if there are
896 * any unfinalized statements - such as the compiled-sql objects in mCompiledQueries.
897 */
898 deallocCachedSqlStatements();
899
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800900 Iterator<Map.Entry<SQLiteClosable, Object>> iter = mPrograms.entrySet().iterator();
901 while (iter.hasNext()) {
902 Map.Entry<SQLiteClosable, Object> entry = iter.next();
903 SQLiteClosable program = entry.getKey();
904 if (program != null) {
905 program.onAllReferencesReleasedFromContainer();
906 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700907 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800908 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700909
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800910 /**
911 * Native call to close the database.
912 */
913 private native void dbclose();
914
915 /**
916 * Gets the database version.
917 *
918 * @return the database version
919 */
920 public int getVersion() {
Vasu Noric8e1f232010-04-13 15:05:09 -0700921 SQLiteStatement prog = null;
922 lock();
Vasu Norif3cf8a42010-03-23 11:41:44 -0700923 if (!isOpen()) {
924 throw new IllegalStateException("database not open");
925 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800926 try {
927 prog = new SQLiteStatement(this, "PRAGMA user_version;");
928 long version = prog.simpleQueryForLong();
929 return (int) version;
930 } finally {
931 if (prog != null) prog.close();
932 unlock();
933 }
934 }
935
936 /**
937 * Sets the database version.
938 *
939 * @param version the new database version
940 */
941 public void setVersion(int version) {
942 execSQL("PRAGMA user_version = " + version);
943 }
944
945 /**
946 * Returns the maximum size the database may grow to.
947 *
948 * @return the new maximum database size
949 */
950 public long getMaximumSize() {
Vasu Noric8e1f232010-04-13 15:05:09 -0700951 SQLiteStatement prog = null;
952 lock();
Vasu Norif3cf8a42010-03-23 11:41:44 -0700953 if (!isOpen()) {
954 throw new IllegalStateException("database not open");
955 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800956 try {
957 prog = new SQLiteStatement(this,
958 "PRAGMA max_page_count;");
959 long pageCount = prog.simpleQueryForLong();
960 return pageCount * getPageSize();
961 } finally {
962 if (prog != null) prog.close();
963 unlock();
964 }
965 }
966
967 /**
968 * Sets the maximum size the database will grow to. The maximum size cannot
969 * be set below the current size.
970 *
971 * @param numBytes the maximum database size, in bytes
972 * @return the new maximum database size
973 */
974 public long setMaximumSize(long numBytes) {
Vasu Noric8e1f232010-04-13 15:05:09 -0700975 SQLiteStatement prog = null;
976 lock();
Vasu Norif3cf8a42010-03-23 11:41:44 -0700977 if (!isOpen()) {
978 throw new IllegalStateException("database not open");
979 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800980 try {
981 long pageSize = getPageSize();
982 long numPages = numBytes / pageSize;
983 // If numBytes isn't a multiple of pageSize, bump up a page
984 if ((numBytes % pageSize) != 0) {
985 numPages++;
986 }
987 prog = new SQLiteStatement(this,
988 "PRAGMA max_page_count = " + numPages);
989 long newPageCount = prog.simpleQueryForLong();
990 return newPageCount * pageSize;
991 } finally {
992 if (prog != null) prog.close();
993 unlock();
994 }
995 }
996
997 /**
998 * Returns the current database page size, in bytes.
999 *
1000 * @return the database page size, in bytes
1001 */
1002 public long getPageSize() {
Vasu Noric8e1f232010-04-13 15:05:09 -07001003 SQLiteStatement prog = null;
1004 lock();
Vasu Norif3cf8a42010-03-23 11:41:44 -07001005 if (!isOpen()) {
1006 throw new IllegalStateException("database not open");
1007 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001008 try {
1009 prog = new SQLiteStatement(this,
1010 "PRAGMA page_size;");
1011 long size = prog.simpleQueryForLong();
1012 return size;
1013 } finally {
1014 if (prog != null) prog.close();
1015 unlock();
1016 }
1017 }
1018
1019 /**
1020 * Sets the database page size. The page size must be a power of two. This
1021 * method does not work if any data has been written to the database file,
1022 * and must be called right after the database has been created.
1023 *
1024 * @param numBytes the database page size, in bytes
1025 */
1026 public void setPageSize(long numBytes) {
1027 execSQL("PRAGMA page_size = " + numBytes);
1028 }
1029
1030 /**
1031 * Mark this table as syncable. When an update occurs in this table the
1032 * _sync_dirty field will be set to ensure proper syncing operation.
1033 *
1034 * @param table the table to mark as syncable
1035 * @param deletedTable The deleted table that corresponds to the
1036 * syncable table
1037 */
1038 public void markTableSyncable(String table, String deletedTable) {
1039 markTableSyncable(table, "_id", table, deletedTable);
1040 }
1041
1042 /**
1043 * Mark this table as syncable, with the _sync_dirty residing in another
1044 * table. When an update occurs in this table the _sync_dirty field of the
1045 * row in updateTable with the _id in foreignKey will be set to
1046 * ensure proper syncing operation.
1047 *
1048 * @param table an update on this table will trigger a sync time removal
1049 * @param foreignKey this is the column in table whose value is an _id in
1050 * updateTable
1051 * @param updateTable this is the table that will have its _sync_dirty
1052 */
1053 public void markTableSyncable(String table, String foreignKey,
1054 String updateTable) {
1055 markTableSyncable(table, foreignKey, updateTable, null);
1056 }
1057
1058 /**
1059 * Mark this table as syncable, with the _sync_dirty residing in another
1060 * table. When an update occurs in this table the _sync_dirty field of the
1061 * row in updateTable with the _id in foreignKey will be set to
1062 * ensure proper syncing operation.
1063 *
1064 * @param table an update on this table will trigger a sync time removal
1065 * @param foreignKey this is the column in table whose value is an _id in
1066 * updateTable
1067 * @param updateTable this is the table that will have its _sync_dirty
1068 * @param deletedTable The deleted table that corresponds to the
1069 * updateTable
1070 */
1071 private void markTableSyncable(String table, String foreignKey,
1072 String updateTable, String deletedTable) {
1073 lock();
1074 try {
1075 native_execSQL("SELECT _sync_dirty FROM " + updateTable
1076 + " LIMIT 0");
1077 native_execSQL("SELECT " + foreignKey + " FROM " + table
1078 + " LIMIT 0");
1079 } finally {
1080 unlock();
1081 }
1082
1083 SyncUpdateInfo info = new SyncUpdateInfo(updateTable, deletedTable,
1084 foreignKey);
1085 synchronized (mSyncUpdateInfo) {
1086 mSyncUpdateInfo.put(table, info);
1087 }
1088 }
1089
1090 /**
1091 * Call for each row that is updated in a cursor.
1092 *
1093 * @param table the table the row is in
1094 * @param rowId the row ID of the updated row
1095 */
1096 /* package */ void rowUpdated(String table, long rowId) {
1097 SyncUpdateInfo info;
1098 synchronized (mSyncUpdateInfo) {
1099 info = mSyncUpdateInfo.get(table);
1100 }
1101 if (info != null) {
1102 execSQL("UPDATE " + info.masterTable
1103 + " SET _sync_dirty=1 WHERE _id=(SELECT " + info.foreignKey
1104 + " FROM " + table + " WHERE _id=" + rowId + ")");
1105 }
1106 }
1107
1108 /**
1109 * Finds the name of the first table, which is editable.
1110 *
1111 * @param tables a list of tables
1112 * @return the first table listed
1113 */
1114 public static String findEditTable(String tables) {
1115 if (!TextUtils.isEmpty(tables)) {
1116 // find the first word terminated by either a space or a comma
1117 int spacepos = tables.indexOf(' ');
1118 int commapos = tables.indexOf(',');
1119
1120 if (spacepos > 0 && (spacepos < commapos || commapos < 0)) {
1121 return tables.substring(0, spacepos);
1122 } else if (commapos > 0 && (commapos < spacepos || spacepos < 0) ) {
1123 return tables.substring(0, commapos);
1124 }
1125 return tables;
1126 } else {
1127 throw new IllegalStateException("Invalid tables");
1128 }
1129 }
1130
1131 /**
1132 * Compiles an SQL statement into a reusable pre-compiled statement object.
1133 * The parameters are identical to {@link #execSQL(String)}. You may put ?s in the
1134 * statement and fill in those values with {@link SQLiteProgram#bindString}
1135 * and {@link SQLiteProgram#bindLong} each time you want to run the
1136 * statement. Statements may not return result sets larger than 1x1.
1137 *
1138 * @param sql The raw SQL statement, may contain ? for unknown values to be
1139 * bound later.
Jeff Hamiltonf3ca9a52010-05-12 15:04:33 -07001140 * @return A pre-compiled {@link SQLiteStatement} object. Note that
1141 * {@link SQLiteStatement}s are not synchronized, see the documentation for more details.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001142 */
1143 public SQLiteStatement compileStatement(String sql) throws SQLException {
Vasu Noric8e1f232010-04-13 15:05:09 -07001144 lock();
Vasu Norif3cf8a42010-03-23 11:41:44 -07001145 if (!isOpen()) {
1146 throw new IllegalStateException("database not open");
1147 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001148 try {
1149 return new SQLiteStatement(this, sql);
1150 } finally {
1151 unlock();
1152 }
1153 }
1154
1155 /**
1156 * Query the given URL, returning a {@link Cursor} over the result set.
1157 *
1158 * @param distinct true if you want each row to be unique, false otherwise.
1159 * @param table The table name to compile the query against.
1160 * @param columns A list of which columns to return. Passing null will
1161 * return all columns, which is discouraged to prevent reading
1162 * data from storage that isn't going to be used.
1163 * @param selection A filter declaring which rows to return, formatted as an
1164 * SQL WHERE clause (excluding the WHERE itself). Passing null
1165 * will return all rows for the given table.
1166 * @param selectionArgs You may include ?s in selection, which will be
1167 * replaced by the values from selectionArgs, in order that they
1168 * appear in the selection. The values will be bound as Strings.
1169 * @param groupBy A filter declaring how to group rows, formatted as an SQL
1170 * GROUP BY clause (excluding the GROUP BY itself). Passing null
1171 * will cause the rows to not be grouped.
1172 * @param having A filter declare which row groups to include in the cursor,
1173 * if row grouping is being used, formatted as an SQL HAVING
1174 * clause (excluding the HAVING itself). Passing null will cause
1175 * all row groups to be included, and is required when row
1176 * grouping is not being used.
1177 * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
1178 * (excluding the ORDER BY itself). Passing null will use the
1179 * default sort order, which may be unordered.
1180 * @param limit Limits the number of rows returned by the query,
1181 * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
Jeff Hamiltonf3ca9a52010-05-12 15:04:33 -07001182 * @return A {@link Cursor} object, which is positioned before the first entry. Note that
1183 * {@link Cursor}s are not synchronized, see the documentation for more details.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001184 * @see Cursor
1185 */
1186 public Cursor query(boolean distinct, String table, String[] columns,
1187 String selection, String[] selectionArgs, String groupBy,
1188 String having, String orderBy, String limit) {
1189 return queryWithFactory(null, distinct, table, columns, selection, selectionArgs,
1190 groupBy, having, orderBy, limit);
1191 }
1192
1193 /**
1194 * Query the given URL, returning a {@link Cursor} over the result set.
1195 *
1196 * @param cursorFactory the cursor factory to use, or null for the default factory
1197 * @param distinct true if you want each row to be unique, false otherwise.
1198 * @param table The table name to compile the query against.
1199 * @param columns A list of which columns to return. Passing null will
1200 * return all columns, which is discouraged to prevent reading
1201 * data from storage that isn't going to be used.
1202 * @param selection A filter declaring which rows to return, formatted as an
1203 * SQL WHERE clause (excluding the WHERE itself). Passing null
1204 * will return all rows for the given table.
1205 * @param selectionArgs You may include ?s in selection, which will be
1206 * replaced by the values from selectionArgs, in order that they
1207 * appear in the selection. The values will be bound as Strings.
1208 * @param groupBy A filter declaring how to group rows, formatted as an SQL
1209 * GROUP BY clause (excluding the GROUP BY itself). Passing null
1210 * will cause the rows to not be grouped.
1211 * @param having A filter declare which row groups to include in the cursor,
1212 * if row grouping is being used, formatted as an SQL HAVING
1213 * clause (excluding the HAVING itself). Passing null will cause
1214 * all row groups to be included, and is required when row
1215 * grouping is not being used.
1216 * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
1217 * (excluding the ORDER BY itself). Passing null will use the
1218 * default sort order, which may be unordered.
1219 * @param limit Limits the number of rows returned by the query,
1220 * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
Jeff Hamiltonf3ca9a52010-05-12 15:04:33 -07001221 * @return A {@link Cursor} object, which is positioned before the first entry. Note that
1222 * {@link Cursor}s are not synchronized, see the documentation for more details.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001223 * @see Cursor
1224 */
1225 public Cursor queryWithFactory(CursorFactory cursorFactory,
1226 boolean distinct, String table, String[] columns,
1227 String selection, String[] selectionArgs, String groupBy,
1228 String having, String orderBy, String limit) {
Vasu Norif3cf8a42010-03-23 11:41:44 -07001229 if (!isOpen()) {
1230 throw new IllegalStateException("database not open");
1231 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001232 String sql = SQLiteQueryBuilder.buildQueryString(
1233 distinct, table, columns, selection, groupBy, having, orderBy, limit);
1234
1235 return rawQueryWithFactory(
1236 cursorFactory, sql, selectionArgs, findEditTable(table));
1237 }
1238
1239 /**
1240 * Query the given table, returning a {@link Cursor} over the result set.
1241 *
1242 * @param table The table name to compile the query against.
1243 * @param columns A list of which columns to return. Passing null will
1244 * return all columns, which is discouraged to prevent reading
1245 * data from storage that isn't going to be used.
1246 * @param selection A filter declaring which rows to return, formatted as an
1247 * SQL WHERE clause (excluding the WHERE itself). Passing null
1248 * will return all rows for the given table.
1249 * @param selectionArgs You may include ?s in selection, which will be
1250 * replaced by the values from selectionArgs, in order that they
1251 * appear in the selection. The values will be bound as Strings.
1252 * @param groupBy A filter declaring how to group rows, formatted as an SQL
1253 * GROUP BY clause (excluding the GROUP BY itself). Passing null
1254 * will cause the rows to not be grouped.
1255 * @param having A filter declare which row groups to include in the cursor,
1256 * if row grouping is being used, formatted as an SQL HAVING
1257 * clause (excluding the HAVING itself). Passing null will cause
1258 * all row groups to be included, and is required when row
1259 * grouping is not being used.
1260 * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
1261 * (excluding the ORDER BY itself). Passing null will use the
1262 * default sort order, which may be unordered.
Jeff Hamiltonf3ca9a52010-05-12 15:04:33 -07001263 * @return A {@link Cursor} object, which is positioned before the first entry. Note that
1264 * {@link Cursor}s are not synchronized, see the documentation for more details.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001265 * @see Cursor
1266 */
1267 public Cursor query(String table, String[] columns, String selection,
1268 String[] selectionArgs, String groupBy, String having,
1269 String orderBy) {
1270
1271 return query(false, table, columns, selection, selectionArgs, groupBy,
1272 having, orderBy, null /* limit */);
1273 }
1274
1275 /**
1276 * Query the given table, returning a {@link Cursor} over the result set.
1277 *
1278 * @param table The table name to compile the query against.
1279 * @param columns A list of which columns to return. Passing null will
1280 * return all columns, which is discouraged to prevent reading
1281 * data from storage that isn't going to be used.
1282 * @param selection A filter declaring which rows to return, formatted as an
1283 * SQL WHERE clause (excluding the WHERE itself). Passing null
1284 * will return all rows for the given table.
1285 * @param selectionArgs You may include ?s in selection, which will be
1286 * replaced by the values from selectionArgs, in order that they
1287 * appear in the selection. The values will be bound as Strings.
1288 * @param groupBy A filter declaring how to group rows, formatted as an SQL
1289 * GROUP BY clause (excluding the GROUP BY itself). Passing null
1290 * will cause the rows to not be grouped.
1291 * @param having A filter declare which row groups to include in the cursor,
1292 * if row grouping is being used, formatted as an SQL HAVING
1293 * clause (excluding the HAVING itself). Passing null will cause
1294 * all row groups to be included, and is required when row
1295 * grouping is not being used.
1296 * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
1297 * (excluding the ORDER BY itself). Passing null will use the
1298 * default sort order, which may be unordered.
1299 * @param limit Limits the number of rows returned by the query,
1300 * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
Jeff Hamiltonf3ca9a52010-05-12 15:04:33 -07001301 * @return A {@link Cursor} object, which is positioned before the first entry. Note that
1302 * {@link Cursor}s are not synchronized, see the documentation for more details.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001303 * @see Cursor
1304 */
1305 public Cursor query(String table, String[] columns, String selection,
1306 String[] selectionArgs, String groupBy, String having,
1307 String orderBy, String limit) {
1308
1309 return query(false, table, columns, selection, selectionArgs, groupBy,
1310 having, orderBy, limit);
1311 }
1312
1313 /**
1314 * Runs the provided SQL and returns a {@link Cursor} over the result set.
1315 *
1316 * @param sql the SQL query. The SQL string must not be ; terminated
1317 * @param selectionArgs You may include ?s in where clause in the query,
1318 * which will be replaced by the values from selectionArgs. The
1319 * values will be bound as Strings.
Jeff Hamiltonf3ca9a52010-05-12 15:04:33 -07001320 * @return A {@link Cursor} object, which is positioned before the first entry. Note that
1321 * {@link Cursor}s are not synchronized, see the documentation for more details.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001322 */
1323 public Cursor rawQuery(String sql, String[] selectionArgs) {
1324 return rawQueryWithFactory(null, sql, selectionArgs, null);
1325 }
1326
1327 /**
1328 * Runs the provided SQL and returns a cursor over the result set.
1329 *
1330 * @param cursorFactory the cursor factory to use, or null for the default factory
1331 * @param sql the SQL query. The SQL string must not be ; terminated
1332 * @param selectionArgs You may include ?s in where clause in the query,
1333 * which will be replaced by the values from selectionArgs. The
1334 * values will be bound as Strings.
1335 * @param editTable the name of the first table, which is editable
Jeff Hamiltonf3ca9a52010-05-12 15:04:33 -07001336 * @return A {@link Cursor} object, which is positioned before the first entry. Note that
1337 * {@link Cursor}s are not synchronized, see the documentation for more details.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001338 */
1339 public Cursor rawQueryWithFactory(
1340 CursorFactory cursorFactory, String sql, String[] selectionArgs,
1341 String editTable) {
Vasu Norif3cf8a42010-03-23 11:41:44 -07001342 if (!isOpen()) {
1343 throw new IllegalStateException("database not open");
1344 }
Brad Fitzpatrickcfda9f32010-06-03 12:52:54 -07001345 BlockGuard.getThreadPolicy().onReadFromDisk();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001346 long timeStart = 0;
1347
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001348 if (Config.LOGV || mSlowQueryThreshold != -1) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001349 timeStart = System.currentTimeMillis();
1350 }
1351
1352 SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable);
1353
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001354 Cursor cursor = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001355 try {
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001356 cursor = driver.query(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001357 cursorFactory != null ? cursorFactory : mFactory,
1358 selectionArgs);
1359 } finally {
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001360 if (Config.LOGV || mSlowQueryThreshold != -1) {
1361
Vasu Nori020e5342010-04-28 14:22:38 -07001362 // Force query execution
1363 int count = -1;
1364 if (cursor != null) {
1365 count = cursor.getCount();
1366 }
1367
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001368 long duration = System.currentTimeMillis() - timeStart;
1369
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001370 if (Config.LOGV || duration >= mSlowQueryThreshold) {
1371 Log.v(SQLiteCursor.TAG,
1372 "query (" + duration + " ms): " + driver.toString() + ", args are "
1373 + (selectionArgs != null
1374 ? TextUtils.join(",", selectionArgs)
Vasu Nori020e5342010-04-28 14:22:38 -07001375 : "<null>") + ", count is " + count);
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001376 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001377 }
1378 }
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001379 return cursor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001380 }
1381
1382 /**
1383 * Runs the provided SQL and returns a cursor over the result set.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001384 * The cursor will read an initial set of rows and the return to the caller.
1385 * It will continue to read in batches and send data changed notifications
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001386 * when the later batches are ready.
1387 * @param sql the SQL query. The SQL string must not be ; terminated
1388 * @param selectionArgs You may include ?s in where clause in the query,
1389 * which will be replaced by the values from selectionArgs. The
1390 * values will be bound as Strings.
1391 * @param initialRead set the initial count of items to read from the cursor
1392 * @param maxRead set the count of items to read on each iteration after the first
Jeff Hamiltonf3ca9a52010-05-12 15:04:33 -07001393 * @return A {@link Cursor} object, which is positioned before the first entry. Note that
1394 * {@link Cursor}s are not synchronized, see the documentation for more details.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001395 *
Andy Stadlerf8a7cea2009-04-10 16:24:47 -07001396 * This work is incomplete and not fully tested or reviewed, so currently
1397 * hidden.
1398 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001399 */
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001400 public Cursor rawQuery(String sql, String[] selectionArgs,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001401 int initialRead, int maxRead) {
1402 SQLiteCursor c = (SQLiteCursor)rawQueryWithFactory(
1403 null, sql, selectionArgs, null);
1404 c.setLoadStyle(initialRead, maxRead);
1405 return c;
1406 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001407
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001408 /**
1409 * Convenience method for inserting a row into the database.
1410 *
1411 * @param table the table to insert the row into
Brad Fitzpatrick69ea4e12011-01-05 11:13:40 -08001412 * @param nullColumnHack optional; may be <code>null</code>.
1413 * SQL doesn't allow inserting a completely empty row without
1414 * naming at least one column name. If your provided <code>values</code> is
1415 * empty, no column names are known and an empty row can't be inserted.
1416 * If not set to null, the <code>nullColumnHack</code> parameter
1417 * provides the name of nullable column name to explicitly insert a NULL into
1418 * in the case where your <code>values</code> is empty.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001419 * @param values this map contains the initial column values for the
1420 * row. The keys should be the column names and the values the
1421 * column values
1422 * @return the row ID of the newly inserted row, or -1 if an error occurred
1423 */
1424 public long insert(String table, String nullColumnHack, ContentValues values) {
1425 try {
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001426 return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001427 } catch (SQLException e) {
1428 Log.e(TAG, "Error inserting " + values, e);
1429 return -1;
1430 }
1431 }
1432
1433 /**
1434 * Convenience method for inserting a row into the database.
1435 *
1436 * @param table the table to insert the row into
Brad Fitzpatrick69ea4e12011-01-05 11:13:40 -08001437 * @param nullColumnHack optional; may be <code>null</code>.
1438 * SQL doesn't allow inserting a completely empty row without
1439 * naming at least one column name. If your provided <code>values</code> is
1440 * empty, no column names are known and an empty row can't be inserted.
1441 * If not set to null, the <code>nullColumnHack</code> parameter
1442 * provides the name of nullable column name to explicitly insert a NULL into
1443 * in the case where your <code>values</code> is empty.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001444 * @param values this map contains the initial column values for the
1445 * row. The keys should be the column names and the values the
1446 * column values
1447 * @throws SQLException
1448 * @return the row ID of the newly inserted row, or -1 if an error occurred
1449 */
1450 public long insertOrThrow(String table, String nullColumnHack, ContentValues values)
1451 throws SQLException {
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001452 return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001453 }
1454
1455 /**
1456 * Convenience method for replacing a row in the database.
1457 *
1458 * @param table the table in which to replace the row
Brad Fitzpatrick69ea4e12011-01-05 11:13:40 -08001459 * @param nullColumnHack optional; may be <code>null</code>.
1460 * SQL doesn't allow inserting a completely empty row without
1461 * naming at least one column name. If your provided <code>initialValues</code> is
1462 * empty, no column names are known and an empty row can't be inserted.
1463 * If not set to null, the <code>nullColumnHack</code> parameter
1464 * provides the name of nullable column name to explicitly insert a NULL into
1465 * in the case where your <code>initialValues</code> is empty.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001466 * @param initialValues this map contains the initial column values for
Brad Fitzpatrick69ea4e12011-01-05 11:13:40 -08001467 * the row.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001468 * @return the row ID of the newly inserted row, or -1 if an error occurred
1469 */
1470 public long replace(String table, String nullColumnHack, ContentValues initialValues) {
1471 try {
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001472 return insertWithOnConflict(table, nullColumnHack, initialValues,
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001473 CONFLICT_REPLACE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001474 } catch (SQLException e) {
1475 Log.e(TAG, "Error inserting " + initialValues, e);
1476 return -1;
1477 }
1478 }
1479
1480 /**
1481 * Convenience method for replacing a row in the database.
1482 *
1483 * @param table the table in which to replace the row
Brad Fitzpatrick69ea4e12011-01-05 11:13:40 -08001484 * @param nullColumnHack optional; may be <code>null</code>.
1485 * SQL doesn't allow inserting a completely empty row without
1486 * naming at least one column name. If your provided <code>initialValues</code> is
1487 * empty, no column names are known and an empty row can't be inserted.
1488 * If not set to null, the <code>nullColumnHack</code> parameter
1489 * provides the name of nullable column name to explicitly insert a NULL into
1490 * in the case where your <code>initialValues</code> is empty.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001491 * @param initialValues this map contains the initial column values for
1492 * the row. The key
1493 * @throws SQLException
1494 * @return the row ID of the newly inserted row, or -1 if an error occurred
1495 */
1496 public long replaceOrThrow(String table, String nullColumnHack,
1497 ContentValues initialValues) throws SQLException {
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001498 return insertWithOnConflict(table, nullColumnHack, initialValues,
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001499 CONFLICT_REPLACE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001500 }
1501
1502 /**
1503 * General method for inserting a row into the database.
1504 *
1505 * @param table the table to insert the row into
Brad Fitzpatrick69ea4e12011-01-05 11:13:40 -08001506 * @param nullColumnHack optional; may be <code>null</code>.
1507 * SQL doesn't allow inserting a completely empty row without
1508 * naming at least one column name. If your provided <code>initialValues</code> is
1509 * empty, no column names are known and an empty row can't be inserted.
1510 * If not set to null, the <code>nullColumnHack</code> parameter
1511 * provides the name of nullable column name to explicitly insert a NULL into
1512 * in the case where your <code>initialValues</code> is empty.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001513 * @param initialValues this map contains the initial column values for the
1514 * row. The keys should be the column names and the values the
1515 * column values
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001516 * @param conflictAlgorithm for insert conflict resolver
Vasu Nori6eb7c452010-01-27 14:31:24 -08001517 * @return the row ID of the newly inserted row
1518 * OR the primary key of the existing row if the input param 'conflictAlgorithm' =
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001519 * {@link #CONFLICT_IGNORE}
Vasu Nori6eb7c452010-01-27 14:31:24 -08001520 * OR -1 if any error
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001521 */
1522 public long insertWithOnConflict(String table, String nullColumnHack,
Vasu Nori6eb7c452010-01-27 14:31:24 -08001523 ContentValues initialValues, int conflictAlgorithm) {
Brad Fitzpatrickcfda9f32010-06-03 12:52:54 -07001524 BlockGuard.getThreadPolicy().onWriteToDisk();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001525 if (!isOpen()) {
1526 throw new IllegalStateException("database not open");
1527 }
1528
1529 // Measurements show most sql lengths <= 152
1530 StringBuilder sql = new StringBuilder(152);
1531 sql.append("INSERT");
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001532 sql.append(CONFLICT_VALUES[conflictAlgorithm]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001533 sql.append(" INTO ");
1534 sql.append(table);
1535 // Measurements show most values lengths < 40
1536 StringBuilder values = new StringBuilder(40);
1537
1538 Set<Map.Entry<String, Object>> entrySet = null;
1539 if (initialValues != null && initialValues.size() > 0) {
1540 entrySet = initialValues.valueSet();
1541 Iterator<Map.Entry<String, Object>> entriesIter = entrySet.iterator();
1542 sql.append('(');
1543
1544 boolean needSeparator = false;
1545 while (entriesIter.hasNext()) {
1546 if (needSeparator) {
1547 sql.append(", ");
1548 values.append(", ");
1549 }
1550 needSeparator = true;
1551 Map.Entry<String, Object> entry = entriesIter.next();
1552 sql.append(entry.getKey());
1553 values.append('?');
1554 }
1555
1556 sql.append(')');
1557 } else {
1558 sql.append("(" + nullColumnHack + ") ");
1559 values.append("NULL");
1560 }
1561
1562 sql.append(" VALUES(");
1563 sql.append(values);
1564 sql.append(");");
1565
1566 lock();
1567 SQLiteStatement statement = null;
1568 try {
1569 statement = compileStatement(sql.toString());
1570
1571 // Bind the values
1572 if (entrySet != null) {
1573 int size = entrySet.size();
1574 Iterator<Map.Entry<String, Object>> entriesIter = entrySet.iterator();
1575 for (int i = 0; i < size; i++) {
1576 Map.Entry<String, Object> entry = entriesIter.next();
1577 DatabaseUtils.bindObjectToProgram(statement, i + 1, entry.getValue());
1578 }
1579 }
1580
1581 // Run the program and then cleanup
1582 statement.execute();
1583
1584 long insertedRowId = lastInsertRow();
1585 if (insertedRowId == -1) {
1586 Log.e(TAG, "Error inserting " + initialValues + " using " + sql);
1587 } else {
1588 if (Config.LOGD && Log.isLoggable(TAG, Log.VERBOSE)) {
1589 Log.v(TAG, "Inserting row " + insertedRowId + " from "
1590 + initialValues + " using " + sql);
1591 }
1592 }
1593 return insertedRowId;
1594 } catch (SQLiteDatabaseCorruptException e) {
1595 onCorruption();
1596 throw e;
1597 } finally {
1598 if (statement != null) {
1599 statement.close();
1600 }
1601 unlock();
1602 }
1603 }
1604
1605 /**
1606 * Convenience method for deleting rows in the database.
1607 *
1608 * @param table the table to delete from
1609 * @param whereClause the optional WHERE clause to apply when deleting.
1610 * Passing null will delete all rows.
1611 * @return the number of rows affected if a whereClause is passed in, 0
1612 * otherwise. To remove all rows and get a count pass "1" as the
1613 * whereClause.
1614 */
1615 public int delete(String table, String whereClause, String[] whereArgs) {
Brad Fitzpatrickcfda9f32010-06-03 12:52:54 -07001616 BlockGuard.getThreadPolicy().onWriteToDisk();
Vasu Noric8e1f232010-04-13 15:05:09 -07001617 lock();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001618 if (!isOpen()) {
1619 throw new IllegalStateException("database not open");
1620 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001621 SQLiteStatement statement = null;
1622 try {
1623 statement = compileStatement("DELETE FROM " + table
1624 + (!TextUtils.isEmpty(whereClause)
1625 ? " WHERE " + whereClause : ""));
1626 if (whereArgs != null) {
1627 int numArgs = whereArgs.length;
1628 for (int i = 0; i < numArgs; i++) {
1629 DatabaseUtils.bindObjectToProgram(statement, i + 1, whereArgs[i]);
1630 }
1631 }
1632 statement.execute();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001633 return lastChangeCount();
1634 } catch (SQLiteDatabaseCorruptException e) {
1635 onCorruption();
1636 throw e;
1637 } finally {
1638 if (statement != null) {
1639 statement.close();
1640 }
1641 unlock();
1642 }
1643 }
1644
1645 /**
1646 * Convenience method for updating rows in the database.
1647 *
1648 * @param table the table to update in
1649 * @param values a map from column names to new column values. null is a
1650 * valid value that will be translated to NULL.
1651 * @param whereClause the optional WHERE clause to apply when updating.
1652 * Passing null will update all rows.
1653 * @return the number of rows affected
1654 */
1655 public int update(String table, ContentValues values, String whereClause, String[] whereArgs) {
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001656 return updateWithOnConflict(table, values, whereClause, whereArgs, CONFLICT_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001657 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001658
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001659 /**
1660 * Convenience method for updating rows in the database.
1661 *
1662 * @param table the table to update in
1663 * @param values a map from column names to new column values. null is a
1664 * valid value that will be translated to NULL.
1665 * @param whereClause the optional WHERE clause to apply when updating.
1666 * Passing null will update all rows.
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001667 * @param conflictAlgorithm for update conflict resolver
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001668 * @return the number of rows affected
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001669 */
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001670 public int updateWithOnConflict(String table, ContentValues values,
Vasu Nori6eb7c452010-01-27 14:31:24 -08001671 String whereClause, String[] whereArgs, int conflictAlgorithm) {
Brad Fitzpatrickcfda9f32010-06-03 12:52:54 -07001672 BlockGuard.getThreadPolicy().onWriteToDisk();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001673 if (values == null || values.size() == 0) {
1674 throw new IllegalArgumentException("Empty values");
1675 }
1676
1677 StringBuilder sql = new StringBuilder(120);
1678 sql.append("UPDATE ");
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001679 sql.append(CONFLICT_VALUES[conflictAlgorithm]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001680 sql.append(table);
1681 sql.append(" SET ");
1682
1683 Set<Map.Entry<String, Object>> entrySet = values.valueSet();
1684 Iterator<Map.Entry<String, Object>> entriesIter = entrySet.iterator();
1685
1686 while (entriesIter.hasNext()) {
1687 Map.Entry<String, Object> entry = entriesIter.next();
1688 sql.append(entry.getKey());
1689 sql.append("=?");
1690 if (entriesIter.hasNext()) {
1691 sql.append(", ");
1692 }
1693 }
1694
1695 if (!TextUtils.isEmpty(whereClause)) {
1696 sql.append(" WHERE ");
1697 sql.append(whereClause);
1698 }
1699
1700 lock();
Vasu Noric8e1f232010-04-13 15:05:09 -07001701 if (!isOpen()) {
1702 throw new IllegalStateException("database not open");
1703 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001704 SQLiteStatement statement = null;
1705 try {
1706 statement = compileStatement(sql.toString());
1707
1708 // Bind the values
1709 int size = entrySet.size();
1710 entriesIter = entrySet.iterator();
1711 int bindArg = 1;
1712 for (int i = 0; i < size; i++) {
1713 Map.Entry<String, Object> entry = entriesIter.next();
1714 DatabaseUtils.bindObjectToProgram(statement, bindArg, entry.getValue());
1715 bindArg++;
1716 }
1717
1718 if (whereArgs != null) {
1719 size = whereArgs.length;
1720 for (int i = 0; i < size; i++) {
1721 statement.bindString(bindArg, whereArgs[i]);
1722 bindArg++;
1723 }
1724 }
1725
1726 // Run the program and then cleanup
1727 statement.execute();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001728 int numChangedRows = lastChangeCount();
1729 if (Config.LOGD && Log.isLoggable(TAG, Log.VERBOSE)) {
1730 Log.v(TAG, "Updated " + numChangedRows + " using " + values + " and " + sql);
1731 }
1732 return numChangedRows;
1733 } catch (SQLiteDatabaseCorruptException e) {
1734 onCorruption();
1735 throw e;
1736 } catch (SQLException e) {
1737 Log.e(TAG, "Error updating " + values + " using " + sql);
1738 throw e;
1739 } finally {
1740 if (statement != null) {
1741 statement.close();
1742 }
1743 unlock();
1744 }
1745 }
1746
1747 /**
1748 * Execute a single SQL statement that is not a query. For example, CREATE
Brad Fitzpatrick69ea4e12011-01-05 11:13:40 -08001749 * TABLE, DELETE, INSERT, etc. Multiple statements separated by semicolons are not
1750 * supported. Takes a write lock.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001751 *
Brad Fitzpatrick69ea4e12011-01-05 11:13:40 -08001752 * @throws SQLException if the SQL string is invalid
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001753 */
1754 public void execSQL(String sql) throws SQLException {
Brad Fitzpatrickcfda9f32010-06-03 12:52:54 -07001755 BlockGuard.getThreadPolicy().onWriteToDisk();
Vasu Noric8e1f232010-04-13 15:05:09 -07001756 long timeStart = SystemClock.uptimeMillis();
1757 lock();
Vasu Norif3cf8a42010-03-23 11:41:44 -07001758 if (!isOpen()) {
1759 throw new IllegalStateException("database not open");
1760 }
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07001761 logTimeStat(mLastSqlStatement, timeStart, GET_LOCK_LOG_PREFIX);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001762 try {
1763 native_execSQL(sql);
1764 } catch (SQLiteDatabaseCorruptException e) {
1765 onCorruption();
1766 throw e;
1767 } finally {
1768 unlock();
1769 }
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -08001770
1771 // Log commit statements along with the most recently executed
1772 // SQL statement for disambiguation. Note that instance
1773 // equality to COMMIT_SQL is safe here.
1774 if (sql == COMMIT_SQL) {
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07001775 logTimeStat(mLastSqlStatement, timeStart, COMMIT_SQL);
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -08001776 } else {
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07001777 logTimeStat(sql, timeStart, null);
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -08001778 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001779 }
1780
1781 /**
1782 * Execute a single SQL statement that is not a query. For example, CREATE
Brad Fitzpatrick69ea4e12011-01-05 11:13:40 -08001783 * TABLE, DELETE, INSERT, etc. Multiple statements separated by semicolons are not
1784 * supported. Takes a write lock.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001785 *
1786 * @param sql
1787 * @param bindArgs only byte[], String, Long and Double are supported in bindArgs.
Brad Fitzpatrick69ea4e12011-01-05 11:13:40 -08001788 * @throws SQLException if the SQL string is invalid
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001789 */
1790 public void execSQL(String sql, Object[] bindArgs) throws SQLException {
Brad Fitzpatrickcfda9f32010-06-03 12:52:54 -07001791 BlockGuard.getThreadPolicy().onWriteToDisk();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001792 if (bindArgs == null) {
1793 throw new IllegalArgumentException("Empty bindArgs");
1794 }
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -08001795 long timeStart = SystemClock.uptimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001796 lock();
Vasu Noric8e1f232010-04-13 15:05:09 -07001797 if (!isOpen()) {
1798 throw new IllegalStateException("database not open");
1799 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001800 SQLiteStatement statement = null;
1801 try {
1802 statement = compileStatement(sql);
1803 if (bindArgs != null) {
1804 int numArgs = bindArgs.length;
1805 for (int i = 0; i < numArgs; i++) {
1806 DatabaseUtils.bindObjectToProgram(statement, i + 1, bindArgs[i]);
1807 }
1808 }
1809 statement.execute();
1810 } catch (SQLiteDatabaseCorruptException e) {
1811 onCorruption();
1812 throw e;
1813 } finally {
1814 if (statement != null) {
1815 statement.close();
1816 }
1817 unlock();
1818 }
Dan Egnor12311952009-11-23 14:47:45 -08001819 logTimeStat(sql, timeStart);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001820 }
1821
1822 @Override
1823 protected void finalize() {
1824 if (isOpen()) {
Vasu Norid606b4b2010-02-24 12:54:20 -08001825 Log.e(TAG, "close() was never explicitly called on database '" +
1826 mPath + "' ", mStackTrace);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001827 closeClosable();
1828 onAllReferencesReleased();
1829 }
1830 }
1831
1832 /**
1833 * Private constructor. See {@link #create} and {@link #openDatabase}.
1834 *
1835 * @param path The full path to the database
1836 * @param factory The factory to use when creating cursors, may be NULL.
1837 * @param flags 0 or {@link #NO_LOCALIZED_COLLATORS}. If the database file already
1838 * exists, mFlags will be updated appropriately.
1839 */
1840 private SQLiteDatabase(String path, CursorFactory factory, int flags) {
1841 if (path == null) {
1842 throw new IllegalArgumentException("path should not be null");
1843 }
1844 mFlags = flags;
1845 mPath = path;
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001846 mSlowQueryThreshold = SystemProperties.getInt(LOG_SLOW_QUERIES_PROPERTY, -1);
Vasu Nori08b448e2010-03-03 10:05:16 -08001847 mStackTrace = new DatabaseObjectNotClosedException().fillInStackTrace();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001848 mFactory = factory;
1849 dbopen(mPath, mFlags);
Vasu Nori5a03f362009-10-20 15:16:35 -07001850 if (SQLiteDebug.DEBUG_SQL_CACHE) {
1851 mTimeOpened = getTime();
1852 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001853 mPrograms = new WeakHashMap<SQLiteClosable,Object>();
1854 try {
1855 setLocale(Locale.getDefault());
1856 } catch (RuntimeException e) {
1857 Log.e(TAG, "Failed to setLocale() when constructing, closing the database", e);
1858 dbclose();
Vasu Nori5a03f362009-10-20 15:16:35 -07001859 if (SQLiteDebug.DEBUG_SQL_CACHE) {
1860 mTimeClosed = getTime();
1861 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001862 throw e;
1863 }
1864 }
1865
Vasu Nori5a03f362009-10-20 15:16:35 -07001866 private String getTime() {
1867 return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS ").format(System.currentTimeMillis());
1868 }
1869
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001870 /**
1871 * return whether the DB is opened as read only.
1872 * @return true if DB is opened as read only
1873 */
1874 public boolean isReadOnly() {
1875 return (mFlags & OPEN_READ_MASK) == OPEN_READONLY;
1876 }
1877
1878 /**
1879 * @return true if the DB is currently open (has not been closed)
1880 */
1881 public boolean isOpen() {
1882 return mNativeHandle != 0;
1883 }
1884
1885 public boolean needUpgrade(int newVersion) {
1886 return newVersion > getVersion();
1887 }
1888
1889 /**
1890 * Getter for the path to the database file.
1891 *
1892 * @return the path to our database file.
1893 */
1894 public final String getPath() {
1895 return mPath;
1896 }
1897
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -08001898 /* package */ void logTimeStat(String sql, long beginMillis) {
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07001899 logTimeStat(sql, beginMillis, null);
1900 }
1901
1902 /* package */ void logTimeStat(String sql, long beginMillis, String prefix) {
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -08001903 // Keep track of the last statement executed here, as this is
1904 // the common funnel through which all methods of hitting
1905 // libsqlite eventually flow.
1906 mLastSqlStatement = sql;
1907
Dan Egnor12311952009-11-23 14:47:45 -08001908 // Sample fast queries in proportion to the time taken.
1909 // Quantize the % first, so the logged sampling probability
1910 // exactly equals the actual sampling rate for this query.
1911
1912 int samplePercent;
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -08001913 long durationMillis = SystemClock.uptimeMillis() - beginMillis;
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07001914 if (durationMillis == 0 && prefix == GET_LOCK_LOG_PREFIX) {
1915 // The common case is locks being uncontended. Don't log those,
1916 // even at 1%, which is our default below.
1917 return;
1918 }
1919 if (sQueryLogTimeInMillis == 0) {
1920 sQueryLogTimeInMillis = SystemProperties.getInt("db.db_operation.threshold_ms", 500);
1921 }
1922 if (durationMillis >= sQueryLogTimeInMillis) {
Dan Egnor12311952009-11-23 14:47:45 -08001923 samplePercent = 100;
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07001924 } else {;
1925 samplePercent = (int) (100 * durationMillis / sQueryLogTimeInMillis) + 1;
Dan Egnor799f7212009-11-24 16:24:44 -08001926 if (mRandom.nextInt(100) >= samplePercent) return;
Dan Egnor12311952009-11-23 14:47:45 -08001927 }
1928
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07001929 // Note: the prefix will be "COMMIT;" or "GETLOCK:" when non-null. We wait to do
1930 // it here so we avoid allocating in the common case.
1931 if (prefix != null) {
1932 sql = prefix + sql;
1933 }
1934
Dan Egnor12311952009-11-23 14:47:45 -08001935 if (sql.length() > QUERY_LOG_SQL_LENGTH) sql = sql.substring(0, QUERY_LOG_SQL_LENGTH);
1936
1937 // ActivityThread.currentPackageName() only returns non-null if the
1938 // current thread is an application main thread. This parameter tells
1939 // us whether an event loop is blocked, and if so, which app it is.
1940 //
1941 // Sadly, there's no fast way to determine app name if this is *not* a
1942 // main thread, or when we are invoked via Binder (e.g. ContentProvider).
1943 // Hopefully the full path to the database will be informative enough.
1944
Dianne Hackborn01e4cfc2010-06-24 15:07:24 -07001945 String blockingPackage = AppGlobals.getInitialPackage();
Dan Egnor12311952009-11-23 14:47:45 -08001946 if (blockingPackage == null) blockingPackage = "";
1947
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -08001948 EventLog.writeEvent(
Brad Fitzpatrickd8330232010-02-19 10:59:01 -08001949 EVENT_DB_OPERATION,
1950 getPathForLogs(),
1951 sql,
1952 durationMillis,
1953 blockingPackage,
1954 samplePercent);
1955 }
1956
1957 /**
1958 * Removes email addresses from database filenames before they're
1959 * logged to the EventLog where otherwise apps could potentially
1960 * read them.
1961 */
1962 private String getPathForLogs() {
1963 if (mPathForLogs != null) {
1964 return mPathForLogs;
1965 }
1966 if (mPath == null) {
1967 return null;
1968 }
1969 if (mPath.indexOf('@') == -1) {
1970 mPathForLogs = mPath;
1971 } else {
1972 mPathForLogs = EMAIL_IN_DB_PATTERN.matcher(mPath).replaceAll("XX@YY");
1973 }
1974 return mPathForLogs;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001975 }
1976
1977 /**
1978 * Sets the locale for this database. Does nothing if this database has
1979 * the NO_LOCALIZED_COLLATORS flag set or was opened read only.
1980 * @throws SQLException if the locale could not be set. The most common reason
1981 * for this is that there is no collator available for the locale you requested.
1982 * In this case the database remains unchanged.
1983 */
1984 public void setLocale(Locale locale) {
1985 lock();
1986 try {
1987 native_setLocale(locale.toString(), mFlags);
1988 } finally {
1989 unlock();
1990 }
1991 }
1992
Vasu Norie495d1f2010-01-06 16:34:19 -08001993 /*
1994 * ============================================================================
1995 *
1996 * The following methods deal with compiled-sql cache
1997 * ============================================================================
1998 */
1999 /**
2000 * adds the given sql and its compiled-statement-id-returned-by-sqlite to the
2001 * cache of compiledQueries attached to 'this'.
2002 *
2003 * if there is already a {@link SQLiteCompiledSql} in compiledQueries for the given sql,
2004 * the new {@link SQLiteCompiledSql} object is NOT inserted into the cache (i.e.,the current
2005 * mapping is NOT replaced with the new mapping).
2006 */
2007 /* package */ void addToCompiledQueries(String sql, SQLiteCompiledSql compiledStatement) {
2008 if (mMaxSqlCacheSize == 0) {
2009 // for this database, there is no cache of compiled sql.
2010 if (SQLiteDebug.DEBUG_SQL_CACHE) {
2011 Log.v(TAG, "|NOT adding_sql_to_cache|" + getPath() + "|" + sql);
2012 }
2013 return;
2014 }
2015
2016 SQLiteCompiledSql compiledSql = null;
2017 synchronized(mCompiledQueries) {
2018 // don't insert the new mapping if a mapping already exists
2019 compiledSql = mCompiledQueries.get(sql);
2020 if (compiledSql != null) {
2021 return;
2022 }
2023 // add this <sql, compiledStatement> to the cache
2024 if (mCompiledQueries.size() == mMaxSqlCacheSize) {
Vasu Nori49d02ac2010-03-05 21:49:30 -08002025 /*
2026 * cache size of {@link #mMaxSqlCacheSize} is not enough for this app.
2027 * log a warning MAX_WARNINGS_ON_CACHESIZE_CONDITION times
2028 * chances are it is NOT using ? for bindargs - so caching is useless.
2029 * TODO: either let the callers set max cchesize for their app, or intelligently
2030 * figure out what should be cached for a given app.
Vasu Norie495d1f2010-01-06 16:34:19 -08002031 */
Vasu Nori49d02ac2010-03-05 21:49:30 -08002032 if (++mCacheFullWarnings == MAX_WARNINGS_ON_CACHESIZE_CONDITION) {
2033 Log.w(TAG, "Reached MAX size for compiled-sql statement cache for database " +
2034 getPath() + "; i.e., NO space for this sql statement in cache: " +
2035 sql + ". Please change your sql statements to use '?' for " +
2036 "bindargs, instead of using actual values");
2037 }
2038 // don't add this entry to cache
2039 } else {
2040 // cache is NOT full. add this to cache.
2041 mCompiledQueries.put(sql, compiledStatement);
2042 if (SQLiteDebug.DEBUG_SQL_CACHE) {
2043 Log.v(TAG, "|adding_sql_to_cache|" + getPath() + "|" +
2044 mCompiledQueries.size() + "|" + sql);
Vasu Norie495d1f2010-01-06 16:34:19 -08002045 }
2046 }
Vasu Norie495d1f2010-01-06 16:34:19 -08002047 }
2048 return;
2049 }
2050
2051
2052 private void deallocCachedSqlStatements() {
2053 synchronized (mCompiledQueries) {
2054 for (SQLiteCompiledSql compiledSql : mCompiledQueries.values()) {
2055 compiledSql.releaseSqlStatement();
2056 }
2057 mCompiledQueries.clear();
2058 }
2059 }
2060
2061 /**
2062 * from the compiledQueries cache, returns the compiled-statement-id for the given sql.
2063 * returns null, if not found in the cache.
2064 */
2065 /* package */ SQLiteCompiledSql getCompiledStatementForSql(String sql) {
2066 SQLiteCompiledSql compiledStatement = null;
2067 boolean cacheHit;
2068 synchronized(mCompiledQueries) {
2069 if (mMaxSqlCacheSize == 0) {
2070 // for this database, there is no cache of compiled sql.
2071 if (SQLiteDebug.DEBUG_SQL_CACHE) {
2072 Log.v(TAG, "|cache NOT found|" + getPath());
2073 }
2074 return null;
2075 }
2076 cacheHit = (compiledStatement = mCompiledQueries.get(sql)) != null;
2077 }
2078 if (cacheHit) {
2079 mNumCacheHits++;
2080 } else {
2081 mNumCacheMisses++;
2082 }
2083
2084 if (SQLiteDebug.DEBUG_SQL_CACHE) {
2085 Log.v(TAG, "|cache_stats|" +
2086 getPath() + "|" + mCompiledQueries.size() +
2087 "|" + mNumCacheHits + "|" + mNumCacheMisses +
2088 "|" + cacheHit + "|" + mTimeOpened + "|" + mTimeClosed + "|" + sql);
2089 }
2090 return compiledStatement;
2091 }
2092
2093 /**
2094 * returns true if the given sql is cached in compiled-sql cache.
2095 * @hide
2096 */
2097 public boolean isInCompiledSqlCache(String sql) {
2098 synchronized(mCompiledQueries) {
2099 return mCompiledQueries.containsKey(sql);
2100 }
2101 }
2102
2103 /**
2104 * purges the given sql from the compiled-sql cache.
2105 * @hide
2106 */
2107 public void purgeFromCompiledSqlCache(String sql) {
2108 synchronized(mCompiledQueries) {
2109 mCompiledQueries.remove(sql);
2110 }
2111 }
2112
2113 /**
2114 * remove everything from the compiled sql cache
2115 * @hide
2116 */
2117 public void resetCompiledSqlCache() {
2118 synchronized(mCompiledQueries) {
2119 mCompiledQueries.clear();
2120 }
2121 }
2122
2123 /**
2124 * return the current maxCacheSqlCacheSize
2125 * @hide
2126 */
2127 public synchronized int getMaxSqlCacheSize() {
2128 return mMaxSqlCacheSize;
2129 }
2130
2131 /**
2132 * set the max size of the compiled sql cache for this database after purging the cache.
2133 * (size of the cache = number of compiled-sql-statements stored in the cache).
2134 *
2135 * max cache size can ONLY be increased from its current size (default = 0).
2136 * if this method is called with smaller size than the current value of mMaxSqlCacheSize,
2137 * then IllegalStateException is thrown
2138 *
2139 * synchronized because we don't want t threads to change cache size at the same time.
2140 * @param cacheSize the size of the cache. can be (0 to MAX_SQL_CACHE_SIZE)
2141 * @throws IllegalStateException if input cacheSize > MAX_SQL_CACHE_SIZE or < 0 or
2142 * < the value set with previous setMaxSqlCacheSize() call.
2143 *
2144 * @hide
2145 */
2146 public synchronized void setMaxSqlCacheSize(int cacheSize) {
2147 if (cacheSize > MAX_SQL_CACHE_SIZE || cacheSize < 0) {
2148 throw new IllegalStateException("expected value between 0 and " + MAX_SQL_CACHE_SIZE);
2149 } else if (cacheSize < mMaxSqlCacheSize) {
2150 throw new IllegalStateException("cannot set cacheSize to a value less than the value " +
2151 "set with previous setMaxSqlCacheSize() call.");
2152 }
2153 mMaxSqlCacheSize = cacheSize;
2154 }
2155
Vasu Noric3849202010-03-09 10:47:25 -08002156 static class ActiveDatabases {
2157 private static final ActiveDatabases activeDatabases = new ActiveDatabases();
2158 private HashSet<WeakReference<SQLiteDatabase>> mActiveDatabases =
2159 new HashSet<WeakReference<SQLiteDatabase>>();
2160 private ActiveDatabases() {} // disable instantiation of this class
2161 static ActiveDatabases getInstance() {return activeDatabases;}
2162 }
2163
Vasu Norif3cf8a42010-03-23 11:41:44 -07002164 /**
2165 * this method is used to collect data about ALL open databases in the current process.
2166 * bugreport is a user of this data.
2167 */
Vasu Noric3849202010-03-09 10:47:25 -08002168 /* package */ static ArrayList<DbStats> getDbStats() {
2169 ArrayList<DbStats> dbStatsList = new ArrayList<DbStats>();
2170 for (WeakReference<SQLiteDatabase> w : ActiveDatabases.getInstance().mActiveDatabases) {
2171 SQLiteDatabase db = w.get();
2172 if (db == null || !db.isOpen()) {
2173 continue;
2174 }
2175 // get SQLITE_DBSTATUS_LOOKASIDE_USED for the db
2176 int lookasideUsed = db.native_getDbLookaside();
2177
2178 // get the lastnode of the dbname
2179 String path = db.getPath();
2180 int indx = path.lastIndexOf("/");
2181 String lastnode = path.substring((indx != -1) ? ++indx : 0);
2182
2183 // get list of attached dbs and for each db, get its size and pagesize
2184 ArrayList<Pair<String, String>> attachedDbs = getAttachedDbs(db);
Vasu Norif3cf8a42010-03-23 11:41:44 -07002185 if (attachedDbs == null) {
2186 continue;
2187 }
Vasu Noric3849202010-03-09 10:47:25 -08002188 for (int i = 0; i < attachedDbs.size(); i++) {
2189 Pair<String, String> p = attachedDbs.get(i);
2190 long pageCount = getPragmaVal(db, p.first + ".page_count;");
2191
2192 // first entry in the attached db list is always the main database
2193 // don't worry about prefixing the dbname with "main"
2194 String dbName;
2195 if (i == 0) {
2196 dbName = lastnode;
2197 } else {
2198 // lookaside is only relevant for the main db
2199 lookasideUsed = 0;
2200 dbName = " (attached) " + p.first;
2201 // if the attached db has a path, attach the lastnode from the path to above
2202 if (p.second.trim().length() > 0) {
2203 int idx = p.second.lastIndexOf("/");
2204 dbName += " : " + p.second.substring((idx != -1) ? ++idx : 0);
2205 }
2206 }
Vasu Norif3cf8a42010-03-23 11:41:44 -07002207 if (pageCount > 0) {
2208 dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(),
2209 lookasideUsed));
2210 }
Vasu Noric3849202010-03-09 10:47:25 -08002211 }
2212 }
2213 return dbStatsList;
2214 }
2215
2216 /**
2217 * get the specified pragma value from sqlite for the specified database.
2218 * only handles pragma's that return int/long.
2219 * NO JAVA locks are held in this method.
2220 * TODO: use this to do all pragma's in this class
2221 */
2222 private static long getPragmaVal(SQLiteDatabase db, String pragma) {
Vasu Norif3cf8a42010-03-23 11:41:44 -07002223 if (!db.isOpen()) {
2224 return 0;
2225 }
Vasu Noric3849202010-03-09 10:47:25 -08002226 SQLiteStatement prog = null;
2227 try {
2228 prog = new SQLiteStatement(db, "PRAGMA " + pragma);
2229 long val = prog.simpleQueryForLong();
2230 return val;
2231 } finally {
2232 if (prog != null) prog.close();
2233 }
2234 }
2235
2236 /**
2237 * returns list of full pathnames of all attached databases
2238 * including the main database
2239 * TODO: move this to {@link DatabaseUtils}
2240 */
2241 private static ArrayList<Pair<String, String>> getAttachedDbs(SQLiteDatabase dbObj) {
Vasu Norif3cf8a42010-03-23 11:41:44 -07002242 if (!dbObj.isOpen()) {
2243 return null;
2244 }
Vasu Noric3849202010-03-09 10:47:25 -08002245 ArrayList<Pair<String, String>> attachedDbs = new ArrayList<Pair<String, String>>();
2246 Cursor c = dbObj.rawQuery("pragma database_list;", null);
2247 while (c.moveToNext()) {
2248 attachedDbs.add(new Pair<String, String>(c.getString(1), c.getString(2)));
2249 }
2250 c.close();
2251 return attachedDbs;
2252 }
2253
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002254 /**
2255 * Native call to open the database.
2256 *
2257 * @param path The full path to the database
2258 */
2259 private native void dbopen(String path, int flags);
2260
2261 /**
Vasu Nori3ef94e22010-02-05 14:49:04 -08002262 * Native call to setup tracing of all sql statements
2263 *
2264 * @param path the full path to the database
2265 */
2266 private native void enableSqlTracing(String path);
2267
2268 /**
2269 * Native call to setup profiling of all sql statements.
2270 * currently, sqlite's profiling = printing of execution-time
2271 * (wall-clock time) of each of the sql statements, as they
2272 * are executed.
2273 *
2274 * @param path the full path to the database
2275 */
2276 private native void enableSqlProfiling(String path);
2277
2278 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002279 * Native call to execute a raw SQL statement. {@link #lock} must be held
2280 * when calling this method.
2281 *
2282 * @param sql The raw SQL string
2283 * @throws SQLException
2284 */
2285 /* package */ native void native_execSQL(String sql) throws SQLException;
2286
2287 /**
2288 * Native call to set the locale. {@link #lock} must be held when calling
2289 * this method.
2290 * @throws SQLException
2291 */
2292 /* package */ native void native_setLocale(String loc, int flags);
2293
2294 /**
2295 * Returns the row ID of the last row inserted into the database.
2296 *
2297 * @return the row ID of the last row inserted into the database.
2298 */
2299 /* package */ native long lastInsertRow();
2300
2301 /**
2302 * Returns the number of changes made in the last statement executed.
2303 *
2304 * @return the number of changes made in the last statement executed.
2305 */
2306 /* package */ native int lastChangeCount();
Vasu Noric3849202010-03-09 10:47:25 -08002307
2308 /**
2309 * return the SQLITE_DBSTATUS_LOOKASIDE_USED documented here
2310 * http://www.sqlite.org/c3ref/c_dbstatus_lookaside_used.html
2311 * @return int value of SQLITE_DBSTATUS_LOOKASIDE_USED
2312 */
2313 private native int native_getDbLookaside();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002314}