blob: b52f6e06d2773c8052dc8e6d756306b6fad19e7b [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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.content.ContentValues;
23import android.database.Cursor;
24import android.database.DatabaseUtils;
25import android.database.SQLException;
Vasu Noric3849202010-03-09 10:47:25 -080026import android.database.sqlite.SQLiteDebug.DbStats;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.os.Debug;
28import android.os.SystemClock;
Dmitri Plotnikov90142c92009-09-15 10:52:17 -070029import android.os.SystemProperties;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.text.TextUtils;
31import android.util.Config;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.util.EventLog;
Dmitri Plotnikov90142c92009-09-15 10:52:17 -070033import android.util.Log;
Vasu Noric3849202010-03-09 10:47:25 -080034import android.util.Pair;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035
36import java.io.File;
Vasu Noric3849202010-03-09 10:47:25 -080037import java.lang.ref.WeakReference;
Vasu Nori5a03f362009-10-20 15:16:35 -070038import java.text.SimpleDateFormat;
Vasu Noric3849202010-03-09 10:47:25 -080039import java.util.ArrayList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import java.util.HashMap;
Vasu Noric3849202010-03-09 10:47:25 -080041import java.util.HashSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import java.util.Iterator;
43import java.util.Locale;
44import java.util.Map;
Dan Egnor12311952009-11-23 14:47:45 -080045import java.util.Random;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import java.util.Set;
47import java.util.WeakHashMap;
48import java.util.concurrent.locks.ReentrantLock;
Brad Fitzpatrickd8330232010-02-19 10:59:01 -080049import java.util.regex.Pattern;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050
51/**
52 * Exposes methods to manage a SQLite database.
53 * <p>SQLiteDatabase has methods to create, delete, execute SQL commands, and
54 * perform other common database management tasks.
55 * <p>See the Notepad sample application in the SDK for an example of creating
56 * and managing a database.
57 * <p> Database names must be unique within an application, not across all
58 * applications.
59 *
60 * <h3>Localized Collation - ORDER BY</h3>
61 * <p>In addition to SQLite's default <code>BINARY</code> collator, Android supplies
62 * two more, <code>LOCALIZED</code>, which changes with the system's current locale
63 * if you wire it up correctly (XXX a link needed!), and <code>UNICODE</code>, which
64 * is the Unicode Collation Algorithm and not tailored to the current locale.
65 */
66public class SQLiteDatabase extends SQLiteClosable {
67 private static final String TAG = "Database";
Jeff Hamilton082c2af2009-09-29 11:49:51 -070068 private static final int EVENT_DB_OPERATION = 52000;
69 private static final int EVENT_DB_CORRUPT = 75004;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070
71 /**
72 * Algorithms used in ON CONFLICT clause
73 * http://www.sqlite.org/lang_conflict.html
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074 */
Vasu Nori8d45e4e2010-02-05 22:35:47 -080075 /**
76 * When a constraint violation occurs, an immediate ROLLBACK occurs,
77 * thus ending the current transaction, and the command aborts with a
78 * return code of SQLITE_CONSTRAINT. If no transaction is active
79 * (other than the implied transaction that is created on every command)
80 * then this algorithm works the same as ABORT.
81 */
82 public static final int CONFLICT_ROLLBACK = 1;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -070083
Vasu Nori8d45e4e2010-02-05 22:35:47 -080084 /**
85 * When a constraint violation occurs,no ROLLBACK is executed
86 * so changes from prior commands within the same transaction
87 * are preserved. This is the default behavior.
88 */
89 public static final int CONFLICT_ABORT = 2;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -070090
Vasu Nori8d45e4e2010-02-05 22:35:47 -080091 /**
92 * When a constraint violation occurs, the command aborts with a return
93 * code SQLITE_CONSTRAINT. But any changes to the database that
94 * the command made prior to encountering the constraint violation
95 * are preserved and are not backed out.
96 */
97 public static final int CONFLICT_FAIL = 3;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -070098
Vasu Nori8d45e4e2010-02-05 22:35:47 -080099 /**
100 * When a constraint violation occurs, the one row that contains
101 * the constraint violation is not inserted or changed.
102 * But the command continues executing normally. Other rows before and
103 * after the row that contained the constraint violation continue to be
104 * inserted or updated normally. No error is returned.
105 */
106 public static final int CONFLICT_IGNORE = 4;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700107
Vasu Nori8d45e4e2010-02-05 22:35:47 -0800108 /**
109 * When a UNIQUE constraint violation occurs, the pre-existing rows that
110 * are causing the constraint violation are removed prior to inserting
111 * or updating the current row. Thus the insert or update always occurs.
112 * The command continues executing normally. No error is returned.
113 * If a NOT NULL constraint violation occurs, the NULL value is replaced
114 * by the default value for that column. If the column has no default
115 * value, then the ABORT algorithm is used. If a CHECK constraint
116 * violation occurs then the IGNORE algorithm is used. When this conflict
117 * resolution strategy deletes rows in order to satisfy a constraint,
118 * it does not invoke delete triggers on those rows.
119 * This behavior might change in a future release.
120 */
121 public static final int CONFLICT_REPLACE = 5;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700122
Vasu Nori8d45e4e2010-02-05 22:35:47 -0800123 /**
124 * use the following when no conflict action is specified.
125 */
126 public static final int CONFLICT_NONE = 0;
127 private static final String[] CONFLICT_VALUES = new String[]
128 {"", " OR ROLLBACK ", " OR ABORT ", " OR FAIL ", " OR IGNORE ", " OR REPLACE "};
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700129
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800130 /**
131 * Maximum Length Of A LIKE Or GLOB Pattern
132 * The pattern matching algorithm used in the default LIKE and GLOB implementation
133 * of SQLite can exhibit O(N^2) performance (where N is the number of characters in
134 * the pattern) for certain pathological cases. To avoid denial-of-service attacks
135 * the length of the LIKE or GLOB pattern is limited to SQLITE_MAX_LIKE_PATTERN_LENGTH bytes.
136 * The default value of this limit is 50000. A modern workstation can evaluate
137 * even a pathological LIKE or GLOB pattern of 50000 bytes relatively quickly.
138 * The denial of service problem only comes into play when the pattern length gets
139 * into millions of bytes. Nevertheless, since most useful LIKE or GLOB patterns
140 * are at most a few dozen bytes in length, paranoid application developers may
141 * want to reduce this parameter to something in the range of a few hundred
142 * if they know that external users are able to generate arbitrary patterns.
143 */
144 public static final int SQLITE_MAX_LIKE_PATTERN_LENGTH = 50000;
145
146 /**
147 * Flag for {@link #openDatabase} to open the database for reading and writing.
148 * If the disk is full, this may fail even before you actually write anything.
149 *
150 * {@more} Note that the value of this flag is 0, so it is the default.
151 */
152 public static final int OPEN_READWRITE = 0x00000000; // update native code if changing
153
154 /**
155 * Flag for {@link #openDatabase} to open the database for reading only.
156 * This is the only reliable way to open a database if the disk may be full.
157 */
158 public static final int OPEN_READONLY = 0x00000001; // update native code if changing
159
160 private static final int OPEN_READ_MASK = 0x00000001; // update native code if changing
161
162 /**
163 * Flag for {@link #openDatabase} to open the database without support for localized collators.
164 *
165 * {@more} This causes the collator <code>LOCALIZED</code> not to be created.
166 * You must be consistent when using this flag to use the setting the database was
167 * created with. If this is set, {@link #setLocale} will do nothing.
168 */
169 public static final int NO_LOCALIZED_COLLATORS = 0x00000010; // update native code if changing
170
171 /**
172 * Flag for {@link #openDatabase} to create the database file if it does not already exist.
173 */
174 public static final int CREATE_IF_NECESSARY = 0x10000000; // update native code if changing
175
176 /**
177 * Indicates whether the most-recently started transaction has been marked as successful.
178 */
179 private boolean mInnerTransactionIsSuccessful;
180
181 /**
182 * Valid during the life of a transaction, and indicates whether the entire transaction (the
183 * outer one and all of the inner ones) so far has been successful.
184 */
185 private boolean mTransactionIsSuccessful;
186
Fred Quintanac4516a72009-09-03 12:14:06 -0700187 /**
188 * Valid during the life of a transaction.
189 */
190 private SQLiteTransactionListener mTransactionListener;
191
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192 /** Synchronize on this when accessing the database */
193 private final ReentrantLock mLock = new ReentrantLock(true);
194
195 private long mLockAcquiredWallTime = 0L;
196 private long mLockAcquiredThreadTime = 0L;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700197
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 // limit the frequency of complaints about each database to one within 20 sec
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700199 // unless run command adb shell setprop log.tag.Database VERBOSE
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800200 private static final int LOCK_WARNING_WINDOW_IN_MS = 20000;
201 /** If the lock is held this long then a warning will be printed when it is released. */
202 private static final int LOCK_ACQUIRED_WARNING_TIME_IN_MS = 300;
203 private static final int LOCK_ACQUIRED_WARNING_THREAD_TIME_IN_MS = 100;
204 private static final int LOCK_ACQUIRED_WARNING_TIME_IN_MS_ALWAYS_PRINT = 2000;
205
Dmitri Plotnikovb43b58d2009-09-09 18:10:42 -0700206 private static final int SLEEP_AFTER_YIELD_QUANTUM = 1000;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700207
Brad Fitzpatrickd8330232010-02-19 10:59:01 -0800208 // The pattern we remove from database filenames before
209 // potentially logging them.
210 private static final Pattern EMAIL_IN_DB_PATTERN = Pattern.compile("[\\w\\.\\-]+@[\\w\\.\\-]+");
211
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212 private long mLastLockMessageTime = 0L;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700213
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -0800214 // Things related to query logging/sampling for debugging
215 // slow/frequent queries during development. Always log queries
Brad Fitzpatrick722802e2010-03-23 22:22:16 -0700216 // which take (by default) 500ms+; shorter queries are sampled
217 // accordingly. Commit statements, which are typically slow, are
218 // logged together with the most recently executed SQL statement,
219 // for disambiguation. The 500ms value is configurable via a
220 // SystemProperty, but developers actively debugging database I/O
221 // should probably use the regular log tunable,
222 // LOG_SLOW_QUERIES_PROPERTY, defined below.
223 private static int sQueryLogTimeInMillis = 0; // lazily initialized
Dan Egnor12311952009-11-23 14:47:45 -0800224 private static final int QUERY_LOG_SQL_LENGTH = 64;
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -0800225 private static final String COMMIT_SQL = "COMMIT;";
Dan Egnor12311952009-11-23 14:47:45 -0800226 private final Random mRandom = new Random();
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -0800227 private String mLastSqlStatement = null;
Dan Egnor12311952009-11-23 14:47:45 -0800228
Brad Fitzpatrick722802e2010-03-23 22:22:16 -0700229 // String prefix for slow database query EventLog records that show
230 // lock acquistions of the database.
231 /* package */ static final String GET_LOCK_LOG_PREFIX = "GETLOCK:";
232
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 /** Used by native code, do not rename */
234 /* package */ int mNativeHandle = 0;
235
236 /** Used to make temp table names unique */
237 /* package */ int mTempTableSequence = 0;
238
239 /** The path for the database file */
240 private String mPath;
241
Brad Fitzpatrickd8330232010-02-19 10:59:01 -0800242 /** The anonymized path for the database file for logging purposes */
243 private String mPathForLogs = null; // lazily populated
244
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800245 /** The flags passed to open/create */
246 private int mFlags;
247
248 /** The optional factory to use when creating new Cursors */
249 private CursorFactory mFactory;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700250
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800251 private WeakHashMap<SQLiteClosable, Object> mPrograms;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700252
Vasu Nori5a03f362009-10-20 15:16:35 -0700253 /**
254 * for each instance of this class, a cache is maintained to store
255 * the compiled query statement ids returned by sqlite database.
256 * key = sql statement with "?" for bind args
257 * value = {@link SQLiteCompiledSql}
258 * If an application opens the database and keeps it open during its entire life, then
259 * there will not be an overhead of compilation of sql statements by sqlite.
260 *
261 * why is this cache NOT static? because sqlite attaches compiledsql statements to the
262 * struct created when {@link SQLiteDatabase#openDatabase(String, CursorFactory, int)} is
263 * invoked.
264 *
265 * this cache has an upper limit of mMaxSqlCacheSize (settable by calling the method
266 * (@link setMaxCacheSize(int)}). its default is 0 - i.e., no caching by default because
267 * most of the apps don't use "?" syntax in their sql, caching is not useful for them.
268 */
Vasu Norie495d1f2010-01-06 16:34:19 -0800269 /* package */ Map<String, SQLiteCompiledSql> mCompiledQueries = Maps.newHashMap();
270 /**
271 * @hide
272 */
273 public static final int MAX_SQL_CACHE_SIZE = 250;
274 private int mMaxSqlCacheSize = MAX_SQL_CACHE_SIZE; // max cache size per Database instance
Vasu Norie9d92102010-01-20 15:07:26 -0800275 private int mCacheFullWarnings;
Vasu Nori49d02ac2010-03-05 21:49:30 -0800276 private static final int MAX_WARNINGS_ON_CACHESIZE_CONDITION = 1;
Vasu Nori5a03f362009-10-20 15:16:35 -0700277
278 /** maintain stats about number of cache hits and misses */
279 private int mNumCacheHits;
280 private int mNumCacheMisses;
281
282 /** the following 2 members maintain the time when a database is opened and closed */
283 private String mTimeOpened = null;
284 private String mTimeClosed = null;
285
Vasu Norid606b4b2010-02-24 12:54:20 -0800286 /** Used to find out where this object was created in case it never got closed. */
287 private Throwable mStackTrace = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800288
Dmitri Plotnikov90142c92009-09-15 10:52:17 -0700289 // System property that enables logging of slow queries. Specify the threshold in ms.
290 private static final String LOG_SLOW_QUERIES_PROPERTY = "db.log.slow_query_threshold";
291 private final int mSlowQueryThreshold;
292
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800293 /**
294 * @param closable
295 */
296 void addSQLiteClosable(SQLiteClosable closable) {
297 lock();
298 try {
299 mPrograms.put(closable, null);
300 } finally {
301 unlock();
302 }
303 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700304
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800305 void removeSQLiteClosable(SQLiteClosable closable) {
306 lock();
307 try {
308 mPrograms.remove(closable);
309 } finally {
310 unlock();
311 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700312 }
313
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 @Override
315 protected void onAllReferencesReleased() {
316 if (isOpen()) {
Vasu Nori5a03f362009-10-20 15:16:35 -0700317 if (SQLiteDebug.DEBUG_SQL_CACHE) {
318 mTimeClosed = getTime();
319 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320 dbclose();
321 }
322 }
323
324 /**
325 * Attempts to release memory that SQLite holds but does not require to
326 * operate properly. Typically this memory will come from the page cache.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700327 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800328 * @return the number of bytes actually released
329 */
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700330 static public native int releaseMemory();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800331
332 /**
333 * Control whether or not the SQLiteDatabase is made thread-safe by using locks
334 * around critical sections. This is pretty expensive, so if you know that your
335 * DB will only be used by a single thread then you should set this to false.
336 * The default is true.
337 * @param lockingEnabled set to true to enable locks, false otherwise
338 */
339 public void setLockingEnabled(boolean lockingEnabled) {
340 mLockingEnabled = lockingEnabled;
341 }
342
343 /**
344 * If set then the SQLiteDatabase is made thread-safe by using locks
345 * around critical sections
346 */
347 private boolean mLockingEnabled = true;
348
349 /* package */ void onCorruption() {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700350 Log.e(TAG, "Removing corrupt database: " + mPath);
351 EventLog.writeEvent(EVENT_DB_CORRUPT, mPath);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 try {
353 // Close the database (if we can), which will cause subsequent operations to fail.
354 close();
355 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356 // Delete the corrupt file. Don't re-create it now -- that would just confuse people
357 // -- but the next time someone tries to open it, they can set it up from scratch.
Vasu Norif3cf8a42010-03-23 11:41:44 -0700358 if (!mPath.equalsIgnoreCase(":memory")) {
359 // delete is only for non-memory database files
360 new File(mPath).delete();
361 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 }
363 }
364
365 /**
366 * Locks the database for exclusive access. The database lock must be held when
367 * touch the native sqlite3* object since it is single threaded and uses
368 * a polling lock contention algorithm. The lock is recursive, and may be acquired
369 * multiple times by the same thread. This is a no-op if mLockingEnabled is false.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700370 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800371 * @see #unlock()
372 */
373 /* package */ void lock() {
374 if (!mLockingEnabled) return;
375 mLock.lock();
376 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
377 if (mLock.getHoldCount() == 1) {
378 // Use elapsed real-time since the CPU may sleep when waiting for IO
379 mLockAcquiredWallTime = SystemClock.elapsedRealtime();
380 mLockAcquiredThreadTime = Debug.threadCpuTimeNanos();
381 }
382 }
383 }
384
385 /**
386 * Locks the database for exclusive access. The database lock must be held when
387 * touch the native sqlite3* object since it is single threaded and uses
388 * a polling lock contention algorithm. The lock is recursive, and may be acquired
389 * multiple times by the same thread.
390 *
391 * @see #unlockForced()
392 */
393 private void lockForced() {
394 mLock.lock();
395 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
396 if (mLock.getHoldCount() == 1) {
397 // Use elapsed real-time since the CPU may sleep when waiting for IO
398 mLockAcquiredWallTime = SystemClock.elapsedRealtime();
399 mLockAcquiredThreadTime = Debug.threadCpuTimeNanos();
400 }
401 }
402 }
403
404 /**
405 * Releases the database lock. This is a no-op if mLockingEnabled is false.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700406 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800407 * @see #unlock()
408 */
409 /* package */ void unlock() {
410 if (!mLockingEnabled) return;
411 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
412 if (mLock.getHoldCount() == 1) {
413 checkLockHoldTime();
414 }
415 }
416 mLock.unlock();
417 }
418
419 /**
420 * Releases the database lock.
421 *
422 * @see #unlockForced()
423 */
424 private void unlockForced() {
425 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
426 if (mLock.getHoldCount() == 1) {
427 checkLockHoldTime();
428 }
429 }
430 mLock.unlock();
431 }
432
433 private void checkLockHoldTime() {
434 // Use elapsed real-time since the CPU may sleep when waiting for IO
435 long elapsedTime = SystemClock.elapsedRealtime();
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700436 long lockedTime = elapsedTime - mLockAcquiredWallTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800437 if (lockedTime < LOCK_ACQUIRED_WARNING_TIME_IN_MS_ALWAYS_PRINT &&
438 !Log.isLoggable(TAG, Log.VERBOSE) &&
439 (elapsedTime - mLastLockMessageTime) < LOCK_WARNING_WINDOW_IN_MS) {
440 return;
441 }
442 if (lockedTime > LOCK_ACQUIRED_WARNING_TIME_IN_MS) {
443 int threadTime = (int)
444 ((Debug.threadCpuTimeNanos() - mLockAcquiredThreadTime) / 1000000);
445 if (threadTime > LOCK_ACQUIRED_WARNING_THREAD_TIME_IN_MS ||
446 lockedTime > LOCK_ACQUIRED_WARNING_TIME_IN_MS_ALWAYS_PRINT) {
447 mLastLockMessageTime = elapsedTime;
448 String msg = "lock held on " + mPath + " for " + lockedTime + "ms. Thread time was "
449 + threadTime + "ms";
450 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING_STACK_TRACE) {
451 Log.d(TAG, msg, new Exception());
452 } else {
453 Log.d(TAG, msg);
454 }
455 }
456 }
457 }
458
459 /**
460 * Begins a transaction. Transactions can be nested. When the outer transaction is ended all of
461 * the work done in that transaction and all of the nested transactions will be committed or
462 * rolled back. The changes will be rolled back if any transaction is ended without being
463 * marked as clean (by calling setTransactionSuccessful). Otherwise they will be committed.
464 *
465 * <p>Here is the standard idiom for transactions:
466 *
467 * <pre>
468 * db.beginTransaction();
469 * try {
470 * ...
471 * db.setTransactionSuccessful();
472 * } finally {
473 * db.endTransaction();
474 * }
475 * </pre>
476 */
477 public void beginTransaction() {
Fred Quintanac4516a72009-09-03 12:14:06 -0700478 beginTransactionWithListener(null /* transactionStatusCallback */);
479 }
480
481 /**
482 * Begins a transaction. Transactions can be nested. When the outer transaction is ended all of
483 * the work done in that transaction and all of the nested transactions will be committed or
484 * rolled back. The changes will be rolled back if any transaction is ended without being
485 * marked as clean (by calling setTransactionSuccessful). Otherwise they will be committed.
486 *
487 * <p>Here is the standard idiom for transactions:
488 *
489 * <pre>
490 * db.beginTransactionWithListener(listener);
491 * try {
492 * ...
493 * db.setTransactionSuccessful();
494 * } finally {
495 * db.endTransaction();
496 * }
497 * </pre>
498 * @param transactionListener listener that should be notified when the transaction begins,
499 * commits, or is rolled back, either explicitly or by a call to
500 * {@link #yieldIfContendedSafely}.
501 */
502 public void beginTransactionWithListener(SQLiteTransactionListener transactionListener) {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700503 if (!isOpen()) {
504 throw new IllegalStateException("database not open");
505 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800506 lockForced();
507 boolean ok = false;
508 try {
509 // If this thread already had the lock then get out
510 if (mLock.getHoldCount() > 1) {
511 if (mInnerTransactionIsSuccessful) {
512 String msg = "Cannot call beginTransaction between "
513 + "calling setTransactionSuccessful and endTransaction";
514 IllegalStateException e = new IllegalStateException(msg);
515 Log.e(TAG, "beginTransaction() failed", e);
516 throw e;
517 }
518 ok = true;
519 return;
520 }
521
522 // This thread didn't already have the lock, so begin a database
523 // transaction now.
524 execSQL("BEGIN EXCLUSIVE;");
Fred Quintanac4516a72009-09-03 12:14:06 -0700525 mTransactionListener = transactionListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800526 mTransactionIsSuccessful = true;
527 mInnerTransactionIsSuccessful = false;
Fred Quintanac4516a72009-09-03 12:14:06 -0700528 if (transactionListener != null) {
529 try {
530 transactionListener.onBegin();
531 } catch (RuntimeException e) {
532 execSQL("ROLLBACK;");
533 throw e;
534 }
535 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536 ok = true;
537 } finally {
538 if (!ok) {
539 // beginTransaction is called before the try block so we must release the lock in
540 // the case of failure.
541 unlockForced();
542 }
543 }
544 }
545
546 /**
547 * End a transaction. See beginTransaction for notes about how to use this and when transactions
548 * are committed and rolled back.
549 */
550 public void endTransaction() {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700551 if (!isOpen()) {
552 throw new IllegalStateException("database not open");
553 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800554 if (!mLock.isHeldByCurrentThread()) {
555 throw new IllegalStateException("no transaction pending");
556 }
557 try {
558 if (mInnerTransactionIsSuccessful) {
559 mInnerTransactionIsSuccessful = false;
560 } else {
561 mTransactionIsSuccessful = false;
562 }
563 if (mLock.getHoldCount() != 1) {
564 return;
565 }
Fred Quintanac4516a72009-09-03 12:14:06 -0700566 RuntimeException savedException = null;
567 if (mTransactionListener != null) {
568 try {
569 if (mTransactionIsSuccessful) {
570 mTransactionListener.onCommit();
571 } else {
572 mTransactionListener.onRollback();
573 }
574 } catch (RuntimeException e) {
575 savedException = e;
576 mTransactionIsSuccessful = false;
577 }
578 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800579 if (mTransactionIsSuccessful) {
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -0800580 execSQL(COMMIT_SQL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800581 } else {
582 try {
583 execSQL("ROLLBACK;");
Fred Quintanac4516a72009-09-03 12:14:06 -0700584 if (savedException != null) {
585 throw savedException;
586 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587 } catch (SQLException e) {
588 if (Config.LOGD) {
589 Log.d(TAG, "exception during rollback, maybe the DB previously "
590 + "performed an auto-rollback");
591 }
592 }
593 }
594 } finally {
Fred Quintanac4516a72009-09-03 12:14:06 -0700595 mTransactionListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800596 unlockForced();
597 if (Config.LOGV) {
598 Log.v(TAG, "unlocked " + Thread.currentThread()
599 + ", holdCount is " + mLock.getHoldCount());
600 }
601 }
602 }
603
604 /**
605 * Marks the current transaction as successful. Do not do any more database work between
606 * calling this and calling endTransaction. Do as little non-database work as possible in that
607 * situation too. If any errors are encountered between this and endTransaction the transaction
608 * will still be committed.
609 *
610 * @throws IllegalStateException if the current thread is not in a transaction or the
611 * transaction is already marked as successful.
612 */
613 public void setTransactionSuccessful() {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700614 if (!isOpen()) {
615 throw new IllegalStateException("database not open");
616 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800617 if (!mLock.isHeldByCurrentThread()) {
618 throw new IllegalStateException("no transaction pending");
619 }
620 if (mInnerTransactionIsSuccessful) {
621 throw new IllegalStateException(
622 "setTransactionSuccessful may only be called once per call to beginTransaction");
623 }
624 mInnerTransactionIsSuccessful = true;
625 }
626
627 /**
628 * return true if there is a transaction pending
629 */
630 public boolean inTransaction() {
631 return mLock.getHoldCount() > 0;
632 }
633
634 /**
635 * Checks if the database lock is held by this thread.
636 *
637 * @return true, if this thread is holding the database lock.
638 */
639 public boolean isDbLockedByCurrentThread() {
640 return mLock.isHeldByCurrentThread();
641 }
642
643 /**
644 * Checks if the database is locked by another thread. This is
645 * just an estimate, since this status can change at any time,
646 * including after the call is made but before the result has
647 * been acted upon.
648 *
649 * @return true, if the database is locked by another thread
650 */
651 public boolean isDbLockedByOtherThreads() {
652 return !mLock.isHeldByCurrentThread() && mLock.isLocked();
653 }
654
655 /**
656 * Temporarily end the transaction to let other threads run. The transaction is assumed to be
657 * successful so far. Do not call setTransactionSuccessful before calling this. When this
658 * returns a new transaction will have been created but not marked as successful.
659 * @return true if the transaction was yielded
660 * @deprecated if the db is locked more than once (becuase of nested transactions) then the lock
661 * will not be yielded. Use yieldIfContendedSafely instead.
662 */
Dianne Hackborn4a51c202009-08-21 15:14:02 -0700663 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800664 public boolean yieldIfContended() {
Fred Quintana5c7aede2009-08-27 21:41:27 -0700665 return yieldIfContendedHelper(false /* do not check yielding */,
666 -1 /* sleepAfterYieldDelay */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800667 }
668
669 /**
670 * Temporarily end the transaction to let other threads run. The transaction is assumed to be
671 * successful so far. Do not call setTransactionSuccessful before calling this. When this
672 * returns a new transaction will have been created but not marked as successful. This assumes
673 * that there are no nested transactions (beginTransaction has only been called once) and will
Fred Quintana5c7aede2009-08-27 21:41:27 -0700674 * throw an exception if that is not the case.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800675 * @return true if the transaction was yielded
676 */
677 public boolean yieldIfContendedSafely() {
Fred Quintana5c7aede2009-08-27 21:41:27 -0700678 return yieldIfContendedHelper(true /* check yielding */, -1 /* sleepAfterYieldDelay*/);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800679 }
680
Fred Quintana5c7aede2009-08-27 21:41:27 -0700681 /**
682 * Temporarily end the transaction to let other threads run. The transaction is assumed to be
683 * successful so far. Do not call setTransactionSuccessful before calling this. When this
684 * returns a new transaction will have been created but not marked as successful. This assumes
685 * that there are no nested transactions (beginTransaction has only been called once) and will
686 * throw an exception if that is not the case.
687 * @param sleepAfterYieldDelay if > 0, sleep this long before starting a new transaction if
688 * the lock was actually yielded. This will allow other background threads to make some
689 * more progress than they would if we started the transaction immediately.
690 * @return true if the transaction was yielded
691 */
692 public boolean yieldIfContendedSafely(long sleepAfterYieldDelay) {
693 return yieldIfContendedHelper(true /* check yielding */, sleepAfterYieldDelay);
694 }
695
696 private boolean yieldIfContendedHelper(boolean checkFullyYielded, long sleepAfterYieldDelay) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800697 if (mLock.getQueueLength() == 0) {
698 // Reset the lock acquire time since we know that the thread was willing to yield
699 // the lock at this time.
700 mLockAcquiredWallTime = SystemClock.elapsedRealtime();
701 mLockAcquiredThreadTime = Debug.threadCpuTimeNanos();
702 return false;
703 }
704 setTransactionSuccessful();
Fred Quintanac4516a72009-09-03 12:14:06 -0700705 SQLiteTransactionListener transactionListener = mTransactionListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800706 endTransaction();
707 if (checkFullyYielded) {
708 if (this.isDbLockedByCurrentThread()) {
709 throw new IllegalStateException(
710 "Db locked more than once. yielfIfContended cannot yield");
711 }
712 }
Fred Quintana5c7aede2009-08-27 21:41:27 -0700713 if (sleepAfterYieldDelay > 0) {
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700714 // Sleep for up to sleepAfterYieldDelay milliseconds, waking up periodically to
715 // check if anyone is using the database. If the database is not contended,
716 // retake the lock and return.
717 long remainingDelay = sleepAfterYieldDelay;
718 while (remainingDelay > 0) {
719 try {
720 Thread.sleep(remainingDelay < SLEEP_AFTER_YIELD_QUANTUM ?
721 remainingDelay : SLEEP_AFTER_YIELD_QUANTUM);
722 } catch (InterruptedException e) {
723 Thread.interrupted();
724 }
725 remainingDelay -= SLEEP_AFTER_YIELD_QUANTUM;
726 if (mLock.getQueueLength() == 0) {
727 break;
728 }
Fred Quintana5c7aede2009-08-27 21:41:27 -0700729 }
730 }
Fred Quintanac4516a72009-09-03 12:14:06 -0700731 beginTransactionWithListener(transactionListener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800732 return true;
733 }
734
735 /** Maps table names to info about what to which _sync_time column to set
736 * to NULL on an update. This is used to support syncing. */
737 private final Map<String, SyncUpdateInfo> mSyncUpdateInfo =
738 new HashMap<String, SyncUpdateInfo>();
739
740 public Map<String, String> getSyncedTables() {
741 synchronized(mSyncUpdateInfo) {
742 HashMap<String, String> tables = new HashMap<String, String>();
743 for (String table : mSyncUpdateInfo.keySet()) {
744 SyncUpdateInfo info = mSyncUpdateInfo.get(table);
745 if (info.deletedTable != null) {
746 tables.put(table, info.deletedTable);
747 }
748 }
749 return tables;
750 }
751 }
752
753 /**
754 * Internal class used to keep track what needs to be marked as changed
755 * when an update occurs. This is used for syncing, so the sync engine
756 * knows what data has been updated locally.
757 */
758 static private class SyncUpdateInfo {
759 /**
760 * Creates the SyncUpdateInfo class.
761 *
762 * @param masterTable The table to set _sync_time to NULL in
763 * @param deletedTable The deleted table that corresponds to the
764 * master table
765 * @param foreignKey The key that refers to the primary key in table
766 */
767 SyncUpdateInfo(String masterTable, String deletedTable,
768 String foreignKey) {
769 this.masterTable = masterTable;
770 this.deletedTable = deletedTable;
771 this.foreignKey = foreignKey;
772 }
773
774 /** The table containing the _sync_time column */
775 String masterTable;
776
777 /** The deleted table that corresponds to the master table */
778 String deletedTable;
779
780 /** The key in the local table the row in table. It may be _id, if table
781 * is the local table. */
782 String foreignKey;
783 }
784
785 /**
786 * Used to allow returning sub-classes of {@link Cursor} when calling query.
787 */
788 public interface CursorFactory {
789 /**
790 * See
791 * {@link SQLiteCursor#SQLiteCursor(SQLiteDatabase, SQLiteCursorDriver,
792 * String, SQLiteQuery)}.
793 */
794 public Cursor newCursor(SQLiteDatabase db,
795 SQLiteCursorDriver masterQuery, String editTable,
796 SQLiteQuery query);
797 }
798
799 /**
800 * Open the database according to the flags {@link #OPEN_READWRITE}
801 * {@link #OPEN_READONLY} {@link #CREATE_IF_NECESSARY} and/or {@link #NO_LOCALIZED_COLLATORS}.
802 *
803 * <p>Sets the locale of the database to the the system's current locale.
804 * Call {@link #setLocale} if you would like something else.</p>
805 *
806 * @param path to database file to open and/or create
807 * @param factory an optional factory class that is called to instantiate a
808 * cursor when query is called, or null for default
809 * @param flags to control database access mode
810 * @return the newly opened database
811 * @throws SQLiteException if the database cannot be opened
812 */
813 public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags) {
Vasu Noric3849202010-03-09 10:47:25 -0800814 SQLiteDatabase sqliteDatabase = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800815 try {
816 // Open the database.
Vasu Noric3849202010-03-09 10:47:25 -0800817 sqliteDatabase = new SQLiteDatabase(path, factory, flags);
Vasu Nori3ef94e22010-02-05 14:49:04 -0800818 if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
819 sqliteDatabase.enableSqlTracing(path);
820 }
821 if (SQLiteDebug.DEBUG_SQL_TIME) {
822 sqliteDatabase.enableSqlProfiling(path);
823 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800824 } catch (SQLiteDatabaseCorruptException e) {
825 // Try to recover from this, if we can.
826 // TODO: should we do this for other open failures?
827 Log.e(TAG, "Deleting and re-creating corrupt database " + path, e);
Jeff Hamilton082c2af2009-09-29 11:49:51 -0700828 EventLog.writeEvent(EVENT_DB_CORRUPT, path);
Vasu Norif3cf8a42010-03-23 11:41:44 -0700829 if (!path.equalsIgnoreCase(":memory")) {
830 // delete is only for non-memory database files
831 new File(path).delete();
832 }
Vasu Noric3849202010-03-09 10:47:25 -0800833 sqliteDatabase = new SQLiteDatabase(path, factory, flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800834 }
Vasu Noric3849202010-03-09 10:47:25 -0800835 ActiveDatabases.getInstance().mActiveDatabases.add(
836 new WeakReference<SQLiteDatabase>(sqliteDatabase));
837 return sqliteDatabase;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800838 }
839
840 /**
841 * Equivalent to openDatabase(file.getPath(), factory, CREATE_IF_NECESSARY).
842 */
843 public static SQLiteDatabase openOrCreateDatabase(File file, CursorFactory factory) {
844 return openOrCreateDatabase(file.getPath(), factory);
845 }
846
847 /**
848 * Equivalent to openDatabase(path, factory, CREATE_IF_NECESSARY).
849 */
850 public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory) {
851 return openDatabase(path, factory, CREATE_IF_NECESSARY);
852 }
853
854 /**
855 * Create a memory backed SQLite database. Its contents will be destroyed
856 * when the database is closed.
857 *
858 * <p>Sets the locale of the database to the the system's current locale.
859 * Call {@link #setLocale} if you would like something else.</p>
860 *
861 * @param factory an optional factory class that is called to instantiate a
862 * cursor when query is called
863 * @return a SQLiteDatabase object, or null if the database can't be created
864 */
865 public static SQLiteDatabase create(CursorFactory factory) {
866 // This is a magic string with special meaning for SQLite.
867 return openDatabase(":memory:", factory, CREATE_IF_NECESSARY);
868 }
869
870 /**
871 * Close the database.
872 */
873 public void close() {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700874 if (!isOpen()) {
875 return; // already closed
876 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800877 lock();
878 try {
879 closeClosable();
Vasu Norif6373e92010-03-16 10:21:00 -0700880 // close this database instance - regardless of its reference count value
881 onAllReferencesReleased();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800882 } finally {
883 unlock();
884 }
885 }
886
887 private void closeClosable() {
Vasu Norie495d1f2010-01-06 16:34:19 -0800888 /* deallocate all compiled sql statement objects from mCompiledQueries cache.
889 * this should be done before de-referencing all {@link SQLiteClosable} objects
890 * from this database object because calling
891 * {@link SQLiteClosable#onAllReferencesReleasedFromContainer()} could cause the database
892 * to be closed. sqlite doesn't let a database close if there are
893 * any unfinalized statements - such as the compiled-sql objects in mCompiledQueries.
894 */
895 deallocCachedSqlStatements();
896
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800897 Iterator<Map.Entry<SQLiteClosable, Object>> iter = mPrograms.entrySet().iterator();
898 while (iter.hasNext()) {
899 Map.Entry<SQLiteClosable, Object> entry = iter.next();
900 SQLiteClosable program = entry.getKey();
901 if (program != null) {
902 program.onAllReferencesReleasedFromContainer();
903 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700904 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800905 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700906
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800907 /**
908 * Native call to close the database.
909 */
910 private native void dbclose();
911
912 /**
913 * Gets the database version.
914 *
915 * @return the database version
916 */
917 public int getVersion() {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700918 if (!isOpen()) {
919 throw new IllegalStateException("database not open");
920 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800921 SQLiteStatement prog = null;
922 lock();
923 try {
924 prog = new SQLiteStatement(this, "PRAGMA user_version;");
925 long version = prog.simpleQueryForLong();
926 return (int) version;
927 } finally {
928 if (prog != null) prog.close();
929 unlock();
930 }
931 }
932
933 /**
934 * Sets the database version.
935 *
936 * @param version the new database version
937 */
938 public void setVersion(int version) {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700939 if (!isOpen()) {
940 throw new IllegalStateException("database not open");
941 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800942 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 Norif3cf8a42010-03-23 11:41:44 -0700951 if (!isOpen()) {
952 throw new IllegalStateException("database not open");
953 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800954 SQLiteStatement prog = null;
955 lock();
956 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 Norif3cf8a42010-03-23 11:41:44 -0700975 if (!isOpen()) {
976 throw new IllegalStateException("database not open");
977 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800978 SQLiteStatement prog = null;
979 lock();
980 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 Norif3cf8a42010-03-23 11:41:44 -07001003 if (!isOpen()) {
1004 throw new IllegalStateException("database not open");
1005 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001006 SQLiteStatement prog = null;
1007 lock();
1008 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) {
Vasu Norif3cf8a42010-03-23 11:41:44 -07001027 if (!isOpen()) {
1028 throw new IllegalStateException("database not open");
1029 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001030 execSQL("PRAGMA page_size = " + numBytes);
1031 }
1032
1033 /**
1034 * Mark this table as syncable. When an update occurs in this table the
1035 * _sync_dirty field will be set to ensure proper syncing operation.
1036 *
1037 * @param table the table to mark as syncable
1038 * @param deletedTable The deleted table that corresponds to the
1039 * syncable table
1040 */
1041 public void markTableSyncable(String table, String deletedTable) {
1042 markTableSyncable(table, "_id", table, deletedTable);
1043 }
1044
1045 /**
1046 * Mark this table as syncable, with the _sync_dirty residing in another
1047 * table. When an update occurs in this table the _sync_dirty field of the
1048 * row in updateTable with the _id in foreignKey will be set to
1049 * ensure proper syncing operation.
1050 *
1051 * @param table an update on this table will trigger a sync time removal
1052 * @param foreignKey this is the column in table whose value is an _id in
1053 * updateTable
1054 * @param updateTable this is the table that will have its _sync_dirty
1055 */
1056 public void markTableSyncable(String table, String foreignKey,
1057 String updateTable) {
1058 markTableSyncable(table, foreignKey, updateTable, null);
1059 }
1060
1061 /**
1062 * Mark this table as syncable, with the _sync_dirty residing in another
1063 * table. When an update occurs in this table the _sync_dirty field of the
1064 * row in updateTable with the _id in foreignKey will be set to
1065 * ensure proper syncing operation.
1066 *
1067 * @param table an update on this table will trigger a sync time removal
1068 * @param foreignKey this is the column in table whose value is an _id in
1069 * updateTable
1070 * @param updateTable this is the table that will have its _sync_dirty
1071 * @param deletedTable The deleted table that corresponds to the
1072 * updateTable
1073 */
1074 private void markTableSyncable(String table, String foreignKey,
1075 String updateTable, String deletedTable) {
1076 lock();
1077 try {
1078 native_execSQL("SELECT _sync_dirty FROM " + updateTable
1079 + " LIMIT 0");
1080 native_execSQL("SELECT " + foreignKey + " FROM " + table
1081 + " LIMIT 0");
1082 } finally {
1083 unlock();
1084 }
1085
1086 SyncUpdateInfo info = new SyncUpdateInfo(updateTable, deletedTable,
1087 foreignKey);
1088 synchronized (mSyncUpdateInfo) {
1089 mSyncUpdateInfo.put(table, info);
1090 }
1091 }
1092
1093 /**
1094 * Call for each row that is updated in a cursor.
1095 *
1096 * @param table the table the row is in
1097 * @param rowId the row ID of the updated row
1098 */
1099 /* package */ void rowUpdated(String table, long rowId) {
1100 SyncUpdateInfo info;
1101 synchronized (mSyncUpdateInfo) {
1102 info = mSyncUpdateInfo.get(table);
1103 }
1104 if (info != null) {
1105 execSQL("UPDATE " + info.masterTable
1106 + " SET _sync_dirty=1 WHERE _id=(SELECT " + info.foreignKey
1107 + " FROM " + table + " WHERE _id=" + rowId + ")");
1108 }
1109 }
1110
1111 /**
1112 * Finds the name of the first table, which is editable.
1113 *
1114 * @param tables a list of tables
1115 * @return the first table listed
1116 */
1117 public static String findEditTable(String tables) {
1118 if (!TextUtils.isEmpty(tables)) {
1119 // find the first word terminated by either a space or a comma
1120 int spacepos = tables.indexOf(' ');
1121 int commapos = tables.indexOf(',');
1122
1123 if (spacepos > 0 && (spacepos < commapos || commapos < 0)) {
1124 return tables.substring(0, spacepos);
1125 } else if (commapos > 0 && (commapos < spacepos || spacepos < 0) ) {
1126 return tables.substring(0, commapos);
1127 }
1128 return tables;
1129 } else {
1130 throw new IllegalStateException("Invalid tables");
1131 }
1132 }
1133
1134 /**
1135 * Compiles an SQL statement into a reusable pre-compiled statement object.
1136 * The parameters are identical to {@link #execSQL(String)}. You may put ?s in the
1137 * statement and fill in those values with {@link SQLiteProgram#bindString}
1138 * and {@link SQLiteProgram#bindLong} each time you want to run the
1139 * statement. Statements may not return result sets larger than 1x1.
1140 *
1141 * @param sql The raw SQL statement, may contain ? for unknown values to be
1142 * bound later.
1143 * @return a pre-compiled statement object.
1144 */
1145 public SQLiteStatement compileStatement(String sql) throws SQLException {
Vasu Norif3cf8a42010-03-23 11:41:44 -07001146 if (!isOpen()) {
1147 throw new IllegalStateException("database not open");
1148 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001149 lock();
1150 try {
1151 return new SQLiteStatement(this, sql);
1152 } finally {
1153 unlock();
1154 }
1155 }
1156
1157 /**
1158 * Query the given URL, returning a {@link Cursor} over the result set.
1159 *
1160 * @param distinct true if you want each row to be unique, false otherwise.
1161 * @param table The table name to compile the query against.
1162 * @param columns A list of which columns to return. Passing null will
1163 * return all columns, which is discouraged to prevent reading
1164 * data from storage that isn't going to be used.
1165 * @param selection A filter declaring which rows to return, formatted as an
1166 * SQL WHERE clause (excluding the WHERE itself). Passing null
1167 * will return all rows for the given table.
1168 * @param selectionArgs You may include ?s in selection, which will be
1169 * replaced by the values from selectionArgs, in order that they
1170 * appear in the selection. The values will be bound as Strings.
1171 * @param groupBy A filter declaring how to group rows, formatted as an SQL
1172 * GROUP BY clause (excluding the GROUP BY itself). Passing null
1173 * will cause the rows to not be grouped.
1174 * @param having A filter declare which row groups to include in the cursor,
1175 * if row grouping is being used, formatted as an SQL HAVING
1176 * clause (excluding the HAVING itself). Passing null will cause
1177 * all row groups to be included, and is required when row
1178 * grouping is not being used.
1179 * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
1180 * (excluding the ORDER BY itself). Passing null will use the
1181 * default sort order, which may be unordered.
1182 * @param limit Limits the number of rows returned by the query,
1183 * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
1184 * @return A Cursor object, which is positioned before the first entry
1185 * @see Cursor
1186 */
1187 public Cursor query(boolean distinct, String table, String[] columns,
1188 String selection, String[] selectionArgs, String groupBy,
1189 String having, String orderBy, String limit) {
1190 return queryWithFactory(null, distinct, table, columns, selection, selectionArgs,
1191 groupBy, having, orderBy, limit);
1192 }
1193
1194 /**
1195 * Query the given URL, returning a {@link Cursor} over the result set.
1196 *
1197 * @param cursorFactory the cursor factory to use, or null for the default factory
1198 * @param distinct true if you want each row to be unique, false otherwise.
1199 * @param table The table name to compile the query against.
1200 * @param columns A list of which columns to return. Passing null will
1201 * return all columns, which is discouraged to prevent reading
1202 * data from storage that isn't going to be used.
1203 * @param selection A filter declaring which rows to return, formatted as an
1204 * SQL WHERE clause (excluding the WHERE itself). Passing null
1205 * will return all rows for the given table.
1206 * @param selectionArgs You may include ?s in selection, which will be
1207 * replaced by the values from selectionArgs, in order that they
1208 * appear in the selection. The values will be bound as Strings.
1209 * @param groupBy A filter declaring how to group rows, formatted as an SQL
1210 * GROUP BY clause (excluding the GROUP BY itself). Passing null
1211 * will cause the rows to not be grouped.
1212 * @param having A filter declare which row groups to include in the cursor,
1213 * if row grouping is being used, formatted as an SQL HAVING
1214 * clause (excluding the HAVING itself). Passing null will cause
1215 * all row groups to be included, and is required when row
1216 * grouping is not being used.
1217 * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
1218 * (excluding the ORDER BY itself). Passing null will use the
1219 * default sort order, which may be unordered.
1220 * @param limit Limits the number of rows returned by the query,
1221 * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
1222 * @return A Cursor object, which is positioned before the first entry
1223 * @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.
1263 * @return A {@link Cursor} object, which is positioned before the first entry
1264 * @see Cursor
1265 */
1266 public Cursor query(String table, String[] columns, String selection,
1267 String[] selectionArgs, String groupBy, String having,
1268 String orderBy) {
1269
1270 return query(false, table, columns, selection, selectionArgs, groupBy,
1271 having, orderBy, null /* limit */);
1272 }
1273
1274 /**
1275 * Query the given table, returning a {@link Cursor} over the result set.
1276 *
1277 * @param table The table name to compile the query against.
1278 * @param columns A list of which columns to return. Passing null will
1279 * return all columns, which is discouraged to prevent reading
1280 * data from storage that isn't going to be used.
1281 * @param selection A filter declaring which rows to return, formatted as an
1282 * SQL WHERE clause (excluding the WHERE itself). Passing null
1283 * will return all rows for the given table.
1284 * @param selectionArgs You may include ?s in selection, which will be
1285 * replaced by the values from selectionArgs, in order that they
1286 * appear in the selection. The values will be bound as Strings.
1287 * @param groupBy A filter declaring how to group rows, formatted as an SQL
1288 * GROUP BY clause (excluding the GROUP BY itself). Passing null
1289 * will cause the rows to not be grouped.
1290 * @param having A filter declare which row groups to include in the cursor,
1291 * if row grouping is being used, formatted as an SQL HAVING
1292 * clause (excluding the HAVING itself). Passing null will cause
1293 * all row groups to be included, and is required when row
1294 * grouping is not being used.
1295 * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
1296 * (excluding the ORDER BY itself). Passing null will use the
1297 * default sort order, which may be unordered.
1298 * @param limit Limits the number of rows returned by the query,
1299 * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
1300 * @return A {@link Cursor} object, which is positioned before the first entry
1301 * @see Cursor
1302 */
1303 public Cursor query(String table, String[] columns, String selection,
1304 String[] selectionArgs, String groupBy, String having,
1305 String orderBy, String limit) {
1306
1307 return query(false, table, columns, selection, selectionArgs, groupBy,
1308 having, orderBy, limit);
1309 }
1310
1311 /**
1312 * Runs the provided SQL and returns a {@link Cursor} over the result set.
1313 *
1314 * @param sql the SQL query. The SQL string must not be ; terminated
1315 * @param selectionArgs You may include ?s in where clause in the query,
1316 * which will be replaced by the values from selectionArgs. The
1317 * values will be bound as Strings.
1318 * @return A {@link Cursor} object, which is positioned before the first entry
1319 */
1320 public Cursor rawQuery(String sql, String[] selectionArgs) {
1321 return rawQueryWithFactory(null, sql, selectionArgs, null);
1322 }
1323
1324 /**
1325 * Runs the provided SQL and returns a cursor over the result set.
1326 *
1327 * @param cursorFactory the cursor factory to use, or null for the default factory
1328 * @param sql the SQL query. The SQL string must not be ; terminated
1329 * @param selectionArgs You may include ?s in where clause in the query,
1330 * which will be replaced by the values from selectionArgs. The
1331 * values will be bound as Strings.
1332 * @param editTable the name of the first table, which is editable
1333 * @return A {@link Cursor} object, which is positioned before the first entry
1334 */
1335 public Cursor rawQueryWithFactory(
1336 CursorFactory cursorFactory, String sql, String[] selectionArgs,
1337 String editTable) {
Vasu Norif3cf8a42010-03-23 11:41:44 -07001338 if (!isOpen()) {
1339 throw new IllegalStateException("database not open");
1340 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001341 long timeStart = 0;
1342
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001343 if (Config.LOGV || mSlowQueryThreshold != -1) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001344 timeStart = System.currentTimeMillis();
1345 }
1346
1347 SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable);
1348
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001349 Cursor cursor = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001350 try {
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001351 cursor = driver.query(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001352 cursorFactory != null ? cursorFactory : mFactory,
1353 selectionArgs);
1354 } finally {
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001355 if (Config.LOGV || mSlowQueryThreshold != -1) {
1356
1357 // Force query execution
1358 if (cursor != null) {
1359 cursor.moveToFirst();
1360 cursor.moveToPosition(-1);
1361 }
1362
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001363 long duration = System.currentTimeMillis() - timeStart;
1364
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001365 if (Config.LOGV || duration >= mSlowQueryThreshold) {
1366 Log.v(SQLiteCursor.TAG,
1367 "query (" + duration + " ms): " + driver.toString() + ", args are "
1368 + (selectionArgs != null
1369 ? TextUtils.join(",", selectionArgs)
1370 : "<null>"));
1371 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001372 }
1373 }
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001374 return cursor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001375 }
1376
1377 /**
1378 * Runs the provided SQL and returns a cursor over the result set.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001379 * The cursor will read an initial set of rows and the return to the caller.
1380 * It will continue to read in batches and send data changed notifications
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001381 * when the later batches are ready.
1382 * @param sql the SQL query. The SQL string must not be ; terminated
1383 * @param selectionArgs You may include ?s in where clause in the query,
1384 * which will be replaced by the values from selectionArgs. The
1385 * values will be bound as Strings.
1386 * @param initialRead set the initial count of items to read from the cursor
1387 * @param maxRead set the count of items to read on each iteration after the first
1388 * @return A {@link Cursor} object, which is positioned before the first entry
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001389 *
Andy Stadlerf8a7cea2009-04-10 16:24:47 -07001390 * This work is incomplete and not fully tested or reviewed, so currently
1391 * hidden.
1392 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001393 */
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001394 public Cursor rawQuery(String sql, String[] selectionArgs,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001395 int initialRead, int maxRead) {
1396 SQLiteCursor c = (SQLiteCursor)rawQueryWithFactory(
1397 null, sql, selectionArgs, null);
1398 c.setLoadStyle(initialRead, maxRead);
1399 return c;
1400 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001401
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001402 /**
1403 * Convenience method for inserting a row into the database.
1404 *
1405 * @param table the table to insert the row into
1406 * @param nullColumnHack SQL doesn't allow inserting a completely empty row,
1407 * so if initialValues is empty this column will explicitly be
1408 * assigned a NULL value
1409 * @param values this map contains the initial column values for the
1410 * row. The keys should be the column names and the values the
1411 * column values
1412 * @return the row ID of the newly inserted row, or -1 if an error occurred
1413 */
1414 public long insert(String table, String nullColumnHack, ContentValues values) {
1415 try {
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001416 return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001417 } catch (SQLException e) {
1418 Log.e(TAG, "Error inserting " + values, e);
1419 return -1;
1420 }
1421 }
1422
1423 /**
1424 * Convenience method for inserting a row into the database.
1425 *
1426 * @param table the table to insert the row into
1427 * @param nullColumnHack SQL doesn't allow inserting a completely empty row,
1428 * so if initialValues is empty this column will explicitly be
1429 * assigned a NULL value
1430 * @param values this map contains the initial column values for the
1431 * row. The keys should be the column names and the values the
1432 * column values
1433 * @throws SQLException
1434 * @return the row ID of the newly inserted row, or -1 if an error occurred
1435 */
1436 public long insertOrThrow(String table, String nullColumnHack, ContentValues values)
1437 throws SQLException {
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001438 return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001439 }
1440
1441 /**
1442 * Convenience method for replacing a row in the database.
1443 *
1444 * @param table the table in which to replace the row
1445 * @param nullColumnHack SQL doesn't allow inserting a completely empty row,
1446 * so if initialValues is empty this row will explicitly be
1447 * assigned a NULL value
1448 * @param initialValues this map contains the initial column values for
1449 * the row. The key
1450 * @return the row ID of the newly inserted row, or -1 if an error occurred
1451 */
1452 public long replace(String table, String nullColumnHack, ContentValues initialValues) {
1453 try {
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001454 return insertWithOnConflict(table, nullColumnHack, initialValues,
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001455 CONFLICT_REPLACE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001456 } catch (SQLException e) {
1457 Log.e(TAG, "Error inserting " + initialValues, e);
1458 return -1;
1459 }
1460 }
1461
1462 /**
1463 * Convenience method for replacing a row in the database.
1464 *
1465 * @param table the table in which to replace the row
1466 * @param nullColumnHack SQL doesn't allow inserting a completely empty row,
1467 * so if initialValues is empty this row will explicitly be
1468 * assigned a NULL value
1469 * @param initialValues this map contains the initial column values for
1470 * the row. The key
1471 * @throws SQLException
1472 * @return the row ID of the newly inserted row, or -1 if an error occurred
1473 */
1474 public long replaceOrThrow(String table, String nullColumnHack,
1475 ContentValues initialValues) throws SQLException {
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001476 return insertWithOnConflict(table, nullColumnHack, initialValues,
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001477 CONFLICT_REPLACE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001478 }
1479
1480 /**
1481 * General method for inserting a row into the database.
1482 *
1483 * @param table the table to insert the row into
1484 * @param nullColumnHack SQL doesn't allow inserting a completely empty row,
1485 * so if initialValues is empty this column will explicitly be
1486 * assigned a NULL value
1487 * @param initialValues this map contains the initial column values for the
1488 * row. The keys should be the column names and the values the
1489 * column values
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001490 * @param conflictAlgorithm for insert conflict resolver
Vasu Nori6eb7c452010-01-27 14:31:24 -08001491 * @return the row ID of the newly inserted row
1492 * OR the primary key of the existing row if the input param 'conflictAlgorithm' =
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001493 * {@link #CONFLICT_IGNORE}
Vasu Nori6eb7c452010-01-27 14:31:24 -08001494 * OR -1 if any error
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001495 */
1496 public long insertWithOnConflict(String table, String nullColumnHack,
Vasu Nori6eb7c452010-01-27 14:31:24 -08001497 ContentValues initialValues, int conflictAlgorithm) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001498 if (!isOpen()) {
1499 throw new IllegalStateException("database not open");
1500 }
1501
1502 // Measurements show most sql lengths <= 152
1503 StringBuilder sql = new StringBuilder(152);
1504 sql.append("INSERT");
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001505 sql.append(CONFLICT_VALUES[conflictAlgorithm]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001506 sql.append(" INTO ");
1507 sql.append(table);
1508 // Measurements show most values lengths < 40
1509 StringBuilder values = new StringBuilder(40);
1510
1511 Set<Map.Entry<String, Object>> entrySet = null;
1512 if (initialValues != null && initialValues.size() > 0) {
1513 entrySet = initialValues.valueSet();
1514 Iterator<Map.Entry<String, Object>> entriesIter = entrySet.iterator();
1515 sql.append('(');
1516
1517 boolean needSeparator = false;
1518 while (entriesIter.hasNext()) {
1519 if (needSeparator) {
1520 sql.append(", ");
1521 values.append(", ");
1522 }
1523 needSeparator = true;
1524 Map.Entry<String, Object> entry = entriesIter.next();
1525 sql.append(entry.getKey());
1526 values.append('?');
1527 }
1528
1529 sql.append(')');
1530 } else {
1531 sql.append("(" + nullColumnHack + ") ");
1532 values.append("NULL");
1533 }
1534
1535 sql.append(" VALUES(");
1536 sql.append(values);
1537 sql.append(");");
1538
1539 lock();
1540 SQLiteStatement statement = null;
1541 try {
1542 statement = compileStatement(sql.toString());
1543
1544 // Bind the values
1545 if (entrySet != null) {
1546 int size = entrySet.size();
1547 Iterator<Map.Entry<String, Object>> entriesIter = entrySet.iterator();
1548 for (int i = 0; i < size; i++) {
1549 Map.Entry<String, Object> entry = entriesIter.next();
1550 DatabaseUtils.bindObjectToProgram(statement, i + 1, entry.getValue());
1551 }
1552 }
1553
1554 // Run the program and then cleanup
1555 statement.execute();
1556
1557 long insertedRowId = lastInsertRow();
1558 if (insertedRowId == -1) {
1559 Log.e(TAG, "Error inserting " + initialValues + " using " + sql);
1560 } else {
1561 if (Config.LOGD && Log.isLoggable(TAG, Log.VERBOSE)) {
1562 Log.v(TAG, "Inserting row " + insertedRowId + " from "
1563 + initialValues + " using " + sql);
1564 }
1565 }
1566 return insertedRowId;
1567 } catch (SQLiteDatabaseCorruptException e) {
1568 onCorruption();
1569 throw e;
1570 } finally {
1571 if (statement != null) {
1572 statement.close();
1573 }
1574 unlock();
1575 }
1576 }
1577
1578 /**
1579 * Convenience method for deleting rows in the database.
1580 *
1581 * @param table the table to delete from
1582 * @param whereClause the optional WHERE clause to apply when deleting.
1583 * Passing null will delete all rows.
1584 * @return the number of rows affected if a whereClause is passed in, 0
1585 * otherwise. To remove all rows and get a count pass "1" as the
1586 * whereClause.
1587 */
1588 public int delete(String table, String whereClause, String[] whereArgs) {
1589 if (!isOpen()) {
1590 throw new IllegalStateException("database not open");
1591 }
1592 lock();
1593 SQLiteStatement statement = null;
1594 try {
1595 statement = compileStatement("DELETE FROM " + table
1596 + (!TextUtils.isEmpty(whereClause)
1597 ? " WHERE " + whereClause : ""));
1598 if (whereArgs != null) {
1599 int numArgs = whereArgs.length;
1600 for (int i = 0; i < numArgs; i++) {
1601 DatabaseUtils.bindObjectToProgram(statement, i + 1, whereArgs[i]);
1602 }
1603 }
1604 statement.execute();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001605 return lastChangeCount();
1606 } catch (SQLiteDatabaseCorruptException e) {
1607 onCorruption();
1608 throw e;
1609 } finally {
1610 if (statement != null) {
1611 statement.close();
1612 }
1613 unlock();
1614 }
1615 }
1616
1617 /**
1618 * Convenience method for updating rows in the database.
1619 *
1620 * @param table the table to update in
1621 * @param values a map from column names to new column values. null is a
1622 * valid value that will be translated to NULL.
1623 * @param whereClause the optional WHERE clause to apply when updating.
1624 * Passing null will update all rows.
1625 * @return the number of rows affected
1626 */
1627 public int update(String table, ContentValues values, String whereClause, String[] whereArgs) {
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001628 return updateWithOnConflict(table, values, whereClause, whereArgs, CONFLICT_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001629 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001630
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001631 /**
1632 * Convenience method for updating rows in the database.
1633 *
1634 * @param table the table to update in
1635 * @param values a map from column names to new column values. null is a
1636 * valid value that will be translated to NULL.
1637 * @param whereClause the optional WHERE clause to apply when updating.
1638 * Passing null will update all rows.
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001639 * @param conflictAlgorithm for update conflict resolver
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001640 * @return the number of rows affected
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001641 */
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001642 public int updateWithOnConflict(String table, ContentValues values,
Vasu Nori6eb7c452010-01-27 14:31:24 -08001643 String whereClause, String[] whereArgs, int conflictAlgorithm) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001644 if (!isOpen()) {
1645 throw new IllegalStateException("database not open");
1646 }
1647
1648 if (values == null || values.size() == 0) {
1649 throw new IllegalArgumentException("Empty values");
1650 }
1651
1652 StringBuilder sql = new StringBuilder(120);
1653 sql.append("UPDATE ");
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001654 sql.append(CONFLICT_VALUES[conflictAlgorithm]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001655 sql.append(table);
1656 sql.append(" SET ");
1657
1658 Set<Map.Entry<String, Object>> entrySet = values.valueSet();
1659 Iterator<Map.Entry<String, Object>> entriesIter = entrySet.iterator();
1660
1661 while (entriesIter.hasNext()) {
1662 Map.Entry<String, Object> entry = entriesIter.next();
1663 sql.append(entry.getKey());
1664 sql.append("=?");
1665 if (entriesIter.hasNext()) {
1666 sql.append(", ");
1667 }
1668 }
1669
1670 if (!TextUtils.isEmpty(whereClause)) {
1671 sql.append(" WHERE ");
1672 sql.append(whereClause);
1673 }
1674
1675 lock();
1676 SQLiteStatement statement = null;
1677 try {
1678 statement = compileStatement(sql.toString());
1679
1680 // Bind the values
1681 int size = entrySet.size();
1682 entriesIter = entrySet.iterator();
1683 int bindArg = 1;
1684 for (int i = 0; i < size; i++) {
1685 Map.Entry<String, Object> entry = entriesIter.next();
1686 DatabaseUtils.bindObjectToProgram(statement, bindArg, entry.getValue());
1687 bindArg++;
1688 }
1689
1690 if (whereArgs != null) {
1691 size = whereArgs.length;
1692 for (int i = 0; i < size; i++) {
1693 statement.bindString(bindArg, whereArgs[i]);
1694 bindArg++;
1695 }
1696 }
1697
1698 // Run the program and then cleanup
1699 statement.execute();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001700 int numChangedRows = lastChangeCount();
1701 if (Config.LOGD && Log.isLoggable(TAG, Log.VERBOSE)) {
1702 Log.v(TAG, "Updated " + numChangedRows + " using " + values + " and " + sql);
1703 }
1704 return numChangedRows;
1705 } catch (SQLiteDatabaseCorruptException e) {
1706 onCorruption();
1707 throw e;
1708 } catch (SQLException e) {
1709 Log.e(TAG, "Error updating " + values + " using " + sql);
1710 throw e;
1711 } finally {
1712 if (statement != null) {
1713 statement.close();
1714 }
1715 unlock();
1716 }
1717 }
1718
1719 /**
1720 * Execute a single SQL statement that is not a query. For example, CREATE
1721 * TABLE, DELETE, INSERT, etc. Multiple statements separated by ;s are not
1722 * supported. it takes a write lock
1723 *
1724 * @throws SQLException If the SQL string is invalid for some reason
1725 */
1726 public void execSQL(String sql) throws SQLException {
Vasu Norif3cf8a42010-03-23 11:41:44 -07001727 if (!isOpen()) {
1728 throw new IllegalStateException("database not open");
1729 }
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -08001730 long timeStart = SystemClock.uptimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001731 lock();
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07001732 logTimeStat(mLastSqlStatement, timeStart, GET_LOCK_LOG_PREFIX);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001733 try {
1734 native_execSQL(sql);
1735 } catch (SQLiteDatabaseCorruptException e) {
1736 onCorruption();
1737 throw e;
1738 } finally {
1739 unlock();
1740 }
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -08001741
1742 // Log commit statements along with the most recently executed
1743 // SQL statement for disambiguation. Note that instance
1744 // equality to COMMIT_SQL is safe here.
1745 if (sql == COMMIT_SQL) {
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07001746 logTimeStat(mLastSqlStatement, timeStart, COMMIT_SQL);
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -08001747 } else {
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07001748 logTimeStat(sql, timeStart, null);
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -08001749 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001750 }
1751
1752 /**
1753 * Execute a single SQL statement that is not a query. For example, CREATE
1754 * TABLE, DELETE, INSERT, etc. Multiple statements separated by ;s are not
1755 * supported. it takes a write lock,
1756 *
1757 * @param sql
1758 * @param bindArgs only byte[], String, Long and Double are supported in bindArgs.
1759 * @throws SQLException If the SQL string is invalid for some reason
1760 */
1761 public void execSQL(String sql, Object[] bindArgs) throws SQLException {
Vasu Norif3cf8a42010-03-23 11:41:44 -07001762 if (!isOpen()) {
1763 throw new IllegalStateException("database not open");
1764 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001765 if (bindArgs == null) {
1766 throw new IllegalArgumentException("Empty bindArgs");
1767 }
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -08001768 long timeStart = SystemClock.uptimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001769 lock();
1770 SQLiteStatement statement = null;
1771 try {
1772 statement = compileStatement(sql);
1773 if (bindArgs != null) {
1774 int numArgs = bindArgs.length;
1775 for (int i = 0; i < numArgs; i++) {
1776 DatabaseUtils.bindObjectToProgram(statement, i + 1, bindArgs[i]);
1777 }
1778 }
1779 statement.execute();
1780 } catch (SQLiteDatabaseCorruptException e) {
1781 onCorruption();
1782 throw e;
1783 } finally {
1784 if (statement != null) {
1785 statement.close();
1786 }
1787 unlock();
1788 }
Dan Egnor12311952009-11-23 14:47:45 -08001789 logTimeStat(sql, timeStart);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001790 }
1791
1792 @Override
1793 protected void finalize() {
1794 if (isOpen()) {
Vasu Norid606b4b2010-02-24 12:54:20 -08001795 Log.e(TAG, "close() was never explicitly called on database '" +
1796 mPath + "' ", mStackTrace);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001797 closeClosable();
1798 onAllReferencesReleased();
1799 }
1800 }
1801
1802 /**
1803 * Private constructor. See {@link #create} and {@link #openDatabase}.
1804 *
1805 * @param path The full path to the database
1806 * @param factory The factory to use when creating cursors, may be NULL.
1807 * @param flags 0 or {@link #NO_LOCALIZED_COLLATORS}. If the database file already
1808 * exists, mFlags will be updated appropriately.
1809 */
1810 private SQLiteDatabase(String path, CursorFactory factory, int flags) {
1811 if (path == null) {
1812 throw new IllegalArgumentException("path should not be null");
1813 }
1814 mFlags = flags;
1815 mPath = path;
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001816 mSlowQueryThreshold = SystemProperties.getInt(LOG_SLOW_QUERIES_PROPERTY, -1);
Vasu Nori08b448e2010-03-03 10:05:16 -08001817 mStackTrace = new DatabaseObjectNotClosedException().fillInStackTrace();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001818 mFactory = factory;
1819 dbopen(mPath, mFlags);
Vasu Nori5a03f362009-10-20 15:16:35 -07001820 if (SQLiteDebug.DEBUG_SQL_CACHE) {
1821 mTimeOpened = getTime();
1822 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001823 mPrograms = new WeakHashMap<SQLiteClosable,Object>();
1824 try {
1825 setLocale(Locale.getDefault());
1826 } catch (RuntimeException e) {
1827 Log.e(TAG, "Failed to setLocale() when constructing, closing the database", e);
1828 dbclose();
Vasu Nori5a03f362009-10-20 15:16:35 -07001829 if (SQLiteDebug.DEBUG_SQL_CACHE) {
1830 mTimeClosed = getTime();
1831 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001832 throw e;
1833 }
1834 }
1835
Vasu Nori5a03f362009-10-20 15:16:35 -07001836 private String getTime() {
1837 return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS ").format(System.currentTimeMillis());
1838 }
1839
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001840 /**
1841 * return whether the DB is opened as read only.
1842 * @return true if DB is opened as read only
1843 */
1844 public boolean isReadOnly() {
1845 return (mFlags & OPEN_READ_MASK) == OPEN_READONLY;
1846 }
1847
1848 /**
1849 * @return true if the DB is currently open (has not been closed)
1850 */
1851 public boolean isOpen() {
1852 return mNativeHandle != 0;
1853 }
1854
1855 public boolean needUpgrade(int newVersion) {
1856 return newVersion > getVersion();
1857 }
1858
1859 /**
1860 * Getter for the path to the database file.
1861 *
1862 * @return the path to our database file.
1863 */
1864 public final String getPath() {
1865 return mPath;
1866 }
1867
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -08001868 /* package */ void logTimeStat(String sql, long beginMillis) {
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07001869 logTimeStat(sql, beginMillis, null);
1870 }
1871
1872 /* package */ void logTimeStat(String sql, long beginMillis, String prefix) {
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -08001873 // Keep track of the last statement executed here, as this is
1874 // the common funnel through which all methods of hitting
1875 // libsqlite eventually flow.
1876 mLastSqlStatement = sql;
1877
Dan Egnor12311952009-11-23 14:47:45 -08001878 // Sample fast queries in proportion to the time taken.
1879 // Quantize the % first, so the logged sampling probability
1880 // exactly equals the actual sampling rate for this query.
1881
1882 int samplePercent;
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -08001883 long durationMillis = SystemClock.uptimeMillis() - beginMillis;
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07001884 if (durationMillis == 0 && prefix == GET_LOCK_LOG_PREFIX) {
1885 // The common case is locks being uncontended. Don't log those,
1886 // even at 1%, which is our default below.
1887 return;
1888 }
1889 if (sQueryLogTimeInMillis == 0) {
1890 sQueryLogTimeInMillis = SystemProperties.getInt("db.db_operation.threshold_ms", 500);
1891 }
1892 if (durationMillis >= sQueryLogTimeInMillis) {
Dan Egnor12311952009-11-23 14:47:45 -08001893 samplePercent = 100;
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07001894 } else {;
1895 samplePercent = (int) (100 * durationMillis / sQueryLogTimeInMillis) + 1;
Dan Egnor799f7212009-11-24 16:24:44 -08001896 if (mRandom.nextInt(100) >= samplePercent) return;
Dan Egnor12311952009-11-23 14:47:45 -08001897 }
1898
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07001899 // Note: the prefix will be "COMMIT;" or "GETLOCK:" when non-null. We wait to do
1900 // it here so we avoid allocating in the common case.
1901 if (prefix != null) {
1902 sql = prefix + sql;
1903 }
1904
Dan Egnor12311952009-11-23 14:47:45 -08001905 if (sql.length() > QUERY_LOG_SQL_LENGTH) sql = sql.substring(0, QUERY_LOG_SQL_LENGTH);
1906
1907 // ActivityThread.currentPackageName() only returns non-null if the
1908 // current thread is an application main thread. This parameter tells
1909 // us whether an event loop is blocked, and if so, which app it is.
1910 //
1911 // Sadly, there's no fast way to determine app name if this is *not* a
1912 // main thread, or when we are invoked via Binder (e.g. ContentProvider).
1913 // Hopefully the full path to the database will be informative enough.
1914
1915 String blockingPackage = ActivityThread.currentPackageName();
1916 if (blockingPackage == null) blockingPackage = "";
1917
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -08001918 EventLog.writeEvent(
Brad Fitzpatrickd8330232010-02-19 10:59:01 -08001919 EVENT_DB_OPERATION,
1920 getPathForLogs(),
1921 sql,
1922 durationMillis,
1923 blockingPackage,
1924 samplePercent);
1925 }
1926
1927 /**
1928 * Removes email addresses from database filenames before they're
1929 * logged to the EventLog where otherwise apps could potentially
1930 * read them.
1931 */
1932 private String getPathForLogs() {
1933 if (mPathForLogs != null) {
1934 return mPathForLogs;
1935 }
1936 if (mPath == null) {
1937 return null;
1938 }
1939 if (mPath.indexOf('@') == -1) {
1940 mPathForLogs = mPath;
1941 } else {
1942 mPathForLogs = EMAIL_IN_DB_PATTERN.matcher(mPath).replaceAll("XX@YY");
1943 }
1944 return mPathForLogs;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001945 }
1946
1947 /**
1948 * Sets the locale for this database. Does nothing if this database has
1949 * the NO_LOCALIZED_COLLATORS flag set or was opened read only.
1950 * @throws SQLException if the locale could not be set. The most common reason
1951 * for this is that there is no collator available for the locale you requested.
1952 * In this case the database remains unchanged.
1953 */
1954 public void setLocale(Locale locale) {
1955 lock();
1956 try {
1957 native_setLocale(locale.toString(), mFlags);
1958 } finally {
1959 unlock();
1960 }
1961 }
1962
Vasu Norie495d1f2010-01-06 16:34:19 -08001963 /*
1964 * ============================================================================
1965 *
1966 * The following methods deal with compiled-sql cache
1967 * ============================================================================
1968 */
1969 /**
1970 * adds the given sql and its compiled-statement-id-returned-by-sqlite to the
1971 * cache of compiledQueries attached to 'this'.
1972 *
1973 * if there is already a {@link SQLiteCompiledSql} in compiledQueries for the given sql,
1974 * the new {@link SQLiteCompiledSql} object is NOT inserted into the cache (i.e.,the current
1975 * mapping is NOT replaced with the new mapping).
1976 */
1977 /* package */ void addToCompiledQueries(String sql, SQLiteCompiledSql compiledStatement) {
1978 if (mMaxSqlCacheSize == 0) {
1979 // for this database, there is no cache of compiled sql.
1980 if (SQLiteDebug.DEBUG_SQL_CACHE) {
1981 Log.v(TAG, "|NOT adding_sql_to_cache|" + getPath() + "|" + sql);
1982 }
1983 return;
1984 }
1985
1986 SQLiteCompiledSql compiledSql = null;
1987 synchronized(mCompiledQueries) {
1988 // don't insert the new mapping if a mapping already exists
1989 compiledSql = mCompiledQueries.get(sql);
1990 if (compiledSql != null) {
1991 return;
1992 }
1993 // add this <sql, compiledStatement> to the cache
1994 if (mCompiledQueries.size() == mMaxSqlCacheSize) {
Vasu Nori49d02ac2010-03-05 21:49:30 -08001995 /*
1996 * cache size of {@link #mMaxSqlCacheSize} is not enough for this app.
1997 * log a warning MAX_WARNINGS_ON_CACHESIZE_CONDITION times
1998 * chances are it is NOT using ? for bindargs - so caching is useless.
1999 * TODO: either let the callers set max cchesize for their app, or intelligently
2000 * figure out what should be cached for a given app.
Vasu Norie495d1f2010-01-06 16:34:19 -08002001 */
Vasu Nori49d02ac2010-03-05 21:49:30 -08002002 if (++mCacheFullWarnings == MAX_WARNINGS_ON_CACHESIZE_CONDITION) {
2003 Log.w(TAG, "Reached MAX size for compiled-sql statement cache for database " +
2004 getPath() + "; i.e., NO space for this sql statement in cache: " +
2005 sql + ". Please change your sql statements to use '?' for " +
2006 "bindargs, instead of using actual values");
2007 }
2008 // don't add this entry to cache
2009 } else {
2010 // cache is NOT full. add this to cache.
2011 mCompiledQueries.put(sql, compiledStatement);
2012 if (SQLiteDebug.DEBUG_SQL_CACHE) {
2013 Log.v(TAG, "|adding_sql_to_cache|" + getPath() + "|" +
2014 mCompiledQueries.size() + "|" + sql);
Vasu Norie495d1f2010-01-06 16:34:19 -08002015 }
2016 }
Vasu Norie495d1f2010-01-06 16:34:19 -08002017 }
2018 return;
2019 }
2020
2021
2022 private void deallocCachedSqlStatements() {
2023 synchronized (mCompiledQueries) {
2024 for (SQLiteCompiledSql compiledSql : mCompiledQueries.values()) {
2025 compiledSql.releaseSqlStatement();
2026 }
2027 mCompiledQueries.clear();
2028 }
2029 }
2030
2031 /**
2032 * from the compiledQueries cache, returns the compiled-statement-id for the given sql.
2033 * returns null, if not found in the cache.
2034 */
2035 /* package */ SQLiteCompiledSql getCompiledStatementForSql(String sql) {
2036 SQLiteCompiledSql compiledStatement = null;
2037 boolean cacheHit;
2038 synchronized(mCompiledQueries) {
2039 if (mMaxSqlCacheSize == 0) {
2040 // for this database, there is no cache of compiled sql.
2041 if (SQLiteDebug.DEBUG_SQL_CACHE) {
2042 Log.v(TAG, "|cache NOT found|" + getPath());
2043 }
2044 return null;
2045 }
2046 cacheHit = (compiledStatement = mCompiledQueries.get(sql)) != null;
2047 }
2048 if (cacheHit) {
2049 mNumCacheHits++;
2050 } else {
2051 mNumCacheMisses++;
2052 }
2053
2054 if (SQLiteDebug.DEBUG_SQL_CACHE) {
2055 Log.v(TAG, "|cache_stats|" +
2056 getPath() + "|" + mCompiledQueries.size() +
2057 "|" + mNumCacheHits + "|" + mNumCacheMisses +
2058 "|" + cacheHit + "|" + mTimeOpened + "|" + mTimeClosed + "|" + sql);
2059 }
2060 return compiledStatement;
2061 }
2062
2063 /**
2064 * returns true if the given sql is cached in compiled-sql cache.
2065 * @hide
2066 */
2067 public boolean isInCompiledSqlCache(String sql) {
2068 synchronized(mCompiledQueries) {
2069 return mCompiledQueries.containsKey(sql);
2070 }
2071 }
2072
2073 /**
2074 * purges the given sql from the compiled-sql cache.
2075 * @hide
2076 */
2077 public void purgeFromCompiledSqlCache(String sql) {
2078 synchronized(mCompiledQueries) {
2079 mCompiledQueries.remove(sql);
2080 }
2081 }
2082
2083 /**
2084 * remove everything from the compiled sql cache
2085 * @hide
2086 */
2087 public void resetCompiledSqlCache() {
2088 synchronized(mCompiledQueries) {
2089 mCompiledQueries.clear();
2090 }
2091 }
2092
2093 /**
2094 * return the current maxCacheSqlCacheSize
2095 * @hide
2096 */
2097 public synchronized int getMaxSqlCacheSize() {
2098 return mMaxSqlCacheSize;
2099 }
2100
2101 /**
2102 * set the max size of the compiled sql cache for this database after purging the cache.
2103 * (size of the cache = number of compiled-sql-statements stored in the cache).
2104 *
2105 * max cache size can ONLY be increased from its current size (default = 0).
2106 * if this method is called with smaller size than the current value of mMaxSqlCacheSize,
2107 * then IllegalStateException is thrown
2108 *
2109 * synchronized because we don't want t threads to change cache size at the same time.
2110 * @param cacheSize the size of the cache. can be (0 to MAX_SQL_CACHE_SIZE)
2111 * @throws IllegalStateException if input cacheSize > MAX_SQL_CACHE_SIZE or < 0 or
2112 * < the value set with previous setMaxSqlCacheSize() call.
2113 *
2114 * @hide
2115 */
2116 public synchronized void setMaxSqlCacheSize(int cacheSize) {
2117 if (cacheSize > MAX_SQL_CACHE_SIZE || cacheSize < 0) {
2118 throw new IllegalStateException("expected value between 0 and " + MAX_SQL_CACHE_SIZE);
2119 } else if (cacheSize < mMaxSqlCacheSize) {
2120 throw new IllegalStateException("cannot set cacheSize to a value less than the value " +
2121 "set with previous setMaxSqlCacheSize() call.");
2122 }
2123 mMaxSqlCacheSize = cacheSize;
2124 }
2125
Vasu Noric3849202010-03-09 10:47:25 -08002126 static class ActiveDatabases {
2127 private static final ActiveDatabases activeDatabases = new ActiveDatabases();
2128 private HashSet<WeakReference<SQLiteDatabase>> mActiveDatabases =
2129 new HashSet<WeakReference<SQLiteDatabase>>();
2130 private ActiveDatabases() {} // disable instantiation of this class
2131 static ActiveDatabases getInstance() {return activeDatabases;}
2132 }
2133
Vasu Norif3cf8a42010-03-23 11:41:44 -07002134 /**
2135 * this method is used to collect data about ALL open databases in the current process.
2136 * bugreport is a user of this data.
2137 */
Vasu Noric3849202010-03-09 10:47:25 -08002138 /* package */ static ArrayList<DbStats> getDbStats() {
2139 ArrayList<DbStats> dbStatsList = new ArrayList<DbStats>();
2140 for (WeakReference<SQLiteDatabase> w : ActiveDatabases.getInstance().mActiveDatabases) {
2141 SQLiteDatabase db = w.get();
2142 if (db == null || !db.isOpen()) {
2143 continue;
2144 }
2145 // get SQLITE_DBSTATUS_LOOKASIDE_USED for the db
2146 int lookasideUsed = db.native_getDbLookaside();
2147
2148 // get the lastnode of the dbname
2149 String path = db.getPath();
2150 int indx = path.lastIndexOf("/");
2151 String lastnode = path.substring((indx != -1) ? ++indx : 0);
2152
2153 // get list of attached dbs and for each db, get its size and pagesize
2154 ArrayList<Pair<String, String>> attachedDbs = getAttachedDbs(db);
Vasu Norif3cf8a42010-03-23 11:41:44 -07002155 if (attachedDbs == null) {
2156 continue;
2157 }
Vasu Noric3849202010-03-09 10:47:25 -08002158 for (int i = 0; i < attachedDbs.size(); i++) {
2159 Pair<String, String> p = attachedDbs.get(i);
2160 long pageCount = getPragmaVal(db, p.first + ".page_count;");
2161
2162 // first entry in the attached db list is always the main database
2163 // don't worry about prefixing the dbname with "main"
2164 String dbName;
2165 if (i == 0) {
2166 dbName = lastnode;
2167 } else {
2168 // lookaside is only relevant for the main db
2169 lookasideUsed = 0;
2170 dbName = " (attached) " + p.first;
2171 // if the attached db has a path, attach the lastnode from the path to above
2172 if (p.second.trim().length() > 0) {
2173 int idx = p.second.lastIndexOf("/");
2174 dbName += " : " + p.second.substring((idx != -1) ? ++idx : 0);
2175 }
2176 }
Vasu Norif3cf8a42010-03-23 11:41:44 -07002177 if (pageCount > 0) {
2178 dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(),
2179 lookasideUsed));
2180 }
Vasu Noric3849202010-03-09 10:47:25 -08002181 }
2182 }
2183 return dbStatsList;
2184 }
2185
2186 /**
2187 * get the specified pragma value from sqlite for the specified database.
2188 * only handles pragma's that return int/long.
2189 * NO JAVA locks are held in this method.
2190 * TODO: use this to do all pragma's in this class
2191 */
2192 private static long getPragmaVal(SQLiteDatabase db, String pragma) {
Vasu Norif3cf8a42010-03-23 11:41:44 -07002193 if (!db.isOpen()) {
2194 return 0;
2195 }
Vasu Noric3849202010-03-09 10:47:25 -08002196 SQLiteStatement prog = null;
2197 try {
2198 prog = new SQLiteStatement(db, "PRAGMA " + pragma);
2199 long val = prog.simpleQueryForLong();
2200 return val;
2201 } finally {
2202 if (prog != null) prog.close();
2203 }
2204 }
2205
2206 /**
2207 * returns list of full pathnames of all attached databases
2208 * including the main database
2209 * TODO: move this to {@link DatabaseUtils}
2210 */
2211 private static ArrayList<Pair<String, String>> getAttachedDbs(SQLiteDatabase dbObj) {
Vasu Norif3cf8a42010-03-23 11:41:44 -07002212 if (!dbObj.isOpen()) {
2213 return null;
2214 }
Vasu Noric3849202010-03-09 10:47:25 -08002215 ArrayList<Pair<String, String>> attachedDbs = new ArrayList<Pair<String, String>>();
2216 Cursor c = dbObj.rawQuery("pragma database_list;", null);
2217 while (c.moveToNext()) {
2218 attachedDbs.add(new Pair<String, String>(c.getString(1), c.getString(2)));
2219 }
2220 c.close();
2221 return attachedDbs;
2222 }
2223
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002224 /**
2225 * Native call to open the database.
2226 *
2227 * @param path The full path to the database
2228 */
2229 private native void dbopen(String path, int flags);
2230
2231 /**
Vasu Nori3ef94e22010-02-05 14:49:04 -08002232 * Native call to setup tracing of all sql statements
2233 *
2234 * @param path the full path to the database
2235 */
2236 private native void enableSqlTracing(String path);
2237
2238 /**
2239 * Native call to setup profiling of all sql statements.
2240 * currently, sqlite's profiling = printing of execution-time
2241 * (wall-clock time) of each of the sql statements, as they
2242 * are executed.
2243 *
2244 * @param path the full path to the database
2245 */
2246 private native void enableSqlProfiling(String path);
2247
2248 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002249 * Native call to execute a raw SQL statement. {@link #lock} must be held
2250 * when calling this method.
2251 *
2252 * @param sql The raw SQL string
2253 * @throws SQLException
2254 */
2255 /* package */ native void native_execSQL(String sql) throws SQLException;
2256
2257 /**
2258 * Native call to set the locale. {@link #lock} must be held when calling
2259 * this method.
2260 * @throws SQLException
2261 */
2262 /* package */ native void native_setLocale(String loc, int flags);
2263
2264 /**
2265 * Returns the row ID of the last row inserted into the database.
2266 *
2267 * @return the row ID of the last row inserted into the database.
2268 */
2269 /* package */ native long lastInsertRow();
2270
2271 /**
2272 * Returns the number of changes made in the last statement executed.
2273 *
2274 * @return the number of changes made in the last statement executed.
2275 */
2276 /* package */ native int lastChangeCount();
Vasu Noric3849202010-03-09 10:47:25 -08002277
2278 /**
2279 * return the SQLITE_DBSTATUS_LOOKASIDE_USED documented here
2280 * http://www.sqlite.org/c3ref/c_dbstatus_lookaside_used.html
2281 * @return int value of SQLITE_DBSTATUS_LOOKASIDE_USED
2282 */
2283 private native int native_getDbLookaside();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002284}