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