1 /* 2 * Copyright (C) 2017 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 17 package androidx.room.integration.testapp.test; 18 19 import static org.hamcrest.CoreMatchers.is; 20 import static org.hamcrest.CoreMatchers.notNullValue; 21 import static org.hamcrest.MatcherAssert.assertThat; 22 23 import android.content.ContentValues; 24 import android.database.Cursor; 25 import android.database.SQLException; 26 import android.database.sqlite.SQLiteTransactionListener; 27 import android.os.CancellationSignal; 28 import android.util.Pair; 29 30 import androidx.arch.core.executor.ArchTaskExecutor; 31 import androidx.arch.core.executor.testing.CountingTaskExecutorRule; 32 import androidx.lifecycle.Lifecycle; 33 import androidx.lifecycle.LiveData; 34 import androidx.lifecycle.testing.TestLifecycleOwner; 35 import androidx.paging.DataSource; 36 import androidx.paging.LivePagedListBuilder; 37 import androidx.paging.PagedList; 38 import androidx.paging.PositionalDataSource; 39 import androidx.room.Dao; 40 import androidx.room.Database; 41 import androidx.room.Entity; 42 import androidx.room.Ignore; 43 import androidx.room.Insert; 44 import androidx.room.PrimaryKey; 45 import androidx.room.Query; 46 import androidx.room.Relation; 47 import androidx.room.Room; 48 import androidx.room.RoomDatabase; 49 import androidx.room.RoomWarnings; 50 import androidx.room.Transaction; 51 import androidx.room.paging.LimitOffsetDataSource; 52 import androidx.sqlite.db.SupportSQLiteDatabase; 53 import androidx.sqlite.db.SupportSQLiteOpenHelper; 54 import androidx.sqlite.db.SupportSQLiteQuery; 55 import androidx.sqlite.db.SupportSQLiteStatement; 56 import androidx.sqlite.db.framework.FrameworkSQLiteOpenHelperFactory; 57 import androidx.test.core.app.ApplicationProvider; 58 import androidx.test.filters.SmallTest; 59 60 import io.reactivex.Flowable; 61 import io.reactivex.Maybe; 62 import io.reactivex.Single; 63 import io.reactivex.observers.TestObserver; 64 import io.reactivex.schedulers.Schedulers; 65 import io.reactivex.subscribers.TestSubscriber; 66 67 import org.jspecify.annotations.NonNull; 68 import org.jspecify.annotations.Nullable; 69 import org.junit.After; 70 import org.junit.Before; 71 import org.junit.Rule; 72 import org.junit.Test; 73 import org.junit.runner.RunWith; 74 import org.junit.runners.Parameterized; 75 76 import java.io.IOException; 77 import java.util.Collections; 78 import java.util.List; 79 import java.util.Locale; 80 import java.util.concurrent.ExecutionException; 81 import java.util.concurrent.FutureTask; 82 import java.util.concurrent.TimeUnit; 83 import java.util.concurrent.TimeoutException; 84 import java.util.concurrent.atomic.AtomicInteger; 85 86 @SmallTest 87 @RunWith(Parameterized.class) 88 @SuppressWarnings("CheckReturnValue") 89 public class QueryTransactionTest { 90 @Rule 91 public CountingTaskExecutorRule countingTaskExecutorRule = new CountingTaskExecutorRule(); 92 private static final AtomicInteger sStartedTransactionCount = new AtomicInteger(0); 93 private TransactionDb mDb; 94 private final boolean mUseTransactionDao; 95 private Entity1Dao mDao; 96 private final TestLifecycleOwner mLifecycleOwner = new TestLifecycleOwner(); 97 98 @Parameterized.Parameters(name = "useTransaction_{0}") getParams()99 public static Boolean @NonNull [] getParams() { 100 return new Boolean[]{false, true}; 101 } 102 QueryTransactionTest(boolean useTransactionDao)103 public QueryTransactionTest(boolean useTransactionDao) { 104 mUseTransactionDao = useTransactionDao; 105 } 106 107 @Before initDb()108 public void initDb() { 109 resetTransactionCount(); 110 mDb = Room.inMemoryDatabaseBuilder( 111 ApplicationProvider.getApplicationContext(), 112 TransactionDb.class) 113 .openHelperFactory(new TransactionOpenHelperFactory()) 114 .build(); 115 mDao = mUseTransactionDao ? mDb.transactionDao() : mDb.dao(); 116 drain(); 117 } 118 119 @After closeDb()120 public void closeDb() { 121 mLifecycleOwner.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY); 122 drain(); 123 mDb.close(); 124 } 125 126 @Test readList()127 public void readList() { 128 mDao.insert(new Entity1(1, "foo")); 129 resetTransactionCount(); 130 131 int expectedTransactionCount = mUseTransactionDao ? 1 : 0; 132 List<Entity1> allEntities = mDao.allEntities(); 133 assertTransactionCount(allEntities, expectedTransactionCount); 134 } 135 136 @Test liveData()137 public void liveData() { 138 LiveData<List<Entity1>> listLiveData = mDao.liveData(); 139 observeForever(listLiveData); 140 drain(); 141 assertThat(listLiveData.getValue(), is(Collections.<Entity1>emptyList())); 142 143 resetTransactionCount(); 144 mDao.insert(new Entity1(1, "foo")); 145 drain(); 146 147 //noinspection ConstantConditions 148 assertThat(listLiveData.getValue().size(), is(1)); 149 int expectedTransactionCount = mUseTransactionDao ? 2 : 1; 150 assertTransactionCount(listLiveData.getValue(), expectedTransactionCount); 151 } 152 153 @Test flowable()154 public void flowable() { 155 Flowable<List<Entity1>> flowable = mDao.flowable(); 156 TestSubscriber<List<Entity1>> subscriber = observe(flowable); 157 drain(); 158 assertThat(subscriber.values().size(), is(1)); 159 160 resetTransactionCount(); 161 mDao.insert(new Entity1(1, "foo")); 162 drain(); 163 164 List<Entity1> allEntities = subscriber.values().get(1); 165 assertThat(allEntities.size(), is(1)); 166 int expectedTransactionCount = mUseTransactionDao ? 2 : 1; 167 assertTransactionCount(allEntities, expectedTransactionCount); 168 } 169 170 @Test maybe()171 public void maybe() { 172 mDao.insert(new Entity1(1, "foo")); 173 resetTransactionCount(); 174 175 int expectedTransactionCount = mUseTransactionDao ? 1 : 0; 176 Maybe<List<Entity1>> listMaybe = mDao.maybe(); 177 TestObserver<List<Entity1>> observer = observe(listMaybe); 178 drain(); 179 List<Entity1> allEntities = observer.values().get(0); 180 assertTransactionCount(allEntities, expectedTransactionCount); 181 } 182 183 @Test single()184 public void single() { 185 mDao.insert(new Entity1(1, "foo")); 186 resetTransactionCount(); 187 188 int expectedTransactionCount = mUseTransactionDao ? 1 : 0; 189 Single<List<Entity1>> listMaybe = mDao.single(); 190 TestObserver<List<Entity1>> observer = observe(listMaybe); 191 drain(); 192 List<Entity1> allEntities = observer.values().get(0); 193 assertTransactionCount(allEntities, expectedTransactionCount); 194 } 195 196 @Test relation()197 public void relation() { 198 mDao.insert(new Entity1(1, "foo")); 199 mDao.insert(new Child(1, 1)); 200 mDao.insert(new Child(2, 1)); 201 resetTransactionCount(); 202 203 List<Entity1WithChildren> result = mDao.withRelation(); 204 int expectedTransactionCount = mUseTransactionDao ? 1 : 0; 205 assertTransactionCountWithChildren(result, expectedTransactionCount); 206 } 207 208 @Test pagedList()209 public void pagedList() { 210 PagedList.Config config = new PagedList.Config.Builder() 211 .setPageSize(1) 212 .setInitialLoadSizeHint(3) 213 .setPrefetchDistance(2) 214 .setEnablePlaceholders(false) 215 .build(); 216 LiveData<PagedList<Entity1>> pagedList = 217 new LivePagedListBuilder<>(mDao.pagedList(), config).build(); 218 observeForever(pagedList); 219 drain(); 220 assertThat(sStartedTransactionCount.get(), is(1)); 221 222 mDao.insert(new Entity1(1, "foo")); 223 drain(); 224 //noinspection ConstantConditions 225 assertThat(pagedList.getValue().size(), is(1)); 226 assertTransactionCount(pagedList.getValue(), 3); 227 228 mDao.insert( 229 new Entity1(2, "bar"), 230 new Entity1(3, "baz"), 231 new Entity1(4, "bup")); 232 drain(); 233 assertThat(pagedList.getValue().size(), is(3)); 234 assertTransactionCount(pagedList.getValue(), 5); 235 236 // only loadRange is affected by transaction dao, since initial load requires transaction 237 pagedList.getValue().loadAround(2); 238 drain(); 239 assertThat(pagedList.getValue().size(), is(4)); 240 // note: we don't use assertTransactionCount here, since last item loaded separately 241 assertThat(sStartedTransactionCount.get(), is(mUseTransactionDao ? 7 : 5)); 242 } 243 244 @Test dataSourceRange()245 public void dataSourceRange() { 246 mDao.insert(new Entity1(2, "bar")); 247 drain(); 248 resetTransactionCount(); 249 LimitOffsetDataSource<Entity1> dataSource = 250 (LimitOffsetDataSource<Entity1>) mDao.dataSource(); 251 dataSource.loadRange(0, 10); 252 assertThat(sStartedTransactionCount.get(), is(mUseTransactionDao ? 1 : 0)); 253 } 254 255 @SuppressWarnings("deprecation") 256 @Test dataSourceInitial()257 public void dataSourceInitial() { 258 mDao.insert(new Entity1(2, "bar")); 259 drain(); 260 resetTransactionCount(); 261 LimitOffsetDataSource<Entity1> dataSource = 262 (LimitOffsetDataSource<Entity1>) mDao.dataSource(); 263 dataSource.loadInitial( 264 new PositionalDataSource.LoadInitialParams(0, 30, 10, true), 265 new PositionalDataSource.LoadInitialCallback<Entity1>() { 266 @Override 267 public void onResult(@NonNull List<? extends Entity1> data, int position, 268 int totalCount) {} 269 270 @Override 271 public void onResult(@NonNull List<? extends Entity1> data, int position) {} 272 }); 273 // always use a transaction, since we're loading count + initial data 274 assertThat(sStartedTransactionCount.get(), is(1)); 275 } 276 assertTransactionCount(List<Entity1> allEntities, int expectedTransactionCount)277 private void assertTransactionCount(List<Entity1> allEntities, int expectedTransactionCount) { 278 assertThat(sStartedTransactionCount.get(), is(expectedTransactionCount)); 279 assertThat(allEntities.isEmpty(), is(false)); 280 for (Entity1 entity1 : allEntities) { 281 assertThat(entity1.transactionId, is(expectedTransactionCount)); 282 } 283 } 284 assertTransactionCountWithChildren(List<Entity1WithChildren> allEntities, int expectedTransactionCount)285 private void assertTransactionCountWithChildren(List<Entity1WithChildren> allEntities, 286 int expectedTransactionCount) { 287 assertThat(sStartedTransactionCount.get(), is(expectedTransactionCount)); 288 assertThat(allEntities.isEmpty(), is(false)); 289 for (Entity1WithChildren entity1 : allEntities) { 290 assertThat(entity1.transactionId, is(expectedTransactionCount)); 291 assertThat(entity1.children, notNullValue()); 292 assertThat(entity1.children.isEmpty(), is(false)); 293 for (Child child : entity1.children) { 294 assertThat(child.transactionId, is(expectedTransactionCount)); 295 } 296 } 297 } 298 incrementTransactionCount()299 private static void incrementTransactionCount() { 300 // When incrementing the transaction count, ignore those coming from the refresh 301 // in the invalidation tracker. 302 StackTraceElement[] stack = Thread.currentThread().getStackTrace(); 303 for (StackTraceElement element : stack) { 304 String fileName = element.getFileName(); 305 if (fileName != null && fileName.equals("InvalidationTracker.kt")) { 306 return; 307 } 308 } 309 sStartedTransactionCount.incrementAndGet(); 310 } 311 resetTransactionCount()312 private void resetTransactionCount() { 313 sStartedTransactionCount.set(0); 314 } 315 drain()316 private void drain() { 317 try { 318 countingTaskExecutorRule.drainTasks(3, TimeUnit.SECONDS); 319 } catch (InterruptedException e) { 320 throw new AssertionError("interrupted", e); 321 } catch (TimeoutException e) { 322 throw new AssertionError("drain timed out", e); 323 } 324 } 325 observe(final Flowable<T> flowable)326 private <T> TestSubscriber<T> observe(final Flowable<T> flowable) { 327 TestSubscriber<T> subscriber = new TestSubscriber<>(); 328 flowable.observeOn(Schedulers.from(ArchTaskExecutor.getMainThreadExecutor())) 329 .subscribeWith(subscriber); 330 return subscriber; 331 } 332 observe(final Maybe<T> maybe)333 private <T> TestObserver<T> observe(final Maybe<T> maybe) { 334 TestObserver<T> observer = new TestObserver<>(); 335 maybe.observeOn(Schedulers.from(ArchTaskExecutor.getMainThreadExecutor())) 336 .subscribeWith(observer); 337 return observer; 338 } 339 observe(final Single<T> single)340 private <T> TestObserver<T> observe(final Single<T> single) { 341 TestObserver<T> observer = new TestObserver<>(); 342 single.observeOn(Schedulers.from(ArchTaskExecutor.getMainThreadExecutor())) 343 .subscribeWith(observer); 344 return observer; 345 } 346 observeForever(final LiveData<T> liveData)347 private <T> void observeForever(final LiveData<T> liveData) { 348 FutureTask<Void> futureTask = new FutureTask<>(() -> { 349 liveData.observe(mLifecycleOwner, t -> { 350 }); 351 return null; 352 }); 353 ArchTaskExecutor.getMainThreadExecutor().execute(futureTask); 354 try { 355 futureTask.get(); 356 } catch (InterruptedException e) { 357 throw new AssertionError("interrupted", e); 358 } catch (ExecutionException e) { 359 throw new AssertionError("execution error", e); 360 } 361 } 362 363 @SuppressWarnings("WeakerAccess") 364 static class Entity1WithChildren extends Entity1 { 365 @Relation(entity = Child.class, parentColumn = "id", 366 entityColumn = "entity1Id") 367 public List<Child> children; 368 Entity1WithChildren(int id, String value)369 Entity1WithChildren(int id, String value) { 370 super(id, value); 371 } 372 } 373 374 @SuppressWarnings("WeakerAccess") 375 @Entity 376 static class Child { 377 @PrimaryKey(autoGenerate = true) 378 public int id; 379 public int entity1Id; 380 @Ignore 381 public final int transactionId = sStartedTransactionCount.get(); 382 Child(int id, int entity1Id)383 Child(int id, int entity1Id) { 384 this.id = id; 385 this.entity1Id = entity1Id; 386 } 387 } 388 389 @SuppressWarnings("WeakerAccess") 390 @Entity 391 static class Entity1 { 392 @PrimaryKey(autoGenerate = true) 393 public int id; 394 public String value; 395 @Ignore 396 public final int transactionId = sStartedTransactionCount.get(); 397 Entity1(int id, String value)398 Entity1(int id, String value) { 399 this.id = id; 400 this.value = value; 401 } 402 } 403 404 // we don't support dao inheritance for queries so for now, go with this 405 interface Entity1Dao { 406 String SELECT_ALL = "select * from Entity1"; 407 allEntities()408 List<Entity1> allEntities(); 409 flowable()410 Flowable<List<Entity1>> flowable(); 411 maybe()412 Maybe<List<Entity1>> maybe(); 413 single()414 Single<List<Entity1>> single(); 415 liveData()416 LiveData<List<Entity1>> liveData(); 417 withRelation()418 List<Entity1WithChildren> withRelation(); 419 pagedList()420 DataSource.Factory<Integer, Entity1> pagedList(); 421 dataSource()422 PositionalDataSource<Entity1> dataSource(); 423 424 @Insert insert(Entity1 entity1)425 void insert(Entity1 entity1); 426 427 @Insert insert(Entity1... entities)428 void insert(Entity1... entities); 429 430 @Insert insert(Child entity1)431 void insert(Child entity1); 432 } 433 434 @Dao 435 interface EntityDao extends Entity1Dao { 436 @Override 437 @Query(SELECT_ALL) allEntities()438 List<Entity1> allEntities(); 439 440 @Override 441 @Query(SELECT_ALL) flowable()442 Flowable<List<Entity1>> flowable(); 443 444 @Override 445 @Query(SELECT_ALL) liveData()446 LiveData<List<Entity1>> liveData(); 447 448 @Override 449 @Query(SELECT_ALL) maybe()450 Maybe<List<Entity1>> maybe(); 451 452 @Override 453 @Query(SELECT_ALL) single()454 Single<List<Entity1>> single(); 455 456 @Override 457 @Query(SELECT_ALL) 458 @SuppressWarnings(RoomWarnings.RELATION_QUERY_WITHOUT_TRANSACTION) withRelation()459 List<Entity1WithChildren> withRelation(); 460 461 @Override 462 @Query(SELECT_ALL) pagedList()463 DataSource.Factory<Integer, Entity1> pagedList(); 464 465 @Override 466 @Query(SELECT_ALL) dataSource()467 PositionalDataSource<Entity1> dataSource(); 468 } 469 470 @Dao 471 interface TransactionDao extends Entity1Dao { 472 @Override 473 @Transaction 474 @Query(SELECT_ALL) allEntities()475 List<Entity1> allEntities(); 476 477 @Override 478 @Transaction 479 @Query(SELECT_ALL) flowable()480 Flowable<List<Entity1>> flowable(); 481 482 @Override 483 @Transaction 484 @Query(SELECT_ALL) liveData()485 LiveData<List<Entity1>> liveData(); 486 487 @Override 488 @Transaction 489 @Query(SELECT_ALL) maybe()490 Maybe<List<Entity1>> maybe(); 491 492 @Override 493 @Transaction 494 @Query(SELECT_ALL) single()495 Single<List<Entity1>> single(); 496 497 @Override 498 @Transaction 499 @Query(SELECT_ALL) withRelation()500 List<Entity1WithChildren> withRelation(); 501 502 @Override 503 @Transaction 504 @Query(SELECT_ALL) pagedList()505 DataSource.Factory<Integer, Entity1> pagedList(); 506 507 @Override 508 @Transaction 509 @Query(SELECT_ALL) dataSource()510 PositionalDataSource<Entity1> dataSource(); 511 } 512 513 @Database(version = 1, entities = {Entity1.class, Child.class}, exportSchema = false) 514 @SuppressWarnings("deprecation") 515 abstract static class TransactionDb extends RoomDatabase { dao()516 abstract EntityDao dao(); 517 transactionDao()518 abstract TransactionDao transactionDao(); 519 } 520 521 static class TransactionOpenHelperFactory implements SupportSQLiteOpenHelper.Factory { 522 523 private final SupportSQLiteOpenHelper.Factory mDelegate = 524 new FrameworkSQLiteOpenHelperFactory(); 525 526 @Override create( SupportSQLiteOpenHelper.@onNull Configuration configuration)527 public @NonNull SupportSQLiteOpenHelper create( 528 SupportSQLiteOpenHelper.@NonNull Configuration configuration) { 529 return new TransactionSupportSQLiteOpenHelper(mDelegate.create(configuration)); 530 } 531 } 532 533 static class TransactionSupportSQLiteOpenHelper implements SupportSQLiteOpenHelper { 534 private final SupportSQLiteOpenHelper mDelegate; 535 TransactionSupportSQLiteOpenHelper(SupportSQLiteOpenHelper delegate)536 TransactionSupportSQLiteOpenHelper(SupportSQLiteOpenHelper delegate) { 537 this.mDelegate = delegate; 538 } 539 540 @Override getDatabaseName()541 public @Nullable String getDatabaseName() { 542 return mDelegate.getDatabaseName(); 543 } 544 545 @Override setWriteAheadLoggingEnabled(boolean enabled)546 public void setWriteAheadLoggingEnabled(boolean enabled) { 547 mDelegate.setWriteAheadLoggingEnabled(enabled); 548 } 549 550 @Override getWritableDatabase()551 public @NonNull SupportSQLiteDatabase getWritableDatabase() { 552 return new TransactionSupportSQLiteDatabase(mDelegate.getWritableDatabase()); 553 } 554 555 @Override getReadableDatabase()556 public @NonNull SupportSQLiteDatabase getReadableDatabase() { 557 return new TransactionSupportSQLiteDatabase(mDelegate.getReadableDatabase()); 558 } 559 560 @Override close()561 public void close() { 562 mDelegate.close(); 563 } 564 } 565 566 static class TransactionSupportSQLiteDatabase implements SupportSQLiteDatabase { 567 private final SupportSQLiteDatabase mDelegate; 568 TransactionSupportSQLiteDatabase(SupportSQLiteDatabase delegate)569 TransactionSupportSQLiteDatabase(SupportSQLiteDatabase delegate) { 570 this.mDelegate = delegate; 571 } 572 573 @Override compileStatement(@onNull String sql)574 public @NonNull SupportSQLiteStatement compileStatement(@NonNull String sql) { 575 return mDelegate.compileStatement(sql); 576 } 577 578 @Override beginTransaction()579 public void beginTransaction() { 580 mDelegate.beginTransaction(); 581 incrementTransactionCount(); 582 } 583 584 @Override beginTransactionNonExclusive()585 public void beginTransactionNonExclusive() { 586 mDelegate.beginTransactionNonExclusive(); 587 incrementTransactionCount(); 588 } 589 590 @Override beginTransactionReadOnly()591 public void beginTransactionReadOnly() { 592 mDelegate.beginTransactionReadOnly(); 593 incrementTransactionCount(); 594 } 595 596 @Override beginTransactionWithListener( @onNull SQLiteTransactionListener transactionListener)597 public void beginTransactionWithListener( 598 @NonNull SQLiteTransactionListener transactionListener) { 599 mDelegate.beginTransactionWithListener(transactionListener); 600 incrementTransactionCount(); 601 } 602 603 @Override beginTransactionWithListenerNonExclusive( @onNull SQLiteTransactionListener transactionListener)604 public void beginTransactionWithListenerNonExclusive( 605 @NonNull SQLiteTransactionListener transactionListener) { 606 mDelegate.beginTransactionWithListenerNonExclusive(transactionListener); 607 incrementTransactionCount(); 608 } 609 610 @Override beginTransactionWithListenerReadOnly( @onNull SQLiteTransactionListener transactionListener)611 public void beginTransactionWithListenerReadOnly( 612 @NonNull SQLiteTransactionListener transactionListener) { 613 mDelegate.beginTransactionWithListenerReadOnly(transactionListener); 614 incrementTransactionCount(); 615 } 616 617 @Override endTransaction()618 public void endTransaction() { 619 mDelegate.endTransaction(); 620 } 621 622 @Override setTransactionSuccessful()623 public void setTransactionSuccessful() { 624 mDelegate.setTransactionSuccessful(); 625 } 626 627 @Override inTransaction()628 public boolean inTransaction() { 629 return mDelegate.inTransaction(); 630 } 631 632 @Override isDbLockedByCurrentThread()633 public boolean isDbLockedByCurrentThread() { 634 return mDelegate.isDbLockedByCurrentThread(); 635 } 636 637 @Override yieldIfContendedSafely()638 public boolean yieldIfContendedSafely() { 639 return mDelegate.yieldIfContendedSafely(); 640 } 641 642 @Override yieldIfContendedSafely(long sleepAfterYieldDelayMillis)643 public boolean yieldIfContendedSafely(long sleepAfterYieldDelayMillis) { 644 return mDelegate.yieldIfContendedSafely(sleepAfterYieldDelayMillis); 645 } 646 647 @Override isExecPerConnectionSQLSupported()648 public boolean isExecPerConnectionSQLSupported() { 649 return mDelegate.isExecPerConnectionSQLSupported(); 650 } 651 652 @Override execPerConnectionSQL(@onNull String sql, Object @Nullable [] bindArgs)653 public void execPerConnectionSQL(@NonNull String sql, Object @Nullable [] bindArgs) { 654 mDelegate.execPerConnectionSQL(sql, bindArgs); 655 } 656 657 @Override getVersion()658 public int getVersion() { 659 return mDelegate.getVersion(); 660 } 661 662 @Override setVersion(int i)663 public void setVersion(int i) { 664 mDelegate.setVersion(i); 665 } 666 667 @Override getMaximumSize()668 public long getMaximumSize() { 669 return mDelegate.getMaximumSize(); 670 } 671 672 @Override setMaximumSize(long numBytes)673 public long setMaximumSize(long numBytes) { 674 return mDelegate.setMaximumSize(numBytes); 675 } 676 677 @Override getPageSize()678 public long getPageSize() { 679 return mDelegate.getPageSize(); 680 } 681 682 @Override setPageSize(long l)683 public void setPageSize(long l) { 684 mDelegate.setPageSize(l); 685 } 686 687 @Override query(@onNull String query)688 public @NonNull Cursor query(@NonNull String query) { 689 return mDelegate.query(query); 690 } 691 692 @Override query(@onNull String query, Object @NonNull [] bindArgs)693 public @NonNull Cursor query(@NonNull String query, Object @NonNull [] bindArgs) { 694 return mDelegate.query(query, bindArgs); 695 } 696 697 @Override query(@onNull SupportSQLiteQuery query)698 public @NonNull Cursor query(@NonNull SupportSQLiteQuery query) { 699 return mDelegate.query(query); 700 } 701 702 @Override query(@onNull SupportSQLiteQuery query, @Nullable CancellationSignal cancellationSignal)703 public @NonNull Cursor query(@NonNull SupportSQLiteQuery query, 704 @Nullable CancellationSignal cancellationSignal) { 705 return mDelegate.query(query, cancellationSignal); 706 } 707 708 @Override insert(@onNull String table, int conflictAlgorithm, @NonNull ContentValues values)709 public long insert(@NonNull String table, int conflictAlgorithm, 710 @NonNull ContentValues values) throws SQLException { 711 return mDelegate.insert(table, conflictAlgorithm, values); 712 } 713 714 @Override delete(@onNull String table, @Nullable String whereClause, Object @Nullable [] whereArgs)715 public int delete(@NonNull String table, @Nullable String whereClause, 716 Object @Nullable [] whereArgs) { 717 return mDelegate.delete(table, whereClause, whereArgs); 718 } 719 720 @Override update(@onNull String table, int conflictAlgorithm, @NonNull ContentValues values, @Nullable String whereClause, Object @Nullable [] whereArgs)721 public int update(@NonNull String table, int conflictAlgorithm, 722 @NonNull ContentValues values, @Nullable String whereClause, 723 Object @Nullable [] whereArgs) { 724 return mDelegate.update(table, conflictAlgorithm, values, whereClause, whereArgs); 725 } 726 727 @Override execSQL(@onNull String sql)728 public void execSQL(@NonNull String sql) throws SQLException { 729 mDelegate.execSQL(sql); 730 } 731 732 @Override execSQL(@onNull String sql, Object @NonNull [] bindArgs)733 public void execSQL(@NonNull String sql, Object @NonNull [] bindArgs) throws SQLException { 734 mDelegate.execSQL(sql, bindArgs); 735 } 736 737 @Override isReadOnly()738 public boolean isReadOnly() { 739 return mDelegate.isReadOnly(); 740 } 741 742 @Override isOpen()743 public boolean isOpen() { 744 return mDelegate.isOpen(); 745 } 746 747 @Override needUpgrade(int newVersion)748 public boolean needUpgrade(int newVersion) { 749 return mDelegate.needUpgrade(newVersion); 750 } 751 752 @Override getPath()753 public @Nullable String getPath() { 754 return mDelegate.getPath(); 755 } 756 757 @Override setLocale(@onNull Locale locale)758 public void setLocale(@NonNull Locale locale) { 759 mDelegate.setLocale(locale); 760 } 761 762 @Override setMaxSqlCacheSize(int cacheSize)763 public void setMaxSqlCacheSize(int cacheSize) { 764 mDelegate.setMaxSqlCacheSize(cacheSize); 765 } 766 767 @Override setForeignKeyConstraintsEnabled(boolean enabled)768 public void setForeignKeyConstraintsEnabled(boolean enabled) { 769 mDelegate.setForeignKeyConstraintsEnabled(enabled); 770 } 771 772 @Override enableWriteAheadLogging()773 public boolean enableWriteAheadLogging() { 774 return mDelegate.enableWriteAheadLogging(); 775 } 776 777 @Override disableWriteAheadLogging()778 public void disableWriteAheadLogging() { 779 mDelegate.disableWriteAheadLogging(); 780 } 781 782 @Override isWriteAheadLoggingEnabled()783 public boolean isWriteAheadLoggingEnabled() { 784 return mDelegate.isWriteAheadLoggingEnabled(); 785 } 786 787 @Override getAttachedDbs()788 public @Nullable List<Pair<String, String>> getAttachedDbs() { 789 return mDelegate.getAttachedDbs(); 790 } 791 792 @Override isDatabaseIntegrityOk()793 public boolean isDatabaseIntegrityOk() { 794 return mDelegate.isDatabaseIntegrityOk(); 795 } 796 797 @Override close()798 public void close() throws IOException { 799 mDelegate.close(); 800 } 801 } 802 } 803