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