blob: 93a6ad3f55c1b48db09adddec85bced43ec49605 [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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.util.EventLog;
Dmitri Plotnikov90142c92009-09-15 10:52:17 -070034import android.util.Log;
Jesse Wilson9b5a9352011-02-10 11:19:09 -080035import android.util.LruCache;
Vasu Noric3849202010-03-09 10:47:25 -080036import android.util.Pair;
Brad Fitzpatrickcfda9f32010-06-03 12:52:54 -070037import dalvik.system.BlockGuard;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038import java.io.File;
Vasu Noric3849202010-03-09 10:47:25 -080039import java.lang.ref.WeakReference;
Vasu Noric3849202010-03-09 10:47:25 -080040import java.util.ArrayList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import java.util.HashMap;
42import java.util.Iterator;
Jesse Wilson9b5a9352011-02-10 11:19:09 -080043import java.util.List;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044import java.util.Locale;
45import java.util.Map;
Dan Egnor12311952009-11-23 14:47:45 -080046import java.util.Random;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import java.util.WeakHashMap;
Vasu Norid4608a32011-02-03 16:24:06 -080048import java.util.concurrent.TimeUnit;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import java.util.concurrent.locks.ReentrantLock;
Brad Fitzpatrickd8330232010-02-19 10:59:01 -080050import java.util.regex.Pattern;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051
52/**
53 * Exposes methods to manage a SQLite database.
54 * <p>SQLiteDatabase has methods to create, delete, execute SQL commands, and
55 * perform other common database management tasks.
56 * <p>See the Notepad sample application in the SDK for an example of creating
57 * and managing a database.
58 * <p> Database names must be unique within an application, not across all
59 * applications.
60 *
61 * <h3>Localized Collation - ORDER BY</h3>
62 * <p>In addition to SQLite's default <code>BINARY</code> collator, Android supplies
63 * two more, <code>LOCALIZED</code>, which changes with the system's current locale
64 * if you wire it up correctly (XXX a link needed!), and <code>UNICODE</code>, which
65 * is the Unicode Collation Algorithm and not tailored to the current locale.
66 */
67public class SQLiteDatabase extends SQLiteClosable {
Vasu Norifb16cbd2010-07-25 16:38:48 -070068 private static final String TAG = "SQLiteDatabase";
Jeff Hamilton082c2af2009-09-29 11:49:51 -070069 private static final int EVENT_DB_OPERATION = 52000;
70 private static final int EVENT_DB_CORRUPT = 75004;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071
72 /**
73 * Algorithms used in ON CONFLICT clause
74 * http://www.sqlite.org/lang_conflict.html
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 */
Vasu Nori8d45e4e2010-02-05 22:35:47 -080076 /**
77 * When a constraint violation occurs, an immediate ROLLBACK occurs,
78 * thus ending the current transaction, and the command aborts with a
79 * return code of SQLITE_CONSTRAINT. If no transaction is active
80 * (other than the implied transaction that is created on every command)
81 * then this algorithm works the same as ABORT.
82 */
83 public static final int CONFLICT_ROLLBACK = 1;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -070084
Vasu Nori8d45e4e2010-02-05 22:35:47 -080085 /**
86 * When a constraint violation occurs,no ROLLBACK is executed
87 * so changes from prior commands within the same transaction
88 * are preserved. This is the default behavior.
89 */
90 public static final int CONFLICT_ABORT = 2;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -070091
Vasu Nori8d45e4e2010-02-05 22:35:47 -080092 /**
93 * When a constraint violation occurs, the command aborts with a return
94 * code SQLITE_CONSTRAINT. But any changes to the database that
95 * the command made prior to encountering the constraint violation
96 * are preserved and are not backed out.
97 */
98 public static final int CONFLICT_FAIL = 3;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -070099
Vasu Nori8d45e4e2010-02-05 22:35:47 -0800100 /**
101 * When a constraint violation occurs, the one row that contains
102 * the constraint violation is not inserted or changed.
103 * But the command continues executing normally. Other rows before and
104 * after the row that contained the constraint violation continue to be
105 * inserted or updated normally. No error is returned.
106 */
107 public static final int CONFLICT_IGNORE = 4;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700108
Vasu Nori8d45e4e2010-02-05 22:35:47 -0800109 /**
110 * When a UNIQUE constraint violation occurs, the pre-existing rows that
111 * are causing the constraint violation are removed prior to inserting
112 * or updating the current row. Thus the insert or update always occurs.
113 * The command continues executing normally. No error is returned.
114 * If a NOT NULL constraint violation occurs, the NULL value is replaced
115 * by the default value for that column. If the column has no default
116 * value, then the ABORT algorithm is used. If a CHECK constraint
117 * violation occurs then the IGNORE algorithm is used. When this conflict
118 * resolution strategy deletes rows in order to satisfy a constraint,
119 * it does not invoke delete triggers on those rows.
120 * This behavior might change in a future release.
121 */
122 public static final int CONFLICT_REPLACE = 5;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700123
Vasu Nori8d45e4e2010-02-05 22:35:47 -0800124 /**
125 * use the following when no conflict action is specified.
126 */
127 public static final int CONFLICT_NONE = 0;
128 private static final String[] CONFLICT_VALUES = new String[]
129 {"", " OR ROLLBACK ", " OR ABORT ", " OR FAIL ", " OR IGNORE ", " OR REPLACE "};
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700130
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131 /**
132 * Maximum Length Of A LIKE Or GLOB Pattern
133 * The pattern matching algorithm used in the default LIKE and GLOB implementation
134 * of SQLite can exhibit O(N^2) performance (where N is the number of characters in
135 * the pattern) for certain pathological cases. To avoid denial-of-service attacks
136 * the length of the LIKE or GLOB pattern is limited to SQLITE_MAX_LIKE_PATTERN_LENGTH bytes.
137 * The default value of this limit is 50000. A modern workstation can evaluate
138 * even a pathological LIKE or GLOB pattern of 50000 bytes relatively quickly.
139 * The denial of service problem only comes into play when the pattern length gets
140 * into millions of bytes. Nevertheless, since most useful LIKE or GLOB patterns
141 * are at most a few dozen bytes in length, paranoid application developers may
142 * want to reduce this parameter to something in the range of a few hundred
143 * if they know that external users are able to generate arbitrary patterns.
144 */
145 public static final int SQLITE_MAX_LIKE_PATTERN_LENGTH = 50000;
146
147 /**
148 * Flag for {@link #openDatabase} to open the database for reading and writing.
149 * If the disk is full, this may fail even before you actually write anything.
150 *
151 * {@more} Note that the value of this flag is 0, so it is the default.
152 */
153 public static final int OPEN_READWRITE = 0x00000000; // update native code if changing
154
155 /**
156 * Flag for {@link #openDatabase} to open the database for reading only.
157 * This is the only reliable way to open a database if the disk may be full.
158 */
159 public static final int OPEN_READONLY = 0x00000001; // update native code if changing
160
161 private static final int OPEN_READ_MASK = 0x00000001; // update native code if changing
162
163 /**
164 * Flag for {@link #openDatabase} to open the database without support for localized collators.
165 *
166 * {@more} This causes the collator <code>LOCALIZED</code> not to be created.
167 * You must be consistent when using this flag to use the setting the database was
168 * created with. If this is set, {@link #setLocale} will do nothing.
169 */
170 public static final int NO_LOCALIZED_COLLATORS = 0x00000010; // update native code if changing
171
172 /**
173 * Flag for {@link #openDatabase} to create the database file if it does not already exist.
174 */
175 public static final int CREATE_IF_NECESSARY = 0x10000000; // update native code if changing
176
177 /**
178 * Indicates whether the most-recently started transaction has been marked as successful.
179 */
180 private boolean mInnerTransactionIsSuccessful;
181
182 /**
183 * Valid during the life of a transaction, and indicates whether the entire transaction (the
184 * outer one and all of the inner ones) so far has been successful.
185 */
186 private boolean mTransactionIsSuccessful;
187
Fred Quintanac4516a72009-09-03 12:14:06 -0700188 /**
189 * Valid during the life of a transaction.
190 */
191 private SQLiteTransactionListener mTransactionListener;
192
Vasu Norice38b982010-07-22 13:57:13 -0700193 /**
194 * this member is set if {@link #execSQL(String)} is used to begin and end transactions.
195 */
196 private boolean mTransactionUsingExecSql;
197
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 /** Synchronize on this when accessing the database */
Vasu Norid4608a32011-02-03 16:24:06 -0800199 private final DatabaseReentrantLock mLock = new DatabaseReentrantLock(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800200
201 private long mLockAcquiredWallTime = 0L;
202 private long mLockAcquiredThreadTime = 0L;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700203
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800204 // limit the frequency of complaints about each database to one within 20 sec
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700205 // unless run command adb shell setprop log.tag.Database VERBOSE
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 private static final int LOCK_WARNING_WINDOW_IN_MS = 20000;
207 /** If the lock is held this long then a warning will be printed when it is released. */
208 private static final int LOCK_ACQUIRED_WARNING_TIME_IN_MS = 300;
209 private static final int LOCK_ACQUIRED_WARNING_THREAD_TIME_IN_MS = 100;
210 private static final int LOCK_ACQUIRED_WARNING_TIME_IN_MS_ALWAYS_PRINT = 2000;
211
Dmitri Plotnikovb43b58d2009-09-09 18:10:42 -0700212 private static final int SLEEP_AFTER_YIELD_QUANTUM = 1000;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700213
Brad Fitzpatrickd8330232010-02-19 10:59:01 -0800214 // The pattern we remove from database filenames before
215 // potentially logging them.
216 private static final Pattern EMAIL_IN_DB_PATTERN = Pattern.compile("[\\w\\.\\-]+@[\\w\\.\\-]+");
217
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 private long mLastLockMessageTime = 0L;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700219
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -0800220 // Things related to query logging/sampling for debugging
221 // slow/frequent queries during development. Always log queries
Brad Fitzpatrick722802e2010-03-23 22:22:16 -0700222 // which take (by default) 500ms+; shorter queries are sampled
223 // accordingly. Commit statements, which are typically slow, are
224 // logged together with the most recently executed SQL statement,
225 // for disambiguation. The 500ms value is configurable via a
226 // SystemProperty, but developers actively debugging database I/O
227 // should probably use the regular log tunable,
228 // LOG_SLOW_QUERIES_PROPERTY, defined below.
229 private static int sQueryLogTimeInMillis = 0; // lazily initialized
Dan Egnor12311952009-11-23 14:47:45 -0800230 private static final int QUERY_LOG_SQL_LENGTH = 64;
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -0800231 private static final String COMMIT_SQL = "COMMIT;";
Vasu Nori16057fa2011-03-18 11:40:37 -0700232 private static final String BEGIN_SQL = "BEGIN;";
Dan Egnor12311952009-11-23 14:47:45 -0800233 private final Random mRandom = new Random();
Vasu Nori16057fa2011-03-18 11:40:37 -0700234 /** the last non-commit/rollback sql statement in a transaction */
235 // guarded by 'this'
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -0800236 private String mLastSqlStatement = null;
Dan Egnor12311952009-11-23 14:47:45 -0800237
Vasu Nori16057fa2011-03-18 11:40:37 -0700238 synchronized String getLastSqlStatement() {
239 return mLastSqlStatement;
240 }
241
242 synchronized void setLastSqlStatement(String sql) {
243 mLastSqlStatement = sql;
244 }
245
246 /** guarded by {@link #mLock} */
247 private long mTransStartTime;
248
Brad Fitzpatrick722802e2010-03-23 22:22:16 -0700249 // String prefix for slow database query EventLog records that show
250 // lock acquistions of the database.
251 /* package */ static final String GET_LOCK_LOG_PREFIX = "GETLOCK:";
252
Vasu Nori6f37f832010-05-19 11:53:25 -0700253 /** Used by native code, do not rename. make it volatile, so it is thread-safe. */
254 /* package */ volatile int mNativeHandle = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800255
Vasu Noria8c24902010-06-01 11:30:27 -0700256 /**
257 * The size, in bytes, of a block on "/data". This corresponds to the Unix
258 * statfs.f_bsize field. note that this field is lazily initialized.
259 */
260 private static int sBlockSize = 0;
261
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800262 /** The path for the database file */
Vasu Noriccd95442010-05-28 17:04:16 -0700263 private final String mPath;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800264
Brad Fitzpatrickd8330232010-02-19 10:59:01 -0800265 /** The anonymized path for the database file for logging purposes */
266 private String mPathForLogs = null; // lazily populated
267
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800268 /** The flags passed to open/create */
Vasu Noriccd95442010-05-28 17:04:16 -0700269 private final int mFlags;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270
271 /** The optional factory to use when creating new Cursors */
Vasu Noriccd95442010-05-28 17:04:16 -0700272 private final CursorFactory mFactory;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700273
Vasu Nori21343692010-06-03 16:01:39 -0700274 private final WeakHashMap<SQLiteClosable, Object> mPrograms;
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700275
Jesse Wilson9b5a9352011-02-10 11:19:09 -0800276 /** Default statement-cache size per database connection ( = instance of this class) */
Jesse Wilsonc2c9a242011-02-10 19:19:02 -0800277 private static final int DEFAULT_SQL_CACHE_SIZE = 25;
Jesse Wilson9b5a9352011-02-10 11:19:09 -0800278
Vasu Nori5a03f362009-10-20 15:16:35 -0700279 /**
Vasu Norib729dcc2010-09-14 11:35:49 -0700280 * for each instance of this class, a LRU cache is maintained to store
281 * the compiled query statement ids returned by sqlite database.
282 * key = SQL statement with "?" for bind args
283 * value = {@link SQLiteCompiledSql}
284 * If an application opens the database and keeps it open during its entire life, then
285 * there will not be an overhead of compilation of SQL statements by sqlite.
286 *
287 * why is this cache NOT static? because sqlite attaches compiledsql statements to the
288 * struct created when {@link SQLiteDatabase#openDatabase(String, CursorFactory, int)} is
289 * invoked.
290 *
Jesse Wilson9b5a9352011-02-10 11:19:09 -0800291 * this cache's max size is settable by calling the method
Jesse Wilsondfe515e2011-02-10 19:06:09 -0800292 * (@link #setMaxSqlCacheSize(int)}.
Vasu Norib729dcc2010-09-14 11:35:49 -0700293 */
Jesse Wilsondfe515e2011-02-10 19:06:09 -0800294 // guarded by this
295 private LruCache<String, SQLiteCompiledSql> mCompiledQueries;
296
Vasu Norib729dcc2010-09-14 11:35:49 -0700297 /**
298 * absolute max value that can be set by {@link #setMaxSqlCacheSize(int)}
299 * size of each prepared-statement is between 1K - 6K, depending on the complexity of the
Vasu Noriccd95442010-05-28 17:04:16 -0700300 * SQL statement & schema.
Vasu Norie495d1f2010-01-06 16:34:19 -0800301 */
Vasu Nori90a367262010-04-12 12:49:09 -0700302 public static final int MAX_SQL_CACHE_SIZE = 100;
Vasu Nori5e89ae22010-09-15 14:23:29 -0700303 private boolean mCacheFullWarning;
Vasu Norib729dcc2010-09-14 11:35:49 -0700304
Vasu Norid606b4b2010-02-24 12:54:20 -0800305 /** Used to find out where this object was created in case it never got closed. */
Vasu Nori21343692010-06-03 16:01:39 -0700306 private final Throwable mStackTrace;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800307
Dmitri Plotnikov90142c92009-09-15 10:52:17 -0700308 // System property that enables logging of slow queries. Specify the threshold in ms.
309 private static final String LOG_SLOW_QUERIES_PROPERTY = "db.log.slow_query_threshold";
310 private final int mSlowQueryThreshold;
311
Vasu Nori6f37f832010-05-19 11:53:25 -0700312 /** stores the list of statement ids that need to be finalized by sqlite */
Vasu Nori21343692010-06-03 16:01:39 -0700313 private final ArrayList<Integer> mClosedStatementIds = new ArrayList<Integer>();
Vasu Nori6f37f832010-05-19 11:53:25 -0700314
Vasu Nori062fc7ce2010-03-31 16:13:05 -0700315 /** {@link DatabaseErrorHandler} to be used when SQLite returns any of the following errors
316 * Corruption
317 * */
Vasu Nori21343692010-06-03 16:01:39 -0700318 private final DatabaseErrorHandler mErrorHandler;
Vasu Nori062fc7ce2010-03-31 16:13:05 -0700319
Vasu Nori6c354da2010-04-26 23:33:39 -0700320 /** The Database connection pool {@link DatabaseConnectionPool}.
321 * Visibility is package-private for testing purposes. otherwise, private visibility is enough.
322 */
323 /* package */ volatile DatabaseConnectionPool mConnectionPool = null;
324
325 /** Each database connection handle in the pool is assigned a number 1..N, where N is the
326 * size of the connection pool.
327 * The main connection handle to which the pool is attached is assigned a value of 0.
328 */
329 /* package */ final short mConnectionNum;
330
Vasu Nori65a88832010-07-16 15:14:08 -0700331 /** on pooled database connections, this member points to the parent ( = main)
332 * database connection handle.
333 * package visibility only for testing purposes
334 */
335 /* package */ SQLiteDatabase mParentConnObj = null;
336
Vasu Noria98cb262010-06-22 13:16:35 -0700337 private static final String MEMORY_DB_PATH = ":memory:";
338
Vasu Nori24675612010-09-27 14:54:19 -0700339 /** set to true if the database has attached databases */
340 private volatile boolean mHasAttachedDbs = false;
341
Vasu Nori0732f792010-07-29 17:24:12 -0700342 /** stores reference to all databases opened in the current process. */
343 private static ArrayList<WeakReference<SQLiteDatabase>> mActiveDatabases =
344 new ArrayList<WeakReference<SQLiteDatabase>>();
345
Vasu Nori2827d6d2010-07-04 00:26:18 -0700346 synchronized void addSQLiteClosable(SQLiteClosable closable) {
347 // mPrograms is per instance of SQLiteDatabase and it doesn't actually touch the database
348 // itself. so, there is no need to lock().
349 mPrograms.put(closable, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700351
Vasu Nori2827d6d2010-07-04 00:26:18 -0700352 synchronized void removeSQLiteClosable(SQLiteClosable closable) {
353 mPrograms.remove(closable);
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700354 }
355
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356 @Override
357 protected void onAllReferencesReleased() {
358 if (isOpen()) {
Vasu Noriad239ab2010-06-14 16:58:47 -0700359 // close the database which will close all pending statements to be finalized also
360 close();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361 }
362 }
363
364 /**
365 * Attempts to release memory that SQLite holds but does not require to
366 * operate properly. Typically this memory will come from the page cache.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700367 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800368 * @return the number of bytes actually released
369 */
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700370 static public native int releaseMemory();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800371
372 /**
373 * Control whether or not the SQLiteDatabase is made thread-safe by using locks
374 * around critical sections. This is pretty expensive, so if you know that your
375 * DB will only be used by a single thread then you should set this to false.
376 * The default is true.
377 * @param lockingEnabled set to true to enable locks, false otherwise
378 */
379 public void setLockingEnabled(boolean lockingEnabled) {
380 mLockingEnabled = lockingEnabled;
381 }
382
383 /**
384 * If set then the SQLiteDatabase is made thread-safe by using locks
385 * around critical sections
386 */
387 private boolean mLockingEnabled = true;
388
389 /* package */ void onCorruption() {
Vasu Norif3cf8a42010-03-23 11:41:44 -0700390 EventLog.writeEvent(EVENT_DB_CORRUPT, mPath);
Vasu Noriccd95442010-05-28 17:04:16 -0700391 mErrorHandler.onCorruption(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392 }
393
394 /**
395 * Locks the database for exclusive access. The database lock must be held when
396 * touch the native sqlite3* object since it is single threaded and uses
397 * a polling lock contention algorithm. The lock is recursive, and may be acquired
398 * multiple times by the same thread. This is a no-op if mLockingEnabled is false.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700399 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 * @see #unlock()
401 */
Vasu Nori16057fa2011-03-18 11:40:37 -0700402 /* package */ void lock(String sql) {
403 lock(sql, false);
Vasu Nori6d970252010-10-05 10:48:49 -0700404 }
Vasu Nori16057fa2011-03-18 11:40:37 -0700405
406 /* pachage */ void lock() {
407 lock(null, false);
408 }
409
Vasu Norid4608a32011-02-03 16:24:06 -0800410 private static final long LOCK_WAIT_PERIOD = 30L;
Vasu Nori16057fa2011-03-18 11:40:37 -0700411 private void lock(String sql, boolean forced) {
Vasu Nori6d970252010-10-05 10:48:49 -0700412 // make sure this method is NOT being called from a 'synchronized' method
413 if (Thread.holdsLock(this)) {
Vasu Nori4b92aee2011-01-26 23:22:34 -0800414 Log.w(TAG, "don't lock() while in a synchronized method");
Vasu Nori6d970252010-10-05 10:48:49 -0700415 }
Vasu Nori7b04c412010-07-20 10:31:21 -0700416 verifyDbIsOpen();
Vasu Nori6d970252010-10-05 10:48:49 -0700417 if (!forced && !mLockingEnabled) return;
Vasu Norid4608a32011-02-03 16:24:06 -0800418 boolean done = false;
Vasu Nori16057fa2011-03-18 11:40:37 -0700419 long timeStart = SystemClock.uptimeMillis();
Vasu Norid4608a32011-02-03 16:24:06 -0800420 while (!done) {
421 try {
422 // wait for 30sec to acquire the lock
423 done = mLock.tryLock(LOCK_WAIT_PERIOD, TimeUnit.SECONDS);
424 if (!done) {
425 // lock not acquired in NSec. print a message and stacktrace saying the lock
426 // has not been available for 30sec.
427 Log.w(TAG, "database lock has not been available for " + LOCK_WAIT_PERIOD +
428 " sec. Current Owner of the lock is " + mLock.getOwnerDescription() +
Vasu Norie9714e62011-02-11 16:50:51 -0800429 ". Continuing to wait in thread: " + Thread.currentThread().getId());
Vasu Norid4608a32011-02-03 16:24:06 -0800430 }
431 } catch (InterruptedException e) {
432 // ignore the interruption
433 }
434 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800435 if (SQLiteDebug.DEBUG_LOCK_TIME_TRACKING) {
436 if (mLock.getHoldCount() == 1) {
437 // Use elapsed real-time since the CPU may sleep when waiting for IO
438 mLockAcquiredWallTime = SystemClock.elapsedRealtime();
439 mLockAcquiredThreadTime = Debug.threadCpuTimeNanos();
440 }
441 }
Vasu Nori16057fa2011-03-18 11:40:37 -0700442 if (sql != null) {
443 logTimeStat(sql, timeStart, GET_LOCK_LOG_PREFIX);
444 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445 }
Vasu Norid4608a32011-02-03 16:24:06 -0800446 private static class DatabaseReentrantLock extends ReentrantLock {
447 DatabaseReentrantLock(boolean fair) {
448 super(fair);
449 }
450 @Override
451 public Thread getOwner() {
452 return super.getOwner();
453 }
454 public String getOwnerDescription() {
455 Thread t = getOwner();
456 return (t== null) ? "none" : String.valueOf(t.getId());
457 }
458 }
459
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800460 /**
461 * Locks the database for exclusive access. The database lock must be held when
462 * touch the native sqlite3* object since it is single threaded and uses
463 * a polling lock contention algorithm. The lock is recursive, and may be acquired
464 * multiple times by the same thread.
465 *
466 * @see #unlockForced()
467 */
468 private void lockForced() {
Vasu Nori16057fa2011-03-18 11:40:37 -0700469 lock(null, true);
470 }
471
472 private void lockForced(String sql) {
473 lock(sql, 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 Nori16057fa2011-03-18 11:40:37 -0700641 lockForced(BEGIN_SQL);
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 }
Vasu Nori16057fa2011-03-18 11:40:37 -0700664 mTransStartTime = SystemClock.uptimeMillis();
Fred Quintanac4516a72009-09-03 12:14:06 -0700665 mTransactionListener = transactionListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800666 mTransactionIsSuccessful = true;
667 mInnerTransactionIsSuccessful = false;
Fred Quintanac4516a72009-09-03 12:14:06 -0700668 if (transactionListener != null) {
669 try {
670 transactionListener.onBegin();
671 } catch (RuntimeException e) {
672 execSQL("ROLLBACK;");
673 throw e;
674 }
675 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800676 ok = true;
677 } finally {
678 if (!ok) {
679 // beginTransaction is called before the try block so we must release the lock in
680 // the case of failure.
681 unlockForced();
682 }
683 }
684 }
685
686 /**
687 * End a transaction. See beginTransaction for notes about how to use this and when transactions
688 * are committed and rolled back.
689 */
690 public void endTransaction() {
Vasu Noriccd95442010-05-28 17:04:16 -0700691 verifyLockOwner();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800692 try {
693 if (mInnerTransactionIsSuccessful) {
694 mInnerTransactionIsSuccessful = false;
695 } else {
696 mTransactionIsSuccessful = false;
697 }
698 if (mLock.getHoldCount() != 1) {
699 return;
700 }
Fred Quintanac4516a72009-09-03 12:14:06 -0700701 RuntimeException savedException = null;
702 if (mTransactionListener != null) {
703 try {
704 if (mTransactionIsSuccessful) {
705 mTransactionListener.onCommit();
706 } else {
707 mTransactionListener.onRollback();
708 }
709 } catch (RuntimeException e) {
710 savedException = e;
711 mTransactionIsSuccessful = false;
712 }
713 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800714 if (mTransactionIsSuccessful) {
Brad Fitzpatrickb28c7972010-02-12 12:49:41 -0800715 execSQL(COMMIT_SQL);
Vasu Nori6c354da2010-04-26 23:33:39 -0700716 // if write-ahead logging is used, we have to take care of checkpoint.
717 // TODO: should applications be given the flexibility of choosing when to
718 // trigger checkpoint?
719 // for now, do checkpoint after every COMMIT because that is the fastest
720 // way to guarantee that readers will see latest data.
721 // but this is the slowest way to run sqlite with in write-ahead logging mode.
722 if (this.mConnectionPool != null) {
723 execSQL("PRAGMA wal_checkpoint;");
724 if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
725 Log.i(TAG, "PRAGMA wal_Checkpoint done");
726 }
727 }
Vasu Nori16057fa2011-03-18 11:40:37 -0700728 // log the transaction time to the Eventlog.
729 logTimeStat(getLastSqlStatement(), mTransStartTime, COMMIT_SQL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800730 } else {
731 try {
732 execSQL("ROLLBACK;");
Fred Quintanac4516a72009-09-03 12:14:06 -0700733 if (savedException != null) {
734 throw savedException;
735 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800736 } catch (SQLException e) {
Joe Onorato43a17652011-04-06 19:22:23 -0700737 if (false) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800738 Log.d(TAG, "exception during rollback, maybe the DB previously "
739 + "performed an auto-rollback");
740 }
741 }
742 }
743 } finally {
Fred Quintanac4516a72009-09-03 12:14:06 -0700744 mTransactionListener = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800745 unlockForced();
Joe Onorato43a17652011-04-06 19:22:23 -0700746 if (false) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800747 Log.v(TAG, "unlocked " + Thread.currentThread()
748 + ", holdCount is " + mLock.getHoldCount());
749 }
750 }
751 }
752
753 /**
754 * Marks the current transaction as successful. Do not do any more database work between
755 * calling this and calling endTransaction. Do as little non-database work as possible in that
756 * situation too. If any errors are encountered between this and endTransaction the transaction
757 * will still be committed.
758 *
759 * @throws IllegalStateException if the current thread is not in a transaction or the
760 * transaction is already marked as successful.
761 */
762 public void setTransactionSuccessful() {
Vasu Noriccd95442010-05-28 17:04:16 -0700763 verifyDbIsOpen();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800764 if (!mLock.isHeldByCurrentThread()) {
765 throw new IllegalStateException("no transaction pending");
766 }
767 if (mInnerTransactionIsSuccessful) {
768 throw new IllegalStateException(
769 "setTransactionSuccessful may only be called once per call to beginTransaction");
770 }
771 mInnerTransactionIsSuccessful = true;
772 }
773
774 /**
775 * return true if there is a transaction pending
776 */
777 public boolean inTransaction() {
Vasu Norice38b982010-07-22 13:57:13 -0700778 return mLock.getHoldCount() > 0 || mTransactionUsingExecSql;
779 }
780
781 /* package */ synchronized void setTransactionUsingExecSqlFlag() {
782 if (Log.isLoggable(TAG, Log.DEBUG)) {
783 Log.i(TAG, "found execSQL('begin transaction')");
784 }
785 mTransactionUsingExecSql = true;
786 }
787
788 /* package */ synchronized void resetTransactionUsingExecSqlFlag() {
789 if (Log.isLoggable(TAG, Log.DEBUG)) {
790 if (mTransactionUsingExecSql) {
791 Log.i(TAG, "found execSQL('commit or end or rollback')");
792 }
793 }
794 mTransactionUsingExecSql = false;
795 }
796
797 /**
798 * Returns true if the caller is considered part of the current transaction, if any.
799 * <p>
800 * Caller is part of the current transaction if either of the following is true
801 * <ol>
802 * <li>If transaction is started by calling beginTransaction() methods AND if the caller is
803 * in the same thread as the thread that started the transaction.
804 * </li>
805 * <li>If the transaction is started by calling {@link #execSQL(String)} like this:
806 * execSQL("BEGIN transaction"). In this case, every thread in the process is considered
807 * part of the current transaction.</li>
808 * </ol>
809 *
810 * @return true if the caller is considered part of the current transaction, if any.
811 */
812 /* package */ synchronized boolean amIInTransaction() {
813 // always do this test on the main database connection - NOT on pooled database connection
814 // since transactions always occur on the main database connections only.
815 SQLiteDatabase db = (isPooledConnection()) ? mParentConnObj : this;
816 boolean b = (!db.inTransaction()) ? false :
817 db.mTransactionUsingExecSql || db.mLock.isHeldByCurrentThread();
818 if (Log.isLoggable(TAG, Log.DEBUG)) {
819 Log.i(TAG, "amIinTransaction: " + b);
820 }
821 return b;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800822 }
823
824 /**
825 * Checks if the database lock is held by this thread.
826 *
827 * @return true, if this thread is holding the database lock.
828 */
829 public boolean isDbLockedByCurrentThread() {
830 return mLock.isHeldByCurrentThread();
831 }
832
833 /**
834 * Checks if the database is locked by another thread. This is
835 * just an estimate, since this status can change at any time,
836 * including after the call is made but before the result has
837 * been acted upon.
838 *
839 * @return true, if the database is locked by another thread
840 */
841 public boolean isDbLockedByOtherThreads() {
842 return !mLock.isHeldByCurrentThread() && mLock.isLocked();
843 }
844
845 /**
846 * Temporarily end the transaction to let other threads run. The transaction is assumed to be
847 * successful so far. Do not call setTransactionSuccessful before calling this. When this
848 * returns a new transaction will have been created but not marked as successful.
849 * @return true if the transaction was yielded
850 * @deprecated if the db is locked more than once (becuase of nested transactions) then the lock
851 * will not be yielded. Use yieldIfContendedSafely instead.
852 */
Dianne Hackborn4a51c202009-08-21 15:14:02 -0700853 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800854 public boolean yieldIfContended() {
Fred Quintana5c7aede2009-08-27 21:41:27 -0700855 return yieldIfContendedHelper(false /* do not check yielding */,
856 -1 /* sleepAfterYieldDelay */);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800857 }
858
859 /**
860 * Temporarily end the transaction to let other threads run. The transaction is assumed to be
861 * successful so far. Do not call setTransactionSuccessful before calling this. When this
862 * returns a new transaction will have been created but not marked as successful. This assumes
863 * that there are no nested transactions (beginTransaction has only been called once) and will
Fred Quintana5c7aede2009-08-27 21:41:27 -0700864 * throw an exception if that is not the case.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800865 * @return true if the transaction was yielded
866 */
867 public boolean yieldIfContendedSafely() {
Fred Quintana5c7aede2009-08-27 21:41:27 -0700868 return yieldIfContendedHelper(true /* check yielding */, -1 /* sleepAfterYieldDelay*/);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800869 }
870
Fred Quintana5c7aede2009-08-27 21:41:27 -0700871 /**
872 * Temporarily end the transaction to let other threads run. The transaction is assumed to be
873 * successful so far. Do not call setTransactionSuccessful before calling this. When this
874 * returns a new transaction will have been created but not marked as successful. This assumes
875 * that there are no nested transactions (beginTransaction has only been called once) and will
876 * throw an exception if that is not the case.
877 * @param sleepAfterYieldDelay if > 0, sleep this long before starting a new transaction if
878 * the lock was actually yielded. This will allow other background threads to make some
879 * more progress than they would if we started the transaction immediately.
880 * @return true if the transaction was yielded
881 */
882 public boolean yieldIfContendedSafely(long sleepAfterYieldDelay) {
883 return yieldIfContendedHelper(true /* check yielding */, sleepAfterYieldDelay);
884 }
885
886 private boolean yieldIfContendedHelper(boolean checkFullyYielded, long sleepAfterYieldDelay) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800887 if (mLock.getQueueLength() == 0) {
888 // Reset the lock acquire time since we know that the thread was willing to yield
889 // the lock at this time.
890 mLockAcquiredWallTime = SystemClock.elapsedRealtime();
891 mLockAcquiredThreadTime = Debug.threadCpuTimeNanos();
892 return false;
893 }
894 setTransactionSuccessful();
Fred Quintanac4516a72009-09-03 12:14:06 -0700895 SQLiteTransactionListener transactionListener = mTransactionListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800896 endTransaction();
897 if (checkFullyYielded) {
898 if (this.isDbLockedByCurrentThread()) {
899 throw new IllegalStateException(
900 "Db locked more than once. yielfIfContended cannot yield");
901 }
902 }
Fred Quintana5c7aede2009-08-27 21:41:27 -0700903 if (sleepAfterYieldDelay > 0) {
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -0700904 // Sleep for up to sleepAfterYieldDelay milliseconds, waking up periodically to
905 // check if anyone is using the database. If the database is not contended,
906 // retake the lock and return.
907 long remainingDelay = sleepAfterYieldDelay;
908 while (remainingDelay > 0) {
909 try {
910 Thread.sleep(remainingDelay < SLEEP_AFTER_YIELD_QUANTUM ?
911 remainingDelay : SLEEP_AFTER_YIELD_QUANTUM);
912 } catch (InterruptedException e) {
913 Thread.interrupted();
914 }
915 remainingDelay -= SLEEP_AFTER_YIELD_QUANTUM;
916 if (mLock.getQueueLength() == 0) {
917 break;
918 }
Fred Quintana5c7aede2009-08-27 21:41:27 -0700919 }
920 }
Fred Quintanac4516a72009-09-03 12:14:06 -0700921 beginTransactionWithListener(transactionListener);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800922 return true;
923 }
924
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800925 /**
Vasu Nori95675132010-07-21 16:24:40 -0700926 * @deprecated This method no longer serves any useful purpose and has been deprecated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800927 */
Vasu Nori95675132010-07-21 16:24:40 -0700928 @Deprecated
929 public Map<String, String> getSyncedTables() {
930 return new HashMap<String, String>(0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800931 }
932
933 /**
934 * Used to allow returning sub-classes of {@link Cursor} when calling query.
935 */
936 public interface CursorFactory {
937 /**
938 * See
Vasu Noribfe1dc22010-08-25 16:29:02 -0700939 * {@link SQLiteCursor#SQLiteCursor(SQLiteCursorDriver, String, SQLiteQuery)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800940 */
941 public Cursor newCursor(SQLiteDatabase db,
942 SQLiteCursorDriver masterQuery, String editTable,
943 SQLiteQuery query);
944 }
945
946 /**
947 * Open the database according to the flags {@link #OPEN_READWRITE}
948 * {@link #OPEN_READONLY} {@link #CREATE_IF_NECESSARY} and/or {@link #NO_LOCALIZED_COLLATORS}.
949 *
950 * <p>Sets the locale of the database to the the system's current locale.
951 * Call {@link #setLocale} if you would like something else.</p>
952 *
953 * @param path to database file to open and/or create
954 * @param factory an optional factory class that is called to instantiate a
955 * cursor when query is called, or null for default
956 * @param flags to control database access mode
957 * @return the newly opened database
958 * @throws SQLiteException if the database cannot be opened
959 */
960 public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags) {
Vasu Nori062fc7ce2010-03-31 16:13:05 -0700961 return openDatabase(path, factory, flags, new DefaultDatabaseErrorHandler());
962 }
963
964 /**
Vasu Nori74f170f2010-06-01 18:06:18 -0700965 * Open the database according to the flags {@link #OPEN_READWRITE}
966 * {@link #OPEN_READONLY} {@link #CREATE_IF_NECESSARY} and/or {@link #NO_LOCALIZED_COLLATORS}.
967 *
968 * <p>Sets the locale of the database to the the system's current locale.
969 * Call {@link #setLocale} if you would like something else.</p>
970 *
971 * <p>Accepts input param: a concrete instance of {@link DatabaseErrorHandler} to be
972 * used to handle corruption when sqlite reports database corruption.</p>
973 *
974 * @param path to database file to open and/or create
975 * @param factory an optional factory class that is called to instantiate a
976 * cursor when query is called, or null for default
977 * @param flags to control database access mode
978 * @param errorHandler the {@link DatabaseErrorHandler} obj to be used to handle corruption
979 * when sqlite reports database corruption
980 * @return the newly opened database
981 * @throws SQLiteException if the database cannot be opened
Vasu Nori062fc7ce2010-03-31 16:13:05 -0700982 */
983 public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags,
984 DatabaseErrorHandler errorHandler) {
Vasu Nori6c354da2010-04-26 23:33:39 -0700985 SQLiteDatabase sqliteDatabase = openDatabase(path, factory, flags, errorHandler,
986 (short) 0 /* the main connection handle */);
Vasu Noria8c24902010-06-01 11:30:27 -0700987
988 // set sqlite pagesize to mBlockSize
989 if (sBlockSize == 0) {
990 // TODO: "/data" should be a static final String constant somewhere. it is hardcoded
991 // in several places right now.
992 sBlockSize = new StatFs("/data").getBlockSize();
993 }
994 sqliteDatabase.setPageSize(sBlockSize);
Vasu Noria22d8842011-01-06 08:30:29 -0800995 sqliteDatabase.setJournalMode(path, "TRUNCATE");
Vasu Norif9e2bd02010-06-04 16:49:51 -0700996
Vasu Noriccd95442010-05-28 17:04:16 -0700997 // add this database to the list of databases opened in this process
Vasu Nori0732f792010-07-29 17:24:12 -0700998 synchronized(mActiveDatabases) {
999 mActiveDatabases.add(new WeakReference<SQLiteDatabase>(sqliteDatabase));
1000 }
Vasu Noric3849202010-03-09 10:47:25 -08001001 return sqliteDatabase;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001002 }
1003
Vasu Nori6c354da2010-04-26 23:33:39 -07001004 private static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags,
1005 DatabaseErrorHandler errorHandler, short connectionNum) {
1006 SQLiteDatabase db = new SQLiteDatabase(path, factory, flags, errorHandler, connectionNum);
Vasu Nori062fc7ce2010-03-31 16:13:05 -07001007 try {
Vasu Norice38b982010-07-22 13:57:13 -07001008 if (Log.isLoggable(TAG, Log.DEBUG)) {
1009 Log.i(TAG, "opening the db : " + path);
1010 }
Vasu Nori6c354da2010-04-26 23:33:39 -07001011 // Open the database.
1012 db.dbopen(path, flags);
1013 db.setLocale(Locale.getDefault());
1014 if (SQLiteDebug.DEBUG_SQL_STATEMENTS) {
1015 db.enableSqlTracing(path, connectionNum);
1016 }
1017 if (SQLiteDebug.DEBUG_SQL_TIME) {
1018 db.enableSqlProfiling(path, connectionNum);
1019 }
1020 return db;
1021 } catch (SQLiteDatabaseCorruptException e) {
Vasu Nori6a904bc2011-01-05 18:35:40 -08001022 db.mErrorHandler.onCorruption(db);
1023 return SQLiteDatabase.openDatabase(path, factory, flags, errorHandler);
Vasu Nori6c354da2010-04-26 23:33:39 -07001024 } catch (SQLiteException e) {
1025 Log.e(TAG, "Failed to open the database. closing it.", e);
1026 db.close();
Vasu Nori062fc7ce2010-03-31 16:13:05 -07001027 throw e;
1028 }
1029 }
1030
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001031 /**
1032 * Equivalent to openDatabase(file.getPath(), factory, CREATE_IF_NECESSARY).
1033 */
1034 public static SQLiteDatabase openOrCreateDatabase(File file, CursorFactory factory) {
1035 return openOrCreateDatabase(file.getPath(), factory);
1036 }
1037
1038 /**
1039 * Equivalent to openDatabase(path, factory, CREATE_IF_NECESSARY).
1040 */
1041 public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory) {
1042 return openDatabase(path, factory, CREATE_IF_NECESSARY);
1043 }
1044
1045 /**
Vasu Nori6c354da2010-04-26 23:33:39 -07001046 * Equivalent to openDatabase(path, factory, CREATE_IF_NECESSARY, errorHandler).
Vasu Nori062fc7ce2010-03-31 16:13:05 -07001047 */
1048 public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory,
1049 DatabaseErrorHandler errorHandler) {
1050 return openDatabase(path, factory, CREATE_IF_NECESSARY, errorHandler);
1051 }
1052
Vasu Noria98cb262010-06-22 13:16:35 -07001053 private void setJournalMode(final String dbPath, final String mode) {
1054 // journal mode can be set only for non-memory databases
Vasu Nori8fcda302010-11-08 13:46:40 -08001055 // AND can't be set for readonly databases
1056 if (dbPath.equalsIgnoreCase(MEMORY_DB_PATH) || isReadOnly()) {
1057 return;
1058 }
1059 String s = DatabaseUtils.stringForQuery(this, "PRAGMA journal_mode=" + mode, null);
1060 if (!s.equalsIgnoreCase(mode)) {
1061 Log.e(TAG, "setting journal_mode to " + mode + " failed for db: " + dbPath +
1062 " (on pragma set journal_mode, sqlite returned:" + s);
Vasu Noria98cb262010-06-22 13:16:35 -07001063 }
1064 }
1065
Vasu Nori062fc7ce2010-03-31 16:13:05 -07001066 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001067 * Create a memory backed SQLite database. Its contents will be destroyed
1068 * when the database is closed.
1069 *
1070 * <p>Sets the locale of the database to the the system's current locale.
1071 * Call {@link #setLocale} if you would like something else.</p>
1072 *
1073 * @param factory an optional factory class that is called to instantiate a
1074 * cursor when query is called
1075 * @return a SQLiteDatabase object, or null if the database can't be created
1076 */
1077 public static SQLiteDatabase create(CursorFactory factory) {
1078 // This is a magic string with special meaning for SQLite.
Vasu Noria98cb262010-06-22 13:16:35 -07001079 return openDatabase(MEMORY_DB_PATH, factory, CREATE_IF_NECESSARY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001080 }
1081
1082 /**
1083 * Close the database.
1084 */
1085 public void close() {
Vasu Nori587423a2010-09-27 18:18:34 -07001086 if (!isOpen()) {
1087 return;
1088 }
Vasu Norice38b982010-07-22 13:57:13 -07001089 if (Log.isLoggable(TAG, Log.DEBUG)) {
Vasu Nori75010102010-07-01 16:23:06 -07001090 Log.i(TAG, "closing db: " + mPath + " (connection # " + mConnectionNum);
1091 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001092 lock();
1093 try {
Vasu Noriffe06122010-09-27 12:32:57 -07001094 // some other thread could have closed this database while I was waiting for lock.
1095 // check the database state
1096 if (!isOpen()) {
1097 return;
1098 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001099 closeClosable();
Vasu Norifea6f6d2010-05-21 15:36:06 -07001100 // finalize ALL statements queued up so far
1101 closePendingStatements();
Mike Lockwood9d9c1be2010-07-13 19:27:52 -04001102 releaseCustomFunctions();
Vasu Norif6373e92010-03-16 10:21:00 -07001103 // close this database instance - regardless of its reference count value
Vasu Nori422dad02010-09-03 16:03:08 -07001104 closeDatabase();
Vasu Nori6c354da2010-04-26 23:33:39 -07001105 if (mConnectionPool != null) {
Vasu Norice38b982010-07-22 13:57:13 -07001106 if (Log.isLoggable(TAG, Log.DEBUG)) {
1107 assert mConnectionPool != null;
1108 Log.i(TAG, mConnectionPool.toString());
1109 }
Vasu Nori6c354da2010-04-26 23:33:39 -07001110 mConnectionPool.close();
1111 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001112 } finally {
Brian Muramatsu46a88512010-11-12 13:53:57 -08001113 unlock();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001114 }
1115 }
1116
1117 private void closeClosable() {
Vasu Noriccd95442010-05-28 17:04:16 -07001118 /* deallocate all compiled SQL statement objects from mCompiledQueries cache.
Vasu Norie495d1f2010-01-06 16:34:19 -08001119 * this should be done before de-referencing all {@link SQLiteClosable} objects
1120 * from this database object because calling
1121 * {@link SQLiteClosable#onAllReferencesReleasedFromContainer()} could cause the database
1122 * to be closed. sqlite doesn't let a database close if there are
1123 * any unfinalized statements - such as the compiled-sql objects in mCompiledQueries.
1124 */
Vasu Norib729dcc2010-09-14 11:35:49 -07001125 deallocCachedSqlStatements();
Vasu Norie495d1f2010-01-06 16:34:19 -08001126
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001127 Iterator<Map.Entry<SQLiteClosable, Object>> iter = mPrograms.entrySet().iterator();
1128 while (iter.hasNext()) {
1129 Map.Entry<SQLiteClosable, Object> entry = iter.next();
1130 SQLiteClosable program = entry.getKey();
1131 if (program != null) {
1132 program.onAllReferencesReleasedFromContainer();
1133 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001134 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001135 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001136
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001137 /**
Vasu Nori422dad02010-09-03 16:03:08 -07001138 * package level access for testing purposes
1139 */
1140 /* package */ void closeDatabase() throws SQLiteException {
1141 try {
1142 dbclose();
1143 } catch (SQLiteUnfinalizedObjectsException e) {
1144 String msg = e.getMessage();
1145 String[] tokens = msg.split(",", 2);
1146 int stmtId = Integer.parseInt(tokens[0]);
1147 // get extra info about this statement, if it is still to be released by closeClosable()
1148 Iterator<Map.Entry<SQLiteClosable, Object>> iter = mPrograms.entrySet().iterator();
1149 boolean found = false;
1150 while (iter.hasNext()) {
1151 Map.Entry<SQLiteClosable, Object> entry = iter.next();
1152 SQLiteClosable program = entry.getKey();
1153 if (program != null && program instanceof SQLiteProgram) {
Vasu Norib4389022010-11-29 14:10:46 -08001154 SQLiteCompiledSql compiledSql = ((SQLiteProgram)program).mCompiledSql;
1155 if (compiledSql.nStatement == stmtId) {
1156 msg = compiledSql.toString();
1157 found = true;
1158 }
Vasu Nori422dad02010-09-03 16:03:08 -07001159 }
1160 }
1161 if (!found) {
1162 // the statement is already released by closeClosable(). is it waiting to be
1163 // finalized?
1164 if (mClosedStatementIds.contains(stmtId)) {
1165 Log.w(TAG, "this shouldn't happen. finalizing the statement now: ");
1166 closePendingStatements();
1167 // try to close the database again
1168 closeDatabase();
1169 }
1170 } else {
1171 // the statement is not yet closed. most probably programming error in the app.
Vasu Norib4389022010-11-29 14:10:46 -08001172 throw new SQLiteUnfinalizedObjectsException(
1173 "close() on database: " + getPath() +
1174 " failed due to un-close()d SQL statements: " + msg);
Vasu Nori422dad02010-09-03 16:03:08 -07001175 }
1176 }
1177 }
1178
1179 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001180 * Native call to close the database.
1181 */
1182 private native void dbclose();
1183
1184 /**
Mike Lockwood9d9c1be2010-07-13 19:27:52 -04001185 * A callback interface for a custom sqlite3 function.
1186 * This can be used to create a function that can be called from
1187 * sqlite3 database triggers.
1188 * @hide
1189 */
1190 public interface CustomFunction {
1191 public void callback(String[] args);
1192 }
1193
1194 /**
1195 * Registers a CustomFunction callback as a function that can be called from
1196 * sqlite3 database triggers.
1197 * @param name the name of the sqlite3 function
1198 * @param numArgs the number of arguments for the function
1199 * @param function callback to call when the function is executed
1200 * @hide
1201 */
1202 public void addCustomFunction(String name, int numArgs, CustomFunction function) {
1203 verifyDbIsOpen();
1204 synchronized (mCustomFunctions) {
1205 int ref = native_addCustomFunction(name, numArgs, function);
1206 if (ref != 0) {
1207 // save a reference to the function for cleanup later
1208 mCustomFunctions.add(new Integer(ref));
1209 } else {
1210 throw new SQLiteException("failed to add custom function " + name);
1211 }
1212 }
1213 }
1214
1215 private void releaseCustomFunctions() {
1216 synchronized (mCustomFunctions) {
1217 for (int i = 0; i < mCustomFunctions.size(); i++) {
1218 Integer function = mCustomFunctions.get(i);
1219 native_releaseCustomFunction(function.intValue());
1220 }
1221 mCustomFunctions.clear();
1222 }
1223 }
1224
1225 // list of CustomFunction references so we can clean up when the database closes
1226 private final ArrayList<Integer> mCustomFunctions =
1227 new ArrayList<Integer>();
1228
1229 private native int native_addCustomFunction(String name, int numArgs, CustomFunction function);
1230 private native void native_releaseCustomFunction(int function);
1231
1232 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001233 * Gets the database version.
1234 *
1235 * @return the database version
1236 */
1237 public int getVersion() {
Vasu Noriccd95442010-05-28 17:04:16 -07001238 return ((Long) DatabaseUtils.longForQuery(this, "PRAGMA user_version;", null)).intValue();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001239 }
1240
1241 /**
1242 * Sets the database version.
1243 *
1244 * @param version the new database version
1245 */
1246 public void setVersion(int version) {
1247 execSQL("PRAGMA user_version = " + version);
1248 }
1249
1250 /**
1251 * Returns the maximum size the database may grow to.
1252 *
1253 * @return the new maximum database size
1254 */
1255 public long getMaximumSize() {
Vasu Noriccd95442010-05-28 17:04:16 -07001256 long pageCount = DatabaseUtils.longForQuery(this, "PRAGMA max_page_count;", null);
1257 return pageCount * getPageSize();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001258 }
1259
1260 /**
1261 * Sets the maximum size the database will grow to. The maximum size cannot
1262 * be set below the current size.
1263 *
1264 * @param numBytes the maximum database size, in bytes
1265 * @return the new maximum database size
1266 */
1267 public long setMaximumSize(long numBytes) {
Vasu Noriccd95442010-05-28 17:04:16 -07001268 long pageSize = getPageSize();
1269 long numPages = numBytes / pageSize;
1270 // If numBytes isn't a multiple of pageSize, bump up a page
1271 if ((numBytes % pageSize) != 0) {
1272 numPages++;
Vasu Norif3cf8a42010-03-23 11:41:44 -07001273 }
Vasu Noriccd95442010-05-28 17:04:16 -07001274 long newPageCount = DatabaseUtils.longForQuery(this, "PRAGMA max_page_count = " + numPages,
1275 null);
1276 return newPageCount * pageSize;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001277 }
1278
1279 /**
1280 * Returns the current database page size, in bytes.
1281 *
1282 * @return the database page size, in bytes
1283 */
1284 public long getPageSize() {
Vasu Noriccd95442010-05-28 17:04:16 -07001285 return DatabaseUtils.longForQuery(this, "PRAGMA page_size;", null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001286 }
1287
1288 /**
1289 * Sets the database page size. The page size must be a power of two. This
1290 * method does not work if any data has been written to the database file,
1291 * and must be called right after the database has been created.
1292 *
1293 * @param numBytes the database page size, in bytes
1294 */
1295 public void setPageSize(long numBytes) {
1296 execSQL("PRAGMA page_size = " + numBytes);
1297 }
1298
1299 /**
1300 * Mark this table as syncable. When an update occurs in this table the
1301 * _sync_dirty field will be set to ensure proper syncing operation.
1302 *
1303 * @param table the table to mark as syncable
1304 * @param deletedTable The deleted table that corresponds to the
1305 * syncable table
Vasu Nori95675132010-07-21 16:24:40 -07001306 * @deprecated This method no longer serves any useful purpose and has been deprecated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001307 */
Vasu Nori95675132010-07-21 16:24:40 -07001308 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001309 public void markTableSyncable(String table, String deletedTable) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001310 }
1311
1312 /**
1313 * Mark this table as syncable, with the _sync_dirty residing in another
1314 * table. When an update occurs in this table the _sync_dirty field of the
1315 * row in updateTable with the _id in foreignKey will be set to
1316 * ensure proper syncing operation.
1317 *
1318 * @param table an update on this table will trigger a sync time removal
1319 * @param foreignKey this is the column in table whose value is an _id in
1320 * updateTable
1321 * @param updateTable this is the table that will have its _sync_dirty
Vasu Nori95675132010-07-21 16:24:40 -07001322 * @deprecated This method no longer serves any useful purpose and has been deprecated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001323 */
Vasu Nori95675132010-07-21 16:24:40 -07001324 @Deprecated
1325 public void markTableSyncable(String table, String foreignKey, String updateTable) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001326 }
1327
1328 /**
1329 * Finds the name of the first table, which is editable.
1330 *
1331 * @param tables a list of tables
1332 * @return the first table listed
1333 */
1334 public static String findEditTable(String tables) {
1335 if (!TextUtils.isEmpty(tables)) {
1336 // find the first word terminated by either a space or a comma
1337 int spacepos = tables.indexOf(' ');
1338 int commapos = tables.indexOf(',');
1339
1340 if (spacepos > 0 && (spacepos < commapos || commapos < 0)) {
1341 return tables.substring(0, spacepos);
1342 } else if (commapos > 0 && (commapos < spacepos || spacepos < 0) ) {
1343 return tables.substring(0, commapos);
1344 }
1345 return tables;
1346 } else {
1347 throw new IllegalStateException("Invalid tables");
1348 }
1349 }
1350
1351 /**
1352 * Compiles an SQL statement into a reusable pre-compiled statement object.
1353 * The parameters are identical to {@link #execSQL(String)}. You may put ?s in the
1354 * statement and fill in those values with {@link SQLiteProgram#bindString}
1355 * and {@link SQLiteProgram#bindLong} each time you want to run the
1356 * statement. Statements may not return result sets larger than 1x1.
Vasu Nori2827d6d2010-07-04 00:26:18 -07001357 *<p>
1358 * No two threads should be using the same {@link SQLiteStatement} at the same time.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001359 *
1360 * @param sql The raw SQL statement, may contain ? for unknown values to be
1361 * bound later.
Jeff Hamiltonf3ca9a52010-05-12 15:04:33 -07001362 * @return A pre-compiled {@link SQLiteStatement} object. Note that
1363 * {@link SQLiteStatement}s are not synchronized, see the documentation for more details.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001364 */
1365 public SQLiteStatement compileStatement(String sql) throws SQLException {
Vasu Noriccd95442010-05-28 17:04:16 -07001366 verifyDbIsOpen();
Vasu Nori0732f792010-07-29 17:24:12 -07001367 return new SQLiteStatement(this, sql, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001368 }
1369
1370 /**
1371 * Query the given URL, returning a {@link Cursor} over the result set.
1372 *
1373 * @param distinct true if you want each row to be unique, false otherwise.
1374 * @param table The table name to compile the query against.
1375 * @param columns A list of which columns to return. Passing null will
1376 * return all columns, which is discouraged to prevent reading
1377 * data from storage that isn't going to be used.
1378 * @param selection A filter declaring which rows to return, formatted as an
1379 * SQL WHERE clause (excluding the WHERE itself). Passing null
1380 * will return all rows for the given table.
1381 * @param selectionArgs You may include ?s in selection, which will be
1382 * replaced by the values from selectionArgs, in order that they
1383 * appear in the selection. The values will be bound as Strings.
1384 * @param groupBy A filter declaring how to group rows, formatted as an SQL
1385 * GROUP BY clause (excluding the GROUP BY itself). Passing null
1386 * will cause the rows to not be grouped.
1387 * @param having A filter declare which row groups to include in the cursor,
1388 * if row grouping is being used, formatted as an SQL HAVING
1389 * clause (excluding the HAVING itself). Passing null will cause
1390 * all row groups to be included, and is required when row
1391 * grouping is not being used.
1392 * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
1393 * (excluding the ORDER BY itself). Passing null will use the
1394 * default sort order, which may be unordered.
1395 * @param limit Limits the number of rows returned by the query,
1396 * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
Jeff Hamiltonf3ca9a52010-05-12 15:04:33 -07001397 * @return A {@link Cursor} object, which is positioned before the first entry. Note that
1398 * {@link Cursor}s are not synchronized, see the documentation for more details.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001399 * @see Cursor
1400 */
1401 public Cursor query(boolean distinct, String table, String[] columns,
1402 String selection, String[] selectionArgs, String groupBy,
1403 String having, String orderBy, String limit) {
1404 return queryWithFactory(null, distinct, table, columns, selection, selectionArgs,
1405 groupBy, having, orderBy, limit);
1406 }
1407
1408 /**
1409 * Query the given URL, returning a {@link Cursor} over the result set.
1410 *
1411 * @param cursorFactory the cursor factory to use, or null for the default factory
1412 * @param distinct true if you want each row to be unique, false otherwise.
1413 * @param table The table name to compile the query against.
1414 * @param columns A list of which columns to return. Passing null will
1415 * return all columns, which is discouraged to prevent reading
1416 * data from storage that isn't going to be used.
1417 * @param selection A filter declaring which rows to return, formatted as an
1418 * SQL WHERE clause (excluding the WHERE itself). Passing null
1419 * will return all rows for the given table.
1420 * @param selectionArgs You may include ?s in selection, which will be
1421 * replaced by the values from selectionArgs, in order that they
1422 * appear in the selection. The values will be bound as Strings.
1423 * @param groupBy A filter declaring how to group rows, formatted as an SQL
1424 * GROUP BY clause (excluding the GROUP BY itself). Passing null
1425 * will cause the rows to not be grouped.
1426 * @param having A filter declare which row groups to include in the cursor,
1427 * if row grouping is being used, formatted as an SQL HAVING
1428 * clause (excluding the HAVING itself). Passing null will cause
1429 * all row groups to be included, and is required when row
1430 * grouping is not being used.
1431 * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
1432 * (excluding the ORDER BY itself). Passing null will use the
1433 * default sort order, which may be unordered.
1434 * @param limit Limits the number of rows returned by the query,
1435 * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
Jeff Hamiltonf3ca9a52010-05-12 15:04:33 -07001436 * @return A {@link Cursor} object, which is positioned before the first entry. Note that
1437 * {@link Cursor}s are not synchronized, see the documentation for more details.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001438 * @see Cursor
1439 */
1440 public Cursor queryWithFactory(CursorFactory cursorFactory,
1441 boolean distinct, String table, String[] columns,
1442 String selection, String[] selectionArgs, String groupBy,
1443 String having, String orderBy, String limit) {
Vasu Noriccd95442010-05-28 17:04:16 -07001444 verifyDbIsOpen();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001445 String sql = SQLiteQueryBuilder.buildQueryString(
1446 distinct, table, columns, selection, groupBy, having, orderBy, limit);
1447
1448 return rawQueryWithFactory(
1449 cursorFactory, sql, selectionArgs, findEditTable(table));
1450 }
1451
1452 /**
1453 * Query the given table, returning a {@link Cursor} over the result set.
1454 *
1455 * @param table The table name to compile the query against.
1456 * @param columns A list of which columns to return. Passing null will
1457 * return all columns, which is discouraged to prevent reading
1458 * data from storage that isn't going to be used.
1459 * @param selection A filter declaring which rows to return, formatted as an
1460 * SQL WHERE clause (excluding the WHERE itself). Passing null
1461 * will return all rows for the given table.
1462 * @param selectionArgs You may include ?s in selection, which will be
1463 * replaced by the values from selectionArgs, in order that they
1464 * appear in the selection. The values will be bound as Strings.
1465 * @param groupBy A filter declaring how to group rows, formatted as an SQL
1466 * GROUP BY clause (excluding the GROUP BY itself). Passing null
1467 * will cause the rows to not be grouped.
1468 * @param having A filter declare which row groups to include in the cursor,
1469 * if row grouping is being used, formatted as an SQL HAVING
1470 * clause (excluding the HAVING itself). Passing null will cause
1471 * all row groups to be included, and is required when row
1472 * grouping is not being used.
1473 * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
1474 * (excluding the ORDER BY itself). Passing null will use the
1475 * default sort order, which may be unordered.
Jeff Hamiltonf3ca9a52010-05-12 15:04:33 -07001476 * @return A {@link Cursor} object, which is positioned before the first entry. Note that
1477 * {@link Cursor}s are not synchronized, see the documentation for more details.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001478 * @see Cursor
1479 */
1480 public Cursor query(String table, String[] columns, String selection,
1481 String[] selectionArgs, String groupBy, String having,
1482 String orderBy) {
1483
1484 return query(false, table, columns, selection, selectionArgs, groupBy,
1485 having, orderBy, null /* limit */);
1486 }
1487
1488 /**
1489 * Query the given table, returning a {@link Cursor} over the result set.
1490 *
1491 * @param table The table name to compile the query against.
1492 * @param columns A list of which columns to return. Passing null will
1493 * return all columns, which is discouraged to prevent reading
1494 * data from storage that isn't going to be used.
1495 * @param selection A filter declaring which rows to return, formatted as an
1496 * SQL WHERE clause (excluding the WHERE itself). Passing null
1497 * will return all rows for the given table.
1498 * @param selectionArgs You may include ?s in selection, which will be
1499 * replaced by the values from selectionArgs, in order that they
1500 * appear in the selection. The values will be bound as Strings.
1501 * @param groupBy A filter declaring how to group rows, formatted as an SQL
1502 * GROUP BY clause (excluding the GROUP BY itself). Passing null
1503 * will cause the rows to not be grouped.
1504 * @param having A filter declare which row groups to include in the cursor,
1505 * if row grouping is being used, formatted as an SQL HAVING
1506 * clause (excluding the HAVING itself). Passing null will cause
1507 * all row groups to be included, and is required when row
1508 * grouping is not being used.
1509 * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
1510 * (excluding the ORDER BY itself). Passing null will use the
1511 * default sort order, which may be unordered.
1512 * @param limit Limits the number of rows returned by the query,
1513 * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
Jeff Hamiltonf3ca9a52010-05-12 15:04:33 -07001514 * @return A {@link Cursor} object, which is positioned before the first entry. Note that
1515 * {@link Cursor}s are not synchronized, see the documentation for more details.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001516 * @see Cursor
1517 */
1518 public Cursor query(String table, String[] columns, String selection,
1519 String[] selectionArgs, String groupBy, String having,
1520 String orderBy, String limit) {
1521
1522 return query(false, table, columns, selection, selectionArgs, groupBy,
1523 having, orderBy, limit);
1524 }
1525
1526 /**
1527 * Runs the provided SQL and returns a {@link Cursor} over the result set.
1528 *
1529 * @param sql the SQL query. The SQL string must not be ; terminated
1530 * @param selectionArgs You may include ?s in where clause in the query,
1531 * which will be replaced by the values from selectionArgs. The
1532 * values will be bound as Strings.
Jeff Hamiltonf3ca9a52010-05-12 15:04:33 -07001533 * @return A {@link Cursor} object, which is positioned before the first entry. Note that
1534 * {@link Cursor}s are not synchronized, see the documentation for more details.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001535 */
1536 public Cursor rawQuery(String sql, String[] selectionArgs) {
1537 return rawQueryWithFactory(null, sql, selectionArgs, null);
1538 }
1539
1540 /**
1541 * Runs the provided SQL and returns a cursor over the result set.
1542 *
1543 * @param cursorFactory the cursor factory to use, or null for the default factory
1544 * @param sql the SQL query. The SQL string must not be ; terminated
1545 * @param selectionArgs You may include ?s in where clause in the query,
1546 * which will be replaced by the values from selectionArgs. The
1547 * values will be bound as Strings.
1548 * @param editTable the name of the first table, which is editable
Jeff Hamiltonf3ca9a52010-05-12 15:04:33 -07001549 * @return A {@link Cursor} object, which is positioned before the first entry. Note that
1550 * {@link Cursor}s are not synchronized, see the documentation for more details.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001551 */
1552 public Cursor rawQueryWithFactory(
1553 CursorFactory cursorFactory, String sql, String[] selectionArgs,
1554 String editTable) {
Vasu Noriccd95442010-05-28 17:04:16 -07001555 verifyDbIsOpen();
Brad Fitzpatrickcfda9f32010-06-03 12:52:54 -07001556 BlockGuard.getThreadPolicy().onReadFromDisk();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001557 long timeStart = 0;
1558
Joe Onorato43a17652011-04-06 19:22:23 -07001559 if (false || mSlowQueryThreshold != -1) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001560 timeStart = System.currentTimeMillis();
1561 }
1562
Vasu Nori6c354da2010-04-26 23:33:39 -07001563 SQLiteDatabase db = getDbConnection(sql);
1564 SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(db, sql, editTable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001565
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001566 Cursor cursor = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001567 try {
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001568 cursor = driver.query(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001569 cursorFactory != null ? cursorFactory : mFactory,
1570 selectionArgs);
1571 } finally {
Joe Onorato43a17652011-04-06 19:22:23 -07001572 if (false || mSlowQueryThreshold != -1) {
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001573
Vasu Nori020e5342010-04-28 14:22:38 -07001574 // Force query execution
1575 int count = -1;
1576 if (cursor != null) {
1577 count = cursor.getCount();
1578 }
1579
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001580 long duration = System.currentTimeMillis() - timeStart;
1581
Joe Onorato43a17652011-04-06 19:22:23 -07001582 if (false || duration >= mSlowQueryThreshold) {
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001583 Log.v(SQLiteCursor.TAG,
1584 "query (" + duration + " ms): " + driver.toString() + ", args are "
1585 + (selectionArgs != null
1586 ? TextUtils.join(",", selectionArgs)
Vasu Nori020e5342010-04-28 14:22:38 -07001587 : "<null>") + ", count is " + count);
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001588 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001589 }
Vasu Nori6c354da2010-04-26 23:33:39 -07001590 releaseDbConnection(db);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001591 }
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001592 return cursor;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001593 }
1594
1595 /**
1596 * Runs the provided SQL and returns a cursor over the result set.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001597 * The cursor will read an initial set of rows and the return to the caller.
1598 * It will continue to read in batches and send data changed notifications
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001599 * when the later batches are ready.
1600 * @param sql the SQL query. The SQL string must not be ; terminated
1601 * @param selectionArgs You may include ?s in where clause in the query,
1602 * which will be replaced by the values from selectionArgs. The
1603 * values will be bound as Strings.
1604 * @param initialRead set the initial count of items to read from the cursor
1605 * @param maxRead set the count of items to read on each iteration after the first
Jeff Hamiltonf3ca9a52010-05-12 15:04:33 -07001606 * @return A {@link Cursor} object, which is positioned before the first entry. Note that
1607 * {@link Cursor}s are not synchronized, see the documentation for more details.
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001608 *
Andy Stadlerf8a7cea2009-04-10 16:24:47 -07001609 * This work is incomplete and not fully tested or reviewed, so currently
1610 * hidden.
1611 * @hide
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001612 */
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001613 public Cursor rawQuery(String sql, String[] selectionArgs,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001614 int initialRead, int maxRead) {
1615 SQLiteCursor c = (SQLiteCursor)rawQueryWithFactory(
1616 null, sql, selectionArgs, null);
1617 c.setLoadStyle(initialRead, maxRead);
1618 return c;
1619 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001620
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001621 /**
1622 * Convenience method for inserting a row into the database.
1623 *
1624 * @param table the table to insert the row into
Brad Fitzpatrick69ea4e12011-01-05 11:13:40 -08001625 * @param nullColumnHack optional; may be <code>null</code>.
1626 * SQL doesn't allow inserting a completely empty row without
1627 * naming at least one column name. If your provided <code>values</code> is
1628 * empty, no column names are known and an empty row can't be inserted.
1629 * If not set to null, the <code>nullColumnHack</code> parameter
1630 * provides the name of nullable column name to explicitly insert a NULL into
1631 * in the case where your <code>values</code> is empty.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001632 * @param values this map contains the initial column values for the
1633 * row. The keys should be the column names and the values the
1634 * column values
1635 * @return the row ID of the newly inserted row, or -1 if an error occurred
1636 */
1637 public long insert(String table, String nullColumnHack, ContentValues values) {
1638 try {
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001639 return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001640 } catch (SQLException e) {
1641 Log.e(TAG, "Error inserting " + values, e);
1642 return -1;
1643 }
1644 }
1645
1646 /**
1647 * Convenience method for inserting a row into the database.
1648 *
1649 * @param table the table to insert the row into
Brad Fitzpatrick69ea4e12011-01-05 11:13:40 -08001650 * @param nullColumnHack optional; may be <code>null</code>.
1651 * SQL doesn't allow inserting a completely empty row without
1652 * naming at least one column name. If your provided <code>values</code> is
1653 * empty, no column names are known and an empty row can't be inserted.
1654 * If not set to null, the <code>nullColumnHack</code> parameter
1655 * provides the name of nullable column name to explicitly insert a NULL into
1656 * in the case where your <code>values</code> is empty.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001657 * @param values this map contains the initial column values for the
1658 * row. The keys should be the column names and the values the
1659 * column values
1660 * @throws SQLException
1661 * @return the row ID of the newly inserted row, or -1 if an error occurred
1662 */
1663 public long insertOrThrow(String table, String nullColumnHack, ContentValues values)
1664 throws SQLException {
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001665 return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001666 }
1667
1668 /**
1669 * Convenience method for replacing a row in the database.
1670 *
1671 * @param table the table in which to replace the row
Brad Fitzpatrick69ea4e12011-01-05 11:13:40 -08001672 * @param nullColumnHack optional; may be <code>null</code>.
1673 * SQL doesn't allow inserting a completely empty row without
1674 * naming at least one column name. If your provided <code>initialValues</code> is
1675 * empty, no column names are known and an empty row can't be inserted.
1676 * If not set to null, the <code>nullColumnHack</code> parameter
1677 * provides the name of nullable column name to explicitly insert a NULL into
1678 * in the case where your <code>initialValues</code> is empty.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001679 * @param initialValues this map contains the initial column values for
Brad Fitzpatrick69ea4e12011-01-05 11:13:40 -08001680 * the row.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001681 * @return the row ID of the newly inserted row, or -1 if an error occurred
1682 */
1683 public long replace(String table, String nullColumnHack, ContentValues initialValues) {
1684 try {
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001685 return insertWithOnConflict(table, nullColumnHack, initialValues,
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001686 CONFLICT_REPLACE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001687 } catch (SQLException e) {
1688 Log.e(TAG, "Error inserting " + initialValues, e);
1689 return -1;
1690 }
1691 }
1692
1693 /**
1694 * Convenience method for replacing a row in the database.
1695 *
1696 * @param table the table in which to replace the row
Brad Fitzpatrick69ea4e12011-01-05 11:13:40 -08001697 * @param nullColumnHack optional; may be <code>null</code>.
1698 * SQL doesn't allow inserting a completely empty row without
1699 * naming at least one column name. If your provided <code>initialValues</code> is
1700 * empty, no column names are known and an empty row can't be inserted.
1701 * If not set to null, the <code>nullColumnHack</code> parameter
1702 * provides the name of nullable column name to explicitly insert a NULL into
1703 * in the case where your <code>initialValues</code> is empty.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001704 * @param initialValues this map contains the initial column values for
1705 * the row. The key
1706 * @throws SQLException
1707 * @return the row ID of the newly inserted row, or -1 if an error occurred
1708 */
1709 public long replaceOrThrow(String table, String nullColumnHack,
1710 ContentValues initialValues) throws SQLException {
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001711 return insertWithOnConflict(table, nullColumnHack, initialValues,
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001712 CONFLICT_REPLACE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001713 }
1714
1715 /**
1716 * General method for inserting a row into the database.
1717 *
1718 * @param table the table to insert the row into
Brad Fitzpatrick69ea4e12011-01-05 11:13:40 -08001719 * @param nullColumnHack optional; may be <code>null</code>.
1720 * SQL doesn't allow inserting a completely empty row without
1721 * naming at least one column name. If your provided <code>initialValues</code> is
1722 * empty, no column names are known and an empty row can't be inserted.
1723 * If not set to null, the <code>nullColumnHack</code> parameter
1724 * provides the name of nullable column name to explicitly insert a NULL into
1725 * in the case where your <code>initialValues</code> is empty.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001726 * @param initialValues this map contains the initial column values for the
1727 * row. The keys should be the column names and the values the
1728 * column values
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001729 * @param conflictAlgorithm for insert conflict resolver
Vasu Nori6eb7c452010-01-27 14:31:24 -08001730 * @return the row ID of the newly inserted row
1731 * OR the primary key of the existing row if the input param 'conflictAlgorithm' =
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001732 * {@link #CONFLICT_IGNORE}
Vasu Nori6eb7c452010-01-27 14:31:24 -08001733 * OR -1 if any error
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001734 */
1735 public long insertWithOnConflict(String table, String nullColumnHack,
Vasu Nori6eb7c452010-01-27 14:31:24 -08001736 ContentValues initialValues, int conflictAlgorithm) {
Vasu Nori0732f792010-07-29 17:24:12 -07001737 StringBuilder sql = new StringBuilder();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001738 sql.append("INSERT");
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001739 sql.append(CONFLICT_VALUES[conflictAlgorithm]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001740 sql.append(" INTO ");
1741 sql.append(table);
Vasu Nori0732f792010-07-29 17:24:12 -07001742 sql.append('(');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001743
Vasu Nori0732f792010-07-29 17:24:12 -07001744 Object[] bindArgs = null;
1745 int size = (initialValues != null && initialValues.size() > 0) ? initialValues.size() : 0;
1746 if (size > 0) {
1747 bindArgs = new Object[size];
1748 int i = 0;
1749 for (String colName : initialValues.keySet()) {
1750 sql.append((i > 0) ? "," : "");
1751 sql.append(colName);
1752 bindArgs[i++] = initialValues.get(colName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001753 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001754 sql.append(')');
Vasu Nori0732f792010-07-29 17:24:12 -07001755 sql.append(" VALUES (");
1756 for (i = 0; i < size; i++) {
1757 sql.append((i > 0) ? ",?" : "?");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001758 }
Vasu Nori0732f792010-07-29 17:24:12 -07001759 } else {
1760 sql.append(nullColumnHack + ") VALUES (NULL");
1761 }
1762 sql.append(')');
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001763
Vasu Nori0732f792010-07-29 17:24:12 -07001764 SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
1765 try {
Vasu Norifb16cbd2010-07-25 16:38:48 -07001766 return statement.executeInsert();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001767 } catch (SQLiteDatabaseCorruptException e) {
1768 onCorruption();
1769 throw e;
1770 } finally {
Vasu Nori0732f792010-07-29 17:24:12 -07001771 statement.close();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001772 }
1773 }
1774
1775 /**
1776 * Convenience method for deleting rows in the database.
1777 *
1778 * @param table the table to delete from
1779 * @param whereClause the optional WHERE clause to apply when deleting.
1780 * Passing null will delete all rows.
1781 * @return the number of rows affected if a whereClause is passed in, 0
1782 * otherwise. To remove all rows and get a count pass "1" as the
1783 * whereClause.
1784 */
1785 public int delete(String table, String whereClause, String[] whereArgs) {
Vasu Nori0732f792010-07-29 17:24:12 -07001786 SQLiteStatement statement = new SQLiteStatement(this, "DELETE FROM " + table +
1787 (!TextUtils.isEmpty(whereClause) ? " WHERE " + whereClause : ""), whereArgs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001788 try {
Vasu Norifb16cbd2010-07-25 16:38:48 -07001789 return statement.executeUpdateDelete();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001790 } catch (SQLiteDatabaseCorruptException e) {
1791 onCorruption();
1792 throw e;
1793 } finally {
Vasu Nori0732f792010-07-29 17:24:12 -07001794 statement.close();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001795 }
1796 }
1797
1798 /**
1799 * Convenience method for updating rows in the database.
1800 *
1801 * @param table the table to update in
1802 * @param values a map from column names to new column values. null is a
1803 * valid value that will be translated to NULL.
1804 * @param whereClause the optional WHERE clause to apply when updating.
1805 * Passing null will update all rows.
1806 * @return the number of rows affected
1807 */
1808 public int update(String table, ContentValues values, String whereClause, String[] whereArgs) {
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001809 return updateWithOnConflict(table, values, whereClause, whereArgs, CONFLICT_NONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001810 }
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001811
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001812 /**
1813 * Convenience method for updating rows in the database.
1814 *
1815 * @param table the table to update in
1816 * @param values a map from column names to new column values. null is a
1817 * valid value that will be translated to NULL.
1818 * @param whereClause the optional WHERE clause to apply when updating.
1819 * Passing null will update all rows.
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001820 * @param conflictAlgorithm for update conflict resolver
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001821 * @return the number of rows affected
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001822 */
Dmitri Plotnikov600bdd82009-09-01 12:12:20 -07001823 public int updateWithOnConflict(String table, ContentValues values,
Vasu Nori6eb7c452010-01-27 14:31:24 -08001824 String whereClause, String[] whereArgs, int conflictAlgorithm) {
Brian Muramatsu46a88512010-11-12 13:53:57 -08001825 if (values == null || values.size() == 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001826 throw new IllegalArgumentException("Empty values");
1827 }
1828
1829 StringBuilder sql = new StringBuilder(120);
1830 sql.append("UPDATE ");
Vasu Nori8d45e4e2010-02-05 22:35:47 -08001831 sql.append(CONFLICT_VALUES[conflictAlgorithm]);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001832 sql.append(table);
1833 sql.append(" SET ");
1834
Vasu Nori0732f792010-07-29 17:24:12 -07001835 // move all bind args to one array
Brian Muramatsu46a88512010-11-12 13:53:57 -08001836 int setValuesSize = values.size();
Vasu Nori0732f792010-07-29 17:24:12 -07001837 int bindArgsSize = (whereArgs == null) ? setValuesSize : (setValuesSize + whereArgs.length);
1838 Object[] bindArgs = new Object[bindArgsSize];
1839 int i = 0;
1840 for (String colName : values.keySet()) {
1841 sql.append((i > 0) ? "," : "");
1842 sql.append(colName);
1843 bindArgs[i++] = values.get(colName);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001844 sql.append("=?");
Vasu Nori0732f792010-07-29 17:24:12 -07001845 }
1846 if (whereArgs != null) {
1847 for (i = setValuesSize; i < bindArgsSize; i++) {
1848 bindArgs[i] = whereArgs[i - setValuesSize];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001849 }
1850 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001851 if (!TextUtils.isEmpty(whereClause)) {
1852 sql.append(" WHERE ");
1853 sql.append(whereClause);
1854 }
1855
Vasu Nori0732f792010-07-29 17:24:12 -07001856 SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001857 try {
Vasu Norifb16cbd2010-07-25 16:38:48 -07001858 return statement.executeUpdateDelete();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001859 } catch (SQLiteDatabaseCorruptException e) {
1860 onCorruption();
1861 throw e;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001862 } finally {
Vasu Nori0732f792010-07-29 17:24:12 -07001863 statement.close();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001864 }
1865 }
1866
1867 /**
Vasu Noriccd95442010-05-28 17:04:16 -07001868 * Execute a single SQL statement that is NOT a SELECT
1869 * or any other SQL statement that returns data.
1870 * <p>
Vasu Norice38b982010-07-22 13:57:13 -07001871 * It has no means to return any data (such as the number of affected rows).
Vasu Noriccd95442010-05-28 17:04:16 -07001872 * Instead, you're encouraged to use {@link #insert(String, String, ContentValues)},
1873 * {@link #update(String, ContentValues, String, String[])}, et al, when possible.
1874 * </p>
Vasu Nori9bf225e2010-07-07 16:38:28 -07001875 * <p>
1876 * When using {@link #enableWriteAheadLogging()}, journal_mode is
1877 * automatically managed by this class. So, do not set journal_mode
1878 * using "PRAGMA journal_mode'<value>" statement if your app is using
1879 * {@link #enableWriteAheadLogging()}
1880 * </p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001881 *
Vasu Noriccd95442010-05-28 17:04:16 -07001882 * @param sql the SQL statement to be executed. Multiple statements separated by semicolons are
1883 * not supported.
Brad Fitzpatrick69ea4e12011-01-05 11:13:40 -08001884 * @throws SQLException if the SQL string is invalid
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001885 */
Vasu Norib83cb7c2010-09-14 13:36:01 -07001886 public void execSQL(String sql) throws SQLException {
Vasu Nori16057fa2011-03-18 11:40:37 -07001887 executeSql(sql, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001888 }
1889
1890 /**
Vasu Noriccd95442010-05-28 17:04:16 -07001891 * Execute a single SQL statement that is NOT a SELECT/INSERT/UPDATE/DELETE.
1892 * <p>
1893 * For INSERT statements, use any of the following instead.
1894 * <ul>
1895 * <li>{@link #insert(String, String, ContentValues)}</li>
1896 * <li>{@link #insertOrThrow(String, String, ContentValues)}</li>
1897 * <li>{@link #insertWithOnConflict(String, String, ContentValues, int)}</li>
1898 * </ul>
1899 * <p>
1900 * For UPDATE statements, use any of the following instead.
1901 * <ul>
1902 * <li>{@link #update(String, ContentValues, String, String[])}</li>
1903 * <li>{@link #updateWithOnConflict(String, ContentValues, String, String[], int)}</li>
1904 * </ul>
1905 * <p>
1906 * For DELETE statements, use any of the following instead.
1907 * <ul>
1908 * <li>{@link #delete(String, String, String[])}</li>
1909 * </ul>
1910 * <p>
1911 * For example, the following are good candidates for using this method:
1912 * <ul>
1913 * <li>ALTER TABLE</li>
1914 * <li>CREATE or DROP table / trigger / view / index / virtual table</li>
1915 * <li>REINDEX</li>
1916 * <li>RELEASE</li>
1917 * <li>SAVEPOINT</li>
1918 * <li>PRAGMA that returns no data</li>
1919 * </ul>
1920 * </p>
Vasu Nori9bf225e2010-07-07 16:38:28 -07001921 * <p>
1922 * When using {@link #enableWriteAheadLogging()}, journal_mode is
1923 * automatically managed by this class. So, do not set journal_mode
1924 * using "PRAGMA journal_mode'<value>" statement if your app is using
1925 * {@link #enableWriteAheadLogging()}
1926 * </p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001927 *
Vasu Noriccd95442010-05-28 17:04:16 -07001928 * @param sql the SQL statement to be executed. Multiple statements separated by semicolons are
1929 * not supported.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001930 * @param bindArgs only byte[], String, Long and Double are supported in bindArgs.
Brad Fitzpatrick69ea4e12011-01-05 11:13:40 -08001931 * @throws SQLException if the SQL string is invalid
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001932 */
Vasu Norib83cb7c2010-09-14 13:36:01 -07001933 public void execSQL(String sql, Object[] bindArgs) throws SQLException {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001934 if (bindArgs == null) {
1935 throw new IllegalArgumentException("Empty bindArgs");
1936 }
Vasu Norib83cb7c2010-09-14 13:36:01 -07001937 executeSql(sql, bindArgs);
Vasu Norice38b982010-07-22 13:57:13 -07001938 }
1939
Vasu Nori54025902010-09-14 12:14:26 -07001940 private int executeSql(String sql, Object[] bindArgs) throws SQLException {
Vasu Noricc1eaf62011-03-14 19:22:16 -07001941 if (DatabaseUtils.getSqlStatementType(sql) == DatabaseUtils.STATEMENT_ATTACH) {
1942 disableWriteAheadLogging();
1943 mHasAttachedDbs = true;
1944 }
Vasu Nori0732f792010-07-29 17:24:12 -07001945 SQLiteStatement statement = new SQLiteStatement(this, sql, bindArgs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001946 try {
Vasu Nori16057fa2011-03-18 11:40:37 -07001947 return statement.executeUpdateDelete();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001948 } catch (SQLiteDatabaseCorruptException e) {
1949 onCorruption();
1950 throw e;
1951 } finally {
Vasu Nori0732f792010-07-29 17:24:12 -07001952 statement.close();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001953 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001954 }
1955
1956 @Override
Mike Lockwood9d9c1be2010-07-13 19:27:52 -04001957 protected void finalize() throws Throwable {
1958 try {
1959 if (isOpen()) {
1960 Log.e(TAG, "close() was never explicitly called on database '" +
1961 mPath + "' ", mStackTrace);
1962 closeClosable();
1963 onAllReferencesReleased();
1964 releaseCustomFunctions();
1965 }
1966 } finally {
1967 super.finalize();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001968 }
1969 }
1970
1971 /**
Vasu Nori21343692010-06-03 16:01:39 -07001972 * Private constructor.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001973 *
1974 * @param path The full path to the database
1975 * @param factory The factory to use when creating cursors, may be NULL.
1976 * @param flags 0 or {@link #NO_LOCALIZED_COLLATORS}. If the database file already
1977 * exists, mFlags will be updated appropriately.
Vasu Nori21343692010-06-03 16:01:39 -07001978 * @param errorHandler The {@link DatabaseErrorHandler} to be used when sqlite reports database
1979 * corruption. may be NULL.
Vasu Nori6c354da2010-04-26 23:33:39 -07001980 * @param connectionNum 0 for main database connection handle. 1..N for pooled database
1981 * connection handles.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001982 */
Vasu Nori21343692010-06-03 16:01:39 -07001983 private SQLiteDatabase(String path, CursorFactory factory, int flags,
Vasu Nori6c354da2010-04-26 23:33:39 -07001984 DatabaseErrorHandler errorHandler, short connectionNum) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001985 if (path == null) {
1986 throw new IllegalArgumentException("path should not be null");
1987 }
Jesse Wilsondfe515e2011-02-10 19:06:09 -08001988 setMaxSqlCacheSize(DEFAULT_SQL_CACHE_SIZE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001989 mFlags = flags;
1990 mPath = path;
Dmitri Plotnikov90142c92009-09-15 10:52:17 -07001991 mSlowQueryThreshold = SystemProperties.getInt(LOG_SLOW_QUERIES_PROPERTY, -1);
Vasu Nori08b448e2010-03-03 10:05:16 -08001992 mStackTrace = new DatabaseObjectNotClosedException().fillInStackTrace();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001993 mFactory = factory;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001994 mPrograms = new WeakHashMap<SQLiteClosable,Object>();
Vasu Nori21343692010-06-03 16:01:39 -07001995 // Set the DatabaseErrorHandler to be used when SQLite reports corruption.
1996 // If the caller sets errorHandler = null, then use default errorhandler.
1997 mErrorHandler = (errorHandler == null) ? new DefaultDatabaseErrorHandler() : errorHandler;
Vasu Nori6c354da2010-04-26 23:33:39 -07001998 mConnectionNum = connectionNum;
Vasu Nori34ad57f02010-12-21 09:32:36 -08001999 /* sqlite soft heap limit http://www.sqlite.org/c3ref/soft_heap_limit64.html
2000 * set it to 4 times the default cursor window size.
Jesse Wilsondfe515e2011-02-10 19:06:09 -08002001 * TODO what is an appropriate value, considering the WAL feature which could burn
Vasu Nori34ad57f02010-12-21 09:32:36 -08002002 * a lot of memory with many connections to the database. needs testing to figure out
2003 * optimal value for this.
2004 */
2005 int limit = Resources.getSystem().getInteger(
2006 com.android.internal.R.integer.config_cursorWindowSize) * 1024 * 4;
2007 native_setSqliteSoftHeapLimit(limit);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002008 }
2009
2010 /**
2011 * return whether the DB is opened as read only.
2012 * @return true if DB is opened as read only
2013 */
2014 public boolean isReadOnly() {
2015 return (mFlags & OPEN_READ_MASK) == OPEN_READONLY;
2016 }
2017
2018 /**
2019 * @return true if the DB is currently open (has not been closed)
2020 */
2021 public boolean isOpen() {
2022 return mNativeHandle != 0;
2023 }
2024
2025 public boolean needUpgrade(int newVersion) {
2026 return newVersion > getVersion();
2027 }
2028
2029 /**
2030 * Getter for the path to the database file.
2031 *
2032 * @return the path to our database file.
2033 */
2034 public final String getPath() {
2035 return mPath;
2036 }
2037
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -08002038 /* package */ void logTimeStat(String sql, long beginMillis) {
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07002039 logTimeStat(sql, beginMillis, null);
2040 }
2041
Vasu Nori16057fa2011-03-18 11:40:37 -07002042 private void logTimeStat(String sql, long beginMillis, String prefix) {
Dan Egnor12311952009-11-23 14:47:45 -08002043 // Sample fast queries in proportion to the time taken.
2044 // Quantize the % first, so the logged sampling probability
2045 // exactly equals the actual sampling rate for this query.
2046
2047 int samplePercent;
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -08002048 long durationMillis = SystemClock.uptimeMillis() - beginMillis;
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07002049 if (durationMillis == 0 && prefix == GET_LOCK_LOG_PREFIX) {
2050 // The common case is locks being uncontended. Don't log those,
2051 // even at 1%, which is our default below.
2052 return;
2053 }
2054 if (sQueryLogTimeInMillis == 0) {
2055 sQueryLogTimeInMillis = SystemProperties.getInt("db.db_operation.threshold_ms", 500);
2056 }
2057 if (durationMillis >= sQueryLogTimeInMillis) {
Dan Egnor12311952009-11-23 14:47:45 -08002058 samplePercent = 100;
Vasu Norifb16cbd2010-07-25 16:38:48 -07002059 } else {
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07002060 samplePercent = (int) (100 * durationMillis / sQueryLogTimeInMillis) + 1;
Dan Egnor799f7212009-11-24 16:24:44 -08002061 if (mRandom.nextInt(100) >= samplePercent) return;
Dan Egnor12311952009-11-23 14:47:45 -08002062 }
2063
Brad Fitzpatrick722802e2010-03-23 22:22:16 -07002064 // Note: the prefix will be "COMMIT;" or "GETLOCK:" when non-null. We wait to do
2065 // it here so we avoid allocating in the common case.
2066 if (prefix != null) {
2067 sql = prefix + sql;
2068 }
Dan Egnor12311952009-11-23 14:47:45 -08002069 if (sql.length() > QUERY_LOG_SQL_LENGTH) sql = sql.substring(0, QUERY_LOG_SQL_LENGTH);
2070
2071 // ActivityThread.currentPackageName() only returns non-null if the
2072 // current thread is an application main thread. This parameter tells
2073 // us whether an event loop is blocked, and if so, which app it is.
2074 //
2075 // Sadly, there's no fast way to determine app name if this is *not* a
2076 // main thread, or when we are invoked via Binder (e.g. ContentProvider).
2077 // Hopefully the full path to the database will be informative enough.
2078
Dianne Hackborn01e4cfc2010-06-24 15:07:24 -07002079 String blockingPackage = AppGlobals.getInitialPackage();
Dan Egnor12311952009-11-23 14:47:45 -08002080 if (blockingPackage == null) blockingPackage = "";
2081
Brad Fitzpatrickd72f7182010-02-11 17:07:51 -08002082 EventLog.writeEvent(
Brad Fitzpatrickd8330232010-02-19 10:59:01 -08002083 EVENT_DB_OPERATION,
2084 getPathForLogs(),
2085 sql,
2086 durationMillis,
2087 blockingPackage,
2088 samplePercent);
2089 }
2090
2091 /**
2092 * Removes email addresses from database filenames before they're
2093 * logged to the EventLog where otherwise apps could potentially
2094 * read them.
2095 */
2096 private String getPathForLogs() {
2097 if (mPathForLogs != null) {
2098 return mPathForLogs;
2099 }
2100 if (mPath == null) {
2101 return null;
2102 }
2103 if (mPath.indexOf('@') == -1) {
2104 mPathForLogs = mPath;
2105 } else {
2106 mPathForLogs = EMAIL_IN_DB_PATTERN.matcher(mPath).replaceAll("XX@YY");
2107 }
2108 return mPathForLogs;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002109 }
2110
2111 /**
2112 * Sets the locale for this database. Does nothing if this database has
2113 * the NO_LOCALIZED_COLLATORS flag set or was opened read only.
2114 * @throws SQLException if the locale could not be set. The most common reason
2115 * for this is that there is no collator available for the locale you requested.
2116 * In this case the database remains unchanged.
2117 */
2118 public void setLocale(Locale locale) {
2119 lock();
2120 try {
2121 native_setLocale(locale.toString(), mFlags);
2122 } finally {
2123 unlock();
2124 }
2125 }
2126
Vasu Noriccd95442010-05-28 17:04:16 -07002127 /* package */ void verifyDbIsOpen() {
Vasu Nori9463f292010-04-30 12:22:18 -07002128 if (!isOpen()) {
Vasu Nori75010102010-07-01 16:23:06 -07002129 throw new IllegalStateException("database " + getPath() + " (conn# " +
2130 mConnectionNum + ") already closed");
Vasu Nori9463f292010-04-30 12:22:18 -07002131 }
Vasu Noriccd95442010-05-28 17:04:16 -07002132 }
2133
2134 /* package */ void verifyLockOwner() {
2135 verifyDbIsOpen();
2136 if (mLockingEnabled && !isDbLockedByCurrentThread()) {
Vasu Nori9463f292010-04-30 12:22:18 -07002137 throw new IllegalStateException("Don't have database lock!");
2138 }
2139 }
2140
Vasu Norib729dcc2010-09-14 11:35:49 -07002141 /**
2142 * Adds the given SQL and its compiled-statement-id-returned-by-sqlite to the
2143 * cache of compiledQueries attached to 'this'.
2144 * <p>
2145 * If there is already a {@link SQLiteCompiledSql} in compiledQueries for the given SQL,
2146 * the new {@link SQLiteCompiledSql} object is NOT inserted into the cache (i.e.,the current
2147 * mapping is NOT replaced with the new mapping).
2148 */
Jesse Wilsondfe515e2011-02-10 19:06:09 -08002149 /* package */ synchronized void addToCompiledQueries(
2150 String sql, SQLiteCompiledSql compiledStatement) {
2151 // don't insert the new mapping if a mapping already exists
2152 if (mCompiledQueries.get(sql) != null) {
2153 return;
2154 }
Vasu Norib729dcc2010-09-14 11:35:49 -07002155
Jesse Wilsondfe515e2011-02-10 19:06:09 -08002156 int maxCacheSz = (mConnectionNum == 0) ? mCompiledQueries.maxSize() :
2157 mParentConnObj.mCompiledQueries.maxSize();
Brian Muramatsu46a88512010-11-12 13:53:57 -08002158
Jesse Wilsondfe515e2011-02-10 19:06:09 -08002159 if (SQLiteDebug.DEBUG_SQL_CACHE) {
2160 boolean printWarning = (mConnectionNum == 0)
2161 ? (!mCacheFullWarning && mCompiledQueries.size() == maxCacheSz)
2162 : (!mParentConnObj.mCacheFullWarning &&
2163 mParentConnObj.mCompiledQueries.size() == maxCacheSz);
2164 if (printWarning) {
2165 /*
2166 * cache size is not enough for this app. log a warning.
2167 * chances are it is NOT using ? for bindargs - or cachesize is too small.
2168 */
2169 Log.w(TAG, "Reached MAX size for compiled-sql statement cache for database " +
2170 getPath() + ". Use setMaxSqlCacheSize() to increase cachesize. ");
2171 mCacheFullWarning = true;
2172 Log.d(TAG, "Here are the SQL statements in Cache of database: " + mPath);
2173 for (String s : mCompiledQueries.snapshot().keySet()) {
2174 Log.d(TAG, "Sql statement in Cache: " + s);
Vasu Nori74fb2682010-10-25 11:48:24 -07002175 }
Vasu Nori7301a232010-11-05 11:46:15 -07002176 }
Vasu Norib729dcc2010-09-14 11:35:49 -07002177 }
Jesse Wilsondfe515e2011-02-10 19:06:09 -08002178 /* add the given SQLiteCompiledSql compiledStatement to cache.
2179 * no need to worry about the cache size - because {@link #mCompiledQueries}
2180 * self-limits its size.
2181 */
2182 mCompiledQueries.put(sql, compiledStatement);
Vasu Norib729dcc2010-09-14 11:35:49 -07002183 }
2184
2185 /** package-level access for testing purposes */
Jesse Wilsondfe515e2011-02-10 19:06:09 -08002186 /* package */ synchronized void deallocCachedSqlStatements() {
2187 for (SQLiteCompiledSql compiledSql : mCompiledQueries.snapshot().values()) {
2188 compiledSql.releaseSqlStatement();
Vasu Norib729dcc2010-09-14 11:35:49 -07002189 }
Jesse Wilsondfe515e2011-02-10 19:06:09 -08002190 mCompiledQueries.evictAll();
Vasu Norib729dcc2010-09-14 11:35:49 -07002191 }
2192
2193 /**
2194 * From the compiledQueries cache, returns the compiled-statement-id for the given SQL.
2195 * Returns null, if not found in the cache.
2196 */
Jesse Wilsondfe515e2011-02-10 19:06:09 -08002197 /* package */ synchronized SQLiteCompiledSql getCompiledStatementForSql(String sql) {
Jesse Wilson9b5a9352011-02-10 11:19:09 -08002198 return mCompiledQueries.get(sql);
Vasu Norib729dcc2010-09-14 11:35:49 -07002199 }
2200
Vasu Norie495d1f2010-01-06 16:34:19 -08002201 /**
Vasu Noriccd95442010-05-28 17:04:16 -07002202 * Sets the maximum size of the prepared-statement cache for this database.
Vasu Norie495d1f2010-01-06 16:34:19 -08002203 * (size of the cache = number of compiled-sql-statements stored in the cache).
Vasu Noriccd95442010-05-28 17:04:16 -07002204 *<p>
Vasu Norib729dcc2010-09-14 11:35:49 -07002205 * Maximum cache size can ONLY be increased from its current size (default = 10).
Vasu Noriccd95442010-05-28 17:04:16 -07002206 * If this method is called with smaller size than the current maximum value,
2207 * then IllegalStateException is thrown.
Vasu Norib729dcc2010-09-14 11:35:49 -07002208 *<p>
2209 * This method is thread-safe.
Vasu Norie495d1f2010-01-06 16:34:19 -08002210 *
Vasu Nori90a367262010-04-12 12:49:09 -07002211 * @param cacheSize the size of the cache. can be (0 to {@link #MAX_SQL_CACHE_SIZE})
2212 * @throws IllegalStateException if input cacheSize > {@link #MAX_SQL_CACHE_SIZE} or
Vasu Noribfe1dc22010-08-25 16:29:02 -07002213 * the value set with previous setMaxSqlCacheSize() call.
Vasu Norie495d1f2010-01-06 16:34:19 -08002214 */
Vasu Nori54025902010-09-14 12:14:26 -07002215 public void setMaxSqlCacheSize(int cacheSize) {
Jesse Wilsondfe515e2011-02-10 19:06:09 -08002216 synchronized (this) {
2217 LruCache<String, SQLiteCompiledSql> oldCompiledQueries = mCompiledQueries;
Vasu Nori54025902010-09-14 12:14:26 -07002218 if (cacheSize > MAX_SQL_CACHE_SIZE || cacheSize < 0) {
Jesse Wilsondfe515e2011-02-10 19:06:09 -08002219 throw new IllegalStateException(
2220 "expected value between 0 and " + MAX_SQL_CACHE_SIZE);
2221 } else if (oldCompiledQueries != null && cacheSize < oldCompiledQueries.maxSize()) {
2222 throw new IllegalStateException("cannot set cacheSize to a value less than the "
2223 + "value set with previous setMaxSqlCacheSize() call.");
Vasu Nori54025902010-09-14 12:14:26 -07002224 }
Jesse Wilsondfe515e2011-02-10 19:06:09 -08002225 mCompiledQueries = new LruCache<String, SQLiteCompiledSql>(cacheSize) {
2226 @Override
Jesse Wilson32c80a22011-02-25 17:28:41 -08002227 protected void entryRemoved(boolean evicted, String key, SQLiteCompiledSql oldValue,
2228 SQLiteCompiledSql newValue) {
Jesse Wilsondfe515e2011-02-10 19:06:09 -08002229 verifyLockOwner();
Jesse Wilson32c80a22011-02-25 17:28:41 -08002230 oldValue.releaseIfNotInUse();
Jesse Wilsondfe515e2011-02-10 19:06:09 -08002231 }
2232 };
2233 if (oldCompiledQueries != null) {
2234 for (Map.Entry<String, SQLiteCompiledSql> entry
2235 : oldCompiledQueries.snapshot().entrySet()) {
2236 mCompiledQueries.put(entry.getKey(), entry.getValue());
2237 }
Vasu Nori24675612010-09-27 14:54:19 -07002238 }
Vasu Nori587423a2010-09-27 18:18:34 -07002239 }
2240 }
2241
Jesse Wilsondfe515e2011-02-10 19:06:09 -08002242 /* package */ synchronized boolean isInStatementCache(String sql) {
2243 return mCompiledQueries.get(sql) != null;
2244 }
2245
2246 /* package */ synchronized void releaseCompiledSqlObj(
2247 String sql, SQLiteCompiledSql compiledSql) {
2248 if (mCompiledQueries.get(sql) == compiledSql) {
2249 // it is in cache - reset its inUse flag
2250 compiledSql.release();
2251 } else {
2252 // it is NOT in cache. finalize it.
2253 compiledSql.releaseSqlStatement();
2254 }
2255 }
2256
2257 private synchronized int getCacheHitNum() {
Jesse Wilson9b5a9352011-02-10 11:19:09 -08002258 return mCompiledQueries.hitCount();
Vasu Nori5e89ae22010-09-15 14:23:29 -07002259 }
2260
Jesse Wilsondfe515e2011-02-10 19:06:09 -08002261 private synchronized int getCacheMissNum() {
Jesse Wilson9b5a9352011-02-10 11:19:09 -08002262 return mCompiledQueries.missCount();
Vasu Nori5e89ae22010-09-15 14:23:29 -07002263 }
2264
Jesse Wilsondfe515e2011-02-10 19:06:09 -08002265 private synchronized int getCachesize() {
Jesse Wilson9b5a9352011-02-10 11:19:09 -08002266 return mCompiledQueries.size();
Vasu Nori5e89ae22010-09-15 14:23:29 -07002267 }
2268
Vasu Nori6f37f832010-05-19 11:53:25 -07002269 /* package */ void finalizeStatementLater(int id) {
2270 if (!isOpen()) {
2271 // database already closed. this statement will already have been finalized.
2272 return;
2273 }
2274 synchronized(mClosedStatementIds) {
2275 if (mClosedStatementIds.contains(id)) {
2276 // this statement id is already queued up for finalization.
2277 return;
2278 }
2279 mClosedStatementIds.add(id);
2280 }
2281 }
2282
Vasu Nori83ff97d2011-01-30 12:47:55 -08002283 /* package */ boolean isInQueueOfStatementsToBeFinalized(int id) {
2284 if (!isOpen()) {
2285 // database already closed. this statement will already have been finalized.
2286 // return true so that the caller doesn't have to worry about finalizing this statement.
2287 return true;
2288 }
2289 synchronized(mClosedStatementIds) {
2290 return mClosedStatementIds.contains(id);
2291 }
2292 }
2293
Vasu Norice38b982010-07-22 13:57:13 -07002294 /* package */ void closePendingStatements() {
Vasu Nori6f37f832010-05-19 11:53:25 -07002295 if (!isOpen()) {
2296 // since this database is already closed, no need to finalize anything.
2297 mClosedStatementIds.clear();
2298 return;
2299 }
2300 verifyLockOwner();
2301 /* to minimize synchronization on mClosedStatementIds, make a copy of the list */
2302 ArrayList<Integer> list = new ArrayList<Integer>(mClosedStatementIds.size());
2303 synchronized(mClosedStatementIds) {
2304 list.addAll(mClosedStatementIds);
2305 mClosedStatementIds.clear();
2306 }
2307 // finalize all the statements from the copied list
2308 int size = list.size();
2309 for (int i = 0; i < size; i++) {
2310 native_finalize(list.get(i));
2311 }
2312 }
2313
2314 /**
2315 * for testing only
Vasu Nori6f37f832010-05-19 11:53:25 -07002316 */
Vasu Norice38b982010-07-22 13:57:13 -07002317 /* package */ ArrayList<Integer> getQueuedUpStmtList() {
Vasu Nori6f37f832010-05-19 11:53:25 -07002318 return mClosedStatementIds;
2319 }
2320
Vasu Nori6c354da2010-04-26 23:33:39 -07002321 /**
2322 * This method enables parallel execution of queries from multiple threads on the same database.
2323 * It does this by opening multiple handles to the database and using a different
2324 * database handle for each query.
2325 * <p>
2326 * If a transaction is in progress on one connection handle and say, a table is updated in the
2327 * transaction, then query on the same table on another connection handle will block for the
2328 * transaction to complete. But this method enables such queries to execute by having them
2329 * return old version of the data from the table. Most often it is the data that existed in the
2330 * table prior to the above transaction updates on that table.
2331 * <p>
2332 * Maximum number of simultaneous handles used to execute queries in parallel is
2333 * dependent upon the device memory and possibly other properties.
2334 * <p>
2335 * After calling this method, execution of queries in parallel is enabled as long as this
2336 * database handle is open. To disable execution of queries in parallel, database should
2337 * be closed and reopened.
2338 * <p>
2339 * If a query is part of a transaction, then it is executed on the same database handle the
2340 * transaction was begun.
Vasu Nori6c354da2010-04-26 23:33:39 -07002341 * <p>
2342 * If the database has any attached databases, then execution of queries in paralel is NOT
Vasu Noria98cb262010-06-22 13:16:35 -07002343 * possible. In such cases, a message is printed to logcat and false is returned.
2344 * <p>
2345 * This feature is not available for :memory: databases. In such cases,
2346 * a message is printed to logcat and false is returned.
Vasu Nori6c354da2010-04-26 23:33:39 -07002347 * <p>
2348 * A typical way to use this method is the following:
2349 * <pre>
2350 * SQLiteDatabase db = SQLiteDatabase.openDatabase("db_filename", cursorFactory,
2351 * CREATE_IF_NECESSARY, myDatabaseErrorHandler);
2352 * db.enableWriteAheadLogging();
2353 * </pre>
2354 * <p>
2355 * Writers should use {@link #beginTransactionNonExclusive()} or
2356 * {@link #beginTransactionWithListenerNonExclusive(SQLiteTransactionListener)}
2357 * to start a trsnsaction.
2358 * Non-exclusive mode allows database file to be in readable by threads executing queries.
2359 * </p>
2360 *
Vasu Noria98cb262010-06-22 13:16:35 -07002361 * @return true if write-ahead-logging is set. false otherwise
Vasu Nori6c354da2010-04-26 23:33:39 -07002362 */
Vasu Noriffe06122010-09-27 12:32:57 -07002363 public boolean enableWriteAheadLogging() {
Paul Westbrookdae6d372011-02-17 10:59:56 -08002364 // make sure the database is not READONLY. WAL doesn't make sense for readonly-databases.
2365 if (isReadOnly()) {
2366 return false;
2367 }
2368 // acquire lock - no that no other thread is enabling WAL at the same time
2369 lock();
2370 try {
2371 if (mConnectionPool != null) {
2372 // already enabled
2373 return true;
2374 }
2375 if (mPath.equalsIgnoreCase(MEMORY_DB_PATH)) {
2376 Log.i(TAG, "can't enable WAL for memory databases.");
2377 return false;
2378 }
2379
2380 // make sure this database has NO attached databases because sqlite's write-ahead-logging
2381 // doesn't work for databases with attached databases
2382 if (mHasAttachedDbs) {
2383 if (Log.isLoggable(TAG, Log.DEBUG)) {
2384 Log.d(TAG,
2385 "this database: " + mPath + " has attached databases. can't enable WAL.");
2386 }
2387 return false;
2388 }
2389 mConnectionPool = new DatabaseConnectionPool(this);
2390 setJournalMode(mPath, "WAL");
2391 return true;
2392 } finally {
2393 unlock();
2394 }
Vasu Nori6c354da2010-04-26 23:33:39 -07002395 }
2396
Vasu Nori2827d6d2010-07-04 00:26:18 -07002397 /**
Vasu Nori7b04c412010-07-20 10:31:21 -07002398 * This method disables the features enabled by {@link #enableWriteAheadLogging()}.
2399 * @hide
Vasu Nori2827d6d2010-07-04 00:26:18 -07002400 */
Vasu Nori7b04c412010-07-20 10:31:21 -07002401 public void disableWriteAheadLogging() {
Paul Westbrookdae6d372011-02-17 10:59:56 -08002402 // grab database lock so that writeAheadLogging is not disabled from 2 different threads
2403 // at the same time
2404 lock();
2405 try {
2406 if (mConnectionPool == null) {
2407 return; // already disabled
2408 }
2409 mConnectionPool.close();
2410 setJournalMode(mPath, "TRUNCATE");
2411 mConnectionPool = null;
2412 } finally {
2413 unlock();
2414 }
Vasu Nori8d111032010-06-22 18:34:21 -07002415 }
2416
Vasu Nori65a88832010-07-16 15:14:08 -07002417 /* package */ SQLiteDatabase getDatabaseHandle(String sql) {
2418 if (isPooledConnection()) {
2419 // this is a pooled database connection
Vasu Norice38b982010-07-22 13:57:13 -07002420 // use it if it is open AND if I am not currently part of a transaction
2421 if (isOpen() && !amIInTransaction()) {
Vasu Nori65a88832010-07-16 15:14:08 -07002422 // TODO: use another connection from the pool
2423 // if this connection is currently in use by some other thread
2424 // AND if there are free connections in the pool
2425 return this;
2426 } else {
2427 // the pooled connection is not open! could have been closed either due
2428 // to corruption on this or some other connection to the database
2429 // OR, maybe the connection pool is disabled after this connection has been
2430 // allocated to me. try to get some other pooled or main database connection
2431 return getParentDbConnObj().getDbConnection(sql);
2432 }
2433 } else {
2434 // this is NOT a pooled connection. can we get one?
2435 return getDbConnection(sql);
2436 }
2437 }
2438
Vasu Nori6c354da2010-04-26 23:33:39 -07002439 /* package */ SQLiteDatabase createPoolConnection(short connectionNum) {
Vasu Nori65a88832010-07-16 15:14:08 -07002440 SQLiteDatabase db = openDatabase(mPath, mFactory, mFlags, mErrorHandler, connectionNum);
2441 db.mParentConnObj = this;
2442 return db;
2443 }
2444
2445 private synchronized SQLiteDatabase getParentDbConnObj() {
2446 return mParentConnObj;
Vasu Nori6c354da2010-04-26 23:33:39 -07002447 }
2448
2449 private boolean isPooledConnection() {
2450 return this.mConnectionNum > 0;
2451 }
2452
Vasu Nori2827d6d2010-07-04 00:26:18 -07002453 /* package */ SQLiteDatabase getDbConnection(String sql) {
Vasu Nori6c354da2010-04-26 23:33:39 -07002454 verifyDbIsOpen();
Vasu Noribfe1dc22010-08-25 16:29:02 -07002455 // this method should always be called with main database connection handle.
2456 // the only time when it is called with pooled database connection handle is
2457 // corruption occurs while trying to open a pooled database connection handle.
2458 // in that case, simply return 'this' handle
Vasu Nori65a88832010-07-16 15:14:08 -07002459 if (isPooledConnection()) {
Vasu Noribfe1dc22010-08-25 16:29:02 -07002460 return this;
Vasu Nori65a88832010-07-16 15:14:08 -07002461 }
Vasu Nori6c354da2010-04-26 23:33:39 -07002462
2463 // use the current connection handle if
Vasu Norice38b982010-07-22 13:57:13 -07002464 // 1. if the caller is part of the ongoing transaction, if any
Vasu Nori65a88832010-07-16 15:14:08 -07002465 // 2. OR, if there is NO connection handle pool setup
Vasu Norice38b982010-07-22 13:57:13 -07002466 if (amIInTransaction() || mConnectionPool == null) {
Vasu Nori65a88832010-07-16 15:14:08 -07002467 return this;
Vasu Nori6c354da2010-04-26 23:33:39 -07002468 } else {
2469 // get a connection handle from the pool
2470 if (Log.isLoggable(TAG, Log.DEBUG)) {
2471 assert mConnectionPool != null;
Vasu Norice38b982010-07-22 13:57:13 -07002472 Log.i(TAG, mConnectionPool.toString());
Vasu Nori6c354da2010-04-26 23:33:39 -07002473 }
Vasu Nori65a88832010-07-16 15:14:08 -07002474 return mConnectionPool.get(sql);
Vasu Nori6c354da2010-04-26 23:33:39 -07002475 }
Vasu Nori6c354da2010-04-26 23:33:39 -07002476 }
2477
2478 private void releaseDbConnection(SQLiteDatabase db) {
2479 // ignore this release call if
2480 // 1. the database is closed
2481 // 2. OR, if db is NOT a pooled connection handle
2482 // 3. OR, if the database being released is same as 'this' (this condition means
2483 // that we should always be releasing a pooled connection handle by calling this method
2484 // from the 'main' connection handle
2485 if (!isOpen() || !db.isPooledConnection() || (db == this)) {
2486 return;
2487 }
2488 if (Log.isLoggable(TAG, Log.DEBUG)) {
2489 assert isPooledConnection();
2490 assert mConnectionPool != null;
2491 Log.d(TAG, "releaseDbConnection threadid = " + Thread.currentThread().getId() +
2492 ", releasing # " + db.mConnectionNum + ", " + getPath());
2493 }
2494 mConnectionPool.release(db);
2495 }
2496
Vasu Norif3cf8a42010-03-23 11:41:44 -07002497 /**
2498 * this method is used to collect data about ALL open databases in the current process.
Vasu Nori0732f792010-07-29 17:24:12 -07002499 * bugreport is a user of this data.
Vasu Norif3cf8a42010-03-23 11:41:44 -07002500 */
Vasu Noric3849202010-03-09 10:47:25 -08002501 /* package */ static ArrayList<DbStats> getDbStats() {
2502 ArrayList<DbStats> dbStatsList = new ArrayList<DbStats>();
Vasu Nori24675612010-09-27 14:54:19 -07002503 // make a local copy of mActiveDatabases - so that this method is not competing
2504 // for synchronization lock on mActiveDatabases
2505 ArrayList<WeakReference<SQLiteDatabase>> tempList;
2506 synchronized(mActiveDatabases) {
2507 tempList = (ArrayList<WeakReference<SQLiteDatabase>>)mActiveDatabases.clone();
2508 }
2509 for (WeakReference<SQLiteDatabase> w : tempList) {
2510 SQLiteDatabase db = w.get();
2511 if (db == null || !db.isOpen()) {
2512 continue;
2513 }
2514
2515 try {
2516 // get SQLITE_DBSTATUS_LOOKASIDE_USED for the db
2517 int lookasideUsed = db.native_getDbLookaside();
2518
2519 // get the lastnode of the dbname
2520 String path = db.getPath();
2521 int indx = path.lastIndexOf("/");
2522 String lastnode = path.substring((indx != -1) ? ++indx : 0);
2523
2524 // get list of attached dbs and for each db, get its size and pagesize
Vasu Noria017eda2011-01-27 10:52:55 -08002525 List<Pair<String, String>> attachedDbs = db.getAttachedDbs();
Vasu Nori24675612010-09-27 14:54:19 -07002526 if (attachedDbs == null) {
2527 continue;
2528 }
2529 for (int i = 0; i < attachedDbs.size(); i++) {
2530 Pair<String, String> p = attachedDbs.get(i);
2531 long pageCount = DatabaseUtils.longForQuery(db, "PRAGMA " + p.first
2532 + ".page_count;", null);
2533
2534 // first entry in the attached db list is always the main database
2535 // don't worry about prefixing the dbname with "main"
2536 String dbName;
2537 if (i == 0) {
2538 dbName = lastnode;
2539 } else {
2540 // lookaside is only relevant for the main db
2541 lookasideUsed = 0;
2542 dbName = " (attached) " + p.first;
2543 // if the attached db has a path, attach the lastnode from the path to above
2544 if (p.second.trim().length() > 0) {
2545 int idx = p.second.lastIndexOf("/");
2546 dbName += " : " + p.second.substring((idx != -1) ? ++idx : 0);
2547 }
2548 }
2549 if (pageCount > 0) {
2550 dbStatsList.add(new DbStats(dbName, pageCount, db.getPageSize(),
2551 lookasideUsed, db.getCacheHitNum(), db.getCacheMissNum(),
Vasu Nori00e40172010-11-29 11:03:23 -08002552 db.getCachesize()));
Vasu Nori24675612010-09-27 14:54:19 -07002553 }
2554 }
2555 // if there are pooled connections, return the cache stats for them also.
2556 // while we are trying to query the pooled connections for stats, some other thread
2557 // could be disabling conneciton pool. so, grab a reference to the connection pool.
2558 DatabaseConnectionPool connPool = db.mConnectionPool;
2559 if (connPool != null) {
2560 for (SQLiteDatabase pDb : connPool.getConnectionList()) {
2561 dbStatsList.add(new DbStats("(pooled # " + pDb.mConnectionNum + ") "
2562 + lastnode, 0, 0, 0, pDb.getCacheHitNum(),
Vasu Nori00e40172010-11-29 11:03:23 -08002563 pDb.getCacheMissNum(), pDb.getCachesize()));
Vasu Nori24675612010-09-27 14:54:19 -07002564 }
2565 }
2566 } catch (SQLiteException e) {
2567 // ignore. we don't care about exceptions when we are taking adb
2568 // bugreport!
2569 }
2570 }
Vasu Noric3849202010-03-09 10:47:25 -08002571 return dbStatsList;
2572 }
2573
2574 /**
Vasu Noriccd95442010-05-28 17:04:16 -07002575 * Returns list of full pathnames of all attached databases including the main database
2576 * by executing 'pragma database_list' on the database.
2577 *
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002578 * @return ArrayList of pairs of (database name, database file path) or null if the database
2579 * is not open.
Vasu Noric3849202010-03-09 10:47:25 -08002580 */
Vasu Noria017eda2011-01-27 10:52:55 -08002581 public List<Pair<String, String>> getAttachedDbs() {
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002582 if (!isOpen()) {
Vasu Norif3cf8a42010-03-23 11:41:44 -07002583 return null;
2584 }
Vasu Noric3849202010-03-09 10:47:25 -08002585 ArrayList<Pair<String, String>> attachedDbs = new ArrayList<Pair<String, String>>();
Vasu Nori24675612010-09-27 14:54:19 -07002586 if (!mHasAttachedDbs) {
2587 // No attached databases.
2588 // There is a small window where attached databases exist but this flag is not set yet.
2589 // This can occur when this thread is in a race condition with another thread
2590 // that is executing the SQL statement: "attach database <blah> as <foo>"
2591 // If this thread is NOT ok with such a race condition (and thus possibly not receive
2592 // the entire list of attached databases), then the caller should ensure that no thread
2593 // is executing any SQL statements while a thread is calling this method.
2594 // Typically, this method is called when 'adb bugreport' is done or the caller wants to
2595 // collect stats on the database and all its attached databases.
2596 attachedDbs.add(new Pair<String, String>("main", mPath));
2597 return attachedDbs;
2598 }
2599 // has attached databases. query sqlite to get the list of attached databases.
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002600 Cursor c = null;
2601 try {
2602 c = rawQuery("pragma database_list;", null);
2603 while (c.moveToNext()) {
2604 // sqlite returns a row for each database in the returned list of databases.
2605 // in each row,
2606 // 1st column is the database name such as main, or the database
2607 // name specified on the "ATTACH" command
2608 // 2nd column is the database file path.
2609 attachedDbs.add(new Pair<String, String>(c.getString(1), c.getString(2)));
2610 }
2611 } finally {
2612 if (c != null) {
2613 c.close();
2614 }
Vasu Noric3849202010-03-09 10:47:25 -08002615 }
Vasu Noric3849202010-03-09 10:47:25 -08002616 return attachedDbs;
2617 }
2618
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002619 /**
Vasu Noriccd95442010-05-28 17:04:16 -07002620 * Runs 'pragma integrity_check' on the given database (and all the attached databases)
2621 * and returns true if the given database (and all its attached databases) pass integrity_check,
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002622 * false otherwise.
Vasu Noriccd95442010-05-28 17:04:16 -07002623 *<p>
2624 * If the result is false, then this method logs the errors reported by the integrity_check
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002625 * command execution.
Vasu Noriccd95442010-05-28 17:04:16 -07002626 *<p>
2627 * Note that 'pragma integrity_check' on a database can take a long time.
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002628 *
2629 * @return true if the given database (and all its attached databases) pass integrity_check,
Vasu Noriccd95442010-05-28 17:04:16 -07002630 * false otherwise.
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002631 */
2632 public boolean isDatabaseIntegrityOk() {
Vasu Noriccd95442010-05-28 17:04:16 -07002633 verifyDbIsOpen();
Vasu Noria017eda2011-01-27 10:52:55 -08002634 List<Pair<String, String>> attachedDbs = null;
Vasu Noribfe1dc22010-08-25 16:29:02 -07002635 try {
2636 attachedDbs = getAttachedDbs();
2637 if (attachedDbs == null) {
2638 throw new IllegalStateException("databaselist for: " + getPath() + " couldn't " +
2639 "be retrieved. probably because the database is closed");
2640 }
2641 } catch (SQLiteException e) {
2642 // can't get attachedDb list. do integrity check on the main database
2643 attachedDbs = new ArrayList<Pair<String, String>>();
2644 attachedDbs.add(new Pair<String, String>("main", this.mPath));
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002645 }
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002646 for (int i = 0; i < attachedDbs.size(); i++) {
2647 Pair<String, String> p = attachedDbs.get(i);
2648 SQLiteStatement prog = null;
2649 try {
2650 prog = compileStatement("PRAGMA " + p.first + ".integrity_check(1);");
2651 String rslt = prog.simpleQueryForString();
2652 if (!rslt.equalsIgnoreCase("ok")) {
2653 // integrity_checker failed on main or attached databases
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002654 Log.e(TAG, "PRAGMA integrity_check on " + p.second + " returned: " + rslt);
Vasu Noribfe1dc22010-08-25 16:29:02 -07002655 return false;
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002656 }
2657 } finally {
2658 if (prog != null) prog.close();
2659 }
2660 }
Vasu Noribfe1dc22010-08-25 16:29:02 -07002661 return true;
Vasu Nori062fc7ce2010-03-31 16:13:05 -07002662 }
2663
2664 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002665 * Native call to open the database.
2666 *
2667 * @param path The full path to the database
2668 */
2669 private native void dbopen(String path, int flags);
2670
2671 /**
Vasu Noriccd95442010-05-28 17:04:16 -07002672 * Native call to setup tracing of all SQL statements
Vasu Nori3ef94e22010-02-05 14:49:04 -08002673 *
2674 * @param path the full path to the database
Vasu Nori6c354da2010-04-26 23:33:39 -07002675 * @param connectionNum connection number: 0 - N, where the main database
2676 * connection handle is numbered 0 and the connection handles in the connection
2677 * pool are numbered 1..N.
Vasu Nori3ef94e22010-02-05 14:49:04 -08002678 */
Vasu Nori6c354da2010-04-26 23:33:39 -07002679 private native void enableSqlTracing(String path, short connectionNum);
Vasu Nori3ef94e22010-02-05 14:49:04 -08002680
2681 /**
Vasu Noriccd95442010-05-28 17:04:16 -07002682 * Native call to setup profiling of all SQL statements.
Vasu Nori3ef94e22010-02-05 14:49:04 -08002683 * currently, sqlite's profiling = printing of execution-time
Vasu Noriccd95442010-05-28 17:04:16 -07002684 * (wall-clock time) of each of the SQL statements, as they
Vasu Nori3ef94e22010-02-05 14:49:04 -08002685 * are executed.
2686 *
2687 * @param path the full path to the database
Vasu Nori6c354da2010-04-26 23:33:39 -07002688 * @param connectionNum connection number: 0 - N, where the main database
2689 * connection handle is numbered 0 and the connection handles in the connection
2690 * pool are numbered 1..N.
Vasu Nori3ef94e22010-02-05 14:49:04 -08002691 */
Vasu Nori6c354da2010-04-26 23:33:39 -07002692 private native void enableSqlProfiling(String path, short connectionNum);
Vasu Nori3ef94e22010-02-05 14:49:04 -08002693
2694 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002695 * Native call to set the locale. {@link #lock} must be held when calling
2696 * this method.
2697 * @throws SQLException
2698 */
Vasu Nori0732f792010-07-29 17:24:12 -07002699 private native void native_setLocale(String loc, int flags);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002700
2701 /**
Vasu Noric3849202010-03-09 10:47:25 -08002702 * return the SQLITE_DBSTATUS_LOOKASIDE_USED documented here
2703 * http://www.sqlite.org/c3ref/c_dbstatus_lookaside_used.html
2704 * @return int value of SQLITE_DBSTATUS_LOOKASIDE_USED
2705 */
2706 private native int native_getDbLookaside();
Vasu Nori6f37f832010-05-19 11:53:25 -07002707
2708 /**
2709 * finalizes the given statement id.
2710 *
2711 * @param statementId statement to be finzlied by sqlite
2712 */
2713 private final native void native_finalize(int statementId);
Vasu Nori34ad57f02010-12-21 09:32:36 -08002714
2715 /**
2716 * set sqlite soft heap limit
2717 * http://www.sqlite.org/c3ref/soft_heap_limit64.html
2718 */
2719 private native void native_setSqliteSoftHeapLimit(int softHeapLimit);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002720}