blob: 84426028cc0e96ba52ee75dc4e18e7332ff8b9ae [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 Fitzpatrick25880962010-02-22 15:17:49 -0800216 // which take 500ms+; shorter queries are sampled accordingly.
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -0800217 // Commit statements, which are typically slow, are logged
218 // together with the most recently executed SQL statement, for
219 // disambiguation.
Brad Fitzpatrick25880962010-02-22 15:17:49 -0800220 private static final int QUERY_LOG_TIME_IN_MILLIS = 500;
Dan Egnor12311952009-11-23 14:47:45 -0800221 private static final int QUERY_LOG_SQL_LENGTH = 64;
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -0800222 private static final String COMMIT_SQL = "COMMIT;";
Dan Egnor12311952009-11-23 14:47:45 -0800223 private final Random mRandom = new Random();
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -0800224 private String mLastSqlStatement = null;
Dan Egnor12311952009-11-23 14:47:45 -0800225
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800226 /** Used by native code, do not rename */
227 /* package */ int mNativeHandle = 0;
228
229 /** Used to make temp table names unique */
230 /* package */ int mTempTableSequence = 0;
231
232 /** The path for the database file */
233 private String mPath;
234
Brad Fitzpatrickd8330232010-02-19 10:59:01 -0800235 /** The anonymized path for the database file for logging purposes */
236 private String mPathForLogs = null; // lazily populated
237
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238 /** The flags passed to open/create */
239 private int mFlags;
240
241 /** The optional factory to use when creating new Cursors */
242 private CursorFactory mFactory;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700243
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800244 private WeakHashMap<SQLiteClosable, Object> mPrograms;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700245
Vasu Nori5a03f362009-10-20 15:16:35 -0700246 /**
247 * for each instance of this class, a cache is maintained to store
248 * the compiled query statement ids returned by sqlite database.
249 * key = sql statement with "?" for bind args
250 * value = {@link SQLiteCompiledSql}
251 * If an application opens the database and keeps it open during its entire life, then
252 * there will not be an overhead of compilation of sql statements by sqlite.
253 *
254 * why is this cache NOT static? because sqlite attaches compiledsql statements to the
255 * struct created when {@link SQLiteDatabase#openDatabase(String, CursorFactory, int)} is
256 * invoked.
257 *
258 * this cache has an upper limit of mMaxSqlCacheSize (settable by calling the method
259 * (@link setMaxCacheSize(int)}). its default is 0 - i.e., no caching by default because
260 * most of the apps don't use "?" syntax in their sql, caching is not useful for them.
261 */
Vasu Norie495d1f2010-01-06 16:34:19 -0800262 /* package */ Map<String, SQLiteCompiledSql> mCompiledQueries = Maps.newHashMap();
263 /**
264 * @hide
265 */
266 public static final int MAX_SQL_CACHE_SIZE = 250;
267 private int mMaxSqlCacheSize = MAX_SQL_CACHE_SIZE; // max cache size per Database instance
Vasu Norie9d92102010-01-20 15:07:26 -0800268 private int mCacheFullWarnings;
Vasu Nori49d02ac2010-03-05 21:49:30 -0800269 private static final int MAX_WARNINGS_ON_CACHESIZE_CONDITION = 1;
Vasu Nori5a03f362009-10-20 15:16:35 -0700270
271 /** maintain stats about number of cache hits and misses */
272 private int mNumCacheHits;
273 private int mNumCacheMisses;
274
275 /** the following 2 members maintain the time when a database is opened and closed */
276 private String mTimeOpened = null;
277 private String mTimeClosed = null;
278
Vasu Norid606b4b2010-02-24 12:54:20 -0800279 /** Used to find out where this object was created in case it never got closed. */
280 private Throwable mStackTrace = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800281
Dmitri Plotnikov90142c92009-09-15 10:52:17 -0700282 // System property that enables logging of slow queries. Specify the threshold in ms.
283 private static final String LOG_SLOW_QUERIES_PROPERTY = "db.log.slow_query_threshold";
284 private final int mSlowQueryThreshold;
285
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 /**
287 * @param closable
288 */
289 void addSQLiteClosable(SQLiteClosable closable) {
290 lock();
291 try {
292 mPrograms.put(closable, null);
293 } finally {
294 unlock();
295 }
296 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700297
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800298 void removeSQLiteClosable(SQLiteClosable closable) {
299 lock();
300 try {
301 mPrograms.remove(closable);
302 } finally {
303 unlock();
304 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700305 }
306
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800307 @Override
308 protected void onAllReferencesReleased() {
309 if (isOpen()) {
Vasu Nori5a03f362009-10-20 15:16:35 -0700310 if (SQLiteDebug.DEBUG_SQL_CACHE) {
311 mTimeClosed = getTime();
312 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800313 dbclose();
314 }
315 }
316
317 /**
318 * Attempts to release memory that SQLite holds but does not require to
319 * operate properly. Typically this memory will come from the page cache.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700320 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800321 * @return the number of bytes actually released
322 */
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700323 static public native int releaseMemory();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324
325 /**
326 * Control whether or not the SQLiteDatabase is made thread-safe by using locks
327 * around critical sections. This is pretty expensive, so if you know that your
328 * DB will only be used by a single thread then you should set this to false.
329 * The default is true.
330 * @param lockingEnabled set to true to enable locks, false otherwise
331 */
332 public void setLockingEnabled(boolean lockingEnabled) {
333 mLockingEnabled = lockingEnabled;
334 }
335
336 /**
337 * If set then the SQLiteDatabase is made thread-safe by using locks
338 * around critical sections
339 */
340 private boolean mLockingEnabled = true;
341
342 /* package */ void onCorruption() {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700343 Log.e(TAG, "Removing corrupt database: " + mPath);
344 EventLog.writeEvent(EVENT_DB_CORRUPT, mPath);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345 try {
346 // Close the database (if we can), which will cause subsequent operations to fail.
347 close();
348 } finally {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 // Delete the corrupt file. Don't re-create it now -- that would just confuse people
350 // -- but the next time someone tries to open it, they can set it up from scratch.
Vasu Norif3cf8a42010-03-23 11:41:44 -0700351 if (!mPath.equalsIgnoreCase(":memory")) {
352 // delete is only for non-memory database files
353 new File(mPath).delete();
354 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800355 }
356 }
357
358 /**
359 * Locks the database for exclusive access. The database lock must be held when
360 * touch the native sqlite3* object since it is single threaded and uses
361 * a polling lock contention algorithm. The lock is recursive, and may be acquired
362 * multiple times by the same thread. This is a no-op if mLockingEnabled is false.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700363 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 * @see #unlock()
365 */
366 /* package */ void lock() {
367 if (!mLockingEnabled) return;
368 mLock.lock();
369 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
370 if (mLock.getHoldCount() == 1) {
371 // Use elapsed real-time since the CPU may sleep when waiting for IO
372 mLockAcquiredWallTime = SystemClock.elapsedRealtime();
373 mLockAcquiredThreadTime = Debug.threadCpuTimeNanos();
374 }
375 }
376 }
377
378 /**
379 * Locks the database for exclusive access. The database lock must be held when
380 * touch the native sqlite3* object since it is single threaded and uses
381 * a polling lock contention algorithm. The lock is recursive, and may be acquired
382 * multiple times by the same thread.
383 *
384 * @see #unlockForced()
385 */
386 private void lockForced() {
387 mLock.lock();
388 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
389 if (mLock.getHoldCount() == 1) {
390 // Use elapsed real-time since the CPU may sleep when waiting for IO
391 mLockAcquiredWallTime = SystemClock.elapsedRealtime();
392 mLockAcquiredThreadTime = Debug.threadCpuTimeNanos();
393 }
394 }
395 }
396
397 /**
398 * Releases the database lock. This is a no-op if mLockingEnabled is false.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700399 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 * @see #unlock()
401 */
402 /* package */ void unlock() {
403 if (!mLockingEnabled) return;
404 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
405 if (mLock.getHoldCount() == 1) {
406 checkLockHoldTime();
407 }
408 }
409 mLock.unlock();
410 }
411
412 /**
413 * Releases the database lock.
414 *
415 * @see #unlockForced()
416 */
417 private void unlockForced() {
418 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
419 if (mLock.getHoldCount() == 1) {
420 checkLockHoldTime();
421 }
422 }
423 mLock.unlock();
424 }
425
426 private void checkLockHoldTime() {
427 // Use elapsed real-time since the CPU may sleep when waiting for IO
428 long elapsedTime = SystemClock.elapsedRealtime();
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700429 long lockedTime = elapsedTime - mLockAcquiredWallTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800430 if (lockedTime < LOCK_ACQUIRED_WARNING_TIME_IN_MS_ALWAYS_PRINT &&
431 !Log.isLoggable(TAG, Log.VERBOSE) &&
432 (elapsedTime - mLastLockMessageTime) < LOCK_WARNING_WINDOW_IN_MS) {
433 return;
434 }
435 if (lockedTime > LOCK_ACQUIRED_WARNING_TIME_IN_MS) {
436 int threadTime = (int)
437 ((Debug.threadCpuTimeNanos() - mLockAcquiredThreadTime) / 1000000);
438 if (threadTime > LOCK_ACQUIRED_WARNING_THREAD_TIME_IN_MS ||
439 lockedTime > LOCK_ACQUIRED_WARNING_TIME_IN_MS_ALWAYS_PRINT) {
440 mLastLockMessageTime = elapsedTime;
441 String msg = "lock held on " + mPath + " for " + lockedTime + "ms. Thread time was "
442 + threadTime + "ms";
443 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING_STACK_TRACE) {
444 Log.d(TAG, msg, new Exception());
445 } else {
446 Log.d(TAG, msg);
447 }
448 }
449 }
450 }
451
452 /**
453 * Begins a transaction. Transactions can be nested. When the outer transaction is ended all of
454 * the work done in that transaction and all of the nested transactions will be committed or
455 * rolled back. The changes will be rolled back if any transaction is ended without being
456 * marked as clean (by calling setTransactionSuccessful). Otherwise they will be committed.
457 *
458 * <p>Here is the standard idiom for transactions:
459 *
460 * <pre>
461 * db.beginTransaction();
462 * try {
463 * ...
464 * db.setTransactionSuccessful();
465 * } finally {
466 * db.endTransaction();
467 * }
468 * </pre>
469 */
470 public void beginTransaction() {
Fred Quintanac4516a72009-09-03 12:14:06 -0700471 beginTransactionWithListener(null /* transactionStatusCallback */);
472 }
473
474 /**
475 * Begins a transaction. Transactions can be nested. When the outer transaction is ended all of
476 * the work done in that transaction and all of the nested transactions will be committed or
477 * rolled back. The changes will be rolled back if any transaction is ended without being
478 * marked as clean (by calling setTransactionSuccessful). Otherwise they will be committed.
479 *
480 * <p>Here is the standard idiom for transactions:
481 *
482 * <pre>
483 * db.beginTransactionWithListener(listener);
484 * try {
485 * ...
486 * db.setTransactionSuccessful();
487 * } finally {
488 * db.endTransaction();
489 * }
490 * </pre>
491 * @param transactionListener listener that should be notified when the transaction begins,
492 * commits, or is rolled back, either explicitly or by a call to
493 * {@link #yieldIfContendedSafely}.
494 */
495 public void beginTransactionWithListener(SQLiteTransactionListener transactionListener) {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700496 if (!isOpen()) {
497 throw new IllegalStateException("database not open");
498 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800499 lockForced();
500 boolean ok = false;
501 try {
502 // If this thread already had the lock then get out
503 if (mLock.getHoldCount() > 1) {
504 if (mInnerTransactionIsSuccessful) {
505 String msg = "Cannot call beginTransaction between "
506 + "calling setTransactionSuccessful and endTransaction";
507 IllegalStateException e = new IllegalStateException(msg);
508 Log.e(TAG, "beginTransaction() failed", e);
509 throw e;
510 }
511 ok = true;
512 return;
513 }
514
515 // This thread didn't already have the lock, so begin a database
516 // transaction now.
517 execSQL("BEGIN EXCLUSIVE;");
Fred Quintanac4516a72009-09-03 12:14:06 -0700518 mTransactionListener = transactionListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800519 mTransactionIsSuccessful = true;
520 mInnerTransactionIsSuccessful = false;
Fred Quintanac4516a72009-09-03 12:14:06 -0700521 if (transactionListener != null) {
522 try {
523 transactionListener.onBegin();
524 } catch (RuntimeException e) {
525 execSQL("ROLLBACK;");
526 throw e;
527 }
528 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800529 ok = true;
530 } finally {
531 if (!ok) {
532 // beginTransaction is called before the try block so we must release the lock in
533 // the case of failure.
534 unlockForced();
535 }
536 }
537 }
538
539 /**
540 * End a transaction. See beginTransaction for notes about how to use this and when transactions
541 * are committed and rolled back.
542 */
543 public void endTransaction() {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700544 if (!isOpen()) {
545 throw new IllegalStateException("database not open");
546 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800547 if (!mLock.isHeldByCurrentThread()) {
548 throw new IllegalStateException("no transaction pending");
549 }
550 try {
551 if (mInnerTransactionIsSuccessful) {
552 mInnerTransactionIsSuccessful = false;
553 } else {
554 mTransactionIsSuccessful = false;
555 }
556 if (mLock.getHoldCount() != 1) {
557 return;
558 }
Fred Quintanac4516a72009-09-03 12:14:06 -0700559 RuntimeException savedException = null;
560 if (mTransactionListener != null) {
561 try {
562 if (mTransactionIsSuccessful) {
563 mTransactionListener.onCommit();
564 } else {
565 mTransactionListener.onRollback();
566 }
567 } catch (RuntimeException e) {
568 savedException = e;
569 mTransactionIsSuccessful = false;
570 }
571 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800572 if (mTransactionIsSuccessful) {
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -0800573 execSQL(COMMIT_SQL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800574 } else {
575 try {
576 execSQL("ROLLBACK;");
Fred Quintanac4516a72009-09-03 12:14:06 -0700577 if (savedException != null) {
578 throw savedException;
579 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800580 } catch (SQLException e) {
581 if (Config.LOGD) {
582 Log.d(TAG, "exception during rollback, maybe the DB previously "
583 + "performed an auto-rollback");
584 }
585 }
586 }
587 } finally {
Fred Quintanac4516a72009-09-03 12:14:06 -0700588 mTransactionListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800589 unlockForced();
590 if (Config.LOGV) {
591 Log.v(TAG, "unlocked " + Thread.currentThread()
592 + ", holdCount is " + mLock.getHoldCount());
593 }
594 }
595 }
596
597 /**
598 * Marks the current transaction as successful. Do not do any more database work between
599 * calling this and calling endTransaction. Do as little non-database work as possible in that
600 * situation too. If any errors are encountered between this and endTransaction the transaction
601 * will still be committed.
602 *
603 * @throws IllegalStateException if the current thread is not in a transaction or the
604 * transaction is already marked as successful.
605 */
606 public void setTransactionSuccessful() {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700607 if (!isOpen()) {
608 throw new IllegalStateException("database not open");
609 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800610 if (!mLock.isHeldByCurrentThread()) {
611 throw new IllegalStateException("no transaction pending");
612 }
613 if (mInnerTransactionIsSuccessful) {
614 throw new IllegalStateException(
615 "setTransactionSuccessful may only be called once per call to beginTransaction");
616 }
617 mInnerTransactionIsSuccessful = true;
618 }
619
620 /**
621 * return true if there is a transaction pending
622 */
623 public boolean inTransaction() {
624 return mLock.getHoldCount() > 0;
625 }
626
627 /**
628 * Checks if the database lock is held by this thread.
629 *
630 * @return true, if this thread is holding the database lock.
631 */
632 public boolean isDbLockedByCurrentThread() {
633 return mLock.isHeldByCurrentThread();
634 }
635
636 /**
637 * Checks if the database is locked by another thread. This is
638 * just an estimate, since this status can change at any time,
639 * including after the call is made but before the result has
640 * been acted upon.
641 *
642 * @return true, if the database is locked by another thread
643 */
644 public boolean isDbLockedByOtherThreads() {
645 return !mLock.isHeldByCurrentThread() && mLock.isLocked();
646 }
647
648 /**
649 * Temporarily end the transaction to let other threads run. The transaction is assumed to be
650 * successful so far. Do not call setTransactionSuccessful before calling this. When this
651 * returns a new transaction will have been created but not marked as successful.
652 * @return true if the transaction was yielded
653 * @deprecated if the db is locked more than once (becuase of nested transactions) then the lock
654 * will not be yielded. Use yieldIfContendedSafely instead.
655 */
Dianne Hackborn4a51c202009-08-21 15:14:02 -0700656 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800657 public boolean yieldIfContended() {
Fred Quintana5c7aede2009-08-27 21:41:27 -0700658 return yieldIfContendedHelper(false /* do not check yielding */,
659 -1 /* sleepAfterYieldDelay */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800660 }
661
662 /**
663 * Temporarily end the transaction to let other threads run. The transaction is assumed to be
664 * successful so far. Do not call setTransactionSuccessful before calling this. When this
665 * returns a new transaction will have been created but not marked as successful. This assumes
666 * that there are no nested transactions (beginTransaction has only been called once) and will
Fred Quintana5c7aede2009-08-27 21:41:27 -0700667 * throw an exception if that is not the case.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800668 * @return true if the transaction was yielded
669 */
670 public boolean yieldIfContendedSafely() {
Fred Quintana5c7aede2009-08-27 21:41:27 -0700671 return yieldIfContendedHelper(true /* check yielding */, -1 /* sleepAfterYieldDelay*/);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800672 }
673
Fred Quintana5c7aede2009-08-27 21:41:27 -0700674 /**
675 * Temporarily end the transaction to let other threads run. The transaction is assumed to be
676 * successful so far. Do not call setTransactionSuccessful before calling this. When this
677 * returns a new transaction will have been created but not marked as successful. This assumes
678 * that there are no nested transactions (beginTransaction has only been called once) and will
679 * throw an exception if that is not the case.
680 * @param sleepAfterYieldDelay if > 0, sleep this long before starting a new transaction if
681 * the lock was actually yielded. This will allow other background threads to make some
682 * more progress than they would if we started the transaction immediately.
683 * @return true if the transaction was yielded
684 */
685 public boolean yieldIfContendedSafely(long sleepAfterYieldDelay) {
686 return yieldIfContendedHelper(true /* check yielding */, sleepAfterYieldDelay);
687 }
688
689 private boolean yieldIfContendedHelper(boolean checkFullyYielded, long sleepAfterYieldDelay) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800690 if (mLock.getQueueLength() == 0) {
691 // Reset the lock acquire time since we know that the thread was willing to yield
692 // the lock at this time.
693 mLockAcquiredWallTime = SystemClock.elapsedRealtime();
694 mLockAcquiredThreadTime = Debug.threadCpuTimeNanos();
695 return false;
696 }
697 setTransactionSuccessful();
Fred Quintanac4516a72009-09-03 12:14:06 -0700698 SQLiteTransactionListener transactionListener = mTransactionListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800699 endTransaction();
700 if (checkFullyYielded) {
701 if (this.isDbLockedByCurrentThread()) {
702 throw new IllegalStateException(
703 "Db locked more than once. yielfIfContended cannot yield");
704 }
705 }
Fred Quintana5c7aede2009-08-27 21:41:27 -0700706 if (sleepAfterYieldDelay > 0) {
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700707 // Sleep for up to sleepAfterYieldDelay milliseconds, waking up periodically to
708 // check if anyone is using the database. If the database is not contended,
709 // retake the lock and return.
710 long remainingDelay = sleepAfterYieldDelay;
711 while (remainingDelay > 0) {
712 try {
713 Thread.sleep(remainingDelay < SLEEP_AFTER_YIELD_QUANTUM ?
714 remainingDelay : SLEEP_AFTER_YIELD_QUANTUM);
715 } catch (InterruptedException e) {
716 Thread.interrupted();
717 }
718 remainingDelay -= SLEEP_AFTER_YIELD_QUANTUM;
719 if (mLock.getQueueLength() == 0) {
720 break;
721 }
Fred Quintana5c7aede2009-08-27 21:41:27 -0700722 }
723 }
Fred Quintanac4516a72009-09-03 12:14:06 -0700724 beginTransactionWithListener(transactionListener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800725 return true;
726 }
727
728 /** Maps table names to info about what to which _sync_time column to set
729 * to NULL on an update. This is used to support syncing. */
730 private final Map<String, SyncUpdateInfo> mSyncUpdateInfo =
731 new HashMap<String, SyncUpdateInfo>();
732
733 public Map<String, String> getSyncedTables() {
734 synchronized(mSyncUpdateInfo) {
735 HashMap<String, String> tables = new HashMap<String, String>();
736 for (String table : mSyncUpdateInfo.keySet()) {
737 SyncUpdateInfo info = mSyncUpdateInfo.get(table);
738 if (info.deletedTable != null) {
739 tables.put(table, info.deletedTable);
740 }
741 }
742 return tables;
743 }
744 }
745
746 /**
747 * Internal class used to keep track what needs to be marked as changed
748 * when an update occurs. This is used for syncing, so the sync engine
749 * knows what data has been updated locally.
750 */
751 static private class SyncUpdateInfo {
752 /**
753 * Creates the SyncUpdateInfo class.
754 *
755 * @param masterTable The table to set _sync_time to NULL in
756 * @param deletedTable The deleted table that corresponds to the
757 * master table
758 * @param foreignKey The key that refers to the primary key in table
759 */
760 SyncUpdateInfo(String masterTable, String deletedTable,
761 String foreignKey) {
762 this.masterTable = masterTable;
763 this.deletedTable = deletedTable;
764 this.foreignKey = foreignKey;
765 }
766
767 /** The table containing the _sync_time column */
768 String masterTable;
769
770 /** The deleted table that corresponds to the master table */
771 String deletedTable;
772
773 /** The key in the local table the row in table. It may be _id, if table
774 * is the local table. */
775 String foreignKey;
776 }
777
778 /**
779 * Used to allow returning sub-classes of {@link Cursor} when calling query.
780 */
781 public interface CursorFactory {
782 /**
783 * See
784 * {@link SQLiteCursor#SQLiteCursor(SQLiteDatabase, SQLiteCursorDriver,
785 * String, SQLiteQuery)}.
786 */
787 public Cursor newCursor(SQLiteDatabase db,
788 SQLiteCursorDriver masterQuery, String editTable,
789 SQLiteQuery query);
790 }
791
792 /**
793 * Open the database according to the flags {@link #OPEN_READWRITE}
794 * {@link #OPEN_READONLY} {@link #CREATE_IF_NECESSARY} and/or {@link #NO_LOCALIZED_COLLATORS}.
795 *
796 * <p>Sets the locale of the database to the the system's current locale.
797 * Call {@link #setLocale} if you would like something else.</p>
798 *
799 * @param path to database file to open and/or create
800 * @param factory an optional factory class that is called to instantiate a
801 * cursor when query is called, or null for default
802 * @param flags to control database access mode
803 * @return the newly opened database
804 * @throws SQLiteException if the database cannot be opened
805 */
806 public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags) {
Vasu Noric3849202010-03-09 10:47:25 -0800807 SQLiteDatabase sqliteDatabase = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800808 try {
809 // Open the database.
Vasu Noric3849202010-03-09 10:47:25 -0800810 sqliteDatabase = new SQLiteDatabase(path, factory, flags);
Vasu Nori3ef94e22010-02-05 14:49:04 -0800811 if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
812 sqliteDatabase.enableSqlTracing(path);
813 }
814 if (SQLiteDebug.DEBUG_SQL_TIME) {
815 sqliteDatabase.enableSqlProfiling(path);
816 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800817 } catch (SQLiteDatabaseCorruptException e) {
818 // Try to recover from this, if we can.
819 // TODO: should we do this for other open failures?
820 Log.e(TAG, "Deleting and re-creating corrupt database " + path, e);
Jeff Hamilton082c2af2009-09-29 11:49:51 -0700821 EventLog.writeEvent(EVENT_DB_CORRUPT, path);
Vasu Norif3cf8a42010-03-23 11:41:44 -0700822 if (!path.equalsIgnoreCase(":memory")) {
823 // delete is only for non-memory database files
824 new File(path).delete();
825 }
Vasu Noric3849202010-03-09 10:47:25 -0800826 sqliteDatabase = new SQLiteDatabase(path, factory, flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800827 }
Vasu Noric3849202010-03-09 10:47:25 -0800828 ActiveDatabases.getInstance().mActiveDatabases.add(
829 new WeakReference<SQLiteDatabase>(sqliteDatabase));
830 return sqliteDatabase;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800831 }
832
833 /**
834 * Equivalent to openDatabase(file.getPath(), factory, CREATE_IF_NECESSARY).
835 */
836 public static SQLiteDatabase openOrCreateDatabase(File file, CursorFactory factory) {
837 return openOrCreateDatabase(file.getPath(), factory);
838 }
839
840 /**
841 * Equivalent to openDatabase(path, factory, CREATE_IF_NECESSARY).
842 */
843 public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory) {
844 return openDatabase(path, factory, CREATE_IF_NECESSARY);
845 }
846
847 /**
848 * Create a memory backed SQLite database. Its contents will be destroyed
849 * when the database is closed.
850 *
851 * <p>Sets the locale of the database to the the system's current locale.
852 * Call {@link #setLocale} if you would like something else.</p>
853 *
854 * @param factory an optional factory class that is called to instantiate a
855 * cursor when query is called
856 * @return a SQLiteDatabase object, or null if the database can't be created
857 */
858 public static SQLiteDatabase create(CursorFactory factory) {
859 // This is a magic string with special meaning for SQLite.
860 return openDatabase(":memory:", factory, CREATE_IF_NECESSARY);
861 }
862
863 /**
864 * Close the database.
865 */
866 public void close() {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700867 if (!isOpen()) {
868 return; // already closed
869 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800870 lock();
871 try {
872 closeClosable();
Vasu Norif6373e92010-03-16 10:21:00 -0700873 // close this database instance - regardless of its reference count value
874 onAllReferencesReleased();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800875 } finally {
876 unlock();
877 }
878 }
879
880 private void closeClosable() {
Vasu Norie495d1f2010-01-06 16:34:19 -0800881 /* deallocate all compiled sql statement objects from mCompiledQueries cache.
882 * this should be done before de-referencing all {@link SQLiteClosable} objects
883 * from this database object because calling
884 * {@link SQLiteClosable#onAllReferencesReleasedFromContainer()} could cause the database
885 * to be closed. sqlite doesn't let a database close if there are
886 * any unfinalized statements - such as the compiled-sql objects in mCompiledQueries.
887 */
888 deallocCachedSqlStatements();
889
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800890 Iterator<Map.Entry<SQLiteClosable, Object>> iter = mPrograms.entrySet().iterator();
891 while (iter.hasNext()) {
892 Map.Entry<SQLiteClosable, Object> entry = iter.next();
893 SQLiteClosable program = entry.getKey();
894 if (program != null) {
895 program.onAllReferencesReleasedFromContainer();
896 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700897 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800898 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700899
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800900 /**
901 * Native call to close the database.
902 */
903 private native void dbclose();
904
905 /**
906 * Gets the database version.
907 *
908 * @return the database version
909 */
910 public int getVersion() {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700911 if (!isOpen()) {
912 throw new IllegalStateException("database not open");
913 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800914 SQLiteStatement prog = null;
915 lock();
916 try {
917 prog = new SQLiteStatement(this, "PRAGMA user_version;");
918 long version = prog.simpleQueryForLong();
919 return (int) version;
920 } finally {
921 if (prog != null) prog.close();
922 unlock();
923 }
924 }
925
926 /**
927 * Sets the database version.
928 *
929 * @param version the new database version
930 */
931 public void setVersion(int version) {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700932 if (!isOpen()) {
933 throw new IllegalStateException("database not open");
934 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800935 execSQL("PRAGMA user_version = " + version);
936 }
937
938 /**
939 * Returns the maximum size the database may grow to.
940 *
941 * @return the new maximum database size
942 */
943 public long getMaximumSize() {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700944 if (!isOpen()) {
945 throw new IllegalStateException("database not open");
946 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800947 SQLiteStatement prog = null;
948 lock();
949 try {
950 prog = new SQLiteStatement(this,
951 "PRAGMA max_page_count;");
952 long pageCount = prog.simpleQueryForLong();
953 return pageCount * getPageSize();
954 } finally {
955 if (prog != null) prog.close();
956 unlock();
957 }
958 }
959
960 /**
961 * Sets the maximum size the database will grow to. The maximum size cannot
962 * be set below the current size.
963 *
964 * @param numBytes the maximum database size, in bytes
965 * @return the new maximum database size
966 */
967 public long setMaximumSize(long numBytes) {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700968 if (!isOpen()) {
969 throw new IllegalStateException("database not open");
970 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800971 SQLiteStatement prog = null;
972 lock();
973 try {
974 long pageSize = getPageSize();
975 long numPages = numBytes / pageSize;
976 // If numBytes isn't a multiple of pageSize, bump up a page
977 if ((numBytes % pageSize) != 0) {
978 numPages++;
979 }
980 prog = new SQLiteStatement(this,
981 "PRAGMA max_page_count = " + numPages);
982 long newPageCount = prog.simpleQueryForLong();
983 return newPageCount * pageSize;
984 } finally {
985 if (prog != null) prog.close();
986 unlock();
987 }
988 }
989
990 /**
991 * Returns the current database page size, in bytes.
992 *
993 * @return the database page size, in bytes
994 */
995 public long getPageSize() {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700996 if (!isOpen()) {
997 throw new IllegalStateException("database not open");
998 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800999 SQLiteStatement prog = null;
1000 lock();
1001 try {
1002 prog = new SQLiteStatement(this,
1003 "PRAGMA page_size;");
1004 long size = prog.simpleQueryForLong();
1005 return size;
1006 } finally {
1007 if (prog != null) prog.close();
1008 unlock();
1009 }
1010 }
1011
1012 /**
1013 * Sets the database page size. The page size must be a power of two. This
1014 * method does not work if any data has been written to the database file,
1015 * and must be called right after the database has been created.
1016 *
1017 * @param numBytes the database page size, in bytes
1018 */
1019 public void setPageSize(long numBytes) {
Vasu Norif3cf8a42010-03-23 11:41:44 -07001020 if (!isOpen()) {
1021 throw new IllegalStateException("database not open");
1022 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001023 execSQL("PRAGMA page_size = " + numBytes);
1024 }
1025
1026 /**
1027 * Mark this table as syncable. When an update occurs in this table the
1028 * _sync_dirty field will be set to ensure proper syncing operation.
1029 *
1030 * @param table the table to mark as syncable
1031 * @param deletedTable The deleted table that corresponds to the
1032 * syncable table
1033 */
1034 public void markTableSyncable(String table, String deletedTable) {
1035 markTableSyncable(table, "_id", table, deletedTable);
1036 }
1037
1038 /**
1039 * Mark this table as syncable, with the _sync_dirty residing in another
1040 * table. When an update occurs in this table the _sync_dirty field of the
1041 * row in updateTable with the _id in foreignKey will be set to
1042 * ensure proper syncing operation.
1043 *
1044 * @param table an update on this table will trigger a sync time removal
1045 * @param foreignKey this is the column in table whose value is an _id in
1046 * updateTable
1047 * @param updateTable this is the table that will have its _sync_dirty
1048 */
1049 public void markTableSyncable(String table, String foreignKey,
1050 String updateTable) {
1051 markTableSyncable(table, foreignKey, updateTable, null);
1052 }
1053
1054 /**
1055 * Mark this table as syncable, with the _sync_dirty residing in another
1056 * table. When an update occurs in this table the _sync_dirty field of the
1057 * row in updateTable with the _id in foreignKey will be set to
1058 * ensure proper syncing operation.
1059 *
1060 * @param table an update on this table will trigger a sync time removal
1061 * @param foreignKey this is the column in table whose value is an _id in
1062 * updateTable
1063 * @param updateTable this is the table that will have its _sync_dirty
1064 * @param deletedTable The deleted table that corresponds to the
1065 * updateTable
1066 */
1067 private void markTableSyncable(String table, String foreignKey,
1068 String updateTable, String deletedTable) {
1069 lock();
1070 try {
1071 native_execSQL("SELECT _sync_dirty FROM " + updateTable
1072 + " LIMIT 0");
1073 native_execSQL("SELECT " + foreignKey + " FROM " + table
1074 + " LIMIT 0");
1075 } finally {
1076 unlock();
1077 }
1078
1079 SyncUpdateInfo info = new SyncUpdateInfo(updateTable, deletedTable,
1080 foreignKey);
1081 synchronized (mSyncUpdateInfo) {
1082 mSyncUpdateInfo.put(table, info);
1083 }
1084 }
1085
1086 /**
1087 * Call for each row that is updated in a cursor.
1088 *
1089 * @param table the table the row is in
1090 * @param rowId the row ID of the updated row
1091 */
1092 /* package */ void rowUpdated(String table, long rowId) {
1093 SyncUpdateInfo info;
1094 synchronized (mSyncUpdateInfo) {
1095 info = mSyncUpdateInfo.get(table);
1096 }
1097 if (info != null) {
1098 execSQL("UPDATE " + info.masterTable
1099 + " SET _sync_dirty=1 WHERE _id=(SELECT " + info.foreignKey
1100 + " FROM " + table + " WHERE _id=" + rowId + ")");
1101 }
1102 }
1103
1104 /**
1105 * Finds the name of the first table, which is editable.
1106 *
1107 * @param tables a list of tables
1108 * @return the first table listed
1109 */
1110 public static String findEditTable(String tables) {
1111 if (!TextUtils.isEmpty(tables)) {
1112 // find the first word terminated by either a space or a comma
1113 int spacepos = tables.indexOf(' ');
1114 int commapos = tables.indexOf(',');
1115
1116 if (spacepos > 0 && (spacepos < commapos || commapos < 0)) {
1117 return tables.substring(0, spacepos);
1118 } else if (commapos > 0 && (commapos < spacepos || spacepos < 0) ) {
1119 return tables.substring(0, commapos);
1120 }
1121 return tables;
1122 } else {
1123 throw new IllegalStateException("Invalid tables");
1124 }
1125 }
1126
1127 /**
1128 * Compiles an SQL statement into a reusable pre-compiled statement object.
1129 * The parameters are identical to {@link #execSQL(String)}. You may put ?s in the
1130 * statement and fill in those values with {@link SQLiteProgram#bindString}
1131 * and {@link SQLiteProgram#bindLong} each time you want to run the
1132 * statement. Statements may not return result sets larger than 1x1.
1133 *
1134 * @param sql The raw SQL statement, may contain ? for unknown values to be
1135 * bound later.
1136 * @return a pre-compiled statement object.
1137 */
1138 public SQLiteStatement compileStatement(String sql) throws SQLException {
Vasu Norif3cf8a42010-03-23 11:41:44 -07001139 if (!isOpen()) {
1140 throw new IllegalStateException("database not open");
1141 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001142 lock();
1143 try {
1144 return new SQLiteStatement(this, sql);
1145 } finally {
1146 unlock();
1147 }
1148 }
1149
1150 /**
1151 * Query the given URL, returning a {@link Cursor} over the result set.
1152 *
1153 * @param distinct true if you want each row to be unique, false otherwise.
1154 * @param table The table name to compile the query against.
1155 * @param columns A list of which columns to return. Passing null will
1156 * return all columns, which is discouraged to prevent reading
1157 * data from storage that isn't going to be used.
1158 * @param selection A filter declaring which rows to return, formatted as an
1159 * SQL WHERE clause (excluding the WHERE itself). Passing null
1160 * will return all rows for the given table.
1161 * @param selectionArgs You may include ?s in selection, which will be
1162 * replaced by the values from selectionArgs, in order that they
1163 * appear in the selection. The values will be bound as Strings.
1164 * @param groupBy A filter declaring how to group rows, formatted as an SQL
1165 * GROUP BY clause (excluding the GROUP BY itself). Passing null
1166 * will cause the rows to not be grouped.
1167 * @param having A filter declare which row groups to include in the cursor,
1168 * if row grouping is being used, formatted as an SQL HAVING
1169 * clause (excluding the HAVING itself). Passing null will cause
1170 * all row groups to be included, and is required when row
1171 * grouping is not being used.
1172 * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
1173 * (excluding the ORDER BY itself). Passing null will use the
1174 * default sort order, which may be unordered.
1175 * @param limit Limits the number of rows returned by the query,
1176 * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
1177 * @return A Cursor object, which is positioned before the first entry
1178 * @see Cursor
1179 */
1180 public Cursor query(boolean distinct, String table, String[] columns,
1181 String selection, String[] selectionArgs, String groupBy,
1182 String having, String orderBy, String limit) {
1183 return queryWithFactory(null, distinct, table, columns, selection, selectionArgs,
1184 groupBy, having, orderBy, limit);
1185 }
1186
1187 /**
1188 * Query the given URL, returning a {@link Cursor} over the result set.
1189 *
1190 * @param cursorFactory the cursor factory to use, or null for the default factory
1191 * @param distinct true if you want each row to be unique, false otherwise.
1192 * @param table The table name to compile the query against.
1193 * @param columns A list of which columns to return. Passing null will
1194 * return all columns, which is discouraged to prevent reading
1195 * data from storage that isn't going to be used.
1196 * @param selection A filter declaring which rows to return, formatted as an
1197 * SQL WHERE clause (excluding the WHERE itself). Passing null
1198 * will return all rows for the given table.
1199 * @param selectionArgs You may include ?s in selection, which will be
1200 * replaced by the values from selectionArgs, in order that they
1201 * appear in the selection. The values will be bound as Strings.
1202 * @param groupBy A filter declaring how to group rows, formatted as an SQL
1203 * GROUP BY clause (excluding the GROUP BY itself). Passing null
1204 * will cause the rows to not be grouped.
1205 * @param having A filter declare which row groups to include in the cursor,
1206 * if row grouping is being used, formatted as an SQL HAVING
1207 * clause (excluding the HAVING itself). Passing null will cause
1208 * all row groups to be included, and is required when row
1209 * grouping is not being used.
1210 * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
1211 * (excluding the ORDER BY itself). Passing null will use the
1212 * default sort order, which may be unordered.
1213 * @param limit Limits the number of rows returned by the query,
1214 * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
1215 * @return A Cursor object, which is positioned before the first entry
1216 * @see Cursor
1217 */
1218 public Cursor queryWithFactory(CursorFactory cursorFactory,
1219 boolean distinct, String table, String[] columns,
1220 String selection, String[] selectionArgs, String groupBy,
1221 String having, String orderBy, String limit) {
Vasu Norif3cf8a42010-03-23 11:41:44 -07001222 if (!isOpen()) {
1223 throw new IllegalStateException("database not open");
1224 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001225 String sql = SQLiteQueryBuilder.buildQueryString(
1226 distinct, table, columns, selection, groupBy, having, orderBy, limit);
1227
1228 return rawQueryWithFactory(
1229 cursorFactory, sql, selectionArgs, findEditTable(table));
1230 }
1231
1232 /**
1233 * Query the given table, returning a {@link Cursor} over the result set.
1234 *
1235 * @param table The table name to compile the query against.
1236 * @param columns A list of which columns to return. Passing null will
1237 * return all columns, which is discouraged to prevent reading
1238 * data from storage that isn't going to be used.
1239 * @param selection A filter declaring which rows to return, formatted as an
1240 * SQL WHERE clause (excluding the WHERE itself). Passing null
1241 * will return all rows for the given table.
1242 * @param selectionArgs You may include ?s in selection, which will be
1243 * replaced by the values from selectionArgs, in order that they
1244 * appear in the selection. The values will be bound as Strings.
1245 * @param groupBy A filter declaring how to group rows, formatted as an SQL
1246 * GROUP BY clause (excluding the GROUP BY itself). Passing null
1247 * will cause the rows to not be grouped.
1248 * @param having A filter declare which row groups to include in the cursor,
1249 * if row grouping is being used, formatted as an SQL HAVING
1250 * clause (excluding the HAVING itself). Passing null will cause
1251 * all row groups to be included, and is required when row
1252 * grouping is not being used.
1253 * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
1254 * (excluding the ORDER BY itself). Passing null will use the
1255 * default sort order, which may be unordered.
1256 * @return A {@link Cursor} object, which is positioned before the first entry
1257 * @see Cursor
1258 */
1259 public Cursor query(String table, String[] columns, String selection,
1260 String[] selectionArgs, String groupBy, String having,
1261 String orderBy) {
1262
1263 return query(false, table, columns, selection, selectionArgs, groupBy,
1264 having, orderBy, null /* limit */);
1265 }
1266
1267 /**
1268 * Query the given table, returning a {@link Cursor} over the result set.
1269 *
1270 * @param table The table name to compile the query against.
1271 * @param columns A list of which columns to return. Passing null will
1272 * return all columns, which is discouraged to prevent reading
1273 * data from storage that isn't going to be used.
1274 * @param selection A filter declaring which rows to return, formatted as an
1275 * SQL WHERE clause (excluding the WHERE itself). Passing null
1276 * will return all rows for the given table.
1277 * @param selectionArgs You may include ?s in selection, which will be
1278 * replaced by the values from selectionArgs, in order that they
1279 * appear in the selection. The values will be bound as Strings.
1280 * @param groupBy A filter declaring how to group rows, formatted as an SQL
1281 * GROUP BY clause (excluding the GROUP BY itself). Passing null
1282 * will cause the rows to not be grouped.
1283 * @param having A filter declare which row groups to include in the cursor,
1284 * if row grouping is being used, formatted as an SQL HAVING
1285 * clause (excluding the HAVING itself). Passing null will cause
1286 * all row groups to be included, and is required when row
1287 * grouping is not being used.
1288 * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
1289 * (excluding the ORDER BY itself). Passing null will use the
1290 * default sort order, which may be unordered.
1291 * @param limit Limits the number of rows returned by the query,
1292 * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
1293 * @return A {@link Cursor} object, which is positioned before the first entry
1294 * @see Cursor
1295 */
1296 public Cursor query(String table, String[] columns, String selection,
1297 String[] selectionArgs, String groupBy, String having,
1298 String orderBy, String limit) {
1299
1300 return query(false, table, columns, selection, selectionArgs, groupBy,
1301 having, orderBy, limit);
1302 }
1303
1304 /**
1305 * Runs the provided SQL and returns a {@link Cursor} over the result set.
1306 *
1307 * @param sql the SQL query. The SQL string must not be ; terminated
1308 * @param selectionArgs You may include ?s in where clause in the query,
1309 * which will be replaced by the values from selectionArgs. The
1310 * values will be bound as Strings.
1311 * @return A {@link Cursor} object, which is positioned before the first entry
1312 */
1313 public Cursor rawQuery(String sql, String[] selectionArgs) {
1314 return rawQueryWithFactory(null, sql, selectionArgs, null);
1315 }
1316
1317 /**
1318 * Runs the provided SQL and returns a cursor over the result set.
1319 *
1320 * @param cursorFactory the cursor factory to use, or null for the default factory
1321 * @param sql the SQL query. The SQL string must not be ; terminated
1322 * @param selectionArgs You may include ?s in where clause in the query,
1323 * which will be replaced by the values from selectionArgs. The
1324 * values will be bound as Strings.
1325 * @param editTable the name of the first table, which is editable
1326 * @return A {@link Cursor} object, which is positioned before the first entry
1327 */
1328 public Cursor rawQueryWithFactory(
1329 CursorFactory cursorFactory, String sql, String[] selectionArgs,
1330 String editTable) {
Vasu Norif3cf8a42010-03-23 11:41:44 -07001331 if (!isOpen()) {
1332 throw new IllegalStateException("database not open");
1333 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001334 long timeStart = 0;
1335
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001336 if (Config.LOGV || mSlowQueryThreshold != -1) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001337 timeStart = System.currentTimeMillis();
1338 }
1339
1340 SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable);
1341
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001342 Cursor cursor = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001343 try {
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001344 cursor = driver.query(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001345 cursorFactory != null ? cursorFactory : mFactory,
1346 selectionArgs);
1347 } finally {
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001348 if (Config.LOGV || mSlowQueryThreshold != -1) {
1349
1350 // Force query execution
1351 if (cursor != null) {
1352 cursor.moveToFirst();
1353 cursor.moveToPosition(-1);
1354 }
1355
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001356 long duration = System.currentTimeMillis() - timeStart;
1357
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001358 if (Config.LOGV || duration >= mSlowQueryThreshold) {
1359 Log.v(SQLiteCursor.TAG,
1360 "query (" + duration + " ms): " + driver.toString() + ", args are "
1361 + (selectionArgs != null
1362 ? TextUtils.join(",", selectionArgs)
1363 : "<null>"));
1364 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001365 }
1366 }
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001367 return cursor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001368 }
1369
1370 /**
1371 * Runs the provided SQL and returns a cursor over the result set.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001372 * The cursor will read an initial set of rows and the return to the caller.
1373 * It will continue to read in batches and send data changed notifications
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001374 * when the later batches are ready.
1375 * @param sql the SQL query. The SQL string must not be ; terminated
1376 * @param selectionArgs You may include ?s in where clause in the query,
1377 * which will be replaced by the values from selectionArgs. The
1378 * values will be bound as Strings.
1379 * @param initialRead set the initial count of items to read from the cursor
1380 * @param maxRead set the count of items to read on each iteration after the first
1381 * @return A {@link Cursor} object, which is positioned before the first entry
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001382 *
Andy Stadlerf8a7cea2009-04-10 16:24:47 -07001383 * This work is incomplete and not fully tested or reviewed, so currently
1384 * hidden.
1385 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001386 */
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001387 public Cursor rawQuery(String sql, String[] selectionArgs,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001388 int initialRead, int maxRead) {
1389 SQLiteCursor c = (SQLiteCursor)rawQueryWithFactory(
1390 null, sql, selectionArgs, null);
1391 c.setLoadStyle(initialRead, maxRead);
1392 return c;
1393 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001394
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001395 /**
1396 * Convenience method for inserting a row into the database.
1397 *
1398 * @param table the table to insert the row into
1399 * @param nullColumnHack SQL doesn't allow inserting a completely empty row,
1400 * so if initialValues is empty this column will explicitly be
1401 * assigned a NULL value
1402 * @param values this map contains the initial column values for the
1403 * row. The keys should be the column names and the values the
1404 * column values
1405 * @return the row ID of the newly inserted row, or -1 if an error occurred
1406 */
1407 public long insert(String table, String nullColumnHack, ContentValues values) {
1408 try {
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001409 return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001410 } catch (SQLException e) {
1411 Log.e(TAG, "Error inserting " + values, e);
1412 return -1;
1413 }
1414 }
1415
1416 /**
1417 * Convenience method for inserting a row into the database.
1418 *
1419 * @param table the table to insert the row into
1420 * @param nullColumnHack SQL doesn't allow inserting a completely empty row,
1421 * so if initialValues is empty this column will explicitly be
1422 * assigned a NULL value
1423 * @param values this map contains the initial column values for the
1424 * row. The keys should be the column names and the values the
1425 * column values
1426 * @throws SQLException
1427 * @return the row ID of the newly inserted row, or -1 if an error occurred
1428 */
1429 public long insertOrThrow(String table, String nullColumnHack, ContentValues values)
1430 throws SQLException {
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001431 return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001432 }
1433
1434 /**
1435 * Convenience method for replacing a row in the database.
1436 *
1437 * @param table the table in which to replace the row
1438 * @param nullColumnHack SQL doesn't allow inserting a completely empty row,
1439 * so if initialValues is empty this row will explicitly be
1440 * assigned a NULL value
1441 * @param initialValues this map contains the initial column values for
1442 * the row. The key
1443 * @return the row ID of the newly inserted row, or -1 if an error occurred
1444 */
1445 public long replace(String table, String nullColumnHack, ContentValues initialValues) {
1446 try {
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001447 return insertWithOnConflict(table, nullColumnHack, initialValues,
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001448 CONFLICT_REPLACE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001449 } catch (SQLException e) {
1450 Log.e(TAG, "Error inserting " + initialValues, e);
1451 return -1;
1452 }
1453 }
1454
1455 /**
1456 * Convenience method for replacing a row in the database.
1457 *
1458 * @param table the table in which to replace the row
1459 * @param nullColumnHack SQL doesn't allow inserting a completely empty row,
1460 * so if initialValues is empty this row will explicitly be
1461 * assigned a NULL value
1462 * @param initialValues this map contains the initial column values for
1463 * the row. The key
1464 * @throws SQLException
1465 * @return the row ID of the newly inserted row, or -1 if an error occurred
1466 */
1467 public long replaceOrThrow(String table, String nullColumnHack,
1468 ContentValues initialValues) throws SQLException {
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001469 return insertWithOnConflict(table, nullColumnHack, initialValues,
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001470 CONFLICT_REPLACE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001471 }
1472
1473 /**
1474 * General method for inserting a row into the database.
1475 *
1476 * @param table the table to insert the row into
1477 * @param nullColumnHack SQL doesn't allow inserting a completely empty row,
1478 * so if initialValues is empty this column will explicitly be
1479 * assigned a NULL value
1480 * @param initialValues this map contains the initial column values for the
1481 * row. The keys should be the column names and the values the
1482 * column values
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001483 * @param conflictAlgorithm for insert conflict resolver
Vasu Nori6eb7c452010-01-27 14:31:24 -08001484 * @return the row ID of the newly inserted row
1485 * OR the primary key of the existing row if the input param 'conflictAlgorithm' =
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001486 * {@link #CONFLICT_IGNORE}
Vasu Nori6eb7c452010-01-27 14:31:24 -08001487 * OR -1 if any error
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001488 */
1489 public long insertWithOnConflict(String table, String nullColumnHack,
Vasu Nori6eb7c452010-01-27 14:31:24 -08001490 ContentValues initialValues, int conflictAlgorithm) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001491 if (!isOpen()) {
1492 throw new IllegalStateException("database not open");
1493 }
1494
1495 // Measurements show most sql lengths <= 152
1496 StringBuilder sql = new StringBuilder(152);
1497 sql.append("INSERT");
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001498 sql.append(CONFLICT_VALUES[conflictAlgorithm]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001499 sql.append(" INTO ");
1500 sql.append(table);
1501 // Measurements show most values lengths < 40
1502 StringBuilder values = new StringBuilder(40);
1503
1504 Set<Map.Entry<String, Object>> entrySet = null;
1505 if (initialValues != null && initialValues.size() > 0) {
1506 entrySet = initialValues.valueSet();
1507 Iterator<Map.Entry<String, Object>> entriesIter = entrySet.iterator();
1508 sql.append('(');
1509
1510 boolean needSeparator = false;
1511 while (entriesIter.hasNext()) {
1512 if (needSeparator) {
1513 sql.append(", ");
1514 values.append(", ");
1515 }
1516 needSeparator = true;
1517 Map.Entry<String, Object> entry = entriesIter.next();
1518 sql.append(entry.getKey());
1519 values.append('?');
1520 }
1521
1522 sql.append(')');
1523 } else {
1524 sql.append("(" + nullColumnHack + ") ");
1525 values.append("NULL");
1526 }
1527
1528 sql.append(" VALUES(");
1529 sql.append(values);
1530 sql.append(");");
1531
1532 lock();
1533 SQLiteStatement statement = null;
1534 try {
1535 statement = compileStatement(sql.toString());
1536
1537 // Bind the values
1538 if (entrySet != null) {
1539 int size = entrySet.size();
1540 Iterator<Map.Entry<String, Object>> entriesIter = entrySet.iterator();
1541 for (int i = 0; i < size; i++) {
1542 Map.Entry<String, Object> entry = entriesIter.next();
1543 DatabaseUtils.bindObjectToProgram(statement, i + 1, entry.getValue());
1544 }
1545 }
1546
1547 // Run the program and then cleanup
1548 statement.execute();
1549
1550 long insertedRowId = lastInsertRow();
1551 if (insertedRowId == -1) {
1552 Log.e(TAG, "Error inserting " + initialValues + " using " + sql);
1553 } else {
1554 if (Config.LOGD && Log.isLoggable(TAG, Log.VERBOSE)) {
1555 Log.v(TAG, "Inserting row " + insertedRowId + " from "
1556 + initialValues + " using " + sql);
1557 }
1558 }
1559 return insertedRowId;
1560 } catch (SQLiteDatabaseCorruptException e) {
1561 onCorruption();
1562 throw e;
1563 } finally {
1564 if (statement != null) {
1565 statement.close();
1566 }
1567 unlock();
1568 }
1569 }
1570
1571 /**
1572 * Convenience method for deleting rows in the database.
1573 *
1574 * @param table the table to delete from
1575 * @param whereClause the optional WHERE clause to apply when deleting.
1576 * Passing null will delete all rows.
1577 * @return the number of rows affected if a whereClause is passed in, 0
1578 * otherwise. To remove all rows and get a count pass "1" as the
1579 * whereClause.
1580 */
1581 public int delete(String table, String whereClause, String[] whereArgs) {
1582 if (!isOpen()) {
1583 throw new IllegalStateException("database not open");
1584 }
1585 lock();
1586 SQLiteStatement statement = null;
1587 try {
1588 statement = compileStatement("DELETE FROM " + table
1589 + (!TextUtils.isEmpty(whereClause)
1590 ? " WHERE " + whereClause : ""));
1591 if (whereArgs != null) {
1592 int numArgs = whereArgs.length;
1593 for (int i = 0; i < numArgs; i++) {
1594 DatabaseUtils.bindObjectToProgram(statement, i + 1, whereArgs[i]);
1595 }
1596 }
1597 statement.execute();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001598 return lastChangeCount();
1599 } catch (SQLiteDatabaseCorruptException e) {
1600 onCorruption();
1601 throw e;
1602 } finally {
1603 if (statement != null) {
1604 statement.close();
1605 }
1606 unlock();
1607 }
1608 }
1609
1610 /**
1611 * Convenience method for updating rows in the database.
1612 *
1613 * @param table the table to update in
1614 * @param values a map from column names to new column values. null is a
1615 * valid value that will be translated to NULL.
1616 * @param whereClause the optional WHERE clause to apply when updating.
1617 * Passing null will update all rows.
1618 * @return the number of rows affected
1619 */
1620 public int update(String table, ContentValues values, String whereClause, String[] whereArgs) {
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001621 return updateWithOnConflict(table, values, whereClause, whereArgs, CONFLICT_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001622 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001623
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001624 /**
1625 * Convenience method for updating rows in the database.
1626 *
1627 * @param table the table to update in
1628 * @param values a map from column names to new column values. null is a
1629 * valid value that will be translated to NULL.
1630 * @param whereClause the optional WHERE clause to apply when updating.
1631 * Passing null will update all rows.
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001632 * @param conflictAlgorithm for update conflict resolver
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001633 * @return the number of rows affected
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001634 */
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001635 public int updateWithOnConflict(String table, ContentValues values,
Vasu Nori6eb7c452010-01-27 14:31:24 -08001636 String whereClause, String[] whereArgs, int conflictAlgorithm) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001637 if (!isOpen()) {
1638 throw new IllegalStateException("database not open");
1639 }
1640
1641 if (values == null || values.size() == 0) {
1642 throw new IllegalArgumentException("Empty values");
1643 }
1644
1645 StringBuilder sql = new StringBuilder(120);
1646 sql.append("UPDATE ");
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001647 sql.append(CONFLICT_VALUES[conflictAlgorithm]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001648 sql.append(table);
1649 sql.append(" SET ");
1650
1651 Set<Map.Entry<String, Object>> entrySet = values.valueSet();
1652 Iterator<Map.Entry<String, Object>> entriesIter = entrySet.iterator();
1653
1654 while (entriesIter.hasNext()) {
1655 Map.Entry<String, Object> entry = entriesIter.next();
1656 sql.append(entry.getKey());
1657 sql.append("=?");
1658 if (entriesIter.hasNext()) {
1659 sql.append(", ");
1660 }
1661 }
1662
1663 if (!TextUtils.isEmpty(whereClause)) {
1664 sql.append(" WHERE ");
1665 sql.append(whereClause);
1666 }
1667
1668 lock();
1669 SQLiteStatement statement = null;
1670 try {
1671 statement = compileStatement(sql.toString());
1672
1673 // Bind the values
1674 int size = entrySet.size();
1675 entriesIter = entrySet.iterator();
1676 int bindArg = 1;
1677 for (int i = 0; i < size; i++) {
1678 Map.Entry<String, Object> entry = entriesIter.next();
1679 DatabaseUtils.bindObjectToProgram(statement, bindArg, entry.getValue());
1680 bindArg++;
1681 }
1682
1683 if (whereArgs != null) {
1684 size = whereArgs.length;
1685 for (int i = 0; i < size; i++) {
1686 statement.bindString(bindArg, whereArgs[i]);
1687 bindArg++;
1688 }
1689 }
1690
1691 // Run the program and then cleanup
1692 statement.execute();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001693 int numChangedRows = lastChangeCount();
1694 if (Config.LOGD && Log.isLoggable(TAG, Log.VERBOSE)) {
1695 Log.v(TAG, "Updated " + numChangedRows + " using " + values + " and " + sql);
1696 }
1697 return numChangedRows;
1698 } catch (SQLiteDatabaseCorruptException e) {
1699 onCorruption();
1700 throw e;
1701 } catch (SQLException e) {
1702 Log.e(TAG, "Error updating " + values + " using " + sql);
1703 throw e;
1704 } finally {
1705 if (statement != null) {
1706 statement.close();
1707 }
1708 unlock();
1709 }
1710 }
1711
1712 /**
1713 * Execute a single SQL statement that is not a query. For example, CREATE
1714 * TABLE, DELETE, INSERT, etc. Multiple statements separated by ;s are not
1715 * supported. it takes a write lock
1716 *
1717 * @throws SQLException If the SQL string is invalid for some reason
1718 */
1719 public void execSQL(String sql) throws SQLException {
Vasu Norif3cf8a42010-03-23 11:41:44 -07001720 if (!isOpen()) {
1721 throw new IllegalStateException("database not open");
1722 }
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -08001723 long timeStart = SystemClock.uptimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001724 lock();
1725 try {
1726 native_execSQL(sql);
1727 } catch (SQLiteDatabaseCorruptException e) {
1728 onCorruption();
1729 throw e;
1730 } finally {
1731 unlock();
1732 }
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -08001733
1734 // Log commit statements along with the most recently executed
1735 // SQL statement for disambiguation. Note that instance
1736 // equality to COMMIT_SQL is safe here.
1737 if (sql == COMMIT_SQL) {
1738 logTimeStat(sql + mLastSqlStatement, timeStart);
1739 } else {
1740 logTimeStat(sql, timeStart);
1741 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001742 }
1743
1744 /**
1745 * Execute a single SQL statement that is not a query. For example, CREATE
1746 * TABLE, DELETE, INSERT, etc. Multiple statements separated by ;s are not
1747 * supported. it takes a write lock,
1748 *
1749 * @param sql
1750 * @param bindArgs only byte[], String, Long and Double are supported in bindArgs.
1751 * @throws SQLException If the SQL string is invalid for some reason
1752 */
1753 public void execSQL(String sql, Object[] bindArgs) throws SQLException {
Vasu Norif3cf8a42010-03-23 11:41:44 -07001754 if (!isOpen()) {
1755 throw new IllegalStateException("database not open");
1756 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001757 if (bindArgs == null) {
1758 throw new IllegalArgumentException("Empty bindArgs");
1759 }
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -08001760 long timeStart = SystemClock.uptimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001761 lock();
1762 SQLiteStatement statement = null;
1763 try {
1764 statement = compileStatement(sql);
1765 if (bindArgs != null) {
1766 int numArgs = bindArgs.length;
1767 for (int i = 0; i < numArgs; i++) {
1768 DatabaseUtils.bindObjectToProgram(statement, i + 1, bindArgs[i]);
1769 }
1770 }
1771 statement.execute();
1772 } catch (SQLiteDatabaseCorruptException e) {
1773 onCorruption();
1774 throw e;
1775 } finally {
1776 if (statement != null) {
1777 statement.close();
1778 }
1779 unlock();
1780 }
Dan Egnor12311952009-11-23 14:47:45 -08001781 logTimeStat(sql, timeStart);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001782 }
1783
1784 @Override
1785 protected void finalize() {
1786 if (isOpen()) {
Vasu Norid606b4b2010-02-24 12:54:20 -08001787 Log.e(TAG, "close() was never explicitly called on database '" +
1788 mPath + "' ", mStackTrace);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001789 closeClosable();
1790 onAllReferencesReleased();
1791 }
1792 }
1793
1794 /**
1795 * Private constructor. See {@link #create} and {@link #openDatabase}.
1796 *
1797 * @param path The full path to the database
1798 * @param factory The factory to use when creating cursors, may be NULL.
1799 * @param flags 0 or {@link #NO_LOCALIZED_COLLATORS}. If the database file already
1800 * exists, mFlags will be updated appropriately.
1801 */
1802 private SQLiteDatabase(String path, CursorFactory factory, int flags) {
1803 if (path == null) {
1804 throw new IllegalArgumentException("path should not be null");
1805 }
1806 mFlags = flags;
1807 mPath = path;
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001808 mSlowQueryThreshold = SystemProperties.getInt(LOG_SLOW_QUERIES_PROPERTY, -1);
Vasu Nori08b448e2010-03-03 10:05:16 -08001809 mStackTrace = new DatabaseObjectNotClosedException().fillInStackTrace();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001810 mFactory = factory;
1811 dbopen(mPath, mFlags);
Vasu Nori5a03f362009-10-20 15:16:35 -07001812 if (SQLiteDebug.DEBUG_SQL_CACHE) {
1813 mTimeOpened = getTime();
1814 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001815 mPrograms = new WeakHashMap<SQLiteClosable,Object>();
1816 try {
1817 setLocale(Locale.getDefault());
1818 } catch (RuntimeException e) {
1819 Log.e(TAG, "Failed to setLocale() when constructing, closing the database", e);
1820 dbclose();
Vasu Nori5a03f362009-10-20 15:16:35 -07001821 if (SQLiteDebug.DEBUG_SQL_CACHE) {
1822 mTimeClosed = getTime();
1823 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001824 throw e;
1825 }
1826 }
1827
Vasu Nori5a03f362009-10-20 15:16:35 -07001828 private String getTime() {
1829 return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS ").format(System.currentTimeMillis());
1830 }
1831
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001832 /**
1833 * return whether the DB is opened as read only.
1834 * @return true if DB is opened as read only
1835 */
1836 public boolean isReadOnly() {
1837 return (mFlags & OPEN_READ_MASK) == OPEN_READONLY;
1838 }
1839
1840 /**
1841 * @return true if the DB is currently open (has not been closed)
1842 */
1843 public boolean isOpen() {
1844 return mNativeHandle != 0;
1845 }
1846
1847 public boolean needUpgrade(int newVersion) {
1848 return newVersion > getVersion();
1849 }
1850
1851 /**
1852 * Getter for the path to the database file.
1853 *
1854 * @return the path to our database file.
1855 */
1856 public final String getPath() {
1857 return mPath;
1858 }
1859
Vasu Nori5a03f362009-10-20 15:16:35 -07001860
Vasu Nori5a03f362009-10-20 15:16:35 -07001861
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -08001862 /* package */ void logTimeStat(String sql, long beginMillis) {
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -08001863 // Keep track of the last statement executed here, as this is
1864 // the common funnel through which all methods of hitting
1865 // libsqlite eventually flow.
1866 mLastSqlStatement = sql;
1867
Dan Egnor12311952009-11-23 14:47:45 -08001868 // Sample fast queries in proportion to the time taken.
1869 // Quantize the % first, so the logged sampling probability
1870 // exactly equals the actual sampling rate for this query.
1871
1872 int samplePercent;
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -08001873 long durationMillis = SystemClock.uptimeMillis() - beginMillis;
1874 if (durationMillis >= QUERY_LOG_TIME_IN_MILLIS) {
Dan Egnor12311952009-11-23 14:47:45 -08001875 samplePercent = 100;
1876 } else {
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -08001877 samplePercent = (int) (100 * durationMillis / QUERY_LOG_TIME_IN_MILLIS) + 1;
Dan Egnor799f7212009-11-24 16:24:44 -08001878 if (mRandom.nextInt(100) >= samplePercent) return;
Dan Egnor12311952009-11-23 14:47:45 -08001879 }
1880
1881 if (sql.length() > QUERY_LOG_SQL_LENGTH) sql = sql.substring(0, QUERY_LOG_SQL_LENGTH);
1882
1883 // ActivityThread.currentPackageName() only returns non-null if the
1884 // current thread is an application main thread. This parameter tells
1885 // us whether an event loop is blocked, and if so, which app it is.
1886 //
1887 // Sadly, there's no fast way to determine app name if this is *not* a
1888 // main thread, or when we are invoked via Binder (e.g. ContentProvider).
1889 // Hopefully the full path to the database will be informative enough.
1890
1891 String blockingPackage = ActivityThread.currentPackageName();
1892 if (blockingPackage == null) blockingPackage = "";
1893
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -08001894 EventLog.writeEvent(
Brad Fitzpatrickd8330232010-02-19 10:59:01 -08001895 EVENT_DB_OPERATION,
1896 getPathForLogs(),
1897 sql,
1898 durationMillis,
1899 blockingPackage,
1900 samplePercent);
1901 }
1902
1903 /**
1904 * Removes email addresses from database filenames before they're
1905 * logged to the EventLog where otherwise apps could potentially
1906 * read them.
1907 */
1908 private String getPathForLogs() {
1909 if (mPathForLogs != null) {
1910 return mPathForLogs;
1911 }
1912 if (mPath == null) {
1913 return null;
1914 }
1915 if (mPath.indexOf('@') == -1) {
1916 mPathForLogs = mPath;
1917 } else {
1918 mPathForLogs = EMAIL_IN_DB_PATTERN.matcher(mPath).replaceAll("XX@YY");
1919 }
1920 return mPathForLogs;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001921 }
1922
1923 /**
1924 * Sets the locale for this database. Does nothing if this database has
1925 * the NO_LOCALIZED_COLLATORS flag set or was opened read only.
1926 * @throws SQLException if the locale could not be set. The most common reason
1927 * for this is that there is no collator available for the locale you requested.
1928 * In this case the database remains unchanged.
1929 */
1930 public void setLocale(Locale locale) {
1931 lock();
1932 try {
1933 native_setLocale(locale.toString(), mFlags);
1934 } finally {
1935 unlock();
1936 }
1937 }
1938
Vasu Norie495d1f2010-01-06 16:34:19 -08001939 /*
1940 * ============================================================================
1941 *
1942 * The following methods deal with compiled-sql cache
1943 * ============================================================================
1944 */
1945 /**
1946 * adds the given sql and its compiled-statement-id-returned-by-sqlite to the
1947 * cache of compiledQueries attached to 'this'.
1948 *
1949 * if there is already a {@link SQLiteCompiledSql} in compiledQueries for the given sql,
1950 * the new {@link SQLiteCompiledSql} object is NOT inserted into the cache (i.e.,the current
1951 * mapping is NOT replaced with the new mapping).
1952 */
1953 /* package */ void addToCompiledQueries(String sql, SQLiteCompiledSql compiledStatement) {
1954 if (mMaxSqlCacheSize == 0) {
1955 // for this database, there is no cache of compiled sql.
1956 if (SQLiteDebug.DEBUG_SQL_CACHE) {
1957 Log.v(TAG, "|NOT adding_sql_to_cache|" + getPath() + "|" + sql);
1958 }
1959 return;
1960 }
1961
1962 SQLiteCompiledSql compiledSql = null;
1963 synchronized(mCompiledQueries) {
1964 // don't insert the new mapping if a mapping already exists
1965 compiledSql = mCompiledQueries.get(sql);
1966 if (compiledSql != null) {
1967 return;
1968 }
1969 // add this <sql, compiledStatement> to the cache
1970 if (mCompiledQueries.size() == mMaxSqlCacheSize) {
Vasu Nori49d02ac2010-03-05 21:49:30 -08001971 /*
1972 * cache size of {@link #mMaxSqlCacheSize} is not enough for this app.
1973 * log a warning MAX_WARNINGS_ON_CACHESIZE_CONDITION times
1974 * chances are it is NOT using ? for bindargs - so caching is useless.
1975 * TODO: either let the callers set max cchesize for their app, or intelligently
1976 * figure out what should be cached for a given app.
Vasu Norie495d1f2010-01-06 16:34:19 -08001977 */
Vasu Nori49d02ac2010-03-05 21:49:30 -08001978 if (++mCacheFullWarnings == MAX_WARNINGS_ON_CACHESIZE_CONDITION) {
1979 Log.w(TAG, "Reached MAX size for compiled-sql statement cache for database " +
1980 getPath() + "; i.e., NO space for this sql statement in cache: " +
1981 sql + ". Please change your sql statements to use '?' for " +
1982 "bindargs, instead of using actual values");
1983 }
1984 // don't add this entry to cache
1985 } else {
1986 // cache is NOT full. add this to cache.
1987 mCompiledQueries.put(sql, compiledStatement);
1988 if (SQLiteDebug.DEBUG_SQL_CACHE) {
1989 Log.v(TAG, "|adding_sql_to_cache|" + getPath() + "|" +
1990 mCompiledQueries.size() + "|" + sql);
Vasu Norie495d1f2010-01-06 16:34:19 -08001991 }
1992 }
Vasu Norie495d1f2010-01-06 16:34:19 -08001993 }
1994 return;
1995 }
1996
1997
1998 private void deallocCachedSqlStatements() {
1999 synchronized (mCompiledQueries) {
2000 for (SQLiteCompiledSql compiledSql : mCompiledQueries.values()) {
2001 compiledSql.releaseSqlStatement();
2002 }
2003 mCompiledQueries.clear();
2004 }
2005 }
2006
2007 /**
2008 * from the compiledQueries cache, returns the compiled-statement-id for the given sql.
2009 * returns null, if not found in the cache.
2010 */
2011 /* package */ SQLiteCompiledSql getCompiledStatementForSql(String sql) {
2012 SQLiteCompiledSql compiledStatement = null;
2013 boolean cacheHit;
2014 synchronized(mCompiledQueries) {
2015 if (mMaxSqlCacheSize == 0) {
2016 // for this database, there is no cache of compiled sql.
2017 if (SQLiteDebug.DEBUG_SQL_CACHE) {
2018 Log.v(TAG, "|cache NOT found|" + getPath());
2019 }
2020 return null;
2021 }
2022 cacheHit = (compiledStatement = mCompiledQueries.get(sql)) != null;
2023 }
2024 if (cacheHit) {
2025 mNumCacheHits++;
2026 } else {
2027 mNumCacheMisses++;
2028 }
2029
2030 if (SQLiteDebug.DEBUG_SQL_CACHE) {
2031 Log.v(TAG, "|cache_stats|" +
2032 getPath() + "|" + mCompiledQueries.size() +
2033 "|" + mNumCacheHits + "|" + mNumCacheMisses +
2034 "|" + cacheHit + "|" + mTimeOpened + "|" + mTimeClosed + "|" + sql);
2035 }
2036 return compiledStatement;
2037 }
2038
2039 /**
2040 * returns true if the given sql is cached in compiled-sql cache.
2041 * @hide
2042 */
2043 public boolean isInCompiledSqlCache(String sql) {
2044 synchronized(mCompiledQueries) {
2045 return mCompiledQueries.containsKey(sql);
2046 }
2047 }
2048
2049 /**
2050 * purges the given sql from the compiled-sql cache.
2051 * @hide
2052 */
2053 public void purgeFromCompiledSqlCache(String sql) {
2054 synchronized(mCompiledQueries) {
2055 mCompiledQueries.remove(sql);
2056 }
2057 }
2058
2059 /**
2060 * remove everything from the compiled sql cache
2061 * @hide
2062 */
2063 public void resetCompiledSqlCache() {
2064 synchronized(mCompiledQueries) {
2065 mCompiledQueries.clear();
2066 }
2067 }
2068
2069 /**
2070 * return the current maxCacheSqlCacheSize
2071 * @hide
2072 */
2073 public synchronized int getMaxSqlCacheSize() {
2074 return mMaxSqlCacheSize;
2075 }
2076
2077 /**
2078 * set the max size of the compiled sql cache for this database after purging the cache.
2079 * (size of the cache = number of compiled-sql-statements stored in the cache).
2080 *
2081 * max cache size can ONLY be increased from its current size (default = 0).
2082 * if this method is called with smaller size than the current value of mMaxSqlCacheSize,
2083 * then IllegalStateException is thrown
2084 *
2085 * synchronized because we don't want t threads to change cache size at the same time.
2086 * @param cacheSize the size of the cache. can be (0 to MAX_SQL_CACHE_SIZE)
2087 * @throws IllegalStateException if input cacheSize > MAX_SQL_CACHE_SIZE or < 0 or
2088 * < the value set with previous setMaxSqlCacheSize() call.
2089 *
2090 * @hide
2091 */
2092 public synchronized void setMaxSqlCacheSize(int cacheSize) {
2093 if (cacheSize > MAX_SQL_CACHE_SIZE || cacheSize < 0) {
2094 throw new IllegalStateException("expected value between 0 and " + MAX_SQL_CACHE_SIZE);
2095 } else if (cacheSize < mMaxSqlCacheSize) {
2096 throw new IllegalStateException("cannot set cacheSize to a value less than the value " +
2097 "set with previous setMaxSqlCacheSize() call.");
2098 }
2099 mMaxSqlCacheSize = cacheSize;
2100 }
2101
Vasu Noric3849202010-03-09 10:47:25 -08002102 static class ActiveDatabases {
2103 private static final ActiveDatabases activeDatabases = new ActiveDatabases();
2104 private HashSet<WeakReference<SQLiteDatabase>> mActiveDatabases =
2105 new HashSet<WeakReference<SQLiteDatabase>>();
2106 private ActiveDatabases() {} // disable instantiation of this class
2107 static ActiveDatabases getInstance() {return activeDatabases;}
2108 }
2109
Vasu Norif3cf8a42010-03-23 11:41:44 -07002110 /**
2111 * this method is used to collect data about ALL open databases in the current process.
2112 * bugreport is a user of this data.
2113 */
Vasu Noric3849202010-03-09 10:47:25 -08002114 /* package */ static ArrayList<DbStats> getDbStats() {
2115 ArrayList<DbStats> dbStatsList = new ArrayList<DbStats>();
2116 for (WeakReference<SQLiteDatabase> w : ActiveDatabases.getInstance().mActiveDatabases) {
2117 SQLiteDatabase db = w.get();
2118 if (db == null || !db.isOpen()) {
2119 continue;
2120 }
2121 // get SQLITE_DBSTATUS_LOOKASIDE_USED for the db
2122 int lookasideUsed = db.native_getDbLookaside();
2123
2124 // get the lastnode of the dbname
2125 String path = db.getPath();
2126 int indx = path.lastIndexOf("/");
2127 String lastnode = path.substring((indx != -1) ? ++indx : 0);
2128
2129 // get list of attached dbs and for each db, get its size and pagesize
2130 ArrayList<Pair<String, String>> attachedDbs = getAttachedDbs(db);
Vasu Norif3cf8a42010-03-23 11:41:44 -07002131 if (attachedDbs == null) {
2132 continue;
2133 }
Vasu Noric3849202010-03-09 10:47:25 -08002134 for (int i = 0; i < attachedDbs.size(); i++) {
2135 Pair<String, String> p = attachedDbs.get(i);
2136 long pageCount = getPragmaVal(db, p.first + ".page_count;");
2137
2138 // first entry in the attached db list is always the main database
2139 // don't worry about prefixing the dbname with "main"
2140 String dbName;
2141 if (i == 0) {
2142 dbName = lastnode;
2143 } else {
2144 // lookaside is only relevant for the main db
2145 lookasideUsed = 0;
2146 dbName = " (attached) " + p.first;
2147 // if the attached db has a path, attach the lastnode from the path to above
2148 if (p.second.trim().length() > 0) {
2149 int idx = p.second.lastIndexOf("/");
2150 dbName += " : " + p.second.substring((idx != -1) ? ++idx : 0);
2151 }
2152 }
Vasu Norif3cf8a42010-03-23 11:41:44 -07002153 if (pageCount > 0) {
2154 dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(),
2155 lookasideUsed));
2156 }
Vasu Noric3849202010-03-09 10:47:25 -08002157 }
2158 }
2159 return dbStatsList;
2160 }
2161
2162 /**
2163 * get the specified pragma value from sqlite for the specified database.
2164 * only handles pragma's that return int/long.
2165 * NO JAVA locks are held in this method.
2166 * TODO: use this to do all pragma's in this class
2167 */
2168 private static long getPragmaVal(SQLiteDatabase db, String pragma) {
Vasu Norif3cf8a42010-03-23 11:41:44 -07002169 if (!db.isOpen()) {
2170 return 0;
2171 }
Vasu Noric3849202010-03-09 10:47:25 -08002172 SQLiteStatement prog = null;
2173 try {
2174 prog = new SQLiteStatement(db, "PRAGMA " + pragma);
2175 long val = prog.simpleQueryForLong();
2176 return val;
2177 } finally {
2178 if (prog != null) prog.close();
2179 }
2180 }
2181
2182 /**
2183 * returns list of full pathnames of all attached databases
2184 * including the main database
2185 * TODO: move this to {@link DatabaseUtils}
2186 */
2187 private static ArrayList<Pair<String, String>> getAttachedDbs(SQLiteDatabase dbObj) {
Vasu Norif3cf8a42010-03-23 11:41:44 -07002188 if (!dbObj.isOpen()) {
2189 return null;
2190 }
Vasu Noric3849202010-03-09 10:47:25 -08002191 ArrayList<Pair<String, String>> attachedDbs = new ArrayList<Pair<String, String>>();
2192 Cursor c = dbObj.rawQuery("pragma database_list;", null);
2193 while (c.moveToNext()) {
2194 attachedDbs.add(new Pair<String, String>(c.getString(1), c.getString(2)));
2195 }
2196 c.close();
2197 return attachedDbs;
2198 }
2199
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002200 /**
2201 * Native call to open the database.
2202 *
2203 * @param path The full path to the database
2204 */
2205 private native void dbopen(String path, int flags);
2206
2207 /**
Vasu Nori3ef94e22010-02-05 14:49:04 -08002208 * Native call to setup tracing of all sql statements
2209 *
2210 * @param path the full path to the database
2211 */
2212 private native void enableSqlTracing(String path);
2213
2214 /**
2215 * Native call to setup profiling of all sql statements.
2216 * currently, sqlite's profiling = printing of execution-time
2217 * (wall-clock time) of each of the sql statements, as they
2218 * are executed.
2219 *
2220 * @param path the full path to the database
2221 */
2222 private native void enableSqlProfiling(String path);
2223
2224 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002225 * Native call to execute a raw SQL statement. {@link #lock} must be held
2226 * when calling this method.
2227 *
2228 * @param sql The raw SQL string
2229 * @throws SQLException
2230 */
2231 /* package */ native void native_execSQL(String sql) throws SQLException;
2232
2233 /**
2234 * Native call to set the locale. {@link #lock} must be held when calling
2235 * this method.
2236 * @throws SQLException
2237 */
2238 /* package */ native void native_setLocale(String loc, int flags);
2239
2240 /**
2241 * Returns the row ID of the last row inserted into the database.
2242 *
2243 * @return the row ID of the last row inserted into the database.
2244 */
2245 /* package */ native long lastInsertRow();
2246
2247 /**
2248 * Returns the number of changes made in the last statement executed.
2249 *
2250 * @return the number of changes made in the last statement executed.
2251 */
2252 /* package */ native int lastChangeCount();
Vasu Noric3849202010-03-09 10:47:25 -08002253
2254 /**
2255 * return the SQLITE_DBSTATUS_LOOKASIDE_USED documented here
2256 * http://www.sqlite.org/c3ref/c_dbstatus_lookaside_used.html
2257 * @return int value of SQLITE_DBSTATUS_LOOKASIDE_USED
2258 */
2259 private native int native_getDbLookaside();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002260}