blob: 965f7dc829fa330f00a8a1ec6ec010023489ba14 [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
Dianne Hackborn01e4cfc2010-06-24 15:07:24 -070019import android.app.AppGlobals;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020import android.content.ContentValues;
Vasu Nori34ad57f02010-12-21 09:32:36 -080021import android.content.res.Resources;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.database.Cursor;
Vasu Nori062fc7ce2010-03-31 16:13:05 -070023import android.database.DatabaseErrorHandler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.database.DatabaseUtils;
Vasu Nori062fc7ce2010-03-31 16:13:05 -070025import android.database.DefaultDatabaseErrorHandler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.database.SQLException;
Vasu Noric3849202010-03-09 10:47:25 -080027import android.database.sqlite.SQLiteDebug.DbStats;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.os.Debug;
Vasu Noria8c24902010-06-01 11:30:27 -070029import android.os.StatFs;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import 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
Brad Fitzpatrickcfda9f32010-06-03 12:52:54 -070038import dalvik.system.BlockGuard;
39
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import java.io.File;
Vasu Noric3849202010-03-09 10:47:25 -080041import java.lang.ref.WeakReference;
Vasu Noric3849202010-03-09 10:47:25 -080042import java.util.ArrayList;
Vasu Noria017eda2011-01-27 10:52:55 -080043import java.util.List;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044import java.util.HashMap;
45import java.util.Iterator;
Vasu Norib729dcc2010-09-14 11:35:49 -070046import java.util.LinkedHashMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import java.util.Locale;
48import java.util.Map;
Dan Egnor12311952009-11-23 14:47:45 -080049import java.util.Random;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import java.util.WeakHashMap;
Vasu Norid4608a32011-02-03 16:24:06 -080051import java.util.concurrent.TimeUnit;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080052import java.util.concurrent.locks.ReentrantLock;
Brad Fitzpatrickd8330232010-02-19 10:59:01 -080053import java.util.regex.Pattern;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054
55/**
56 * Exposes methods to manage a SQLite database.
57 * <p>SQLiteDatabase has methods to create, delete, execute SQL commands, and
58 * perform other common database management tasks.
59 * <p>See the Notepad sample application in the SDK for an example of creating
60 * and managing a database.
61 * <p> Database names must be unique within an application, not across all
62 * applications.
63 *
64 * <h3>Localized Collation - ORDER BY</h3>
65 * <p>In addition to SQLite's default <code>BINARY</code> collator, Android supplies
66 * two more, <code>LOCALIZED</code>, which changes with the system's current locale
67 * if you wire it up correctly (XXX a link needed!), and <code>UNICODE</code>, which
68 * is the Unicode Collation Algorithm and not tailored to the current locale.
69 */
70public class SQLiteDatabase extends SQLiteClosable {
Vasu Norifb16cbd2010-07-25 16:38:48 -070071 private static final String TAG = "SQLiteDatabase";
Jeff Hamilton082c2af2009-09-29 11:49:51 -070072 private static final int EVENT_DB_OPERATION = 52000;
73 private static final int EVENT_DB_CORRUPT = 75004;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074
75 /**
76 * Algorithms used in ON CONFLICT clause
77 * http://www.sqlite.org/lang_conflict.html
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078 */
Vasu Nori8d45e4e2010-02-05 22:35:47 -080079 /**
80 * When a constraint violation occurs, an immediate ROLLBACK occurs,
81 * thus ending the current transaction, and the command aborts with a
82 * return code of SQLITE_CONSTRAINT. If no transaction is active
83 * (other than the implied transaction that is created on every command)
84 * then this algorithm works the same as ABORT.
85 */
86 public static final int CONFLICT_ROLLBACK = 1;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -070087
Vasu Nori8d45e4e2010-02-05 22:35:47 -080088 /**
89 * When a constraint violation occurs,no ROLLBACK is executed
90 * so changes from prior commands within the same transaction
91 * are preserved. This is the default behavior.
92 */
93 public static final int CONFLICT_ABORT = 2;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -070094
Vasu Nori8d45e4e2010-02-05 22:35:47 -080095 /**
96 * When a constraint violation occurs, the command aborts with a return
97 * code SQLITE_CONSTRAINT. But any changes to the database that
98 * the command made prior to encountering the constraint violation
99 * are preserved and are not backed out.
100 */
101 public static final int CONFLICT_FAIL = 3;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700102
Vasu Nori8d45e4e2010-02-05 22:35:47 -0800103 /**
104 * When a constraint violation occurs, the one row that contains
105 * the constraint violation is not inserted or changed.
106 * But the command continues executing normally. Other rows before and
107 * after the row that contained the constraint violation continue to be
108 * inserted or updated normally. No error is returned.
109 */
110 public static final int CONFLICT_IGNORE = 4;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700111
Vasu Nori8d45e4e2010-02-05 22:35:47 -0800112 /**
113 * When a UNIQUE constraint violation occurs, the pre-existing rows that
114 * are causing the constraint violation are removed prior to inserting
115 * or updating the current row. Thus the insert or update always occurs.
116 * The command continues executing normally. No error is returned.
117 * If a NOT NULL constraint violation occurs, the NULL value is replaced
118 * by the default value for that column. If the column has no default
119 * value, then the ABORT algorithm is used. If a CHECK constraint
120 * violation occurs then the IGNORE algorithm is used. When this conflict
121 * resolution strategy deletes rows in order to satisfy a constraint,
122 * it does not invoke delete triggers on those rows.
123 * This behavior might change in a future release.
124 */
125 public static final int CONFLICT_REPLACE = 5;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700126
Vasu Nori8d45e4e2010-02-05 22:35:47 -0800127 /**
128 * use the following when no conflict action is specified.
129 */
130 public static final int CONFLICT_NONE = 0;
131 private static final String[] CONFLICT_VALUES = new String[]
132 {"", " OR ROLLBACK ", " OR ABORT ", " OR FAIL ", " OR IGNORE ", " OR REPLACE "};
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700133
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134 /**
135 * Maximum Length Of A LIKE Or GLOB Pattern
136 * The pattern matching algorithm used in the default LIKE and GLOB implementation
137 * of SQLite can exhibit O(N^2) performance (where N is the number of characters in
138 * the pattern) for certain pathological cases. To avoid denial-of-service attacks
139 * the length of the LIKE or GLOB pattern is limited to SQLITE_MAX_LIKE_PATTERN_LENGTH bytes.
140 * The default value of this limit is 50000. A modern workstation can evaluate
141 * even a pathological LIKE or GLOB pattern of 50000 bytes relatively quickly.
142 * The denial of service problem only comes into play when the pattern length gets
143 * into millions of bytes. Nevertheless, since most useful LIKE or GLOB patterns
144 * are at most a few dozen bytes in length, paranoid application developers may
145 * want to reduce this parameter to something in the range of a few hundred
146 * if they know that external users are able to generate arbitrary patterns.
147 */
148 public static final int SQLITE_MAX_LIKE_PATTERN_LENGTH = 50000;
149
150 /**
151 * Flag for {@link #openDatabase} to open the database for reading and writing.
152 * If the disk is full, this may fail even before you actually write anything.
153 *
154 * {@more} Note that the value of this flag is 0, so it is the default.
155 */
156 public static final int OPEN_READWRITE = 0x00000000; // update native code if changing
157
158 /**
159 * Flag for {@link #openDatabase} to open the database for reading only.
160 * This is the only reliable way to open a database if the disk may be full.
161 */
162 public static final int OPEN_READONLY = 0x00000001; // update native code if changing
163
164 private static final int OPEN_READ_MASK = 0x00000001; // update native code if changing
165
166 /**
167 * Flag for {@link #openDatabase} to open the database without support for localized collators.
168 *
169 * {@more} This causes the collator <code>LOCALIZED</code> not to be created.
170 * You must be consistent when using this flag to use the setting the database was
171 * created with. If this is set, {@link #setLocale} will do nothing.
172 */
173 public static final int NO_LOCALIZED_COLLATORS = 0x00000010; // update native code if changing
174
175 /**
176 * Flag for {@link #openDatabase} to create the database file if it does not already exist.
177 */
178 public static final int CREATE_IF_NECESSARY = 0x10000000; // update native code if changing
179
180 /**
181 * Indicates whether the most-recently started transaction has been marked as successful.
182 */
183 private boolean mInnerTransactionIsSuccessful;
184
185 /**
186 * Valid during the life of a transaction, and indicates whether the entire transaction (the
187 * outer one and all of the inner ones) so far has been successful.
188 */
189 private boolean mTransactionIsSuccessful;
190
Fred Quintanac4516a72009-09-03 12:14:06 -0700191 /**
192 * Valid during the life of a transaction.
193 */
194 private SQLiteTransactionListener mTransactionListener;
195
Vasu Norice38b982010-07-22 13:57:13 -0700196 /**
197 * this member is set if {@link #execSQL(String)} is used to begin and end transactions.
198 */
199 private boolean mTransactionUsingExecSql;
200
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800201 /** Synchronize on this when accessing the database */
Vasu Norid4608a32011-02-03 16:24:06 -0800202 private final DatabaseReentrantLock mLock = new DatabaseReentrantLock(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203
204 private long mLockAcquiredWallTime = 0L;
205 private long mLockAcquiredThreadTime = 0L;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700206
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 // limit the frequency of complaints about each database to one within 20 sec
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700208 // unless run command adb shell setprop log.tag.Database VERBOSE
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800209 private static final int LOCK_WARNING_WINDOW_IN_MS = 20000;
210 /** If the lock is held this long then a warning will be printed when it is released. */
211 private static final int LOCK_ACQUIRED_WARNING_TIME_IN_MS = 300;
212 private static final int LOCK_ACQUIRED_WARNING_THREAD_TIME_IN_MS = 100;
213 private static final int LOCK_ACQUIRED_WARNING_TIME_IN_MS_ALWAYS_PRINT = 2000;
214
Dmitri Plotnikovb43b58d2009-09-09 18:10:42 -0700215 private static final int SLEEP_AFTER_YIELD_QUANTUM = 1000;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700216
Brad Fitzpatrickd8330232010-02-19 10:59:01 -0800217 // The pattern we remove from database filenames before
218 // potentially logging them.
219 private static final Pattern EMAIL_IN_DB_PATTERN = Pattern.compile("[\\w\\.\\-]+@[\\w\\.\\-]+");
220
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221 private long mLastLockMessageTime = 0L;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700222
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -0800223 // Things related to query logging/sampling for debugging
224 // slow/frequent queries during development. Always log queries
Brad Fitzpatrick722802e2010-03-23 22:22:16 -0700225 // which take (by default) 500ms+; shorter queries are sampled
226 // accordingly. Commit statements, which are typically slow, are
227 // logged together with the most recently executed SQL statement,
228 // for disambiguation. The 500ms value is configurable via a
229 // SystemProperty, but developers actively debugging database I/O
230 // should probably use the regular log tunable,
231 // LOG_SLOW_QUERIES_PROPERTY, defined below.
232 private static int sQueryLogTimeInMillis = 0; // lazily initialized
Dan Egnor12311952009-11-23 14:47:45 -0800233 private static final int QUERY_LOG_SQL_LENGTH = 64;
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -0800234 private static final String COMMIT_SQL = "COMMIT;";
Dan Egnor12311952009-11-23 14:47:45 -0800235 private final Random mRandom = new Random();
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -0800236 private String mLastSqlStatement = null;
Dan Egnor12311952009-11-23 14:47:45 -0800237
Brad Fitzpatrick722802e2010-03-23 22:22:16 -0700238 // String prefix for slow database query EventLog records that show
239 // lock acquistions of the database.
240 /* package */ static final String GET_LOCK_LOG_PREFIX = "GETLOCK:";
241
Vasu Nori6f37f832010-05-19 11:53:25 -0700242 /** Used by native code, do not rename. make it volatile, so it is thread-safe. */
243 /* package */ volatile int mNativeHandle = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800244
Vasu Noria8c24902010-06-01 11:30:27 -0700245 /**
246 * The size, in bytes, of a block on "/data". This corresponds to the Unix
247 * statfs.f_bsize field. note that this field is lazily initialized.
248 */
249 private static int sBlockSize = 0;
250
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800251 /** The path for the database file */
Vasu Noriccd95442010-05-28 17:04:16 -0700252 private final String mPath;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800253
Brad Fitzpatrickd8330232010-02-19 10:59:01 -0800254 /** The anonymized path for the database file for logging purposes */
255 private String mPathForLogs = null; // lazily populated
256
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800257 /** The flags passed to open/create */
Vasu Noriccd95442010-05-28 17:04:16 -0700258 private final int mFlags;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800259
260 /** The optional factory to use when creating new Cursors */
Vasu Noriccd95442010-05-28 17:04:16 -0700261 private final CursorFactory mFactory;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700262
Vasu Nori21343692010-06-03 16:01:39 -0700263 private final WeakHashMap<SQLiteClosable, Object> mPrograms;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700264
Vasu Nori5a03f362009-10-20 15:16:35 -0700265 /**
Vasu Norib729dcc2010-09-14 11:35:49 -0700266 * for each instance of this class, a LRU cache is maintained to store
267 * the compiled query statement ids returned by sqlite database.
268 * key = SQL statement with "?" for bind args
269 * value = {@link SQLiteCompiledSql}
270 * If an application opens the database and keeps it open during its entire life, then
271 * there will not be an overhead of compilation of SQL statements by sqlite.
272 *
273 * why is this cache NOT static? because sqlite attaches compiledsql statements to the
274 * struct created when {@link SQLiteDatabase#openDatabase(String, CursorFactory, int)} is
275 * invoked.
276 *
277 * this cache has an upper limit of mMaxSqlCacheSize (settable by calling the method
278 * (@link #setMaxSqlCacheSize(int)}).
279 */
280 // default statement-cache size per database connection ( = instance of this class)
281 private int mMaxSqlCacheSize = 25;
Vasu Nori24675612010-09-27 14:54:19 -0700282 // guarded by itself
Vasu Norib729dcc2010-09-14 11:35:49 -0700283 /* package */ final Map<String, SQLiteCompiledSql> mCompiledQueries =
284 new LinkedHashMap<String, SQLiteCompiledSql>(mMaxSqlCacheSize + 1, 0.75f, true) {
285 @Override
286 public boolean removeEldestEntry(Map.Entry<String, SQLiteCompiledSql> eldest) {
287 // eldest = least-recently used entry
288 // if it needs to be removed to accommodate a new entry,
289 // close {@link SQLiteCompiledSql} represented by this entry, if not in use
290 // and then let it be removed from the Map.
291 // when this is called, the caller must be trying to add a just-compiled stmt
292 // to cache; i.e., caller should already have acquired database lock AND
293 // the lock on mCompiledQueries. do as assert of these two 2 facts.
294 verifyLockOwner();
295 if (this.size() <= mMaxSqlCacheSize) {
296 // cache is not full. nothing needs to be removed
297 return false;
298 }
299 // cache is full. eldest will be removed.
Vasu Nori5e89ae22010-09-15 14:23:29 -0700300 eldest.getValue().releaseIfNotInUse();
Vasu Norib729dcc2010-09-14 11:35:49 -0700301 // return true, so that this entry is removed automatically by the caller.
302 return true;
303 }
304 };
305 /**
306 * absolute max value that can be set by {@link #setMaxSqlCacheSize(int)}
307 * size of each prepared-statement is between 1K - 6K, depending on the complexity of the
Vasu Noriccd95442010-05-28 17:04:16 -0700308 * SQL statement & schema.
Vasu Norie495d1f2010-01-06 16:34:19 -0800309 */
Vasu Nori90a367262010-04-12 12:49:09 -0700310 public static final int MAX_SQL_CACHE_SIZE = 100;
Vasu Nori5e89ae22010-09-15 14:23:29 -0700311 private boolean mCacheFullWarning;
Vasu Norib729dcc2010-09-14 11:35:49 -0700312
Vasu Nori24675612010-09-27 14:54:19 -0700313 /** Number of cache hits on this database connection. guarded by {@link #mCompiledQueries}. */
Vasu Norib729dcc2010-09-14 11:35:49 -0700314 private int mNumCacheHits;
Vasu Nori24675612010-09-27 14:54:19 -0700315 /** Number of cache misses on this database connection. guarded by {@link #mCompiledQueries}. */
Vasu Norib729dcc2010-09-14 11:35:49 -0700316 private int mNumCacheMisses;
Vasu Nori5a03f362009-10-20 15:16:35 -0700317
Vasu Norid606b4b2010-02-24 12:54:20 -0800318 /** Used to find out where this object was created in case it never got closed. */
Vasu Nori21343692010-06-03 16:01:39 -0700319 private final Throwable mStackTrace;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320
Dmitri Plotnikov90142c92009-09-15 10:52:17 -0700321 // System property that enables logging of slow queries. Specify the threshold in ms.
322 private static final String LOG_SLOW_QUERIES_PROPERTY = "db.log.slow_query_threshold";
323 private final int mSlowQueryThreshold;
324
Vasu Nori6f37f832010-05-19 11:53:25 -0700325 /** stores the list of statement ids that need to be finalized by sqlite */
Vasu Nori21343692010-06-03 16:01:39 -0700326 private final ArrayList<Integer> mClosedStatementIds = new ArrayList<Integer>();
Vasu Nori6f37f832010-05-19 11:53:25 -0700327
Vasu Nori062fc7ce2010-03-31 16:13:05 -0700328 /** {@link DatabaseErrorHandler} to be used when SQLite returns any of the following errors
329 * Corruption
330 * */
Vasu Nori21343692010-06-03 16:01:39 -0700331 private final DatabaseErrorHandler mErrorHandler;
Vasu Nori062fc7ce2010-03-31 16:13:05 -0700332
Vasu Nori6c354da2010-04-26 23:33:39 -0700333 /** The Database connection pool {@link DatabaseConnectionPool}.
334 * Visibility is package-private for testing purposes. otherwise, private visibility is enough.
335 */
336 /* package */ volatile DatabaseConnectionPool mConnectionPool = null;
337
338 /** Each database connection handle in the pool is assigned a number 1..N, where N is the
339 * size of the connection pool.
340 * The main connection handle to which the pool is attached is assigned a value of 0.
341 */
342 /* package */ final short mConnectionNum;
343
Vasu Nori65a88832010-07-16 15:14:08 -0700344 /** on pooled database connections, this member points to the parent ( = main)
345 * database connection handle.
346 * package visibility only for testing purposes
347 */
348 /* package */ SQLiteDatabase mParentConnObj = null;
349
Vasu Noria98cb262010-06-22 13:16:35 -0700350 private static final String MEMORY_DB_PATH = ":memory:";
351
Vasu Nori24675612010-09-27 14:54:19 -0700352 /** set to true if the database has attached databases */
353 private volatile boolean mHasAttachedDbs = false;
354
Vasu Nori0732f792010-07-29 17:24:12 -0700355 /** stores reference to all databases opened in the current process. */
356 private static ArrayList<WeakReference<SQLiteDatabase>> mActiveDatabases =
357 new ArrayList<WeakReference<SQLiteDatabase>>();
358
Vasu Nori2827d6d2010-07-04 00:26:18 -0700359 synchronized void addSQLiteClosable(SQLiteClosable closable) {
360 // mPrograms is per instance of SQLiteDatabase and it doesn't actually touch the database
361 // itself. so, there is no need to lock().
362 mPrograms.put(closable, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700364
Vasu Nori2827d6d2010-07-04 00:26:18 -0700365 synchronized void removeSQLiteClosable(SQLiteClosable closable) {
366 mPrograms.remove(closable);
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700367 }
368
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369 @Override
370 protected void onAllReferencesReleased() {
371 if (isOpen()) {
Vasu Noriad239ab2010-06-14 16:58:47 -0700372 // close the database which will close all pending statements to be finalized also
373 close();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374 }
375 }
376
377 /**
378 * Attempts to release memory that SQLite holds but does not require to
379 * operate properly. Typically this memory will come from the page cache.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700380 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800381 * @return the number of bytes actually released
382 */
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700383 static public native int releaseMemory();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384
385 /**
386 * Control whether or not the SQLiteDatabase is made thread-safe by using locks
387 * around critical sections. This is pretty expensive, so if you know that your
388 * DB will only be used by a single thread then you should set this to false.
389 * The default is true.
390 * @param lockingEnabled set to true to enable locks, false otherwise
391 */
392 public void setLockingEnabled(boolean lockingEnabled) {
393 mLockingEnabled = lockingEnabled;
394 }
395
396 /**
397 * If set then the SQLiteDatabase is made thread-safe by using locks
398 * around critical sections
399 */
400 private boolean mLockingEnabled = true;
401
402 /* package */ void onCorruption() {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700403 EventLog.writeEvent(EVENT_DB_CORRUPT, mPath);
Vasu Noriccd95442010-05-28 17:04:16 -0700404 mErrorHandler.onCorruption(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800405 }
406
407 /**
408 * Locks the database for exclusive access. The database lock must be held when
409 * touch the native sqlite3* object since it is single threaded and uses
410 * a polling lock contention algorithm. The lock is recursive, and may be acquired
411 * multiple times by the same thread. This is a no-op if mLockingEnabled is false.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700412 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 * @see #unlock()
414 */
415 /* package */ void lock() {
Vasu Nori6d970252010-10-05 10:48:49 -0700416 lock(false);
417 }
Vasu Norid4608a32011-02-03 16:24:06 -0800418 private static final long LOCK_WAIT_PERIOD = 30L;
Vasu Nori6d970252010-10-05 10:48:49 -0700419 private void lock(boolean forced) {
420 // make sure this method is NOT being called from a 'synchronized' method
421 if (Thread.holdsLock(this)) {
Vasu Nori4b92aee2011-01-26 23:22:34 -0800422 Log.w(TAG, "don't lock() while in a synchronized method");
Vasu Nori6d970252010-10-05 10:48:49 -0700423 }
Vasu Nori7b04c412010-07-20 10:31:21 -0700424 verifyDbIsOpen();
Vasu Nori6d970252010-10-05 10:48:49 -0700425 if (!forced && !mLockingEnabled) return;
Vasu Norid4608a32011-02-03 16:24:06 -0800426 boolean done = false;
427 while (!done) {
428 try {
429 // wait for 30sec to acquire the lock
430 done = mLock.tryLock(LOCK_WAIT_PERIOD, TimeUnit.SECONDS);
431 if (!done) {
432 // lock not acquired in NSec. print a message and stacktrace saying the lock
433 // has not been available for 30sec.
434 Log.w(TAG, "database lock has not been available for " + LOCK_WAIT_PERIOD +
435 " sec. Current Owner of the lock is " + mLock.getOwnerDescription() +
436 ". Continuing to wait");
437 }
438 } catch (InterruptedException e) {
439 // ignore the interruption
440 }
441 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800442 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
443 if (mLock.getHoldCount() == 1) {
444 // Use elapsed real-time since the CPU may sleep when waiting for IO
445 mLockAcquiredWallTime = SystemClock.elapsedRealtime();
446 mLockAcquiredThreadTime = Debug.threadCpuTimeNanos();
447 }
448 }
449 }
Vasu Norid4608a32011-02-03 16:24:06 -0800450 private static class DatabaseReentrantLock extends ReentrantLock {
451 DatabaseReentrantLock(boolean fair) {
452 super(fair);
453 }
454 @Override
455 public Thread getOwner() {
456 return super.getOwner();
457 }
458 public String getOwnerDescription() {
459 Thread t = getOwner();
460 return (t== null) ? "none" : String.valueOf(t.getId());
461 }
462 }
463
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800464 /**
465 * Locks the database for exclusive access. The database lock must be held when
466 * touch the native sqlite3* object since it is single threaded and uses
467 * a polling lock contention algorithm. The lock is recursive, and may be acquired
468 * multiple times by the same thread.
469 *
470 * @see #unlockForced()
471 */
472 private void lockForced() {
Vasu Nori6d970252010-10-05 10:48:49 -0700473 lock(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 }
475
476 /**
477 * Releases the database lock. This is a no-op if mLockingEnabled is false.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700478 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800479 * @see #unlock()
480 */
481 /* package */ void unlock() {
482 if (!mLockingEnabled) return;
483 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
484 if (mLock.getHoldCount() == 1) {
485 checkLockHoldTime();
486 }
487 }
488 mLock.unlock();
489 }
490
491 /**
492 * Releases the database lock.
493 *
494 * @see #unlockForced()
495 */
496 private void unlockForced() {
497 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
498 if (mLock.getHoldCount() == 1) {
499 checkLockHoldTime();
500 }
501 }
502 mLock.unlock();
503 }
504
505 private void checkLockHoldTime() {
506 // Use elapsed real-time since the CPU may sleep when waiting for IO
507 long elapsedTime = SystemClock.elapsedRealtime();
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700508 long lockedTime = elapsedTime - mLockAcquiredWallTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509 if (lockedTime < LOCK_ACQUIRED_WARNING_TIME_IN_MS_ALWAYS_PRINT &&
510 !Log.isLoggable(TAG, Log.VERBOSE) &&
511 (elapsedTime - mLastLockMessageTime) < LOCK_WARNING_WINDOW_IN_MS) {
512 return;
513 }
514 if (lockedTime > LOCK_ACQUIRED_WARNING_TIME_IN_MS) {
515 int threadTime = (int)
516 ((Debug.threadCpuTimeNanos() - mLockAcquiredThreadTime) / 1000000);
517 if (threadTime > LOCK_ACQUIRED_WARNING_THREAD_TIME_IN_MS ||
518 lockedTime > LOCK_ACQUIRED_WARNING_TIME_IN_MS_ALWAYS_PRINT) {
519 mLastLockMessageTime = elapsedTime;
520 String msg = "lock held on " + mPath + " for " + lockedTime + "ms. Thread time was "
521 + threadTime + "ms";
522 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING_STACK_TRACE) {
523 Log.d(TAG, msg, new Exception());
524 } else {
525 Log.d(TAG, msg);
526 }
527 }
528 }
529 }
530
531 /**
Vasu Noriccd95442010-05-28 17:04:16 -0700532 * Begins a transaction in EXCLUSIVE mode.
533 * <p>
534 * Transactions can be nested.
535 * When the outer transaction is ended all of
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800536 * the work done in that transaction and all of the nested transactions will be committed or
537 * rolled back. The changes will be rolled back if any transaction is ended without being
538 * marked as clean (by calling setTransactionSuccessful). Otherwise they will be committed.
Vasu Noriccd95442010-05-28 17:04:16 -0700539 * </p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800540 * <p>Here is the standard idiom for transactions:
541 *
542 * <pre>
543 * db.beginTransaction();
544 * try {
545 * ...
546 * db.setTransactionSuccessful();
547 * } finally {
548 * db.endTransaction();
549 * }
550 * </pre>
551 */
552 public void beginTransaction() {
Vasu Nori6c354da2010-04-26 23:33:39 -0700553 beginTransaction(null /* transactionStatusCallback */, true);
554 }
555
556 /**
557 * Begins a transaction in IMMEDIATE mode. Transactions can be nested. When
558 * the outer transaction is ended all of the work done in that transaction
559 * and all of the nested transactions will be committed or rolled back. The
560 * changes will be rolled back if any transaction is ended without being
561 * marked as clean (by calling setTransactionSuccessful). Otherwise they
562 * will be committed.
563 * <p>
564 * Here is the standard idiom for transactions:
565 *
566 * <pre>
567 * db.beginTransactionNonExclusive();
568 * try {
569 * ...
570 * db.setTransactionSuccessful();
571 * } finally {
572 * db.endTransaction();
573 * }
574 * </pre>
575 */
576 public void beginTransactionNonExclusive() {
577 beginTransaction(null /* transactionStatusCallback */, false);
Fred Quintanac4516a72009-09-03 12:14:06 -0700578 }
579
580 /**
Vasu Noriccd95442010-05-28 17:04:16 -0700581 * Begins a transaction in EXCLUSIVE mode.
582 * <p>
583 * Transactions can be nested.
584 * When the outer transaction is ended all of
Fred Quintanac4516a72009-09-03 12:14:06 -0700585 * the work done in that transaction and all of the nested transactions will be committed or
586 * rolled back. The changes will be rolled back if any transaction is ended without being
587 * marked as clean (by calling setTransactionSuccessful). Otherwise they will be committed.
Vasu Noriccd95442010-05-28 17:04:16 -0700588 * </p>
Fred Quintanac4516a72009-09-03 12:14:06 -0700589 * <p>Here is the standard idiom for transactions:
590 *
591 * <pre>
592 * db.beginTransactionWithListener(listener);
593 * try {
594 * ...
595 * db.setTransactionSuccessful();
596 * } finally {
597 * db.endTransaction();
598 * }
599 * </pre>
Vasu Noriccd95442010-05-28 17:04:16 -0700600 *
Fred Quintanac4516a72009-09-03 12:14:06 -0700601 * @param transactionListener listener that should be notified when the transaction begins,
602 * commits, or is rolled back, either explicitly or by a call to
603 * {@link #yieldIfContendedSafely}.
604 */
605 public void beginTransactionWithListener(SQLiteTransactionListener transactionListener) {
Vasu Nori6c354da2010-04-26 23:33:39 -0700606 beginTransaction(transactionListener, true);
607 }
608
609 /**
610 * Begins a transaction in IMMEDIATE mode. Transactions can be nested. When
611 * the outer transaction is ended all of the work done in that transaction
612 * and all of the nested transactions will be committed or rolled back. The
613 * changes will be rolled back if any transaction is ended without being
614 * marked as clean (by calling setTransactionSuccessful). Otherwise they
615 * will be committed.
616 * <p>
617 * Here is the standard idiom for transactions:
618 *
619 * <pre>
620 * db.beginTransactionWithListenerNonExclusive(listener);
621 * try {
622 * ...
623 * db.setTransactionSuccessful();
624 * } finally {
625 * db.endTransaction();
626 * }
627 * </pre>
628 *
629 * @param transactionListener listener that should be notified when the
630 * transaction begins, commits, or is rolled back, either
631 * explicitly or by a call to {@link #yieldIfContendedSafely}.
632 */
633 public void beginTransactionWithListenerNonExclusive(
634 SQLiteTransactionListener transactionListener) {
635 beginTransaction(transactionListener, false);
636 }
637
638 private void beginTransaction(SQLiteTransactionListener transactionListener,
639 boolean exclusive) {
Vasu Noriccd95442010-05-28 17:04:16 -0700640 verifyDbIsOpen();
Vasu Noric8e1f232010-04-13 15:05:09 -0700641 lockForced();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800642 boolean ok = false;
643 try {
644 // If this thread already had the lock then get out
645 if (mLock.getHoldCount() > 1) {
646 if (mInnerTransactionIsSuccessful) {
647 String msg = "Cannot call beginTransaction between "
648 + "calling setTransactionSuccessful and endTransaction";
649 IllegalStateException e = new IllegalStateException(msg);
650 Log.e(TAG, "beginTransaction() failed", e);
651 throw e;
652 }
653 ok = true;
654 return;
655 }
656
657 // This thread didn't already have the lock, so begin a database
658 // transaction now.
Vasu Nori57feb5d2010-06-22 10:39:04 -0700659 if (exclusive && mConnectionPool == null) {
Vasu Nori6c354da2010-04-26 23:33:39 -0700660 execSQL("BEGIN EXCLUSIVE;");
661 } else {
662 execSQL("BEGIN IMMEDIATE;");
663 }
Fred Quintanac4516a72009-09-03 12:14:06 -0700664 mTransactionListener = transactionListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800665 mTransactionIsSuccessful = true;
666 mInnerTransactionIsSuccessful = false;
Fred Quintanac4516a72009-09-03 12:14:06 -0700667 if (transactionListener != null) {
668 try {
669 transactionListener.onBegin();
670 } catch (RuntimeException e) {
671 execSQL("ROLLBACK;");
672 throw e;
673 }
674 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800675 ok = true;
676 } finally {
677 if (!ok) {
678 // beginTransaction is called before the try block so we must release the lock in
679 // the case of failure.
680 unlockForced();
681 }
682 }
683 }
684
685 /**
686 * End a transaction. See beginTransaction for notes about how to use this and when transactions
687 * are committed and rolled back.
688 */
689 public void endTransaction() {
Vasu Noriccd95442010-05-28 17:04:16 -0700690 verifyLockOwner();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800691 try {
692 if (mInnerTransactionIsSuccessful) {
693 mInnerTransactionIsSuccessful = false;
694 } else {
695 mTransactionIsSuccessful = false;
696 }
697 if (mLock.getHoldCount() != 1) {
698 return;
699 }
Fred Quintanac4516a72009-09-03 12:14:06 -0700700 RuntimeException savedException = null;
701 if (mTransactionListener != null) {
702 try {
703 if (mTransactionIsSuccessful) {
704 mTransactionListener.onCommit();
705 } else {
706 mTransactionListener.onRollback();
707 }
708 } catch (RuntimeException e) {
709 savedException = e;
710 mTransactionIsSuccessful = false;
711 }
712 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800713 if (mTransactionIsSuccessful) {
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -0800714 execSQL(COMMIT_SQL);
Vasu Nori6c354da2010-04-26 23:33:39 -0700715 // if write-ahead logging is used, we have to take care of checkpoint.
716 // TODO: should applications be given the flexibility of choosing when to
717 // trigger checkpoint?
718 // for now, do checkpoint after every COMMIT because that is the fastest
719 // way to guarantee that readers will see latest data.
720 // but this is the slowest way to run sqlite with in write-ahead logging mode.
721 if (this.mConnectionPool != null) {
722 execSQL("PRAGMA wal_checkpoint;");
723 if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
724 Log.i(TAG, "PRAGMA wal_Checkpoint done");
725 }
726 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800727 } else {
728 try {
729 execSQL("ROLLBACK;");
Fred Quintanac4516a72009-09-03 12:14:06 -0700730 if (savedException != null) {
731 throw savedException;
732 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800733 } catch (SQLException e) {
734 if (Config.LOGD) {
735 Log.d(TAG, "exception during rollback, maybe the DB previously "
736 + "performed an auto-rollback");
737 }
738 }
739 }
740 } finally {
Fred Quintanac4516a72009-09-03 12:14:06 -0700741 mTransactionListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800742 unlockForced();
743 if (Config.LOGV) {
744 Log.v(TAG, "unlocked " + Thread.currentThread()
745 + ", holdCount is " + mLock.getHoldCount());
746 }
747 }
748 }
749
750 /**
751 * Marks the current transaction as successful. Do not do any more database work between
752 * calling this and calling endTransaction. Do as little non-database work as possible in that
753 * situation too. If any errors are encountered between this and endTransaction the transaction
754 * will still be committed.
755 *
756 * @throws IllegalStateException if the current thread is not in a transaction or the
757 * transaction is already marked as successful.
758 */
759 public void setTransactionSuccessful() {
Vasu Noriccd95442010-05-28 17:04:16 -0700760 verifyDbIsOpen();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800761 if (!mLock.isHeldByCurrentThread()) {
762 throw new IllegalStateException("no transaction pending");
763 }
764 if (mInnerTransactionIsSuccessful) {
765 throw new IllegalStateException(
766 "setTransactionSuccessful may only be called once per call to beginTransaction");
767 }
768 mInnerTransactionIsSuccessful = true;
769 }
770
771 /**
772 * return true if there is a transaction pending
773 */
774 public boolean inTransaction() {
Vasu Norice38b982010-07-22 13:57:13 -0700775 return mLock.getHoldCount() > 0 || mTransactionUsingExecSql;
776 }
777
778 /* package */ synchronized void setTransactionUsingExecSqlFlag() {
779 if (Log.isLoggable(TAG, Log.DEBUG)) {
780 Log.i(TAG, "found execSQL('begin transaction')");
781 }
782 mTransactionUsingExecSql = true;
783 }
784
785 /* package */ synchronized void resetTransactionUsingExecSqlFlag() {
786 if (Log.isLoggable(TAG, Log.DEBUG)) {
787 if (mTransactionUsingExecSql) {
788 Log.i(TAG, "found execSQL('commit or end or rollback')");
789 }
790 }
791 mTransactionUsingExecSql = false;
792 }
793
794 /**
795 * Returns true if the caller is considered part of the current transaction, if any.
796 * <p>
797 * Caller is part of the current transaction if either of the following is true
798 * <ol>
799 * <li>If transaction is started by calling beginTransaction() methods AND if the caller is
800 * in the same thread as the thread that started the transaction.
801 * </li>
802 * <li>If the transaction is started by calling {@link #execSQL(String)} like this:
803 * execSQL("BEGIN transaction"). In this case, every thread in the process is considered
804 * part of the current transaction.</li>
805 * </ol>
806 *
807 * @return true if the caller is considered part of the current transaction, if any.
808 */
809 /* package */ synchronized boolean amIInTransaction() {
810 // always do this test on the main database connection - NOT on pooled database connection
811 // since transactions always occur on the main database connections only.
812 SQLiteDatabase db = (isPooledConnection()) ? mParentConnObj : this;
813 boolean b = (!db.inTransaction()) ? false :
814 db.mTransactionUsingExecSql || db.mLock.isHeldByCurrentThread();
815 if (Log.isLoggable(TAG, Log.DEBUG)) {
816 Log.i(TAG, "amIinTransaction: " + b);
817 }
818 return b;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800819 }
820
821 /**
822 * Checks if the database lock is held by this thread.
823 *
824 * @return true, if this thread is holding the database lock.
825 */
826 public boolean isDbLockedByCurrentThread() {
827 return mLock.isHeldByCurrentThread();
828 }
829
830 /**
831 * Checks if the database is locked by another thread. This is
832 * just an estimate, since this status can change at any time,
833 * including after the call is made but before the result has
834 * been acted upon.
835 *
836 * @return true, if the database is locked by another thread
837 */
838 public boolean isDbLockedByOtherThreads() {
839 return !mLock.isHeldByCurrentThread() && mLock.isLocked();
840 }
841
842 /**
843 * Temporarily end the transaction to let other threads run. The transaction is assumed to be
844 * successful so far. Do not call setTransactionSuccessful before calling this. When this
845 * returns a new transaction will have been created but not marked as successful.
846 * @return true if the transaction was yielded
847 * @deprecated if the db is locked more than once (becuase of nested transactions) then the lock
848 * will not be yielded. Use yieldIfContendedSafely instead.
849 */
Dianne Hackborn4a51c202009-08-21 15:14:02 -0700850 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800851 public boolean yieldIfContended() {
Fred Quintana5c7aede2009-08-27 21:41:27 -0700852 return yieldIfContendedHelper(false /* do not check yielding */,
853 -1 /* sleepAfterYieldDelay */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800854 }
855
856 /**
857 * Temporarily end the transaction to let other threads run. The transaction is assumed to be
858 * successful so far. Do not call setTransactionSuccessful before calling this. When this
859 * returns a new transaction will have been created but not marked as successful. This assumes
860 * that there are no nested transactions (beginTransaction has only been called once) and will
Fred Quintana5c7aede2009-08-27 21:41:27 -0700861 * throw an exception if that is not the case.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800862 * @return true if the transaction was yielded
863 */
864 public boolean yieldIfContendedSafely() {
Fred Quintana5c7aede2009-08-27 21:41:27 -0700865 return yieldIfContendedHelper(true /* check yielding */, -1 /* sleepAfterYieldDelay*/);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800866 }
867
Fred Quintana5c7aede2009-08-27 21:41:27 -0700868 /**
869 * Temporarily end the transaction to let other threads run. The transaction is assumed to be
870 * successful so far. Do not call setTransactionSuccessful before calling this. When this
871 * returns a new transaction will have been created but not marked as successful. This assumes
872 * that there are no nested transactions (beginTransaction has only been called once) and will
873 * throw an exception if that is not the case.
874 * @param sleepAfterYieldDelay if > 0, sleep this long before starting a new transaction if
875 * the lock was actually yielded. This will allow other background threads to make some
876 * more progress than they would if we started the transaction immediately.
877 * @return true if the transaction was yielded
878 */
879 public boolean yieldIfContendedSafely(long sleepAfterYieldDelay) {
880 return yieldIfContendedHelper(true /* check yielding */, sleepAfterYieldDelay);
881 }
882
883 private boolean yieldIfContendedHelper(boolean checkFullyYielded, long sleepAfterYieldDelay) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800884 if (mLock.getQueueLength() == 0) {
885 // Reset the lock acquire time since we know that the thread was willing to yield
886 // the lock at this time.
887 mLockAcquiredWallTime = SystemClock.elapsedRealtime();
888 mLockAcquiredThreadTime = Debug.threadCpuTimeNanos();
889 return false;
890 }
891 setTransactionSuccessful();
Fred Quintanac4516a72009-09-03 12:14:06 -0700892 SQLiteTransactionListener transactionListener = mTransactionListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800893 endTransaction();
894 if (checkFullyYielded) {
895 if (this.isDbLockedByCurrentThread()) {
896 throw new IllegalStateException(
897 "Db locked more than once. yielfIfContended cannot yield");
898 }
899 }
Fred Quintana5c7aede2009-08-27 21:41:27 -0700900 if (sleepAfterYieldDelay > 0) {
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700901 // Sleep for up to sleepAfterYieldDelay milliseconds, waking up periodically to
902 // check if anyone is using the database. If the database is not contended,
903 // retake the lock and return.
904 long remainingDelay = sleepAfterYieldDelay;
905 while (remainingDelay > 0) {
906 try {
907 Thread.sleep(remainingDelay < SLEEP_AFTER_YIELD_QUANTUM ?
908 remainingDelay : SLEEP_AFTER_YIELD_QUANTUM);
909 } catch (InterruptedException e) {
910 Thread.interrupted();
911 }
912 remainingDelay -= SLEEP_AFTER_YIELD_QUANTUM;
913 if (mLock.getQueueLength() == 0) {
914 break;
915 }
Fred Quintana5c7aede2009-08-27 21:41:27 -0700916 }
917 }
Fred Quintanac4516a72009-09-03 12:14:06 -0700918 beginTransactionWithListener(transactionListener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800919 return true;
920 }
921
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800922 /**
Vasu Nori95675132010-07-21 16:24:40 -0700923 * @deprecated This method no longer serves any useful purpose and has been deprecated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800924 */
Vasu Nori95675132010-07-21 16:24:40 -0700925 @Deprecated
926 public Map<String, String> getSyncedTables() {
927 return new HashMap<String, String>(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800928 }
929
930 /**
931 * Used to allow returning sub-classes of {@link Cursor} when calling query.
932 */
933 public interface CursorFactory {
934 /**
935 * See
Vasu Noribfe1dc22010-08-25 16:29:02 -0700936 * {@link SQLiteCursor#SQLiteCursor(SQLiteCursorDriver, String, SQLiteQuery)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800937 */
938 public Cursor newCursor(SQLiteDatabase db,
939 SQLiteCursorDriver masterQuery, String editTable,
940 SQLiteQuery query);
941 }
942
943 /**
944 * Open the database according to the flags {@link #OPEN_READWRITE}
945 * {@link #OPEN_READONLY} {@link #CREATE_IF_NECESSARY} and/or {@link #NO_LOCALIZED_COLLATORS}.
946 *
947 * <p>Sets the locale of the database to the the system's current locale.
948 * Call {@link #setLocale} if you would like something else.</p>
949 *
950 * @param path to database file to open and/or create
951 * @param factory an optional factory class that is called to instantiate a
952 * cursor when query is called, or null for default
953 * @param flags to control database access mode
954 * @return the newly opened database
955 * @throws SQLiteException if the database cannot be opened
956 */
957 public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags) {
Vasu Nori062fc7ce2010-03-31 16:13:05 -0700958 return openDatabase(path, factory, flags, new DefaultDatabaseErrorHandler());
959 }
960
961 /**
Vasu Nori74f170f2010-06-01 18:06:18 -0700962 * Open the database according to the flags {@link #OPEN_READWRITE}
963 * {@link #OPEN_READONLY} {@link #CREATE_IF_NECESSARY} and/or {@link #NO_LOCALIZED_COLLATORS}.
964 *
965 * <p>Sets the locale of the database to the the system's current locale.
966 * Call {@link #setLocale} if you would like something else.</p>
967 *
968 * <p>Accepts input param: a concrete instance of {@link DatabaseErrorHandler} to be
969 * used to handle corruption when sqlite reports database corruption.</p>
970 *
971 * @param path to database file to open and/or create
972 * @param factory an optional factory class that is called to instantiate a
973 * cursor when query is called, or null for default
974 * @param flags to control database access mode
975 * @param errorHandler the {@link DatabaseErrorHandler} obj to be used to handle corruption
976 * when sqlite reports database corruption
977 * @return the newly opened database
978 * @throws SQLiteException if the database cannot be opened
Vasu Nori062fc7ce2010-03-31 16:13:05 -0700979 */
980 public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags,
981 DatabaseErrorHandler errorHandler) {
Vasu Nori6c354da2010-04-26 23:33:39 -0700982 SQLiteDatabase sqliteDatabase = openDatabase(path, factory, flags, errorHandler,
983 (short) 0 /* the main connection handle */);
Vasu Noria8c24902010-06-01 11:30:27 -0700984
985 // set sqlite pagesize to mBlockSize
986 if (sBlockSize == 0) {
987 // TODO: "/data" should be a static final String constant somewhere. it is hardcoded
988 // in several places right now.
989 sBlockSize = new StatFs("/data").getBlockSize();
990 }
991 sqliteDatabase.setPageSize(sBlockSize);
Vasu Noria22d8842011-01-06 08:30:29 -0800992 sqliteDatabase.setJournalMode(path, "TRUNCATE");
Vasu Norif9e2bd02010-06-04 16:49:51 -0700993
Vasu Noriccd95442010-05-28 17:04:16 -0700994 // add this database to the list of databases opened in this process
Vasu Nori0732f792010-07-29 17:24:12 -0700995 synchronized(mActiveDatabases) {
996 mActiveDatabases.add(new WeakReference<SQLiteDatabase>(sqliteDatabase));
997 }
Vasu Noric3849202010-03-09 10:47:25 -0800998 return sqliteDatabase;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800999 }
1000
Vasu Nori6c354da2010-04-26 23:33:39 -07001001 private static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags,
1002 DatabaseErrorHandler errorHandler, short connectionNum) {
1003 SQLiteDatabase db = new SQLiteDatabase(path, factory, flags, errorHandler, connectionNum);
Vasu Nori062fc7ce2010-03-31 16:13:05 -07001004 try {
Vasu Norice38b982010-07-22 13:57:13 -07001005 if (Log.isLoggable(TAG, Log.DEBUG)) {
1006 Log.i(TAG, "opening the db : " + path);
1007 }
Vasu Nori6c354da2010-04-26 23:33:39 -07001008 // Open the database.
1009 db.dbopen(path, flags);
1010 db.setLocale(Locale.getDefault());
1011 if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
1012 db.enableSqlTracing(path, connectionNum);
1013 }
1014 if (SQLiteDebug.DEBUG_SQL_TIME) {
1015 db.enableSqlProfiling(path, connectionNum);
1016 }
1017 return db;
1018 } catch (SQLiteDatabaseCorruptException e) {
Vasu Nori6a904bc2011-01-05 18:35:40 -08001019 db.mErrorHandler.onCorruption(db);
1020 return SQLiteDatabase.openDatabase(path, factory, flags, errorHandler);
Vasu Nori6c354da2010-04-26 23:33:39 -07001021 } catch (SQLiteException e) {
1022 Log.e(TAG, "Failed to open the database. closing it.", e);
1023 db.close();
Vasu Nori062fc7ce2010-03-31 16:13:05 -07001024 throw e;
1025 }
1026 }
1027
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001028 /**
1029 * Equivalent to openDatabase(file.getPath(), factory, CREATE_IF_NECESSARY).
1030 */
1031 public static SQLiteDatabase openOrCreateDatabase(File file, CursorFactory factory) {
1032 return openOrCreateDatabase(file.getPath(), factory);
1033 }
1034
1035 /**
1036 * Equivalent to openDatabase(path, factory, CREATE_IF_NECESSARY).
1037 */
1038 public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory) {
1039 return openDatabase(path, factory, CREATE_IF_NECESSARY);
1040 }
1041
1042 /**
Vasu Nori6c354da2010-04-26 23:33:39 -07001043 * Equivalent to openDatabase(path, factory, CREATE_IF_NECESSARY, errorHandler).
Vasu Nori062fc7ce2010-03-31 16:13:05 -07001044 */
1045 public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory,
1046 DatabaseErrorHandler errorHandler) {
1047 return openDatabase(path, factory, CREATE_IF_NECESSARY, errorHandler);
1048 }
1049
Vasu Noria98cb262010-06-22 13:16:35 -07001050 private void setJournalMode(final String dbPath, final String mode) {
1051 // journal mode can be set only for non-memory databases
Vasu Nori8fcda302010-11-08 13:46:40 -08001052 // AND can't be set for readonly databases
1053 if (dbPath.equalsIgnoreCase(MEMORY_DB_PATH) || isReadOnly()) {
1054 return;
1055 }
1056 String s = DatabaseUtils.stringForQuery(this, "PRAGMA journal_mode=" + mode, null);
1057 if (!s.equalsIgnoreCase(mode)) {
1058 Log.e(TAG, "setting journal_mode to " + mode + " failed for db: " + dbPath +
1059 " (on pragma set journal_mode, sqlite returned:" + s);
Vasu Noria98cb262010-06-22 13:16:35 -07001060 }
1061 }
1062
Vasu Nori062fc7ce2010-03-31 16:13:05 -07001063 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001064 * Create a memory backed SQLite database. Its contents will be destroyed
1065 * when the database is closed.
1066 *
1067 * <p>Sets the locale of the database to the the system's current locale.
1068 * Call {@link #setLocale} if you would like something else.</p>
1069 *
1070 * @param factory an optional factory class that is called to instantiate a
1071 * cursor when query is called
1072 * @return a SQLiteDatabase object, or null if the database can't be created
1073 */
1074 public static SQLiteDatabase create(CursorFactory factory) {
1075 // This is a magic string with special meaning for SQLite.
Vasu Noria98cb262010-06-22 13:16:35 -07001076 return openDatabase(MEMORY_DB_PATH, factory, CREATE_IF_NECESSARY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001077 }
1078
1079 /**
1080 * Close the database.
1081 */
1082 public void close() {
Vasu Nori587423a2010-09-27 18:18:34 -07001083 if (!isOpen()) {
1084 return;
1085 }
Vasu Norice38b982010-07-22 13:57:13 -07001086 if (Log.isLoggable(TAG, Log.DEBUG)) {
Vasu Nori75010102010-07-01 16:23:06 -07001087 Log.i(TAG, "closing db: " + mPath + " (connection # " + mConnectionNum);
1088 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001089 lock();
1090 try {
Vasu Noriffe06122010-09-27 12:32:57 -07001091 // some other thread could have closed this database while I was waiting for lock.
1092 // check the database state
1093 if (!isOpen()) {
1094 return;
1095 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001096 closeClosable();
Vasu Norifea6f6d2010-05-21 15:36:06 -07001097 // finalize ALL statements queued up so far
1098 closePendingStatements();
Mike Lockwood9d9c1be2010-07-13 19:27:52 -04001099 releaseCustomFunctions();
Vasu Norif6373e92010-03-16 10:21:00 -07001100 // close this database instance - regardless of its reference count value
Vasu Nori422dad02010-09-03 16:03:08 -07001101 closeDatabase();
Vasu Nori6c354da2010-04-26 23:33:39 -07001102 if (mConnectionPool != null) {
Vasu Norice38b982010-07-22 13:57:13 -07001103 if (Log.isLoggable(TAG, Log.DEBUG)) {
1104 assert mConnectionPool != null;
1105 Log.i(TAG, mConnectionPool.toString());
1106 }
Vasu Nori6c354da2010-04-26 23:33:39 -07001107 mConnectionPool.close();
1108 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001109 } finally {
Brian Muramatsu46a88512010-11-12 13:53:57 -08001110 unlock();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001111 }
1112 }
1113
1114 private void closeClosable() {
Vasu Noriccd95442010-05-28 17:04:16 -07001115 /* deallocate all compiled SQL statement objects from mCompiledQueries cache.
Vasu Norie495d1f2010-01-06 16:34:19 -08001116 * this should be done before de-referencing all {@link SQLiteClosable} objects
1117 * from this database object because calling
1118 * {@link SQLiteClosable#onAllReferencesReleasedFromContainer()} could cause the database
1119 * to be closed. sqlite doesn't let a database close if there are
1120 * any unfinalized statements - such as the compiled-sql objects in mCompiledQueries.
1121 */
Vasu Norib729dcc2010-09-14 11:35:49 -07001122 deallocCachedSqlStatements();
Vasu Norie495d1f2010-01-06 16:34:19 -08001123
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001124 Iterator<Map.Entry<SQLiteClosable, Object>> iter = mPrograms.entrySet().iterator();
1125 while (iter.hasNext()) {
1126 Map.Entry<SQLiteClosable, Object> entry = iter.next();
1127 SQLiteClosable program = entry.getKey();
1128 if (program != null) {
1129 program.onAllReferencesReleasedFromContainer();
1130 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001131 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001132 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001133
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001134 /**
Vasu Nori422dad02010-09-03 16:03:08 -07001135 * package level access for testing purposes
1136 */
1137 /* package */ void closeDatabase() throws SQLiteException {
1138 try {
1139 dbclose();
1140 } catch (SQLiteUnfinalizedObjectsException e) {
1141 String msg = e.getMessage();
1142 String[] tokens = msg.split(",", 2);
1143 int stmtId = Integer.parseInt(tokens[0]);
1144 // get extra info about this statement, if it is still to be released by closeClosable()
1145 Iterator<Map.Entry<SQLiteClosable, Object>> iter = mPrograms.entrySet().iterator();
1146 boolean found = false;
1147 while (iter.hasNext()) {
1148 Map.Entry<SQLiteClosable, Object> entry = iter.next();
1149 SQLiteClosable program = entry.getKey();
1150 if (program != null && program instanceof SQLiteProgram) {
Vasu Norib4389022010-11-29 14:10:46 -08001151 SQLiteCompiledSql compiledSql = ((SQLiteProgram)program).mCompiledSql;
1152 if (compiledSql.nStatement == stmtId) {
1153 msg = compiledSql.toString();
1154 found = true;
1155 }
Vasu Nori422dad02010-09-03 16:03:08 -07001156 }
1157 }
1158 if (!found) {
1159 // the statement is already released by closeClosable(). is it waiting to be
1160 // finalized?
1161 if (mClosedStatementIds.contains(stmtId)) {
1162 Log.w(TAG, "this shouldn't happen. finalizing the statement now: ");
1163 closePendingStatements();
1164 // try to close the database again
1165 closeDatabase();
1166 }
1167 } else {
1168 // the statement is not yet closed. most probably programming error in the app.
Vasu Norib4389022010-11-29 14:10:46 -08001169 throw new SQLiteUnfinalizedObjectsException(
1170 "close() on database: " + getPath() +
1171 " failed due to un-close()d SQL statements: " + msg);
Vasu Nori422dad02010-09-03 16:03:08 -07001172 }
1173 }
1174 }
1175
1176 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001177 * Native call to close the database.
1178 */
1179 private native void dbclose();
1180
1181 /**
Mike Lockwood9d9c1be2010-07-13 19:27:52 -04001182 * A callback interface for a custom sqlite3 function.
1183 * This can be used to create a function that can be called from
1184 * sqlite3 database triggers.
1185 * @hide
1186 */
1187 public interface CustomFunction {
1188 public void callback(String[] args);
1189 }
1190
1191 /**
1192 * Registers a CustomFunction callback as a function that can be called from
1193 * sqlite3 database triggers.
1194 * @param name the name of the sqlite3 function
1195 * @param numArgs the number of arguments for the function
1196 * @param function callback to call when the function is executed
1197 * @hide
1198 */
1199 public void addCustomFunction(String name, int numArgs, CustomFunction function) {
1200 verifyDbIsOpen();
1201 synchronized (mCustomFunctions) {
1202 int ref = native_addCustomFunction(name, numArgs, function);
1203 if (ref != 0) {
1204 // save a reference to the function for cleanup later
1205 mCustomFunctions.add(new Integer(ref));
1206 } else {
1207 throw new SQLiteException("failed to add custom function " + name);
1208 }
1209 }
1210 }
1211
1212 private void releaseCustomFunctions() {
1213 synchronized (mCustomFunctions) {
1214 for (int i = 0; i < mCustomFunctions.size(); i++) {
1215 Integer function = mCustomFunctions.get(i);
1216 native_releaseCustomFunction(function.intValue());
1217 }
1218 mCustomFunctions.clear();
1219 }
1220 }
1221
1222 // list of CustomFunction references so we can clean up when the database closes
1223 private final ArrayList<Integer> mCustomFunctions =
1224 new ArrayList<Integer>();
1225
1226 private native int native_addCustomFunction(String name, int numArgs, CustomFunction function);
1227 private native void native_releaseCustomFunction(int function);
1228
1229 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001230 * Gets the database version.
1231 *
1232 * @return the database version
1233 */
1234 public int getVersion() {
Vasu Noriccd95442010-05-28 17:04:16 -07001235 return ((Long) DatabaseUtils.longForQuery(this, "PRAGMA user_version;", null)).intValue();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001236 }
1237
1238 /**
1239 * Sets the database version.
1240 *
1241 * @param version the new database version
1242 */
1243 public void setVersion(int version) {
1244 execSQL("PRAGMA user_version = " + version);
1245 }
1246
1247 /**
1248 * Returns the maximum size the database may grow to.
1249 *
1250 * @return the new maximum database size
1251 */
1252 public long getMaximumSize() {
Vasu Noriccd95442010-05-28 17:04:16 -07001253 long pageCount = DatabaseUtils.longForQuery(this, "PRAGMA max_page_count;", null);
1254 return pageCount * getPageSize();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001255 }
1256
1257 /**
1258 * Sets the maximum size the database will grow to. The maximum size cannot
1259 * be set below the current size.
1260 *
1261 * @param numBytes the maximum database size, in bytes
1262 * @return the new maximum database size
1263 */
1264 public long setMaximumSize(long numBytes) {
Vasu Noriccd95442010-05-28 17:04:16 -07001265 long pageSize = getPageSize();
1266 long numPages = numBytes / pageSize;
1267 // If numBytes isn't a multiple of pageSize, bump up a page
1268 if ((numBytes % pageSize) != 0) {
1269 numPages++;
Vasu Norif3cf8a42010-03-23 11:41:44 -07001270 }
Vasu Noriccd95442010-05-28 17:04:16 -07001271 long newPageCount = DatabaseUtils.longForQuery(this, "PRAGMA max_page_count = " + numPages,
1272 null);
1273 return newPageCount * pageSize;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001274 }
1275
1276 /**
1277 * Returns the current database page size, in bytes.
1278 *
1279 * @return the database page size, in bytes
1280 */
1281 public long getPageSize() {
Vasu Noriccd95442010-05-28 17:04:16 -07001282 return DatabaseUtils.longForQuery(this, "PRAGMA page_size;", null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001283 }
1284
1285 /**
1286 * Sets the database page size. The page size must be a power of two. This
1287 * method does not work if any data has been written to the database file,
1288 * and must be called right after the database has been created.
1289 *
1290 * @param numBytes the database page size, in bytes
1291 */
1292 public void setPageSize(long numBytes) {
1293 execSQL("PRAGMA page_size = " + numBytes);
1294 }
1295
1296 /**
1297 * Mark this table as syncable. When an update occurs in this table the
1298 * _sync_dirty field will be set to ensure proper syncing operation.
1299 *
1300 * @param table the table to mark as syncable
1301 * @param deletedTable The deleted table that corresponds to the
1302 * syncable table
Vasu Nori95675132010-07-21 16:24:40 -07001303 * @deprecated This method no longer serves any useful purpose and has been deprecated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001304 */
Vasu Nori95675132010-07-21 16:24:40 -07001305 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001306 public void markTableSyncable(String table, String deletedTable) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001307 }
1308
1309 /**
1310 * Mark this table as syncable, with the _sync_dirty residing in another
1311 * table. When an update occurs in this table the _sync_dirty field of the
1312 * row in updateTable with the _id in foreignKey will be set to
1313 * ensure proper syncing operation.
1314 *
1315 * @param table an update on this table will trigger a sync time removal
1316 * @param foreignKey this is the column in table whose value is an _id in
1317 * updateTable
1318 * @param updateTable this is the table that will have its _sync_dirty
Vasu Nori95675132010-07-21 16:24:40 -07001319 * @deprecated This method no longer serves any useful purpose and has been deprecated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001320 */
Vasu Nori95675132010-07-21 16:24:40 -07001321 @Deprecated
1322 public void markTableSyncable(String table, String foreignKey, String updateTable) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001323 }
1324
1325 /**
1326 * Finds the name of the first table, which is editable.
1327 *
1328 * @param tables a list of tables
1329 * @return the first table listed
1330 */
1331 public static String findEditTable(String tables) {
1332 if (!TextUtils.isEmpty(tables)) {
1333 // find the first word terminated by either a space or a comma
1334 int spacepos = tables.indexOf(' ');
1335 int commapos = tables.indexOf(',');
1336
1337 if (spacepos > 0 && (spacepos < commapos || commapos < 0)) {
1338 return tables.substring(0, spacepos);
1339 } else if (commapos > 0 && (commapos < spacepos || spacepos < 0) ) {
1340 return tables.substring(0, commapos);
1341 }
1342 return tables;
1343 } else {
1344 throw new IllegalStateException("Invalid tables");
1345 }
1346 }
1347
1348 /**
1349 * Compiles an SQL statement into a reusable pre-compiled statement object.
1350 * The parameters are identical to {@link #execSQL(String)}. You may put ?s in the
1351 * statement and fill in those values with {@link SQLiteProgram#bindString}
1352 * and {@link SQLiteProgram#bindLong} each time you want to run the
1353 * statement. Statements may not return result sets larger than 1x1.
Vasu Nori2827d6d2010-07-04 00:26:18 -07001354 *<p>
1355 * No two threads should be using the same {@link SQLiteStatement} at the same time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001356 *
1357 * @param sql The raw SQL statement, may contain ? for unknown values to be
1358 * bound later.
Jeff Hamiltonf3ca9a52010-05-12 15:04:33 -07001359 * @return A pre-compiled {@link SQLiteStatement} object. Note that
1360 * {@link SQLiteStatement}s are not synchronized, see the documentation for more details.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001361 */
1362 public SQLiteStatement compileStatement(String sql) throws SQLException {
Vasu Noriccd95442010-05-28 17:04:16 -07001363 verifyDbIsOpen();
Vasu Nori0732f792010-07-29 17:24:12 -07001364 return new SQLiteStatement(this, sql, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001365 }
1366
1367 /**
1368 * Query the given URL, returning a {@link Cursor} over the result set.
1369 *
1370 * @param distinct true if you want each row to be unique, false otherwise.
1371 * @param table The table name to compile the query against.
1372 * @param columns A list of which columns to return. Passing null will
1373 * return all columns, which is discouraged to prevent reading
1374 * data from storage that isn't going to be used.
1375 * @param selection A filter declaring which rows to return, formatted as an
1376 * SQL WHERE clause (excluding the WHERE itself). Passing null
1377 * will return all rows for the given table.
1378 * @param selectionArgs You may include ?s in selection, which will be
1379 * replaced by the values from selectionArgs, in order that they
1380 * appear in the selection. The values will be bound as Strings.
1381 * @param groupBy A filter declaring how to group rows, formatted as an SQL
1382 * GROUP BY clause (excluding the GROUP BY itself). Passing null
1383 * will cause the rows to not be grouped.
1384 * @param having A filter declare which row groups to include in the cursor,
1385 * if row grouping is being used, formatted as an SQL HAVING
1386 * clause (excluding the HAVING itself). Passing null will cause
1387 * all row groups to be included, and is required when row
1388 * grouping is not being used.
1389 * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
1390 * (excluding the ORDER BY itself). Passing null will use the
1391 * default sort order, which may be unordered.
1392 * @param limit Limits the number of rows returned by the query,
1393 * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
Jeff Hamiltonf3ca9a52010-05-12 15:04:33 -07001394 * @return A {@link Cursor} object, which is positioned before the first entry. Note that
1395 * {@link Cursor}s are not synchronized, see the documentation for more details.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001396 * @see Cursor
1397 */
1398 public Cursor query(boolean distinct, String table, String[] columns,
1399 String selection, String[] selectionArgs, String groupBy,
1400 String having, String orderBy, String limit) {
1401 return queryWithFactory(null, distinct, table, columns, selection, selectionArgs,
1402 groupBy, having, orderBy, limit);
1403 }
1404
1405 /**
1406 * Query the given URL, returning a {@link Cursor} over the result set.
1407 *
1408 * @param cursorFactory the cursor factory to use, or null for the default factory
1409 * @param distinct true if you want each row to be unique, false otherwise.
1410 * @param table The table name to compile the query against.
1411 * @param columns A list of which columns to return. Passing null will
1412 * return all columns, which is discouraged to prevent reading
1413 * data from storage that isn't going to be used.
1414 * @param selection A filter declaring which rows to return, formatted as an
1415 * SQL WHERE clause (excluding the WHERE itself). Passing null
1416 * will return all rows for the given table.
1417 * @param selectionArgs You may include ?s in selection, which will be
1418 * replaced by the values from selectionArgs, in order that they
1419 * appear in the selection. The values will be bound as Strings.
1420 * @param groupBy A filter declaring how to group rows, formatted as an SQL
1421 * GROUP BY clause (excluding the GROUP BY itself). Passing null
1422 * will cause the rows to not be grouped.
1423 * @param having A filter declare which row groups to include in the cursor,
1424 * if row grouping is being used, formatted as an SQL HAVING
1425 * clause (excluding the HAVING itself). Passing null will cause
1426 * all row groups to be included, and is required when row
1427 * grouping is not being used.
1428 * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
1429 * (excluding the ORDER BY itself). Passing null will use the
1430 * default sort order, which may be unordered.
1431 * @param limit Limits the number of rows returned by the query,
1432 * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
Jeff Hamiltonf3ca9a52010-05-12 15:04:33 -07001433 * @return A {@link Cursor} object, which is positioned before the first entry. Note that
1434 * {@link Cursor}s are not synchronized, see the documentation for more details.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001435 * @see Cursor
1436 */
1437 public Cursor queryWithFactory(CursorFactory cursorFactory,
1438 boolean distinct, String table, String[] columns,
1439 String selection, String[] selectionArgs, String groupBy,
1440 String having, String orderBy, String limit) {
Vasu Noriccd95442010-05-28 17:04:16 -07001441 verifyDbIsOpen();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001442 String sql = SQLiteQueryBuilder.buildQueryString(
1443 distinct, table, columns, selection, groupBy, having, orderBy, limit);
1444
1445 return rawQueryWithFactory(
1446 cursorFactory, sql, selectionArgs, findEditTable(table));
1447 }
1448
1449 /**
1450 * Query the given table, returning a {@link Cursor} over the result set.
1451 *
1452 * @param table The table name to compile the query against.
1453 * @param columns A list of which columns to return. Passing null will
1454 * return all columns, which is discouraged to prevent reading
1455 * data from storage that isn't going to be used.
1456 * @param selection A filter declaring which rows to return, formatted as an
1457 * SQL WHERE clause (excluding the WHERE itself). Passing null
1458 * will return all rows for the given table.
1459 * @param selectionArgs You may include ?s in selection, which will be
1460 * replaced by the values from selectionArgs, in order that they
1461 * appear in the selection. The values will be bound as Strings.
1462 * @param groupBy A filter declaring how to group rows, formatted as an SQL
1463 * GROUP BY clause (excluding the GROUP BY itself). Passing null
1464 * will cause the rows to not be grouped.
1465 * @param having A filter declare which row groups to include in the cursor,
1466 * if row grouping is being used, formatted as an SQL HAVING
1467 * clause (excluding the HAVING itself). Passing null will cause
1468 * all row groups to be included, and is required when row
1469 * grouping is not being used.
1470 * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
1471 * (excluding the ORDER BY itself). Passing null will use the
1472 * default sort order, which may be unordered.
Jeff Hamiltonf3ca9a52010-05-12 15:04:33 -07001473 * @return A {@link Cursor} object, which is positioned before the first entry. Note that
1474 * {@link Cursor}s are not synchronized, see the documentation for more details.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001475 * @see Cursor
1476 */
1477 public Cursor query(String table, String[] columns, String selection,
1478 String[] selectionArgs, String groupBy, String having,
1479 String orderBy) {
1480
1481 return query(false, table, columns, selection, selectionArgs, groupBy,
1482 having, orderBy, null /* limit */);
1483 }
1484
1485 /**
1486 * Query the given table, returning a {@link Cursor} over the result set.
1487 *
1488 * @param table The table name to compile the query against.
1489 * @param columns A list of which columns to return. Passing null will
1490 * return all columns, which is discouraged to prevent reading
1491 * data from storage that isn't going to be used.
1492 * @param selection A filter declaring which rows to return, formatted as an
1493 * SQL WHERE clause (excluding the WHERE itself). Passing null
1494 * will return all rows for the given table.
1495 * @param selectionArgs You may include ?s in selection, which will be
1496 * replaced by the values from selectionArgs, in order that they
1497 * appear in the selection. The values will be bound as Strings.
1498 * @param groupBy A filter declaring how to group rows, formatted as an SQL
1499 * GROUP BY clause (excluding the GROUP BY itself). Passing null
1500 * will cause the rows to not be grouped.
1501 * @param having A filter declare which row groups to include in the cursor,
1502 * if row grouping is being used, formatted as an SQL HAVING
1503 * clause (excluding the HAVING itself). Passing null will cause
1504 * all row groups to be included, and is required when row
1505 * grouping is not being used.
1506 * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
1507 * (excluding the ORDER BY itself). Passing null will use the
1508 * default sort order, which may be unordered.
1509 * @param limit Limits the number of rows returned by the query,
1510 * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
Jeff Hamiltonf3ca9a52010-05-12 15:04:33 -07001511 * @return A {@link Cursor} object, which is positioned before the first entry. Note that
1512 * {@link Cursor}s are not synchronized, see the documentation for more details.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001513 * @see Cursor
1514 */
1515 public Cursor query(String table, String[] columns, String selection,
1516 String[] selectionArgs, String groupBy, String having,
1517 String orderBy, String limit) {
1518
1519 return query(false, table, columns, selection, selectionArgs, groupBy,
1520 having, orderBy, limit);
1521 }
1522
1523 /**
1524 * Runs the provided SQL and returns a {@link Cursor} over the result set.
1525 *
1526 * @param sql the SQL query. The SQL string must not be ; terminated
1527 * @param selectionArgs You may include ?s in where clause in the query,
1528 * which will be replaced by the values from selectionArgs. The
1529 * values will be bound as Strings.
Jeff Hamiltonf3ca9a52010-05-12 15:04:33 -07001530 * @return A {@link Cursor} object, which is positioned before the first entry. Note that
1531 * {@link Cursor}s are not synchronized, see the documentation for more details.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001532 */
1533 public Cursor rawQuery(String sql, String[] selectionArgs) {
1534 return rawQueryWithFactory(null, sql, selectionArgs, null);
1535 }
1536
1537 /**
1538 * Runs the provided SQL and returns a cursor over the result set.
1539 *
1540 * @param cursorFactory the cursor factory to use, or null for the default factory
1541 * @param sql the SQL query. The SQL string must not be ; terminated
1542 * @param selectionArgs You may include ?s in where clause in the query,
1543 * which will be replaced by the values from selectionArgs. The
1544 * values will be bound as Strings.
1545 * @param editTable the name of the first table, which is editable
Jeff Hamiltonf3ca9a52010-05-12 15:04:33 -07001546 * @return A {@link Cursor} object, which is positioned before the first entry. Note that
1547 * {@link Cursor}s are not synchronized, see the documentation for more details.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001548 */
1549 public Cursor rawQueryWithFactory(
1550 CursorFactory cursorFactory, String sql, String[] selectionArgs,
1551 String editTable) {
Vasu Noriccd95442010-05-28 17:04:16 -07001552 verifyDbIsOpen();
Brad Fitzpatrickcfda9f32010-06-03 12:52:54 -07001553 BlockGuard.getThreadPolicy().onReadFromDisk();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001554 long timeStart = 0;
1555
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001556 if (Config.LOGV || mSlowQueryThreshold != -1) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001557 timeStart = System.currentTimeMillis();
1558 }
1559
Vasu Nori6c354da2010-04-26 23:33:39 -07001560 SQLiteDatabase db = getDbConnection(sql);
1561 SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(db, sql, editTable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001562
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001563 Cursor cursor = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001564 try {
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001565 cursor = driver.query(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001566 cursorFactory != null ? cursorFactory : mFactory,
1567 selectionArgs);
1568 } finally {
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001569 if (Config.LOGV || mSlowQueryThreshold != -1) {
1570
Vasu Nori020e5342010-04-28 14:22:38 -07001571 // Force query execution
1572 int count = -1;
1573 if (cursor != null) {
1574 count = cursor.getCount();
1575 }
1576
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001577 long duration = System.currentTimeMillis() - timeStart;
1578
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001579 if (Config.LOGV || duration >= mSlowQueryThreshold) {
1580 Log.v(SQLiteCursor.TAG,
1581 "query (" + duration + " ms): " + driver.toString() + ", args are "
1582 + (selectionArgs != null
1583 ? TextUtils.join(",", selectionArgs)
Vasu Nori020e5342010-04-28 14:22:38 -07001584 : "<null>") + ", count is " + count);
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001585 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001586 }
Vasu Nori6c354da2010-04-26 23:33:39 -07001587 releaseDbConnection(db);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001588 }
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001589 return cursor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001590 }
1591
1592 /**
1593 * Runs the provided SQL and returns a cursor over the result set.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001594 * The cursor will read an initial set of rows and the return to the caller.
1595 * It will continue to read in batches and send data changed notifications
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001596 * when the later batches are ready.
1597 * @param sql the SQL query. The SQL string must not be ; terminated
1598 * @param selectionArgs You may include ?s in where clause in the query,
1599 * which will be replaced by the values from selectionArgs. The
1600 * values will be bound as Strings.
1601 * @param initialRead set the initial count of items to read from the cursor
1602 * @param maxRead set the count of items to read on each iteration after the first
Jeff Hamiltonf3ca9a52010-05-12 15:04:33 -07001603 * @return A {@link Cursor} object, which is positioned before the first entry. Note that
1604 * {@link Cursor}s are not synchronized, see the documentation for more details.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001605 *
Andy Stadlerf8a7cea2009-04-10 16:24:47 -07001606 * This work is incomplete and not fully tested or reviewed, so currently
1607 * hidden.
1608 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001609 */
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001610 public Cursor rawQuery(String sql, String[] selectionArgs,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001611 int initialRead, int maxRead) {
1612 SQLiteCursor c = (SQLiteCursor)rawQueryWithFactory(
1613 null, sql, selectionArgs, null);
1614 c.setLoadStyle(initialRead, maxRead);
1615 return c;
1616 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001617
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001618 /**
1619 * Convenience method for inserting a row into the database.
1620 *
1621 * @param table the table to insert the row into
Brad Fitzpatrick69ea4e12011-01-05 11:13:40 -08001622 * @param nullColumnHack optional; may be <code>null</code>.
1623 * SQL doesn't allow inserting a completely empty row without
1624 * naming at least one column name. If your provided <code>values</code> is
1625 * empty, no column names are known and an empty row can't be inserted.
1626 * If not set to null, the <code>nullColumnHack</code> parameter
1627 * provides the name of nullable column name to explicitly insert a NULL into
1628 * in the case where your <code>values</code> is empty.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001629 * @param values this map contains the initial column values for the
1630 * row. The keys should be the column names and the values the
1631 * column values
1632 * @return the row ID of the newly inserted row, or -1 if an error occurred
1633 */
1634 public long insert(String table, String nullColumnHack, ContentValues values) {
1635 try {
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001636 return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001637 } catch (SQLException e) {
1638 Log.e(TAG, "Error inserting " + values, e);
1639 return -1;
1640 }
1641 }
1642
1643 /**
1644 * Convenience method for inserting a row into the database.
1645 *
1646 * @param table the table to insert the row into
Brad Fitzpatrick69ea4e12011-01-05 11:13:40 -08001647 * @param nullColumnHack optional; may be <code>null</code>.
1648 * SQL doesn't allow inserting a completely empty row without
1649 * naming at least one column name. If your provided <code>values</code> is
1650 * empty, no column names are known and an empty row can't be inserted.
1651 * If not set to null, the <code>nullColumnHack</code> parameter
1652 * provides the name of nullable column name to explicitly insert a NULL into
1653 * in the case where your <code>values</code> is empty.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001654 * @param values this map contains the initial column values for the
1655 * row. The keys should be the column names and the values the
1656 * column values
1657 * @throws SQLException
1658 * @return the row ID of the newly inserted row, or -1 if an error occurred
1659 */
1660 public long insertOrThrow(String table, String nullColumnHack, ContentValues values)
1661 throws SQLException {
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001662 return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001663 }
1664
1665 /**
1666 * Convenience method for replacing a row in the database.
1667 *
1668 * @param table the table in which to replace the row
Brad Fitzpatrick69ea4e12011-01-05 11:13:40 -08001669 * @param nullColumnHack optional; may be <code>null</code>.
1670 * SQL doesn't allow inserting a completely empty row without
1671 * naming at least one column name. If your provided <code>initialValues</code> is
1672 * empty, no column names are known and an empty row can't be inserted.
1673 * If not set to null, the <code>nullColumnHack</code> parameter
1674 * provides the name of nullable column name to explicitly insert a NULL into
1675 * in the case where your <code>initialValues</code> is empty.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001676 * @param initialValues this map contains the initial column values for
Brad Fitzpatrick69ea4e12011-01-05 11:13:40 -08001677 * the row.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001678 * @return the row ID of the newly inserted row, or -1 if an error occurred
1679 */
1680 public long replace(String table, String nullColumnHack, ContentValues initialValues) {
1681 try {
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001682 return insertWithOnConflict(table, nullColumnHack, initialValues,
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001683 CONFLICT_REPLACE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001684 } catch (SQLException e) {
1685 Log.e(TAG, "Error inserting " + initialValues, e);
1686 return -1;
1687 }
1688 }
1689
1690 /**
1691 * Convenience method for replacing a row in the database.
1692 *
1693 * @param table the table in which to replace the row
Brad Fitzpatrick69ea4e12011-01-05 11:13:40 -08001694 * @param nullColumnHack optional; may be <code>null</code>.
1695 * SQL doesn't allow inserting a completely empty row without
1696 * naming at least one column name. If your provided <code>initialValues</code> is
1697 * empty, no column names are known and an empty row can't be inserted.
1698 * If not set to null, the <code>nullColumnHack</code> parameter
1699 * provides the name of nullable column name to explicitly insert a NULL into
1700 * in the case where your <code>initialValues</code> is empty.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001701 * @param initialValues this map contains the initial column values for
1702 * the row. The key
1703 * @throws SQLException
1704 * @return the row ID of the newly inserted row, or -1 if an error occurred
1705 */
1706 public long replaceOrThrow(String table, String nullColumnHack,
1707 ContentValues initialValues) throws SQLException {
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001708 return insertWithOnConflict(table, nullColumnHack, initialValues,
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001709 CONFLICT_REPLACE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001710 }
1711
1712 /**
1713 * General method for inserting a row into the database.
1714 *
1715 * @param table the table to insert the row into
Brad Fitzpatrick69ea4e12011-01-05 11:13:40 -08001716 * @param nullColumnHack optional; may be <code>null</code>.
1717 * SQL doesn't allow inserting a completely empty row without
1718 * naming at least one column name. If your provided <code>initialValues</code> is
1719 * empty, no column names are known and an empty row can't be inserted.
1720 * If not set to null, the <code>nullColumnHack</code> parameter
1721 * provides the name of nullable column name to explicitly insert a NULL into
1722 * in the case where your <code>initialValues</code> is empty.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001723 * @param initialValues this map contains the initial column values for the
1724 * row. The keys should be the column names and the values the
1725 * column values
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001726 * @param conflictAlgorithm for insert conflict resolver
Vasu Nori6eb7c452010-01-27 14:31:24 -08001727 * @return the row ID of the newly inserted row
1728 * OR the primary key of the existing row if the input param 'conflictAlgorithm' =
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001729 * {@link #CONFLICT_IGNORE}
Vasu Nori6eb7c452010-01-27 14:31:24 -08001730 * OR -1 if any error
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001731 */
1732 public long insertWithOnConflict(String table, String nullColumnHack,
Vasu Nori6eb7c452010-01-27 14:31:24 -08001733 ContentValues initialValues, int conflictAlgorithm) {
Vasu Nori0732f792010-07-29 17:24:12 -07001734 StringBuilder sql = new StringBuilder();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001735 sql.append("INSERT");
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001736 sql.append(CONFLICT_VALUES[conflictAlgorithm]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001737 sql.append(" INTO ");
1738 sql.append(table);
Vasu Nori0732f792010-07-29 17:24:12 -07001739 sql.append('(');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001740
Vasu Nori0732f792010-07-29 17:24:12 -07001741 Object[] bindArgs = null;
1742 int size = (initialValues != null && initialValues.size() > 0) ? initialValues.size() : 0;
1743 if (size > 0) {
1744 bindArgs = new Object[size];
1745 int i = 0;
1746 for (String colName : initialValues.keySet()) {
1747 sql.append((i > 0) ? "," : "");
1748 sql.append(colName);
1749 bindArgs[i++] = initialValues.get(colName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001750 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001751 sql.append(')');
Vasu Nori0732f792010-07-29 17:24:12 -07001752 sql.append(" VALUES (");
1753 for (i = 0; i < size; i++) {
1754 sql.append((i > 0) ? ",?" : "?");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001755 }
Vasu Nori0732f792010-07-29 17:24:12 -07001756 } else {
1757 sql.append(nullColumnHack + ") VALUES (NULL");
1758 }
1759 sql.append(')');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001760
Vasu Nori0732f792010-07-29 17:24:12 -07001761 SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
1762 try {
Vasu Norifb16cbd2010-07-25 16:38:48 -07001763 return statement.executeInsert();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001764 } catch (SQLiteDatabaseCorruptException e) {
1765 onCorruption();
1766 throw e;
1767 } finally {
Vasu Nori0732f792010-07-29 17:24:12 -07001768 statement.close();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001769 }
1770 }
1771
1772 /**
1773 * Convenience method for deleting rows in the database.
1774 *
1775 * @param table the table to delete from
1776 * @param whereClause the optional WHERE clause to apply when deleting.
1777 * Passing null will delete all rows.
1778 * @return the number of rows affected if a whereClause is passed in, 0
1779 * otherwise. To remove all rows and get a count pass "1" as the
1780 * whereClause.
1781 */
1782 public int delete(String table, String whereClause, String[] whereArgs) {
Vasu Nori0732f792010-07-29 17:24:12 -07001783 SQLiteStatement statement = new SQLiteStatement(this, "DELETE FROM " + table +
1784 (!TextUtils.isEmpty(whereClause) ? " WHERE " + whereClause : ""), whereArgs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001785 try {
Vasu Norifb16cbd2010-07-25 16:38:48 -07001786 return statement.executeUpdateDelete();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001787 } catch (SQLiteDatabaseCorruptException e) {
1788 onCorruption();
1789 throw e;
1790 } finally {
Vasu Nori0732f792010-07-29 17:24:12 -07001791 statement.close();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001792 }
1793 }
1794
1795 /**
1796 * Convenience method for updating rows in the database.
1797 *
1798 * @param table the table to update in
1799 * @param values a map from column names to new column values. null is a
1800 * valid value that will be translated to NULL.
1801 * @param whereClause the optional WHERE clause to apply when updating.
1802 * Passing null will update all rows.
1803 * @return the number of rows affected
1804 */
1805 public int update(String table, ContentValues values, String whereClause, String[] whereArgs) {
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001806 return updateWithOnConflict(table, values, whereClause, whereArgs, CONFLICT_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001807 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001808
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001809 /**
1810 * Convenience method for updating rows in the database.
1811 *
1812 * @param table the table to update in
1813 * @param values a map from column names to new column values. null is a
1814 * valid value that will be translated to NULL.
1815 * @param whereClause the optional WHERE clause to apply when updating.
1816 * Passing null will update all rows.
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001817 * @param conflictAlgorithm for update conflict resolver
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001818 * @return the number of rows affected
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001819 */
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001820 public int updateWithOnConflict(String table, ContentValues values,
Vasu Nori6eb7c452010-01-27 14:31:24 -08001821 String whereClause, String[] whereArgs, int conflictAlgorithm) {
Brian Muramatsu46a88512010-11-12 13:53:57 -08001822 if (values == null || values.size() == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001823 throw new IllegalArgumentException("Empty values");
1824 }
1825
1826 StringBuilder sql = new StringBuilder(120);
1827 sql.append("UPDATE ");
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001828 sql.append(CONFLICT_VALUES[conflictAlgorithm]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001829 sql.append(table);
1830 sql.append(" SET ");
1831
Vasu Nori0732f792010-07-29 17:24:12 -07001832 // move all bind args to one array
Brian Muramatsu46a88512010-11-12 13:53:57 -08001833 int setValuesSize = values.size();
Vasu Nori0732f792010-07-29 17:24:12 -07001834 int bindArgsSize = (whereArgs == null) ? setValuesSize : (setValuesSize + whereArgs.length);
1835 Object[] bindArgs = new Object[bindArgsSize];
1836 int i = 0;
1837 for (String colName : values.keySet()) {
1838 sql.append((i > 0) ? "," : "");
1839 sql.append(colName);
1840 bindArgs[i++] = values.get(colName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001841 sql.append("=?");
Vasu Nori0732f792010-07-29 17:24:12 -07001842 }
1843 if (whereArgs != null) {
1844 for (i = setValuesSize; i < bindArgsSize; i++) {
1845 bindArgs[i] = whereArgs[i - setValuesSize];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001846 }
1847 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001848 if (!TextUtils.isEmpty(whereClause)) {
1849 sql.append(" WHERE ");
1850 sql.append(whereClause);
1851 }
1852
Vasu Nori0732f792010-07-29 17:24:12 -07001853 SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001854 try {
Vasu Norifb16cbd2010-07-25 16:38:48 -07001855 return statement.executeUpdateDelete();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001856 } catch (SQLiteDatabaseCorruptException e) {
1857 onCorruption();
1858 throw e;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001859 } finally {
Vasu Nori0732f792010-07-29 17:24:12 -07001860 statement.close();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001861 }
1862 }
1863
1864 /**
Vasu Noriccd95442010-05-28 17:04:16 -07001865 * Execute a single SQL statement that is NOT a SELECT
1866 * or any other SQL statement that returns data.
1867 * <p>
Vasu Norice38b982010-07-22 13:57:13 -07001868 * It has no means to return any data (such as the number of affected rows).
Vasu Noriccd95442010-05-28 17:04:16 -07001869 * Instead, you're encouraged to use {@link #insert(String, String, ContentValues)},
1870 * {@link #update(String, ContentValues, String, String[])}, et al, when possible.
1871 * </p>
Vasu Nori9bf225e2010-07-07 16:38:28 -07001872 * <p>
1873 * When using {@link #enableWriteAheadLogging()}, journal_mode is
1874 * automatically managed by this class. So, do not set journal_mode
1875 * using "PRAGMA journal_mode'<value>" statement if your app is using
1876 * {@link #enableWriteAheadLogging()}
1877 * </p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001878 *
Vasu Noriccd95442010-05-28 17:04:16 -07001879 * @param sql the SQL statement to be executed. Multiple statements separated by semicolons are
1880 * not supported.
Brad Fitzpatrick69ea4e12011-01-05 11:13:40 -08001881 * @throws SQLException if the SQL string is invalid
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001882 */
Vasu Norib83cb7c2010-09-14 13:36:01 -07001883 public void execSQL(String sql) throws SQLException {
Vasu Norice38b982010-07-22 13:57:13 -07001884 int stmtType = DatabaseUtils.getSqlStatementType(sql);
1885 if (stmtType == DatabaseUtils.STATEMENT_ATTACH) {
Vasu Nori8d111032010-06-22 18:34:21 -07001886 disableWriteAheadLogging();
1887 }
Vasu Noric8e1f232010-04-13 15:05:09 -07001888 long timeStart = SystemClock.uptimeMillis();
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07001889 logTimeStat(mLastSqlStatement, timeStart, GET_LOCK_LOG_PREFIX);
Vasu Norib83cb7c2010-09-14 13:36:01 -07001890 executeSql(sql, null);
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -08001891
Vasu Nori24675612010-09-27 14:54:19 -07001892 if (stmtType == DatabaseUtils.STATEMENT_ATTACH) {
1893 mHasAttachedDbs = true;
1894 }
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -08001895 // Log commit statements along with the most recently executed
Vasu Norice38b982010-07-22 13:57:13 -07001896 // SQL statement for disambiguation.
1897 if (stmtType == DatabaseUtils.STATEMENT_COMMIT) {
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07001898 logTimeStat(mLastSqlStatement, timeStart, COMMIT_SQL);
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -08001899 } else {
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07001900 logTimeStat(sql, timeStart, null);
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -08001901 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001902 }
1903
1904 /**
Vasu Noriccd95442010-05-28 17:04:16 -07001905 * Execute a single SQL statement that is NOT a SELECT/INSERT/UPDATE/DELETE.
1906 * <p>
1907 * For INSERT statements, use any of the following instead.
1908 * <ul>
1909 * <li>{@link #insert(String, String, ContentValues)}</li>
1910 * <li>{@link #insertOrThrow(String, String, ContentValues)}</li>
1911 * <li>{@link #insertWithOnConflict(String, String, ContentValues, int)}</li>
1912 * </ul>
1913 * <p>
1914 * For UPDATE statements, use any of the following instead.
1915 * <ul>
1916 * <li>{@link #update(String, ContentValues, String, String[])}</li>
1917 * <li>{@link #updateWithOnConflict(String, ContentValues, String, String[], int)}</li>
1918 * </ul>
1919 * <p>
1920 * For DELETE statements, use any of the following instead.
1921 * <ul>
1922 * <li>{@link #delete(String, String, String[])}</li>
1923 * </ul>
1924 * <p>
1925 * For example, the following are good candidates for using this method:
1926 * <ul>
1927 * <li>ALTER TABLE</li>
1928 * <li>CREATE or DROP table / trigger / view / index / virtual table</li>
1929 * <li>REINDEX</li>
1930 * <li>RELEASE</li>
1931 * <li>SAVEPOINT</li>
1932 * <li>PRAGMA that returns no data</li>
1933 * </ul>
1934 * </p>
Vasu Nori9bf225e2010-07-07 16:38:28 -07001935 * <p>
1936 * When using {@link #enableWriteAheadLogging()}, journal_mode is
1937 * automatically managed by this class. So, do not set journal_mode
1938 * using "PRAGMA journal_mode'<value>" statement if your app is using
1939 * {@link #enableWriteAheadLogging()}
1940 * </p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001941 *
Vasu Noriccd95442010-05-28 17:04:16 -07001942 * @param sql the SQL statement to be executed. Multiple statements separated by semicolons are
1943 * not supported.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001944 * @param bindArgs only byte[], String, Long and Double are supported in bindArgs.
Brad Fitzpatrick69ea4e12011-01-05 11:13:40 -08001945 * @throws SQLException if the SQL string is invalid
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001946 */
Vasu Norib83cb7c2010-09-14 13:36:01 -07001947 public void execSQL(String sql, Object[] bindArgs) throws SQLException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001948 if (bindArgs == null) {
1949 throw new IllegalArgumentException("Empty bindArgs");
1950 }
Vasu Norib83cb7c2010-09-14 13:36:01 -07001951 executeSql(sql, bindArgs);
Vasu Norice38b982010-07-22 13:57:13 -07001952 }
1953
Vasu Nori54025902010-09-14 12:14:26 -07001954 private int executeSql(String sql, Object[] bindArgs) throws SQLException {
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -08001955 long timeStart = SystemClock.uptimeMillis();
Vasu Nori54025902010-09-14 12:14:26 -07001956 int n;
Vasu Nori0732f792010-07-29 17:24:12 -07001957 SQLiteStatement statement = new SQLiteStatement(this, sql, bindArgs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001958 try {
Vasu Nori54025902010-09-14 12:14:26 -07001959 n = statement.executeUpdateDelete();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001960 } catch (SQLiteDatabaseCorruptException e) {
1961 onCorruption();
1962 throw e;
1963 } finally {
Vasu Nori0732f792010-07-29 17:24:12 -07001964 statement.close();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001965 }
Dan Egnor12311952009-11-23 14:47:45 -08001966 logTimeStat(sql, timeStart);
Vasu Nori54025902010-09-14 12:14:26 -07001967 return n;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001968 }
1969
1970 @Override
Mike Lockwood9d9c1be2010-07-13 19:27:52 -04001971 protected void finalize() throws Throwable {
1972 try {
1973 if (isOpen()) {
1974 Log.e(TAG, "close() was never explicitly called on database '" +
1975 mPath + "' ", mStackTrace);
1976 closeClosable();
1977 onAllReferencesReleased();
1978 releaseCustomFunctions();
1979 }
1980 } finally {
1981 super.finalize();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001982 }
1983 }
1984
1985 /**
Vasu Nori21343692010-06-03 16:01:39 -07001986 * Private constructor.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001987 *
1988 * @param path The full path to the database
1989 * @param factory The factory to use when creating cursors, may be NULL.
1990 * @param flags 0 or {@link #NO_LOCALIZED_COLLATORS}. If the database file already
1991 * exists, mFlags will be updated appropriately.
Vasu Nori21343692010-06-03 16:01:39 -07001992 * @param errorHandler The {@link DatabaseErrorHandler} to be used when sqlite reports database
1993 * corruption. may be NULL.
Vasu Nori6c354da2010-04-26 23:33:39 -07001994 * @param connectionNum 0 for main database connection handle. 1..N for pooled database
1995 * connection handles.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001996 */
Vasu Nori21343692010-06-03 16:01:39 -07001997 private SQLiteDatabase(String path, CursorFactory factory, int flags,
Vasu Nori6c354da2010-04-26 23:33:39 -07001998 DatabaseErrorHandler errorHandler, short connectionNum) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001999 if (path == null) {
2000 throw new IllegalArgumentException("path should not be null");
2001 }
2002 mFlags = flags;
2003 mPath = path;
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07002004 mSlowQueryThreshold = SystemProperties.getInt(LOG_SLOW_QUERIES_PROPERTY, -1);
Vasu Nori08b448e2010-03-03 10:05:16 -08002005 mStackTrace = new DatabaseObjectNotClosedException().fillInStackTrace();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002006 mFactory = factory;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002007 mPrograms = new WeakHashMap<SQLiteClosable,Object>();
Vasu Nori21343692010-06-03 16:01:39 -07002008 // Set the DatabaseErrorHandler to be used when SQLite reports corruption.
2009 // If the caller sets errorHandler = null, then use default errorhandler.
2010 mErrorHandler = (errorHandler == null) ? new DefaultDatabaseErrorHandler() : errorHandler;
Vasu Nori6c354da2010-04-26 23:33:39 -07002011 mConnectionNum = connectionNum;
Vasu Nori34ad57f02010-12-21 09:32:36 -08002012 /* sqlite soft heap limit http://www.sqlite.org/c3ref/soft_heap_limit64.html
2013 * set it to 4 times the default cursor window size.
2014 * TODO what is an appropriate value, considring the WAL feature which could burn
2015 * a lot of memory with many connections to the database. needs testing to figure out
2016 * optimal value for this.
2017 */
2018 int limit = Resources.getSystem().getInteger(
2019 com.android.internal.R.integer.config_cursorWindowSize) * 1024 * 4;
2020 native_setSqliteSoftHeapLimit(limit);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002021 }
2022
2023 /**
2024 * return whether the DB is opened as read only.
2025 * @return true if DB is opened as read only
2026 */
2027 public boolean isReadOnly() {
2028 return (mFlags & OPEN_READ_MASK) == OPEN_READONLY;
2029 }
2030
2031 /**
2032 * @return true if the DB is currently open (has not been closed)
2033 */
2034 public boolean isOpen() {
2035 return mNativeHandle != 0;
2036 }
2037
2038 public boolean needUpgrade(int newVersion) {
2039 return newVersion > getVersion();
2040 }
2041
2042 /**
2043 * Getter for the path to the database file.
2044 *
2045 * @return the path to our database file.
2046 */
2047 public final String getPath() {
2048 return mPath;
2049 }
2050
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -08002051 /* package */ void logTimeStat(String sql, long beginMillis) {
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07002052 logTimeStat(sql, beginMillis, null);
2053 }
2054
2055 /* package */ void logTimeStat(String sql, long beginMillis, String prefix) {
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -08002056 // Keep track of the last statement executed here, as this is
2057 // the common funnel through which all methods of hitting
2058 // libsqlite eventually flow.
2059 mLastSqlStatement = sql;
2060
Dan Egnor12311952009-11-23 14:47:45 -08002061 // Sample fast queries in proportion to the time taken.
2062 // Quantize the % first, so the logged sampling probability
2063 // exactly equals the actual sampling rate for this query.
2064
2065 int samplePercent;
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -08002066 long durationMillis = SystemClock.uptimeMillis() - beginMillis;
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07002067 if (durationMillis == 0 && prefix == GET_LOCK_LOG_PREFIX) {
2068 // The common case is locks being uncontended. Don't log those,
2069 // even at 1%, which is our default below.
2070 return;
2071 }
2072 if (sQueryLogTimeInMillis == 0) {
2073 sQueryLogTimeInMillis = SystemProperties.getInt("db.db_operation.threshold_ms", 500);
2074 }
2075 if (durationMillis >= sQueryLogTimeInMillis) {
Dan Egnor12311952009-11-23 14:47:45 -08002076 samplePercent = 100;
Vasu Norifb16cbd2010-07-25 16:38:48 -07002077 } else {
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07002078 samplePercent = (int) (100 * durationMillis / sQueryLogTimeInMillis) + 1;
Dan Egnor799f7212009-11-24 16:24:44 -08002079 if (mRandom.nextInt(100) >= samplePercent) return;
Dan Egnor12311952009-11-23 14:47:45 -08002080 }
2081
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07002082 // Note: the prefix will be "COMMIT;" or "GETLOCK:" when non-null. We wait to do
2083 // it here so we avoid allocating in the common case.
2084 if (prefix != null) {
2085 sql = prefix + sql;
2086 }
2087
Dan Egnor12311952009-11-23 14:47:45 -08002088 if (sql.length() > QUERY_LOG_SQL_LENGTH) sql = sql.substring(0, QUERY_LOG_SQL_LENGTH);
2089
2090 // ActivityThread.currentPackageName() only returns non-null if the
2091 // current thread is an application main thread. This parameter tells
2092 // us whether an event loop is blocked, and if so, which app it is.
2093 //
2094 // Sadly, there's no fast way to determine app name if this is *not* a
2095 // main thread, or when we are invoked via Binder (e.g. ContentProvider).
2096 // Hopefully the full path to the database will be informative enough.
2097
Dianne Hackborn01e4cfc2010-06-24 15:07:24 -07002098 String blockingPackage = AppGlobals.getInitialPackage();
Dan Egnor12311952009-11-23 14:47:45 -08002099 if (blockingPackage == null) blockingPackage = "";
2100
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -08002101 EventLog.writeEvent(
Brad Fitzpatrickd8330232010-02-19 10:59:01 -08002102 EVENT_DB_OPERATION,
2103 getPathForLogs(),
2104 sql,
2105 durationMillis,
2106 blockingPackage,
2107 samplePercent);
2108 }
2109
2110 /**
2111 * Removes email addresses from database filenames before they're
2112 * logged to the EventLog where otherwise apps could potentially
2113 * read them.
2114 */
2115 private String getPathForLogs() {
2116 if (mPathForLogs != null) {
2117 return mPathForLogs;
2118 }
2119 if (mPath == null) {
2120 return null;
2121 }
2122 if (mPath.indexOf('@') == -1) {
2123 mPathForLogs = mPath;
2124 } else {
2125 mPathForLogs = EMAIL_IN_DB_PATTERN.matcher(mPath).replaceAll("XX@YY");
2126 }
2127 return mPathForLogs;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002128 }
2129
2130 /**
2131 * Sets the locale for this database. Does nothing if this database has
2132 * the NO_LOCALIZED_COLLATORS flag set or was opened read only.
2133 * @throws SQLException if the locale could not be set. The most common reason
2134 * for this is that there is no collator available for the locale you requested.
2135 * In this case the database remains unchanged.
2136 */
2137 public void setLocale(Locale locale) {
2138 lock();
2139 try {
2140 native_setLocale(locale.toString(), mFlags);
2141 } finally {
2142 unlock();
2143 }
2144 }
2145
Vasu Noriccd95442010-05-28 17:04:16 -07002146 /* package */ void verifyDbIsOpen() {
Vasu Nori9463f292010-04-30 12:22:18 -07002147 if (!isOpen()) {
Vasu Nori75010102010-07-01 16:23:06 -07002148 throw new IllegalStateException("database " + getPath() + " (conn# " +
2149 mConnectionNum + ") already closed");
Vasu Nori9463f292010-04-30 12:22:18 -07002150 }
Vasu Noriccd95442010-05-28 17:04:16 -07002151 }
2152
2153 /* package */ void verifyLockOwner() {
2154 verifyDbIsOpen();
2155 if (mLockingEnabled && !isDbLockedByCurrentThread()) {
Vasu Nori9463f292010-04-30 12:22:18 -07002156 throw new IllegalStateException("Don't have database lock!");
2157 }
2158 }
2159
Vasu Norib729dcc2010-09-14 11:35:49 -07002160 /**
2161 * Adds the given SQL and its compiled-statement-id-returned-by-sqlite to the
2162 * cache of compiledQueries attached to 'this'.
2163 * <p>
2164 * If there is already a {@link SQLiteCompiledSql} in compiledQueries for the given SQL,
2165 * the new {@link SQLiteCompiledSql} object is NOT inserted into the cache (i.e.,the current
2166 * mapping is NOT replaced with the new mapping).
2167 */
2168 /* package */ void addToCompiledQueries(String sql, SQLiteCompiledSql compiledStatement) {
2169 synchronized(mCompiledQueries) {
2170 // don't insert the new mapping if a mapping already exists
2171 if (mCompiledQueries.containsKey(sql)) {
2172 return;
2173 }
2174
Vasu Norif0808f82010-10-08 14:48:48 -07002175 int maxCacheSz = (mConnectionNum == 0) ? mMaxSqlCacheSize :
2176 mParentConnObj.mMaxSqlCacheSize;
Brian Muramatsu46a88512010-11-12 13:53:57 -08002177
Vasu Norib6425182010-11-05 14:47:59 -07002178 if (SQLiteDebug.DEBUG_SQL_CACHE) {
2179 boolean printWarning = (mConnectionNum == 0)
2180 ? (!mCacheFullWarning && mCompiledQueries.size() == maxCacheSz)
2181 : (!mParentConnObj.mCacheFullWarning &&
2182 mParentConnObj.mCompiledQueries.size() == maxCacheSz);
2183 if (printWarning) {
2184 /*
2185 * cache size of {@link #mMaxSqlCacheSize} is not enough for this app.
2186 * log a warning.
2187 * chances are it is NOT using ? for bindargs - or cachesize is too small.
2188 */
2189 Log.w(TAG, "Reached MAX size for compiled-sql statement cache for database " +
2190 getPath() + ". Use setMaxSqlCacheSize() to increase cachesize. ");
2191 mCacheFullWarning = true;
2192 Log.d(TAG, "Here are the SQL statements in Cache of database: " + mPath);
2193 for (String s : mCompiledQueries.keySet()) {
2194 Log.d(TAG, "Sql stament in Cache: " + s);
2195 }
Vasu Nori74fb2682010-10-25 11:48:24 -07002196 }
Vasu Nori7301a232010-11-05 11:46:15 -07002197 }
Vasu Norib729dcc2010-09-14 11:35:49 -07002198 /* add the given SQLiteCompiledSql compiledStatement to cache.
2199 * no need to worry about the cache size - because {@link #mCompiledQueries}
2200 * self-limits its size to {@link #mMaxSqlCacheSize}.
2201 */
2202 mCompiledQueries.put(sql, compiledStatement);
Vasu Norib729dcc2010-09-14 11:35:49 -07002203 }
2204 }
2205
2206 /** package-level access for testing purposes */
2207 /* package */ void deallocCachedSqlStatements() {
2208 synchronized (mCompiledQueries) {
2209 for (SQLiteCompiledSql compiledSql : mCompiledQueries.values()) {
2210 compiledSql.releaseSqlStatement();
2211 }
2212 mCompiledQueries.clear();
2213 }
2214 }
2215
2216 /**
2217 * From the compiledQueries cache, returns the compiled-statement-id for the given SQL.
2218 * Returns null, if not found in the cache.
2219 */
Vasu Noriffe06122010-09-27 12:32:57 -07002220 /* package */ SQLiteCompiledSql getCompiledStatementForSql(String sql) {
2221 synchronized (mCompiledQueries) {
2222 SQLiteCompiledSql compiledStatement = mCompiledQueries.get(sql);
2223 if (compiledStatement == null) {
2224 mNumCacheMisses++;
2225 return null;
2226 }
2227 mNumCacheHits++;
2228 return compiledStatement;
Vasu Norib729dcc2010-09-14 11:35:49 -07002229 }
Vasu Norib729dcc2010-09-14 11:35:49 -07002230 }
2231
Vasu Norie495d1f2010-01-06 16:34:19 -08002232 /**
Vasu Noriccd95442010-05-28 17:04:16 -07002233 * Sets the maximum size of the prepared-statement cache for this database.
Vasu Norie495d1f2010-01-06 16:34:19 -08002234 * (size of the cache = number of compiled-sql-statements stored in the cache).
Vasu Noriccd95442010-05-28 17:04:16 -07002235 *<p>
Vasu Norib729dcc2010-09-14 11:35:49 -07002236 * Maximum cache size can ONLY be increased from its current size (default = 10).
Vasu Noriccd95442010-05-28 17:04:16 -07002237 * If this method is called with smaller size than the current maximum value,
2238 * then IllegalStateException is thrown.
Vasu Norib729dcc2010-09-14 11:35:49 -07002239 *<p>
2240 * This method is thread-safe.
Vasu Norie495d1f2010-01-06 16:34:19 -08002241 *
Vasu Nori90a367262010-04-12 12:49:09 -07002242 * @param cacheSize the size of the cache. can be (0 to {@link #MAX_SQL_CACHE_SIZE})
2243 * @throws IllegalStateException if input cacheSize > {@link #MAX_SQL_CACHE_SIZE} or
Vasu Noribfe1dc22010-08-25 16:29:02 -07002244 * the value set with previous setMaxSqlCacheSize() call.
Vasu Norie495d1f2010-01-06 16:34:19 -08002245 */
Vasu Nori54025902010-09-14 12:14:26 -07002246 public void setMaxSqlCacheSize(int cacheSize) {
Vasu Noriffe06122010-09-27 12:32:57 -07002247 synchronized(mCompiledQueries) {
Vasu Nori54025902010-09-14 12:14:26 -07002248 if (cacheSize > MAX_SQL_CACHE_SIZE || cacheSize < 0) {
2249 throw new IllegalStateException("expected value between 0 and " + MAX_SQL_CACHE_SIZE);
2250 } else if (cacheSize < mMaxSqlCacheSize) {
2251 throw new IllegalStateException("cannot set cacheSize to a value less than the value " +
2252 "set with previous setMaxSqlCacheSize() call.");
2253 }
2254 mMaxSqlCacheSize = cacheSize;
Vasu Norib729dcc2010-09-14 11:35:49 -07002255 }
Vasu Norib729dcc2010-09-14 11:35:49 -07002256 }
2257
Vasu Nori587423a2010-09-27 18:18:34 -07002258 /* package */ boolean isInStatementCache(String sql) {
Vasu Norib729dcc2010-09-14 11:35:49 -07002259 synchronized (mCompiledQueries) {
2260 return mCompiledQueries.containsKey(sql);
2261 }
Vasu Nori6c354da2010-04-26 23:33:39 -07002262 }
2263
Vasu Nori24675612010-09-27 14:54:19 -07002264 /* package */ void releaseCompiledSqlObj(SQLiteCompiledSql compiledSql) {
Vasu Nori587423a2010-09-27 18:18:34 -07002265 synchronized (mCompiledQueries) {
Vasu Nori24675612010-09-27 14:54:19 -07002266 if (mCompiledQueries.containsValue(compiledSql)) {
2267 // it is in cache - reset its inUse flag
2268 compiledSql.release();
2269 } else {
2270 // it is NOT in cache. finalize it.
2271 compiledSql.releaseSqlStatement();
2272 }
Vasu Nori587423a2010-09-27 18:18:34 -07002273 }
2274 }
2275
Vasu Nori24675612010-09-27 14:54:19 -07002276 private int getCacheHitNum() {
2277 synchronized(mCompiledQueries) {
2278 return mNumCacheHits;
2279 }
Vasu Nori5e89ae22010-09-15 14:23:29 -07002280 }
2281
Vasu Nori24675612010-09-27 14:54:19 -07002282 private int getCacheMissNum() {
2283 synchronized(mCompiledQueries) {
2284 return mNumCacheMisses;
2285 }
Vasu Nori5e89ae22010-09-15 14:23:29 -07002286 }
2287
Vasu Nori24675612010-09-27 14:54:19 -07002288 private int getCachesize() {
2289 synchronized(mCompiledQueries) {
2290 return mCompiledQueries.size();
2291 }
Vasu Nori5e89ae22010-09-15 14:23:29 -07002292 }
2293
Vasu Nori6f37f832010-05-19 11:53:25 -07002294 /* package */ void finalizeStatementLater(int id) {
2295 if (!isOpen()) {
2296 // database already closed. this statement will already have been finalized.
2297 return;
2298 }
2299 synchronized(mClosedStatementIds) {
2300 if (mClosedStatementIds.contains(id)) {
2301 // this statement id is already queued up for finalization.
2302 return;
2303 }
2304 mClosedStatementIds.add(id);
2305 }
2306 }
2307
Vasu Nori83ff97d2011-01-30 12:47:55 -08002308 /* package */ boolean isInQueueOfStatementsToBeFinalized(int id) {
2309 if (!isOpen()) {
2310 // database already closed. this statement will already have been finalized.
2311 // return true so that the caller doesn't have to worry about finalizing this statement.
2312 return true;
2313 }
2314 synchronized(mClosedStatementIds) {
2315 return mClosedStatementIds.contains(id);
2316 }
2317 }
2318
Vasu Norice38b982010-07-22 13:57:13 -07002319 /* package */ void closePendingStatements() {
Vasu Nori6f37f832010-05-19 11:53:25 -07002320 if (!isOpen()) {
2321 // since this database is already closed, no need to finalize anything.
2322 mClosedStatementIds.clear();
2323 return;
2324 }
2325 verifyLockOwner();
2326 /* to minimize synchronization on mClosedStatementIds, make a copy of the list */
2327 ArrayList<Integer> list = new ArrayList<Integer>(mClosedStatementIds.size());
2328 synchronized(mClosedStatementIds) {
2329 list.addAll(mClosedStatementIds);
2330 mClosedStatementIds.clear();
2331 }
2332 // finalize all the statements from the copied list
2333 int size = list.size();
2334 for (int i = 0; i < size; i++) {
2335 native_finalize(list.get(i));
2336 }
2337 }
2338
2339 /**
2340 * for testing only
Vasu Nori6f37f832010-05-19 11:53:25 -07002341 */
Vasu Norice38b982010-07-22 13:57:13 -07002342 /* package */ ArrayList<Integer> getQueuedUpStmtList() {
Vasu Nori6f37f832010-05-19 11:53:25 -07002343 return mClosedStatementIds;
2344 }
2345
Vasu Nori6c354da2010-04-26 23:33:39 -07002346 /**
2347 * This method enables parallel execution of queries from multiple threads on the same database.
2348 * It does this by opening multiple handles to the database and using a different
2349 * database handle for each query.
2350 * <p>
2351 * If a transaction is in progress on one connection handle and say, a table is updated in the
2352 * transaction, then query on the same table on another connection handle will block for the
2353 * transaction to complete. But this method enables such queries to execute by having them
2354 * return old version of the data from the table. Most often it is the data that existed in the
2355 * table prior to the above transaction updates on that table.
2356 * <p>
2357 * Maximum number of simultaneous handles used to execute queries in parallel is
2358 * dependent upon the device memory and possibly other properties.
2359 * <p>
2360 * After calling this method, execution of queries in parallel is enabled as long as this
2361 * database handle is open. To disable execution of queries in parallel, database should
2362 * be closed and reopened.
2363 * <p>
2364 * If a query is part of a transaction, then it is executed on the same database handle the
2365 * transaction was begun.
Vasu Nori6c354da2010-04-26 23:33:39 -07002366 * <p>
2367 * If the database has any attached databases, then execution of queries in paralel is NOT
Vasu Noria98cb262010-06-22 13:16:35 -07002368 * possible. In such cases, a message is printed to logcat and false is returned.
2369 * <p>
2370 * This feature is not available for :memory: databases. In such cases,
2371 * a message is printed to logcat and false is returned.
Vasu Nori6c354da2010-04-26 23:33:39 -07002372 * <p>
2373 * A typical way to use this method is the following:
2374 * <pre>
2375 * SQLiteDatabase db = SQLiteDatabase.openDatabase("db_filename", cursorFactory,
2376 * CREATE_IF_NECESSARY, myDatabaseErrorHandler);
2377 * db.enableWriteAheadLogging();
2378 * </pre>
2379 * <p>
2380 * Writers should use {@link #beginTransactionNonExclusive()} or
2381 * {@link #beginTransactionWithListenerNonExclusive(SQLiteTransactionListener)}
2382 * to start a trsnsaction.
2383 * Non-exclusive mode allows database file to be in readable by threads executing queries.
2384 * </p>
2385 *
Vasu Noria98cb262010-06-22 13:16:35 -07002386 * @return true if write-ahead-logging is set. false otherwise
Vasu Nori6c354da2010-04-26 23:33:39 -07002387 */
Vasu Noriffe06122010-09-27 12:32:57 -07002388 public boolean enableWriteAheadLogging() {
Vasu Nori8fcda302010-11-08 13:46:40 -08002389 // make sure the database is not READONLY. WAL doesn't make sense for readonly-databases.
2390 if (isReadOnly()) {
2391 return false;
2392 }
Vasu Nori24675612010-09-27 14:54:19 -07002393 // acquire lock - no that no other thread is enabling WAL at the same time
2394 lock();
2395 try {
2396 if (mConnectionPool != null) {
2397 // already enabled
2398 return true;
2399 }
Vasu Noriffe06122010-09-27 12:32:57 -07002400 if (mPath.equalsIgnoreCase(MEMORY_DB_PATH)) {
2401 Log.i(TAG, "can't enable WAL for memory databases.");
2402 return false;
Vasu Norice38b982010-07-22 13:57:13 -07002403 }
Vasu Noriffe06122010-09-27 12:32:57 -07002404
2405 // make sure this database has NO attached databases because sqlite's write-ahead-logging
2406 // doesn't work for databases with attached databases
Vasu Nori24675612010-09-27 14:54:19 -07002407 if (mHasAttachedDbs) {
Vasu Noriffe06122010-09-27 12:32:57 -07002408 if (Log.isLoggable(TAG, Log.DEBUG)) {
2409 Log.d(TAG,
2410 "this database: " + mPath + " has attached databases. can't enable WAL.");
2411 }
2412 return false;
2413 }
Vasu Nori24675612010-09-27 14:54:19 -07002414 mConnectionPool = new DatabaseConnectionPool(this);
2415 setJournalMode(mPath, "WAL");
Vasu Noriffe06122010-09-27 12:32:57 -07002416 return true;
Vasu Nori24675612010-09-27 14:54:19 -07002417 } finally {
2418 unlock();
Vasu Nori6c354da2010-04-26 23:33:39 -07002419 }
Vasu Nori6c354da2010-04-26 23:33:39 -07002420 }
2421
Vasu Nori2827d6d2010-07-04 00:26:18 -07002422 /**
Vasu Nori7b04c412010-07-20 10:31:21 -07002423 * This method disables the features enabled by {@link #enableWriteAheadLogging()}.
2424 * @hide
Vasu Nori2827d6d2010-07-04 00:26:18 -07002425 */
Vasu Nori7b04c412010-07-20 10:31:21 -07002426 public void disableWriteAheadLogging() {
Vasu Nori24675612010-09-27 14:54:19 -07002427 // grab database lock so that writeAheadLogging is not disabled from 2 different threads
2428 // at the same time
2429 lock();
2430 try {
Vasu Nori7b04c412010-07-20 10:31:21 -07002431 if (mConnectionPool == null) {
Vasu Nori24675612010-09-27 14:54:19 -07002432 return; // already disabled
Vasu Nori7b04c412010-07-20 10:31:21 -07002433 }
2434 mConnectionPool.close();
Vasu Nori7b04c412010-07-20 10:31:21 -07002435 setJournalMode(mPath, "TRUNCATE");
Vasu Nori24675612010-09-27 14:54:19 -07002436 mConnectionPool = null;
2437 } finally {
2438 unlock();
Vasu Nori8d111032010-06-22 18:34:21 -07002439 }
Vasu Nori8d111032010-06-22 18:34:21 -07002440 }
2441
Vasu Nori65a88832010-07-16 15:14:08 -07002442 /* package */ SQLiteDatabase getDatabaseHandle(String sql) {
2443 if (isPooledConnection()) {
2444 // this is a pooled database connection
Vasu Norice38b982010-07-22 13:57:13 -07002445 // use it if it is open AND if I am not currently part of a transaction
2446 if (isOpen() && !amIInTransaction()) {
Vasu Nori65a88832010-07-16 15:14:08 -07002447 // TODO: use another connection from the pool
2448 // if this connection is currently in use by some other thread
2449 // AND if there are free connections in the pool
2450 return this;
2451 } else {
2452 // the pooled connection is not open! could have been closed either due
2453 // to corruption on this or some other connection to the database
2454 // OR, maybe the connection pool is disabled after this connection has been
2455 // allocated to me. try to get some other pooled or main database connection
2456 return getParentDbConnObj().getDbConnection(sql);
2457 }
2458 } else {
2459 // this is NOT a pooled connection. can we get one?
2460 return getDbConnection(sql);
2461 }
2462 }
2463
Vasu Nori6c354da2010-04-26 23:33:39 -07002464 /* package */ SQLiteDatabase createPoolConnection(short connectionNum) {
Vasu Nori65a88832010-07-16 15:14:08 -07002465 SQLiteDatabase db = openDatabase(mPath, mFactory, mFlags, mErrorHandler, connectionNum);
2466 db.mParentConnObj = this;
2467 return db;
2468 }
2469
2470 private synchronized SQLiteDatabase getParentDbConnObj() {
2471 return mParentConnObj;
Vasu Nori6c354da2010-04-26 23:33:39 -07002472 }
2473
2474 private boolean isPooledConnection() {
2475 return this.mConnectionNum > 0;
2476 }
2477
Vasu Nori2827d6d2010-07-04 00:26:18 -07002478 /* package */ SQLiteDatabase getDbConnection(String sql) {
Vasu Nori6c354da2010-04-26 23:33:39 -07002479 verifyDbIsOpen();
Vasu Noribfe1dc22010-08-25 16:29:02 -07002480 // this method should always be called with main database connection handle.
2481 // the only time when it is called with pooled database connection handle is
2482 // corruption occurs while trying to open a pooled database connection handle.
2483 // in that case, simply return 'this' handle
Vasu Nori65a88832010-07-16 15:14:08 -07002484 if (isPooledConnection()) {
Vasu Noribfe1dc22010-08-25 16:29:02 -07002485 return this;
Vasu Nori65a88832010-07-16 15:14:08 -07002486 }
Vasu Nori6c354da2010-04-26 23:33:39 -07002487
2488 // use the current connection handle if
Vasu Norice38b982010-07-22 13:57:13 -07002489 // 1. if the caller is part of the ongoing transaction, if any
Vasu Nori65a88832010-07-16 15:14:08 -07002490 // 2. OR, if there is NO connection handle pool setup
Vasu Norice38b982010-07-22 13:57:13 -07002491 if (amIInTransaction() || mConnectionPool == null) {
Vasu Nori65a88832010-07-16 15:14:08 -07002492 return this;
Vasu Nori6c354da2010-04-26 23:33:39 -07002493 } else {
2494 // get a connection handle from the pool
2495 if (Log.isLoggable(TAG, Log.DEBUG)) {
2496 assert mConnectionPool != null;
Vasu Norice38b982010-07-22 13:57:13 -07002497 Log.i(TAG, mConnectionPool.toString());
Vasu Nori6c354da2010-04-26 23:33:39 -07002498 }
Vasu Nori65a88832010-07-16 15:14:08 -07002499 return mConnectionPool.get(sql);
Vasu Nori6c354da2010-04-26 23:33:39 -07002500 }
Vasu Nori6c354da2010-04-26 23:33:39 -07002501 }
2502
2503 private void releaseDbConnection(SQLiteDatabase db) {
2504 // ignore this release call if
2505 // 1. the database is closed
2506 // 2. OR, if db is NOT a pooled connection handle
2507 // 3. OR, if the database being released is same as 'this' (this condition means
2508 // that we should always be releasing a pooled connection handle by calling this method
2509 // from the 'main' connection handle
2510 if (!isOpen() || !db.isPooledConnection() || (db == this)) {
2511 return;
2512 }
2513 if (Log.isLoggable(TAG, Log.DEBUG)) {
2514 assert isPooledConnection();
2515 assert mConnectionPool != null;
2516 Log.d(TAG, "releaseDbConnection threadid = " + Thread.currentThread().getId() +
2517 ", releasing # " + db.mConnectionNum + ", " + getPath());
2518 }
2519 mConnectionPool.release(db);
2520 }
2521
Vasu Norif3cf8a42010-03-23 11:41:44 -07002522 /**
2523 * this method is used to collect data about ALL open databases in the current process.
Vasu Nori0732f792010-07-29 17:24:12 -07002524 * bugreport is a user of this data.
Vasu Norif3cf8a42010-03-23 11:41:44 -07002525 */
Vasu Noric3849202010-03-09 10:47:25 -08002526 /* package */ static ArrayList<DbStats> getDbStats() {
2527 ArrayList<DbStats> dbStatsList = new ArrayList<DbStats>();
Vasu Nori24675612010-09-27 14:54:19 -07002528 // make a local copy of mActiveDatabases - so that this method is not competing
2529 // for synchronization lock on mActiveDatabases
2530 ArrayList<WeakReference<SQLiteDatabase>> tempList;
2531 synchronized(mActiveDatabases) {
2532 tempList = (ArrayList<WeakReference<SQLiteDatabase>>)mActiveDatabases.clone();
2533 }
2534 for (WeakReference<SQLiteDatabase> w : tempList) {
2535 SQLiteDatabase db = w.get();
2536 if (db == null || !db.isOpen()) {
2537 continue;
2538 }
2539
2540 try {
2541 // get SQLITE_DBSTATUS_LOOKASIDE_USED for the db
2542 int lookasideUsed = db.native_getDbLookaside();
2543
2544 // get the lastnode of the dbname
2545 String path = db.getPath();
2546 int indx = path.lastIndexOf("/");
2547 String lastnode = path.substring((indx != -1) ? ++indx : 0);
2548
2549 // get list of attached dbs and for each db, get its size and pagesize
Vasu Noria017eda2011-01-27 10:52:55 -08002550 List<Pair<String, String>> attachedDbs = db.getAttachedDbs();
Vasu Nori24675612010-09-27 14:54:19 -07002551 if (attachedDbs == null) {
2552 continue;
2553 }
2554 for (int i = 0; i < attachedDbs.size(); i++) {
2555 Pair<String, String> p = attachedDbs.get(i);
2556 long pageCount = DatabaseUtils.longForQuery(db, "PRAGMA " + p.first
2557 + ".page_count;", null);
2558
2559 // first entry in the attached db list is always the main database
2560 // don't worry about prefixing the dbname with "main"
2561 String dbName;
2562 if (i == 0) {
2563 dbName = lastnode;
2564 } else {
2565 // lookaside is only relevant for the main db
2566 lookasideUsed = 0;
2567 dbName = " (attached) " + p.first;
2568 // if the attached db has a path, attach the lastnode from the path to above
2569 if (p.second.trim().length() > 0) {
2570 int idx = p.second.lastIndexOf("/");
2571 dbName += " : " + p.second.substring((idx != -1) ? ++idx : 0);
2572 }
2573 }
2574 if (pageCount > 0) {
2575 dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(),
2576 lookasideUsed, db.getCacheHitNum(), db.getCacheMissNum(),
Vasu Nori00e40172010-11-29 11:03:23 -08002577 db.getCachesize()));
Vasu Nori24675612010-09-27 14:54:19 -07002578 }
2579 }
2580 // if there are pooled connections, return the cache stats for them also.
2581 // while we are trying to query the pooled connections for stats, some other thread
2582 // could be disabling conneciton pool. so, grab a reference to the connection pool.
2583 DatabaseConnectionPool connPool = db.mConnectionPool;
2584 if (connPool != null) {
2585 for (SQLiteDatabase pDb : connPool.getConnectionList()) {
2586 dbStatsList.add(new DbStats("(pooled # " + pDb.mConnectionNum + ") "
2587 + lastnode, 0, 0, 0, pDb.getCacheHitNum(),
Vasu Nori00e40172010-11-29 11:03:23 -08002588 pDb.getCacheMissNum(), pDb.getCachesize()));
Vasu Nori24675612010-09-27 14:54:19 -07002589 }
2590 }
2591 } catch (SQLiteException e) {
2592 // ignore. we don't care about exceptions when we are taking adb
2593 // bugreport!
2594 }
2595 }
Vasu Noric3849202010-03-09 10:47:25 -08002596 return dbStatsList;
2597 }
2598
2599 /**
Vasu Noriccd95442010-05-28 17:04:16 -07002600 * Returns list of full pathnames of all attached databases including the main database
2601 * by executing 'pragma database_list' on the database.
2602 *
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002603 * @return ArrayList of pairs of (database name, database file path) or null if the database
2604 * is not open.
Vasu Noric3849202010-03-09 10:47:25 -08002605 */
Vasu Noria017eda2011-01-27 10:52:55 -08002606 public List<Pair<String, String>> getAttachedDbs() {
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002607 if (!isOpen()) {
Vasu Norif3cf8a42010-03-23 11:41:44 -07002608 return null;
2609 }
Vasu Noric3849202010-03-09 10:47:25 -08002610 ArrayList<Pair<String, String>> attachedDbs = new ArrayList<Pair<String, String>>();
Vasu Nori24675612010-09-27 14:54:19 -07002611 if (!mHasAttachedDbs) {
2612 // No attached databases.
2613 // There is a small window where attached databases exist but this flag is not set yet.
2614 // This can occur when this thread is in a race condition with another thread
2615 // that is executing the SQL statement: "attach database <blah> as <foo>"
2616 // If this thread is NOT ok with such a race condition (and thus possibly not receive
2617 // the entire list of attached databases), then the caller should ensure that no thread
2618 // is executing any SQL statements while a thread is calling this method.
2619 // Typically, this method is called when 'adb bugreport' is done or the caller wants to
2620 // collect stats on the database and all its attached databases.
2621 attachedDbs.add(new Pair<String, String>("main", mPath));
2622 return attachedDbs;
2623 }
2624 // has attached databases. query sqlite to get the list of attached databases.
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002625 Cursor c = null;
2626 try {
2627 c = rawQuery("pragma database_list;", null);
2628 while (c.moveToNext()) {
2629 // sqlite returns a row for each database in the returned list of databases.
2630 // in each row,
2631 // 1st column is the database name such as main, or the database
2632 // name specified on the "ATTACH" command
2633 // 2nd column is the database file path.
2634 attachedDbs.add(new Pair<String, String>(c.getString(1), c.getString(2)));
2635 }
2636 } finally {
2637 if (c != null) {
2638 c.close();
2639 }
Vasu Noric3849202010-03-09 10:47:25 -08002640 }
Vasu Noric3849202010-03-09 10:47:25 -08002641 return attachedDbs;
2642 }
2643
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002644 /**
Vasu Noriccd95442010-05-28 17:04:16 -07002645 * Runs 'pragma integrity_check' on the given database (and all the attached databases)
2646 * and returns true if the given database (and all its attached databases) pass integrity_check,
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002647 * false otherwise.
Vasu Noriccd95442010-05-28 17:04:16 -07002648 *<p>
2649 * If the result is false, then this method logs the errors reported by the integrity_check
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002650 * command execution.
Vasu Noriccd95442010-05-28 17:04:16 -07002651 *<p>
2652 * Note that 'pragma integrity_check' on a database can take a long time.
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002653 *
2654 * @return true if the given database (and all its attached databases) pass integrity_check,
Vasu Noriccd95442010-05-28 17:04:16 -07002655 * false otherwise.
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002656 */
2657 public boolean isDatabaseIntegrityOk() {
Vasu Noriccd95442010-05-28 17:04:16 -07002658 verifyDbIsOpen();
Vasu Noria017eda2011-01-27 10:52:55 -08002659 List<Pair<String, String>> attachedDbs = null;
Vasu Noribfe1dc22010-08-25 16:29:02 -07002660 try {
2661 attachedDbs = getAttachedDbs();
2662 if (attachedDbs == null) {
2663 throw new IllegalStateException("databaselist for: " + getPath() + " couldn't " +
2664 "be retrieved. probably because the database is closed");
2665 }
2666 } catch (SQLiteException e) {
2667 // can't get attachedDb list. do integrity check on the main database
2668 attachedDbs = new ArrayList<Pair<String, String>>();
2669 attachedDbs.add(new Pair<String, String>("main", this.mPath));
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002670 }
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002671 for (int i = 0; i < attachedDbs.size(); i++) {
2672 Pair<String, String> p = attachedDbs.get(i);
2673 SQLiteStatement prog = null;
2674 try {
2675 prog = compileStatement("PRAGMA " + p.first + ".integrity_check(1);");
2676 String rslt = prog.simpleQueryForString();
2677 if (!rslt.equalsIgnoreCase("ok")) {
2678 // integrity_checker failed on main or attached databases
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002679 Log.e(TAG, "PRAGMA integrity_check on " + p.second + " returned: " + rslt);
Vasu Noribfe1dc22010-08-25 16:29:02 -07002680 return false;
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002681 }
2682 } finally {
2683 if (prog != null) prog.close();
2684 }
2685 }
Vasu Noribfe1dc22010-08-25 16:29:02 -07002686 return true;
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002687 }
2688
2689 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002690 * Native call to open the database.
2691 *
2692 * @param path The full path to the database
2693 */
2694 private native void dbopen(String path, int flags);
2695
2696 /**
Vasu Noriccd95442010-05-28 17:04:16 -07002697 * Native call to setup tracing of all SQL statements
Vasu Nori3ef94e22010-02-05 14:49:04 -08002698 *
2699 * @param path the full path to the database
Vasu Nori6c354da2010-04-26 23:33:39 -07002700 * @param connectionNum connection number: 0 - N, where the main database
2701 * connection handle is numbered 0 and the connection handles in the connection
2702 * pool are numbered 1..N.
Vasu Nori3ef94e22010-02-05 14:49:04 -08002703 */
Vasu Nori6c354da2010-04-26 23:33:39 -07002704 private native void enableSqlTracing(String path, short connectionNum);
Vasu Nori3ef94e22010-02-05 14:49:04 -08002705
2706 /**
Vasu Noriccd95442010-05-28 17:04:16 -07002707 * Native call to setup profiling of all SQL statements.
Vasu Nori3ef94e22010-02-05 14:49:04 -08002708 * currently, sqlite's profiling = printing of execution-time
Vasu Noriccd95442010-05-28 17:04:16 -07002709 * (wall-clock time) of each of the SQL statements, as they
Vasu Nori3ef94e22010-02-05 14:49:04 -08002710 * are executed.
2711 *
2712 * @param path the full path to the database
Vasu Nori6c354da2010-04-26 23:33:39 -07002713 * @param connectionNum connection number: 0 - N, where the main database
2714 * connection handle is numbered 0 and the connection handles in the connection
2715 * pool are numbered 1..N.
Vasu Nori3ef94e22010-02-05 14:49:04 -08002716 */
Vasu Nori6c354da2010-04-26 23:33:39 -07002717 private native void enableSqlProfiling(String path, short connectionNum);
Vasu Nori3ef94e22010-02-05 14:49:04 -08002718
2719 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002720 * Native call to set the locale. {@link #lock} must be held when calling
2721 * this method.
2722 * @throws SQLException
2723 */
Vasu Nori0732f792010-07-29 17:24:12 -07002724 private native void native_setLocale(String loc, int flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002725
2726 /**
Vasu Noric3849202010-03-09 10:47:25 -08002727 * return the SQLITE_DBSTATUS_LOOKASIDE_USED documented here
2728 * http://www.sqlite.org/c3ref/c_dbstatus_lookaside_used.html
2729 * @return int value of SQLITE_DBSTATUS_LOOKASIDE_USED
2730 */
2731 private native int native_getDbLookaside();
Vasu Nori6f37f832010-05-19 11:53:25 -07002732
2733 /**
2734 * finalizes the given statement id.
2735 *
2736 * @param statementId statement to be finzlied by sqlite
2737 */
2738 private final native void native_finalize(int statementId);
Vasu Nori34ad57f02010-12-21 09:32:36 -08002739
2740 /**
2741 * set sqlite soft heap limit
2742 * http://www.sqlite.org/c3ref/soft_heap_limit64.html
2743 */
2744 private native void native_setSqliteSoftHeapLimit(int softHeapLimit);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002745}