blob: 93f13afb33a6f9b3ff3b487111feb35c6e777c4f [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.database.sqlite;
18
Vasu Nori5a03f362009-10-20 15:16:35 -070019import com.google.android.collect.Maps;
20
Dan Egnor12311952009-11-23 14:47:45 -080021import android.app.ActivityThread;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.content.ContentValues;
23import android.database.Cursor;
Vasu Nori062fc7ce2010-03-31 16:13:05 -070024import android.database.DatabaseErrorHandler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.database.DatabaseUtils;
Vasu Nori062fc7ce2010-03-31 16:13:05 -070026import android.database.DefaultDatabaseErrorHandler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.database.SQLException;
Vasu Noric3849202010-03-09 10:47:25 -080028import android.database.sqlite.SQLiteDebug.DbStats;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.os.Debug;
30import android.os.SystemClock;
Dmitri Plotnikov90142c92009-09-15 10:52:17 -070031import android.os.SystemProperties;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.text.TextUtils;
33import android.util.Config;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.util.EventLog;
Dmitri Plotnikov90142c92009-09-15 10:52:17 -070035import android.util.Log;
Vasu Noric3849202010-03-09 10:47:25 -080036import android.util.Pair;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037
38import java.io.File;
Vasu Noric3849202010-03-09 10:47:25 -080039import java.lang.ref.WeakReference;
Vasu Nori5a03f362009-10-20 15:16:35 -070040import java.text.SimpleDateFormat;
Vasu Noric3849202010-03-09 10:47:25 -080041import java.util.ArrayList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import java.util.HashMap;
Vasu Noric3849202010-03-09 10:47:25 -080043import java.util.HashSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044import java.util.Iterator;
45import java.util.Locale;
46import java.util.Map;
Dan Egnor12311952009-11-23 14:47:45 -080047import java.util.Random;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import java.util.Set;
49import java.util.WeakHashMap;
50import java.util.concurrent.locks.ReentrantLock;
Brad Fitzpatrickd8330232010-02-19 10:59:01 -080051import java.util.regex.Pattern;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052
53/**
54 * Exposes methods to manage a SQLite database.
55 * <p>SQLiteDatabase has methods to create, delete, execute SQL commands, and
56 * perform other common database management tasks.
57 * <p>See the Notepad sample application in the SDK for an example of creating
58 * and managing a database.
59 * <p> Database names must be unique within an application, not across all
60 * applications.
61 *
62 * <h3>Localized Collation - ORDER BY</h3>
63 * <p>In addition to SQLite's default <code>BINARY</code> collator, Android supplies
64 * two more, <code>LOCALIZED</code>, which changes with the system's current locale
65 * if you wire it up correctly (XXX a link needed!), and <code>UNICODE</code>, which
66 * is the Unicode Collation Algorithm and not tailored to the current locale.
67 */
68public class SQLiteDatabase extends SQLiteClosable {
69 private static final String TAG = "Database";
Jeff Hamilton082c2af2009-09-29 11:49:51 -070070 private static final int EVENT_DB_OPERATION = 52000;
71 private static final int EVENT_DB_CORRUPT = 75004;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072
73 /**
74 * Algorithms used in ON CONFLICT clause
75 * http://www.sqlite.org/lang_conflict.html
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076 */
Vasu Nori8d45e4e2010-02-05 22:35:47 -080077 /**
78 * When a constraint violation occurs, an immediate ROLLBACK occurs,
79 * thus ending the current transaction, and the command aborts with a
80 * return code of SQLITE_CONSTRAINT. If no transaction is active
81 * (other than the implied transaction that is created on every command)
82 * then this algorithm works the same as ABORT.
83 */
84 public static final int CONFLICT_ROLLBACK = 1;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -070085
Vasu Nori8d45e4e2010-02-05 22:35:47 -080086 /**
87 * When a constraint violation occurs,no ROLLBACK is executed
88 * so changes from prior commands within the same transaction
89 * are preserved. This is the default behavior.
90 */
91 public static final int CONFLICT_ABORT = 2;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -070092
Vasu Nori8d45e4e2010-02-05 22:35:47 -080093 /**
94 * When a constraint violation occurs, the command aborts with a return
95 * code SQLITE_CONSTRAINT. But any changes to the database that
96 * the command made prior to encountering the constraint violation
97 * are preserved and are not backed out.
98 */
99 public static final int CONFLICT_FAIL = 3;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700100
Vasu Nori8d45e4e2010-02-05 22:35:47 -0800101 /**
102 * When a constraint violation occurs, the one row that contains
103 * the constraint violation is not inserted or changed.
104 * But the command continues executing normally. Other rows before and
105 * after the row that contained the constraint violation continue to be
106 * inserted or updated normally. No error is returned.
107 */
108 public static final int CONFLICT_IGNORE = 4;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700109
Vasu Nori8d45e4e2010-02-05 22:35:47 -0800110 /**
111 * When a UNIQUE constraint violation occurs, the pre-existing rows that
112 * are causing the constraint violation are removed prior to inserting
113 * or updating the current row. Thus the insert or update always occurs.
114 * The command continues executing normally. No error is returned.
115 * If a NOT NULL constraint violation occurs, the NULL value is replaced
116 * by the default value for that column. If the column has no default
117 * value, then the ABORT algorithm is used. If a CHECK constraint
118 * violation occurs then the IGNORE algorithm is used. When this conflict
119 * resolution strategy deletes rows in order to satisfy a constraint,
120 * it does not invoke delete triggers on those rows.
121 * This behavior might change in a future release.
122 */
123 public static final int CONFLICT_REPLACE = 5;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700124
Vasu Nori8d45e4e2010-02-05 22:35:47 -0800125 /**
126 * use the following when no conflict action is specified.
127 */
128 public static final int CONFLICT_NONE = 0;
129 private static final String[] CONFLICT_VALUES = new String[]
130 {"", " OR ROLLBACK ", " OR ABORT ", " OR FAIL ", " OR IGNORE ", " OR REPLACE "};
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700131
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132 /**
133 * Maximum Length Of A LIKE Or GLOB Pattern
134 * The pattern matching algorithm used in the default LIKE and GLOB implementation
135 * of SQLite can exhibit O(N^2) performance (where N is the number of characters in
136 * the pattern) for certain pathological cases. To avoid denial-of-service attacks
137 * the length of the LIKE or GLOB pattern is limited to SQLITE_MAX_LIKE_PATTERN_LENGTH bytes.
138 * The default value of this limit is 50000. A modern workstation can evaluate
139 * even a pathological LIKE or GLOB pattern of 50000 bytes relatively quickly.
140 * The denial of service problem only comes into play when the pattern length gets
141 * into millions of bytes. Nevertheless, since most useful LIKE or GLOB patterns
142 * are at most a few dozen bytes in length, paranoid application developers may
143 * want to reduce this parameter to something in the range of a few hundred
144 * if they know that external users are able to generate arbitrary patterns.
145 */
146 public static final int SQLITE_MAX_LIKE_PATTERN_LENGTH = 50000;
147
148 /**
149 * Flag for {@link #openDatabase} to open the database for reading and writing.
150 * If the disk is full, this may fail even before you actually write anything.
151 *
152 * {@more} Note that the value of this flag is 0, so it is the default.
153 */
154 public static final int OPEN_READWRITE = 0x00000000; // update native code if changing
155
156 /**
157 * Flag for {@link #openDatabase} to open the database for reading only.
158 * This is the only reliable way to open a database if the disk may be full.
159 */
160 public static final int OPEN_READONLY = 0x00000001; // update native code if changing
161
162 private static final int OPEN_READ_MASK = 0x00000001; // update native code if changing
163
164 /**
165 * Flag for {@link #openDatabase} to open the database without support for localized collators.
166 *
167 * {@more} This causes the collator <code>LOCALIZED</code> not to be created.
168 * You must be consistent when using this flag to use the setting the database was
169 * created with. If this is set, {@link #setLocale} will do nothing.
170 */
171 public static final int NO_LOCALIZED_COLLATORS = 0x00000010; // update native code if changing
172
173 /**
174 * Flag for {@link #openDatabase} to create the database file if it does not already exist.
175 */
176 public static final int CREATE_IF_NECESSARY = 0x10000000; // update native code if changing
177
178 /**
179 * Indicates whether the most-recently started transaction has been marked as successful.
180 */
181 private boolean mInnerTransactionIsSuccessful;
182
183 /**
184 * Valid during the life of a transaction, and indicates whether the entire transaction (the
185 * outer one and all of the inner ones) so far has been successful.
186 */
187 private boolean mTransactionIsSuccessful;
188
Fred Quintanac4516a72009-09-03 12:14:06 -0700189 /**
190 * Valid during the life of a transaction.
191 */
192 private SQLiteTransactionListener mTransactionListener;
193
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800194 /** Synchronize on this when accessing the database */
195 private final ReentrantLock mLock = new ReentrantLock(true);
196
197 private long mLockAcquiredWallTime = 0L;
198 private long mLockAcquiredThreadTime = 0L;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700199
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800200 // limit the frequency of complaints about each database to one within 20 sec
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700201 // unless run command adb shell setprop log.tag.Database VERBOSE
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 private static final int LOCK_WARNING_WINDOW_IN_MS = 20000;
203 /** If the lock is held this long then a warning will be printed when it is released. */
204 private static final int LOCK_ACQUIRED_WARNING_TIME_IN_MS = 300;
205 private static final int LOCK_ACQUIRED_WARNING_THREAD_TIME_IN_MS = 100;
206 private static final int LOCK_ACQUIRED_WARNING_TIME_IN_MS_ALWAYS_PRINT = 2000;
207
Dmitri Plotnikovb43b58d2009-09-09 18:10:42 -0700208 private static final int SLEEP_AFTER_YIELD_QUANTUM = 1000;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700209
Brad Fitzpatrickd8330232010-02-19 10:59:01 -0800210 // The pattern we remove from database filenames before
211 // potentially logging them.
212 private static final Pattern EMAIL_IN_DB_PATTERN = Pattern.compile("[\\w\\.\\-]+@[\\w\\.\\-]+");
213
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 private long mLastLockMessageTime = 0L;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700215
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -0800216 // Things related to query logging/sampling for debugging
217 // slow/frequent queries during development. Always log queries
Brad Fitzpatrick722802e2010-03-23 22:22:16 -0700218 // which take (by default) 500ms+; shorter queries are sampled
219 // accordingly. Commit statements, which are typically slow, are
220 // logged together with the most recently executed SQL statement,
221 // for disambiguation. The 500ms value is configurable via a
222 // SystemProperty, but developers actively debugging database I/O
223 // should probably use the regular log tunable,
224 // LOG_SLOW_QUERIES_PROPERTY, defined below.
225 private static int sQueryLogTimeInMillis = 0; // lazily initialized
Dan Egnor12311952009-11-23 14:47:45 -0800226 private static final int QUERY_LOG_SQL_LENGTH = 64;
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -0800227 private static final String COMMIT_SQL = "COMMIT;";
Dan Egnor12311952009-11-23 14:47:45 -0800228 private final Random mRandom = new Random();
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -0800229 private String mLastSqlStatement = null;
Dan Egnor12311952009-11-23 14:47:45 -0800230
Brad Fitzpatrick722802e2010-03-23 22:22:16 -0700231 // String prefix for slow database query EventLog records that show
232 // lock acquistions of the database.
233 /* package */ static final String GET_LOCK_LOG_PREFIX = "GETLOCK:";
234
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800235 /** Used by native code, do not rename */
236 /* package */ int mNativeHandle = 0;
237
238 /** Used to make temp table names unique */
239 /* package */ int mTempTableSequence = 0;
240
241 /** The path for the database file */
242 private String mPath;
243
Brad Fitzpatrickd8330232010-02-19 10:59:01 -0800244 /** The anonymized path for the database file for logging purposes */
245 private String mPathForLogs = null; // lazily populated
246
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800247 /** The flags passed to open/create */
248 private int mFlags;
249
250 /** The optional factory to use when creating new Cursors */
251 private CursorFactory mFactory;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700252
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800253 private WeakHashMap<SQLiteClosable, Object> mPrograms;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700254
Vasu Nori5a03f362009-10-20 15:16:35 -0700255 /**
256 * for each instance of this class, a cache is maintained to store
257 * the compiled query statement ids returned by sqlite database.
258 * key = sql statement with "?" for bind args
259 * value = {@link SQLiteCompiledSql}
260 * If an application opens the database and keeps it open during its entire life, then
261 * there will not be an overhead of compilation of sql statements by sqlite.
262 *
263 * why is this cache NOT static? because sqlite attaches compiledsql statements to the
264 * struct created when {@link SQLiteDatabase#openDatabase(String, CursorFactory, int)} is
265 * invoked.
266 *
267 * this cache has an upper limit of mMaxSqlCacheSize (settable by calling the method
268 * (@link setMaxCacheSize(int)}). its default is 0 - i.e., no caching by default because
269 * most of the apps don't use "?" syntax in their sql, caching is not useful for them.
270 */
Vasu Norie495d1f2010-01-06 16:34:19 -0800271 /* package */ Map<String, SQLiteCompiledSql> mCompiledQueries = Maps.newHashMap();
272 /**
Vasu Nori90a367262010-04-12 12:49:09 -0700273 * absolute max value that can set by {@link #setMaxSqlCacheSize(int)}
274 * size of each prepared-statement is between 1K - 6K, depending on the complexity of the
275 * sql statement & schema.
Vasu Norie495d1f2010-01-06 16:34:19 -0800276 */
Vasu Nori90a367262010-04-12 12:49:09 -0700277 public static final int MAX_SQL_CACHE_SIZE = 100;
278
279 // default statement-cache size per database connection ( = instance of this class)
280 private int mMaxSqlCacheSize = 25;
281
Vasu Norie9d92102010-01-20 15:07:26 -0800282 private int mCacheFullWarnings;
Vasu Nori49d02ac2010-03-05 21:49:30 -0800283 private static final int MAX_WARNINGS_ON_CACHESIZE_CONDITION = 1;
Vasu Nori5a03f362009-10-20 15:16:35 -0700284
285 /** maintain stats about number of cache hits and misses */
286 private int mNumCacheHits;
287 private int mNumCacheMisses;
288
289 /** the following 2 members maintain the time when a database is opened and closed */
290 private String mTimeOpened = null;
291 private String mTimeClosed = null;
292
Vasu Norid606b4b2010-02-24 12:54:20 -0800293 /** Used to find out where this object was created in case it never got closed. */
294 private Throwable mStackTrace = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800295
Dmitri Plotnikov90142c92009-09-15 10:52:17 -0700296 // System property that enables logging of slow queries. Specify the threshold in ms.
297 private static final String LOG_SLOW_QUERIES_PROPERTY = "db.log.slow_query_threshold";
298 private final int mSlowQueryThreshold;
299
Vasu Nori062fc7ce2010-03-31 16:13:05 -0700300 /** {@link DatabaseErrorHandler} to be used when SQLite returns any of the following errors
301 * Corruption
302 * */
303 private DatabaseErrorHandler errorHandler;
304
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800305 /**
306 * @param closable
307 */
308 void addSQLiteClosable(SQLiteClosable closable) {
309 lock();
310 try {
311 mPrograms.put(closable, null);
312 } finally {
313 unlock();
314 }
315 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700316
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800317 void removeSQLiteClosable(SQLiteClosable closable) {
318 lock();
319 try {
320 mPrograms.remove(closable);
321 } finally {
322 unlock();
323 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700324 }
325
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800326 @Override
327 protected void onAllReferencesReleased() {
328 if (isOpen()) {
Vasu Nori5a03f362009-10-20 15:16:35 -0700329 if (SQLiteDebug.DEBUG_SQL_CACHE) {
330 mTimeClosed = getTime();
331 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800332 dbclose();
333 }
334 }
335
336 /**
337 * Attempts to release memory that SQLite holds but does not require to
338 * operate properly. Typically this memory will come from the page cache.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700339 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340 * @return the number of bytes actually released
341 */
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700342 static public native int releaseMemory();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343
344 /**
345 * Control whether or not the SQLiteDatabase is made thread-safe by using locks
346 * around critical sections. This is pretty expensive, so if you know that your
347 * DB will only be used by a single thread then you should set this to false.
348 * The default is true.
349 * @param lockingEnabled set to true to enable locks, false otherwise
350 */
351 public void setLockingEnabled(boolean lockingEnabled) {
352 mLockingEnabled = lockingEnabled;
353 }
354
355 /**
356 * If set then the SQLiteDatabase is made thread-safe by using locks
357 * around critical sections
358 */
359 private boolean mLockingEnabled = true;
360
361 /* package */ void onCorruption() {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700362 EventLog.writeEvent(EVENT_DB_CORRUPT, mPath);
Vasu Nori062fc7ce2010-03-31 16:13:05 -0700363 errorHandler.onCorruption(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364 }
365
366 /**
367 * Locks the database for exclusive access. The database lock must be held when
368 * touch the native sqlite3* object since it is single threaded and uses
369 * a polling lock contention algorithm. The lock is recursive, and may be acquired
370 * multiple times by the same thread. This is a no-op if mLockingEnabled is false.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700371 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372 * @see #unlock()
373 */
374 /* package */ void lock() {
375 if (!mLockingEnabled) return;
376 mLock.lock();
377 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
378 if (mLock.getHoldCount() == 1) {
379 // Use elapsed real-time since the CPU may sleep when waiting for IO
380 mLockAcquiredWallTime = SystemClock.elapsedRealtime();
381 mLockAcquiredThreadTime = Debug.threadCpuTimeNanos();
382 }
383 }
384 }
385
386 /**
387 * Locks the database for exclusive access. The database lock must be held when
388 * touch the native sqlite3* object since it is single threaded and uses
389 * a polling lock contention algorithm. The lock is recursive, and may be acquired
390 * multiple times by the same thread.
391 *
392 * @see #unlockForced()
393 */
394 private void lockForced() {
395 mLock.lock();
396 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
397 if (mLock.getHoldCount() == 1) {
398 // Use elapsed real-time since the CPU may sleep when waiting for IO
399 mLockAcquiredWallTime = SystemClock.elapsedRealtime();
400 mLockAcquiredThreadTime = Debug.threadCpuTimeNanos();
401 }
402 }
403 }
404
405 /**
406 * Releases the database lock. This is a no-op if mLockingEnabled is false.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700407 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800408 * @see #unlock()
409 */
410 /* package */ void unlock() {
411 if (!mLockingEnabled) return;
412 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
413 if (mLock.getHoldCount() == 1) {
414 checkLockHoldTime();
415 }
416 }
417 mLock.unlock();
418 }
419
420 /**
421 * Releases the database lock.
422 *
423 * @see #unlockForced()
424 */
425 private void unlockForced() {
426 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
427 if (mLock.getHoldCount() == 1) {
428 checkLockHoldTime();
429 }
430 }
431 mLock.unlock();
432 }
433
434 private void checkLockHoldTime() {
435 // Use elapsed real-time since the CPU may sleep when waiting for IO
436 long elapsedTime = SystemClock.elapsedRealtime();
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700437 long lockedTime = elapsedTime - mLockAcquiredWallTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800438 if (lockedTime < LOCK_ACQUIRED_WARNING_TIME_IN_MS_ALWAYS_PRINT &&
439 !Log.isLoggable(TAG, Log.VERBOSE) &&
440 (elapsedTime - mLastLockMessageTime) < LOCK_WARNING_WINDOW_IN_MS) {
441 return;
442 }
443 if (lockedTime > LOCK_ACQUIRED_WARNING_TIME_IN_MS) {
444 int threadTime = (int)
445 ((Debug.threadCpuTimeNanos() - mLockAcquiredThreadTime) / 1000000);
446 if (threadTime > LOCK_ACQUIRED_WARNING_THREAD_TIME_IN_MS ||
447 lockedTime > LOCK_ACQUIRED_WARNING_TIME_IN_MS_ALWAYS_PRINT) {
448 mLastLockMessageTime = elapsedTime;
449 String msg = "lock held on " + mPath + " for " + lockedTime + "ms. Thread time was "
450 + threadTime + "ms";
451 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING_STACK_TRACE) {
452 Log.d(TAG, msg, new Exception());
453 } else {
454 Log.d(TAG, msg);
455 }
456 }
457 }
458 }
459
460 /**
461 * Begins a transaction. Transactions can be nested. When the outer transaction is ended all of
462 * the work done in that transaction and all of the nested transactions will be committed or
463 * rolled back. The changes will be rolled back if any transaction is ended without being
464 * marked as clean (by calling setTransactionSuccessful). Otherwise they will be committed.
465 *
466 * <p>Here is the standard idiom for transactions:
467 *
468 * <pre>
469 * db.beginTransaction();
470 * try {
471 * ...
472 * db.setTransactionSuccessful();
473 * } finally {
474 * db.endTransaction();
475 * }
476 * </pre>
477 */
478 public void beginTransaction() {
Fred Quintanac4516a72009-09-03 12:14:06 -0700479 beginTransactionWithListener(null /* transactionStatusCallback */);
480 }
481
482 /**
483 * Begins a transaction. Transactions can be nested. When the outer transaction is ended all of
484 * the work done in that transaction and all of the nested transactions will be committed or
485 * rolled back. The changes will be rolled back if any transaction is ended without being
486 * marked as clean (by calling setTransactionSuccessful). Otherwise they will be committed.
487 *
488 * <p>Here is the standard idiom for transactions:
489 *
490 * <pre>
491 * db.beginTransactionWithListener(listener);
492 * try {
493 * ...
494 * db.setTransactionSuccessful();
495 * } finally {
496 * db.endTransaction();
497 * }
498 * </pre>
499 * @param transactionListener listener that should be notified when the transaction begins,
500 * commits, or is rolled back, either explicitly or by a call to
501 * {@link #yieldIfContendedSafely}.
502 */
503 public void beginTransactionWithListener(SQLiteTransactionListener transactionListener) {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700504 if (!isOpen()) {
505 throw new IllegalStateException("database not open");
506 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800507 lockForced();
508 boolean ok = false;
509 try {
510 // If this thread already had the lock then get out
511 if (mLock.getHoldCount() > 1) {
512 if (mInnerTransactionIsSuccessful) {
513 String msg = "Cannot call beginTransaction between "
514 + "calling setTransactionSuccessful and endTransaction";
515 IllegalStateException e = new IllegalStateException(msg);
516 Log.e(TAG, "beginTransaction() failed", e);
517 throw e;
518 }
519 ok = true;
520 return;
521 }
522
523 // This thread didn't already have the lock, so begin a database
524 // transaction now.
525 execSQL("BEGIN EXCLUSIVE;");
Fred Quintanac4516a72009-09-03 12:14:06 -0700526 mTransactionListener = transactionListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800527 mTransactionIsSuccessful = true;
528 mInnerTransactionIsSuccessful = false;
Fred Quintanac4516a72009-09-03 12:14:06 -0700529 if (transactionListener != null) {
530 try {
531 transactionListener.onBegin();
532 } catch (RuntimeException e) {
533 execSQL("ROLLBACK;");
534 throw e;
535 }
536 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800537 ok = true;
538 } finally {
539 if (!ok) {
540 // beginTransaction is called before the try block so we must release the lock in
541 // the case of failure.
542 unlockForced();
543 }
544 }
545 }
546
547 /**
548 * End a transaction. See beginTransaction for notes about how to use this and when transactions
549 * are committed and rolled back.
550 */
551 public void endTransaction() {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700552 if (!isOpen()) {
553 throw new IllegalStateException("database not open");
554 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800555 if (!mLock.isHeldByCurrentThread()) {
556 throw new IllegalStateException("no transaction pending");
557 }
558 try {
559 if (mInnerTransactionIsSuccessful) {
560 mInnerTransactionIsSuccessful = false;
561 } else {
562 mTransactionIsSuccessful = false;
563 }
564 if (mLock.getHoldCount() != 1) {
565 return;
566 }
Fred Quintanac4516a72009-09-03 12:14:06 -0700567 RuntimeException savedException = null;
568 if (mTransactionListener != null) {
569 try {
570 if (mTransactionIsSuccessful) {
571 mTransactionListener.onCommit();
572 } else {
573 mTransactionListener.onRollback();
574 }
575 } catch (RuntimeException e) {
576 savedException = e;
577 mTransactionIsSuccessful = false;
578 }
579 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800580 if (mTransactionIsSuccessful) {
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -0800581 execSQL(COMMIT_SQL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800582 } else {
583 try {
584 execSQL("ROLLBACK;");
Fred Quintanac4516a72009-09-03 12:14:06 -0700585 if (savedException != null) {
586 throw savedException;
587 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800588 } catch (SQLException e) {
589 if (Config.LOGD) {
590 Log.d(TAG, "exception during rollback, maybe the DB previously "
591 + "performed an auto-rollback");
592 }
593 }
594 }
595 } finally {
Fred Quintanac4516a72009-09-03 12:14:06 -0700596 mTransactionListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800597 unlockForced();
598 if (Config.LOGV) {
599 Log.v(TAG, "unlocked " + Thread.currentThread()
600 + ", holdCount is " + mLock.getHoldCount());
601 }
602 }
603 }
604
605 /**
606 * Marks the current transaction as successful. Do not do any more database work between
607 * calling this and calling endTransaction. Do as little non-database work as possible in that
608 * situation too. If any errors are encountered between this and endTransaction the transaction
609 * will still be committed.
610 *
611 * @throws IllegalStateException if the current thread is not in a transaction or the
612 * transaction is already marked as successful.
613 */
614 public void setTransactionSuccessful() {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700615 if (!isOpen()) {
616 throw new IllegalStateException("database not open");
617 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800618 if (!mLock.isHeldByCurrentThread()) {
619 throw new IllegalStateException("no transaction pending");
620 }
621 if (mInnerTransactionIsSuccessful) {
622 throw new IllegalStateException(
623 "setTransactionSuccessful may only be called once per call to beginTransaction");
624 }
625 mInnerTransactionIsSuccessful = true;
626 }
627
628 /**
629 * return true if there is a transaction pending
630 */
631 public boolean inTransaction() {
632 return mLock.getHoldCount() > 0;
633 }
634
635 /**
636 * Checks if the database lock is held by this thread.
637 *
638 * @return true, if this thread is holding the database lock.
639 */
640 public boolean isDbLockedByCurrentThread() {
641 return mLock.isHeldByCurrentThread();
642 }
643
644 /**
645 * Checks if the database is locked by another thread. This is
646 * just an estimate, since this status can change at any time,
647 * including after the call is made but before the result has
648 * been acted upon.
649 *
650 * @return true, if the database is locked by another thread
651 */
652 public boolean isDbLockedByOtherThreads() {
653 return !mLock.isHeldByCurrentThread() && mLock.isLocked();
654 }
655
656 /**
657 * Temporarily end the transaction to let other threads run. The transaction is assumed to be
658 * successful so far. Do not call setTransactionSuccessful before calling this. When this
659 * returns a new transaction will have been created but not marked as successful.
660 * @return true if the transaction was yielded
661 * @deprecated if the db is locked more than once (becuase of nested transactions) then the lock
662 * will not be yielded. Use yieldIfContendedSafely instead.
663 */
Dianne Hackborn4a51c202009-08-21 15:14:02 -0700664 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800665 public boolean yieldIfContended() {
Fred Quintana5c7aede2009-08-27 21:41:27 -0700666 return yieldIfContendedHelper(false /* do not check yielding */,
667 -1 /* sleepAfterYieldDelay */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800668 }
669
670 /**
671 * Temporarily end the transaction to let other threads run. The transaction is assumed to be
672 * successful so far. Do not call setTransactionSuccessful before calling this. When this
673 * returns a new transaction will have been created but not marked as successful. This assumes
674 * that there are no nested transactions (beginTransaction has only been called once) and will
Fred Quintana5c7aede2009-08-27 21:41:27 -0700675 * throw an exception if that is not the case.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800676 * @return true if the transaction was yielded
677 */
678 public boolean yieldIfContendedSafely() {
Fred Quintana5c7aede2009-08-27 21:41:27 -0700679 return yieldIfContendedHelper(true /* check yielding */, -1 /* sleepAfterYieldDelay*/);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800680 }
681
Fred Quintana5c7aede2009-08-27 21:41:27 -0700682 /**
683 * Temporarily end the transaction to let other threads run. The transaction is assumed to be
684 * successful so far. Do not call setTransactionSuccessful before calling this. When this
685 * returns a new transaction will have been created but not marked as successful. This assumes
686 * that there are no nested transactions (beginTransaction has only been called once) and will
687 * throw an exception if that is not the case.
688 * @param sleepAfterYieldDelay if > 0, sleep this long before starting a new transaction if
689 * the lock was actually yielded. This will allow other background threads to make some
690 * more progress than they would if we started the transaction immediately.
691 * @return true if the transaction was yielded
692 */
693 public boolean yieldIfContendedSafely(long sleepAfterYieldDelay) {
694 return yieldIfContendedHelper(true /* check yielding */, sleepAfterYieldDelay);
695 }
696
697 private boolean yieldIfContendedHelper(boolean checkFullyYielded, long sleepAfterYieldDelay) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800698 if (mLock.getQueueLength() == 0) {
699 // Reset the lock acquire time since we know that the thread was willing to yield
700 // the lock at this time.
701 mLockAcquiredWallTime = SystemClock.elapsedRealtime();
702 mLockAcquiredThreadTime = Debug.threadCpuTimeNanos();
703 return false;
704 }
705 setTransactionSuccessful();
Fred Quintanac4516a72009-09-03 12:14:06 -0700706 SQLiteTransactionListener transactionListener = mTransactionListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800707 endTransaction();
708 if (checkFullyYielded) {
709 if (this.isDbLockedByCurrentThread()) {
710 throw new IllegalStateException(
711 "Db locked more than once. yielfIfContended cannot yield");
712 }
713 }
Fred Quintana5c7aede2009-08-27 21:41:27 -0700714 if (sleepAfterYieldDelay > 0) {
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700715 // Sleep for up to sleepAfterYieldDelay milliseconds, waking up periodically to
716 // check if anyone is using the database. If the database is not contended,
717 // retake the lock and return.
718 long remainingDelay = sleepAfterYieldDelay;
719 while (remainingDelay > 0) {
720 try {
721 Thread.sleep(remainingDelay < SLEEP_AFTER_YIELD_QUANTUM ?
722 remainingDelay : SLEEP_AFTER_YIELD_QUANTUM);
723 } catch (InterruptedException e) {
724 Thread.interrupted();
725 }
726 remainingDelay -= SLEEP_AFTER_YIELD_QUANTUM;
727 if (mLock.getQueueLength() == 0) {
728 break;
729 }
Fred Quintana5c7aede2009-08-27 21:41:27 -0700730 }
731 }
Fred Quintanac4516a72009-09-03 12:14:06 -0700732 beginTransactionWithListener(transactionListener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800733 return true;
734 }
735
736 /** Maps table names to info about what to which _sync_time column to set
737 * to NULL on an update. This is used to support syncing. */
738 private final Map<String, SyncUpdateInfo> mSyncUpdateInfo =
739 new HashMap<String, SyncUpdateInfo>();
740
741 public Map<String, String> getSyncedTables() {
742 synchronized(mSyncUpdateInfo) {
743 HashMap<String, String> tables = new HashMap<String, String>();
744 for (String table : mSyncUpdateInfo.keySet()) {
745 SyncUpdateInfo info = mSyncUpdateInfo.get(table);
746 if (info.deletedTable != null) {
747 tables.put(table, info.deletedTable);
748 }
749 }
750 return tables;
751 }
752 }
753
754 /**
755 * Internal class used to keep track what needs to be marked as changed
756 * when an update occurs. This is used for syncing, so the sync engine
757 * knows what data has been updated locally.
758 */
759 static private class SyncUpdateInfo {
760 /**
761 * Creates the SyncUpdateInfo class.
762 *
763 * @param masterTable The table to set _sync_time to NULL in
764 * @param deletedTable The deleted table that corresponds to the
765 * master table
766 * @param foreignKey The key that refers to the primary key in table
767 */
768 SyncUpdateInfo(String masterTable, String deletedTable,
769 String foreignKey) {
770 this.masterTable = masterTable;
771 this.deletedTable = deletedTable;
772 this.foreignKey = foreignKey;
773 }
774
775 /** The table containing the _sync_time column */
776 String masterTable;
777
778 /** The deleted table that corresponds to the master table */
779 String deletedTable;
780
781 /** The key in the local table the row in table. It may be _id, if table
782 * is the local table. */
783 String foreignKey;
784 }
785
786 /**
787 * Used to allow returning sub-classes of {@link Cursor} when calling query.
788 */
789 public interface CursorFactory {
790 /**
791 * See
792 * {@link SQLiteCursor#SQLiteCursor(SQLiteDatabase, SQLiteCursorDriver,
793 * String, SQLiteQuery)}.
794 */
795 public Cursor newCursor(SQLiteDatabase db,
796 SQLiteCursorDriver masterQuery, String editTable,
797 SQLiteQuery query);
798 }
799
800 /**
801 * Open the database according to the flags {@link #OPEN_READWRITE}
802 * {@link #OPEN_READONLY} {@link #CREATE_IF_NECESSARY} and/or {@link #NO_LOCALIZED_COLLATORS}.
803 *
804 * <p>Sets the locale of the database to the the system's current locale.
805 * Call {@link #setLocale} if you would like something else.</p>
806 *
807 * @param path to database file to open and/or create
808 * @param factory an optional factory class that is called to instantiate a
809 * cursor when query is called, or null for default
810 * @param flags to control database access mode
811 * @return the newly opened database
812 * @throws SQLiteException if the database cannot be opened
813 */
814 public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags) {
Vasu Nori062fc7ce2010-03-31 16:13:05 -0700815 return openDatabase(path, factory, flags, new DefaultDatabaseErrorHandler());
816 }
817
818 /**
819 * same as {@link #openDatabase(String, CursorFactory, int)} except for an additional param
820 * errorHandler.
821 * @param errorHandler the {@link DatabaseErrorHandler} obj to be used when database
822 * corruption is detected on the database.
823 */
824 public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags,
825 DatabaseErrorHandler errorHandler) {
826 SQLiteDatabase sqliteDatabase = new SQLiteDatabase(path, factory, flags);
827
828 // set the ErrorHandler to be used when SQLite reports exceptions
829 sqliteDatabase.errorHandler = errorHandler;
830
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800831 try {
832 // Open the database.
Vasu Nori062fc7ce2010-03-31 16:13:05 -0700833 sqliteDatabase.openDatabase(path, flags);
Vasu Nori3ef94e22010-02-05 14:49:04 -0800834 if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
835 sqliteDatabase.enableSqlTracing(path);
836 }
837 if (SQLiteDebug.DEBUG_SQL_TIME) {
838 sqliteDatabase.enableSqlProfiling(path);
839 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800840 } catch (SQLiteDatabaseCorruptException e) {
Vasu Nori062fc7ce2010-03-31 16:13:05 -0700841 // Database is not even openable.
842 errorHandler.onCorruption(sqliteDatabase);
Vasu Noric3849202010-03-09 10:47:25 -0800843 sqliteDatabase = new SQLiteDatabase(path, factory, flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800844 }
Vasu Noric3849202010-03-09 10:47:25 -0800845 ActiveDatabases.getInstance().mActiveDatabases.add(
846 new WeakReference<SQLiteDatabase>(sqliteDatabase));
847 return sqliteDatabase;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800848 }
849
Vasu Nori062fc7ce2010-03-31 16:13:05 -0700850 private void openDatabase(String path, int flags) {
851 // Open the database.
852 dbopen(path, flags);
853 try {
854 setLocale(Locale.getDefault());
855 } catch (RuntimeException e) {
856 Log.e(TAG, "Failed to setLocale(). closing the database", e);
857 dbclose();
858 throw e;
859 }
860 }
861
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800862 /**
863 * Equivalent to openDatabase(file.getPath(), factory, CREATE_IF_NECESSARY).
864 */
865 public static SQLiteDatabase openOrCreateDatabase(File file, CursorFactory factory) {
866 return openOrCreateDatabase(file.getPath(), factory);
867 }
868
869 /**
870 * Equivalent to openDatabase(path, factory, CREATE_IF_NECESSARY).
871 */
872 public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory) {
873 return openDatabase(path, factory, CREATE_IF_NECESSARY);
874 }
875
876 /**
Vasu Nori062fc7ce2010-03-31 16:13:05 -0700877 * same as {@link #openOrCreateDatabase(String, CursorFactory)} except for an additional param
878 * errorHandler.
879 * @param errorHandler the {@link DatabaseErrorHandler} obj to be used when database
880 * corruption is detected on the database.
881 */
882 public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory,
883 DatabaseErrorHandler errorHandler) {
884 return openDatabase(path, factory, CREATE_IF_NECESSARY, errorHandler);
885 }
886
887 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800888 * Create a memory backed SQLite database. Its contents will be destroyed
889 * when the database is closed.
890 *
891 * <p>Sets the locale of the database to the the system's current locale.
892 * Call {@link #setLocale} if you would like something else.</p>
893 *
894 * @param factory an optional factory class that is called to instantiate a
895 * cursor when query is called
896 * @return a SQLiteDatabase object, or null if the database can't be created
897 */
898 public static SQLiteDatabase create(CursorFactory factory) {
899 // This is a magic string with special meaning for SQLite.
900 return openDatabase(":memory:", factory, CREATE_IF_NECESSARY);
901 }
902
903 /**
904 * Close the database.
905 */
906 public void close() {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700907 if (!isOpen()) {
908 return; // already closed
909 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800910 lock();
911 try {
912 closeClosable();
Vasu Norif6373e92010-03-16 10:21:00 -0700913 // close this database instance - regardless of its reference count value
914 onAllReferencesReleased();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800915 } finally {
916 unlock();
917 }
918 }
919
920 private void closeClosable() {
Vasu Norie495d1f2010-01-06 16:34:19 -0800921 /* deallocate all compiled sql statement objects from mCompiledQueries cache.
922 * this should be done before de-referencing all {@link SQLiteClosable} objects
923 * from this database object because calling
924 * {@link SQLiteClosable#onAllReferencesReleasedFromContainer()} could cause the database
925 * to be closed. sqlite doesn't let a database close if there are
926 * any unfinalized statements - such as the compiled-sql objects in mCompiledQueries.
927 */
928 deallocCachedSqlStatements();
929
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800930 Iterator<Map.Entry<SQLiteClosable, Object>> iter = mPrograms.entrySet().iterator();
931 while (iter.hasNext()) {
932 Map.Entry<SQLiteClosable, Object> entry = iter.next();
933 SQLiteClosable program = entry.getKey();
934 if (program != null) {
935 program.onAllReferencesReleasedFromContainer();
936 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700937 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800938 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700939
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800940 /**
941 * Native call to close the database.
942 */
943 private native void dbclose();
944
945 /**
946 * Gets the database version.
947 *
948 * @return the database version
949 */
950 public int getVersion() {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700951 if (!isOpen()) {
952 throw new IllegalStateException("database not open");
953 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800954 SQLiteStatement prog = null;
955 lock();
956 try {
957 prog = new SQLiteStatement(this, "PRAGMA user_version;");
958 long version = prog.simpleQueryForLong();
959 return (int) version;
960 } finally {
961 if (prog != null) prog.close();
962 unlock();
963 }
964 }
965
966 /**
967 * Sets the database version.
968 *
969 * @param version the new database version
970 */
971 public void setVersion(int version) {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700972 if (!isOpen()) {
973 throw new IllegalStateException("database not open");
974 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800975 execSQL("PRAGMA user_version = " + version);
976 }
977
978 /**
979 * Returns the maximum size the database may grow to.
980 *
981 * @return the new maximum database size
982 */
983 public long getMaximumSize() {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700984 if (!isOpen()) {
985 throw new IllegalStateException("database not open");
986 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800987 SQLiteStatement prog = null;
988 lock();
989 try {
990 prog = new SQLiteStatement(this,
991 "PRAGMA max_page_count;");
992 long pageCount = prog.simpleQueryForLong();
993 return pageCount * getPageSize();
994 } finally {
995 if (prog != null) prog.close();
996 unlock();
997 }
998 }
999
1000 /**
1001 * Sets the maximum size the database will grow to. The maximum size cannot
1002 * be set below the current size.
1003 *
1004 * @param numBytes the maximum database size, in bytes
1005 * @return the new maximum database size
1006 */
1007 public long setMaximumSize(long numBytes) {
Vasu Norif3cf8a42010-03-23 11:41:44 -07001008 if (!isOpen()) {
1009 throw new IllegalStateException("database not open");
1010 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001011 SQLiteStatement prog = null;
1012 lock();
1013 try {
1014 long pageSize = getPageSize();
1015 long numPages = numBytes / pageSize;
1016 // If numBytes isn't a multiple of pageSize, bump up a page
1017 if ((numBytes % pageSize) != 0) {
1018 numPages++;
1019 }
1020 prog = new SQLiteStatement(this,
1021 "PRAGMA max_page_count = " + numPages);
1022 long newPageCount = prog.simpleQueryForLong();
1023 return newPageCount * pageSize;
1024 } finally {
1025 if (prog != null) prog.close();
1026 unlock();
1027 }
1028 }
1029
1030 /**
1031 * Returns the current database page size, in bytes.
1032 *
1033 * @return the database page size, in bytes
1034 */
1035 public long getPageSize() {
Vasu Norif3cf8a42010-03-23 11:41:44 -07001036 if (!isOpen()) {
1037 throw new IllegalStateException("database not open");
1038 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039 SQLiteStatement prog = null;
1040 lock();
1041 try {
1042 prog = new SQLiteStatement(this,
1043 "PRAGMA page_size;");
1044 long size = prog.simpleQueryForLong();
1045 return size;
1046 } finally {
1047 if (prog != null) prog.close();
1048 unlock();
1049 }
1050 }
1051
1052 /**
1053 * Sets the database page size. The page size must be a power of two. This
1054 * method does not work if any data has been written to the database file,
1055 * and must be called right after the database has been created.
1056 *
1057 * @param numBytes the database page size, in bytes
1058 */
1059 public void setPageSize(long numBytes) {
Vasu Norif3cf8a42010-03-23 11:41:44 -07001060 if (!isOpen()) {
1061 throw new IllegalStateException("database not open");
1062 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001063 execSQL("PRAGMA page_size = " + numBytes);
1064 }
1065
1066 /**
1067 * Mark this table as syncable. When an update occurs in this table the
1068 * _sync_dirty field will be set to ensure proper syncing operation.
1069 *
1070 * @param table the table to mark as syncable
1071 * @param deletedTable The deleted table that corresponds to the
1072 * syncable table
1073 */
1074 public void markTableSyncable(String table, String deletedTable) {
1075 markTableSyncable(table, "_id", table, deletedTable);
1076 }
1077
1078 /**
1079 * Mark this table as syncable, with the _sync_dirty residing in another
1080 * table. When an update occurs in this table the _sync_dirty field of the
1081 * row in updateTable with the _id in foreignKey will be set to
1082 * ensure proper syncing operation.
1083 *
1084 * @param table an update on this table will trigger a sync time removal
1085 * @param foreignKey this is the column in table whose value is an _id in
1086 * updateTable
1087 * @param updateTable this is the table that will have its _sync_dirty
1088 */
1089 public void markTableSyncable(String table, String foreignKey,
1090 String updateTable) {
1091 markTableSyncable(table, foreignKey, updateTable, null);
1092 }
1093
1094 /**
1095 * Mark this table as syncable, with the _sync_dirty residing in another
1096 * table. When an update occurs in this table the _sync_dirty field of the
1097 * row in updateTable with the _id in foreignKey will be set to
1098 * ensure proper syncing operation.
1099 *
1100 * @param table an update on this table will trigger a sync time removal
1101 * @param foreignKey this is the column in table whose value is an _id in
1102 * updateTable
1103 * @param updateTable this is the table that will have its _sync_dirty
1104 * @param deletedTable The deleted table that corresponds to the
1105 * updateTable
1106 */
1107 private void markTableSyncable(String table, String foreignKey,
1108 String updateTable, String deletedTable) {
1109 lock();
1110 try {
1111 native_execSQL("SELECT _sync_dirty FROM " + updateTable
1112 + " LIMIT 0");
1113 native_execSQL("SELECT " + foreignKey + " FROM " + table
1114 + " LIMIT 0");
1115 } finally {
1116 unlock();
1117 }
1118
1119 SyncUpdateInfo info = new SyncUpdateInfo(updateTable, deletedTable,
1120 foreignKey);
1121 synchronized (mSyncUpdateInfo) {
1122 mSyncUpdateInfo.put(table, info);
1123 }
1124 }
1125
1126 /**
1127 * Call for each row that is updated in a cursor.
1128 *
1129 * @param table the table the row is in
1130 * @param rowId the row ID of the updated row
1131 */
1132 /* package */ void rowUpdated(String table, long rowId) {
1133 SyncUpdateInfo info;
1134 synchronized (mSyncUpdateInfo) {
1135 info = mSyncUpdateInfo.get(table);
1136 }
1137 if (info != null) {
1138 execSQL("UPDATE " + info.masterTable
1139 + " SET _sync_dirty=1 WHERE _id=(SELECT " + info.foreignKey
1140 + " FROM " + table + " WHERE _id=" + rowId + ")");
1141 }
1142 }
1143
1144 /**
1145 * Finds the name of the first table, which is editable.
1146 *
1147 * @param tables a list of tables
1148 * @return the first table listed
1149 */
1150 public static String findEditTable(String tables) {
1151 if (!TextUtils.isEmpty(tables)) {
1152 // find the first word terminated by either a space or a comma
1153 int spacepos = tables.indexOf(' ');
1154 int commapos = tables.indexOf(',');
1155
1156 if (spacepos > 0 && (spacepos < commapos || commapos < 0)) {
1157 return tables.substring(0, spacepos);
1158 } else if (commapos > 0 && (commapos < spacepos || spacepos < 0) ) {
1159 return tables.substring(0, commapos);
1160 }
1161 return tables;
1162 } else {
1163 throw new IllegalStateException("Invalid tables");
1164 }
1165 }
1166
1167 /**
1168 * Compiles an SQL statement into a reusable pre-compiled statement object.
1169 * The parameters are identical to {@link #execSQL(String)}. You may put ?s in the
1170 * statement and fill in those values with {@link SQLiteProgram#bindString}
1171 * and {@link SQLiteProgram#bindLong} each time you want to run the
1172 * statement. Statements may not return result sets larger than 1x1.
1173 *
1174 * @param sql The raw SQL statement, may contain ? for unknown values to be
1175 * bound later.
1176 * @return a pre-compiled statement object.
1177 */
1178 public SQLiteStatement compileStatement(String sql) throws SQLException {
Vasu Norif3cf8a42010-03-23 11:41:44 -07001179 if (!isOpen()) {
1180 throw new IllegalStateException("database not open");
1181 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001182 lock();
1183 try {
1184 return new SQLiteStatement(this, sql);
1185 } finally {
1186 unlock();
1187 }
1188 }
1189
1190 /**
1191 * Query the given URL, returning a {@link Cursor} over the result set.
1192 *
1193 * @param distinct true if you want each row to be unique, false otherwise.
1194 * @param table The table name to compile the query against.
1195 * @param columns A list of which columns to return. Passing null will
1196 * return all columns, which is discouraged to prevent reading
1197 * data from storage that isn't going to be used.
1198 * @param selection A filter declaring which rows to return, formatted as an
1199 * SQL WHERE clause (excluding the WHERE itself). Passing null
1200 * will return all rows for the given table.
1201 * @param selectionArgs You may include ?s in selection, which will be
1202 * replaced by the values from selectionArgs, in order that they
1203 * appear in the selection. The values will be bound as Strings.
1204 * @param groupBy A filter declaring how to group rows, formatted as an SQL
1205 * GROUP BY clause (excluding the GROUP BY itself). Passing null
1206 * will cause the rows to not be grouped.
1207 * @param having A filter declare which row groups to include in the cursor,
1208 * if row grouping is being used, formatted as an SQL HAVING
1209 * clause (excluding the HAVING itself). Passing null will cause
1210 * all row groups to be included, and is required when row
1211 * grouping is not being used.
1212 * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
1213 * (excluding the ORDER BY itself). Passing null will use the
1214 * default sort order, which may be unordered.
1215 * @param limit Limits the number of rows returned by the query,
1216 * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
1217 * @return A Cursor object, which is positioned before the first entry
1218 * @see Cursor
1219 */
1220 public Cursor query(boolean distinct, String table, String[] columns,
1221 String selection, String[] selectionArgs, String groupBy,
1222 String having, String orderBy, String limit) {
1223 return queryWithFactory(null, distinct, table, columns, selection, selectionArgs,
1224 groupBy, having, orderBy, limit);
1225 }
1226
1227 /**
1228 * Query the given URL, returning a {@link Cursor} over the result set.
1229 *
1230 * @param cursorFactory the cursor factory to use, or null for the default factory
1231 * @param distinct true if you want each row to be unique, false otherwise.
1232 * @param table The table name to compile the query against.
1233 * @param columns A list of which columns to return. Passing null will
1234 * return all columns, which is discouraged to prevent reading
1235 * data from storage that isn't going to be used.
1236 * @param selection A filter declaring which rows to return, formatted as an
1237 * SQL WHERE clause (excluding the WHERE itself). Passing null
1238 * will return all rows for the given table.
1239 * @param selectionArgs You may include ?s in selection, which will be
1240 * replaced by the values from selectionArgs, in order that they
1241 * appear in the selection. The values will be bound as Strings.
1242 * @param groupBy A filter declaring how to group rows, formatted as an SQL
1243 * GROUP BY clause (excluding the GROUP BY itself). Passing null
1244 * will cause the rows to not be grouped.
1245 * @param having A filter declare which row groups to include in the cursor,
1246 * if row grouping is being used, formatted as an SQL HAVING
1247 * clause (excluding the HAVING itself). Passing null will cause
1248 * all row groups to be included, and is required when row
1249 * grouping is not being used.
1250 * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
1251 * (excluding the ORDER BY itself). Passing null will use the
1252 * default sort order, which may be unordered.
1253 * @param limit Limits the number of rows returned by the query,
1254 * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
1255 * @return A Cursor object, which is positioned before the first entry
1256 * @see Cursor
1257 */
1258 public Cursor queryWithFactory(CursorFactory cursorFactory,
1259 boolean distinct, String table, String[] columns,
1260 String selection, String[] selectionArgs, String groupBy,
1261 String having, String orderBy, String limit) {
Vasu Norif3cf8a42010-03-23 11:41:44 -07001262 if (!isOpen()) {
1263 throw new IllegalStateException("database not open");
1264 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001265 String sql = SQLiteQueryBuilder.buildQueryString(
1266 distinct, table, columns, selection, groupBy, having, orderBy, limit);
1267
1268 return rawQueryWithFactory(
1269 cursorFactory, sql, selectionArgs, findEditTable(table));
1270 }
1271
1272 /**
1273 * Query the given table, returning a {@link Cursor} over the result set.
1274 *
1275 * @param table The table name to compile the query against.
1276 * @param columns A list of which columns to return. Passing null will
1277 * return all columns, which is discouraged to prevent reading
1278 * data from storage that isn't going to be used.
1279 * @param selection A filter declaring which rows to return, formatted as an
1280 * SQL WHERE clause (excluding the WHERE itself). Passing null
1281 * will return all rows for the given table.
1282 * @param selectionArgs You may include ?s in selection, which will be
1283 * replaced by the values from selectionArgs, in order that they
1284 * appear in the selection. The values will be bound as Strings.
1285 * @param groupBy A filter declaring how to group rows, formatted as an SQL
1286 * GROUP BY clause (excluding the GROUP BY itself). Passing null
1287 * will cause the rows to not be grouped.
1288 * @param having A filter declare which row groups to include in the cursor,
1289 * if row grouping is being used, formatted as an SQL HAVING
1290 * clause (excluding the HAVING itself). Passing null will cause
1291 * all row groups to be included, and is required when row
1292 * grouping is not being used.
1293 * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
1294 * (excluding the ORDER BY itself). Passing null will use the
1295 * default sort order, which may be unordered.
1296 * @return A {@link Cursor} object, which is positioned before the first entry
1297 * @see Cursor
1298 */
1299 public Cursor query(String table, String[] columns, String selection,
1300 String[] selectionArgs, String groupBy, String having,
1301 String orderBy) {
1302
1303 return query(false, table, columns, selection, selectionArgs, groupBy,
1304 having, orderBy, null /* limit */);
1305 }
1306
1307 /**
1308 * Query the given table, returning a {@link Cursor} over the result set.
1309 *
1310 * @param table The table name to compile the query against.
1311 * @param columns A list of which columns to return. Passing null will
1312 * return all columns, which is discouraged to prevent reading
1313 * data from storage that isn't going to be used.
1314 * @param selection A filter declaring which rows to return, formatted as an
1315 * SQL WHERE clause (excluding the WHERE itself). Passing null
1316 * will return all rows for the given table.
1317 * @param selectionArgs You may include ?s in selection, which will be
1318 * replaced by the values from selectionArgs, in order that they
1319 * appear in the selection. The values will be bound as Strings.
1320 * @param groupBy A filter declaring how to group rows, formatted as an SQL
1321 * GROUP BY clause (excluding the GROUP BY itself). Passing null
1322 * will cause the rows to not be grouped.
1323 * @param having A filter declare which row groups to include in the cursor,
1324 * if row grouping is being used, formatted as an SQL HAVING
1325 * clause (excluding the HAVING itself). Passing null will cause
1326 * all row groups to be included, and is required when row
1327 * grouping is not being used.
1328 * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
1329 * (excluding the ORDER BY itself). Passing null will use the
1330 * default sort order, which may be unordered.
1331 * @param limit Limits the number of rows returned by the query,
1332 * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
1333 * @return A {@link Cursor} object, which is positioned before the first entry
1334 * @see Cursor
1335 */
1336 public Cursor query(String table, String[] columns, String selection,
1337 String[] selectionArgs, String groupBy, String having,
1338 String orderBy, String limit) {
1339
1340 return query(false, table, columns, selection, selectionArgs, groupBy,
1341 having, orderBy, limit);
1342 }
1343
1344 /**
1345 * Runs the provided SQL and returns a {@link Cursor} over the result set.
1346 *
1347 * @param sql the SQL query. The SQL string must not be ; terminated
1348 * @param selectionArgs You may include ?s in where clause in the query,
1349 * which will be replaced by the values from selectionArgs. The
1350 * values will be bound as Strings.
1351 * @return A {@link Cursor} object, which is positioned before the first entry
1352 */
1353 public Cursor rawQuery(String sql, String[] selectionArgs) {
1354 return rawQueryWithFactory(null, sql, selectionArgs, null);
1355 }
1356
1357 /**
1358 * Runs the provided SQL and returns a cursor over the result set.
1359 *
1360 * @param cursorFactory the cursor factory to use, or null for the default factory
1361 * @param sql the SQL query. The SQL string must not be ; terminated
1362 * @param selectionArgs You may include ?s in where clause in the query,
1363 * which will be replaced by the values from selectionArgs. The
1364 * values will be bound as Strings.
1365 * @param editTable the name of the first table, which is editable
1366 * @return A {@link Cursor} object, which is positioned before the first entry
1367 */
1368 public Cursor rawQueryWithFactory(
1369 CursorFactory cursorFactory, String sql, String[] selectionArgs,
1370 String editTable) {
Vasu Norif3cf8a42010-03-23 11:41:44 -07001371 if (!isOpen()) {
1372 throw new IllegalStateException("database not open");
1373 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001374 long timeStart = 0;
1375
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001376 if (Config.LOGV || mSlowQueryThreshold != -1) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001377 timeStart = System.currentTimeMillis();
1378 }
1379
1380 SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable);
1381
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001382 Cursor cursor = null;
Dmitri Plotnikov8f29c122010-03-24 10:20:46 -07001383 int count = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001384 try {
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001385 cursor = driver.query(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001386 cursorFactory != null ? cursorFactory : mFactory,
1387 selectionArgs);
Dmitri Plotnikov8f29c122010-03-24 10:20:46 -07001388
1389 // Force query execution
1390 if (cursor != null) {
1391 count = cursor.getCount();
1392 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001393 } finally {
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001394 if (Config.LOGV || mSlowQueryThreshold != -1) {
1395
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001396 long duration = System.currentTimeMillis() - timeStart;
1397
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001398 if (Config.LOGV || duration >= mSlowQueryThreshold) {
1399 Log.v(SQLiteCursor.TAG,
1400 "query (" + duration + " ms): " + driver.toString() + ", args are "
1401 + (selectionArgs != null
1402 ? TextUtils.join(",", selectionArgs)
Dmitri Plotnikov8f29c122010-03-24 10:20:46 -07001403 : "<null>") + ", count is " + count);
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001404 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001405 }
1406 }
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001407 return cursor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001408 }
1409
1410 /**
1411 * Runs the provided SQL and returns a cursor over the result set.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001412 * The cursor will read an initial set of rows and the return to the caller.
1413 * It will continue to read in batches and send data changed notifications
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001414 * when the later batches are ready.
1415 * @param sql the SQL query. The SQL string must not be ; terminated
1416 * @param selectionArgs You may include ?s in where clause in the query,
1417 * which will be replaced by the values from selectionArgs. The
1418 * values will be bound as Strings.
1419 * @param initialRead set the initial count of items to read from the cursor
1420 * @param maxRead set the count of items to read on each iteration after the first
1421 * @return A {@link Cursor} object, which is positioned before the first entry
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001422 *
Andy Stadlerf8a7cea2009-04-10 16:24:47 -07001423 * This work is incomplete and not fully tested or reviewed, so currently
1424 * hidden.
1425 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001426 */
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001427 public Cursor rawQuery(String sql, String[] selectionArgs,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001428 int initialRead, int maxRead) {
1429 SQLiteCursor c = (SQLiteCursor)rawQueryWithFactory(
1430 null, sql, selectionArgs, null);
1431 c.setLoadStyle(initialRead, maxRead);
1432 return c;
1433 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001434
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001435 /**
1436 * Convenience method for inserting a row into the database.
1437 *
1438 * @param table the table to insert the row into
1439 * @param nullColumnHack SQL doesn't allow inserting a completely empty row,
1440 * so if initialValues is empty this column will explicitly be
1441 * assigned a NULL value
1442 * @param values this map contains the initial column values for the
1443 * row. The keys should be the column names and the values the
1444 * column values
1445 * @return the row ID of the newly inserted row, or -1 if an error occurred
1446 */
1447 public long insert(String table, String nullColumnHack, ContentValues values) {
1448 try {
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001449 return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001450 } catch (SQLException e) {
1451 Log.e(TAG, "Error inserting " + values, e);
1452 return -1;
1453 }
1454 }
1455
1456 /**
1457 * Convenience method for inserting a row into the database.
1458 *
1459 * @param table the table to insert the row into
1460 * @param nullColumnHack SQL doesn't allow inserting a completely empty row,
1461 * so if initialValues is empty this column will explicitly be
1462 * assigned a NULL value
1463 * @param values this map contains the initial column values for the
1464 * row. The keys should be the column names and the values the
1465 * column values
1466 * @throws SQLException
1467 * @return the row ID of the newly inserted row, or -1 if an error occurred
1468 */
1469 public long insertOrThrow(String table, String nullColumnHack, ContentValues values)
1470 throws SQLException {
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001471 return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001472 }
1473
1474 /**
1475 * Convenience method for replacing a row in the database.
1476 *
1477 * @param table the table in which to replace the row
1478 * @param nullColumnHack SQL doesn't allow inserting a completely empty row,
1479 * so if initialValues is empty this row will explicitly be
1480 * assigned a NULL value
1481 * @param initialValues this map contains the initial column values for
1482 * the row. The key
1483 * @return the row ID of the newly inserted row, or -1 if an error occurred
1484 */
1485 public long replace(String table, String nullColumnHack, ContentValues initialValues) {
1486 try {
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001487 return insertWithOnConflict(table, nullColumnHack, initialValues,
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001488 CONFLICT_REPLACE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001489 } catch (SQLException e) {
1490 Log.e(TAG, "Error inserting " + initialValues, e);
1491 return -1;
1492 }
1493 }
1494
1495 /**
1496 * Convenience method for replacing a row in the database.
1497 *
1498 * @param table the table in which to replace the row
1499 * @param nullColumnHack SQL doesn't allow inserting a completely empty row,
1500 * so if initialValues is empty this row will explicitly be
1501 * assigned a NULL value
1502 * @param initialValues this map contains the initial column values for
1503 * the row. The key
1504 * @throws SQLException
1505 * @return the row ID of the newly inserted row, or -1 if an error occurred
1506 */
1507 public long replaceOrThrow(String table, String nullColumnHack,
1508 ContentValues initialValues) throws SQLException {
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001509 return insertWithOnConflict(table, nullColumnHack, initialValues,
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001510 CONFLICT_REPLACE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001511 }
1512
1513 /**
1514 * General method for inserting a row into the database.
1515 *
1516 * @param table the table to insert the row into
1517 * @param nullColumnHack SQL doesn't allow inserting a completely empty row,
1518 * so if initialValues is empty this column will explicitly be
1519 * assigned a NULL value
1520 * @param initialValues this map contains the initial column values for the
1521 * row. The keys should be the column names and the values the
1522 * column values
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001523 * @param conflictAlgorithm for insert conflict resolver
Vasu Nori6eb7c452010-01-27 14:31:24 -08001524 * @return the row ID of the newly inserted row
1525 * OR the primary key of the existing row if the input param 'conflictAlgorithm' =
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001526 * {@link #CONFLICT_IGNORE}
Vasu Nori6eb7c452010-01-27 14:31:24 -08001527 * OR -1 if any error
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001528 */
1529 public long insertWithOnConflict(String table, String nullColumnHack,
Vasu Nori6eb7c452010-01-27 14:31:24 -08001530 ContentValues initialValues, int conflictAlgorithm) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001531 if (!isOpen()) {
1532 throw new IllegalStateException("database not open");
1533 }
1534
1535 // Measurements show most sql lengths <= 152
1536 StringBuilder sql = new StringBuilder(152);
1537 sql.append("INSERT");
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001538 sql.append(CONFLICT_VALUES[conflictAlgorithm]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001539 sql.append(" INTO ");
1540 sql.append(table);
1541 // Measurements show most values lengths < 40
1542 StringBuilder values = new StringBuilder(40);
1543
1544 Set<Map.Entry<String, Object>> entrySet = null;
1545 if (initialValues != null && initialValues.size() > 0) {
1546 entrySet = initialValues.valueSet();
1547 Iterator<Map.Entry<String, Object>> entriesIter = entrySet.iterator();
1548 sql.append('(');
1549
1550 boolean needSeparator = false;
1551 while (entriesIter.hasNext()) {
1552 if (needSeparator) {
1553 sql.append(", ");
1554 values.append(", ");
1555 }
1556 needSeparator = true;
1557 Map.Entry<String, Object> entry = entriesIter.next();
1558 sql.append(entry.getKey());
1559 values.append('?');
1560 }
1561
1562 sql.append(')');
1563 } else {
1564 sql.append("(" + nullColumnHack + ") ");
1565 values.append("NULL");
1566 }
1567
1568 sql.append(" VALUES(");
1569 sql.append(values);
1570 sql.append(");");
1571
1572 lock();
1573 SQLiteStatement statement = null;
1574 try {
1575 statement = compileStatement(sql.toString());
1576
1577 // Bind the values
1578 if (entrySet != null) {
1579 int size = entrySet.size();
1580 Iterator<Map.Entry<String, Object>> entriesIter = entrySet.iterator();
1581 for (int i = 0; i < size; i++) {
1582 Map.Entry<String, Object> entry = entriesIter.next();
1583 DatabaseUtils.bindObjectToProgram(statement, i + 1, entry.getValue());
1584 }
1585 }
1586
1587 // Run the program and then cleanup
1588 statement.execute();
1589
1590 long insertedRowId = lastInsertRow();
1591 if (insertedRowId == -1) {
1592 Log.e(TAG, "Error inserting " + initialValues + " using " + sql);
1593 } else {
1594 if (Config.LOGD && Log.isLoggable(TAG, Log.VERBOSE)) {
1595 Log.v(TAG, "Inserting row " + insertedRowId + " from "
1596 + initialValues + " using " + sql);
1597 }
1598 }
1599 return insertedRowId;
1600 } catch (SQLiteDatabaseCorruptException e) {
1601 onCorruption();
1602 throw e;
1603 } finally {
1604 if (statement != null) {
1605 statement.close();
1606 }
1607 unlock();
1608 }
1609 }
1610
1611 /**
1612 * Convenience method for deleting rows in the database.
1613 *
1614 * @param table the table to delete from
1615 * @param whereClause the optional WHERE clause to apply when deleting.
1616 * Passing null will delete all rows.
1617 * @return the number of rows affected if a whereClause is passed in, 0
1618 * otherwise. To remove all rows and get a count pass "1" as the
1619 * whereClause.
1620 */
1621 public int delete(String table, String whereClause, String[] whereArgs) {
1622 if (!isOpen()) {
1623 throw new IllegalStateException("database not open");
1624 }
1625 lock();
1626 SQLiteStatement statement = null;
1627 try {
1628 statement = compileStatement("DELETE FROM " + table
1629 + (!TextUtils.isEmpty(whereClause)
1630 ? " WHERE " + whereClause : ""));
1631 if (whereArgs != null) {
1632 int numArgs = whereArgs.length;
1633 for (int i = 0; i < numArgs; i++) {
1634 DatabaseUtils.bindObjectToProgram(statement, i + 1, whereArgs[i]);
1635 }
1636 }
1637 statement.execute();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001638 return lastChangeCount();
1639 } catch (SQLiteDatabaseCorruptException e) {
1640 onCorruption();
1641 throw e;
1642 } finally {
1643 if (statement != null) {
1644 statement.close();
1645 }
1646 unlock();
1647 }
1648 }
1649
1650 /**
1651 * Convenience method for updating rows in the database.
1652 *
1653 * @param table the table to update in
1654 * @param values a map from column names to new column values. null is a
1655 * valid value that will be translated to NULL.
1656 * @param whereClause the optional WHERE clause to apply when updating.
1657 * Passing null will update all rows.
1658 * @return the number of rows affected
1659 */
1660 public int update(String table, ContentValues values, String whereClause, String[] whereArgs) {
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001661 return updateWithOnConflict(table, values, whereClause, whereArgs, CONFLICT_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001662 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001663
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001664 /**
1665 * Convenience method for updating rows in the database.
1666 *
1667 * @param table the table to update in
1668 * @param values a map from column names to new column values. null is a
1669 * valid value that will be translated to NULL.
1670 * @param whereClause the optional WHERE clause to apply when updating.
1671 * Passing null will update all rows.
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001672 * @param conflictAlgorithm for update conflict resolver
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001673 * @return the number of rows affected
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001674 */
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001675 public int updateWithOnConflict(String table, ContentValues values,
Vasu Nori6eb7c452010-01-27 14:31:24 -08001676 String whereClause, String[] whereArgs, int conflictAlgorithm) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001677 if (!isOpen()) {
1678 throw new IllegalStateException("database not open");
1679 }
1680
1681 if (values == null || values.size() == 0) {
1682 throw new IllegalArgumentException("Empty values");
1683 }
1684
1685 StringBuilder sql = new StringBuilder(120);
1686 sql.append("UPDATE ");
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001687 sql.append(CONFLICT_VALUES[conflictAlgorithm]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001688 sql.append(table);
1689 sql.append(" SET ");
1690
1691 Set<Map.Entry<String, Object>> entrySet = values.valueSet();
1692 Iterator<Map.Entry<String, Object>> entriesIter = entrySet.iterator();
1693
1694 while (entriesIter.hasNext()) {
1695 Map.Entry<String, Object> entry = entriesIter.next();
1696 sql.append(entry.getKey());
1697 sql.append("=?");
1698 if (entriesIter.hasNext()) {
1699 sql.append(", ");
1700 }
1701 }
1702
1703 if (!TextUtils.isEmpty(whereClause)) {
1704 sql.append(" WHERE ");
1705 sql.append(whereClause);
1706 }
1707
1708 lock();
1709 SQLiteStatement statement = null;
1710 try {
1711 statement = compileStatement(sql.toString());
1712
1713 // Bind the values
1714 int size = entrySet.size();
1715 entriesIter = entrySet.iterator();
1716 int bindArg = 1;
1717 for (int i = 0; i < size; i++) {
1718 Map.Entry<String, Object> entry = entriesIter.next();
1719 DatabaseUtils.bindObjectToProgram(statement, bindArg, entry.getValue());
1720 bindArg++;
1721 }
1722
1723 if (whereArgs != null) {
1724 size = whereArgs.length;
1725 for (int i = 0; i < size; i++) {
1726 statement.bindString(bindArg, whereArgs[i]);
1727 bindArg++;
1728 }
1729 }
1730
1731 // Run the program and then cleanup
1732 statement.execute();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001733 int numChangedRows = lastChangeCount();
1734 if (Config.LOGD && Log.isLoggable(TAG, Log.VERBOSE)) {
1735 Log.v(TAG, "Updated " + numChangedRows + " using " + values + " and " + sql);
1736 }
1737 return numChangedRows;
1738 } catch (SQLiteDatabaseCorruptException e) {
1739 onCorruption();
1740 throw e;
1741 } catch (SQLException e) {
1742 Log.e(TAG, "Error updating " + values + " using " + sql);
1743 throw e;
1744 } finally {
1745 if (statement != null) {
1746 statement.close();
1747 }
1748 unlock();
1749 }
1750 }
1751
1752 /**
1753 * Execute a single SQL statement that is not a query. For example, CREATE
1754 * TABLE, DELETE, INSERT, etc. Multiple statements separated by ;s are not
1755 * supported. it takes a write lock
1756 *
1757 * @throws SQLException If the SQL string is invalid for some reason
1758 */
1759 public void execSQL(String sql) throws SQLException {
Vasu Norif3cf8a42010-03-23 11:41:44 -07001760 if (!isOpen()) {
1761 throw new IllegalStateException("database not open");
1762 }
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -08001763 long timeStart = SystemClock.uptimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001764 lock();
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07001765 logTimeStat(mLastSqlStatement, timeStart, GET_LOCK_LOG_PREFIX);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001766 try {
1767 native_execSQL(sql);
1768 } catch (SQLiteDatabaseCorruptException e) {
1769 onCorruption();
1770 throw e;
1771 } finally {
1772 unlock();
1773 }
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -08001774
1775 // Log commit statements along with the most recently executed
1776 // SQL statement for disambiguation. Note that instance
1777 // equality to COMMIT_SQL is safe here.
1778 if (sql == COMMIT_SQL) {
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07001779 logTimeStat(mLastSqlStatement, timeStart, COMMIT_SQL);
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -08001780 } else {
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07001781 logTimeStat(sql, timeStart, null);
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -08001782 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001783 }
1784
1785 /**
1786 * Execute a single SQL statement that is not a query. For example, CREATE
1787 * TABLE, DELETE, INSERT, etc. Multiple statements separated by ;s are not
1788 * supported. it takes a write lock,
1789 *
1790 * @param sql
1791 * @param bindArgs only byte[], String, Long and Double are supported in bindArgs.
1792 * @throws SQLException If the SQL string is invalid for some reason
1793 */
1794 public void execSQL(String sql, Object[] bindArgs) throws SQLException {
Vasu Norif3cf8a42010-03-23 11:41:44 -07001795 if (!isOpen()) {
1796 throw new IllegalStateException("database not open");
1797 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001798 if (bindArgs == null) {
1799 throw new IllegalArgumentException("Empty bindArgs");
1800 }
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -08001801 long timeStart = SystemClock.uptimeMillis();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001802 lock();
1803 SQLiteStatement statement = null;
1804 try {
1805 statement = compileStatement(sql);
1806 if (bindArgs != null) {
1807 int numArgs = bindArgs.length;
1808 for (int i = 0; i < numArgs; i++) {
1809 DatabaseUtils.bindObjectToProgram(statement, i + 1, bindArgs[i]);
1810 }
1811 }
1812 statement.execute();
1813 } catch (SQLiteDatabaseCorruptException e) {
1814 onCorruption();
1815 throw e;
1816 } finally {
1817 if (statement != null) {
1818 statement.close();
1819 }
1820 unlock();
1821 }
Dan Egnor12311952009-11-23 14:47:45 -08001822 logTimeStat(sql, timeStart);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001823 }
1824
1825 @Override
1826 protected void finalize() {
1827 if (isOpen()) {
Vasu Norid606b4b2010-02-24 12:54:20 -08001828 Log.e(TAG, "close() was never explicitly called on database '" +
1829 mPath + "' ", mStackTrace);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001830 closeClosable();
1831 onAllReferencesReleased();
1832 }
1833 }
1834
1835 /**
1836 * Private constructor. See {@link #create} and {@link #openDatabase}.
1837 *
1838 * @param path The full path to the database
1839 * @param factory The factory to use when creating cursors, may be NULL.
1840 * @param flags 0 or {@link #NO_LOCALIZED_COLLATORS}. If the database file already
1841 * exists, mFlags will be updated appropriately.
1842 */
1843 private SQLiteDatabase(String path, CursorFactory factory, int flags) {
1844 if (path == null) {
1845 throw new IllegalArgumentException("path should not be null");
1846 }
1847 mFlags = flags;
1848 mPath = path;
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001849 mSlowQueryThreshold = SystemProperties.getInt(LOG_SLOW_QUERIES_PROPERTY, -1);
Vasu Nori08b448e2010-03-03 10:05:16 -08001850 mStackTrace = new DatabaseObjectNotClosedException().fillInStackTrace();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001851 mFactory = factory;
Vasu Nori5a03f362009-10-20 15:16:35 -07001852 if (SQLiteDebug.DEBUG_SQL_CACHE) {
1853 mTimeOpened = getTime();
1854 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001855 mPrograms = new WeakHashMap<SQLiteClosable,Object>();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001856 }
1857
Vasu Nori5a03f362009-10-20 15:16:35 -07001858 private String getTime() {
1859 return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS ").format(System.currentTimeMillis());
1860 }
1861
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001862 /**
1863 * return whether the DB is opened as read only.
1864 * @return true if DB is opened as read only
1865 */
1866 public boolean isReadOnly() {
1867 return (mFlags & OPEN_READ_MASK) == OPEN_READONLY;
1868 }
1869
1870 /**
1871 * @return true if the DB is currently open (has not been closed)
1872 */
1873 public boolean isOpen() {
1874 return mNativeHandle != 0;
1875 }
1876
1877 public boolean needUpgrade(int newVersion) {
1878 return newVersion > getVersion();
1879 }
1880
1881 /**
1882 * Getter for the path to the database file.
1883 *
1884 * @return the path to our database file.
1885 */
1886 public final String getPath() {
1887 return mPath;
1888 }
1889
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -08001890 /* package */ void logTimeStat(String sql, long beginMillis) {
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07001891 logTimeStat(sql, beginMillis, null);
1892 }
1893
1894 /* package */ void logTimeStat(String sql, long beginMillis, String prefix) {
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -08001895 // Keep track of the last statement executed here, as this is
1896 // the common funnel through which all methods of hitting
1897 // libsqlite eventually flow.
1898 mLastSqlStatement = sql;
1899
Dan Egnor12311952009-11-23 14:47:45 -08001900 // Sample fast queries in proportion to the time taken.
1901 // Quantize the % first, so the logged sampling probability
1902 // exactly equals the actual sampling rate for this query.
1903
1904 int samplePercent;
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -08001905 long durationMillis = SystemClock.uptimeMillis() - beginMillis;
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07001906 if (durationMillis == 0 && prefix == GET_LOCK_LOG_PREFIX) {
1907 // The common case is locks being uncontended. Don't log those,
1908 // even at 1%, which is our default below.
1909 return;
1910 }
1911 if (sQueryLogTimeInMillis == 0) {
1912 sQueryLogTimeInMillis = SystemProperties.getInt("db.db_operation.threshold_ms", 500);
1913 }
1914 if (durationMillis >= sQueryLogTimeInMillis) {
Dan Egnor12311952009-11-23 14:47:45 -08001915 samplePercent = 100;
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07001916 } else {;
1917 samplePercent = (int) (100 * durationMillis / sQueryLogTimeInMillis) + 1;
Dan Egnor799f7212009-11-24 16:24:44 -08001918 if (mRandom.nextInt(100) >= samplePercent) return;
Dan Egnor12311952009-11-23 14:47:45 -08001919 }
1920
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07001921 // Note: the prefix will be "COMMIT;" or "GETLOCK:" when non-null. We wait to do
1922 // it here so we avoid allocating in the common case.
1923 if (prefix != null) {
1924 sql = prefix + sql;
1925 }
1926
Dan Egnor12311952009-11-23 14:47:45 -08001927 if (sql.length() > QUERY_LOG_SQL_LENGTH) sql = sql.substring(0, QUERY_LOG_SQL_LENGTH);
1928
1929 // ActivityThread.currentPackageName() only returns non-null if the
1930 // current thread is an application main thread. This parameter tells
1931 // us whether an event loop is blocked, and if so, which app it is.
1932 //
1933 // Sadly, there's no fast way to determine app name if this is *not* a
1934 // main thread, or when we are invoked via Binder (e.g. ContentProvider).
1935 // Hopefully the full path to the database will be informative enough.
1936
1937 String blockingPackage = ActivityThread.currentPackageName();
1938 if (blockingPackage == null) blockingPackage = "";
1939
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -08001940 EventLog.writeEvent(
Brad Fitzpatrickd8330232010-02-19 10:59:01 -08001941 EVENT_DB_OPERATION,
1942 getPathForLogs(),
1943 sql,
1944 durationMillis,
1945 blockingPackage,
1946 samplePercent);
1947 }
1948
1949 /**
1950 * Removes email addresses from database filenames before they're
1951 * logged to the EventLog where otherwise apps could potentially
1952 * read them.
1953 */
1954 private String getPathForLogs() {
1955 if (mPathForLogs != null) {
1956 return mPathForLogs;
1957 }
1958 if (mPath == null) {
1959 return null;
1960 }
1961 if (mPath.indexOf('@') == -1) {
1962 mPathForLogs = mPath;
1963 } else {
1964 mPathForLogs = EMAIL_IN_DB_PATTERN.matcher(mPath).replaceAll("XX@YY");
1965 }
1966 return mPathForLogs;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001967 }
1968
1969 /**
1970 * Sets the locale for this database. Does nothing if this database has
1971 * the NO_LOCALIZED_COLLATORS flag set or was opened read only.
1972 * @throws SQLException if the locale could not be set. The most common reason
1973 * for this is that there is no collator available for the locale you requested.
1974 * In this case the database remains unchanged.
1975 */
1976 public void setLocale(Locale locale) {
1977 lock();
1978 try {
1979 native_setLocale(locale.toString(), mFlags);
1980 } finally {
1981 unlock();
1982 }
1983 }
1984
Vasu Norie495d1f2010-01-06 16:34:19 -08001985 /*
1986 * ============================================================================
1987 *
1988 * The following methods deal with compiled-sql cache
1989 * ============================================================================
1990 */
1991 /**
1992 * adds the given sql and its compiled-statement-id-returned-by-sqlite to the
1993 * cache of compiledQueries attached to 'this'.
1994 *
1995 * if there is already a {@link SQLiteCompiledSql} in compiledQueries for the given sql,
1996 * the new {@link SQLiteCompiledSql} object is NOT inserted into the cache (i.e.,the current
1997 * mapping is NOT replaced with the new mapping).
1998 */
1999 /* package */ void addToCompiledQueries(String sql, SQLiteCompiledSql compiledStatement) {
Vasu Norie495d1f2010-01-06 16:34:19 -08002000 SQLiteCompiledSql compiledSql = null;
2001 synchronized(mCompiledQueries) {
2002 // don't insert the new mapping if a mapping already exists
2003 compiledSql = mCompiledQueries.get(sql);
2004 if (compiledSql != null) {
2005 return;
2006 }
2007 // add this <sql, compiledStatement> to the cache
2008 if (mCompiledQueries.size() == mMaxSqlCacheSize) {
Vasu Nori49d02ac2010-03-05 21:49:30 -08002009 /*
2010 * cache size of {@link #mMaxSqlCacheSize} is not enough for this app.
2011 * log a warning MAX_WARNINGS_ON_CACHESIZE_CONDITION times
2012 * chances are it is NOT using ? for bindargs - so caching is useless.
2013 * TODO: either let the callers set max cchesize for their app, or intelligently
2014 * figure out what should be cached for a given app.
Vasu Norie495d1f2010-01-06 16:34:19 -08002015 */
Vasu Nori49d02ac2010-03-05 21:49:30 -08002016 if (++mCacheFullWarnings == MAX_WARNINGS_ON_CACHESIZE_CONDITION) {
2017 Log.w(TAG, "Reached MAX size for compiled-sql statement cache for database " +
2018 getPath() + "; i.e., NO space for this sql statement in cache: " +
2019 sql + ". Please change your sql statements to use '?' for " +
2020 "bindargs, instead of using actual values");
2021 }
2022 // don't add this entry to cache
2023 } else {
2024 // cache is NOT full. add this to cache.
2025 mCompiledQueries.put(sql, compiledStatement);
2026 if (SQLiteDebug.DEBUG_SQL_CACHE) {
2027 Log.v(TAG, "|adding_sql_to_cache|" + getPath() + "|" +
2028 mCompiledQueries.size() + "|" + sql);
Vasu Norie495d1f2010-01-06 16:34:19 -08002029 }
2030 }
Vasu Norie495d1f2010-01-06 16:34:19 -08002031 }
2032 return;
2033 }
2034
2035
2036 private void deallocCachedSqlStatements() {
2037 synchronized (mCompiledQueries) {
2038 for (SQLiteCompiledSql compiledSql : mCompiledQueries.values()) {
2039 compiledSql.releaseSqlStatement();
2040 }
2041 mCompiledQueries.clear();
2042 }
2043 }
2044
2045 /**
2046 * from the compiledQueries cache, returns the compiled-statement-id for the given sql.
2047 * returns null, if not found in the cache.
2048 */
2049 /* package */ SQLiteCompiledSql getCompiledStatementForSql(String sql) {
2050 SQLiteCompiledSql compiledStatement = null;
2051 boolean cacheHit;
2052 synchronized(mCompiledQueries) {
Vasu Norie495d1f2010-01-06 16:34:19 -08002053 cacheHit = (compiledStatement = mCompiledQueries.get(sql)) != null;
2054 }
2055 if (cacheHit) {
2056 mNumCacheHits++;
2057 } else {
2058 mNumCacheMisses++;
2059 }
2060
2061 if (SQLiteDebug.DEBUG_SQL_CACHE) {
2062 Log.v(TAG, "|cache_stats|" +
2063 getPath() + "|" + mCompiledQueries.size() +
2064 "|" + mNumCacheHits + "|" + mNumCacheMisses +
2065 "|" + cacheHit + "|" + mTimeOpened + "|" + mTimeClosed + "|" + sql);
2066 }
2067 return compiledStatement;
2068 }
2069
2070 /**
2071 * returns true if the given sql is cached in compiled-sql cache.
2072 * @hide
2073 */
2074 public boolean isInCompiledSqlCache(String sql) {
2075 synchronized(mCompiledQueries) {
2076 return mCompiledQueries.containsKey(sql);
2077 }
2078 }
2079
2080 /**
2081 * purges the given sql from the compiled-sql cache.
2082 * @hide
2083 */
2084 public void purgeFromCompiledSqlCache(String sql) {
2085 synchronized(mCompiledQueries) {
2086 mCompiledQueries.remove(sql);
2087 }
2088 }
2089
2090 /**
2091 * remove everything from the compiled sql cache
2092 * @hide
2093 */
2094 public void resetCompiledSqlCache() {
2095 synchronized(mCompiledQueries) {
2096 mCompiledQueries.clear();
2097 }
2098 }
2099
2100 /**
2101 * return the current maxCacheSqlCacheSize
2102 * @hide
2103 */
2104 public synchronized int getMaxSqlCacheSize() {
2105 return mMaxSqlCacheSize;
2106 }
2107
2108 /**
Vasu Nori90a367262010-04-12 12:49:09 -07002109 * set the max size of the prepared-statement cache for this database.
Vasu Norie495d1f2010-01-06 16:34:19 -08002110 * (size of the cache = number of compiled-sql-statements stored in the cache).
2111 *
Vasu Nori90a367262010-04-12 12:49:09 -07002112 * max cache size can ONLY be increased from its current size (default = 10).
Vasu Norie495d1f2010-01-06 16:34:19 -08002113 * if this method is called with smaller size than the current value of mMaxSqlCacheSize,
2114 * then IllegalStateException is thrown
2115 *
2116 * synchronized because we don't want t threads to change cache size at the same time.
Vasu Nori90a367262010-04-12 12:49:09 -07002117 * @param cacheSize the size of the cache. can be (0 to {@link #MAX_SQL_CACHE_SIZE})
2118 * @throws IllegalStateException if input cacheSize > {@link #MAX_SQL_CACHE_SIZE} or
2119 * > the value set with previous setMaxSqlCacheSize() call.
Vasu Norie495d1f2010-01-06 16:34:19 -08002120 */
2121 public synchronized void setMaxSqlCacheSize(int cacheSize) {
2122 if (cacheSize > MAX_SQL_CACHE_SIZE || cacheSize < 0) {
2123 throw new IllegalStateException("expected value between 0 and " + MAX_SQL_CACHE_SIZE);
2124 } else if (cacheSize < mMaxSqlCacheSize) {
2125 throw new IllegalStateException("cannot set cacheSize to a value less than the value " +
2126 "set with previous setMaxSqlCacheSize() call.");
2127 }
2128 mMaxSqlCacheSize = cacheSize;
2129 }
2130
Vasu Noric3849202010-03-09 10:47:25 -08002131 static class ActiveDatabases {
2132 private static final ActiveDatabases activeDatabases = new ActiveDatabases();
2133 private HashSet<WeakReference<SQLiteDatabase>> mActiveDatabases =
2134 new HashSet<WeakReference<SQLiteDatabase>>();
2135 private ActiveDatabases() {} // disable instantiation of this class
2136 static ActiveDatabases getInstance() {return activeDatabases;}
2137 }
2138
Vasu Norif3cf8a42010-03-23 11:41:44 -07002139 /**
2140 * this method is used to collect data about ALL open databases in the current process.
2141 * bugreport is a user of this data.
2142 */
Vasu Noric3849202010-03-09 10:47:25 -08002143 /* package */ static ArrayList<DbStats> getDbStats() {
2144 ArrayList<DbStats> dbStatsList = new ArrayList<DbStats>();
2145 for (WeakReference<SQLiteDatabase> w : ActiveDatabases.getInstance().mActiveDatabases) {
2146 SQLiteDatabase db = w.get();
2147 if (db == null || !db.isOpen()) {
2148 continue;
2149 }
2150 // get SQLITE_DBSTATUS_LOOKASIDE_USED for the db
2151 int lookasideUsed = db.native_getDbLookaside();
2152
2153 // get the lastnode of the dbname
2154 String path = db.getPath();
2155 int indx = path.lastIndexOf("/");
2156 String lastnode = path.substring((indx != -1) ? ++indx : 0);
2157
2158 // get list of attached dbs and for each db, get its size and pagesize
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002159 ArrayList<Pair<String, String>> attachedDbs = db.getAttachedDbs();
Vasu Norif3cf8a42010-03-23 11:41:44 -07002160 if (attachedDbs == null) {
2161 continue;
2162 }
Vasu Noric3849202010-03-09 10:47:25 -08002163 for (int i = 0; i < attachedDbs.size(); i++) {
2164 Pair<String, String> p = attachedDbs.get(i);
2165 long pageCount = getPragmaVal(db, p.first + ".page_count;");
2166
2167 // first entry in the attached db list is always the main database
2168 // don't worry about prefixing the dbname with "main"
2169 String dbName;
2170 if (i == 0) {
2171 dbName = lastnode;
2172 } else {
2173 // lookaside is only relevant for the main db
2174 lookasideUsed = 0;
2175 dbName = " (attached) " + p.first;
2176 // if the attached db has a path, attach the lastnode from the path to above
2177 if (p.second.trim().length() > 0) {
2178 int idx = p.second.lastIndexOf("/");
2179 dbName += " : " + p.second.substring((idx != -1) ? ++idx : 0);
2180 }
2181 }
Vasu Norif3cf8a42010-03-23 11:41:44 -07002182 if (pageCount > 0) {
2183 dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(),
Vasu Nori90a367262010-04-12 12:49:09 -07002184 lookasideUsed, db.mNumCacheHits, db.mNumCacheMisses,
2185 db.mCompiledQueries.size()));
Vasu Norif3cf8a42010-03-23 11:41:44 -07002186 }
Vasu Noric3849202010-03-09 10:47:25 -08002187 }
2188 }
2189 return dbStatsList;
2190 }
2191
2192 /**
2193 * get the specified pragma value from sqlite for the specified database.
2194 * only handles pragma's that return int/long.
2195 * NO JAVA locks are held in this method.
2196 * TODO: use this to do all pragma's in this class
2197 */
2198 private static long getPragmaVal(SQLiteDatabase db, String pragma) {
Vasu Norif3cf8a42010-03-23 11:41:44 -07002199 if (!db.isOpen()) {
2200 return 0;
2201 }
Vasu Noric3849202010-03-09 10:47:25 -08002202 SQLiteStatement prog = null;
2203 try {
2204 prog = new SQLiteStatement(db, "PRAGMA " + pragma);
2205 long val = prog.simpleQueryForLong();
2206 return val;
2207 } finally {
2208 if (prog != null) prog.close();
2209 }
2210 }
2211
2212 /**
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002213 * returns list of full pathnames of all attached databases including the main database
2214 * @return ArrayList of pairs of (database name, database file path) or null if the database
2215 * is not open.
Vasu Noric3849202010-03-09 10:47:25 -08002216 */
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002217 public ArrayList<Pair<String, String>> getAttachedDbs() {
2218 if (!isOpen()) {
Vasu Norif3cf8a42010-03-23 11:41:44 -07002219 return null;
2220 }
Vasu Noric3849202010-03-09 10:47:25 -08002221 ArrayList<Pair<String, String>> attachedDbs = new ArrayList<Pair<String, String>>();
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002222 Cursor c = null;
2223 try {
2224 c = rawQuery("pragma database_list;", null);
2225 while (c.moveToNext()) {
2226 // sqlite returns a row for each database in the returned list of databases.
2227 // in each row,
2228 // 1st column is the database name such as main, or the database
2229 // name specified on the "ATTACH" command
2230 // 2nd column is the database file path.
2231 attachedDbs.add(new Pair<String, String>(c.getString(1), c.getString(2)));
2232 }
2233 } finally {
2234 if (c != null) {
2235 c.close();
2236 }
Vasu Noric3849202010-03-09 10:47:25 -08002237 }
Vasu Noric3849202010-03-09 10:47:25 -08002238 return attachedDbs;
2239 }
2240
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002241 /**
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002242 * run pragma integrity_check on the given database (and all the attached databases)
2243 * and return true if the given database (and all its attached databases) pass integrity_check,
2244 * false otherwise.
2245 *
2246 * if the result is false, then this method logs the errors reported by the integrity_check
2247 * command execution.
2248 *
2249 * @return true if the given database (and all its attached databases) pass integrity_check,
2250 * false otherwise
2251 */
2252 public boolean isDatabaseIntegrityOk() {
2253 if (!isOpen()) {
2254 throw new IllegalStateException("database: " + getPath() + " is NOT open");
2255 }
2256 ArrayList<Pair<String, String>> attachedDbs = getAttachedDbs();
2257 if (attachedDbs == null) {
2258 throw new IllegalStateException("databaselist for: " + getPath() + " couldn't " +
2259 "be retrieved. probably because the database is closed");
2260 }
2261 boolean isDatabaseCorrupt = false;
2262 for (int i = 0; i < attachedDbs.size(); i++) {
2263 Pair<String, String> p = attachedDbs.get(i);
2264 SQLiteStatement prog = null;
2265 try {
2266 prog = compileStatement("PRAGMA " + p.first + ".integrity_check(1);");
2267 String rslt = prog.simpleQueryForString();
2268 if (!rslt.equalsIgnoreCase("ok")) {
2269 // integrity_checker failed on main or attached databases
2270 isDatabaseCorrupt = true;
2271 Log.e(TAG, "PRAGMA integrity_check on " + p.second + " returned: " + rslt);
2272 }
2273 } finally {
2274 if (prog != null) prog.close();
2275 }
2276 }
2277 return isDatabaseCorrupt;
2278 }
2279
2280 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002281 * Native call to open the database.
2282 *
2283 * @param path The full path to the database
2284 */
2285 private native void dbopen(String path, int flags);
2286
2287 /**
Vasu Nori3ef94e22010-02-05 14:49:04 -08002288 * Native call to setup tracing of all sql statements
2289 *
2290 * @param path the full path to the database
2291 */
2292 private native void enableSqlTracing(String path);
2293
2294 /**
2295 * Native call to setup profiling of all sql statements.
2296 * currently, sqlite's profiling = printing of execution-time
2297 * (wall-clock time) of each of the sql statements, as they
2298 * are executed.
2299 *
2300 * @param path the full path to the database
2301 */
2302 private native void enableSqlProfiling(String path);
2303
2304 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002305 * Native call to execute a raw SQL statement. {@link #lock} must be held
2306 * when calling this method.
2307 *
2308 * @param sql The raw SQL string
2309 * @throws SQLException
2310 */
2311 /* package */ native void native_execSQL(String sql) throws SQLException;
2312
2313 /**
2314 * Native call to set the locale. {@link #lock} must be held when calling
2315 * this method.
2316 * @throws SQLException
2317 */
2318 /* package */ native void native_setLocale(String loc, int flags);
2319
2320 /**
2321 * Returns the row ID of the last row inserted into the database.
2322 *
2323 * @return the row ID of the last row inserted into the database.
2324 */
2325 /* package */ native long lastInsertRow();
2326
2327 /**
2328 * Returns the number of changes made in the last statement executed.
2329 *
2330 * @return the number of changes made in the last statement executed.
2331 */
2332 /* package */ native int lastChangeCount();
Vasu Noric3849202010-03-09 10:47:25 -08002333
2334 /**
2335 * return the SQLITE_DBSTATUS_LOOKASIDE_USED documented here
2336 * http://www.sqlite.org/c3ref/c_dbstatus_lookaside_used.html
2337 * @return int value of SQLITE_DBSTATUS_LOOKASIDE_USED
2338 */
2339 private native int native_getDbLookaside();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002340}