blob: 57bf3f7c7743b7f3d402d16990ade96692062473 [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
19import android.content.ContentValues;
20import android.database.Cursor;
21import android.database.DatabaseUtils;
22import android.database.SQLException;
23import android.os.Debug;
24import android.os.SystemClock;
25import android.text.TextUtils;
26import android.util.Config;
27import android.util.Log;
28import android.util.EventLog;
29
30import java.io.File;
31import java.util.HashMap;
32import java.util.Iterator;
33import java.util.Locale;
34import java.util.Map;
35import java.util.Set;
36import java.util.WeakHashMap;
37import java.util.concurrent.locks.ReentrantLock;
38
39/**
40 * Exposes methods to manage a SQLite database.
41 * <p>SQLiteDatabase has methods to create, delete, execute SQL commands, and
42 * perform other common database management tasks.
43 * <p>See the Notepad sample application in the SDK for an example of creating
44 * and managing a database.
45 * <p> Database names must be unique within an application, not across all
46 * applications.
47 *
48 * <h3>Localized Collation - ORDER BY</h3>
49 * <p>In addition to SQLite's default <code>BINARY</code> collator, Android supplies
50 * two more, <code>LOCALIZED</code>, which changes with the system's current locale
51 * if you wire it up correctly (XXX a link needed!), and <code>UNICODE</code>, which
52 * is the Unicode Collation Algorithm and not tailored to the current locale.
53 */
54public class SQLiteDatabase extends SQLiteClosable {
55 private static final String TAG = "Database";
56 private static final int DB_OPERATION_EVENT = 52000;
57
58 /**
59 * Algorithms used in ON CONFLICT clause
60 * http://www.sqlite.org/lang_conflict.html
61 * @hide
62 */
63 public enum ConflictAlgorithm {
64 /**
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -070065 * When a constraint violation occurs, an immediate ROLLBACK occurs,
66 * thus ending the current transaction, and the command aborts with a
67 * return code of SQLITE_CONSTRAINT. If no transaction is active
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068 * (other than the implied transaction that is created on every command)
69 * then this algorithm works the same as ABORT.
70 */
71 ROLLBACK("ROLLBACK"),
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -070072
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073 /**
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -070074 * When a constraint violation occurs,no ROLLBACK is executed
75 * so changes from prior commands within the same transaction
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076 * are preserved. This is the default behavior.
77 */
78 ABORT("ABORT"),
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -070079
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080 /**
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -070081 * When a constraint violation occurs, the command aborts with a return
82 * code SQLITE_CONSTRAINT. But any changes to the database that
83 * the command made prior to encountering the constraint violation
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084 * are preserved and are not backed out.
85 */
86 FAIL("FAIL"),
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -070087
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088 /**
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -070089 * When a constraint violation occurs, the one row that contains
90 * the constraint violation is not inserted or changed.
91 * But the command continues executing normally. Other rows before and
92 * after the row that contained the constraint violation continue to be
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093 * inserted or updated normally. No error is returned.
94 */
95 IGNORE("IGNORE"),
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -070096
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097 /**
98 * When a UNIQUE constraint violation occurs, the pre-existing rows that
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -070099 * are causing the constraint violation are removed prior to inserting
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100 * or updating the current row. Thus the insert or update always occurs.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700101 * The command continues executing normally. No error is returned.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102 * If a NOT NULL constraint violation occurs, the NULL value is replaced
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700103 * by the default value for that column. If the column has no default
104 * value, then the ABORT algorithm is used. If a CHECK constraint
105 * violation occurs then the IGNORE algorithm is used. When this conflict
106 * resolution strategy deletes rows in order to satisfy a constraint,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107 * it does not invoke delete triggers on those rows.
108 * This behavior might change in a future release.
109 */
110 REPLACE("REPLACE");
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700111
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 private final String mValue;
113 ConflictAlgorithm(String value) {
114 mValue = value;
115 }
116 public String value() {
117 return mValue;
118 }
119 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700120
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 /**
122 * Maximum Length Of A LIKE Or GLOB Pattern
123 * The pattern matching algorithm used in the default LIKE and GLOB implementation
124 * of SQLite can exhibit O(N^2) performance (where N is the number of characters in
125 * the pattern) for certain pathological cases. To avoid denial-of-service attacks
126 * the length of the LIKE or GLOB pattern is limited to SQLITE_MAX_LIKE_PATTERN_LENGTH bytes.
127 * The default value of this limit is 50000. A modern workstation can evaluate
128 * even a pathological LIKE or GLOB pattern of 50000 bytes relatively quickly.
129 * The denial of service problem only comes into play when the pattern length gets
130 * into millions of bytes. Nevertheless, since most useful LIKE or GLOB patterns
131 * are at most a few dozen bytes in length, paranoid application developers may
132 * want to reduce this parameter to something in the range of a few hundred
133 * if they know that external users are able to generate arbitrary patterns.
134 */
135 public static final int SQLITE_MAX_LIKE_PATTERN_LENGTH = 50000;
136
137 /**
138 * Flag for {@link #openDatabase} to open the database for reading and writing.
139 * If the disk is full, this may fail even before you actually write anything.
140 *
141 * {@more} Note that the value of this flag is 0, so it is the default.
142 */
143 public static final int OPEN_READWRITE = 0x00000000; // update native code if changing
144
145 /**
146 * Flag for {@link #openDatabase} to open the database for reading only.
147 * This is the only reliable way to open a database if the disk may be full.
148 */
149 public static final int OPEN_READONLY = 0x00000001; // update native code if changing
150
151 private static final int OPEN_READ_MASK = 0x00000001; // update native code if changing
152
153 /**
154 * Flag for {@link #openDatabase} to open the database without support for localized collators.
155 *
156 * {@more} This causes the collator <code>LOCALIZED</code> not to be created.
157 * You must be consistent when using this flag to use the setting the database was
158 * created with. If this is set, {@link #setLocale} will do nothing.
159 */
160 public static final int NO_LOCALIZED_COLLATORS = 0x00000010; // update native code if changing
161
162 /**
163 * Flag for {@link #openDatabase} to create the database file if it does not already exist.
164 */
165 public static final int CREATE_IF_NECESSARY = 0x10000000; // update native code if changing
166
167 /**
168 * Indicates whether the most-recently started transaction has been marked as successful.
169 */
170 private boolean mInnerTransactionIsSuccessful;
171
172 /**
173 * Valid during the life of a transaction, and indicates whether the entire transaction (the
174 * outer one and all of the inner ones) so far has been successful.
175 */
176 private boolean mTransactionIsSuccessful;
177
178 /** Synchronize on this when accessing the database */
179 private final ReentrantLock mLock = new ReentrantLock(true);
180
181 private long mLockAcquiredWallTime = 0L;
182 private long mLockAcquiredThreadTime = 0L;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700183
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184 // limit the frequency of complaints about each database to one within 20 sec
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700185 // unless run command adb shell setprop log.tag.Database VERBOSE
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800186 private static final int LOCK_WARNING_WINDOW_IN_MS = 20000;
187 /** If the lock is held this long then a warning will be printed when it is released. */
188 private static final int LOCK_ACQUIRED_WARNING_TIME_IN_MS = 300;
189 private static final int LOCK_ACQUIRED_WARNING_THREAD_TIME_IN_MS = 100;
190 private static final int LOCK_ACQUIRED_WARNING_TIME_IN_MS_ALWAYS_PRINT = 2000;
191
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700192 private static final int SLEEP_AFTER_YIELD_QUANTUM = 500;
193
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 private long mLastLockMessageTime = 0L;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700195
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 /** Used by native code, do not rename */
197 /* package */ int mNativeHandle = 0;
198
199 /** Used to make temp table names unique */
200 /* package */ int mTempTableSequence = 0;
201
202 /** The path for the database file */
203 private String mPath;
204
205 /** The flags passed to open/create */
206 private int mFlags;
207
208 /** The optional factory to use when creating new Cursors */
209 private CursorFactory mFactory;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700210
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 private WeakHashMap<SQLiteClosable, Object> mPrograms;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700212
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800213 private final RuntimeException mLeakedException;
214
215 // package visible, since callers will access directly to minimize overhead in the case
216 // that logging is not enabled.
217 /* package */ final boolean mLogStats;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700218
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219 /**
220 * @param closable
221 */
222 void addSQLiteClosable(SQLiteClosable closable) {
223 lock();
224 try {
225 mPrograms.put(closable, null);
226 } finally {
227 unlock();
228 }
229 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700230
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 void removeSQLiteClosable(SQLiteClosable closable) {
232 lock();
233 try {
234 mPrograms.remove(closable);
235 } finally {
236 unlock();
237 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700238 }
239
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240 @Override
241 protected void onAllReferencesReleased() {
242 if (isOpen()) {
243 dbclose();
244 }
245 }
246
247 /**
248 * Attempts to release memory that SQLite holds but does not require to
249 * operate properly. Typically this memory will come from the page cache.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700250 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800251 * @return the number of bytes actually released
252 */
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700253 static public native int releaseMemory();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800254
255 /**
256 * Control whether or not the SQLiteDatabase is made thread-safe by using locks
257 * around critical sections. This is pretty expensive, so if you know that your
258 * DB will only be used by a single thread then you should set this to false.
259 * The default is true.
260 * @param lockingEnabled set to true to enable locks, false otherwise
261 */
262 public void setLockingEnabled(boolean lockingEnabled) {
263 mLockingEnabled = lockingEnabled;
264 }
265
266 /**
267 * If set then the SQLiteDatabase is made thread-safe by using locks
268 * around critical sections
269 */
270 private boolean mLockingEnabled = true;
271
272 /* package */ void onCorruption() {
273 try {
274 // Close the database (if we can), which will cause subsequent operations to fail.
275 close();
276 } finally {
277 Log.e(TAG, "Removing corrupt database: " + mPath);
278 // Delete the corrupt file. Don't re-create it now -- that would just confuse people
279 // -- but the next time someone tries to open it, they can set it up from scratch.
280 new File(mPath).delete();
281 }
282 }
283
284 /**
285 * Locks the database for exclusive access. The database lock must be held when
286 * touch the native sqlite3* object since it is single threaded and uses
287 * a polling lock contention algorithm. The lock is recursive, and may be acquired
288 * multiple times by the same thread. This is a no-op if mLockingEnabled is false.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700289 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800290 * @see #unlock()
291 */
292 /* package */ void lock() {
293 if (!mLockingEnabled) return;
294 mLock.lock();
295 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
296 if (mLock.getHoldCount() == 1) {
297 // Use elapsed real-time since the CPU may sleep when waiting for IO
298 mLockAcquiredWallTime = SystemClock.elapsedRealtime();
299 mLockAcquiredThreadTime = Debug.threadCpuTimeNanos();
300 }
301 }
302 }
303
304 /**
305 * Locks the database for exclusive access. The database lock must be held when
306 * touch the native sqlite3* object since it is single threaded and uses
307 * a polling lock contention algorithm. The lock is recursive, and may be acquired
308 * multiple times by the same thread.
309 *
310 * @see #unlockForced()
311 */
312 private void lockForced() {
313 mLock.lock();
314 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
315 if (mLock.getHoldCount() == 1) {
316 // Use elapsed real-time since the CPU may sleep when waiting for IO
317 mLockAcquiredWallTime = SystemClock.elapsedRealtime();
318 mLockAcquiredThreadTime = Debug.threadCpuTimeNanos();
319 }
320 }
321 }
322
323 /**
324 * Releases the database lock. This is a no-op if mLockingEnabled is false.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700325 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800326 * @see #unlock()
327 */
328 /* package */ void unlock() {
329 if (!mLockingEnabled) return;
330 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
331 if (mLock.getHoldCount() == 1) {
332 checkLockHoldTime();
333 }
334 }
335 mLock.unlock();
336 }
337
338 /**
339 * Releases the database lock.
340 *
341 * @see #unlockForced()
342 */
343 private void unlockForced() {
344 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
345 if (mLock.getHoldCount() == 1) {
346 checkLockHoldTime();
347 }
348 }
349 mLock.unlock();
350 }
351
352 private void checkLockHoldTime() {
353 // Use elapsed real-time since the CPU may sleep when waiting for IO
354 long elapsedTime = SystemClock.elapsedRealtime();
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700355 long lockedTime = elapsedTime - mLockAcquiredWallTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356 if (lockedTime < LOCK_ACQUIRED_WARNING_TIME_IN_MS_ALWAYS_PRINT &&
357 !Log.isLoggable(TAG, Log.VERBOSE) &&
358 (elapsedTime - mLastLockMessageTime) < LOCK_WARNING_WINDOW_IN_MS) {
359 return;
360 }
361 if (lockedTime > LOCK_ACQUIRED_WARNING_TIME_IN_MS) {
362 int threadTime = (int)
363 ((Debug.threadCpuTimeNanos() - mLockAcquiredThreadTime) / 1000000);
364 if (threadTime > LOCK_ACQUIRED_WARNING_THREAD_TIME_IN_MS ||
365 lockedTime > LOCK_ACQUIRED_WARNING_TIME_IN_MS_ALWAYS_PRINT) {
366 mLastLockMessageTime = elapsedTime;
367 String msg = "lock held on " + mPath + " for " + lockedTime + "ms. Thread time was "
368 + threadTime + "ms";
369 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING_STACK_TRACE) {
370 Log.d(TAG, msg, new Exception());
371 } else {
372 Log.d(TAG, msg);
373 }
374 }
375 }
376 }
377
378 /**
379 * Begins a transaction. Transactions can be nested. When the outer transaction is ended all of
380 * the work done in that transaction and all of the nested transactions will be committed or
381 * rolled back. The changes will be rolled back if any transaction is ended without being
382 * marked as clean (by calling setTransactionSuccessful). Otherwise they will be committed.
383 *
384 * <p>Here is the standard idiom for transactions:
385 *
386 * <pre>
387 * db.beginTransaction();
388 * try {
389 * ...
390 * db.setTransactionSuccessful();
391 * } finally {
392 * db.endTransaction();
393 * }
394 * </pre>
395 */
396 public void beginTransaction() {
397 lockForced();
398 boolean ok = false;
399 try {
400 // If this thread already had the lock then get out
401 if (mLock.getHoldCount() > 1) {
402 if (mInnerTransactionIsSuccessful) {
403 String msg = "Cannot call beginTransaction between "
404 + "calling setTransactionSuccessful and endTransaction";
405 IllegalStateException e = new IllegalStateException(msg);
406 Log.e(TAG, "beginTransaction() failed", e);
407 throw e;
408 }
409 ok = true;
410 return;
411 }
412
413 // This thread didn't already have the lock, so begin a database
414 // transaction now.
415 execSQL("BEGIN EXCLUSIVE;");
416 mTransactionIsSuccessful = true;
417 mInnerTransactionIsSuccessful = false;
418 ok = true;
419 } finally {
420 if (!ok) {
421 // beginTransaction is called before the try block so we must release the lock in
422 // the case of failure.
423 unlockForced();
424 }
425 }
426 }
427
428 /**
429 * End a transaction. See beginTransaction for notes about how to use this and when transactions
430 * are committed and rolled back.
431 */
432 public void endTransaction() {
433 if (!mLock.isHeldByCurrentThread()) {
434 throw new IllegalStateException("no transaction pending");
435 }
436 try {
437 if (mInnerTransactionIsSuccessful) {
438 mInnerTransactionIsSuccessful = false;
439 } else {
440 mTransactionIsSuccessful = false;
441 }
442 if (mLock.getHoldCount() != 1) {
443 return;
444 }
445 if (mTransactionIsSuccessful) {
446 execSQL("COMMIT;");
447 } else {
448 try {
449 execSQL("ROLLBACK;");
450 } catch (SQLException e) {
451 if (Config.LOGD) {
452 Log.d(TAG, "exception during rollback, maybe the DB previously "
453 + "performed an auto-rollback");
454 }
455 }
456 }
457 } finally {
458 unlockForced();
459 if (Config.LOGV) {
460 Log.v(TAG, "unlocked " + Thread.currentThread()
461 + ", holdCount is " + mLock.getHoldCount());
462 }
463 }
464 }
465
466 /**
467 * Marks the current transaction as successful. Do not do any more database work between
468 * calling this and calling endTransaction. Do as little non-database work as possible in that
469 * situation too. If any errors are encountered between this and endTransaction the transaction
470 * will still be committed.
471 *
472 * @throws IllegalStateException if the current thread is not in a transaction or the
473 * transaction is already marked as successful.
474 */
475 public void setTransactionSuccessful() {
476 if (!mLock.isHeldByCurrentThread()) {
477 throw new IllegalStateException("no transaction pending");
478 }
479 if (mInnerTransactionIsSuccessful) {
480 throw new IllegalStateException(
481 "setTransactionSuccessful may only be called once per call to beginTransaction");
482 }
483 mInnerTransactionIsSuccessful = true;
484 }
485
486 /**
487 * return true if there is a transaction pending
488 */
489 public boolean inTransaction() {
490 return mLock.getHoldCount() > 0;
491 }
492
493 /**
494 * Checks if the database lock is held by this thread.
495 *
496 * @return true, if this thread is holding the database lock.
497 */
498 public boolean isDbLockedByCurrentThread() {
499 return mLock.isHeldByCurrentThread();
500 }
501
502 /**
503 * Checks if the database is locked by another thread. This is
504 * just an estimate, since this status can change at any time,
505 * including after the call is made but before the result has
506 * been acted upon.
507 *
508 * @return true, if the database is locked by another thread
509 */
510 public boolean isDbLockedByOtherThreads() {
511 return !mLock.isHeldByCurrentThread() && mLock.isLocked();
512 }
513
514 /**
515 * Temporarily end the transaction to let other threads run. The transaction is assumed to be
516 * successful so far. Do not call setTransactionSuccessful before calling this. When this
517 * returns a new transaction will have been created but not marked as successful.
518 * @return true if the transaction was yielded
519 * @deprecated if the db is locked more than once (becuase of nested transactions) then the lock
520 * will not be yielded. Use yieldIfContendedSafely instead.
521 */
Dianne Hackborn4a51c202009-08-21 15:14:02 -0700522 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800523 public boolean yieldIfContended() {
Fred Quintana5c7aede2009-08-27 21:41:27 -0700524 return yieldIfContendedHelper(false /* do not check yielding */,
525 -1 /* sleepAfterYieldDelay */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800526 }
527
528 /**
529 * Temporarily end the transaction to let other threads run. The transaction is assumed to be
530 * successful so far. Do not call setTransactionSuccessful before calling this. When this
531 * returns a new transaction will have been created but not marked as successful. This assumes
532 * that there are no nested transactions (beginTransaction has only been called once) and will
Fred Quintana5c7aede2009-08-27 21:41:27 -0700533 * throw an exception if that is not the case.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800534 * @return true if the transaction was yielded
535 */
536 public boolean yieldIfContendedSafely() {
Fred Quintana5c7aede2009-08-27 21:41:27 -0700537 return yieldIfContendedHelper(true /* check yielding */, -1 /* sleepAfterYieldDelay*/);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800538 }
539
Fred Quintana5c7aede2009-08-27 21:41:27 -0700540 /**
541 * Temporarily end the transaction to let other threads run. The transaction is assumed to be
542 * successful so far. Do not call setTransactionSuccessful before calling this. When this
543 * returns a new transaction will have been created but not marked as successful. This assumes
544 * that there are no nested transactions (beginTransaction has only been called once) and will
545 * throw an exception if that is not the case.
546 * @param sleepAfterYieldDelay if > 0, sleep this long before starting a new transaction if
547 * the lock was actually yielded. This will allow other background threads to make some
548 * more progress than they would if we started the transaction immediately.
549 * @return true if the transaction was yielded
550 */
551 public boolean yieldIfContendedSafely(long sleepAfterYieldDelay) {
552 return yieldIfContendedHelper(true /* check yielding */, sleepAfterYieldDelay);
553 }
554
555 private boolean yieldIfContendedHelper(boolean checkFullyYielded, long sleepAfterYieldDelay) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800556 if (mLock.getQueueLength() == 0) {
557 // Reset the lock acquire time since we know that the thread was willing to yield
558 // the lock at this time.
559 mLockAcquiredWallTime = SystemClock.elapsedRealtime();
560 mLockAcquiredThreadTime = Debug.threadCpuTimeNanos();
561 return false;
562 }
563 setTransactionSuccessful();
564 endTransaction();
565 if (checkFullyYielded) {
566 if (this.isDbLockedByCurrentThread()) {
567 throw new IllegalStateException(
568 "Db locked more than once. yielfIfContended cannot yield");
569 }
570 }
Fred Quintana5c7aede2009-08-27 21:41:27 -0700571 if (sleepAfterYieldDelay > 0) {
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700572 // Sleep for up to sleepAfterYieldDelay milliseconds, waking up periodically to
573 // check if anyone is using the database. If the database is not contended,
574 // retake the lock and return.
575 long remainingDelay = sleepAfterYieldDelay;
576 while (remainingDelay > 0) {
577 try {
578 Thread.sleep(remainingDelay < SLEEP_AFTER_YIELD_QUANTUM ?
579 remainingDelay : SLEEP_AFTER_YIELD_QUANTUM);
580 } catch (InterruptedException e) {
581 Thread.interrupted();
582 }
583 remainingDelay -= SLEEP_AFTER_YIELD_QUANTUM;
584 if (mLock.getQueueLength() == 0) {
585 break;
586 }
Fred Quintana5c7aede2009-08-27 21:41:27 -0700587 }
588 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800589 beginTransaction();
590 return true;
591 }
592
593 /** Maps table names to info about what to which _sync_time column to set
594 * to NULL on an update. This is used to support syncing. */
595 private final Map<String, SyncUpdateInfo> mSyncUpdateInfo =
596 new HashMap<String, SyncUpdateInfo>();
597
598 public Map<String, String> getSyncedTables() {
599 synchronized(mSyncUpdateInfo) {
600 HashMap<String, String> tables = new HashMap<String, String>();
601 for (String table : mSyncUpdateInfo.keySet()) {
602 SyncUpdateInfo info = mSyncUpdateInfo.get(table);
603 if (info.deletedTable != null) {
604 tables.put(table, info.deletedTable);
605 }
606 }
607 return tables;
608 }
609 }
610
611 /**
612 * Internal class used to keep track what needs to be marked as changed
613 * when an update occurs. This is used for syncing, so the sync engine
614 * knows what data has been updated locally.
615 */
616 static private class SyncUpdateInfo {
617 /**
618 * Creates the SyncUpdateInfo class.
619 *
620 * @param masterTable The table to set _sync_time to NULL in
621 * @param deletedTable The deleted table that corresponds to the
622 * master table
623 * @param foreignKey The key that refers to the primary key in table
624 */
625 SyncUpdateInfo(String masterTable, String deletedTable,
626 String foreignKey) {
627 this.masterTable = masterTable;
628 this.deletedTable = deletedTable;
629 this.foreignKey = foreignKey;
630 }
631
632 /** The table containing the _sync_time column */
633 String masterTable;
634
635 /** The deleted table that corresponds to the master table */
636 String deletedTable;
637
638 /** The key in the local table the row in table. It may be _id, if table
639 * is the local table. */
640 String foreignKey;
641 }
642
643 /**
644 * Used to allow returning sub-classes of {@link Cursor} when calling query.
645 */
646 public interface CursorFactory {
647 /**
648 * See
649 * {@link SQLiteCursor#SQLiteCursor(SQLiteDatabase, SQLiteCursorDriver,
650 * String, SQLiteQuery)}.
651 */
652 public Cursor newCursor(SQLiteDatabase db,
653 SQLiteCursorDriver masterQuery, String editTable,
654 SQLiteQuery query);
655 }
656
657 /**
658 * Open the database according to the flags {@link #OPEN_READWRITE}
659 * {@link #OPEN_READONLY} {@link #CREATE_IF_NECESSARY} and/or {@link #NO_LOCALIZED_COLLATORS}.
660 *
661 * <p>Sets the locale of the database to the the system's current locale.
662 * Call {@link #setLocale} if you would like something else.</p>
663 *
664 * @param path to database file to open and/or create
665 * @param factory an optional factory class that is called to instantiate a
666 * cursor when query is called, or null for default
667 * @param flags to control database access mode
668 * @return the newly opened database
669 * @throws SQLiteException if the database cannot be opened
670 */
671 public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags) {
672 SQLiteDatabase db = null;
673 try {
674 // Open the database.
675 return new SQLiteDatabase(path, factory, flags);
676 } catch (SQLiteDatabaseCorruptException e) {
677 // Try to recover from this, if we can.
678 // TODO: should we do this for other open failures?
679 Log.e(TAG, "Deleting and re-creating corrupt database " + path, e);
680 new File(path).delete();
681 return new SQLiteDatabase(path, factory, flags);
682 }
683 }
684
685 /**
686 * Equivalent to openDatabase(file.getPath(), factory, CREATE_IF_NECESSARY).
687 */
688 public static SQLiteDatabase openOrCreateDatabase(File file, CursorFactory factory) {
689 return openOrCreateDatabase(file.getPath(), factory);
690 }
691
692 /**
693 * Equivalent to openDatabase(path, factory, CREATE_IF_NECESSARY).
694 */
695 public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory) {
696 return openDatabase(path, factory, CREATE_IF_NECESSARY);
697 }
698
699 /**
700 * Create a memory backed SQLite database. Its contents will be destroyed
701 * when the database is closed.
702 *
703 * <p>Sets the locale of the database to the the system's current locale.
704 * Call {@link #setLocale} if you would like something else.</p>
705 *
706 * @param factory an optional factory class that is called to instantiate a
707 * cursor when query is called
708 * @return a SQLiteDatabase object, or null if the database can't be created
709 */
710 public static SQLiteDatabase create(CursorFactory factory) {
711 // This is a magic string with special meaning for SQLite.
712 return openDatabase(":memory:", factory, CREATE_IF_NECESSARY);
713 }
714
715 /**
716 * Close the database.
717 */
718 public void close() {
719 lock();
720 try {
721 closeClosable();
722 releaseReference();
723 } finally {
724 unlock();
725 }
726 }
727
728 private void closeClosable() {
729 Iterator<Map.Entry<SQLiteClosable, Object>> iter = mPrograms.entrySet().iterator();
730 while (iter.hasNext()) {
731 Map.Entry<SQLiteClosable, Object> entry = iter.next();
732 SQLiteClosable program = entry.getKey();
733 if (program != null) {
734 program.onAllReferencesReleasedFromContainer();
735 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700736 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800737 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700738
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800739 /**
740 * Native call to close the database.
741 */
742 private native void dbclose();
743
744 /**
745 * Gets the database version.
746 *
747 * @return the database version
748 */
749 public int getVersion() {
750 SQLiteStatement prog = null;
751 lock();
752 try {
753 prog = new SQLiteStatement(this, "PRAGMA user_version;");
754 long version = prog.simpleQueryForLong();
755 return (int) version;
756 } finally {
757 if (prog != null) prog.close();
758 unlock();
759 }
760 }
761
762 /**
763 * Sets the database version.
764 *
765 * @param version the new database version
766 */
767 public void setVersion(int version) {
768 execSQL("PRAGMA user_version = " + version);
769 }
770
771 /**
772 * Returns the maximum size the database may grow to.
773 *
774 * @return the new maximum database size
775 */
776 public long getMaximumSize() {
777 SQLiteStatement prog = null;
778 lock();
779 try {
780 prog = new SQLiteStatement(this,
781 "PRAGMA max_page_count;");
782 long pageCount = prog.simpleQueryForLong();
783 return pageCount * getPageSize();
784 } finally {
785 if (prog != null) prog.close();
786 unlock();
787 }
788 }
789
790 /**
791 * Sets the maximum size the database will grow to. The maximum size cannot
792 * be set below the current size.
793 *
794 * @param numBytes the maximum database size, in bytes
795 * @return the new maximum database size
796 */
797 public long setMaximumSize(long numBytes) {
798 SQLiteStatement prog = null;
799 lock();
800 try {
801 long pageSize = getPageSize();
802 long numPages = numBytes / pageSize;
803 // If numBytes isn't a multiple of pageSize, bump up a page
804 if ((numBytes % pageSize) != 0) {
805 numPages++;
806 }
807 prog = new SQLiteStatement(this,
808 "PRAGMA max_page_count = " + numPages);
809 long newPageCount = prog.simpleQueryForLong();
810 return newPageCount * pageSize;
811 } finally {
812 if (prog != null) prog.close();
813 unlock();
814 }
815 }
816
817 /**
818 * Returns the current database page size, in bytes.
819 *
820 * @return the database page size, in bytes
821 */
822 public long getPageSize() {
823 SQLiteStatement prog = null;
824 lock();
825 try {
826 prog = new SQLiteStatement(this,
827 "PRAGMA page_size;");
828 long size = prog.simpleQueryForLong();
829 return size;
830 } finally {
831 if (prog != null) prog.close();
832 unlock();
833 }
834 }
835
836 /**
837 * Sets the database page size. The page size must be a power of two. This
838 * method does not work if any data has been written to the database file,
839 * and must be called right after the database has been created.
840 *
841 * @param numBytes the database page size, in bytes
842 */
843 public void setPageSize(long numBytes) {
844 execSQL("PRAGMA page_size = " + numBytes);
845 }
846
847 /**
848 * Mark this table as syncable. When an update occurs in this table the
849 * _sync_dirty field will be set to ensure proper syncing operation.
850 *
851 * @param table the table to mark as syncable
852 * @param deletedTable The deleted table that corresponds to the
853 * syncable table
854 */
855 public void markTableSyncable(String table, String deletedTable) {
856 markTableSyncable(table, "_id", table, deletedTable);
857 }
858
859 /**
860 * Mark this table as syncable, with the _sync_dirty residing in another
861 * table. When an update occurs in this table the _sync_dirty field of the
862 * row in updateTable with the _id in foreignKey will be set to
863 * ensure proper syncing operation.
864 *
865 * @param table an update on this table will trigger a sync time removal
866 * @param foreignKey this is the column in table whose value is an _id in
867 * updateTable
868 * @param updateTable this is the table that will have its _sync_dirty
869 */
870 public void markTableSyncable(String table, String foreignKey,
871 String updateTable) {
872 markTableSyncable(table, foreignKey, updateTable, null);
873 }
874
875 /**
876 * Mark this table as syncable, with the _sync_dirty residing in another
877 * table. When an update occurs in this table the _sync_dirty field of the
878 * row in updateTable with the _id in foreignKey will be set to
879 * ensure proper syncing operation.
880 *
881 * @param table an update on this table will trigger a sync time removal
882 * @param foreignKey this is the column in table whose value is an _id in
883 * updateTable
884 * @param updateTable this is the table that will have its _sync_dirty
885 * @param deletedTable The deleted table that corresponds to the
886 * updateTable
887 */
888 private void markTableSyncable(String table, String foreignKey,
889 String updateTable, String deletedTable) {
890 lock();
891 try {
892 native_execSQL("SELECT _sync_dirty FROM " + updateTable
893 + " LIMIT 0");
894 native_execSQL("SELECT " + foreignKey + " FROM " + table
895 + " LIMIT 0");
896 } finally {
897 unlock();
898 }
899
900 SyncUpdateInfo info = new SyncUpdateInfo(updateTable, deletedTable,
901 foreignKey);
902 synchronized (mSyncUpdateInfo) {
903 mSyncUpdateInfo.put(table, info);
904 }
905 }
906
907 /**
908 * Call for each row that is updated in a cursor.
909 *
910 * @param table the table the row is in
911 * @param rowId the row ID of the updated row
912 */
913 /* package */ void rowUpdated(String table, long rowId) {
914 SyncUpdateInfo info;
915 synchronized (mSyncUpdateInfo) {
916 info = mSyncUpdateInfo.get(table);
917 }
918 if (info != null) {
919 execSQL("UPDATE " + info.masterTable
920 + " SET _sync_dirty=1 WHERE _id=(SELECT " + info.foreignKey
921 + " FROM " + table + " WHERE _id=" + rowId + ")");
922 }
923 }
924
925 /**
926 * Finds the name of the first table, which is editable.
927 *
928 * @param tables a list of tables
929 * @return the first table listed
930 */
931 public static String findEditTable(String tables) {
932 if (!TextUtils.isEmpty(tables)) {
933 // find the first word terminated by either a space or a comma
934 int spacepos = tables.indexOf(' ');
935 int commapos = tables.indexOf(',');
936
937 if (spacepos > 0 && (spacepos < commapos || commapos < 0)) {
938 return tables.substring(0, spacepos);
939 } else if (commapos > 0 && (commapos < spacepos || spacepos < 0) ) {
940 return tables.substring(0, commapos);
941 }
942 return tables;
943 } else {
944 throw new IllegalStateException("Invalid tables");
945 }
946 }
947
948 /**
949 * Compiles an SQL statement into a reusable pre-compiled statement object.
950 * The parameters are identical to {@link #execSQL(String)}. You may put ?s in the
951 * statement and fill in those values with {@link SQLiteProgram#bindString}
952 * and {@link SQLiteProgram#bindLong} each time you want to run the
953 * statement. Statements may not return result sets larger than 1x1.
954 *
955 * @param sql The raw SQL statement, may contain ? for unknown values to be
956 * bound later.
957 * @return a pre-compiled statement object.
958 */
959 public SQLiteStatement compileStatement(String sql) throws SQLException {
960 lock();
961 try {
962 return new SQLiteStatement(this, sql);
963 } finally {
964 unlock();
965 }
966 }
967
968 /**
969 * Query the given URL, returning a {@link Cursor} over the result set.
970 *
971 * @param distinct true if you want each row to be unique, false otherwise.
972 * @param table The table name to compile the query against.
973 * @param columns A list of which columns to return. Passing null will
974 * return all columns, which is discouraged to prevent reading
975 * data from storage that isn't going to be used.
976 * @param selection A filter declaring which rows to return, formatted as an
977 * SQL WHERE clause (excluding the WHERE itself). Passing null
978 * will return all rows for the given table.
979 * @param selectionArgs You may include ?s in selection, which will be
980 * replaced by the values from selectionArgs, in order that they
981 * appear in the selection. The values will be bound as Strings.
982 * @param groupBy A filter declaring how to group rows, formatted as an SQL
983 * GROUP BY clause (excluding the GROUP BY itself). Passing null
984 * will cause the rows to not be grouped.
985 * @param having A filter declare which row groups to include in the cursor,
986 * if row grouping is being used, formatted as an SQL HAVING
987 * clause (excluding the HAVING itself). Passing null will cause
988 * all row groups to be included, and is required when row
989 * grouping is not being used.
990 * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
991 * (excluding the ORDER BY itself). Passing null will use the
992 * default sort order, which may be unordered.
993 * @param limit Limits the number of rows returned by the query,
994 * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
995 * @return A Cursor object, which is positioned before the first entry
996 * @see Cursor
997 */
998 public Cursor query(boolean distinct, String table, String[] columns,
999 String selection, String[] selectionArgs, String groupBy,
1000 String having, String orderBy, String limit) {
1001 return queryWithFactory(null, distinct, table, columns, selection, selectionArgs,
1002 groupBy, having, orderBy, limit);
1003 }
1004
1005 /**
1006 * Query the given URL, returning a {@link Cursor} over the result set.
1007 *
1008 * @param cursorFactory the cursor factory to use, or null for the default factory
1009 * @param distinct true if you want each row to be unique, false otherwise.
1010 * @param table The table name to compile the query against.
1011 * @param columns A list of which columns to return. Passing null will
1012 * return all columns, which is discouraged to prevent reading
1013 * data from storage that isn't going to be used.
1014 * @param selection A filter declaring which rows to return, formatted as an
1015 * SQL WHERE clause (excluding the WHERE itself). Passing null
1016 * will return all rows for the given table.
1017 * @param selectionArgs You may include ?s in selection, which will be
1018 * replaced by the values from selectionArgs, in order that they
1019 * appear in the selection. The values will be bound as Strings.
1020 * @param groupBy A filter declaring how to group rows, formatted as an SQL
1021 * GROUP BY clause (excluding the GROUP BY itself). Passing null
1022 * will cause the rows to not be grouped.
1023 * @param having A filter declare which row groups to include in the cursor,
1024 * if row grouping is being used, formatted as an SQL HAVING
1025 * clause (excluding the HAVING itself). Passing null will cause
1026 * all row groups to be included, and is required when row
1027 * grouping is not being used.
1028 * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
1029 * (excluding the ORDER BY itself). Passing null will use the
1030 * default sort order, which may be unordered.
1031 * @param limit Limits the number of rows returned by the query,
1032 * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
1033 * @return A Cursor object, which is positioned before the first entry
1034 * @see Cursor
1035 */
1036 public Cursor queryWithFactory(CursorFactory cursorFactory,
1037 boolean distinct, String table, String[] columns,
1038 String selection, String[] selectionArgs, String groupBy,
1039 String having, String orderBy, String limit) {
1040 String sql = SQLiteQueryBuilder.buildQueryString(
1041 distinct, table, columns, selection, groupBy, having, orderBy, limit);
1042
1043 return rawQueryWithFactory(
1044 cursorFactory, sql, selectionArgs, findEditTable(table));
1045 }
1046
1047 /**
1048 * Query the given table, returning a {@link Cursor} over the result set.
1049 *
1050 * @param table The table name to compile the query against.
1051 * @param columns A list of which columns to return. Passing null will
1052 * return all columns, which is discouraged to prevent reading
1053 * data from storage that isn't going to be used.
1054 * @param selection A filter declaring which rows to return, formatted as an
1055 * SQL WHERE clause (excluding the WHERE itself). Passing null
1056 * will return all rows for the given table.
1057 * @param selectionArgs You may include ?s in selection, which will be
1058 * replaced by the values from selectionArgs, in order that they
1059 * appear in the selection. The values will be bound as Strings.
1060 * @param groupBy A filter declaring how to group rows, formatted as an SQL
1061 * GROUP BY clause (excluding the GROUP BY itself). Passing null
1062 * will cause the rows to not be grouped.
1063 * @param having A filter declare which row groups to include in the cursor,
1064 * if row grouping is being used, formatted as an SQL HAVING
1065 * clause (excluding the HAVING itself). Passing null will cause
1066 * all row groups to be included, and is required when row
1067 * grouping is not being used.
1068 * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
1069 * (excluding the ORDER BY itself). Passing null will use the
1070 * default sort order, which may be unordered.
1071 * @return A {@link Cursor} object, which is positioned before the first entry
1072 * @see Cursor
1073 */
1074 public Cursor query(String table, String[] columns, String selection,
1075 String[] selectionArgs, String groupBy, String having,
1076 String orderBy) {
1077
1078 return query(false, table, columns, selection, selectionArgs, groupBy,
1079 having, orderBy, null /* limit */);
1080 }
1081
1082 /**
1083 * Query the given table, returning a {@link Cursor} over the result set.
1084 *
1085 * @param table The table name to compile the query against.
1086 * @param columns A list of which columns to return. Passing null will
1087 * return all columns, which is discouraged to prevent reading
1088 * data from storage that isn't going to be used.
1089 * @param selection A filter declaring which rows to return, formatted as an
1090 * SQL WHERE clause (excluding the WHERE itself). Passing null
1091 * will return all rows for the given table.
1092 * @param selectionArgs You may include ?s in selection, which will be
1093 * replaced by the values from selectionArgs, in order that they
1094 * appear in the selection. The values will be bound as Strings.
1095 * @param groupBy A filter declaring how to group rows, formatted as an SQL
1096 * GROUP BY clause (excluding the GROUP BY itself). Passing null
1097 * will cause the rows to not be grouped.
1098 * @param having A filter declare which row groups to include in the cursor,
1099 * if row grouping is being used, formatted as an SQL HAVING
1100 * clause (excluding the HAVING itself). Passing null will cause
1101 * all row groups to be included, and is required when row
1102 * grouping is not being used.
1103 * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
1104 * (excluding the ORDER BY itself). Passing null will use the
1105 * default sort order, which may be unordered.
1106 * @param limit Limits the number of rows returned by the query,
1107 * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
1108 * @return A {@link Cursor} object, which is positioned before the first entry
1109 * @see Cursor
1110 */
1111 public Cursor query(String table, String[] columns, String selection,
1112 String[] selectionArgs, String groupBy, String having,
1113 String orderBy, String limit) {
1114
1115 return query(false, table, columns, selection, selectionArgs, groupBy,
1116 having, orderBy, limit);
1117 }
1118
1119 /**
1120 * Runs the provided SQL and returns a {@link Cursor} over the result set.
1121 *
1122 * @param sql the SQL query. The SQL string must not be ; terminated
1123 * @param selectionArgs You may include ?s in where clause in the query,
1124 * which will be replaced by the values from selectionArgs. The
1125 * values will be bound as Strings.
1126 * @return A {@link Cursor} object, which is positioned before the first entry
1127 */
1128 public Cursor rawQuery(String sql, String[] selectionArgs) {
1129 return rawQueryWithFactory(null, sql, selectionArgs, null);
1130 }
1131
1132 /**
1133 * Runs the provided SQL and returns a cursor over the result set.
1134 *
1135 * @param cursorFactory the cursor factory to use, or null for the default factory
1136 * @param sql the SQL query. The SQL string must not be ; terminated
1137 * @param selectionArgs You may include ?s in where clause in the query,
1138 * which will be replaced by the values from selectionArgs. The
1139 * values will be bound as Strings.
1140 * @param editTable the name of the first table, which is editable
1141 * @return A {@link Cursor} object, which is positioned before the first entry
1142 */
1143 public Cursor rawQueryWithFactory(
1144 CursorFactory cursorFactory, String sql, String[] selectionArgs,
1145 String editTable) {
1146 long timeStart = 0;
1147
1148 if (Config.LOGV) {
1149 timeStart = System.currentTimeMillis();
1150 }
1151
1152 SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable);
1153
1154 try {
1155 return driver.query(
1156 cursorFactory != null ? cursorFactory : mFactory,
1157 selectionArgs);
1158 } finally {
1159 if (Config.LOGV) {
1160 long duration = System.currentTimeMillis() - timeStart;
1161
1162 Log.v(SQLiteCursor.TAG,
1163 "query (" + duration + " ms): " + driver.toString() + ", args are "
1164 + (selectionArgs != null
1165 ? TextUtils.join(",", selectionArgs)
1166 : "<null>"));
1167 }
1168 }
1169 }
1170
1171 /**
1172 * Runs the provided SQL and returns a cursor over the result set.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001173 * The cursor will read an initial set of rows and the return to the caller.
1174 * It will continue to read in batches and send data changed notifications
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001175 * when the later batches are ready.
1176 * @param sql the SQL query. The SQL string must not be ; terminated
1177 * @param selectionArgs You may include ?s in where clause in the query,
1178 * which will be replaced by the values from selectionArgs. The
1179 * values will be bound as Strings.
1180 * @param initialRead set the initial count of items to read from the cursor
1181 * @param maxRead set the count of items to read on each iteration after the first
1182 * @return A {@link Cursor} object, which is positioned before the first entry
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001183 *
Andy Stadlerf8a7cea2009-04-10 16:24:47 -07001184 * This work is incomplete and not fully tested or reviewed, so currently
1185 * hidden.
1186 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001187 */
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001188 public Cursor rawQuery(String sql, String[] selectionArgs,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001189 int initialRead, int maxRead) {
1190 SQLiteCursor c = (SQLiteCursor)rawQueryWithFactory(
1191 null, sql, selectionArgs, null);
1192 c.setLoadStyle(initialRead, maxRead);
1193 return c;
1194 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001195
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001196 /**
1197 * Convenience method for inserting a row into the database.
1198 *
1199 * @param table the table to insert the row into
1200 * @param nullColumnHack SQL doesn't allow inserting a completely empty row,
1201 * so if initialValues is empty this column will explicitly be
1202 * assigned a NULL value
1203 * @param values this map contains the initial column values for the
1204 * row. The keys should be the column names and the values the
1205 * column values
1206 * @return the row ID of the newly inserted row, or -1 if an error occurred
1207 */
1208 public long insert(String table, String nullColumnHack, ContentValues values) {
1209 try {
1210 return insertWithOnConflict(table, nullColumnHack, values, null);
1211 } catch (SQLException e) {
1212 Log.e(TAG, "Error inserting " + values, e);
1213 return -1;
1214 }
1215 }
1216
1217 /**
1218 * Convenience method for inserting a row into the database.
1219 *
1220 * @param table the table to insert the row into
1221 * @param nullColumnHack SQL doesn't allow inserting a completely empty row,
1222 * so if initialValues is empty this column will explicitly be
1223 * assigned a NULL value
1224 * @param values this map contains the initial column values for the
1225 * row. The keys should be the column names and the values the
1226 * column values
1227 * @throws SQLException
1228 * @return the row ID of the newly inserted row, or -1 if an error occurred
1229 */
1230 public long insertOrThrow(String table, String nullColumnHack, ContentValues values)
1231 throws SQLException {
1232 return insertWithOnConflict(table, nullColumnHack, values, null);
1233 }
1234
1235 /**
1236 * Convenience method for replacing a row in the database.
1237 *
1238 * @param table the table in which to replace the row
1239 * @param nullColumnHack SQL doesn't allow inserting a completely empty row,
1240 * so if initialValues is empty this row will explicitly be
1241 * assigned a NULL value
1242 * @param initialValues this map contains the initial column values for
1243 * the row. The key
1244 * @return the row ID of the newly inserted row, or -1 if an error occurred
1245 */
1246 public long replace(String table, String nullColumnHack, ContentValues initialValues) {
1247 try {
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001248 return insertWithOnConflict(table, nullColumnHack, initialValues,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001249 ConflictAlgorithm.REPLACE);
1250 } catch (SQLException e) {
1251 Log.e(TAG, "Error inserting " + initialValues, e);
1252 return -1;
1253 }
1254 }
1255
1256 /**
1257 * Convenience method for replacing a row in the database.
1258 *
1259 * @param table the table in which to replace the row
1260 * @param nullColumnHack SQL doesn't allow inserting a completely empty row,
1261 * so if initialValues is empty this row will explicitly be
1262 * assigned a NULL value
1263 * @param initialValues this map contains the initial column values for
1264 * the row. The key
1265 * @throws SQLException
1266 * @return the row ID of the newly inserted row, or -1 if an error occurred
1267 */
1268 public long replaceOrThrow(String table, String nullColumnHack,
1269 ContentValues initialValues) throws SQLException {
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001270 return insertWithOnConflict(table, nullColumnHack, initialValues,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001271 ConflictAlgorithm.REPLACE);
1272 }
1273
1274 /**
1275 * General method for inserting a row into the database.
1276 *
1277 * @param table the table to insert the row into
1278 * @param nullColumnHack SQL doesn't allow inserting a completely empty row,
1279 * so if initialValues is empty this column will explicitly be
1280 * assigned a NULL value
1281 * @param initialValues this map contains the initial column values for the
1282 * row. The keys should be the column names and the values the
1283 * column values
1284 * @param algorithm {@link ConflictAlgorithm} for insert conflict resolver
1285 * @return the row ID of the newly inserted row, or -1 if an error occurred
1286 * @hide
1287 */
1288 public long insertWithOnConflict(String table, String nullColumnHack,
1289 ContentValues initialValues, ConflictAlgorithm algorithm) {
1290 if (!isOpen()) {
1291 throw new IllegalStateException("database not open");
1292 }
1293
1294 // Measurements show most sql lengths <= 152
1295 StringBuilder sql = new StringBuilder(152);
1296 sql.append("INSERT");
1297 if (algorithm != null) {
1298 sql.append(" OR ");
1299 sql.append(algorithm.value());
1300 }
1301 sql.append(" INTO ");
1302 sql.append(table);
1303 // Measurements show most values lengths < 40
1304 StringBuilder values = new StringBuilder(40);
1305
1306 Set<Map.Entry<String, Object>> entrySet = null;
1307 if (initialValues != null && initialValues.size() > 0) {
1308 entrySet = initialValues.valueSet();
1309 Iterator<Map.Entry<String, Object>> entriesIter = entrySet.iterator();
1310 sql.append('(');
1311
1312 boolean needSeparator = false;
1313 while (entriesIter.hasNext()) {
1314 if (needSeparator) {
1315 sql.append(", ");
1316 values.append(", ");
1317 }
1318 needSeparator = true;
1319 Map.Entry<String, Object> entry = entriesIter.next();
1320 sql.append(entry.getKey());
1321 values.append('?');
1322 }
1323
1324 sql.append(')');
1325 } else {
1326 sql.append("(" + nullColumnHack + ") ");
1327 values.append("NULL");
1328 }
1329
1330 sql.append(" VALUES(");
1331 sql.append(values);
1332 sql.append(");");
1333
1334 lock();
1335 SQLiteStatement statement = null;
1336 try {
1337 statement = compileStatement(sql.toString());
1338
1339 // Bind the values
1340 if (entrySet != null) {
1341 int size = entrySet.size();
1342 Iterator<Map.Entry<String, Object>> entriesIter = entrySet.iterator();
1343 for (int i = 0; i < size; i++) {
1344 Map.Entry<String, Object> entry = entriesIter.next();
1345 DatabaseUtils.bindObjectToProgram(statement, i + 1, entry.getValue());
1346 }
1347 }
1348
1349 // Run the program and then cleanup
1350 statement.execute();
1351
1352 long insertedRowId = lastInsertRow();
1353 if (insertedRowId == -1) {
1354 Log.e(TAG, "Error inserting " + initialValues + " using " + sql);
1355 } else {
1356 if (Config.LOGD && Log.isLoggable(TAG, Log.VERBOSE)) {
1357 Log.v(TAG, "Inserting row " + insertedRowId + " from "
1358 + initialValues + " using " + sql);
1359 }
1360 }
1361 return insertedRowId;
1362 } catch (SQLiteDatabaseCorruptException e) {
1363 onCorruption();
1364 throw e;
1365 } finally {
1366 if (statement != null) {
1367 statement.close();
1368 }
1369 unlock();
1370 }
1371 }
1372
1373 /**
1374 * Convenience method for deleting rows in the database.
1375 *
1376 * @param table the table to delete from
1377 * @param whereClause the optional WHERE clause to apply when deleting.
1378 * Passing null will delete all rows.
1379 * @return the number of rows affected if a whereClause is passed in, 0
1380 * otherwise. To remove all rows and get a count pass "1" as the
1381 * whereClause.
1382 */
1383 public int delete(String table, String whereClause, String[] whereArgs) {
1384 if (!isOpen()) {
1385 throw new IllegalStateException("database not open");
1386 }
1387 lock();
1388 SQLiteStatement statement = null;
1389 try {
1390 statement = compileStatement("DELETE FROM " + table
1391 + (!TextUtils.isEmpty(whereClause)
1392 ? " WHERE " + whereClause : ""));
1393 if (whereArgs != null) {
1394 int numArgs = whereArgs.length;
1395 for (int i = 0; i < numArgs; i++) {
1396 DatabaseUtils.bindObjectToProgram(statement, i + 1, whereArgs[i]);
1397 }
1398 }
1399 statement.execute();
1400 statement.close();
1401 return lastChangeCount();
1402 } catch (SQLiteDatabaseCorruptException e) {
1403 onCorruption();
1404 throw e;
1405 } finally {
1406 if (statement != null) {
1407 statement.close();
1408 }
1409 unlock();
1410 }
1411 }
1412
1413 /**
1414 * Convenience method for updating rows in the database.
1415 *
1416 * @param table the table to update in
1417 * @param values a map from column names to new column values. null is a
1418 * valid value that will be translated to NULL.
1419 * @param whereClause the optional WHERE clause to apply when updating.
1420 * Passing null will update all rows.
1421 * @return the number of rows affected
1422 */
1423 public int update(String table, ContentValues values, String whereClause, String[] whereArgs) {
1424 return updateWithOnConflict(table, values, whereClause, whereArgs, null);
1425 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001426
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001427 /**
1428 * Convenience method for updating rows in the database.
1429 *
1430 * @param table the table to update in
1431 * @param values a map from column names to new column values. null is a
1432 * valid value that will be translated to NULL.
1433 * @param whereClause the optional WHERE clause to apply when updating.
1434 * Passing null will update all rows.
1435 * @param algorithm {@link ConflictAlgorithm} for update conflict resolver
1436 * @return the number of rows affected
1437 * @hide
1438 */
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001439 public int updateWithOnConflict(String table, ContentValues values,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001440 String whereClause, String[] whereArgs, ConflictAlgorithm algorithm) {
1441 if (!isOpen()) {
1442 throw new IllegalStateException("database not open");
1443 }
1444
1445 if (values == null || values.size() == 0) {
1446 throw new IllegalArgumentException("Empty values");
1447 }
1448
1449 StringBuilder sql = new StringBuilder(120);
1450 sql.append("UPDATE ");
1451 if (algorithm != null) {
Bjorn Bringert7f4c2ea2009-07-22 12:49:17 +01001452 sql.append("OR ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001453 sql.append(algorithm.value());
Bjorn Bringert7f4c2ea2009-07-22 12:49:17 +01001454 sql.append(" ");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001455 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001456
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001457 sql.append(table);
1458 sql.append(" SET ");
1459
1460 Set<Map.Entry<String, Object>> entrySet = values.valueSet();
1461 Iterator<Map.Entry<String, Object>> entriesIter = entrySet.iterator();
1462
1463 while (entriesIter.hasNext()) {
1464 Map.Entry<String, Object> entry = entriesIter.next();
1465 sql.append(entry.getKey());
1466 sql.append("=?");
1467 if (entriesIter.hasNext()) {
1468 sql.append(", ");
1469 }
1470 }
1471
1472 if (!TextUtils.isEmpty(whereClause)) {
1473 sql.append(" WHERE ");
1474 sql.append(whereClause);
1475 }
1476
1477 lock();
1478 SQLiteStatement statement = null;
1479 try {
1480 statement = compileStatement(sql.toString());
1481
1482 // Bind the values
1483 int size = entrySet.size();
1484 entriesIter = entrySet.iterator();
1485 int bindArg = 1;
1486 for (int i = 0; i < size; i++) {
1487 Map.Entry<String, Object> entry = entriesIter.next();
1488 DatabaseUtils.bindObjectToProgram(statement, bindArg, entry.getValue());
1489 bindArg++;
1490 }
1491
1492 if (whereArgs != null) {
1493 size = whereArgs.length;
1494 for (int i = 0; i < size; i++) {
1495 statement.bindString(bindArg, whereArgs[i]);
1496 bindArg++;
1497 }
1498 }
1499
1500 // Run the program and then cleanup
1501 statement.execute();
1502 statement.close();
1503 int numChangedRows = lastChangeCount();
1504 if (Config.LOGD && Log.isLoggable(TAG, Log.VERBOSE)) {
1505 Log.v(TAG, "Updated " + numChangedRows + " using " + values + " and " + sql);
1506 }
1507 return numChangedRows;
1508 } catch (SQLiteDatabaseCorruptException e) {
1509 onCorruption();
1510 throw e;
1511 } catch (SQLException e) {
1512 Log.e(TAG, "Error updating " + values + " using " + sql);
1513 throw e;
1514 } finally {
1515 if (statement != null) {
1516 statement.close();
1517 }
1518 unlock();
1519 }
1520 }
1521
1522 /**
1523 * Execute a single SQL statement that is not a query. For example, CREATE
1524 * TABLE, DELETE, INSERT, etc. Multiple statements separated by ;s are not
1525 * supported. it takes a write lock
1526 *
1527 * @throws SQLException If the SQL string is invalid for some reason
1528 */
1529 public void execSQL(String sql) throws SQLException {
1530 boolean logStats = mLogStats;
1531 long timeStart = logStats ? SystemClock.elapsedRealtime() : 0;
1532 lock();
1533 try {
1534 native_execSQL(sql);
1535 } catch (SQLiteDatabaseCorruptException e) {
1536 onCorruption();
1537 throw e;
1538 } finally {
1539 unlock();
1540 }
1541 if (logStats) {
1542 logTimeStat(false /* not a read */, timeStart, SystemClock.elapsedRealtime());
1543 }
1544 }
1545
1546 /**
1547 * Execute a single SQL statement that is not a query. For example, CREATE
1548 * TABLE, DELETE, INSERT, etc. Multiple statements separated by ;s are not
1549 * supported. it takes a write lock,
1550 *
1551 * @param sql
1552 * @param bindArgs only byte[], String, Long and Double are supported in bindArgs.
1553 * @throws SQLException If the SQL string is invalid for some reason
1554 */
1555 public void execSQL(String sql, Object[] bindArgs) throws SQLException {
1556 if (bindArgs == null) {
1557 throw new IllegalArgumentException("Empty bindArgs");
1558 }
1559
1560 boolean logStats = mLogStats;
1561 long timeStart = logStats ? SystemClock.elapsedRealtime() : 0;
1562 lock();
1563 SQLiteStatement statement = null;
1564 try {
1565 statement = compileStatement(sql);
1566 if (bindArgs != null) {
1567 int numArgs = bindArgs.length;
1568 for (int i = 0; i < numArgs; i++) {
1569 DatabaseUtils.bindObjectToProgram(statement, i + 1, bindArgs[i]);
1570 }
1571 }
1572 statement.execute();
1573 } catch (SQLiteDatabaseCorruptException e) {
1574 onCorruption();
1575 throw e;
1576 } finally {
1577 if (statement != null) {
1578 statement.close();
1579 }
1580 unlock();
1581 }
1582 if (logStats) {
1583 logTimeStat(false /* not a read */, timeStart, SystemClock.elapsedRealtime());
1584 }
1585 }
1586
1587 @Override
1588 protected void finalize() {
1589 if (isOpen()) {
1590 if (mPrograms.isEmpty()) {
1591 Log.e(TAG, "Leak found", mLeakedException);
1592 } else {
1593 IllegalStateException leakProgram = new IllegalStateException(
1594 "mPrograms size " + mPrograms.size(), mLeakedException);
1595 Log.e(TAG, "Leak found", leakProgram);
1596 }
1597 closeClosable();
1598 onAllReferencesReleased();
1599 }
1600 }
1601
1602 /**
1603 * Private constructor. See {@link #create} and {@link #openDatabase}.
1604 *
1605 * @param path The full path to the database
1606 * @param factory The factory to use when creating cursors, may be NULL.
1607 * @param flags 0 or {@link #NO_LOCALIZED_COLLATORS}. If the database file already
1608 * exists, mFlags will be updated appropriately.
1609 */
1610 private SQLiteDatabase(String path, CursorFactory factory, int flags) {
1611 if (path == null) {
1612 throw new IllegalArgumentException("path should not be null");
1613 }
1614 mFlags = flags;
1615 mPath = path;
1616 mLogStats = "1".equals(android.os.SystemProperties.get("db.logstats"));
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001617
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001618 mLeakedException = new IllegalStateException(path +
1619 " SQLiteDatabase created and never closed");
1620 mFactory = factory;
1621 dbopen(mPath, mFlags);
1622 mPrograms = new WeakHashMap<SQLiteClosable,Object>();
1623 try {
1624 setLocale(Locale.getDefault());
1625 } catch (RuntimeException e) {
1626 Log.e(TAG, "Failed to setLocale() when constructing, closing the database", e);
1627 dbclose();
1628 throw e;
1629 }
1630 }
1631
1632 /**
1633 * return whether the DB is opened as read only.
1634 * @return true if DB is opened as read only
1635 */
1636 public boolean isReadOnly() {
1637 return (mFlags & OPEN_READ_MASK) == OPEN_READONLY;
1638 }
1639
1640 /**
1641 * @return true if the DB is currently open (has not been closed)
1642 */
1643 public boolean isOpen() {
1644 return mNativeHandle != 0;
1645 }
1646
1647 public boolean needUpgrade(int newVersion) {
1648 return newVersion > getVersion();
1649 }
1650
1651 /**
1652 * Getter for the path to the database file.
1653 *
1654 * @return the path to our database file.
1655 */
1656 public final String getPath() {
1657 return mPath;
1658 }
1659
1660 /* package */ void logTimeStat(boolean read, long begin, long end) {
1661 EventLog.writeEvent(DB_OPERATION_EVENT, mPath, read ? 0 : 1, end - begin);
1662 }
1663
1664 /**
1665 * Sets the locale for this database. Does nothing if this database has
1666 * the NO_LOCALIZED_COLLATORS flag set or was opened read only.
1667 * @throws SQLException if the locale could not be set. The most common reason
1668 * for this is that there is no collator available for the locale you requested.
1669 * In this case the database remains unchanged.
1670 */
1671 public void setLocale(Locale locale) {
1672 lock();
1673 try {
1674 native_setLocale(locale.toString(), mFlags);
1675 } finally {
1676 unlock();
1677 }
1678 }
1679
1680 /**
1681 * Native call to open the database.
1682 *
1683 * @param path The full path to the database
1684 */
1685 private native void dbopen(String path, int flags);
1686
1687 /**
1688 * Native call to execute a raw SQL statement. {@link #lock} must be held
1689 * when calling this method.
1690 *
1691 * @param sql The raw SQL string
1692 * @throws SQLException
1693 */
1694 /* package */ native void native_execSQL(String sql) throws SQLException;
1695
1696 /**
1697 * Native call to set the locale. {@link #lock} must be held when calling
1698 * this method.
1699 * @throws SQLException
1700 */
1701 /* package */ native void native_setLocale(String loc, int flags);
1702
1703 /**
1704 * Returns the row ID of the last row inserted into the database.
1705 *
1706 * @return the row ID of the last row inserted into the database.
1707 */
1708 /* package */ native long lastInsertRow();
1709
1710 /**
1711 * Returns the number of changes made in the last statement executed.
1712 *
1713 * @return the number of changes made in the last statement executed.
1714 */
1715 /* package */ native int lastChangeCount();
1716}