forked from requery/sqlite-android
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSQLiteDatabase.java
More file actions
2616 lines (2422 loc) · 106 KB
/
SQLiteDatabase.java
File metadata and controls
2616 lines (2422 loc) · 106 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// modified from original source see README at the top level of this project
/*
** Modified to support SQLite extensions by the SQLite developers:
** sqlite-dev@sqlite.org.
*/
package io.requery.android.database.sqlite;
import android.annotation.SuppressLint;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabaseCorruptException;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteTransactionListener;
import android.os.Build;
import android.os.Looper;
import android.os.ParcelFileDescriptor;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
import android.util.Pair;
import android.util.Printer;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.os.CancellationSignal;
import androidx.core.os.OperationCanceledException;
import androidx.sqlite.db.SupportSQLiteDatabase;
import androidx.sqlite.db.SupportSQLiteQuery;
import io.requery.android.database.DatabaseErrorHandler;
import io.requery.android.database.DefaultDatabaseErrorHandler;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.WeakHashMap;
/**
* Exposes methods to manage a SQLite database.
*
* <p>
* SQLiteDatabase has methods to create, delete, execute SQL commands, and
* perform other common database management tasks.
* </p><p>
* See the Notepad sample application in the SDK for an example of creating
* and managing a database.
* </p><p>
* Database names must be unique within an application, not across all applications.
* </p>
*
* <h3>Localized Collation - ORDER BY</h3>
* <p>
* In addition to SQLite's default <code>BINARY</code> collator, Android supplies
* two more, <code>LOCALIZED</code>, which changes with the system's current locale,
* and <code>UNICODE</code>, which is the Unicode Collation Algorithm and not tailored
* to the current locale.
* </p>
*/
@SuppressWarnings({"unused", "JavaDoc", "TryFinallyCanBeTryWithResources"})
@SuppressLint("ShiftFlags") // suppressed for readability with native code
public final class SQLiteDatabase extends SQLiteClosable implements SupportSQLiteDatabase {
/**
* Name of the compiled native library.
*/
public static final String LIBRARY_NAME = "sqlite3x";
static {
System.loadLibrary(LIBRARY_NAME);
}
private static final String TAG = "SQLiteDatabase";
private static final int EVENT_DB_CORRUPT = 75004;
// Stores reference to all databases opened in the current process.
// (The referent Object is not used at this time.)
// INVARIANT: Guarded by sActiveDatabases.
private static final WeakHashMap<SQLiteDatabase, Object> sActiveDatabases = new WeakHashMap<>();
// Thread-local for database sessions that belong to this database.
// Each thread has its own database session.
// INVARIANT: Immutable.
private final ThreadLocal<SQLiteSession> mThreadSession = new ThreadLocal<SQLiteSession>() {
@Override
protected SQLiteSession initialValue() {
return createSession();
}
};
// The optional factory to use when creating new Cursors. May be null.
// INVARIANT: Immutable.
private final CursorFactory mCursorFactory;
// Error handler to be used when SQLite returns corruption errors.
// INVARIANT: Immutable.
private final DatabaseErrorHandler mErrorHandler;
// Shared database state lock.
// This lock guards all of the shared state of the database, such as its
// configuration, whether it is open or closed, and so on. This lock should
// be held for as little time as possible.
//
// The lock MUST NOT be held while attempting to acquire database connections or
// while executing SQL statements on behalf of the client as it can lead to deadlock.
//
// It is ok to hold the lock while reconfiguring the connection pool or dumping
// statistics because those operations are non-reentrant and do not try to acquire
// connections that might be held by other threads.
//
// Basic rule: grab the lock, access or modify global state, release the lock, then
// do the required SQL work.
private final Object mLock = new Object();
// Warns if the database is finalized without being closed properly.
// INVARIANT: Guarded by mLock.
private final CloseGuard mCloseGuardLocked = CloseGuard.get();
// The database configuration.
// INVARIANT: Guarded by mLock.
private final SQLiteDatabaseConfiguration mConfigurationLocked;
// The connection pool for the database, null when closed.
// The pool itself is thread-safe, but the reference to it can only be acquired
// when the lock is held.
// INVARIANT: Guarded by mLock.
private SQLiteConnectionPool mConnectionPoolLocked;
/**
* When a constraint violation occurs, an immediate ROLLBACK occurs,
* thus ending the current transaction, and the command aborts with a
* return code of SQLITE_CONSTRAINT. If no transaction is active
* (other than the implied transaction that is created on every command)
* then this algorithm works the same as ABORT.
*/
public static final int CONFLICT_ROLLBACK = 1;
/**
* When a constraint violation occurs,no ROLLBACK is executed
* so changes from prior commands within the same transaction
* are preserved. This is the default behavior.
*/
public static final int CONFLICT_ABORT = 2;
/**
* When a constraint violation occurs, the command aborts with a return
* code SQLITE_CONSTRAINT. But any changes to the database that
* the command made prior to encountering the constraint violation
* are preserved and are not backed out.
*/
public static final int CONFLICT_FAIL = 3;
/**
* When a constraint violation occurs, the one row that contains
* the constraint violation is not inserted or changed.
* But the command continues executing normally. Other rows before and
* after the row that contained the constraint violation continue to be
* inserted or updated normally. No error is returned.
*/
public static final int CONFLICT_IGNORE = 4;
/**
* When a UNIQUE constraint violation occurs, the pre-existing rows that
* are causing the constraint violation are removed prior to inserting
* or updating the current row. Thus the insert or update always occurs.
* The command continues executing normally. No error is returned.
* If a NOT NULL constraint violation occurs, the NULL value is replaced
* by the default value for that column. If the column has no default
* value, then the ABORT algorithm is used. If a CHECK constraint
* violation occurs then the IGNORE algorithm is used. When this conflict
* resolution strategy deletes rows in order to satisfy a constraint,
* it does not invoke delete triggers on those rows.
* This behavior might change in a future release.
*/
public static final int CONFLICT_REPLACE = 5;
/**
* Use the following when no conflict action is specified.
*/
public static final int CONFLICT_NONE = 0;
/** Conflict options integer enumeration definition */
@IntDef({
CONFLICT_ABORT,
CONFLICT_FAIL,
CONFLICT_IGNORE,
CONFLICT_NONE,
CONFLICT_REPLACE,
CONFLICT_ROLLBACK})
@Retention(RetentionPolicy.SOURCE)
public @interface ConflictAlgorithm {
}
private static final String[] CONFLICT_VALUES = new String[]
{"", " OR ROLLBACK ", " OR ABORT ", " OR FAIL ", " OR IGNORE ", " OR REPLACE "};
/** Open flag to open in the database in read only mode */
public static final int OPEN_READONLY = 0x00000001;
/** Open flag to open in the database in read/write mode */
public static final int OPEN_READWRITE = 0x00000002;
/** Open flag to create the database if it does not exist */
public static final int OPEN_CREATE = 0x00000004;
/** Open flag to support URI filenames */
public static final int OPEN_URI = 0x00000040;
/** Open flag opens the database in multi-thread threading mode */
public static final int OPEN_NOMUTEX = 0x00008000;
/** Open flag opens the database in serialized threading mode */
public static final int OPEN_FULLMUTEX = 0x00010000;
/** Open flag opens the database in shared cache mode */
public static final int OPEN_SHAREDCACHE = 0x00020000;
/** Open flag opens the database in private cache mode */
public static final int OPEN_PRIVATECACHE = 0x00040000;
/** Open flag equivalent to {@link #OPEN_READWRITE} | {@link #OPEN_CREATE} */
public static final int CREATE_IF_NECESSARY = OPEN_READWRITE | OPEN_CREATE;
/** Open flag to enable write-ahead logging */ // custom flag remove for sqlite3_open_v2
public static final int ENABLE_WRITE_AHEAD_LOGGING = 0x20000000;
/** Integer flag definition for the database open options */
@SuppressLint("UniqueConstants") // duplicate values provided for compatibility
@IntDef(flag = true, value = {
OPEN_READONLY,
OPEN_READWRITE,
OPEN_CREATE,
OPEN_URI,
OPEN_NOMUTEX,
OPEN_FULLMUTEX,
OPEN_SHAREDCACHE,
OPEN_PRIVATECACHE,
CREATE_IF_NECESSARY,
ENABLE_WRITE_AHEAD_LOGGING})
@Retention(RetentionPolicy.SOURCE)
public @interface OpenFlags {
}
/**
* Absolute max value that can be set by {@link #setMaxSqlCacheSize(int)}.
*
* Each prepared-statement is between 1K - 6K, depending on the complexity of the
* SQL statement & schema. A large SQL cache may use a significant amount of memory.
*/
public static final int MAX_SQL_CACHE_SIZE = 100;
private SQLiteDatabase(SQLiteDatabaseConfiguration configuration,
CursorFactory cursorFactory,
DatabaseErrorHandler errorHandler) {
mCursorFactory = cursorFactory;
mErrorHandler = errorHandler != null ? errorHandler : new DefaultDatabaseErrorHandler();
mConfigurationLocked = configuration;
}
@SuppressWarnings("ThrowFromFinallyBlock")
@Override
protected void finalize() throws Throwable {
try {
dispose(true);
} finally {
super.finalize();
}
}
@Override
protected void onAllReferencesReleased() {
dispose(false);
}
private void dispose(boolean finalized) {
final SQLiteConnectionPool pool;
synchronized (mLock) {
if (mCloseGuardLocked != null) {
if (finalized) {
mCloseGuardLocked.warnIfOpen();
}
mCloseGuardLocked.close();
}
pool = mConnectionPoolLocked;
mConnectionPoolLocked = null;
}
if (!finalized) {
synchronized (sActiveDatabases) {
sActiveDatabases.remove(this);
}
if (pool != null) {
pool.close();
}
}
}
/**
* Attempts to release memory that SQLite holds but does not require to
* operate properly. Typically this memory will come from the page cache.
*
* @return the number of bytes actually released
*/
public static int releaseMemory() {
return SQLiteGlobal.releaseMemory();
}
/**
* Gets a label to use when describing the database in log messages.
* @return The label.
*/
String getLabel() {
synchronized (mLock) {
return mConfigurationLocked.label;
}
}
/**
* Sends a corruption message to the database error handler.
*/
void onCorruption() {
EventLog.writeEvent(EVENT_DB_CORRUPT, getLabel());
mErrorHandler.onCorruption(this);
}
/**
* Gets the {@link SQLiteSession} that belongs to this thread for this database.
* Once a thread has obtained a session, it will continue to obtain the same
* session even after the database has been closed (although the session will not
* be usable). However, a thread that does not already have a session cannot
* obtain one after the database has been closed.
*
* The idea is that threads that have active connections to the database may still
* have work to complete even after the call to {@link #close}. Active database
* connections are not actually disposed until they are released by the threads
* that own them.
*
* @return The session, never null.
*
* @throws IllegalStateException if the thread does not yet have a session and
* the database is not open.
*/
SQLiteSession getThreadSession() {
return mThreadSession.get(); // initialValue() throws if database closed
}
SQLiteSession createSession() {
final SQLiteConnectionPool pool;
synchronized (mLock) {
throwIfNotOpenLocked();
pool = mConnectionPoolLocked;
}
return new SQLiteSession(pool);
}
/**
* Gets default connection flags that are appropriate for this thread, taking into
* account whether the thread is acting on behalf of the UI.
*
* @param readOnly True if the connection should be read-only.
* @return The connection flags.
*/
int getThreadDefaultConnectionFlags(boolean readOnly) {
int flags = readOnly ? SQLiteConnectionPool.CONNECTION_FLAG_READ_ONLY :
SQLiteConnectionPool.CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY;
if (isMainThread()) {
flags |= SQLiteConnectionPool.CONNECTION_FLAG_INTERACTIVE;
}
return flags;
}
private static boolean isMainThread() {
// FIXME: There should be a better way to do this.
// Would also be nice to have something that would work across Binder calls.
Looper looper = Looper.myLooper();
return looper != null && looper == Looper.getMainLooper();
}
/**
* Begins a transaction in EXCLUSIVE mode.
* <p>
* Transactions can be nested.
* When the outer transaction is ended all of
* the work done in that transaction and all of the nested transactions will be committed or
* rolled back. The changes will be rolled back if any transaction is ended without being
* marked as clean (by calling setTransactionSuccessful). Otherwise they will be committed.
* </p>
* <p>Here is the standard idiom for transactions:
*
* <pre>
* db.beginTransaction();
* try {
* ...
* db.setTransactionSuccessful();
* } finally {
* db.endTransaction();
* }
* </pre>
*/
@Override
public void beginTransaction() {
beginTransaction(null, SQLiteSession.TRANSACTION_MODE_EXCLUSIVE);
}
/**
* Begins a transaction in IMMEDIATE mode. Transactions can be nested. When
* the outer transaction is ended all of the work done in that transaction
* and all of the nested transactions will be committed or rolled back. The
* changes will be rolled back if any transaction is ended without being
* marked as clean (by calling setTransactionSuccessful). Otherwise they
* will be committed.
* <p>
* Here is the standard idiom for transactions:
*
* <pre>
* db.beginTransactionNonExclusive();
* try {
* ...
* db.setTransactionSuccessful();
* } finally {
* db.endTransaction();
* }
* </pre>
*/
@Override
public void beginTransactionNonExclusive() {
beginTransaction(null, SQLiteSession.TRANSACTION_MODE_IMMEDIATE);
}
/**
* Begins a transaction in DEFERRED mode.
*/
public void beginTransactionDeferred() {
beginTransaction(null, SQLiteSession.TRANSACTION_MODE_DEFERRED);
}
/**
* Begins a transaction in DEFERRED mode.
*
* @param transactionListener listener that should be notified when the transaction begins,
* commits, or is rolled back, either explicitly or by a call to
* {@link #yieldIfContendedSafely}.
*/
public void beginTransactionWithListenerDeferred(
SQLiteTransactionListener transactionListener) {
beginTransaction(transactionListener, SQLiteSession.TRANSACTION_MODE_DEFERRED);
}
/**
* Begins a transaction in EXCLUSIVE mode.
* <p>
* Transactions can be nested.
* When the outer transaction is ended all of
* the work done in that transaction and all of the nested transactions will be committed or
* rolled back. The changes will be rolled back if any transaction is ended without being
* marked as clean (by calling setTransactionSuccessful). Otherwise they will be committed.
* </p>
* <p>Here is the standard idiom for transactions:
*
* <pre>
* db.beginTransactionWithListener(listener);
* try {
* ...
* db.setTransactionSuccessful();
* } finally {
* db.endTransaction();
* }
* </pre>
*
* @param transactionListener listener that should be notified when the transaction begins,
* commits, or is rolled back, either explicitly or by a call to
* {@link #yieldIfContendedSafely}.
*/
@Override
public void beginTransactionWithListener(SQLiteTransactionListener transactionListener) {
beginTransaction(transactionListener, SQLiteSession.TRANSACTION_MODE_EXCLUSIVE);
}
/**
* Begins a transaction in IMMEDIATE mode. Transactions can be nested. When
* the outer transaction is ended all of the work done in that transaction
* and all of the nested transactions will be committed or rolled back. The
* changes will be rolled back if any transaction is ended without being
* marked as clean (by calling setTransactionSuccessful). Otherwise they
* will be committed.
* <p>
* Here is the standard idiom for transactions:
*
* <pre>
* db.beginTransactionWithListenerNonExclusive(listener);
* try {
* ...
* db.setTransactionSuccessful();
* } finally {
* db.endTransaction();
* }
* </pre>
*
* @param transactionListener listener that should be notified when the
* transaction begins, commits, or is rolled back, either
* explicitly or by a call to {@link #yieldIfContendedSafely}.
*/
@Override
public void beginTransactionWithListenerNonExclusive(
SQLiteTransactionListener transactionListener) {
beginTransaction(transactionListener, SQLiteSession.TRANSACTION_MODE_IMMEDIATE);
}
private void beginTransaction(SQLiteTransactionListener transactionListener, int mode) {
acquireReference();
try {
getThreadSession().beginTransaction(mode, transactionListener,
getThreadDefaultConnectionFlags(false /*readOnly*/), null);
} finally {
releaseReference();
}
}
/**
* End a transaction. See beginTransaction for notes about how to use this and when transactions
* are committed and rolled back.
*/
@Override
public void endTransaction() {
acquireReference();
try {
getThreadSession().endTransaction(null);
} finally {
releaseReference();
}
}
/**
* Marks the current transaction as successful. Do not do any more database work between
* calling this and calling endTransaction. Do as little non-database work as possible in that
* situation too. If any errors are encountered between this and endTransaction the transaction
* will still be committed.
*
* @throws IllegalStateException if the current thread is not in a transaction or the
* transaction is already marked as successful.
*/
@Override
public void setTransactionSuccessful() {
acquireReference();
try {
getThreadSession().setTransactionSuccessful();
} finally {
releaseReference();
}
}
/**
* Returns true if the current thread has a transaction pending.
*
* @return True if the current thread is in a transaction.
*/
@Override
public boolean inTransaction() {
acquireReference();
try {
return getThreadSession().hasTransaction();
} finally {
releaseReference();
}
}
/**
* Returns true if the current thread is holding an active connection to the database.
* <p>
* The name of this method comes from a time when having an active connection
* to the database meant that the thread was holding an actual lock on the
* database. Nowadays, there is no longer a true "database lock" although threads
* may block if they cannot acquire a database connection to perform a
* particular operation.
* </p>
*
* @return True if the current thread is holding an active connection to the database.
*/
@Override
public boolean isDbLockedByCurrentThread() {
acquireReference();
try {
return getThreadSession().hasConnection();
} finally {
releaseReference();
}
}
/**
* Temporarily end the transaction to let other threads run. The transaction is assumed to be
* successful so far. Do not call setTransactionSuccessful before calling this. When this
* returns a new transaction will have been created but not marked as successful. This assumes
* that there are no nested transactions (beginTransaction has only been called once) and will
* throw an exception if that is not the case.
* @return true if the transaction was yielded
*/
@Override
public boolean yieldIfContendedSafely() {
return yieldIfContendedHelper(true /* check yielding */, -1 /* sleepAfterYieldDelay*/);
}
/**
* Temporarily end the transaction to let other threads run. The transaction is assumed to be
* successful so far. Do not call setTransactionSuccessful before calling this. When this
* returns a new transaction will have been created but not marked as successful. This assumes
* that there are no nested transactions (beginTransaction has only been called once) and will
* throw an exception if that is not the case.
* @param sleepAfterYieldDelay if > 0, sleep this long before starting a new transaction if
* the lock was actually yielded. This will allow other background threads to make some
* more progress than they would if we started the transaction immediately.
* @return true if the transaction was yielded
*/
@Override
public boolean yieldIfContendedSafely(long sleepAfterYieldDelay) {
return yieldIfContendedHelper(true /* check yielding */, sleepAfterYieldDelay);
}
private boolean yieldIfContendedHelper(boolean throwIfUnsafe, long sleepAfterYieldDelay) {
acquireReference();
try {
return getThreadSession().yieldTransaction(sleepAfterYieldDelay, throwIfUnsafe, null);
} finally {
releaseReference();
}
}
/**
* Open the database according to the flags {@link OpenFlags}
*
* <p>Sets the locale of the database to the the system's current locale.
* Call {@link #setLocale} if you would like something else.</p>
*
* @param path to database file to open and/or create
* @param factory an optional factory class that is called to instantiate a
* cursor when query is called, or null for default
* @param flags to control database access mode
* @return the newly opened database
* @throws SQLiteException if the database cannot be opened
*/
public static SQLiteDatabase openDatabase(String path,
CursorFactory factory,
@OpenFlags int flags) {
return openDatabase(path, factory, flags, null);
}
/**
* Open the database according to the flags {@link OpenFlags}
*
* <p>Sets the locale of the database to the the system's current locale.
* Call {@link #setLocale} if you would like something else.</p>
*
* <p>Accepts input param: a concrete instance of {@link DatabaseErrorHandler} to be
* used to handle corruption when sqlite reports database corruption.</p>
*
* @param path to database file to open and/or create
* @param factory an optional factory class that is called to instantiate a
* cursor when query is called, or null for default
* @param flags to control database access mode
* @param errorHandler the {@link DatabaseErrorHandler} obj to be used to handle corruption
* when sqlite reports database corruption
* @return the newly opened database
* @throws SQLiteException if the database cannot be opened
*/
public static SQLiteDatabase openDatabase(String path,
CursorFactory factory,
@OpenFlags int flags,
DatabaseErrorHandler errorHandler) {
SQLiteDatabaseConfiguration configuration = new SQLiteDatabaseConfiguration(path, flags);
SQLiteDatabase db = new SQLiteDatabase(configuration, factory, errorHandler);
db.open();
return db;
}
/**
* Open the database according to the given configuration.
*
* <p>Sets the locale of the database to the the system's current locale.
* Call {@link #setLocale} if you would like something else.</p>
*
* <p>Accepts input param: a concrete instance of {@link DatabaseErrorHandler} to be
* used to handle corruption when sqlite reports database corruption.</p>
*
* @param configuration to database configuration to use
* @param factory an optional factory class that is called to instantiate a
* cursor when query is called, or null for default
* @param errorHandler the {@link DatabaseErrorHandler} obj to be used to handle corruption
* when sqlite reports database corruption
* @return the newly opened database
* @throws SQLiteException if the database cannot be opened
*/
public static SQLiteDatabase openDatabase(SQLiteDatabaseConfiguration configuration,
CursorFactory factory,
DatabaseErrorHandler errorHandler) {
SQLiteDatabase db = new SQLiteDatabase(configuration, factory, errorHandler);
db.open();
return db;
}
/**
* Equivalent to openDatabase(file.getPath(), factory, CREATE_IF_NECESSARY).
*/
public static SQLiteDatabase openOrCreateDatabase(File file, CursorFactory factory) {
return openOrCreateDatabase(file.getPath(), factory);
}
/**
* Equivalent to openDatabase(path, factory, CREATE_IF_NECESSARY).
*/
public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory) {
return openDatabase(path, factory, CREATE_IF_NECESSARY, null);
}
/**
* Equivalent to openDatabase(path, factory, CREATE_IF_NECESSARY, errorHandler).
*/
public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory,
DatabaseErrorHandler errorHandler) {
return openDatabase(path, factory, CREATE_IF_NECESSARY, errorHandler);
}
/**
* Deletes a database including its journal file and other auxiliary files
* that may have been created by the database engine.
*
* @param file The database file path.
* @return True if the database was successfully deleted.
*/
public static boolean deleteDatabase(File file) {
if (file == null) {
throw new IllegalArgumentException("file must not be null");
}
boolean deleted;
deleted = file.delete();
deleted |= new File(file.getPath() + "-journal").delete();
deleted |= new File(file.getPath() + "-shm").delete();
deleted |= new File(file.getPath() + "-wal").delete();
File dir = file.getParentFile();
if (dir != null) {
final String prefix = file.getName() + "-mj";
final FileFilter filter = new FileFilter() {
@Override
public boolean accept(File candidate) {
return candidate.getName().startsWith(prefix);
}
};
for (File masterJournal : dir.listFiles(filter)) {
deleted |= masterJournal.delete();
}
}
return deleted;
}
/**
* Reopens the database in read-write mode.
* If the database is already read-write, does nothing.
*
* @throws SQLiteException if the database could not be reopened as requested, in which
* case it remains open in read only mode.
* @throws IllegalStateException if the database is not open.
*
* @see #isReadOnly()
* @hide
*/
public void reopenReadWrite() {
synchronized (mLock) {
throwIfNotOpenLocked();
if (!isReadOnlyLocked()) {
return; // nothing to do
}
// Reopen the database in read-write mode.
final int oldOpenFlags = mConfigurationLocked.openFlags;
mConfigurationLocked.openFlags = (mConfigurationLocked.openFlags & ~OPEN_READONLY);
try {
mConnectionPoolLocked.reconfigure(mConfigurationLocked);
} catch (RuntimeException ex) {
mConfigurationLocked.openFlags = oldOpenFlags;
throw ex;
}
}
}
private void open() {
try {
if (!mConfigurationLocked.isInMemoryDb()
&& (mConfigurationLocked.openFlags & OPEN_CREATE) != 0) {
ensureFile(mConfigurationLocked.path);
}
try {
openInner();
} catch (SQLiteDatabaseCorruptException ex) {
onCorruption();
openInner();
}
} catch (SQLiteException ex) {
Log.e(TAG, "Failed to open database '" + getLabel() + "'.", ex);
close();
throw ex;
}
}
private static void ensureFile(String path) {
File file = new File(path);
if (!file.exists()) {
try {
if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) {
// Fixes #103: Check parent directory's existence before
// attempting to create.
Log.e(TAG, "Couldn't mkdirs " + file);
}
if (!file.createNewFile()) {
Log.e(TAG, "Couldn't create " + file);
}
} catch (IOException e) {
Log.e(TAG, "Couldn't ensure file " + file, e);
}
}
}
private void openInner() {
synchronized (mLock) {
assert mConnectionPoolLocked == null;
mConnectionPoolLocked = SQLiteConnectionPool.open(mConfigurationLocked);
mCloseGuardLocked.open("close");
}
synchronized (sActiveDatabases) {
sActiveDatabases.put(this, null);
}
}
/**
* Create a memory backed SQLite database. Its contents will be destroyed
* when the database is closed.
*
* <p>Sets the locale of the database to the the system's current locale.
* Call {@link #setLocale} if you would like something else.</p>
*
* @param factory an optional factory class that is called to instantiate a
* cursor when query is called
* @return a SQLiteDatabase object, or null if the database can't be created
*/
public static SQLiteDatabase create(CursorFactory factory) {
// This is a magic string with special meaning for SQLite.
return openDatabase(SQLiteDatabaseConfiguration.MEMORY_DB_PATH,
factory, CREATE_IF_NECESSARY);
}
/**
* Registers a CustomFunction callback as a function that can be called from
* SQLite database triggers.
*
* @param name the name of the sqlite3 function
* @param numArgs the number of arguments for the function
* @param function callback to call when the function is executed
* @hide
*/
@Deprecated
public void addCustomFunction(String name, int numArgs, CustomFunction function) {
// Create wrapper (also validates arguments).
SQLiteCustomFunction wrapper = new SQLiteCustomFunction(name, numArgs, function);
synchronized (mLock) {
throwIfNotOpenLocked();
mConfigurationLocked.customFunctions.add(wrapper);
try {
mConnectionPoolLocked.reconfigure(mConfigurationLocked);
} catch (RuntimeException ex) {
mConfigurationLocked.customFunctions.remove(wrapper);
throw ex;
}
}
}
/**
* Registers a Function callback as a function that can be called from
* SQLite database triggers.
*
* @param name the name of the sqlite3 function
* @param numArgs the number of arguments for the function
* @param function callback to call when the function is executed
* @hide
*/
public void addFunction(String name, int numArgs, Function function) {
addFunction(name, numArgs, function, 0);
}
/**
* Registers a Function callback as a function that can be called from
* SQLite database triggers.
*
* @param name the name of the sqlite3 function
* @param numArgs the number of arguments for the function
* @param function callback to call when the function is executed
* @param flags
* @hide
*/
public void addFunction(String name, int numArgs, Function function, int flags) {
// Create wrapper (also validates arguments).
SQLiteFunction wrapper = new SQLiteFunction(name, numArgs, function, flags);
synchronized (mLock) {
throwIfNotOpenLocked();
mConfigurationLocked.functions.add(wrapper);
try {
mConnectionPoolLocked.reconfigure(mConfigurationLocked);
} catch (RuntimeException ex) {
mConfigurationLocked.functions.remove(wrapper);
throw ex;
}
}
}
public void setUpdateHook(SQLiteUpdateHook updateHook) {
synchronized (mLock) {
throwIfNotOpenLocked();
mConfigurationLocked.sqliteUpdateHook = updateHook;
try {
mConnectionPoolLocked.reconfigure(mConfigurationLocked);
} catch (RuntimeException ex) {
mConfigurationLocked.sqliteUpdateHook = null;
throw ex;
}
}
}
/**
* Gets the database version.
*
* @return the database version
*/
@Override
public int getVersion() {
return ((Long) longForQuery("PRAGMA user_version;", null)).intValue();
}
/**
* Sets the database version.
*
* @param version the new database version
*/
@Override
public void setVersion(int version) {
execSQL("PRAGMA user_version = " + version);
}
/**
* Returns the maximum size the database may grow to.
*
* @return the new maximum database size
*/
@Override
public long getMaximumSize() {
long pageCount = longForQuery("PRAGMA max_page_count;", null);
return pageCount * getPageSize();
}
/**
* Sets the maximum size the database will grow to. The maximum size cannot
* be set below the current size.
*
* @param numBytes the maximum database size, in bytes
* @return the new maximum database size
*/
@Override
public long setMaximumSize(long numBytes) {
long pageSize = getPageSize();
long numPages = numBytes / pageSize;
// If numBytes isn't a multiple of pageSize, bump up a page
if ((numBytes % pageSize) != 0) {