├── .github └── workflows │ └── ci.yml ├── .gitignore ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── jitpack.yml ├── settings.gradle └── sqlite-android ├── .gitignore ├── build.gradle ├── gradle.properties ├── proguard-rules.pro └── src ├── androidTest └── java │ └── io │ └── requery │ └── android │ └── database │ ├── CursorWindowTest.java │ ├── DatabaseCursorTest.java │ ├── DatabaseErrorHandlerTest.java │ ├── DatabaseGeneralTest.java │ ├── DatabaseLocaleTest.java │ ├── DatabaseStatementTest.java │ ├── NewDatabasePerformanceTestSuite.java │ ├── NewDatabasePerformanceTests.java │ ├── benchmark │ └── Benchmark.java │ └── sqlite │ └── SQLiteStatementTypeTest.java └── main ├── AndroidManifest.xml ├── java └── io │ └── requery │ └── android │ └── database │ ├── AbstractCursor.java │ ├── AbstractWindowedCursor.java │ ├── CursorWindow.java │ ├── CursorWindowAllocationException.java │ ├── DatabaseErrorHandler.java │ ├── DefaultDatabaseErrorHandler.java │ └── sqlite │ ├── CloseGuard.java │ ├── RequerySQLiteOpenHelperFactory.java │ ├── SQLiteClosable.java │ ├── SQLiteConnection.java │ ├── SQLiteConnectionPool.java │ ├── SQLiteCursor.java │ ├── SQLiteCursorDriver.java │ ├── SQLiteCustomExtension.java │ ├── SQLiteCustomFunction.java │ ├── SQLiteDatabase.java │ ├── SQLiteDatabaseConfiguration.java │ ├── SQLiteDebug.java │ ├── SQLiteDirectCursorDriver.java │ ├── SQLiteFunction.java │ ├── SQLiteGlobal.java │ ├── SQLiteOpenHelper.java │ ├── SQLiteProgram.java │ ├── SQLiteQuery.java │ ├── SQLiteQueryBuilder.java │ ├── SQLiteSession.java │ ├── SQLiteStatement.java │ ├── SQLiteStatementInfo.java │ ├── SQLiteStatementType.java │ └── SQLiteUpdateHook.java └── jni ├── Android.mk ├── Application.mk └── sqlite ├── ALog-priv.h ├── Android.mk ├── CursorWindow.cpp ├── CursorWindow.h ├── Errors.h ├── JNIHelp.cpp ├── JNIHelp.h ├── JNIString.cpp ├── README ├── android_database_CursorWindow.cpp ├── android_database_SQLiteCommon.cpp ├── android_database_SQLiteCommon.h ├── android_database_SQLiteConnection.cpp ├── android_database_SQLiteDebug.cpp ├── android_database_SQLiteFunction.cpp └── android_database_SQLiteGlobal.cpp /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | permissions: 4 | contents: read 5 | 6 | on: [push, pull_request] 7 | 8 | jobs: 9 | ci: 10 | runs-on: macos-latest 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | api-level: [29] 15 | steps: 16 | - uses: actions/checkout@v4 17 | with: 18 | fetch-depth: 1 19 | 20 | - name: Set up JDK 17 21 | uses: actions/setup-java@v4 22 | with: 23 | java-version: "17" 24 | distribution: "adopt" 25 | 26 | - name: Run Android tests 27 | uses: reactivecircus/android-emulator-runner@v2 28 | with: 29 | api-level: ${{ matrix.api-level }} 30 | emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none 31 | disable-animations: true 32 | arch: x86 33 | script: ./gradlew connectedAndroidTest --stacktrace 34 | 35 | - name: Upload results 36 | if: ${{ always() }} 37 | uses: actions/upload-artifact@v4 38 | with: 39 | name: instrumentation-test-results ${{ matrix.api-level }} 40 | path: ./**/build/reports/androidTests/connected/** 41 | 42 | - name: Upload snapshot (master only) 43 | env: 44 | ORG_GRADLE_PROJECT_mavenCentralUsername: ${{ secrets.SonatypeUsername }} 45 | ORG_GRADLE_PROJECT_mavenCentralPassword: ${{ secrets.SonatypePassword }} 46 | run: ./gradlew publish 47 | if: github.ref == 'refs/heads/master' && github.event_name == 'push' 48 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .idea 3 | .gradle 4 | /local.properties 5 | /.idea/workspace.xml 6 | /.idea/libraries 7 | .DS_Store 8 | /build 9 | /captures 10 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Change Log 2 | ========== 3 | 4 | ## 3.48.0 5 | - [SQLite 3.49.0](https://www.sqlite.org/releaselog/3_49_0.html) 6 | - [SQLite 3.48.0](https://www.sqlite.org/releaselog/3_48_0.html) 7 | - [SQLite 3.47.2](https://www.sqlite.org/releaselog/3_47_2.html) 8 | - [SQLite 3.47.1](https://www.sqlite.org/releaselog/3_47_2.html) 9 | - [SQLite 3.47.0](https://www.sqlite.org/releaselog/3_47_0.html) 10 | - [SQLite 3.46.1](https://www.sqlite.org/releaselog/3_46_1.html) 11 | - [SQLite 3.46.0](https://www.sqlite.org/releaselog/3_46_0.html) 12 | - [SQLite 3.45.3](https://www.sqlite.org/releaselog/3_45_3.html) 13 | - [SQLite 3.45.2](https://www.sqlite.org/releaselog/3_45_2.html) 14 | - [SQLite 3.45.1](https://www.sqlite.org/releaselog/3_45_2.html) 15 | 16 | ## 3.45.0 17 | - [SQLite 3.45.0](https://www.sqlite.org/releaselog/3_45_0.html) 18 | - [SQLite 3.44.2](https://www.sqlite.org/releaselog/3_44_2.html) 19 | - [SQLite 3.44.1](https://www.sqlite.org/releaselog/3_44_1.html) 20 | - [SQLite 3.44.0](https://www.sqlite.org/releaselog/3_44_0.html) 21 | - [SQLite 3.43.2](https://www.sqlite.org/releaselog/3_43_2.html) 22 | - [SQLite 3.43.1](https://www.sqlite.org/releaselog/3_43_1.html) 23 | 24 | ## 3.43.0 25 | - [SQLite 3.43.0](https://www.sqlite.org/releaselog/3_43_0.html) 26 | 27 | ## 3.42.0 28 | - [SQLite 3.42.0](https://www.sqlite.org/releaselog/3_42_0.html) 29 | - [SQLite 3.41.2](https://www.sqlite.org/releaselog/3_41_2.html) 30 | 31 | ## 3.41.1 32 | - [SQLite 3.41.1](https://www.sqlite.org/releaselog/3_41_1.html) 33 | - [SQLite 3.41.0](https://www.sqlite.org/releaselog/3_41_0.html) 34 | - Fix getSqlStatementType statement type detection 35 | 36 | ## 3.40.1 37 | - [SQLite 3.40.1](https://www.sqlite.org/releaselog/3_40_1.html) 38 | - [SQLite 3.40.0](https://www.sqlite.org/releaselog/3_40_0.html) 39 | - 40 | ## 3.39.2 41 | - [SQLite 3.39.2](https://www.sqlite.org/releaselog/3_39_2.html) 42 | - [SQLite 3.39.1](https://www.sqlite.org/releaselog/3_39_1.html) 43 | 44 | ## 3.39.0 45 | - [SQLite 3.39.0](https://www.sqlite.org/releaselog/3_39_0.html) 46 | - 47 | ## 3.38.5 48 | - [SQLite 3.38.5](https://www.sqlite.org/releaselog/3_38_5.html) 49 | 50 | ## 3.38.4 51 | - [SQLite 3.38.4](https://www.sqlite.org/releaselog/3_38_4.html) 52 | - [SQLite 3.38.3](https://www.sqlite.org/releaselog/3_38_3.html) 53 | - [SQLite 3.38.2](https://www.sqlite.org/releaselog/3_38_2.html) 54 | - [SQLite 3.38.1](https://www.sqlite.org/releaselog/3_38_1.html) 55 | - [SQLite 3.38.0](https://www.sqlite.org/releaselog/3_38_0.html) 56 | 57 | ## 3.37.1 58 | - [SQLite 3.37.2](http://sqlite.org/releaselog/3_37_2.html) 59 | - [SQLite 3.37.1](http://sqlite.org/releaselog/3_37_1.html) 60 | - [SQLite 3.37.0](http://sqlite.org/releaselog/3_37_0.html) 61 | 62 | ## 3.36.0 63 | - [SQLite 3.36.0](https://sqlite.org/releaselog/3_36_0.html) 64 | 65 | ## 3.35.5 66 | - [SQLite 3.35.5](http://sqlite.org/releaselog/3_35_5.html) 67 | 68 | ## 3.35.4 69 | - [SQLite 3.35.4](http://sqlite.org/releaselog/3_35_4.html) 70 | - [SQLite 3.35.3](http://sqlite.org/releaselog/3_35_3.html) 71 | - [SQLite 3.35.2](http://sqlite.org/releaselog/3_35_2.html) 72 | 73 | ## 3.35.1 74 | - [SQLite 3.35.1](http://sqlite.org/releaselog/3_35_1.html) 75 | - [SQLite 3.35.0](http://sqlite.org/releaselog/3_35_0.html) 76 | 77 | ## 3.34.1 78 | - [SQLite 3.34.1](http://sqlite.org/releaselog/3_34_1.html) 79 | 80 | ## 3.34.0 81 | - [SQLite 3.34.0](http://sqlite.org/releaselog/3_34_0.html) 82 | 83 | ## 3.33.0 84 | - [SQLite 3.33.0](http://sqlite.org/releaselog/3_33_0.html) 85 | 86 | ## 3.32.2 87 | - [SQLite 3.32.3](http://sqlite.org/releaselog/3_32_3.html) 88 | - [SQLite 3.32.2](http://sqlite.org/releaselog/3_32_2.html) 89 | - [SQLite 3.32.1](http://sqlite.org/releaselog/3_32_1.html) 90 | - [SQLite 3.32.0](http://sqlite.org/releaselog/3_32_0.html) 91 | 92 | ## 3.31.0 93 | - [SQLite 3.31.0](http://sqlite.org/releaselog/3_31_0.html) 94 | 95 | ## 3.30.1 96 | - [SQLite 3.30.0](http://sqlite.org/releaselog/3_30_0.html) 97 | - [SQLite 3.30.1](http://sqlite.org/releaselog/3_30_1.html) 98 | 99 | ## 3.29.0 100 | - [SQLite 3.29.0](http://sqlite.org/releaselog/3_29_0.html) 101 | 102 | ## 3.28.0 103 | - [SQLite 3.28.0](http://sqlite.org/releaselog/3_28_0.html) 104 | 105 | ## 3.27.2 106 | - [SQLite 3.27.2](http://sqlite.org/releaselog/3_27_2.html) 107 | 108 | ## 3.27.1 109 | - [SQLite 3.27.1](http://sqlite.org/releaselog/3_27_1.html) 110 | 111 | ## 3.26.0 112 | - [SQLite 3.26.0](http://sqlite.org/releaselog/3_26_0.html) 113 | - Update `org.apache.httpcomponents:httpclient` dependency to 4.5.6 114 | - Update `com.android.tools.build:gradle` dependency to 3.2.1 115 | - Update gradle to 4.10.2 116 | - Update `androidx.core:core` dependency to 1.0.1 117 | - Switch to androidx for test libraries 118 | 119 | ## 3.25.3 120 | 121 | - [SQLite 3.25.3](http://sqlite.org/releaselog/3_25_3.html) 122 | - Switch to androidx libraries 123 | - Remove mips abi support 124 | - Fix hash collision in SQLiteCursor 125 | 126 | ## 3.25.2 127 | 128 | - [SQLite 3.25.2](http://sqlite.org/releaselog/3_25_2.html) 129 | 130 | ## 3.25.1 131 | 132 | - [SQLite 3.25.1](http://sqlite.org/releaselog/3_25_1.html) 133 | 134 | ## 3.24.0 135 | 136 | - [SQLite 3.24.0](http://sqlite.org/releaselog/3_24_0.html) 137 | 138 | ## 3.23.1 139 | 140 | - [SQLite 3.23.1](http://sqlite.org/releaselog/3_23_1.html) 141 | - Add new enhanced custom function interfaces and methods 142 | 143 | ## 3.22.0 144 | 145 | - [SQLite 3.22.0](https://sqlite.org/releaselog/3_22_0.html) 146 | - Update `android.arch.persistence:db` dependency to `1.0.0` 147 | - Improved error messaging on loading custom extensions 148 | - Increase CursorWindow size to match AOSP 149 | - Add custom extension and function loading to new SupportSQLiteDatabase API 150 | 151 | ## 3.21.0 152 | 153 | - [SQLite 3.21.0](https://sqlite.org/releaselog/3_21_0.html) 154 | - Support SupportSQLiteDatabase interfaces provided in `android.arch.persistence:db` 155 | - Support MIPS abi 156 | - Fix local reference overflow when using custom functions 157 | 158 | ## 3.20.1 159 | 160 | - [SQLite 3.20.1](https://sqlite.org/releaselog/3_20_1.html) 161 | 162 | ## 3.20.0 163 | 164 | - [SQLite 3.20.0](https://sqlite.org/releaselog/3_20_0.html) 165 | 166 | ## 3.19.3 167 | 168 | - [SQLite 3.19.3](https://sqlite.org/releaselog/3_19_3.html) 169 | - Add flags for enhanced FTS query syntax 170 | 171 | ## 3.19.2 172 | 173 | - [SQLite 3.19.2](https://sqlite.org/releaselog/3_19_2.html) 174 | 175 | ## 3.18.0 176 | 177 | - [SQLite 3.18.0](https://sqlite.org/releaselog/3_18_0.html) 178 | - Fix conversion of strings larger than the available stack size (#35) 179 | 180 | ## 3.17.0 181 | 182 | - [SQLite 3.17.0](https://sqlite.org/releaselog/3_17_0.html) 183 | 184 | ## 3.16.2 185 | 186 | - [SQLite 3.16.2](https://sqlite.org/releaselog/3_16_2.html) 187 | - Support additional SQLite open flags 188 | - Add methods from DatabaseUtils into base classes 189 | 190 | ## 3.16.0 191 | 192 | - [SQLite 3.16.0](https://sqlite.org/releaselog/3_16_0.html) 193 | 194 | ## 3.15.1 195 | 196 | - [SQLite 3.15.1](https://sqlite.org/releaselog/3_15_1.html) 197 | 198 | ## 3.15.0 199 | 200 | - [SQLite 3.15.0](https://www.sqlite.org/releaselog/3_15_0.html) 201 | 202 | ## 3.14.2 203 | 204 | - [SQLite 3.14.2](https://www.sqlite.org/releaselog/3_14_2.html) 205 | - Removed code that disabled WAL when executing a ATTACH statement 206 | 207 | ## 3.14.1 208 | 209 | - [SQLite 3.14.1](https://www.sqlite.org/releaselog/3_14_1.html) 210 | 211 | ## 3.14.0 212 | 213 | - [SQLite 3.14.0](https://www.sqlite.org/releaselog/3_14.html) 214 | - Support return value in `SQLiteDatabase.CustomFunction` 215 | - Support `Object[]` array in `SQLiteDatabase` query methods 216 | 217 | ## 3.13.0-3 218 | 219 | - Support x86_64 target 220 | 221 | ## 3.13.0-2 222 | 223 | - More proguard rules fixes 224 | 225 | ## 3.13.0-1 226 | 227 | - Fix proguard rules file 228 | 229 | ## 3.13.0 230 | 231 | - [SQLite 3.13.0](https://www.sqlite.org/releaselog/3_13_0.html) 232 | - Updated proguard rules 233 | 234 | ## 3.12.2-2 235 | 236 | - Minimum API level supported is now 9 (Gingerbread) previously was 15 (ICS) 237 | - Fix missing support lib dependency missing from maven POM publish 238 | 239 | ## 3.12.2-1 240 | 241 | - Fix native error code SQLITE_CANTOPEN(14) creating a database for the first time 242 | - Fix SQLiteOpenHelper setWriteAheadLoggingEnabled flag not passed to openDatabase 243 | - Change SQLiteGlobal default config values to match Android defaults 244 | 245 | ## 3.12.2 246 | 247 | - [SQLite 3.12.2](https://www.sqlite.org/releaselog/3_12_2.html) 248 | 249 | ## 3.12.1-1 250 | 251 | - Fix CursorWindow deactivate/close 252 | - Fix Cursor setNotificationUri not working 253 | 254 | ## 3.12.1 255 | 256 | - [SQLite 3.12.1](https://www.sqlite.org/releaselog/3_12_1.html) 257 | - Support runtime extension loading 258 | - Support custom functions 259 | - `beginTransactionDeferred`/`beginTransactionWithListenerDeferred` added 260 | - `CancellationSignal` dependency changed to support-v4 from app-compat 261 | - Sources included in artifacts 262 | 263 | ## 3.12.0 264 | 265 | - Initial release with SQLite 3.12.0 266 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing 2 | ============ 3 | 4 | Contributions are welcome. In order to contribute first fork the repository and create a pull 5 | request. When submitting a PR the request will be automatically built and tested in a CI 6 | environment, these tests must pass before any change is merged. One of the primary goals of this 7 | library is compatibility with the Android SQLite API be sure to keep that in mind when proposing 8 | any change. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android SQLite support library 2 | 3 | ![Build Status](https://github.com/requery/sqlite-android/actions/workflows/ci.yml/badge.svg) 4 | [![Download](https://jitpack.io/v/requery/sqlite-android.svg)](https://jitpack.io/#requery/sqlite-android) 5 | 6 | This is an Android specific distribution of the latest versions of SQLite. 7 | It contains the latest SQLite version and the Android specific database APIs 8 | derived from AOSP packaged as an AAR library distributed on jitpack. 9 | 10 | Why? 11 | ---- 12 | 13 | - **Consistent** 14 | - **Faster** 15 | - **Up-to-date** 16 | 17 | Even the latest version of Android is several versions behind the latest version of SQLite. 18 | These versions do not have bug fixes, performance improvements, or new features present in 19 | current versions of SQLite. This problem is worse the older the version of the OS the device has. 20 | Using this library, you can keep up to date with the latest versions of SQLite and provide a 21 | consistent version across OS versions and devices. 22 | 23 | Use new SQLite features: 24 | 25 | - **[JSON1 extension](https://www.sqlite.org/json1.html)** 26 | - **[Common Table expressions](https://www.sqlite.org/lang_with.html)** 27 | - **[Indexes on expressions](https://www.sqlite.org/expridx.html)** 28 | - **[Full-Text Search 5](https://www.sqlite.org/fts5.html)** 29 | - **[Generated Columns](https://www.sqlite.org/gencol.html)** 30 | - **[DROP COLUMN support](https://www.sqlite.org/lang_altertable.html#altertabdropcol)** 31 | 32 | Usage 33 | ----- 34 | 35 | Follow the guidelines from [jitpack.io](https://jitpack.io) to add the JitPack repository 36 | to your build file if you have not. 37 | 38 | Typically, this means an edit to your `build.gradle` file to add a new `repository` definition 39 | in the `allprojects` block, like this: 40 | 41 | ```gradle 42 | allprojects { 43 | repositories { 44 | ... 45 | maven { url 'https://jitpack.io' } 46 | } 47 | } 48 | ``` 49 | 50 | Then add the sqlite-android artifact from this repository as a dependency: 51 | 52 | ```gradle 53 | dependencies { 54 | implementation 'com.github.requery:sqlite-android:3.49.0' 55 | } 56 | ``` 57 | Then change usages of `android.database.sqlite.SQLiteDatabase` to 58 | `io.requery.android.database.sqlite.SQLiteDatabase`, similarly extend 59 | `io.requery.android.database.sqlite.SQLiteOpenHelper` instead of 60 | `android.database.sqlite.SQLiteOpenHelper`. Note similar changes maybe required for classes that 61 | depended on `android.database.sqlite.SQLiteDatabase` equivalent APIs are provided in the 62 | `io.requery.android.database.sqlite` package. 63 | 64 | If you expose `Cursor` instances across processes you should wrap the returned cursors in a 65 | [CrossProcessCursorWrapper](http://developer.android.com/reference/android/database/CrossProcessCursorWrapper.html) 66 | for performance reasons the cursors are not a cross process by default. 67 | 68 | ### Support library compatibility 69 | 70 | The library implements the SupportSQLite interfaces provided by the support library. Use 71 | `RequerySQLiteOpenHelperFactory` to obtain an implementation of `(Support)SQLiteOpenHelper` based 72 | on a `SupportSQLiteOpenHelper.Configuration` and `SupportSQLiteOpenHelper.Callback`. 73 | 74 | This also allows you to use sqlite-android with libraries like Room by passing an instance 75 | of `RequerySQLiteOpenHelperFactory` to them. 76 | 77 | 78 | CPU Architectures 79 | ----------------- 80 | 81 | The native library is built for the following CPU architectures: 82 | 83 | - `armeabi-v7a` ~1.2 MB 84 | - `arm64-v8a` ~1.7 MB 85 | - `x86` ~1.7 MB 86 | - `x86_64` ~1.8 MB 87 | 88 | However, you may not want to include all binaries in your apk. 89 | You can exclude certain variants by using `packagingOptions`: 90 | 91 | ```gradle 92 | android { 93 | packagingOptions { 94 | exclude 'lib/armeabi-v7a/libsqlite3x.so' 95 | exclude 'lib/arm64-v8a/libsqlite3x.so' 96 | exclude 'lib/x86/libsqlite3x.so' 97 | exclude 'lib/x86_64/libsqlite3x.so' 98 | } 99 | } 100 | ``` 101 | 102 | The size of the artifacts with only the armeabi-v7a binary is **~1.2 MB**. 103 | In general, you can use armeabi-v7a on the majority of Android devices including Intel Atom 104 | which provides a native translation layer, however, performance under the translation layer 105 | is worse than using the x86 binary. 106 | 107 | Note that starting August 1, 2019, your apps published on Google Play will [need to support 64-bit architectures](https://developer.android.com/distribute/best-practices/develop/64-bit). 108 | 109 | Requirements 110 | ------------ 111 | 112 | The min SDK level is API level 21 (Lollipop). 113 | 114 | Versioning 115 | ---------- 116 | 117 | The library is versioned after the version of SQLite it contains. For changes specific to just the 118 | wrapper API, a revision number is added, e.g., 3.49.0-X, where X is the revision number. 119 | 120 | Acknowledgements 121 | ---------------- 122 | This project is based on the AOSP code and the [Android SQLite bindings](https://www.sqlite.org/android/doc/trunk/www/index.wiki) 123 | No official distributions are made from the Android SQLite bindings it, and it has not been updated 124 | in a while, this project starts there and makes significant changes: 125 | 126 | Changes 127 | ------- 128 | 129 | - **Fast read performance:** The original SQLite bindings filled the CursorWindow using its 130 | Java methods from native C++. This was because there is no access to the native CursorWindow 131 | native API from the NDK. Unfortunately, this slowed read performance significantly (roughly 2x 132 | worse vs the android database API) because of extra JNI roundtrips. This has been rewritten 133 | without the JNI to Java calls (so more like the original AOSP code) and also using a local memory 134 | CursorWindow. 135 | - Reuse of android.database.sqlite.*, the original SQLite bindings replicated the entire 136 | android.database.sqlite API structure including exceptions & interfaces. This project does not 137 | do that, instead it reuses the original classes/interfaces when possible to simplify 138 | migration and/or use with existing code. 139 | - Unit tests added 140 | - Compile with [clang](http://clang.llvm.org/) toolchain 141 | - Compile with FTS3, FTS4, & JSON1 extension 142 | - Migrate to Gradle build 143 | - buildscript dynamically fetches and builds the latest sqlite source from sqlite.org 144 | - Added consumer proguard rules 145 | - Use androidx-core version of `CancellationSignal` 146 | - Fix bug in `SQLiteOpenHelper.getDatabaseLocked()` wrong path to `openOrCreateDatabase` 147 | - Fix removed members in AbstractWindowCursor 148 | - Made the AOSP code (mostly) warning free but still mergable from source 149 | - Deprecated classes/methods removed 150 | - Loadable extension support 151 | - STL dependency removed 152 | 153 | License 154 | ------- 155 | 156 | Copyright (C) 2017-2025 requery.io 157 | Copyright (C) 2005-2012 The Android Open Source Project 158 | 159 | Licensed under the Apache License, Version 2.0 (the "License"); 160 | you may not use this file except in compliance with the License. 161 | You may obtain a copy of the License at 162 | 163 | http://www.apache.org/licenses/LICENSE-2.0 164 | 165 | Unless required by applicable law or agreed to in writing, software 166 | distributed under the License is distributed on an "AS IS" BASIS, 167 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 168 | See the License for the specific language governing permissions and 169 | limitations under the License. 170 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.library" version "8.8.0" apply false 3 | id "de.undercouch.download" version "5.6.0" apply false 4 | } 5 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | ## For more details on how to configure your build environment visit 2 | # http://www.gradle.org/docs/current/userguide/build_environment.html 3 | # 4 | # Specifies the JVM arguments used for the daemon process. 5 | # The setting is particularly useful for tweaking memory settings. 6 | # Default value: -Xmx1024m -XX:MaxPermSize=256m 7 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 8 | # 9 | # When configured, Gradle will run in incubating parallel mode. 10 | # This option should only be used with decoupled projects. For more details, visit 11 | # https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects 12 | # org.gradle.parallel=true 13 | #Fri Jan 19 20:33:06 EET 2024 14 | # 15 | org.gradle.jvmargs=-Xmx2048M -Dkotlin.daemon.jvm.options\="-Xmx2048M" 16 | android.useAndroidX=true 17 | # 18 | GROUP=io.requery 19 | VERSION_NAME=3.49.0-SNAPSHOT 20 | # 21 | POM_DEVELOPER_ID=TODO 22 | POM_DEVELOPER_NAME=TODO 23 | POM_DEVELOPER_URL=TODO 24 | POM_INCEPTION_YEAR=TODO 25 | POM_LICENCE_DIST=repo 26 | POM_LICENCE_NAME=The Apache Software License, Version 2.0 27 | POM_LICENCE_URL=http\://www.apache.org/licenses/LICENSE-2.0.txt 28 | POM_SCM_CONNECTION=scm\:git\:git\://github.com/requery/sqlite-android.git 29 | POM_SCM_DEV_CONNECTION=scm\:git\:ssh\://git@github.com/requery/sqlite-android.git 30 | POM_SCM_URL=https\://github.com/requery/sqlite-android/ 31 | POM_URL=https\://github.com/requery/sqlite-android/ 32 | # 33 | android.defaults.buildfeatures.aidl=false 34 | android.defaults.buildfeatures.buildconfig=false 35 | android.defaults.buildfeatures.databinding=false 36 | android.defaults.buildfeatures.renderscript=false 37 | android.defaults.buildfeatures.resvalues=false 38 | android.defaults.buildfeatures.shaders=false 39 | android.defaults.buildfeatures.viewbinding=false 40 | android.library.defaults.buildfeatures.androidresources=false 41 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/requery/sqlite-android/fc24cc4284eaa77a391d48caaf47d15c37e7bbe3/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.12.1-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Stop when "xargs" is not available. 209 | if ! command -v xargs >/dev/null 2>&1 210 | then 211 | die "xargs is not available" 212 | fi 213 | 214 | # Use "xargs" to parse quoted args. 215 | # 216 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 217 | # 218 | # In Bash we could simply go: 219 | # 220 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 221 | # set -- "${ARGS[@]}" "$@" 222 | # 223 | # but POSIX shell has neither arrays nor command substitution, so instead we 224 | # post-process each arg (as a line of input to sed) to backslash-escape any 225 | # character that might be a shell metacharacter, then use eval to reverse 226 | # that process (while maintaining the separation between arguments), and wrap 227 | # the whole thing up as a single "set" statement. 228 | # 229 | # This will of course break if any of these variables contains a newline or 230 | # an unmatched quote. 231 | # 232 | 233 | eval "set -- $( 234 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 235 | xargs -n1 | 236 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 237 | tr '\n' ' ' 238 | )" '"$@"' 239 | 240 | exec "$JAVACMD" "$@" 241 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%"=="" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%"=="" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if %ERRORLEVEL% equ 0 goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if %ERRORLEVEL% equ 0 goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | set EXIT_CODE=%ERRORLEVEL% 84 | if %EXIT_CODE% equ 0 set EXIT_CODE=1 85 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% 86 | exit /b %EXIT_CODE% 87 | 88 | :mainEnd 89 | if "%OS%"=="Windows_NT" endlocal 90 | 91 | :omega 92 | -------------------------------------------------------------------------------- /jitpack.yml: -------------------------------------------------------------------------------- 1 | jdk: 2 | - openjdk17 -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google() 4 | gradlePluginPortal() 5 | mavenCentral() 6 | } 7 | } 8 | 9 | dependencyResolutionManagement { 10 | repositories { 11 | google() 12 | mavenCentral() 13 | } 14 | } 15 | 16 | rootProject.name = 'requery-sqlite' 17 | 18 | include ':sqlite-android' 19 | -------------------------------------------------------------------------------- /sqlite-android/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | /src/main/jniLibs 3 | /src/main/jni/sqlite/sqlite3.h 4 | /src/main/jni/sqlite/sqlite3.c 5 | /src/main/jni/sqlite.zip 6 | /src/main/obj 7 | .externalNativeBuild/ 8 | -------------------------------------------------------------------------------- /sqlite-android/build.gradle: -------------------------------------------------------------------------------- 1 | import com.vanniktech.maven.publish.SonatypeHost 2 | 3 | plugins { 4 | id "de.undercouch.download" 5 | id "com.android.library" 6 | id "com.vanniktech.maven.publish" version "0.30.0" 7 | } 8 | 9 | android { 10 | buildToolsVersion = "35.0.1" 11 | ndkVersion "28.0.13004108" 12 | 13 | compileSdk 35 14 | 15 | namespace "io.requery.android.sqlite" 16 | 17 | defaultConfig { 18 | minSdkVersion 21 19 | versionName VERSION_NAME 20 | 21 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 22 | consumerProguardFiles "proguard-rules.pro" 23 | ndk { 24 | abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64" 25 | } 26 | } 27 | 28 | buildTypes { 29 | release { 30 | minifyEnabled false 31 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 32 | } 33 | } 34 | 35 | lintOptions { 36 | abortOnError false 37 | } 38 | compileOptions { 39 | sourceCompatibility JavaVersion.VERSION_17 40 | targetCompatibility JavaVersion.VERSION_17 41 | } 42 | 43 | externalNativeBuild { 44 | ndkBuild { 45 | path "src/main/jni/Android.mk" 46 | } 47 | } 48 | } 49 | 50 | dependencies { 51 | api("androidx.core:core:1.15.0") 52 | api("androidx.sqlite:sqlite:2.4.0") 53 | testImplementation("junit:junit:4.13.2") 54 | androidTestImplementation("androidx.test:core:1.6.1") 55 | androidTestImplementation("androidx.test:runner:1.6.2") 56 | androidTestImplementation("androidx.test:rules:1.6.1") 57 | androidTestImplementation("androidx.test.ext:junit:1.2.1") 58 | } 59 | 60 | ext { 61 | sqliteDistributionUrl = "https://www.sqlite.org/2025/sqlite-amalgamation-3490000.zip" 62 | } 63 | 64 | tasks.register("downloadSqlite", Download) { 65 | src project.sqliteDistributionUrl 66 | dest "src/main/jni/sqlite.zip" 67 | } 68 | 69 | tasks.register("installSqlite", Copy) { 70 | dependsOn downloadSqlite 71 | from zipTree(downloadSqlite.dest).matching { 72 | include "*/sqlite3.*" 73 | eachFile { it.setPath(it.getName()) } 74 | } 75 | into "src/main/jni/sqlite" 76 | } 77 | 78 | preBuild.dependsOn installSqlite 79 | 80 | tasks.register("javadoc", Javadoc) { 81 | source = android.sourceSets.main.java.srcDirs 82 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 83 | android.libraryVariants.configureEach { variant -> 84 | if (variant.name == "release") { 85 | owner.classpath += variant.javaCompileProvider.get().classpath 86 | } 87 | } 88 | exclude "**/R.html", "**/R.*.html", "**/index.html" 89 | if (JavaVersion.current().isJava9Compatible()) { 90 | options.addBooleanOption("html5", true) 91 | } 92 | 93 | failOnError false 94 | } 95 | 96 | mavenPublishing { 97 | publishToMavenCentral(SonatypeHost.DEFAULT, /* automaticRelease */ true) 98 | signAllPublications() 99 | } 100 | -------------------------------------------------------------------------------- /sqlite-android/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_ARTIFACT_ID=sqlite-android 2 | POM_NAME=sqlite-android 3 | POM_DESCRIPTION=Android SQLite compatibility library -------------------------------------------------------------------------------- /sqlite-android/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | -keepclasseswithmembers class io.requery.android.database.** { 2 | native ; 3 | public (...); 4 | } 5 | -keepnames class io.requery.android.database.** { *; } 6 | -keep public class io.requery.android.database.sqlite.SQLiteFunction { *; } 7 | -keep public class io.requery.android.database.sqlite.SQLiteConnection { *; } 8 | -keep public class io.requery.android.database.sqlite.SQLiteCustomFunction { *; } 9 | -keep public class io.requery.android.database.sqlite.SQLiteCursor { *; } 10 | -keep public class io.requery.android.database.sqlite.SQLiteDebug** { *; } 11 | -keep public class io.requery.android.database.sqlite.SQLiteDatabase { *; } 12 | -keep public class io.requery.android.database.sqlite.SQLiteOpenHelper { *; } 13 | -keep public class io.requery.android.database.sqlite.SQLiteStatement { *; } 14 | -keep public class io.requery.android.database.sqlite.SQLiteUpdateHook { *; } 15 | -keep public class io.requery.android.database.CursorWindow { *; } 16 | -keepattributes Exceptions,InnerClasses 17 | -------------------------------------------------------------------------------- /sqlite-android/src/androidTest/java/io/requery/android/database/CursorWindowTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2008 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 | // modified from original source see README at the top level of this project 17 | 18 | package io.requery.android.database; 19 | 20 | import org.junit.Test; 21 | import org.junit.runner.RunWith; 22 | 23 | import java.util.Arrays; 24 | 25 | import androidx.test.ext.junit.runners.AndroidJUnit4; 26 | import androidx.test.filters.SmallTest; 27 | 28 | import static org.junit.Assert.assertEquals; 29 | import static org.junit.Assert.fail; 30 | import static org.junit.Assert.assertTrue; 31 | 32 | @RunWith(AndroidJUnit4.class) 33 | public class CursorWindowTest { 34 | static { 35 | System.loadLibrary("sqlite3x"); 36 | } 37 | public boolean isPerformanceOnly() { 38 | return false; 39 | } 40 | 41 | @SmallTest 42 | @Test 43 | public void testConstructor_WithName() { 44 | CursorWindow window = new CursorWindow("MyWindow"); 45 | assertEquals("MyWindow", window.getName()); 46 | assertEquals(0, window.getStartPosition()); 47 | assertEquals(2048 * 1024, window.getWindowSizeBytes()); 48 | window.close(); 49 | } 50 | 51 | @SmallTest 52 | @Test 53 | public void testConstructorWithEmptyName() { 54 | CursorWindow window = new CursorWindow(""); 55 | assertEquals("", window.getName()); 56 | assertEquals(0, window.getStartPosition()); 57 | assertEquals(2048 * 1024, window.getWindowSizeBytes()); 58 | window.close(); 59 | } 60 | 61 | @SmallTest 62 | @Test 63 | public void testConstructorWithNullName() { 64 | CursorWindow window = new CursorWindow(null); 65 | assertEquals("", window.getName()); 66 | assertEquals(0, window.getStartPosition()); 67 | assertEquals(2048 * 1024, window.getWindowSizeBytes()); 68 | window.close(); 69 | } 70 | 71 | @SmallTest 72 | @Test 73 | public void testValues() { 74 | CursorWindow window = new CursorWindow("MyWindow"); 75 | doTestValues(window); 76 | window.close(); 77 | } 78 | 79 | private void doTestValues(CursorWindow window) { 80 | assertTrue(window.setNumColumns(7)); 81 | assertTrue(window.allocRow()); 82 | double db1 = 1.26; 83 | assertTrue(window.putDouble(db1, 0, 0)); 84 | double db2 = window.getDouble(0, 0); 85 | assertEquals(db1, db2, 0); 86 | 87 | long int1 = Long.MAX_VALUE; 88 | assertTrue(window.putLong(int1, 0, 1)); 89 | long int2 = window.getLong(0, 1); 90 | assertEquals(int1, int2); 91 | 92 | assertTrue(window.putString("1198032740000", 0, 3)); 93 | assertEquals("1198032740000", window.getString(0, 3)); 94 | assertEquals(1198032740000L, window.getLong(0, 3)); 95 | 96 | assertTrue(window.putString(Long.toString(1198032740000L), 0, 3)); 97 | assertEquals(Long.toString(1198032740000L), window.getString(0, 3)); 98 | assertEquals(1198032740000L, window.getLong(0, 3)); 99 | 100 | assertTrue(window.putString(Double.toString(42.0), 0, 4)); 101 | assertEquals(Double.toString(42.0), window.getString(0, 4)); 102 | assertEquals(42.0, window.getDouble(0, 4), 0); 103 | 104 | // put blob 105 | byte[] blob = new byte[1000]; 106 | byte value = 99; 107 | Arrays.fill(blob, value); 108 | assertTrue(window.putBlob(blob, 0, 6)); 109 | assertTrue(Arrays.equals(blob, window.getBlob(0, 6))); 110 | } 111 | 112 | 113 | 114 | @SmallTest 115 | @Test(expected = AssertionError.class) 116 | public void testConstructorDifferentSize() { 117 | CursorWindow window = new CursorWindow("big", 8); 118 | assertEquals("big", window.getName()); 119 | assertEquals(0, window.getStartPosition()); 120 | assertEquals(8, window.getWindowSizeBytes()); 121 | try { 122 | // For window of size 8, the test should fail 123 | doTestValues(window); 124 | } finally { 125 | window.close(); 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /sqlite-android/src/androidTest/java/io/requery/android/database/DatabaseErrorHandlerTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 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 | // modified from original source see README at the top level of this project 17 | 18 | package io.requery.android.database; 19 | 20 | import android.content.Context; 21 | import android.database.sqlite.SQLiteDiskIOException; 22 | import android.database.sqlite.SQLiteException; 23 | import android.util.Log; 24 | 25 | import org.junit.After; 26 | import org.junit.Before; 27 | import org.junit.Test; 28 | import org.junit.runner.RunWith; 29 | 30 | import androidx.test.core.app.ApplicationProvider; 31 | import androidx.test.ext.junit.runners.AndroidJUnit4; 32 | import androidx.test.filters.Suppress; 33 | import io.requery.android.database.sqlite.SQLiteDatabase; 34 | 35 | import java.io.BufferedWriter; 36 | import java.io.File; 37 | import java.io.FileWriter; 38 | import java.io.IOException; 39 | 40 | import static org.junit.Assert.assertFalse; 41 | import static org.junit.Assert.assertNotNull; 42 | import static org.junit.Assert.assertTrue; 43 | import static org.junit.Assert.fail; 44 | 45 | @Suppress // https://code.google.com/p/android/issues/detail?id=125986 46 | // not clear how this test ever worked 47 | @SuppressWarnings("ResultOfMethodCallIgnored") 48 | @RunWith(AndroidJUnit4.class) 49 | public class DatabaseErrorHandlerTest { 50 | 51 | private SQLiteDatabase mDatabase; 52 | private File mDatabaseFile; 53 | private static final String DB_NAME = "database_test.db"; 54 | private File dbDir; 55 | 56 | @Before 57 | public void setUp() { 58 | dbDir = ApplicationProvider.getApplicationContext().getDir(this.getClass().getName(), Context.MODE_PRIVATE); 59 | mDatabaseFile = new File(dbDir, DB_NAME); 60 | if (mDatabaseFile.exists()) { 61 | mDatabaseFile.delete(); 62 | } 63 | mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null, 64 | new MyDatabaseCorruptionHandler()); 65 | assertNotNull(mDatabase); 66 | } 67 | 68 | @After 69 | public void tearDown() { 70 | mDatabase.close(); 71 | mDatabaseFile.delete(); 72 | } 73 | 74 | @Test 75 | public void testNoCorruptionCase() { 76 | new MyDatabaseCorruptionHandler().onCorruption(mDatabase); 77 | // database file should still exist 78 | assertTrue(mDatabaseFile.exists()); 79 | } 80 | 81 | 82 | @Test 83 | public void testDatabaseIsCorrupt() throws IOException { 84 | mDatabase.execSQL("create table t (i int);"); 85 | // write junk into the database file 86 | BufferedWriter writer = new BufferedWriter(new FileWriter(mDatabaseFile.getPath())); 87 | writer.write("blah"); 88 | writer.close(); 89 | assertTrue(mDatabaseFile.exists()); 90 | // since the database file is now corrupt, doing any sql on this database connection 91 | // should trigger call to MyDatabaseCorruptionHandler.onCorruption 92 | try { 93 | mDatabase.execSQL("select * from t;"); 94 | fail("expected exception"); 95 | } catch (SQLiteDiskIOException e) { 96 | // 97 | // this test used to produce a corrupted db. but with new sqlite it instead reports 98 | // Disk I/O error. meh.. 99 | // need to figure out how to cause corruption in db 100 | // 101 | // expected 102 | if (mDatabaseFile.exists()) { 103 | mDatabaseFile.delete(); 104 | } 105 | } catch (SQLiteException ignored) { 106 | 107 | } 108 | // database file should be gone 109 | assertFalse(mDatabaseFile.exists()); 110 | // after corruption handler is called, the database file should be free of 111 | // database corruption 112 | SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null, 113 | new MyDatabaseCorruptionHandler()); 114 | assertTrue(db.isDatabaseIntegrityOk()); 115 | } 116 | 117 | /** 118 | * An example implementation of {@link DatabaseErrorHandler} to demonstrate 119 | * database corruption handler which checks to make sure database is indeed 120 | * corrupt before deleting the file. 121 | */ 122 | public class MyDatabaseCorruptionHandler implements DatabaseErrorHandler { 123 | public void onCorruption(SQLiteDatabase dbObj) { 124 | boolean databaseOk = dbObj.isDatabaseIntegrityOk(); 125 | // close the database 126 | try { 127 | dbObj.close(); 128 | } catch (SQLiteException e) { 129 | /* ignore */ 130 | } 131 | if (databaseOk) { 132 | // database is just fine. no need to delete the database file 133 | Log.e("CorruptionHandler", "no corruption in the database: " + 134 | mDatabaseFile.getPath()); 135 | } else { 136 | // database is corrupt. delete the database file 137 | Log.e("CorruptionHandler", "deleting the database file: " + 138 | mDatabaseFile.getPath()); 139 | new File(dbDir, DB_NAME).delete(); 140 | } 141 | } 142 | } 143 | } -------------------------------------------------------------------------------- /sqlite-android/src/androidTest/java/io/requery/android/database/DatabaseLocaleTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 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 | // modified from original source see README at the top level of this project 17 | 18 | package io.requery.android.database; 19 | 20 | import android.database.Cursor; 21 | import android.util.Log; 22 | 23 | import androidx.test.ext.junit.runners.AndroidJUnit4; 24 | import androidx.test.filters.MediumTest; 25 | import androidx.test.filters.SmallTest; 26 | import androidx.test.filters.Suppress; 27 | import io.requery.android.database.sqlite.SQLiteDatabase; 28 | 29 | import org.junit.After; 30 | import org.junit.Assert; 31 | import org.junit.Before; 32 | import org.junit.Test; 33 | import org.junit.runner.RunWith; 34 | 35 | import java.util.ArrayList; 36 | import java.util.Locale; 37 | 38 | import static org.junit.Assert.assertEquals; 39 | import static org.junit.Assert.assertNotNull; 40 | import static org.junit.Assert.assertTrue; 41 | 42 | @RunWith(AndroidJUnit4.class) 43 | public class DatabaseLocaleTest { 44 | 45 | private SQLiteDatabase mDatabase; 46 | 47 | private static final String[] STRINGS = { 48 | "c\u00f4t\u00e9", 49 | "cote", 50 | "c\u00f4te", 51 | "cot\u00e9", 52 | "boy", 53 | "dog", 54 | "COTE", 55 | }; 56 | 57 | @Before 58 | public void setUp() { 59 | mDatabase = SQLiteDatabase.create(null); 60 | mDatabase.execSQL( 61 | "CREATE TABLE test (id INTEGER PRIMARY KEY, data TEXT COLLATE LOCALIZED);"); 62 | } 63 | 64 | private void insertStrings() { 65 | for (String s : STRINGS) { 66 | mDatabase.execSQL("INSERT INTO test (data) VALUES('" + s + "');"); 67 | } 68 | } 69 | 70 | @After 71 | public void tearDown() { 72 | mDatabase.close(); 73 | } 74 | 75 | private String[] query(String sql) { 76 | Log.i("LocaleTest", "Querying: " + sql); 77 | Cursor c = mDatabase.rawQuery(sql, null); 78 | Assert.assertNotNull(c); 79 | ArrayList items = new ArrayList<>(); 80 | while (c.moveToNext()) { 81 | items.add(c.getString(0)); 82 | Log.i("LocaleTest", "...." + c.getString(0)); 83 | } 84 | String[] result = items.toArray(new String[items.size()]); 85 | assertEquals(STRINGS.length, result.length); 86 | c.close(); 87 | return result; 88 | } 89 | 90 | @MediumTest 91 | @Test 92 | public void testLocaleInsertOrder() { 93 | insertStrings(); 94 | String[] results = query("SELECT data FROM test"); 95 | assertEquals(STRINGS, results); 96 | } 97 | 98 | @Suppress // not supporting localized collators 99 | @MediumTest 100 | @Test 101 | public void testLocaleenUS() { 102 | insertStrings(); 103 | Log.i("LocaleTest", "about to call setLocale en_US"); 104 | mDatabase.setLocale(new Locale("en", "US")); 105 | String[] results; 106 | results = query("SELECT data FROM test ORDER BY data COLLATE LOCALIZED ASC"); 107 | 108 | // The database code currently uses PRIMARY collation strength, 109 | // meaning that all versions of a character compare equal (regardless 110 | // of case or accents), leaving the "cote" flavors in database order. 111 | assertEquals(results, new String[] { 112 | STRINGS[4], // "boy" 113 | STRINGS[0], // sundry forms of "cote" 114 | STRINGS[1], 115 | STRINGS[2], 116 | STRINGS[3], 117 | STRINGS[6], // "COTE" 118 | STRINGS[5], // "dog" 119 | }); 120 | } 121 | 122 | @SmallTest 123 | @Test 124 | public void testHoge() throws Exception { 125 | Cursor cursor = null; 126 | try { 127 | String expectedString = new String(new int[] {0xFE000}, 0, 1); 128 | mDatabase.execSQL("INSERT INTO test(id, data) VALUES(1, '" + expectedString + "')"); 129 | cursor = mDatabase.rawQuery("SELECT data FROM test WHERE id = 1", null); 130 | 131 | assertNotNull(cursor); 132 | assertTrue(cursor.moveToFirst()); 133 | String actualString = cursor.getString(0); 134 | assertEquals(expectedString.length(), actualString.length()); 135 | for (int i = 0; i < expectedString.length(); i++) { 136 | assertEquals((int)expectedString.charAt(i), (int)actualString.charAt(i)); 137 | } 138 | assertEquals(expectedString, actualString); 139 | } finally { 140 | if (cursor != null) cursor.close(); 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /sqlite-android/src/androidTest/java/io/requery/android/database/NewDatabasePerformanceTestSuite.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2005 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 | // modified from original source see README at the top level of this project 17 | 18 | package io.requery.android.database; 19 | 20 | import junit.framework.TestSuite; 21 | import org.junit.Ignore; 22 | import org.junit.runner.RunWith; 23 | 24 | import androidx.test.ext.junit.runners.AndroidJUnit4; 25 | 26 | @Ignore("No reason to ignore") 27 | @RunWith(AndroidJUnit4.class) 28 | public class NewDatabasePerformanceTestSuite extends TestSuite { 29 | 30 | public static TestSuite suite() { 31 | TestSuite suite = 32 | new TestSuite(NewDatabasePerformanceTestSuite.class.getName()); 33 | 34 | suite.addTestSuite(NewDatabasePerformanceTests. 35 | Insert1000.class); 36 | suite.addTestSuite(NewDatabasePerformanceTests. 37 | InsertIndexed1000.class); 38 | suite.addTestSuite(NewDatabasePerformanceTests. 39 | Select100.class); 40 | suite.addTestSuite(NewDatabasePerformanceTests. 41 | SelectStringComparison100.class); 42 | suite.addTestSuite(NewDatabasePerformanceTests. 43 | SelectIndex100.class); 44 | suite.addTestSuite(NewDatabasePerformanceTests. 45 | InnerJoin100.class); 46 | suite.addTestSuite(NewDatabasePerformanceTests. 47 | InnerJoinOneSide100.class); 48 | suite.addTestSuite(NewDatabasePerformanceTests. 49 | InnerJoinNoIndex100.class); 50 | suite.addTestSuite(NewDatabasePerformanceTests. 51 | SelectSubQIndex100.class); 52 | suite.addTestSuite(NewDatabasePerformanceTests. 53 | SelectIndexStringComparison100.class); 54 | suite.addTestSuite(NewDatabasePerformanceTests. 55 | SelectInteger100.class); 56 | suite.addTestSuite(NewDatabasePerformanceTests. 57 | SelectString100.class); 58 | suite.addTestSuite(NewDatabasePerformanceTests. 59 | SelectIntegerIndex100.class); 60 | suite.addTestSuite(NewDatabasePerformanceTests. 61 | SelectIndexString100.class); 62 | suite.addTestSuite(NewDatabasePerformanceTests. 63 | SelectStringStartsWith100.class); 64 | suite.addTestSuite(NewDatabasePerformanceTests. 65 | DeleteIndexed1000.class); 66 | suite.addTestSuite(NewDatabasePerformanceTests. 67 | Delete1000.class); 68 | suite.addTestSuite(NewDatabasePerformanceTests. 69 | DeleteWhere1000.class); 70 | suite.addTestSuite(NewDatabasePerformanceTests. 71 | DeleteIndexWhere1000.class); 72 | suite.addTestSuite(NewDatabasePerformanceTests. 73 | UpdateIndexWhere1000.class); 74 | suite.addTestSuite(NewDatabasePerformanceTests. 75 | UpdateWhere1000.class); 76 | suite.addTestSuite(NewDatabasePerformanceTests. 77 | InsertInteger10000.class); 78 | suite.addTestSuite(NewDatabasePerformanceTests. 79 | InsertIntegerIndex10000.class); 80 | suite.addTestSuite(NewDatabasePerformanceTests. 81 | InsertString10000.class); 82 | suite.addTestSuite(NewDatabasePerformanceTests. 83 | InsertStringIndexed10000.class); 84 | suite.addTestSuite(NewDatabasePerformanceTests. 85 | SelectStringStartsWith10000.class); 86 | suite.addTestSuite(NewDatabasePerformanceTests. 87 | SelectStringIndexedStartsWith10000.class); 88 | suite.addTestSuite(NewDatabasePerformanceTests. 89 | SelectInteger10000.class); 90 | suite.addTestSuite(NewDatabasePerformanceTests. 91 | SelectIntegerIndexed10000.class); 92 | suite.addTestSuite(NewDatabasePerformanceTests. 93 | SelectStringContains10000.class); 94 | suite.addTestSuite(NewDatabasePerformanceTests. 95 | SelectStringIndexedContains10000.class); 96 | 97 | return suite; 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /sqlite-android/src/androidTest/java/io/requery/android/database/sqlite/SQLiteStatementTypeTest.java: -------------------------------------------------------------------------------- 1 | package io.requery.android.database.sqlite; 2 | 3 | import static io.requery.android.database.sqlite.SQLiteStatementTypeTest.TestData.test; 4 | 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | 8 | public class SQLiteStatementTypeTest { 9 | 10 | @Test 11 | public void recognizesSelectQueryWithComments() { 12 | String query = "--comment --comment\nSELECT * FROM table WHERE id = 1"; 13 | Assert.assertEquals(SQLiteStatementType.STATEMENT_SELECT, SQLiteStatementType.getSqlStatementType(query)); 14 | } 15 | 16 | @Test 17 | public void recognizesUntrimmedSelectQueryWithoutComments() { 18 | String query = " \n SELECT * FROM table WHERE id = 1"; 19 | Assert.assertEquals(SQLiteStatementType.STATEMENT_SELECT, SQLiteStatementType.getSqlStatementType(query)); 20 | } 21 | 22 | @Test 23 | public void recognizesTrimmedSelectQueryWithoutComments() { 24 | String query = "SELECT * FROM table WHERE id = 1"; 25 | Assert.assertEquals(SQLiteStatementType.STATEMENT_SELECT, SQLiteStatementType.getSqlStatementType(query)); 26 | } 27 | 28 | @Test 29 | public void recognizesUpdateQueryWithComments() { 30 | String query = "--comment\nINSERT INTO phones (num) VALUES ('911');"; 31 | Assert.assertEquals(SQLiteStatementType.STATEMENT_UPDATE, SQLiteStatementType.getSqlStatementType(query)); 32 | } 33 | 34 | @Test 35 | public void notCrashingOnInvalidQuery() { 36 | // Checking for index out of bounds, because `getSqlStatementType` uses (statementStartIndex + 3) as its end index 37 | String query = "--comment\nSE"; 38 | Assert.assertEquals(SQLiteStatementType.STATEMENT_OTHER, SQLiteStatementType.getSqlStatementType(query)); 39 | } 40 | 41 | @Test 42 | public void testStripSqlComments() { 43 | for (TestData test : queriesTestData) { 44 | int start = SQLiteStatementType.statementStartIndex(test.inputQuery); 45 | String strippedSql = test.inputQuery.substring(start); 46 | Assert.assertEquals("Error in test case\n" + test.inputQuery, test.expectedQuery, strippedSql); 47 | } 48 | } 49 | 50 | private static final TestData[] queriesTestData = { 51 | test("", ""), 52 | test(" ", " "), 53 | test("\n", "\n"), 54 | test( 55 | "\n-- ?1 - version id, required\n-- ?2 - account id, optional\nSELECT\n SUM(col1 + col2) AS count\nFROM\n Accounts\nWHERE\n id = ?1\nAND\n col3 = 0\nAND\n CASE WHEN COALESCE(?2, '') = '' THEN 1 ELSE entityId = ?2 END\n", 56 | "SELECT\n SUM(col1 + col2) AS count\nFROM\n Accounts\nWHERE\n id = ?1\nAND\n col3 = 0\nAND\n CASE WHEN COALESCE(?2, '') = '' THEN 1 ELSE entityId = ?2 END\n" 57 | ), 58 | test( 59 | "select * from employees", 60 | "select * from employees" 61 | ), 62 | test( 63 | "select * from employees -- this is a comment", 64 | "select * from employees -- this is a comment" 65 | ), 66 | test( 67 | "select * from employees /* first comment */-- second comment", 68 | "select * from employees /* first comment */-- second comment" 69 | ), 70 | test( 71 | "-- this is a comment\nselect * from employees", 72 | "select * from employees" 73 | ), 74 | test( 75 | "-- this is a comment\nselect \"--col\" from employees", 76 | "select \"--col\" from employees" 77 | ), 78 | test( 79 | "-------this is a comment\nselect * from employees", 80 | "select * from employees" 81 | ), 82 | test( 83 | "\n-- ?1 first parameter id\n-- ?2 second parameter format \"yyyy-mm-01\"\n-- ?3 third parameter either 'value1' or 'value2'\n\nselect * from employees where param1 = ?1 AND param2 = ?2 AND param3 = ?3", 84 | "select * from employees where param1 = ?1 AND param2 = ?2 AND param3 = ?3" 85 | ), 86 | test( 87 | "/* Single Line Block Comment */ select * from employees", 88 | "select * from employees" 89 | ), 90 | test( 91 | "/* Single Line Block Comment */\nselect * from employees", 92 | "select * from employees" 93 | ), 94 | test( 95 | "/* Multiline Line Block Comment\nHere is another line\nAnd another */\nselect * from employees", 96 | "select * from employees" 97 | ), 98 | test( 99 | "/*Multiline Line Block Comment\nHere is another line\nAnd another */\nselect * from employees", 100 | "select * from employees" 101 | ), 102 | test( 103 | "\nselect * from employees where /* this is param 1 */ param1 = ?1 AND /* this is param 2 */ param2 = ?2 AND /* this is param 3 */ param3 = ?3", 104 | "select * from employees where /* this is param 1 */ param1 = ?1 AND /* this is param 2 */ param2 = ?2 AND /* this is param 3 */ param3 = ?3" 105 | ), 106 | test( 107 | "/* Single Line Block Comment */\n-- another comment\nselect * from employees", 108 | "select * from employees" 109 | ), 110 | test( 111 | "/* Single Line Block Comment */\n--another comment\nselect * from employees", 112 | "select * from employees" 113 | ), 114 | test( 115 | "/* Multiline Line Block Comment\nLine 2\nLine 3 */\n-- dashed comment\nselect * from employees", 116 | "select * from employees" 117 | ), 118 | test( 119 | "/* Multiline Line Block Comment\nLine 2\n-- dashed comment inside block comment\nLine 3 */\nselect * from employees", 120 | "select * from employees" 121 | ), 122 | test( 123 | "\nSELECT\n 'All Accounts' AS name,\n 'all-accounts' AS internal_name\nFROM\n Accounts\nWHERE\n id = ?1\nAND\n col3 = 0\n ", 124 | "SELECT\n 'All Accounts' AS name,\n 'all-accounts' AS internal_name\nFROM\n Accounts\nWHERE\n id = ?1\nAND\n col3 = 0\n " 125 | ), 126 | test( 127 | "/* Multiline Line Block Comment\nLine 2\nLine 3 */-- single line comment\nselect * from employees", 128 | "select * from employees" 129 | ), 130 | test( 131 | "/* Multiline Line Block Comment\nhttps://foo.bar.com/document/d/283472938749/foo.ts\nLine 3 */-- single line comment\nSELECT\n 'All Accounts' AS name,\n 'all-accounts' AS internal_name\nFROM\n Accounts\nWHERE\n id = ?1\nAND\n col3 = 0\n ", 132 | "SELECT\n 'All Accounts' AS name,\n 'all-accounts' AS internal_name\nFROM\n Accounts\nWHERE\n id = ?1\nAND\n col3 = 0\n " 133 | ), 134 | // Shouldn't crash on invalid query 135 | test( 136 | "/* Single Line Block Comment */SE", 137 | "SE" 138 | ), 139 | }; 140 | 141 | static class TestData { 142 | final String inputQuery; 143 | final String expectedQuery; 144 | 145 | TestData(String inputQuery, String expectedQuery) { 146 | this.inputQuery = inputQuery; 147 | this.expectedQuery = expectedQuery; 148 | } 149 | 150 | static TestData test(String inputQuery, String expectedQuery) { 151 | return new TestData(inputQuery, expectedQuery); 152 | } 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /sqlite-android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /sqlite-android/src/main/java/io/requery/android/database/AbstractWindowedCursor.java: -------------------------------------------------------------------------------- 1 | /* 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 | // modified from original source see README at the top level of this project 17 | 18 | package io.requery.android.database; 19 | 20 | import android.database.CharArrayBuffer; 21 | import android.database.Cursor; 22 | import android.database.StaleDataException; 23 | 24 | /** 25 | * A base class for Cursors that store their data in {@link android.database.CursorWindow}s. 26 | *

27 | * The cursor owns the cursor window it uses. When the cursor is closed, 28 | * its window is also closed. Likewise, when the window used by the cursor is 29 | * changed, its old window is closed. This policy of strict ownership ensures 30 | * that cursor windows are not leaked. 31 | *

32 | * Subclasses are responsible for filling the cursor window with data during 33 | * {@link #onMove(int, int)}, allocating a new cursor window if necessary. 34 | * During {@link #requery()}, the existing cursor window should be cleared and 35 | * filled with new data. 36 | *

37 | * If the contents of the cursor change or become invalid, the old window must be closed 38 | * (because it is owned by the cursor) and set to null. 39 | *

40 | */ 41 | @SuppressWarnings("unused") 42 | public abstract class AbstractWindowedCursor extends AbstractCursor { 43 | /** 44 | * The cursor window owned by this cursor. 45 | */ 46 | protected CursorWindow mWindow; 47 | 48 | @Override 49 | public byte[] getBlob(int columnIndex) { 50 | checkPosition(); 51 | return mWindow.getBlob(mPos, columnIndex); 52 | } 53 | 54 | @Override 55 | public String getString(int columnIndex) { 56 | checkPosition(); 57 | return mWindow.getString(mPos, columnIndex); 58 | } 59 | 60 | @Override 61 | public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) { 62 | mWindow.copyStringToBuffer(mPos, columnIndex, buffer); 63 | } 64 | 65 | @Override 66 | public short getShort(int columnIndex) { 67 | checkPosition(); 68 | return mWindow.getShort(mPos, columnIndex); 69 | } 70 | 71 | @Override 72 | public int getInt(int columnIndex) { 73 | checkPosition(); 74 | return mWindow.getInt(mPos, columnIndex); 75 | } 76 | 77 | @Override 78 | public long getLong(int columnIndex) { 79 | checkPosition(); 80 | return mWindow.getLong(mPos, columnIndex); 81 | } 82 | 83 | @Override 84 | public float getFloat(int columnIndex) { 85 | checkPosition(); 86 | return mWindow.getFloat(mPos, columnIndex); 87 | } 88 | 89 | @Override 90 | public double getDouble(int columnIndex) { 91 | checkPosition(); 92 | return mWindow.getDouble(mPos, columnIndex); 93 | } 94 | 95 | @Override 96 | public boolean isNull(int columnIndex) { 97 | return mWindow.getType(mPos, columnIndex) == Cursor.FIELD_TYPE_NULL; 98 | } 99 | 100 | @Override 101 | public int getType(int columnIndex) { 102 | return mWindow.getType(mPos, columnIndex); 103 | } 104 | 105 | @Override 106 | protected void checkPosition() { 107 | super.checkPosition(); 108 | if (mWindow == null) { 109 | throw new StaleDataException("Attempting to access a closed CursorWindow." + 110 | "Most probable cause: cursor is deactivated prior to calling this method."); 111 | } 112 | } 113 | 114 | public CursorWindow getWindow() { 115 | return mWindow; 116 | } 117 | 118 | /** 119 | * Sets a new cursor window for the cursor to use. 120 | *

121 | * The cursor takes ownership of the provided cursor window; the cursor window 122 | * will be closed when the cursor is closed or when the cursor adopts a new 123 | * cursor window. 124 | *

125 | * If the cursor previously had a cursor window, then it is closed when the 126 | * new cursor window is assigned. 127 | *

128 | * 129 | * @param window The new cursor window, typically a remote cursor window. 130 | */ 131 | public void setWindow(CursorWindow window) { 132 | if (window != mWindow) { 133 | closeWindow(); 134 | mWindow = window; 135 | } 136 | } 137 | 138 | /** 139 | * Returns true if the cursor has an associated cursor window. 140 | * 141 | * @return True if the cursor has an associated cursor window. 142 | */ 143 | public boolean hasWindow() { 144 | return mWindow != null; 145 | } 146 | 147 | /** 148 | * Closes the cursor window and sets {@link #mWindow} to null. 149 | * @hide 150 | */ 151 | protected void closeWindow() { 152 | if (mWindow != null) { 153 | mWindow.close(); 154 | mWindow = null; 155 | } 156 | } 157 | 158 | /** 159 | * If there is a window, clear it. Otherwise, creates a new window. 160 | * 161 | * @param name The window name. 162 | * @hide 163 | */ 164 | protected void clearOrCreateWindow(String name) { 165 | if (mWindow == null) { 166 | mWindow = new CursorWindow(name); 167 | } else { 168 | mWindow.clear(); 169 | } 170 | } 171 | 172 | @Override 173 | protected void onDeactivateOrClose() { 174 | super.onDeactivateOrClose(); 175 | closeWindow(); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /sqlite-android/src/main/java/io/requery/android/database/CursorWindowAllocationException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 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 io.requery.android.database; 18 | 19 | /** 20 | * This exception is thrown when a CursorWindow couldn't be allocated, 21 | * most probably due to memory not being available. 22 | * 23 | * @hide 24 | */ 25 | public class CursorWindowAllocationException extends RuntimeException { 26 | public CursorWindowAllocationException(String description) { 27 | super(description); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /sqlite-android/src/main/java/io/requery/android/database/DatabaseErrorHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 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 | // modified from original source see README at the top level of this project 17 | 18 | package io.requery.android.database; 19 | 20 | import io.requery.android.database.sqlite.SQLiteDatabase; 21 | 22 | /** 23 | * An interface to let apps define an action to take when database corruption is detected. 24 | */ 25 | public interface DatabaseErrorHandler { 26 | 27 | /** 28 | * The method invoked when database corruption is detected. 29 | * @param dbObj the {@link SQLiteDatabase} object representing the database on which corruption 30 | * is detected. 31 | */ 32 | void onCorruption(SQLiteDatabase dbObj); 33 | } 34 | -------------------------------------------------------------------------------- /sqlite-android/src/main/java/io/requery/android/database/DefaultDatabaseErrorHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 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 | // modified from original source see README at the top level of this project 17 | 18 | package io.requery.android.database; 19 | 20 | import android.database.sqlite.SQLiteException; 21 | import android.util.Log; 22 | import android.util.Pair; 23 | import io.requery.android.database.sqlite.SQLiteDatabase; 24 | 25 | import java.io.File; 26 | import java.util.List; 27 | 28 | /** 29 | * Default class used to define the actions to take when the database corruption is reported 30 | * by sqlite. 31 | *

32 | * An application can specify an implementation of {@link DatabaseErrorHandler} on the 33 | * following: 34 | *

    35 | *
  • {@link SQLiteDatabase#openOrCreateDatabase(String, 36 | * SQLiteDatabase.CursorFactory, DatabaseErrorHandler)}
  • 37 | *
  • {@link SQLiteDatabase#openDatabase(String, 38 | * SQLiteDatabase.CursorFactory, int, DatabaseErrorHandler)}
  • 39 | *
40 | * The specified {@link DatabaseErrorHandler} is used to handle database corruption errors, if they 41 | * occur. 42 | *

43 | * If null is specified for DatabaeErrorHandler param in the above calls, then this class is used 44 | * as the default {@link DatabaseErrorHandler}. 45 | */ 46 | public final class DefaultDatabaseErrorHandler implements DatabaseErrorHandler { 47 | 48 | private static final String TAG = "DefaultDatabaseError"; 49 | 50 | @Override 51 | public void onCorruption(SQLiteDatabase dbObj) { 52 | Log.e(TAG, "Corruption reported by sqlite on database: " + dbObj.getPath()); 53 | 54 | // is the corruption detected even before database could be 'opened'? 55 | if (!dbObj.isOpen()) { 56 | // database files are not even openable. delete this database file. 57 | // NOTE if the database has attached databases, then any of them could be corrupt. 58 | // and not deleting all of them could cause corrupted database file to remain and 59 | // make the application crash on database open operation. To avoid this problem, 60 | // the application should provide its own {@link DatabaseErrorHandler} impl class 61 | // to delete ALL files of the database (including the attached databases). 62 | deleteDatabaseFile(dbObj.getPath()); 63 | return; 64 | } 65 | 66 | List> attachedDbs = null; 67 | try { 68 | // Close the database, which will cause subsequent operations to fail. 69 | // before that, get the attached database list first. 70 | try { 71 | attachedDbs = dbObj.getAttachedDbs(); 72 | } catch (SQLiteException e) { 73 | /* ignore */ 74 | } 75 | try { 76 | dbObj.close(); 77 | } catch (SQLiteException e) { 78 | /* ignore */ 79 | } 80 | } finally { 81 | // Delete all files of this corrupt database and/or attached databases 82 | if (attachedDbs != null) { 83 | for (Pair p : attachedDbs) { 84 | deleteDatabaseFile(p.second); 85 | } 86 | } else { 87 | // attachedDbs = null is possible when the database is so corrupt that even 88 | // "PRAGMA database_list;" also fails. delete the main database file 89 | deleteDatabaseFile(dbObj.getPath()); 90 | } 91 | } 92 | } 93 | 94 | private void deleteDatabaseFile(String fileName) { 95 | if (fileName.equalsIgnoreCase(":memory:") || fileName.trim().length() == 0) { 96 | return; 97 | } 98 | Log.e(TAG, "deleting the database file: " + fileName); 99 | try { 100 | SQLiteDatabase.deleteDatabase(new File(fileName)); 101 | } catch (Exception e) { 102 | /* print warning and ignore exception */ 103 | Log.w(TAG, "delete failed: " + e.getMessage()); 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /sqlite-android/src/main/java/io/requery/android/database/sqlite/CloseGuard.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2010 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 | // modified from original source see README at the top level of this project 17 | 18 | package io.requery.android.database.sqlite; 19 | 20 | import android.util.Log; 21 | 22 | /** 23 | * CloseGuard is a mechanism for flagging implicit finalizer cleanup of 24 | * resources that should have been cleaned up by explicit close 25 | * methods (aka "explicit termination methods" in Effective Java). 26 | *

27 | * A simple example:

   {@code
 28 |  *   class Foo {
 29 |  *
 30 |  *       private final CloseGuard guard = CloseGuard.get();
 31 |  *
 32 |  *       ...
 33 |  *
 34 |  *       public Foo() {
 35 |  *           ...;
 36 |  *           guard.open("cleanup");
 37 |  *       }
 38 |  *
 39 |  *       public void cleanup() {
 40 |  *          guard.close();
 41 |  *          ...;
 42 |  *       }
 43 |  *
 44 |  *       protected void finalize() throws Throwable {
 45 |  *           try {
 46 |  *               if (guard != null) {
 47 |  *                   guard.warnIfOpen();
 48 |  *               }
 49 |  *               cleanup();
 50 |  *           } finally {
 51 |  *               super.finalize();
 52 |  *           }
 53 |  *       }
 54 |  *   }
 55 |  * }
56 | * 57 | * In usage where the resource to be explicitly cleaned up are 58 | * allocated after object construction, CloseGuard protection can 59 | * be deferred. For example:
   {@code
 60 |  *   class Bar {
 61 |  *
 62 |  *       private final CloseGuard guard = CloseGuard.get();
 63 |  *
 64 |  *       ...
 65 |  *
 66 |  *       public Bar() {
 67 |  *           ...;
 68 |  *       }
 69 |  *
 70 |  *       public void connect() {
 71 |  *          ...;
 72 |  *          guard.open("cleanup");
 73 |  *       }
 74 |  *
 75 |  *       public void cleanup() {
 76 |  *          guard.close();
 77 |  *          ...;
 78 |  *       }
 79 |  *
 80 |  *       protected void finalize() throws Throwable {
 81 |  *           try {
 82 |  *               if (guard != null) {
 83 |  *                   guard.warnIfOpen();
 84 |  *               }
 85 |  *               cleanup();
 86 |  *           } finally {
 87 |  *               super.finalize();
 88 |  *           }
 89 |  *       }
 90 |  *   }
 91 |  * }
92 | * 93 | * When used in a constructor calls to {@code open} should occur at 94 | * the end of the constructor since an exception that would cause 95 | * abrupt termination of the constructor will mean that the user will 96 | * not have a reference to the object to cleanup explicitly. When used 97 | * in a method, the call to {@code open} should occur just after 98 | * resource acquisition. 99 | * 100 | *

101 | * 102 | * Note that the null check on {@code guard} in the finalizer is to 103 | * cover cases where a constructor throws an exception causing the 104 | * {@code guard} to be uninitialized. 105 | * 106 | * @hide 107 | */ 108 | @SuppressWarnings("unused") 109 | public final class CloseGuard { 110 | 111 | /** 112 | * Instance used when CloseGuard is disabled to avoid allocation. 113 | */ 114 | private static final CloseGuard NOOP = new CloseGuard(); 115 | 116 | /** 117 | * Enabled by default so we can catch issues early in VM startup. 118 | * Note, however, that Android disables this early in its startup, 119 | * but enables it with DropBoxing for system apps on debug builds. 120 | */ 121 | private static volatile boolean ENABLED = true; 122 | 123 | /** 124 | * Hook for customizing how CloseGuard issues are reported. 125 | */ 126 | private static volatile Reporter REPORTER = new DefaultReporter(); 127 | 128 | /** 129 | * Returns a CloseGuard instance. If CloseGuard is enabled, {@code 130 | * #open(String)} can be used to set up the instance to warn on 131 | * failure to close. If CloseGuard is disabled, a non-null no-op 132 | * instance is returned. 133 | */ 134 | public static CloseGuard get() { 135 | if (!ENABLED) { 136 | return NOOP; 137 | } 138 | return new CloseGuard(); 139 | } 140 | 141 | /** 142 | * Used to enable or disable CloseGuard. Note that CloseGuard only 143 | * warns if it is enabled for both allocation and finalization. 144 | */ 145 | public static void setEnabled(boolean enabled) { 146 | ENABLED = enabled; 147 | } 148 | 149 | /** 150 | * Used to replace default Reporter used to warn of CloseGuard 151 | * violations. Must be non-null. 152 | */ 153 | public static void setReporter(Reporter reporter) { 154 | if (reporter == null) { 155 | throw new NullPointerException("reporter == null"); 156 | } 157 | REPORTER = reporter; 158 | } 159 | 160 | /** 161 | * Returns non-null CloseGuard.Reporter. 162 | */ 163 | public static Reporter getReporter() { 164 | return REPORTER; 165 | } 166 | 167 | private CloseGuard() {} 168 | 169 | /** 170 | * If CloseGuard is enabled, {@code open} initializes the instance 171 | * with a warning that the caller should have explicitly called the 172 | * {@code closer} method instead of relying on finalization. 173 | * 174 | * @param closer non-null name of explicit termination method 175 | * @throws NullPointerException if closer is null, regardless of 176 | * whether or not CloseGuard is enabled 177 | */ 178 | public void open(String closer) { 179 | // always perform the check for valid API usage... 180 | if (closer == null) { 181 | throw new NullPointerException("closer == null"); 182 | } 183 | // ...but avoid allocating an allocationSite if disabled 184 | if (this == NOOP || !ENABLED) { 185 | return; 186 | } 187 | String message = "Explicit termination method '" + closer + "' not called"; 188 | allocationSite = new Throwable(message); 189 | } 190 | 191 | private Throwable allocationSite; 192 | 193 | /** 194 | * Marks this CloseGuard instance as closed to avoid warnings on 195 | * finalization. 196 | */ 197 | public void close() { 198 | allocationSite = null; 199 | } 200 | 201 | /** 202 | * If CloseGuard is enabled, logs a warning if the caller did not 203 | * properly cleanup by calling an explicit close method 204 | * before finalization. If CloseGuard is disabled, no action is 205 | * performed. 206 | */ 207 | public void warnIfOpen() { 208 | if (allocationSite == null || !ENABLED) { 209 | return; 210 | } 211 | 212 | String message = 213 | ("A resource was acquired at attached stack trace but never released. " 214 | + "See java.io.Closeable for information on avoiding resource leaks."); 215 | 216 | REPORTER.report(message, allocationSite); 217 | } 218 | 219 | /** 220 | * Interface to allow customization of reporting behavior. 221 | */ 222 | public interface Reporter { 223 | void report(String message, Throwable allocationSite); 224 | } 225 | 226 | /** 227 | * Default Reporter which reports CloseGuard violations to the log. 228 | */ 229 | private static final class DefaultReporter implements Reporter { 230 | @Override public void report (String message, Throwable allocationSite) { 231 | Log.w("SQLite", message, allocationSite); 232 | } 233 | } 234 | } 235 | -------------------------------------------------------------------------------- /sqlite-android/src/main/java/io/requery/android/database/sqlite/RequerySQLiteOpenHelperFactory.java: -------------------------------------------------------------------------------- 1 | package io.requery.android.database.sqlite; 2 | 3 | import android.content.Context; 4 | import androidx.sqlite.db.SupportSQLiteOpenHelper; 5 | import io.requery.android.database.DatabaseErrorHandler; 6 | 7 | import java.util.Collections; 8 | 9 | /** 10 | * Implements {@link SupportSQLiteOpenHelper.Factory} using the SQLite implementation shipped in 11 | * this library. 12 | */ 13 | @SuppressWarnings("unused") 14 | public final class RequerySQLiteOpenHelperFactory implements SupportSQLiteOpenHelper.Factory { 15 | private final Iterable configurationOptions; 16 | 17 | @SuppressWarnings("WeakerAccess") 18 | public RequerySQLiteOpenHelperFactory(Iterable configurationOptions) { 19 | this.configurationOptions = configurationOptions; 20 | } 21 | 22 | public RequerySQLiteOpenHelperFactory() { 23 | this(Collections.emptyList()); 24 | } 25 | 26 | @Override 27 | public SupportSQLiteOpenHelper create(SupportSQLiteOpenHelper.Configuration config) { 28 | return new CallbackSQLiteOpenHelper(config.context, config.name, config.callback, configurationOptions); 29 | } 30 | 31 | private static final class CallbackSQLiteOpenHelper extends SQLiteOpenHelper { 32 | 33 | private final SupportSQLiteOpenHelper.Callback callback; 34 | private final Iterable configurationOptions; 35 | 36 | CallbackSQLiteOpenHelper(Context context, String name, SupportSQLiteOpenHelper.Callback cb, Iterable ops) { 37 | super(context, name, null, cb.version, new CallbackDatabaseErrorHandler(cb)); 38 | this.callback = cb; 39 | this.configurationOptions = ops; 40 | } 41 | 42 | @Override 43 | public void onConfigure(SQLiteDatabase db) { 44 | callback.onConfigure(db); 45 | } 46 | 47 | @Override 48 | public void onCreate(SQLiteDatabase db) { 49 | callback.onCreate(db); 50 | } 51 | 52 | @Override 53 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 54 | callback.onUpgrade(db, oldVersion, newVersion); 55 | } 56 | 57 | @Override 58 | public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { 59 | callback.onDowngrade(db, oldVersion, newVersion); 60 | } 61 | 62 | @Override 63 | public void onOpen(SQLiteDatabase db) { 64 | callback.onOpen(db); 65 | } 66 | 67 | @Override protected SQLiteDatabaseConfiguration createConfiguration(String path, int openFlags) { 68 | SQLiteDatabaseConfiguration config = super.createConfiguration(path, openFlags); 69 | 70 | for (ConfigurationOptions option : configurationOptions) { 71 | config = option.apply(config); 72 | } 73 | 74 | return config; 75 | } 76 | } 77 | 78 | private static final class CallbackDatabaseErrorHandler implements DatabaseErrorHandler { 79 | 80 | private final SupportSQLiteOpenHelper.Callback callback; 81 | 82 | CallbackDatabaseErrorHandler(SupportSQLiteOpenHelper.Callback callback) { 83 | this.callback = callback; 84 | } 85 | 86 | @Override 87 | public void onCorruption(SQLiteDatabase db) { 88 | callback.onCorruption(db); 89 | } 90 | } 91 | 92 | public interface ConfigurationOptions { 93 | SQLiteDatabaseConfiguration apply(SQLiteDatabaseConfiguration configuration); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteClosable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 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 | // modified from original source see README at the top level of this project 17 | 18 | package io.requery.android.database.sqlite; 19 | 20 | import java.io.Closeable; 21 | 22 | /** 23 | * An object created from a SQLiteDatabase that can be closed. 24 | * 25 | * This class implements a primitive reference counting scheme for database objects. 26 | */ 27 | public abstract class SQLiteClosable implements Closeable { 28 | private int mReferenceCount = 1; 29 | 30 | /** 31 | * Called when the last reference to the object was released by 32 | * a call to {@link #releaseReference()} or {@link #close()}. 33 | */ 34 | protected abstract void onAllReferencesReleased(); 35 | 36 | /** 37 | * Acquires a reference to the object. 38 | * 39 | * @throws IllegalStateException if the last reference to the object has already 40 | * been released. 41 | */ 42 | public void acquireReference() { 43 | synchronized(this) { 44 | if (mReferenceCount <= 0) { 45 | throw new IllegalStateException( 46 | "attempt to re-open an already-closed object: " + this); 47 | } 48 | mReferenceCount++; 49 | } 50 | } 51 | 52 | /** 53 | * Releases a reference to the object, closing the object if the last reference 54 | * was released. 55 | * 56 | * @see #onAllReferencesReleased() 57 | */ 58 | public void releaseReference() { 59 | boolean refCountIsZero; 60 | synchronized(this) { 61 | refCountIsZero = --mReferenceCount == 0; 62 | } 63 | if (refCountIsZero) { 64 | onAllReferencesReleased(); 65 | } 66 | } 67 | 68 | /** 69 | * Releases a reference to the object, closing the object if the last reference 70 | * was released. 71 | * 72 | * Calling this method is equivalent to calling {@link #releaseReference}. 73 | * 74 | * @see #releaseReference() 75 | * @see #onAllReferencesReleased() 76 | */ 77 | public void close() { 78 | releaseReference(); 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteCursorDriver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 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 | // modified from original source see README at the top level of this project 17 | 18 | package io.requery.android.database.sqlite; 19 | 20 | import android.database.Cursor; 21 | 22 | /** 23 | * A driver for SQLiteCursors that is used to create them and gets notified 24 | * by the cursors it creates on significant events in their lifetimes. 25 | */ 26 | public interface SQLiteCursorDriver { 27 | /** 28 | * Executes the query returning a Cursor over the result set. 29 | * 30 | * @param factory The CursorFactory to use when creating the Cursors, or 31 | * null if standard SQLiteCursors should be returned. 32 | * @return a Cursor over the result set 33 | */ 34 | Cursor query(SQLiteDatabase.CursorFactory factory, Object[] bindArgs); 35 | 36 | /** 37 | * Called by a SQLiteCursor when it is released. 38 | */ 39 | void cursorDeactivated(); 40 | 41 | /** 42 | * Called by a SQLiteCursor when it is requeried. 43 | */ 44 | void cursorRequeried(Cursor cursor); 45 | 46 | /** 47 | * Called by a SQLiteCursor when it it closed to destroy this object as well. 48 | */ 49 | void cursorClosed(); 50 | 51 | /** 52 | * Set new bind arguments. These will take effect in cursorRequeried(). 53 | * @param bindArgs the new arguments 54 | */ 55 | void setBindArguments(String[] bindArgs); 56 | } 57 | -------------------------------------------------------------------------------- /sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteCustomExtension.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2016 requery.io 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 io.requery.android.database.sqlite; 18 | 19 | /** 20 | * Describes a SQLite extension entry point. 21 | */ 22 | public final class SQLiteCustomExtension { 23 | 24 | public final String path; 25 | public final String entryPoint; 26 | 27 | /** 28 | * Creates a SQLite extension description. 29 | * 30 | * @param path path to the loadable extension shared library 31 | * e.g. "/data/data/(package)/lib/libSqliteICU.so" 32 | * @param entryPoint extension entry point (optional) 33 | * e.g. "sqlite3_icu_init" 34 | */ 35 | public SQLiteCustomExtension(String path, String entryPoint) { 36 | if (path == null) { 37 | throw new IllegalArgumentException("null path"); 38 | } 39 | this.path = path; 40 | this.entryPoint = entryPoint; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteCustomFunction.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 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 | // modified from original source see README at the top level of this project 17 | 18 | package io.requery.android.database.sqlite; 19 | 20 | /** 21 | * Describes a custom SQL function. 22 | * 23 | * @hide 24 | */ 25 | public final class SQLiteCustomFunction { 26 | public final String name; 27 | public final int numArgs; 28 | public final SQLiteDatabase.CustomFunction callback; 29 | 30 | /** 31 | * Create custom function. 32 | * 33 | * @param name The name of the sqlite3 function. 34 | * @param numArgs The number of arguments for the function, or -1 to 35 | * support any number of arguments. 36 | * @param callback The callback to invoke when the function is executed. 37 | */ 38 | public SQLiteCustomFunction(String name, int numArgs, 39 | SQLiteDatabase.CustomFunction callback) { 40 | if (name == null) { 41 | throw new IllegalArgumentException("name must not be null."); 42 | } 43 | 44 | this.name = name; 45 | this.numArgs = numArgs; 46 | this.callback = callback; 47 | } 48 | 49 | // Called from native. 50 | @SuppressWarnings("unused") 51 | private String dispatchCallback(String[] args) { 52 | return callback.callback(args); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteDatabaseConfiguration.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 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 | // modified from original source see README at the top level of this project 17 | 18 | package io.requery.android.database.sqlite; 19 | 20 | import java.util.ArrayList; 21 | import java.util.List; 22 | import java.util.Locale; 23 | import java.util.regex.Pattern; 24 | 25 | /** 26 | * Describes how to configure a database. 27 | *

28 | * The purpose of this object is to keep track of all of the little 29 | * configuration settings that are applied to a database after it 30 | * is opened so that they can be applied to all connections in the 31 | * connection pool uniformly. 32 | *

33 | * Each connection maintains its own copy of this object so it can 34 | * keep track of which settings have already been applied. 35 | *

36 | * 37 | * @hide 38 | */ 39 | public final class SQLiteDatabaseConfiguration { 40 | // The pattern we use to strip email addresses from database paths 41 | // when constructing a label to use in log messages. 42 | private static final Pattern EMAIL_IN_DB_PATTERN = 43 | Pattern.compile("[\\w\\.\\-]+@[\\w\\.\\-]+"); 44 | 45 | /** 46 | * Special path used by in-memory databases. 47 | */ 48 | public static final String MEMORY_DB_PATH = ":memory:"; 49 | 50 | /** 51 | * The database path. 52 | */ 53 | public final String path; 54 | 55 | /** 56 | * The label to use to describe the database when it appears in logs. 57 | * This is derived from the path but is stripped to remove PII. 58 | */ 59 | public final String label; 60 | 61 | /** 62 | * The flags used to open the database. 63 | */ 64 | public @SQLiteDatabase.OpenFlags int openFlags; 65 | 66 | public SQLiteUpdateHook sqliteUpdateHook; 67 | 68 | /** 69 | * The maximum size of the prepared statement cache for each database connection. 70 | * Must be non-negative. 71 | * 72 | * Default is 25. 73 | */ 74 | public int maxSqlCacheSize; 75 | 76 | /** 77 | * The database locale. 78 | * 79 | * Default is the value returned by {@link Locale#getDefault()}. 80 | */ 81 | public Locale locale; 82 | 83 | /** 84 | * True if foreign key constraints are enabled. 85 | * 86 | * Default is false. 87 | */ 88 | public boolean foreignKeyConstraintsEnabled; 89 | 90 | /** 91 | * The custom functions to register. 92 | * 93 | * This interface is deprecated; see {@link SQLiteFunction} 94 | */ 95 | @Deprecated 96 | public final List customFunctions = new ArrayList<>(); 97 | 98 | /** 99 | * The {@link SQLiteFunction}s to register. 100 | */ 101 | public final List functions = new ArrayList<>(); 102 | 103 | /** 104 | * The custom extensions to register. 105 | */ 106 | public final List customExtensions = new ArrayList<>(); 107 | 108 | /** 109 | * Creates a database configuration with the required parameters for opening a 110 | * database and default values for all other parameters. 111 | * 112 | * @param path The database path. 113 | * @param openFlags Open flags for the database, such as {@link SQLiteDatabase#OPEN_READWRITE}. 114 | */ 115 | public SQLiteDatabaseConfiguration(String path, @SQLiteDatabase.OpenFlags int openFlags) { 116 | if (path == null) { 117 | throw new IllegalArgumentException("path must not be null."); 118 | } 119 | 120 | this.path = path; 121 | label = stripPathForLogs(path); 122 | this.openFlags = openFlags; 123 | 124 | // Set default values for optional parameters. 125 | maxSqlCacheSize = 25; 126 | locale = Locale.getDefault(); 127 | } 128 | 129 | /** 130 | * Creates a database configuration with the required parameters for opening a 131 | * database and default values for all other parameters. 132 | * 133 | * @param path The database path. 134 | * @param openFlags Open flags for the database, such as {@link SQLiteDatabase#OPEN_READWRITE}. 135 | * @param functions custom functions to use. 136 | * @param extensions custom extensions to use. 137 | */ 138 | public SQLiteDatabaseConfiguration(String path, 139 | @SQLiteDatabase.OpenFlags int openFlags, 140 | List customFunctions, 141 | List functions, 142 | List extensions) { 143 | this(path, openFlags); 144 | this.customFunctions.addAll(customFunctions); 145 | this.customExtensions.addAll(extensions); 146 | this.functions.addAll(functions); 147 | } 148 | 149 | /** 150 | * Creates a database configuration as a copy of another configuration. 151 | * 152 | * @param other The other configuration. 153 | */ 154 | SQLiteDatabaseConfiguration(SQLiteDatabaseConfiguration other) { 155 | if (other == null) { 156 | throw new IllegalArgumentException("other must not be null."); 157 | } 158 | 159 | this.path = other.path; 160 | this.label = other.label; 161 | updateParametersFrom(other); 162 | } 163 | 164 | /** 165 | * Updates the non-immutable parameters of this configuration object 166 | * from the other configuration object. 167 | * 168 | * @param other The object from which to copy the parameters. 169 | */ 170 | void updateParametersFrom(SQLiteDatabaseConfiguration other) { 171 | if (other == null) { 172 | throw new IllegalArgumentException("other must not be null."); 173 | } 174 | if (!path.equals(other.path)) { 175 | throw new IllegalArgumentException("other configuration must refer to " 176 | + "the same database."); 177 | } 178 | 179 | openFlags = other.openFlags; 180 | maxSqlCacheSize = other.maxSqlCacheSize; 181 | locale = other.locale; 182 | foreignKeyConstraintsEnabled = other.foreignKeyConstraintsEnabled; 183 | customFunctions.clear(); 184 | customFunctions.addAll(other.customFunctions); 185 | customExtensions.clear(); 186 | customExtensions.addAll(other.customExtensions); 187 | functions.clear(); 188 | functions.addAll(other.functions); 189 | sqliteUpdateHook = other.sqliteUpdateHook; 190 | } 191 | 192 | /** 193 | * Returns true if the database is in-memory. 194 | * @return True if the database is in-memory. 195 | */ 196 | public boolean isInMemoryDb() { 197 | return path.equalsIgnoreCase(MEMORY_DB_PATH); 198 | } 199 | 200 | private static String stripPathForLogs(String path) { 201 | if (path.indexOf('@') == -1) { 202 | return path; 203 | } 204 | return EMAIL_IN_DB_PATTERN.matcher(path).replaceAll("XX@YY"); 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteDebug.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 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 | // modified from original source see README at the top level of this project 17 | 18 | package io.requery.android.database.sqlite; 19 | 20 | import android.util.Log; 21 | import android.util.Printer; 22 | 23 | import java.util.ArrayList; 24 | 25 | /** 26 | * Provides debugging info about all SQLite databases running in the current process. 27 | * 28 | * {@hide} 29 | */ 30 | @SuppressWarnings("unused") 31 | public final class SQLiteDebug { 32 | private static native void nativeGetPagerStats(PagerStats stats); 33 | 34 | /** 35 | * Controls the printing of informational SQL log messages. 36 | * 37 | * Enable using "adb shell setprop log.tag.SQLiteLog VERBOSE". 38 | */ 39 | public static final boolean DEBUG_SQL_LOG = 40 | Log.isLoggable("SQLiteLog", Log.VERBOSE); 41 | 42 | /** 43 | * Controls the printing of SQL statements as they are executed. 44 | * 45 | * Enable using "adb shell setprop log.tag.SQLiteStatements VERBOSE". 46 | */ 47 | public static final boolean DEBUG_SQL_STATEMENTS = 48 | Log.isLoggable("SQLiteStatements", Log.VERBOSE); 49 | 50 | /** 51 | * Controls the printing of wall-clock time taken to execute SQL statements 52 | * as they are executed. 53 | * 54 | * Enable using "adb shell setprop log.tag.SQLiteTime VERBOSE". 55 | */ 56 | public static final boolean DEBUG_SQL_TIME = 57 | Log.isLoggable("SQLiteTime", Log.VERBOSE); 58 | 59 | /** 60 | * True to enable database performance testing instrumentation. 61 | * @hide 62 | */ 63 | public static final boolean DEBUG_LOG_SLOW_QUERIES = false; 64 | 65 | private SQLiteDebug() { 66 | } 67 | 68 | /** 69 | * Determines whether a query should be logged. 70 | * 71 | * Reads the "db.log.slow_query_threshold" system property, which can be changed 72 | * by the user at any time. If the value is zero, then all queries will 73 | * be considered slow. If the value does not exist or is negative, then no queries will 74 | * be considered slow. 75 | * 76 | * This value can be changed dynamically while the system is running. 77 | * For example, "adb shell setprop db.log.slow_query_threshold 200" will 78 | * log all queries that take 200ms or longer to run. 79 | * @hide 80 | */ 81 | public static boolean shouldLogSlowQuery(long elapsedTimeMillis) { 82 | int slowQueryMillis = Integer.parseInt( 83 | System.getProperty("db.log.slow_query_threshold", "-1")); 84 | return slowQueryMillis >= 0 && elapsedTimeMillis >= slowQueryMillis; 85 | } 86 | 87 | /** 88 | * Contains statistics about the active pagers in the current process. 89 | * 90 | * @see #nativeGetPagerStats(PagerStats) 91 | */ 92 | public static class PagerStats { 93 | /** the current amount of memory checked out by sqlite using sqlite3_malloc(). 94 | * documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html 95 | */ 96 | public int memoryUsed; 97 | 98 | /** the number of bytes of page cache allocation which could not be sattisfied by the 99 | * SQLITE_CONFIG_PAGECACHE buffer and where forced to overflow to sqlite3_malloc(). 100 | * The returned value includes allocations that overflowed because they where too large 101 | * (they were larger than the "sz" parameter to SQLITE_CONFIG_PAGECACHE) and allocations 102 | * that overflowed because no space was left in the page cache. 103 | * documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html 104 | */ 105 | public int pageCacheOverflow; 106 | 107 | /** records the largest memory allocation request handed to sqlite3. 108 | * documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html 109 | */ 110 | public int largestMemAlloc; 111 | 112 | /** a list of {@link DbStats} - one for each main database opened by the applications 113 | * running on the android device 114 | */ 115 | public ArrayList dbStats; 116 | } 117 | 118 | /** 119 | * contains statistics about a database 120 | */ 121 | public static class DbStats { 122 | /** name of the database */ 123 | public String dbName; 124 | 125 | /** the page size for the database */ 126 | public long pageSize; 127 | 128 | /** the database size */ 129 | public long dbSize; 130 | 131 | /** documented here http://www.sqlite.org/c3ref/c_dbstatus_lookaside_used.html */ 132 | public int lookaside; 133 | 134 | /** statement cache stats: hits/misses/cachesize */ 135 | public String cache; 136 | 137 | public DbStats(String dbName, long pageCount, long pageSize, int lookaside, 138 | int hits, int misses, int cachesize) { 139 | this.dbName = dbName; 140 | this.pageSize = pageSize / 1024; 141 | dbSize = (pageCount * pageSize) / 1024; 142 | this.lookaside = lookaside; 143 | this.cache = hits + "/" + misses + "/" + cachesize; 144 | } 145 | } 146 | 147 | /** 148 | * return all pager and database stats for the current process. 149 | * @return {@link PagerStats} 150 | */ 151 | public static PagerStats getDatabaseInfo() { 152 | PagerStats stats = new PagerStats(); 153 | nativeGetPagerStats(stats); 154 | stats.dbStats = SQLiteDatabase.getDbStats(); 155 | return stats; 156 | } 157 | 158 | /** 159 | * Dumps detailed information about all databases used by the process. 160 | * @param printer The printer for dumping database state. 161 | * @param args Command-line arguments supplied to dumpsys dbinfo 162 | */ 163 | public static void dump(Printer printer, String[] args) { 164 | boolean verbose = false; 165 | for (String arg : args) { 166 | if (arg.equals("-v")) { 167 | verbose = true; 168 | } 169 | } 170 | 171 | SQLiteDatabase.dumpAll(printer, verbose); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteDirectCursorDriver.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 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 | // modified from original source see README at the top level of this project 17 | 18 | package io.requery.android.database.sqlite; 19 | 20 | import android.database.Cursor; 21 | import androidx.core.os.CancellationSignal; 22 | 23 | /** 24 | * A cursor driver that uses the given query directly. 25 | * 26 | * @hide 27 | */ 28 | public final class SQLiteDirectCursorDriver implements SQLiteCursorDriver { 29 | private final SQLiteDatabase mDatabase; 30 | private final String mEditTable; 31 | private final String mSql; 32 | private final CancellationSignal mCancellationSignal; 33 | private SQLiteQuery mQuery; 34 | 35 | public SQLiteDirectCursorDriver(SQLiteDatabase db, String sql, String editTable, 36 | CancellationSignal cancellationSignal) { 37 | mDatabase = db; 38 | mEditTable = editTable; 39 | mSql = sql; 40 | mCancellationSignal = cancellationSignal; 41 | } 42 | 43 | public Cursor query(SQLiteDatabase.CursorFactory factory, Object[] selectionArgs) { 44 | SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, selectionArgs, mCancellationSignal); 45 | final Cursor cursor; 46 | try { 47 | if (factory == null) { 48 | cursor = new SQLiteCursor(this, mEditTable, query); 49 | } else { 50 | cursor = factory.newCursor(mDatabase, this, mEditTable, query); 51 | } 52 | } catch (RuntimeException ex) { 53 | query.close(); 54 | throw ex; 55 | } 56 | 57 | mQuery = query; 58 | return cursor; 59 | } 60 | 61 | @Override 62 | public void cursorClosed() { 63 | // Do nothing 64 | } 65 | 66 | @Override 67 | public void setBindArguments(String[] bindArgs) { 68 | mQuery.bindAllArgsAsStrings(bindArgs); 69 | } 70 | 71 | @Override 72 | public void cursorDeactivated() { 73 | // Do nothing 74 | } 75 | 76 | @Override 77 | public void cursorRequeried(Cursor cursor) { 78 | // Do nothing 79 | } 80 | 81 | @Override 82 | public String toString() { 83 | return "SQLiteDirectCursorDriver: " + mSql; 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteFunction.java: -------------------------------------------------------------------------------- 1 | package io.requery.android.database.sqlite; 2 | 3 | /** 4 | * @author dhleong 5 | */ 6 | public class SQLiteFunction { 7 | public final String name; 8 | public final int numArgs; 9 | public final SQLiteDatabase.Function callback; 10 | 11 | // accessed from native code 12 | final int flags; 13 | 14 | // NOTE: from a single database connection, all calls to 15 | // functions are serialized by SQLITE-internal mutexes, 16 | // so we save on GC churn by reusing a single, shared instance 17 | private final MyArgs args = new MyArgs(); 18 | private final MyResult result = new MyResult(); 19 | 20 | /** 21 | * Create custom function. 22 | * 23 | * @param name The name of the sqlite3 function. 24 | * @param numArgs The number of arguments for the function, or -1 to 25 | * support any number of arguments. 26 | * @param callback The callback to invoke when the function is executed. 27 | * @param flags Extra SQLITE flags to pass when creating the function 28 | * in native code. 29 | */ 30 | public SQLiteFunction(String name, int numArgs, 31 | SQLiteDatabase.Function callback) { 32 | this(name, numArgs, callback, 0); 33 | } 34 | 35 | /** 36 | * Create custom function. 37 | * 38 | * @param name The name of the sqlite3 function. 39 | * @param numArgs The number of arguments for the function, or -1 to 40 | * support any number of arguments. 41 | * @param callback The callback to invoke when the function is executed. 42 | * @param flags Extra SQLITE flags to pass when creating the function 43 | * in native code. 44 | */ 45 | public SQLiteFunction(String name, int numArgs, 46 | SQLiteDatabase.Function callback, 47 | int flags) { 48 | if (name == null) { 49 | throw new IllegalArgumentException("name must not be null."); 50 | } 51 | 52 | this.name = name; 53 | this.numArgs = numArgs; 54 | this.callback = callback; 55 | this.flags = flags; 56 | } 57 | 58 | // Called from native. 59 | @SuppressWarnings("unused") 60 | private void dispatchCallback(long contextPtr, long argsPtr, int argsCount) { 61 | result.contextPtr = contextPtr; 62 | args.argsPtr = argsPtr; 63 | args.argsCount = argsCount; 64 | 65 | try { 66 | callback.callback(args, result); 67 | 68 | if (!result.isSet) { 69 | result.setNull(); 70 | } 71 | 72 | } finally { 73 | result.contextPtr = 0; 74 | result.isSet = false; 75 | args.argsPtr = 0; 76 | args.argsCount = 0; 77 | } 78 | } 79 | 80 | static native byte[] nativeGetArgBlob(long argsPtr, int arg); 81 | static native String nativeGetArgString(long argsPtr, int arg); 82 | static native double nativeGetArgDouble(long argsPtr, int arg); 83 | static native int nativeGetArgInt(long argsPtr, int arg); 84 | static native long nativeGetArgLong(long argsPtr, int arg); 85 | 86 | static native void nativeSetResultBlob(long contextPtr, byte[] result); 87 | static native void nativeSetResultString(long contextPtr, String result); 88 | static native void nativeSetResultDouble(long contextPtr, double result); 89 | static native void nativeSetResultInt(long contextPtr, int result); 90 | static native void nativeSetResultLong(long contextPtr, long result); 91 | static native void nativeSetResultError(long contextPtr, String error); 92 | static native void nativeSetResultNull(long contextPtr); 93 | 94 | private static class MyArgs implements SQLiteDatabase.Function.Args { 95 | long argsPtr; 96 | int argsCount; 97 | 98 | @Override 99 | public byte[] getBlob(int arg) { 100 | return nativeGetArgBlob(argsPtr, checkArg(arg)); 101 | } 102 | 103 | @Override 104 | public String getString(int arg) { 105 | return nativeGetArgString(argsPtr, checkArg(arg)); 106 | } 107 | 108 | @Override 109 | public double getDouble(int arg) { 110 | return nativeGetArgDouble(argsPtr, checkArg(arg)); 111 | } 112 | 113 | @Override 114 | public int getInt(int arg) { 115 | return nativeGetArgInt(argsPtr, checkArg(arg)); 116 | } 117 | 118 | @Override 119 | public long getLong(int arg) { 120 | return nativeGetArgLong(argsPtr, checkArg(arg)); 121 | } 122 | 123 | private int checkArg(int arg) { 124 | if (arg < 0 || arg >= argsCount) { 125 | throw new IllegalArgumentException( 126 | "Requested arg " + arg + " but had " + argsCount 127 | ); 128 | } 129 | 130 | return arg; 131 | } 132 | } 133 | 134 | private static class MyResult implements SQLiteDatabase.Function.Result { 135 | long contextPtr; 136 | boolean isSet; 137 | 138 | @Override 139 | public void set(byte[] value) { 140 | checkSet(); 141 | nativeSetResultBlob(contextPtr, value); 142 | } 143 | 144 | @Override 145 | public void set(double value) { 146 | checkSet(); 147 | nativeSetResultDouble(contextPtr, value); 148 | } 149 | 150 | @Override 151 | public void set(int value) { 152 | checkSet(); 153 | nativeSetResultInt(contextPtr, value); 154 | } 155 | 156 | @Override 157 | public void set(long value) { 158 | checkSet(); 159 | nativeSetResultLong(contextPtr, value); 160 | } 161 | 162 | @Override 163 | public void set(String value) { 164 | checkSet(); 165 | nativeSetResultString(contextPtr, value); 166 | } 167 | 168 | @Override 169 | public void setError(String error) { 170 | checkSet(); 171 | nativeSetResultError(contextPtr, error); 172 | } 173 | 174 | @Override 175 | public void setNull() { 176 | checkSet(); 177 | nativeSetResultNull(contextPtr); 178 | } 179 | 180 | private void checkSet() { 181 | if (isSet) throw new IllegalStateException("Result is already set"); 182 | isSet = true; 183 | } 184 | } 185 | } 186 | -------------------------------------------------------------------------------- /sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteGlobal.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 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 | // modified from original source see README at the top level of this project 17 | /* 18 | ** Modified to support SQLite extensions by the SQLite developers: 19 | ** sqlite-dev@sqlite.org. 20 | */ 21 | 22 | package io.requery.android.database.sqlite; 23 | 24 | import android.os.StatFs; 25 | 26 | /** 27 | * Provides access to SQLite functions that affect all database connection, 28 | * such as memory management. 29 | * 30 | * The native code associated with SQLiteGlobal is also sets global configuration options 31 | * using sqlite3_config() then calls sqlite3_initialize() to ensure that the SQLite 32 | * library is properly initialized exactly once before any other framework or application 33 | * code has a chance to run. 34 | * 35 | * Verbose SQLite logging is enabled if the "log.tag.SQLiteLog" property is set to "V". 36 | * (per {@link SQLiteDebug#DEBUG_SQL_LOG}). 37 | * 38 | * @hide 39 | */ 40 | public final class SQLiteGlobal { 41 | private static final Object sLock = new Object(); 42 | private static int sDefaultPageSize; 43 | 44 | private static native int nativeReleaseMemory(); 45 | 46 | private SQLiteGlobal() { 47 | } 48 | 49 | /** 50 | * Attempts to release memory by pruning the SQLite page cache and other 51 | * internal data structures. 52 | * 53 | * @return The number of bytes that were freed. 54 | */ 55 | public static int releaseMemory() { 56 | return nativeReleaseMemory(); 57 | } 58 | 59 | // values derived from: 60 | // https://android.googlesource.com/platform/frameworks/base.git/+/master/core/res/res/values/config.xml 61 | 62 | /** 63 | * Gets the default page size to use when creating a database. 64 | */ 65 | @SuppressWarnings("deprecation") 66 | public static int getDefaultPageSize() { 67 | synchronized (sLock) { 68 | if (sDefaultPageSize == 0) { 69 | sDefaultPageSize = new StatFs("/data").getBlockSize(); 70 | } 71 | return 1024; 72 | } 73 | } 74 | 75 | /** 76 | * Gets the default journal mode when WAL is not in use. 77 | */ 78 | public static String getDefaultJournalMode() { 79 | return "TRUNCATE"; 80 | } 81 | 82 | /** 83 | * Gets the journal size limit in bytes. 84 | */ 85 | public static int getJournalSizeLimit() { 86 | return 524288; 87 | } 88 | 89 | /** 90 | * Gets the default database synchronization mode when WAL is not in use. 91 | */ 92 | public static String getDefaultSyncMode() { 93 | return "FULL"; 94 | } 95 | 96 | /** 97 | * Gets the database synchronization mode when in WAL mode. 98 | */ 99 | public static String getWALSyncMode() { 100 | return "normal"; 101 | } 102 | 103 | /** 104 | * Gets the WAL auto-checkpoint integer in database pages. 105 | */ 106 | public static int getWALAutoCheckpoint() { 107 | return 1000; 108 | } 109 | 110 | /** 111 | * Gets the connection pool size when in WAL mode. 112 | */ 113 | public static int getWALConnectionPoolSize() { 114 | return 10; 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteProgram.java: -------------------------------------------------------------------------------- 1 | /* 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 | // modified from original source see README at the top level of this project 17 | 18 | package io.requery.android.database.sqlite; 19 | 20 | import androidx.core.os.CancellationSignal; 21 | import androidx.sqlite.db.SupportSQLiteProgram; 22 | 23 | import java.util.Arrays; 24 | 25 | /** 26 | * A base class for compiled SQLite programs. 27 | *

28 | * This class is not thread-safe. 29 | *

30 | */ 31 | @SuppressWarnings("unused") 32 | public abstract class SQLiteProgram extends SQLiteClosable implements SupportSQLiteProgram { 33 | private static final String[] EMPTY_STRING_ARRAY = new String[0]; 34 | 35 | private final SQLiteDatabase mDatabase; 36 | private final String mSql; 37 | private final boolean mReadOnly; 38 | private final String[] mColumnNames; 39 | private final int mNumParameters; 40 | private final Object[] mBindArgs; 41 | 42 | SQLiteProgram(SQLiteDatabase db, String sql, Object[] bindArgs, 43 | CancellationSignal cancellationSignalForPrepare) { 44 | mDatabase = db; 45 | mSql = sql.trim(); 46 | 47 | int n = SQLiteStatementType.getSqlStatementType(mSql); 48 | switch (n) { 49 | case SQLiteStatementType.STATEMENT_BEGIN: 50 | case SQLiteStatementType.STATEMENT_COMMIT: 51 | case SQLiteStatementType.STATEMENT_ABORT: 52 | mReadOnly = false; 53 | mColumnNames = EMPTY_STRING_ARRAY; 54 | mNumParameters = 0; 55 | break; 56 | 57 | default: 58 | boolean assumeReadOnly = (n == SQLiteStatementType.STATEMENT_SELECT); 59 | SQLiteStatementInfo info = new SQLiteStatementInfo(); 60 | db.getThreadSession().prepare(mSql, 61 | db.getThreadDefaultConnectionFlags(assumeReadOnly), 62 | cancellationSignalForPrepare, info); 63 | mReadOnly = info.readOnly; 64 | mColumnNames = info.columnNames; 65 | mNumParameters = info.numParameters; 66 | break; 67 | } 68 | 69 | if (bindArgs != null && bindArgs.length > mNumParameters) { 70 | throw new IllegalArgumentException("Too many bind arguments. " 71 | + bindArgs.length + " arguments were provided but the statement needs " 72 | + mNumParameters + " arguments."); 73 | } 74 | 75 | if (mNumParameters != 0) { 76 | mBindArgs = new Object[mNumParameters]; 77 | if (bindArgs != null) { 78 | System.arraycopy(bindArgs, 0, mBindArgs, 0, bindArgs.length); 79 | } 80 | } else { 81 | mBindArgs = null; 82 | } 83 | } 84 | 85 | final SQLiteDatabase getDatabase() { 86 | return mDatabase; 87 | } 88 | 89 | final String getSql() { 90 | return mSql; 91 | } 92 | 93 | final Object[] getBindArgs() { 94 | return mBindArgs; 95 | } 96 | 97 | final String[] getColumnNames() { 98 | return mColumnNames; 99 | } 100 | 101 | /** @hide */ 102 | protected final SQLiteSession getSession() { 103 | return mDatabase.getThreadSession(); 104 | } 105 | 106 | /** @hide */ 107 | protected final int getConnectionFlags() { 108 | return mDatabase.getThreadDefaultConnectionFlags(mReadOnly); 109 | } 110 | 111 | /** @hide */ 112 | protected final void onCorruption() { 113 | mDatabase.onCorruption(); 114 | } 115 | 116 | /** 117 | * Bind a NULL value to this statement. The value remains bound until 118 | * {@link #clearBindings} is called. 119 | * 120 | * @param index The 1-based index to the parameter to bind null to 121 | */ 122 | @Override 123 | public void bindNull(int index) { 124 | bind(index, null); 125 | } 126 | 127 | /** 128 | * Bind a long value to this statement. The value remains bound until 129 | * {@link #clearBindings} is called. 130 | *addToBindArgs 131 | * @param index The 1-based index to the parameter to bind 132 | * @param value The value to bind 133 | */ 134 | @Override 135 | public void bindLong(int index, long value) { 136 | bind(index, value); 137 | } 138 | 139 | /** 140 | * Bind a double value to this statement. The value remains bound until 141 | * {@link #clearBindings} is called. 142 | * 143 | * @param index The 1-based index to the parameter to bind 144 | * @param value The value to bind 145 | */ 146 | @Override 147 | public void bindDouble(int index, double value) { 148 | bind(index, value); 149 | } 150 | 151 | /** 152 | * Bind a String value to this statement. The value remains bound until 153 | * {@link #clearBindings} is called. 154 | * 155 | * @param index The 1-based index to the parameter to bind 156 | * @param value The value to bind, must not be null 157 | */ 158 | @Override 159 | public void bindString(int index, String value) { 160 | if (value == null) { 161 | throw new IllegalArgumentException("the bind value at index " + index + " is null"); 162 | } 163 | bind(index, value); 164 | } 165 | 166 | /** 167 | * Bind a byte array value to this statement. The value remains bound until 168 | * {@link #clearBindings} is called. 169 | * 170 | * @param index The 1-based index to the parameter to bind 171 | * @param value The value to bind, must not be null 172 | */ 173 | @Override 174 | public void bindBlob(int index, byte[] value) { 175 | if (value == null) { 176 | throw new IllegalArgumentException("the bind value at index " + index + " is null"); 177 | } 178 | bind(index, value); 179 | } 180 | 181 | /** 182 | * Binds the given Object to the given SQLiteProgram using the proper 183 | * typing. For example, bind numbers as longs/doubles, and everything else 184 | * as a string by call toString() on it. 185 | * 186 | * @param index the 1-based index to bind at 187 | * @param value the value to bind 188 | */ 189 | public void bindObject(int index, Object value) { 190 | if (value == null) { 191 | bindNull(index); 192 | } else if (value instanceof Double || value instanceof Float) { 193 | bindDouble(index, ((Number)value).doubleValue()); 194 | } else if (value instanceof Number) { 195 | bindLong(index, ((Number)value).longValue()); 196 | } else if (value instanceof Boolean) { 197 | Boolean bool = (Boolean)value; 198 | if (bool) { 199 | bindLong(index, 1); 200 | } else { 201 | bindLong(index, 0); 202 | } 203 | } else if (value instanceof byte[]){ 204 | bindBlob(index, (byte[]) value); 205 | } else { 206 | bindString(index, value.toString()); 207 | } 208 | } 209 | 210 | /** 211 | * Clears all existing bindings. Unset bindings are treated as NULL. 212 | */ 213 | @Override 214 | public void clearBindings() { 215 | if (mBindArgs != null) { 216 | Arrays.fill(mBindArgs, null); 217 | } 218 | } 219 | 220 | /** 221 | * Given an array of String bindArgs, this method binds all of them in one single call. 222 | * 223 | * @param bindArgs the String array of bind args, none of which must be null. 224 | */ 225 | public void bindAllArgsAsStrings(String[] bindArgs) { 226 | if (bindArgs != null) { 227 | for (int i = bindArgs.length; i != 0; i--) { 228 | bindString(i, bindArgs[i - 1]); 229 | } 230 | } 231 | } 232 | 233 | @Override 234 | protected void onAllReferencesReleased() { 235 | clearBindings(); 236 | } 237 | 238 | private void bind(int index, Object value) { 239 | if (index < 1 || index > mNumParameters) { 240 | throw new IllegalArgumentException("Cannot bind argument at index " 241 | + index + " because the index is out of range. " 242 | + "The statement has " + mNumParameters + " parameters."); 243 | } 244 | mBindArgs[index - 1] = value; 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteQuery.java: -------------------------------------------------------------------------------- 1 | /* 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 | // modified from original source see README at the top level of this project 17 | 18 | package io.requery.android.database.sqlite; 19 | 20 | import android.database.sqlite.SQLiteDatabaseCorruptException; 21 | import android.database.sqlite.SQLiteException; 22 | import android.util.Log; 23 | import androidx.core.os.CancellationSignal; 24 | import androidx.core.os.OperationCanceledException; 25 | import io.requery.android.database.CursorWindow; 26 | 27 | /** 28 | * Represents a query that reads the resulting rows into a {@link SQLiteQuery}. 29 | * This class is used by {@link SQLiteCursor} and isn't useful itself. 30 | *

31 | * This class is not thread-safe. 32 | *

33 | */ 34 | public final class SQLiteQuery extends SQLiteProgram { 35 | private static final String TAG = "SQLiteQuery"; 36 | 37 | private final CancellationSignal mCancellationSignal; 38 | 39 | SQLiteQuery(SQLiteDatabase db, String query, Object[] bindArgs, 40 | CancellationSignal cancellationSignal) { 41 | super(db, query, bindArgs, cancellationSignal); 42 | mCancellationSignal = cancellationSignal; 43 | } 44 | 45 | /** 46 | * Reads rows into a buffer. 47 | * 48 | * @param window The window to fill into 49 | * @param startPos The start position for filling the window. 50 | * @param requiredPos The position of a row that MUST be in the window. 51 | * If it won't fit, then the query should discard part of what it filled. 52 | * @param countAllRows True to count all rows that the query would 53 | * return regardless of whether they fit in the window. 54 | * @return Number of rows that were enumerated. Might not be all rows 55 | * unless countAllRows is true. 56 | * 57 | * @throws SQLiteException if an error occurs. 58 | * @throws OperationCanceledException if the operation was canceled. 59 | */ 60 | int fillWindow(CursorWindow window, int startPos, int requiredPos, boolean countAllRows) { 61 | acquireReference(); 62 | try { 63 | window.acquireReference(); 64 | try { 65 | return getSession().executeForCursorWindow(getSql(), getBindArgs(), 66 | window, startPos, requiredPos, countAllRows, getConnectionFlags(), 67 | mCancellationSignal); 68 | } catch (SQLiteDatabaseCorruptException ex) { 69 | onCorruption(); 70 | throw ex; 71 | } catch (SQLiteException ex) { 72 | Log.e(TAG, "exception: " + ex.getMessage() + "; query: " + getSql()); 73 | throw ex; 74 | } finally { 75 | window.releaseReference(); 76 | } 77 | } finally { 78 | releaseReference(); 79 | } 80 | } 81 | 82 | @Override 83 | public String toString() { 84 | return "SQLiteQuery: " + getSql(); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteStatement.java: -------------------------------------------------------------------------------- 1 | /* 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 | // modified from original source see README at the top level of this project 17 | 18 | package io.requery.android.database.sqlite; 19 | 20 | import android.database.SQLException; 21 | import android.database.sqlite.SQLiteDatabaseCorruptException; 22 | import android.database.sqlite.SQLiteDoneException; 23 | import android.os.ParcelFileDescriptor; 24 | import androidx.sqlite.db.SupportSQLiteStatement; 25 | 26 | /** 27 | * Represents a statement that can be executed against a database. The statement 28 | * cannot return multiple rows or columns, but single value (1 x 1) result sets 29 | * are supported. 30 | *

31 | * This class is not thread-safe. 32 | *

33 | */ 34 | @SuppressWarnings("unused") 35 | public final class SQLiteStatement extends SQLiteProgram implements SupportSQLiteStatement { 36 | 37 | SQLiteStatement(SQLiteDatabase db, String sql, Object[] bindArgs) { 38 | super(db, sql, bindArgs, null); 39 | } 40 | 41 | /** 42 | * Execute this SQL statement, if it is not a SELECT / INSERT / DELETE / UPDATE, for example 43 | * CREATE / DROP table, view, trigger, index etc. 44 | * 45 | * @throws SQLException If the SQL string is invalid for some reason 46 | */ 47 | @Override 48 | public void execute() { 49 | acquireReference(); 50 | try { 51 | getSession().execute(getSql(), getBindArgs(), getConnectionFlags(), null); 52 | } catch (SQLiteDatabaseCorruptException ex) { 53 | onCorruption(); 54 | throw ex; 55 | } finally { 56 | releaseReference(); 57 | } 58 | } 59 | 60 | /** 61 | * Execute this SQL statement, if the the number of rows affected by execution of this SQL 62 | * statement is of any importance to the caller - for example, UPDATE / DELETE SQL statements. 63 | * 64 | * @return the number of rows affected by this SQL statement execution. 65 | * @throws SQLException If the SQL string is invalid for some reason 66 | */ 67 | @Override 68 | public int executeUpdateDelete() { 69 | acquireReference(); 70 | try { 71 | return getSession().executeForChangedRowCount( 72 | getSql(), getBindArgs(), getConnectionFlags(), null); 73 | } catch (SQLiteDatabaseCorruptException ex) { 74 | onCorruption(); 75 | throw ex; 76 | } finally { 77 | releaseReference(); 78 | } 79 | } 80 | 81 | /** 82 | * Execute this SQL statement and return the ID of the row inserted due to this call. 83 | * The SQL statement should be an INSERT for this to be a useful call. 84 | * 85 | * @return the row ID of the last row inserted, if this insert is successful. -1 otherwise. 86 | * 87 | * @throws SQLException If the SQL string is invalid for some reason 88 | */ 89 | @Override 90 | public long executeInsert() { 91 | acquireReference(); 92 | try { 93 | return getSession().executeForLastInsertedRowId( 94 | getSql(), getBindArgs(), getConnectionFlags(), null); 95 | } catch (SQLiteDatabaseCorruptException ex) { 96 | onCorruption(); 97 | throw ex; 98 | } finally { 99 | releaseReference(); 100 | } 101 | } 102 | 103 | /** 104 | * Execute a statement that returns a 1 by 1 table with a numeric value. 105 | * For example, SELECT COUNT(*) FROM table; 106 | * 107 | * @return The result of the query. 108 | * 109 | * @throws SQLiteDoneException if the query returns zero rows 110 | */ 111 | @Override 112 | public long simpleQueryForLong() { 113 | acquireReference(); 114 | try { 115 | return getSession().executeForLong( 116 | getSql(), getBindArgs(), getConnectionFlags(), null); 117 | } catch (SQLiteDatabaseCorruptException ex) { 118 | onCorruption(); 119 | throw ex; 120 | } finally { 121 | releaseReference(); 122 | } 123 | } 124 | 125 | /** 126 | * Execute a statement that returns a 1 by 1 table with a text value. 127 | * For example, SELECT COUNT(*) FROM table; 128 | * 129 | * @return The result of the query. 130 | * 131 | * @throws SQLiteDoneException if the query returns zero rows 132 | */ 133 | @Override 134 | public String simpleQueryForString() { 135 | acquireReference(); 136 | try { 137 | return getSession().executeForString( 138 | getSql(), getBindArgs(), getConnectionFlags(), null); 139 | } catch (SQLiteDatabaseCorruptException ex) { 140 | onCorruption(); 141 | throw ex; 142 | } finally { 143 | releaseReference(); 144 | } 145 | } 146 | 147 | /** 148 | * Executes a statement that returns a 1 by 1 table with a blob value. 149 | * 150 | * @return A read-only file descriptor for a copy of the blob value, or {@code null} 151 | * if the value is null or could not be read for some reason. 152 | * 153 | * @throws SQLiteDoneException if the query returns zero rows 154 | */ 155 | public ParcelFileDescriptor simpleQueryForBlobFileDescriptor() { 156 | acquireReference(); 157 | try { 158 | return getSession().executeForBlobFileDescriptor( 159 | getSql(), getBindArgs(), getConnectionFlags(), null); 160 | } catch (SQLiteDatabaseCorruptException ex) { 161 | onCorruption(); 162 | throw ex; 163 | } finally { 164 | releaseReference(); 165 | } 166 | } 167 | 168 | @Override 169 | public String toString() { 170 | return "SQLiteProgram: " + getSql(); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteStatementInfo.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 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 | // modified from original source see README at the top level of this project 17 | 18 | package io.requery.android.database.sqlite; 19 | 20 | /** 21 | * Describes a SQLite statement. 22 | * 23 | * @hide 24 | */ 25 | public final class SQLiteStatementInfo { 26 | /** 27 | * The number of parameters that the statement has. 28 | */ 29 | public int numParameters; 30 | 31 | /** 32 | * The names of all columns in the result set of the statement. 33 | */ 34 | public String[] columnNames; 35 | 36 | /** 37 | * True if the statement is read-only. 38 | */ 39 | public boolean readOnly; 40 | } 41 | -------------------------------------------------------------------------------- /sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteStatementType.java: -------------------------------------------------------------------------------- 1 | /* 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 | // modified from original source see README at the top level of this project 17 | 18 | package io.requery.android.database.sqlite; 19 | 20 | import androidx.annotation.VisibleForTesting; 21 | 22 | class SQLiteStatementType { 23 | 24 | /** One of the values returned by {@link #getSqlStatementType(String)}. */ 25 | public static final int STATEMENT_SELECT = 1; 26 | /** One of the values returned by {@link #getSqlStatementType(String)}. */ 27 | public static final int STATEMENT_UPDATE = 2; 28 | /** One of the values returned by {@link #getSqlStatementType(String)}. */ 29 | public static final int STATEMENT_ATTACH = 3; 30 | /** One of the values returned by {@link #getSqlStatementType(String)}. */ 31 | public static final int STATEMENT_BEGIN = 4; 32 | /** One of the values returned by {@link #getSqlStatementType(String)}. */ 33 | public static final int STATEMENT_COMMIT = 5; 34 | /** One of the values returned by {@link #getSqlStatementType(String)}. */ 35 | public static final int STATEMENT_ABORT = 6; 36 | /** One of the values returned by {@link #getSqlStatementType(String)}. */ 37 | public static final int STATEMENT_PRAGMA = 7; 38 | /** One of the values returned by {@link #getSqlStatementType(String)}. */ 39 | public static final int STATEMENT_DDL = 8; 40 | /** One of the values returned by {@link #getSqlStatementType(String)}. */ 41 | public static final int STATEMENT_UNPREPARED = 9; 42 | /** One of the values returned by {@link #getSqlStatementType(String)}. */ 43 | public static final int STATEMENT_OTHER = 99; 44 | 45 | private SQLiteStatementType() { 46 | } 47 | 48 | /** 49 | * Returns one of the following which represent the type of the given SQL statement. 50 | *
    51 | *
  1. {@link #STATEMENT_SELECT}
  2. 52 | *
  3. {@link #STATEMENT_UPDATE}
  4. 53 | *
  5. {@link #STATEMENT_ATTACH}
  6. 54 | *
  7. {@link #STATEMENT_BEGIN}
  8. 55 | *
  9. {@link #STATEMENT_COMMIT}
  10. 56 | *
  11. {@link #STATEMENT_ABORT}
  12. 57 | *
  13. {@link #STATEMENT_OTHER}
  14. 58 | *
59 | * @param sql the SQL statement whose type is returned by this method 60 | * @return one of the values listed above 61 | */ 62 | public static int getSqlStatementType(String sql) { 63 | if (sql.length() < 3) { 64 | return STATEMENT_OTHER; 65 | } 66 | // Skip leading comments to properly recognize the statement type 67 | int statementStart = statementStartIndex(sql); 68 | String prefixSql = sql.substring(statementStart, Math.min(statementStart + 3, sql.length())); 69 | 70 | if (prefixSql.equalsIgnoreCase("SEL") 71 | || prefixSql.equalsIgnoreCase("WIT")) { 72 | return STATEMENT_SELECT; 73 | } 74 | if (prefixSql.equalsIgnoreCase("INS") 75 | || prefixSql.equalsIgnoreCase("UPD") 76 | || prefixSql.equalsIgnoreCase("REP") 77 | || prefixSql.equalsIgnoreCase("DEL")) { 78 | return STATEMENT_UPDATE; 79 | } 80 | if (prefixSql.equalsIgnoreCase("ATT")) { 81 | return STATEMENT_ATTACH; 82 | } 83 | if (prefixSql.equalsIgnoreCase("COM") 84 | || prefixSql.equalsIgnoreCase("END")) { 85 | return STATEMENT_COMMIT; 86 | } 87 | if (prefixSql.equalsIgnoreCase("ROL")) { 88 | return STATEMENT_ABORT; 89 | } 90 | if (prefixSql.equalsIgnoreCase("BEG")) { 91 | return STATEMENT_BEGIN; 92 | } 93 | if (prefixSql.equalsIgnoreCase("PRA")) { 94 | return STATEMENT_PRAGMA; 95 | } 96 | if (prefixSql.equalsIgnoreCase("CRE") 97 | || prefixSql.equalsIgnoreCase("DRO") 98 | || prefixSql.equalsIgnoreCase("ALT")) { 99 | return STATEMENT_DDL; 100 | } 101 | 102 | if (prefixSql.equalsIgnoreCase("ANA") || prefixSql.equalsIgnoreCase("DET")) { 103 | return STATEMENT_UNPREPARED; 104 | } 105 | 106 | return STATEMENT_OTHER; 107 | } 108 | 109 | /** 110 | * @param sql sql statement to check 111 | * @return index of the SQL statement start, skipping leading comments 112 | */ 113 | @VisibleForTesting 114 | static int statementStartIndex(String sql) { 115 | boolean inSingleLineComment = false; 116 | boolean inMultiLineComment = false; 117 | int statementStartIndex = 0; 118 | 119 | for (int i = 0; i < sql.length(); i++) { 120 | char c = sql.charAt(i); 121 | 122 | if (inSingleLineComment) { 123 | if (c == '\n') { 124 | inSingleLineComment = false; 125 | } 126 | } else if (inMultiLineComment) { 127 | if (c == '*' && i + 1 < sql.length() && sql.charAt(i + 1) == '/') { 128 | inMultiLineComment = false; 129 | } 130 | } else if (c == '-') { 131 | if (i + 1 < sql.length() && sql.charAt(i + 1) == '-') { 132 | inSingleLineComment = true; 133 | } 134 | } else if (c == '/') { 135 | if (i + 1 < sql.length() && sql.charAt(i + 1) == '*') { 136 | inMultiLineComment = true; 137 | } 138 | } else if (c != '\n' && c != '\r' && c != ' ' && c != '\t') { 139 | statementStartIndex = i; 140 | break; 141 | } 142 | } 143 | 144 | return statementStartIndex; 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteUpdateHook.java: -------------------------------------------------------------------------------- 1 | package io.requery.android.database.sqlite; 2 | 3 | public interface SQLiteUpdateHook { 4 | void onUpdateFromNative(int operationType, String databaseName, String tableName, long rowId); 5 | } 6 | -------------------------------------------------------------------------------- /sqlite-android/src/main/jni/Android.mk: -------------------------------------------------------------------------------- 1 | 2 | LOCAL_PATH:= $(call my-dir) 3 | include $(LOCAL_PATH)/sqlite/Android.mk 4 | 5 | -------------------------------------------------------------------------------- /sqlite-android/src/main/jni/Application.mk: -------------------------------------------------------------------------------- 1 | APP_STL:=none 2 | APP_OPTIM := release 3 | APP_ABI := armeabi-v7a,arm64-v8a,x86,x86_64 4 | NDK_TOOLCHAIN_VERSION := clang 5 | NDK_APP_LIBS_OUT=../jniLibs 6 | -------------------------------------------------------------------------------- /sqlite-android/src/main/jni/sqlite/ALog-priv.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 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 | #ifndef NATIVEHELPER_ALOGPRIV_H_ 18 | #define NATIVEHELPER_ALOGPRIV_H_ 19 | 20 | #include 21 | 22 | #ifndef LOG_NDEBUG 23 | #ifdef NDEBUG 24 | #define LOG_NDEBUG 1 25 | #else 26 | #define LOG_NDEBUG 0 27 | #endif 28 | #endif 29 | 30 | 31 | /* 32 | * Basic log message macros intended to emulate the behavior of log/log.h 33 | * in system core. This should be dependent only on ndk exposed logging 34 | * functionality. 35 | */ 36 | 37 | #ifndef ALOG 38 | #define ALOG(priority, tag, fmt...) \ 39 | __android_log_print(ANDROID_##priority, tag, fmt) 40 | #endif 41 | 42 | #ifndef ALOGV 43 | #if LOG_NDEBUG 44 | #define ALOGV(...) ((void)0) 45 | #else 46 | #define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) 47 | #endif 48 | #endif 49 | 50 | #ifndef ALOGD 51 | #define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) 52 | #endif 53 | 54 | #ifndef ALOGI 55 | #define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) 56 | #endif 57 | 58 | #ifndef ALOGW 59 | #define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) 60 | #endif 61 | 62 | #ifndef ALOGE 63 | #define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) 64 | #endif 65 | 66 | /* 67 | ** Not quite the same as the core android LOG_FATAL_IF (which also 68 | ** sends a SIGTRAP), but close enough. 69 | */ 70 | #define LOG_FATAL_IF(bCond, zErr) if( bCond ) ALOGE(zErr); 71 | 72 | #endif 73 | -------------------------------------------------------------------------------- /sqlite-android/src/main/jni/sqlite/Android.mk: -------------------------------------------------------------------------------- 1 | LOCAL_PATH:= $(call my-dir) 2 | include $(CLEAR_VARS) 3 | 4 | # NOTE the following flags, 5 | # SQLITE_TEMP_STORE=3 causes all TEMP files to go into RAM. and thats the behavior we want 6 | # SQLITE_ENABLE_FTS3 enables usage of FTS3 - NOT FTS1 or 2. 7 | # SQLITE_DEFAULT_AUTOVACUUM=1 causes the databases to be subject to auto-vacuum 8 | sqlite_flags := \ 9 | -DNDEBUG=1 \ 10 | -DHAVE_USLEEP=1 \ 11 | -DSQLITE_HAVE_ISNAN \ 12 | -DSQLITE_DEFAULT_JOURNAL_SIZE_LIMIT=1048576 \ 13 | -DSQLITE_THREADSAFE=2 \ 14 | -DSQLITE_TEMP_STORE=3 \ 15 | -DSQLITE_POWERSAFE_OVERWRITE=1 \ 16 | -DSQLITE_DEFAULT_FILE_FORMAT=4 \ 17 | -DSQLITE_DEFAULT_AUTOVACUUM=1 \ 18 | -DSQLITE_ENABLE_MEMORY_MANAGEMENT=1 \ 19 | -DSQLITE_ENABLE_FTS3 \ 20 | -DSQLITE_ENABLE_FTS3_PARENTHESIS \ 21 | -DSQLITE_ENABLE_FTS4 \ 22 | -DSQLITE_ENABLE_FTS4_PARENTHESIS \ 23 | -DSQLITE_ENABLE_FTS5 \ 24 | -DSQLITE_ENABLE_FTS5_PARENTHESIS \ 25 | -DSQLITE_ENABLE_JSON1 \ 26 | -DSQLITE_ENABLE_RTREE=1 \ 27 | -DSQLITE_UNTESTABLE \ 28 | -DSQLITE_OMIT_COMPILEOPTION_DIAGS \ 29 | -DSQLITE_DEFAULT_FILE_PERMISSIONS=0600 \ 30 | -DSQLITE_DEFAULT_MEMSTATUS=0 \ 31 | -DSQLITE_MAX_EXPR_DEPTH=0 \ 32 | -DSQLITE_USE_ALLOCA \ 33 | -DSQLITE_ENABLE_BATCH_ATOMIC_WRITE \ 34 | -O3 35 | 36 | LOCAL_CFLAGS += $(sqlite_flags) 37 | LOCAL_CFLAGS += -Wno-unused-parameter -Wno-int-to-pointer-cast 38 | LOCAL_CFLAGS += -Wno-uninitialized -Wno-parentheses 39 | LOCAL_CPPFLAGS += -Wno-conversion-null 40 | 41 | 42 | ifeq ($(TARGET_ARCH), arm) 43 | LOCAL_CFLAGS += -DPACKED="__attribute__ ((packed))" 44 | else 45 | LOCAL_CFLAGS += -DPACKED="" 46 | endif 47 | 48 | LOCAL_SRC_FILES:= \ 49 | android_database_SQLiteCommon.cpp \ 50 | android_database_SQLiteConnection.cpp \ 51 | android_database_SQLiteFunction.cpp \ 52 | android_database_SQLiteGlobal.cpp \ 53 | android_database_SQLiteDebug.cpp \ 54 | android_database_CursorWindow.cpp \ 55 | CursorWindow.cpp \ 56 | JNIHelp.cpp \ 57 | JNIString.cpp 58 | 59 | LOCAL_SRC_FILES += sqlite3.c 60 | 61 | LOCAL_C_INCLUDES += $(LOCAL_PATH) 62 | 63 | LOCAL_MODULE:= libsqlite3x 64 | LOCAL_LDLIBS += -ldl -llog -latomic 65 | 66 | include $(BUILD_SHARED_LIBRARY) 67 | 68 | -------------------------------------------------------------------------------- /sqlite-android/src/main/jni/sqlite/CursorWindow.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2006-2007 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 | // modified from original source see README at the top level of this project 17 | 18 | #undef LOG_TAG 19 | #define LOG_TAG "CursorWindow" 20 | 21 | #include "CursorWindow.h" 22 | #include "ALog-priv.h" 23 | 24 | #include 25 | #include 26 | #include 27 | 28 | namespace android { 29 | 30 | CursorWindow::CursorWindow(const char* name, void* data, size_t size, bool readOnly) : 31 | mData(data), mSize(size), mReadOnly(readOnly) { 32 | mName = strdup(name); 33 | mHeader = static_cast(mData); 34 | } 35 | 36 | CursorWindow::~CursorWindow() { 37 | free(mName); 38 | free(mData); 39 | } 40 | 41 | status_t CursorWindow::create(const char* name, size_t size, CursorWindow** outWindow) { 42 | status_t result; 43 | void* data = malloc(size); 44 | if (!data) { 45 | return NO_MEMORY; 46 | } 47 | CursorWindow* window = new CursorWindow(name, data, size, false); 48 | result = window->clear(); 49 | if (!result) { 50 | LOG_WINDOW("Created new CursorWindow: freeOffset=%d, " 51 | "numRows=%d, numColumns=%d, mSize=%d, mData=%p", 52 | window->mHeader->freeOffset, 53 | window->mHeader->numRows, 54 | window->mHeader->numColumns, 55 | window->mSize, window->mData); 56 | *outWindow = window; 57 | return OK; 58 | } 59 | delete window; 60 | return result; 61 | } 62 | 63 | status_t CursorWindow::clear() { 64 | if (mReadOnly) { 65 | return INVALID_OPERATION; 66 | } 67 | 68 | mHeader->freeOffset = sizeof(Header) + sizeof(RowSlotChunk); 69 | mHeader->firstChunkOffset = sizeof(Header); 70 | mHeader->numRows = 0; 71 | mHeader->numColumns = 0; 72 | 73 | RowSlotChunk* firstChunk = static_cast(offsetToPtr(mHeader->firstChunkOffset)); 74 | firstChunk->nextChunkOffset = 0; 75 | return OK; 76 | } 77 | 78 | status_t CursorWindow::setNumColumns(uint32_t numColumns) { 79 | if (mReadOnly) { 80 | return INVALID_OPERATION; 81 | } 82 | 83 | uint32_t cur = mHeader->numColumns; 84 | if ((cur > 0 || mHeader->numRows > 0) && cur != numColumns) { 85 | ALOGE("Trying to go from %d columns to %d", cur, numColumns); 86 | return INVALID_OPERATION; 87 | } 88 | mHeader->numColumns = numColumns; 89 | return OK; 90 | } 91 | 92 | status_t CursorWindow::allocRow() { 93 | if (mReadOnly) { 94 | return INVALID_OPERATION; 95 | } 96 | 97 | // Fill in the row slot 98 | RowSlot* rowSlot = allocRowSlot(); 99 | if (rowSlot == NULL) { 100 | return NO_MEMORY; 101 | } 102 | 103 | // Allocate the slots for the field directory 104 | size_t fieldDirSize = mHeader->numColumns * sizeof(FieldSlot); 105 | uint32_t fieldDirOffset = alloc(fieldDirSize, true /*aligned*/); 106 | if (!fieldDirOffset) { 107 | mHeader->numRows--; 108 | LOG_WINDOW("The row failed, so back out the new row accounting " 109 | "from allocRowSlot %d", mHeader->numRows); 110 | return NO_MEMORY; 111 | } 112 | FieldSlot* fieldDir = static_cast(offsetToPtr(fieldDirOffset)); 113 | memset(fieldDir, 0, fieldDirSize); 114 | 115 | //LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %d bytes at offset %u\n", 116 | // mHeader->numRows - 1, offsetFromPtr(rowSlot), fieldDirSize, fieldDirOffset); 117 | rowSlot->offset = fieldDirOffset; 118 | return OK; 119 | } 120 | 121 | status_t CursorWindow::freeLastRow() { 122 | if (mReadOnly) { 123 | return INVALID_OPERATION; 124 | } 125 | 126 | if (mHeader->numRows > 0) { 127 | mHeader->numRows--; 128 | } 129 | return OK; 130 | } 131 | 132 | uint32_t CursorWindow::alloc(size_t size, bool aligned) { 133 | uint32_t padding; 134 | if (aligned) { 135 | // 4 byte alignment 136 | padding = (~mHeader->freeOffset + 1) & 3; 137 | } else { 138 | padding = 0; 139 | } 140 | 141 | uint32_t offset = mHeader->freeOffset + padding; 142 | uint32_t nextFreeOffset = offset + size; 143 | if (nextFreeOffset > mSize) { 144 | ALOGW("Window is full: requested allocation %zu bytes, " 145 | "free space %zu bytes, window size %zu bytes", 146 | size, freeSpace(), mSize); 147 | return 0; 148 | } 149 | 150 | mHeader->freeOffset = nextFreeOffset; 151 | return offset; 152 | } 153 | 154 | CursorWindow::RowSlot* CursorWindow::getRowSlot(uint32_t row) { 155 | uint32_t chunkPos = row; 156 | RowSlotChunk* chunk = static_cast( 157 | offsetToPtr(mHeader->firstChunkOffset)); 158 | while (chunkPos >= ROW_SLOT_CHUNK_NUM_ROWS) { 159 | chunk = static_cast(offsetToPtr(chunk->nextChunkOffset)); 160 | chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS; 161 | } 162 | return &chunk->slots[chunkPos]; 163 | } 164 | 165 | CursorWindow::RowSlot* CursorWindow::allocRowSlot() { 166 | uint32_t chunkPos = mHeader->numRows; 167 | RowSlotChunk* chunk = static_cast( 168 | offsetToPtr(mHeader->firstChunkOffset)); 169 | while (chunkPos > ROW_SLOT_CHUNK_NUM_ROWS) { 170 | chunk = static_cast(offsetToPtr(chunk->nextChunkOffset)); 171 | chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS; 172 | } 173 | if (chunkPos == ROW_SLOT_CHUNK_NUM_ROWS) { 174 | if (!chunk->nextChunkOffset) { 175 | chunk->nextChunkOffset = alloc(sizeof(RowSlotChunk), true /*aligned*/); 176 | if (!chunk->nextChunkOffset) { 177 | return NULL; 178 | } 179 | } 180 | chunk = static_cast(offsetToPtr(chunk->nextChunkOffset)); 181 | chunk->nextChunkOffset = 0; 182 | chunkPos = 0; 183 | } 184 | mHeader->numRows += 1; 185 | return &chunk->slots[chunkPos]; 186 | } 187 | 188 | CursorWindow::FieldSlot* CursorWindow::getFieldSlot(uint32_t row, uint32_t column) { 189 | if (row >= mHeader->numRows || column >= mHeader->numColumns) { 190 | ALOGE("Failed to read row %d, column %d from a CursorWindow which " 191 | "has %d rows, %d columns.", 192 | row, column, mHeader->numRows, mHeader->numColumns); 193 | return NULL; 194 | } 195 | RowSlot* rowSlot = getRowSlot(row); 196 | if (!rowSlot) { 197 | ALOGE("Failed to find rowSlot for row %d.", row); 198 | return NULL; 199 | } 200 | FieldSlot* fieldDir = static_cast(offsetToPtr(rowSlot->offset)); 201 | return &fieldDir[column]; 202 | } 203 | 204 | status_t CursorWindow::putBlob(uint32_t row, uint32_t column, const void* value, size_t size) { 205 | return putBlobOrString(row, column, value, size, FIELD_TYPE_BLOB); 206 | } 207 | 208 | status_t CursorWindow::putString(uint32_t row, uint32_t column, const char* value, 209 | size_t sizeIncludingNull) { 210 | return putBlobOrString(row, column, value, sizeIncludingNull, FIELD_TYPE_STRING); 211 | } 212 | 213 | status_t CursorWindow::putBlobOrString(uint32_t row, uint32_t column, 214 | const void* value, size_t size, int32_t type) { 215 | if (mReadOnly) { 216 | return INVALID_OPERATION; 217 | } 218 | 219 | FieldSlot* fieldSlot = getFieldSlot(row, column); 220 | if (!fieldSlot) { 221 | return BAD_VALUE; 222 | } 223 | 224 | uint32_t offset = alloc(size); 225 | if (!offset) { 226 | return NO_MEMORY; 227 | } 228 | 229 | memcpy(offsetToPtr(offset), value, size); 230 | 231 | fieldSlot->type = type; 232 | fieldSlot->data.buffer.offset = offset; 233 | fieldSlot->data.buffer.size = size; 234 | return OK; 235 | } 236 | 237 | status_t CursorWindow::putLong(uint32_t row, uint32_t column, int64_t value) { 238 | if (mReadOnly) { 239 | return INVALID_OPERATION; 240 | } 241 | 242 | FieldSlot* fieldSlot = getFieldSlot(row, column); 243 | if (!fieldSlot) { 244 | return BAD_VALUE; 245 | } 246 | 247 | fieldSlot->type = FIELD_TYPE_INTEGER; 248 | fieldSlot->data.l = value; 249 | return OK; 250 | } 251 | 252 | status_t CursorWindow::putDouble(uint32_t row, uint32_t column, double value) { 253 | if (mReadOnly) { 254 | return INVALID_OPERATION; 255 | } 256 | 257 | FieldSlot* fieldSlot = getFieldSlot(row, column); 258 | if (!fieldSlot) { 259 | return BAD_VALUE; 260 | } 261 | 262 | fieldSlot->type = FIELD_TYPE_FLOAT; 263 | fieldSlot->data.d = value; 264 | return OK; 265 | } 266 | 267 | status_t CursorWindow::putNull(uint32_t row, uint32_t column) { 268 | if (mReadOnly) { 269 | return INVALID_OPERATION; 270 | } 271 | 272 | FieldSlot* fieldSlot = getFieldSlot(row, column); 273 | if (!fieldSlot) { 274 | return BAD_VALUE; 275 | } 276 | 277 | fieldSlot->type = FIELD_TYPE_NULL; 278 | fieldSlot->data.buffer.offset = 0; 279 | fieldSlot->data.buffer.size = 0; 280 | return OK; 281 | } 282 | 283 | }; // namespace android 284 | -------------------------------------------------------------------------------- /sqlite-android/src/main/jni/sqlite/CursorWindow.h: -------------------------------------------------------------------------------- 1 | /* 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 | // modified from original source see README at the top level of this project 17 | 18 | #ifndef _ANDROID__DATABASE_WINDOW_H 19 | #define _ANDROID__DATABASE_WINDOW_H 20 | 21 | #include "ALog-priv.h" 22 | #include 23 | #include 24 | 25 | #include "Errors.h" 26 | 27 | #if LOG_NDEBUG 28 | 29 | #define IF_LOG_WINDOW() if (false) 30 | #define LOG_WINDOW(...) 31 | 32 | #else 33 | 34 | #define IF_LOG_WINDOW() IF_ALOG(LOG_DEBUG, "CursorWindow") 35 | #define LOG_WINDOW(...) ALOG(LOG_DEBUG, "CursorWindow", __VA_ARGS__) 36 | 37 | #endif 38 | 39 | namespace android { 40 | 41 | /** 42 | * This class stores a set of rows from a database in a buffer. The beginning of the 43 | * window has first chunk of RowSlots, which are offsets to the row directory, followed by 44 | * an offset to the next chunk in a linked-list of additional chunk of RowSlots in case 45 | * the pre-allocated chunk isn't big enough to refer to all rows. Each row directory has a 46 | * FieldSlot per column, which has the size, offset, and type of the data for that field. 47 | * Note that the data types come from sqlite3.h. 48 | * 49 | * Strings are stored in UTF-8. 50 | */ 51 | class CursorWindow { 52 | CursorWindow(const char* name, void* data, size_t size, bool readOnly); 53 | 54 | public: 55 | /* Field types. */ 56 | enum { 57 | FIELD_TYPE_NULL = 0, 58 | FIELD_TYPE_INTEGER = 1, 59 | FIELD_TYPE_FLOAT = 2, 60 | FIELD_TYPE_STRING = 3, 61 | FIELD_TYPE_BLOB = 4, 62 | }; 63 | 64 | /* Opaque type that describes a field slot. */ 65 | struct FieldSlot { 66 | private: 67 | int32_t type; 68 | union { 69 | double d; 70 | int64_t l; 71 | struct { 72 | uint32_t offset; 73 | uint32_t size; 74 | } buffer; 75 | } data; 76 | 77 | friend class CursorWindow; 78 | } __attribute((packed)); 79 | 80 | ~CursorWindow(); 81 | 82 | static status_t create(const char* name, size_t size, CursorWindow** outCursorWindow); 83 | 84 | inline const char* name() { return mName; } 85 | inline size_t size() { return mSize; } 86 | inline size_t freeSpace() { return mSize - mHeader->freeOffset; } 87 | inline uint32_t getNumRows() { return mHeader->numRows; } 88 | inline uint32_t getNumColumns() { return mHeader->numColumns; } 89 | 90 | status_t clear(); 91 | status_t setNumColumns(uint32_t numColumns); 92 | 93 | /** 94 | * Allocate a row slot and its directory. 95 | * The row is initialized will null entries for each field. 96 | */ 97 | status_t allocRow(); 98 | status_t freeLastRow(); 99 | 100 | status_t putBlob(uint32_t row, uint32_t column, const void* value, size_t size); 101 | status_t putString(uint32_t row, uint32_t column, const char* value, size_t sizeIncludingNull); 102 | status_t putLong(uint32_t row, uint32_t column, int64_t value); 103 | status_t putDouble(uint32_t row, uint32_t column, double value); 104 | status_t putNull(uint32_t row, uint32_t column); 105 | 106 | /** 107 | * Gets the field slot at the specified row and column. 108 | * Returns null if the requested row or column is not in the window. 109 | */ 110 | FieldSlot* getFieldSlot(uint32_t row, uint32_t column); 111 | 112 | inline int32_t getFieldSlotType(FieldSlot* fieldSlot) { 113 | return fieldSlot->type; 114 | } 115 | 116 | inline int64_t getFieldSlotValueLong(FieldSlot* fieldSlot) { 117 | return fieldSlot->data.l; 118 | } 119 | 120 | inline double getFieldSlotValueDouble(FieldSlot* fieldSlot) { 121 | return fieldSlot->data.d; 122 | } 123 | 124 | inline const char* getFieldSlotValueString(FieldSlot* fieldSlot, 125 | size_t* outSizeIncludingNull) { 126 | *outSizeIncludingNull = fieldSlot->data.buffer.size; 127 | return static_cast(offsetToPtr(fieldSlot->data.buffer.offset)); 128 | } 129 | 130 | inline const void* getFieldSlotValueBlob(FieldSlot* fieldSlot, size_t* outSize) { 131 | *outSize = fieldSlot->data.buffer.size; 132 | return offsetToPtr(fieldSlot->data.buffer.offset); 133 | } 134 | 135 | private: 136 | static const size_t ROW_SLOT_CHUNK_NUM_ROWS = 100; 137 | 138 | struct Header { 139 | // Offset of the lowest unused byte in the window. 140 | uint32_t freeOffset; 141 | 142 | // Offset of the first row slot chunk. 143 | uint32_t firstChunkOffset; 144 | 145 | uint32_t numRows; 146 | uint32_t numColumns; 147 | }; 148 | 149 | struct RowSlot { 150 | uint32_t offset; 151 | }; 152 | 153 | struct RowSlotChunk { 154 | RowSlot slots[ROW_SLOT_CHUNK_NUM_ROWS]; 155 | uint32_t nextChunkOffset; 156 | }; 157 | 158 | char* mName; 159 | void* mData; 160 | size_t mSize; 161 | bool mReadOnly; 162 | Header* mHeader; 163 | 164 | inline void* offsetToPtr(uint32_t offset) { 165 | return static_cast(mData) + offset; 166 | } 167 | 168 | inline uint32_t offsetFromPtr(void* ptr) { 169 | return static_cast(ptr) - static_cast(mData); 170 | } 171 | 172 | /** 173 | * Allocate a portion of the window. Returns the offset 174 | * of the allocation, or 0 if there isn't enough space. 175 | * If aligned is true, the allocation gets 4 byte alignment. 176 | */ 177 | uint32_t alloc(size_t size, bool aligned = false); 178 | 179 | RowSlot* getRowSlot(uint32_t row); 180 | RowSlot* allocRowSlot(); 181 | 182 | status_t putBlobOrString(uint32_t row, uint32_t column, 183 | const void* value, size_t size, int32_t type); 184 | }; 185 | 186 | }; // namespace android 187 | 188 | #endif 189 | -------------------------------------------------------------------------------- /sqlite-android/src/main/jni/sqlite/Errors.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 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 | #ifndef ANDROID_ERRORS_H 18 | #define ANDROID_ERRORS_H 19 | 20 | #include 21 | #include 22 | 23 | namespace android { 24 | 25 | // use this type to return error codes 26 | #ifdef HAVE_MS_C_RUNTIME 27 | typedef int status_t; 28 | #else 29 | typedef int32_t status_t; 30 | #endif 31 | 32 | /* the MS C runtime lacks a few error codes */ 33 | 34 | /* 35 | * Error codes. 36 | * All error codes are negative values. 37 | */ 38 | 39 | // Win32 #defines NO_ERROR as well. It has the same value, so there's no 40 | // real conflict, though it's a bit awkward. 41 | #ifdef _WIN32 42 | # undef NO_ERROR 43 | #endif 44 | 45 | enum { 46 | OK = 0, // Everything's swell. 47 | NO_ERROR = 0, // No errors. 48 | 49 | UNKNOWN_ERROR = 0x80000000, 50 | 51 | NO_MEMORY = -ENOMEM, 52 | INVALID_OPERATION = -ENOSYS, 53 | BAD_VALUE = -EINVAL, 54 | BAD_TYPE = 0x80000001, 55 | NAME_NOT_FOUND = -ENOENT, 56 | PERMISSION_DENIED = -EPERM, 57 | NO_INIT = -ENODEV, 58 | ALREADY_EXISTS = -EEXIST, 59 | DEAD_OBJECT = -EPIPE, 60 | FAILED_TRANSACTION = 0x80000002, 61 | JPARKS_BROKE_IT = -EPIPE, 62 | #if !defined(HAVE_MS_C_RUNTIME) 63 | BAD_INDEX = -EOVERFLOW, 64 | NOT_ENOUGH_DATA = -ENODATA, 65 | WOULD_BLOCK = -EWOULDBLOCK, 66 | TIMED_OUT = -ETIMEDOUT, 67 | UNKNOWN_TRANSACTION = -EBADMSG, 68 | #else 69 | BAD_INDEX = -E2BIG, 70 | NOT_ENOUGH_DATA = 0x80000003, 71 | WOULD_BLOCK = 0x80000004, 72 | TIMED_OUT = 0x80000005, 73 | UNKNOWN_TRANSACTION = 0x80000006, 74 | #endif 75 | FDS_NOT_ALLOWED = 0x80000007, 76 | }; 77 | 78 | // Restore define; enumeration is in "android" namespace, so the value defined 79 | // there won't work for Win32 code in a different namespace. 80 | #ifdef _WIN32 81 | # define NO_ERROR 0L 82 | #endif 83 | 84 | }; // namespace android 85 | 86 | // --------------------------------------------------------------------------- 87 | 88 | #endif // ANDROID_ERRORS_H 89 | -------------------------------------------------------------------------------- /sqlite-android/src/main/jni/sqlite/JNIHelp.cpp: -------------------------------------------------------------------------------- 1 | /* 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 | 17 | #define LOG_TAG "JNIHelp" 18 | 19 | #include "JNIHelp.h" 20 | #include "ALog-priv.h" 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | /** 28 | * Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.) 29 | */ 30 | template 31 | class scoped_local_ref { 32 | public: 33 | scoped_local_ref(C_JNIEnv* env, T localRef = NULL) 34 | : mEnv(env), mLocalRef(localRef) 35 | { 36 | } 37 | 38 | ~scoped_local_ref() { 39 | reset(); 40 | } 41 | 42 | void reset(T localRef = NULL) { 43 | if (mLocalRef != NULL) { 44 | (*mEnv)->DeleteLocalRef(reinterpret_cast(mEnv), mLocalRef); 45 | mLocalRef = localRef; 46 | } 47 | } 48 | 49 | T get() const { 50 | return mLocalRef; 51 | } 52 | 53 | private: 54 | C_JNIEnv* mEnv; 55 | T mLocalRef; 56 | 57 | // Disallow copy and assignment. 58 | scoped_local_ref(const scoped_local_ref&); 59 | void operator=(const scoped_local_ref&); 60 | }; 61 | 62 | static jclass findClass(C_JNIEnv* env, const char* className) { 63 | JNIEnv* e = reinterpret_cast(env); 64 | return (*env)->FindClass(e, className); 65 | } 66 | 67 | extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className, 68 | const JNINativeMethod* gMethods, int numMethods) 69 | { 70 | JNIEnv* e = reinterpret_cast(env); 71 | 72 | ALOGV("Registering %s's %d native methods...", className, numMethods); 73 | 74 | scoped_local_ref c(env, findClass(env, className)); 75 | if (c.get() == NULL) { 76 | char* msg; 77 | asprintf(&msg, "Native registration unable to find class '%s'; aborting...", className); 78 | e->FatalError(msg); 79 | } 80 | 81 | if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) { 82 | char* msg; 83 | asprintf(&msg, "RegisterNatives failed for '%s'; aborting...", className); 84 | e->FatalError(msg); 85 | } 86 | 87 | return 0; 88 | } 89 | 90 | /* 91 | * Returns a human-readable summary of an exception object. The buffer will 92 | * be populated with the "binary" class name and, if present, the 93 | * exception message. 94 | */ 95 | static bool logExceptionSummary(C_JNIEnv *env, jthrowable exception, 96 | const char* exceptionClassName) { 97 | JNIEnv* e = reinterpret_cast(env); 98 | 99 | /* get the name of the exception's class */ 100 | scoped_local_ref exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail 101 | scoped_local_ref classClass(env, 102 | (*env)->GetObjectClass(e, exceptionClass.get())); // java.lang.Class, can't fail 103 | jmethodID classGetNameMethod = 104 | (*env)->GetMethodID(e, classClass.get(), "getName", "()Ljava/lang/String;"); 105 | scoped_local_ref classNameStr(env, 106 | (jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod)); 107 | if (classNameStr.get() == NULL) { 108 | (*env)->ExceptionClear(e); 109 | ALOGW("Discarding pending exception (%s) to throw %s", "", 110 | exceptionClassName); 111 | return false; 112 | } 113 | const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL); 114 | if (classNameChars == NULL) { 115 | (*env)->ExceptionClear(e); 116 | ALOGW("Discarding pending exception (%s) to throw %s", "", 117 | exceptionClassName); 118 | return false; 119 | } 120 | (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars); 121 | 122 | /* if the exception has a detail message, get that */ 123 | jmethodID getMessage = 124 | (*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;"); 125 | scoped_local_ref messageStr(env, 126 | (jstring) (*env)->CallObjectMethod(e, exception, getMessage)); 127 | if (messageStr.get() == NULL) { 128 | return true; 129 | } 130 | 131 | const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL); 132 | if (messageChars != NULL) { 133 | ALOGW("Discarding pending exception (%s: %s) to throw %s", 134 | classNameChars, 135 | messageChars, 136 | exceptionClassName); 137 | (*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars); 138 | } else { 139 | ALOGW("Discarding pending exception (%s: ) to throw %s", 140 | classNameChars, 141 | exceptionClassName); 142 | (*env)->ExceptionClear(e); // clear OOM 143 | } 144 | 145 | return true; 146 | } 147 | 148 | extern "C" int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) { 149 | JNIEnv* e = reinterpret_cast(env); 150 | 151 | if ((*env)->ExceptionCheck(e)) { 152 | /* TODO: consider creating the new exception with this as "cause" */ 153 | scoped_local_ref exception(env, (*env)->ExceptionOccurred(e)); 154 | (*env)->ExceptionClear(e); 155 | 156 | if (exception.get() != NULL) { 157 | logExceptionSummary(env, exception.get(), className); 158 | } 159 | } 160 | 161 | scoped_local_ref exceptionClass(env, findClass(env, className)); 162 | if (exceptionClass.get() == NULL) { 163 | ALOGE("Unable to find exception class %s", className); 164 | /* ClassNotFoundException now pending */ 165 | return -1; 166 | } 167 | 168 | if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) { 169 | ALOGE("Failed throwing '%s' '%s'", className, msg); 170 | /* an exception, most likely OOM, will now be pending */ 171 | return -1; 172 | } 173 | 174 | return 0; 175 | } 176 | 177 | int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) { 178 | char msgBuf[512]; 179 | vsnprintf(msgBuf, sizeof(msgBuf), fmt, args); 180 | return jniThrowException(env, className, msgBuf); 181 | } 182 | 183 | int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) { 184 | return jniThrowException(env, "java/lang/NullPointerException", msg); 185 | } 186 | 187 | int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) { 188 | return jniThrowException(env, "java/lang/RuntimeException", msg); 189 | } 190 | 191 | int jniThrowIOException(C_JNIEnv* env, int errnum) { 192 | char buffer[80]; 193 | const char* message = jniStrError(errnum, buffer, sizeof(buffer)); 194 | return jniThrowException(env, "java/io/IOException", message); 195 | } 196 | 197 | const char* jniStrError(int errnum, char* buf, size_t buflen) { 198 | #if __GLIBC__ 199 | // Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int. 200 | // char *strerror_r(int errnum, char *buf, size_t n); 201 | return strerror_r(errnum, buf, buflen); 202 | #else 203 | int rc = strerror_r(errnum, buf, buflen); 204 | if (rc != 0) { 205 | // (POSIX only guarantees a value other than 0. The safest 206 | // way to implement this function is to use C++ and overload on the 207 | // type of strerror_r to accurately distinguish GNU from POSIX.) 208 | snprintf(buf, buflen, "errno %d", errnum); 209 | } 210 | return buf; 211 | #endif 212 | } 213 | 214 | void* operator new (size_t size) { return malloc(size); } 215 | void* operator new [] (size_t size) { return malloc(size); } 216 | void operator delete (void* pointer) { free(pointer); } 217 | void operator delete [] (void* pointer) { free(pointer); } 218 | -------------------------------------------------------------------------------- /sqlite-android/src/main/jni/sqlite/JNIHelp.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 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 | /* 18 | * JNI helper functions. 19 | * 20 | * This file may be included by C or C++ code, which is trouble because jni.h 21 | * uses different typedefs for JNIEnv in each language. 22 | * 23 | * TODO: remove C support. 24 | */ 25 | #ifndef NATIVEHELPER_JNIHELP_H_ 26 | #define NATIVEHELPER_JNIHELP_H_ 27 | 28 | #include "jni.h" 29 | #include 30 | 31 | #ifndef NELEM 32 | # define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0]))) 33 | #endif 34 | 35 | #ifdef __cplusplus 36 | extern "C" { 37 | #endif 38 | 39 | /* 40 | * Register one or more native methods with a particular class. 41 | * "className" looks like "java/lang/String". Aborts on failure. 42 | * TODO: fix all callers and change the return type to void. 43 | */ 44 | int jniRegisterNativeMethods(C_JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods); 45 | 46 | /* 47 | * Throw an exception with the specified class and an optional message. 48 | * 49 | * The "className" argument will be passed directly to FindClass, which 50 | * takes strings with slashes (e.g. "java/lang/Object"). 51 | * 52 | * If an exception is currently pending, we log a warning message and 53 | * clear it. 54 | * 55 | * Returns 0 on success, nonzero if something failed (e.g. the exception 56 | * class couldn't be found, so *an* exception will still be pending). 57 | * 58 | * Currently aborts the VM if it can't throw the exception. 59 | */ 60 | int jniThrowException(C_JNIEnv* env, const char* className, const char* msg); 61 | 62 | /* 63 | * Throw a java.lang.NullPointerException, with an optional message. 64 | */ 65 | int jniThrowNullPointerException(C_JNIEnv* env, const char* msg); 66 | 67 | /* 68 | * Throw a java.lang.RuntimeException, with an optional message. 69 | */ 70 | int jniThrowRuntimeException(C_JNIEnv* env, const char* msg); 71 | 72 | /* 73 | * Throw a java.io.IOException, generating the message from errno. 74 | */ 75 | int jniThrowIOException(C_JNIEnv* env, int errnum); 76 | 77 | /* 78 | * Return a pointer to a locale-dependent error string explaining errno 79 | * value 'errnum'. The returned pointer may or may not be equal to 'buf'. 80 | * This function is thread-safe (unlike strerror) and portable (unlike 81 | * strerror_r). 82 | */ 83 | const char* jniStrError(int errnum, char* buf, size_t buflen); 84 | 85 | /* 86 | * Returns a new java.io.FileDescriptor for the given int fd. 87 | */ 88 | jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd); 89 | 90 | /* 91 | * Returns the int fd from a java.io.FileDescriptor. 92 | */ 93 | int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor); 94 | 95 | /* 96 | * Sets the int fd in a java.io.FileDescriptor. 97 | */ 98 | void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value); 99 | 100 | /* 101 | * Returns the reference from a java.lang.ref.Reference. 102 | */ 103 | jobject jniGetReferent(C_JNIEnv* env, jobject ref); 104 | 105 | #ifdef __cplusplus 106 | } 107 | #endif 108 | 109 | 110 | /* 111 | * For C++ code, we provide inlines that map to the C functions. g++ always 112 | * inlines these, even on non-optimized builds. 113 | */ 114 | #if defined(__cplusplus) 115 | inline int jniRegisterNativeMethods(JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods) { 116 | return jniRegisterNativeMethods(&env->functions, className, gMethods, numMethods); 117 | } 118 | 119 | inline int jniThrowException(JNIEnv* env, const char* className, const char* msg) { 120 | return jniThrowException(&env->functions, className, msg); 121 | } 122 | 123 | extern "C" int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args); 124 | 125 | /* 126 | * Equivalent to jniThrowException but with a printf-like format string and 127 | * variable-length argument list. This is only available in C++. 128 | */ 129 | inline int jniThrowExceptionFmt(JNIEnv* env, const char* className, const char* fmt, ...) { 130 | va_list args; 131 | va_start(args, fmt); 132 | return jniThrowExceptionFmt(&env->functions, className, fmt, args); 133 | va_end(args); 134 | } 135 | 136 | inline int jniThrowNullPointerException(JNIEnv* env, const char* msg) { 137 | return jniThrowNullPointerException(&env->functions, msg); 138 | } 139 | 140 | inline int jniThrowRuntimeException(JNIEnv* env, const char* msg) { 141 | return jniThrowRuntimeException(&env->functions, msg); 142 | } 143 | 144 | inline int jniThrowIOException(JNIEnv* env, int errnum) { 145 | return jniThrowIOException(&env->functions, errnum); 146 | } 147 | 148 | inline jobject jniCreateFileDescriptor(JNIEnv* env, int fd) { 149 | return jniCreateFileDescriptor(&env->functions, fd); 150 | } 151 | 152 | inline int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor) { 153 | return jniGetFDFromFileDescriptor(&env->functions, fileDescriptor); 154 | } 155 | 156 | inline void jniSetFileDescriptorOfFD(JNIEnv* env, jobject fileDescriptor, int value) { 157 | jniSetFileDescriptorOfFD(&env->functions, fileDescriptor, value); 158 | } 159 | 160 | inline jobject jniGetReferent(JNIEnv* env, jobject ref) { 161 | return jniGetReferent(&env->functions, ref); 162 | } 163 | 164 | #endif 165 | 166 | #define FIND_CLASS(var, className) \ 167 | var = env->FindClass(className); \ 168 | LOG_FATAL_IF(! var, "Unable to find class " className); 169 | 170 | #define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \ 171 | var = env->GetMethodID(clazz, methodName, fieldDescriptor); \ 172 | LOG_FATAL_IF(! var, "Unable to find method" methodName); 173 | 174 | #define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \ 175 | var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \ 176 | LOG_FATAL_IF(! var, "Unable to find field " fieldName); 177 | 178 | #endif /* NATIVEHELPER_JNIHELP_H_ */ 179 | -------------------------------------------------------------------------------- /sqlite-android/src/main/jni/sqlite/JNIString.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed to the Apache Software Foundation (ASF) under one or more 3 | * contributor license agreements. See the NOTICE file distributed with 4 | * this work for additional information regarding copyright ownership. 5 | * The ASF licenses this file to You under the Apache License, Version 2.0 6 | * (the "License"); you may not use this file except in compliance with 7 | * the License. You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | // Note this code is adapted from AOSP implementation of String, now located at 19 | // https://android.googlesource.com/platform/libcore/+/master/libart/src/main/java/java/lang/StringFactory.java 20 | 21 | #include 22 | 23 | #define REPLACEMENT_CHAR 0xfffd; 24 | 25 | namespace android { 26 | 27 | jsize utf8ToJavaCharArray(const char* d, jchar v[], jint byteCount) { 28 | jint idx = 0; 29 | jint last = byteCount; 30 | jint s = 0; 31 | outer: 32 | while (idx < last) { 33 | jbyte b0 = d[idx++]; 34 | if ((b0 & 0x80) == 0) { 35 | // 0xxxxxxx 36 | // Range: U-00000000 - U-0000007F 37 | jint val = b0 & 0xff; 38 | v[s++] = (jchar) val; 39 | } else if (((b0 & 0xe0) == 0xc0) || ((b0 & 0xf0) == 0xe0) || 40 | ((b0 & 0xf8) == 0xf0) || ((b0 & 0xfc) == 0xf8) || ((b0 & 0xfe) == 0xfc)) { 41 | jint utfCount = 1; 42 | if ((b0 & 0xf0) == 0xe0) utfCount = 2; 43 | else if ((b0 & 0xf8) == 0xf0) utfCount = 3; 44 | else if ((b0 & 0xfc) == 0xf8) utfCount = 4; 45 | else if ((b0 & 0xfe) == 0xfc) utfCount = 5; 46 | 47 | // 110xxxxx (10xxxxxx)+ 48 | // Range: U-00000080 - U-000007FF (count == 1) 49 | // Range: U-00000800 - U-0000FFFF (count == 2) 50 | // Range: U-00010000 - U-001FFFFF (count == 3) 51 | // Range: U-00200000 - U-03FFFFFF (count == 4) 52 | // Range: U-04000000 - U-7FFFFFFF (count == 5) 53 | 54 | if (idx + utfCount > last) { 55 | v[s++] = REPLACEMENT_CHAR; 56 | continue; 57 | } 58 | 59 | // Extract usable bits from b0 60 | jint val = b0 & (0x1f >> (utfCount - 1)); 61 | for (int i = 0; i < utfCount; ++i) { 62 | jbyte b = d[idx++]; 63 | if ((b & 0xc0) != 0x80) { 64 | v[s++] = REPLACEMENT_CHAR; 65 | idx--; // Put the input char back 66 | goto outer; 67 | } 68 | // Push new bits in from the right side 69 | val <<= 6; 70 | val |= b & 0x3f; 71 | } 72 | 73 | // Note: Java allows overlong char 74 | // specifications To disallow, check that val 75 | // is greater than or equal to the minimum 76 | // value for each count: 77 | // 78 | // count min value 79 | // ----- ---------- 80 | // 1 0x80 81 | // 2 0x800 82 | // 3 0x10000 83 | // 4 0x200000 84 | // 5 0x4000000 85 | 86 | // Allow surrogate values (0xD800 - 0xDFFF) to 87 | // be specified using 3-byte UTF values only 88 | if ((utfCount != 2) && (val >= 0xD800) && (val <= 0xDFFF)) { 89 | v[s++] = REPLACEMENT_CHAR; 90 | continue; 91 | } 92 | 93 | // Reject chars greater than the Unicode maximum of U+10FFFF. 94 | if (val > 0x10FFFF) { 95 | v[s++] = REPLACEMENT_CHAR; 96 | continue; 97 | } 98 | 99 | // Encode chars from U+10000 up as surrogate pairs 100 | if (val < 0x10000) { 101 | v[s++] = (jchar) val; 102 | } else { 103 | int x = val & 0xffff; 104 | int u = (val >> 16) & 0x1f; 105 | int w = (u - 1) & 0xffff; 106 | int hi = 0xd800 | (w << 6) | (x >> 10); 107 | int lo = 0xdc00 | (x & 0x3ff); 108 | v[s++] = (jchar) hi; 109 | v[s++] = (jchar) lo; 110 | } 111 | } else { 112 | // Illegal values 0x8*, 0x9*, 0xa*, 0xb*, 0xfd-0xff 113 | v[s++] = REPLACEMENT_CHAR; 114 | } 115 | } 116 | return s; 117 | } 118 | } -------------------------------------------------------------------------------- /sqlite-android/src/main/jni/sqlite/README: -------------------------------------------------------------------------------- 1 | 2 | All the files in this directory are copied from stock android. The following 3 | files: 4 | 5 | JNIHelp.cpp 6 | ALog-priv.h 7 | 8 | are copied in from Android's libnativehelper module (altogether less than 1000 9 | lines of code). The remainder are from the core framework (directory 10 | /frameworks/base/core/jni). 11 | 12 | Notes on changes: 13 | 14 | The ashmem_XXX() interfaces are used for the various "xxxForBlobDescriptor()" 15 | API functions. The code in libcutils for this seems to be platform 16 | dependent - some platforms have kernel support, others have a user space 17 | implementation. So these functions are not supported for now. 18 | 19 | The original SQLiteConnection.cpp uses AndroidRuntime::genJNIEnv() to obtain a 20 | pointer to the current threads environment. Changed to store a pointer to the 21 | process JavaVM (Android allows only one) as a global variable. Then retrieve 22 | the JNIEnv as needed using GetEnv(). 23 | 24 | Replaced uses of class String8 with std::string in SQLiteConnection.cpp and a 25 | few other places. 26 | 27 | The "LOCALIZED" collation and some miscellaneous user-functions added by the 28 | sqlite3_android.cpp module are not included. A collation called LOCALIZED 29 | that is equivalent to BINARY is added instead to keep various things working. 30 | This should not cause serious problems - class SQLiteConnection always 31 | runs "REINDEX LOCALIZED" immediately after opening a connection. 32 | 33 | -------------------------------------------------------------------------------- /sqlite-android/src/main/jni/sqlite/android_database_SQLiteCommon.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 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 | // modified from original source see README at the top level of this project 17 | 18 | #include "android_database_SQLiteCommon.h" 19 | 20 | namespace android { 21 | 22 | /* throw a SQLiteException with a message appropriate for the error in handle */ 23 | void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle) { 24 | throw_sqlite3_exception(env, handle, NULL); 25 | } 26 | 27 | /* throw a SQLiteException with the given message */ 28 | void throw_sqlite3_exception(JNIEnv* env, const char* message) { 29 | throw_sqlite3_exception(env, NULL, message); 30 | } 31 | 32 | /* throw a SQLiteException with a message appropriate for the error in handle 33 | concatenated with the given message 34 | */ 35 | void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle, const char* message) { 36 | if (handle) { 37 | // get the error code and message from the SQLite connection 38 | // the error message may contain more information than the error code 39 | // because it is based on the extended error code rather than the simplified 40 | // error code that SQLite normally returns. 41 | throw_sqlite3_exception(env, sqlite3_extended_errcode(handle), 42 | sqlite3_errmsg(handle), message); 43 | } else { 44 | // we use SQLITE_OK so that a generic SQLiteException is thrown; 45 | // any code not specified in the switch statement below would do. 46 | throw_sqlite3_exception(env, SQLITE_OK, "unknown error", message); 47 | } 48 | } 49 | 50 | /* throw a SQLiteException for a given error code 51 | * should only be used when the database connection is not available because the 52 | * error information will not be quite as rich */ 53 | void throw_sqlite3_exception_errcode(JNIEnv* env, int errcode, const char* message) { 54 | throw_sqlite3_exception(env, errcode, "unknown error", message); 55 | } 56 | 57 | /* throw a SQLiteException for a given error code, sqlite3message, and 58 | user message 59 | */ 60 | void throw_sqlite3_exception(JNIEnv* env, int errcode, 61 | const char* sqlite3Message, const char* message) { 62 | const char* exceptionClass; 63 | switch (errcode & 0xff) { /* mask off extended error code */ 64 | case SQLITE_IOERR: 65 | exceptionClass = "android/database/sqlite/SQLiteDiskIOException"; 66 | break; 67 | case SQLITE_CORRUPT: 68 | case SQLITE_NOTADB: // treat "unsupported file format" error as corruption also 69 | exceptionClass = "android/database/sqlite/SQLiteDatabaseCorruptException"; 70 | break; 71 | case SQLITE_CONSTRAINT: 72 | exceptionClass = "android/database/sqlite/SQLiteConstraintException"; 73 | break; 74 | case SQLITE_ABORT: 75 | exceptionClass = "android/database/sqlite/SQLiteAbortException"; 76 | break; 77 | case SQLITE_DONE: 78 | exceptionClass = "android/database/sqlite/SQLiteDoneException"; 79 | sqlite3Message = NULL; // SQLite error message is irrelevant in this case 80 | break; 81 | case SQLITE_FULL: 82 | exceptionClass = "android/database/sqlite/SQLiteFullException"; 83 | break; 84 | case SQLITE_MISUSE: 85 | exceptionClass = "android/database/sqlite/SQLiteMisuseException"; 86 | break; 87 | case SQLITE_PERM: 88 | exceptionClass = "android/database/sqlite/SQLiteAccessPermException"; 89 | break; 90 | case SQLITE_BUSY: 91 | exceptionClass = "android/database/sqlite/SQLiteDatabaseLockedException"; 92 | break; 93 | case SQLITE_LOCKED: 94 | exceptionClass = "android/database/sqlite/SQLiteTableLockedException"; 95 | break; 96 | case SQLITE_READONLY: 97 | exceptionClass = "android/database/sqlite/SQLiteReadOnlyDatabaseException"; 98 | break; 99 | case SQLITE_CANTOPEN: 100 | exceptionClass = "android/database/sqlite/SQLiteCantOpenDatabaseException"; 101 | break; 102 | case SQLITE_TOOBIG: 103 | exceptionClass = "android/database/sqlite/SQLiteBlobTooBigException"; 104 | break; 105 | case SQLITE_RANGE: 106 | exceptionClass = "android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException"; 107 | break; 108 | case SQLITE_NOMEM: 109 | exceptionClass = "android/database/sqlite/SQLiteOutOfMemoryException"; 110 | break; 111 | case SQLITE_MISMATCH: 112 | exceptionClass = "android/database/sqlite/SQLiteDatatypeMismatchException"; 113 | break; 114 | case SQLITE_INTERRUPT: 115 | exceptionClass = "androidx/core/os/OperationCanceledException"; 116 | break; 117 | default: 118 | exceptionClass = "android/database/sqlite/SQLiteException"; 119 | break; 120 | } 121 | 122 | // check this exception class exists otherwise just default to SQLiteException 123 | if (env->FindClass(exceptionClass) == NULL) { 124 | exceptionClass = "android/database/sqlite/SQLiteException"; 125 | } 126 | 127 | if (sqlite3Message) { 128 | char *zFullmsg = sqlite3_mprintf( 129 | "%s (code %d)%s%s", sqlite3Message, errcode, 130 | (message ? ": " : ""), (message ? message : "") 131 | ); 132 | jniThrowException(env, exceptionClass, zFullmsg); 133 | sqlite3_free(zFullmsg); 134 | } else { 135 | jniThrowException(env, exceptionClass, message); 136 | } 137 | } 138 | 139 | 140 | } // namespace android 141 | -------------------------------------------------------------------------------- /sqlite-android/src/main/jni/sqlite/android_database_SQLiteCommon.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 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 | // modified from original source see README at the top level of this project 17 | 18 | #ifndef _ANDROID_DATABASE_SQLITE_COMMON_H 19 | #define _ANDROID_DATABASE_SQLITE_COMMON_H 20 | 21 | #include 22 | #include "JNIHelp.h" 23 | 24 | #include 25 | 26 | // Special log tags defined in SQLiteDebug.java. 27 | #define SQLITE_LOG_TAG "SQLiteLog" 28 | #define SQLITE_TRACE_TAG "SQLiteStatements" 29 | #define SQLITE_PROFILE_TAG "SQLiteTime" 30 | 31 | namespace android { 32 | 33 | /* throw a SQLiteException with a message appropriate for the error in handle */ 34 | void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle); 35 | 36 | /* throw a SQLiteException with the given message */ 37 | void throw_sqlite3_exception(JNIEnv* env, const char* message); 38 | 39 | /* throw a SQLiteException with a message appropriate for the error in handle 40 | concatenated with the given message 41 | */ 42 | void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle, const char* message); 43 | 44 | /* throw a SQLiteException for a given error code */ 45 | void throw_sqlite3_exception_errcode(JNIEnv* env, int errcode, const char* message); 46 | 47 | void throw_sqlite3_exception(JNIEnv* env, int errcode, 48 | const char* sqlite3Message, const char* message); 49 | 50 | } 51 | 52 | #endif // _ANDROID_DATABASE_SQLITE_COMMON_H 53 | -------------------------------------------------------------------------------- /sqlite-android/src/main/jni/sqlite/android_database_SQLiteDebug.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2007 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 | // modified from original source see README at the top level of this project 17 | 18 | #define LOG_TAG "SQLiteDebug" 19 | 20 | #include 21 | #include "JNIHelp.h" 22 | #include "ALog-priv.h" 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include 30 | 31 | namespace android { 32 | 33 | static struct { 34 | jfieldID memoryUsed; 35 | jfieldID pageCacheOverflow; 36 | jfieldID largestMemAlloc; 37 | } gSQLiteDebugPagerStatsClassInfo; 38 | 39 | static void nativeGetPagerStats(JNIEnv *env, jobject clazz, jobject statsObj) 40 | { 41 | int memoryUsed; 42 | int pageCacheOverflow; 43 | int largestMemAlloc; 44 | int unused; 45 | 46 | sqlite3_status(SQLITE_STATUS_MEMORY_USED, &memoryUsed, &unused, 0); 47 | sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &unused, &largestMemAlloc, 0); 48 | sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &pageCacheOverflow, &unused, 0); 49 | env->SetIntField(statsObj, gSQLiteDebugPagerStatsClassInfo.memoryUsed, memoryUsed); 50 | env->SetIntField(statsObj, gSQLiteDebugPagerStatsClassInfo.pageCacheOverflow, 51 | pageCacheOverflow); 52 | env->SetIntField(statsObj, gSQLiteDebugPagerStatsClassInfo.largestMemAlloc, largestMemAlloc); 53 | } 54 | 55 | /* 56 | * JNI registration. 57 | */ 58 | 59 | static JNINativeMethod gMethods[] = 60 | { 61 | { "nativeGetPagerStats", "(Lio/requery/android/database/sqlite/SQLiteDebug$PagerStats;)V", 62 | (void*) nativeGetPagerStats }, 63 | }; 64 | 65 | int register_android_database_SQLiteDebug(JNIEnv *env) 66 | { 67 | jclass clazz; 68 | FIND_CLASS(clazz, "io/requery/android/database/sqlite/SQLiteDebug$PagerStats"); 69 | 70 | GET_FIELD_ID(gSQLiteDebugPagerStatsClassInfo.memoryUsed, clazz, 71 | "memoryUsed", "I"); 72 | GET_FIELD_ID(gSQLiteDebugPagerStatsClassInfo.largestMemAlloc, clazz, 73 | "largestMemAlloc", "I"); 74 | GET_FIELD_ID(gSQLiteDebugPagerStatsClassInfo.pageCacheOverflow, clazz, 75 | "pageCacheOverflow", "I"); 76 | 77 | return jniRegisterNativeMethods(env, "io/requery/android/database/sqlite/SQLiteDebug", 78 | gMethods, NELEM(gMethods)); 79 | } 80 | 81 | } // namespace android 82 | -------------------------------------------------------------------------------- /sqlite-android/src/main/jni/sqlite/android_database_SQLiteFunction.cpp: -------------------------------------------------------------------------------- 1 | #define LOG_TAG "SQLiteFunction" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "sqlite3.h" 10 | #include "JNIHelp.h" 11 | #include "ALog-priv.h" 12 | #include "android_database_SQLiteCommon.h" 13 | 14 | namespace android { 15 | 16 | /* Returns the sqlite3_value for the given arg of the given function. 17 | * If 0 is returned, an exception has been thrown to report the reason. */ 18 | static sqlite3_value *tovalue(JNIEnv *env, jlong argsPtr, jint arg) { 19 | if (arg < 0) { 20 | throw_sqlite3_exception(env, "Invalid arg index"); 21 | return 0; 22 | } 23 | if (!argsPtr) { 24 | throw_sqlite3_exception(env, "Invalid argsPtr"); 25 | return 0; 26 | } 27 | 28 | sqlite3_value **args = reinterpret_cast(argsPtr); 29 | return args[arg]; 30 | } 31 | 32 | static sqlite3_context *tocontext(JNIEnv *env, jlong contextPtr) { 33 | if (!contextPtr) { 34 | throw_sqlite3_exception(env, "Invalid contextPtr"); 35 | return 0; 36 | } 37 | 38 | return reinterpret_cast(contextPtr); 39 | } 40 | 41 | /* 42 | * Getters 43 | */ 44 | 45 | static jbyteArray nativeGetArgBlob(JNIEnv* env, jclass clazz, jlong argsPtr, 46 | jint arg) { 47 | int length; 48 | jbyteArray byteArray; 49 | const void *blob; 50 | 51 | sqlite3_value *value = tovalue(env, argsPtr, arg); 52 | if (!value) return NULL; 53 | 54 | blob = sqlite3_value_blob(value); 55 | if (!blob) return NULL; 56 | 57 | length = sqlite3_value_bytes(value); 58 | byteArray = env->NewByteArray(length); 59 | if (!byteArray) { 60 | env->ExceptionClear(); 61 | throw_sqlite3_exception(env, "Native could not create new byte[]"); 62 | return NULL; 63 | } 64 | 65 | env->SetByteArrayRegion(byteArray, 0, length, static_cast(blob)); 66 | return byteArray; 67 | } 68 | 69 | static jstring nativeGetArgString(JNIEnv* env, jclass clazz, jlong argsPtr, 70 | jint arg) { 71 | sqlite3_value *value = tovalue(env, argsPtr, arg); 72 | if (!value) return NULL; 73 | 74 | const jchar* chars = static_cast(sqlite3_value_text16(value)); 75 | if (!chars) return NULL; 76 | 77 | size_t len = sqlite3_value_bytes16(value) / sizeof(jchar); 78 | jstring str = env->NewString(chars, len); 79 | if (!str) { 80 | env->ExceptionClear(); 81 | throw_sqlite3_exception(env, "Native could not allocate string"); 82 | return NULL; 83 | } 84 | 85 | return str; 86 | } 87 | 88 | static jlong nativeGetArgLong(JNIEnv* env, jclass clazz, jlong argsPtr, 89 | jint arg) { 90 | sqlite3_value *value = tovalue(env, argsPtr, arg); 91 | return value ? sqlite3_value_int64(value) : 0; 92 | } 93 | 94 | static jdouble nativeGetArgDouble(JNIEnv* env, jclass clazz, jlong argsPtr, 95 | jint arg) { 96 | sqlite3_value *value = tovalue(env, argsPtr, arg); 97 | return value ? sqlite3_value_double(value) : 0; 98 | } 99 | 100 | static jint nativeGetArgInt(JNIEnv* env, jclass clazz, jlong argsPtr, 101 | jint arg) { 102 | sqlite3_value *value = tovalue(env, argsPtr, arg); 103 | return value ? sqlite3_value_int(value) : 0; 104 | } 105 | 106 | /* 107 | * Setters 108 | */ 109 | 110 | static void nativeSetResultBlob(JNIEnv* env, jclass clazz, 111 | jlong contextPtr, jbyteArray result) { 112 | sqlite3_context *context = tocontext(env, contextPtr); 113 | if (!context) return; 114 | if (result == NULL) { 115 | sqlite3_result_null(context); 116 | return; 117 | } 118 | 119 | jsize len = env->GetArrayLength(result); 120 | void *bytes = env->GetPrimitiveArrayCritical(result, NULL); 121 | if (!bytes) { 122 | env->ExceptionClear(); 123 | throw_sqlite3_exception(env, "Out of memory accepting blob"); 124 | return; 125 | } 126 | 127 | sqlite3_result_blob(context, bytes, len, SQLITE_TRANSIENT); 128 | env->ReleasePrimitiveArrayCritical(result, bytes, JNI_ABORT); 129 | } 130 | 131 | static void nativeSetResultString(JNIEnv* env, jclass clazz, 132 | jlong contextPtr, jstring result) { 133 | sqlite3_context *context = tocontext(env, contextPtr); 134 | if (result == NULL) { 135 | sqlite3_result_null(context); 136 | return; 137 | } 138 | 139 | const char* chars = env->GetStringUTFChars(result, NULL); 140 | if (!chars) { 141 | ALOGE("result value can't be transferred to UTFChars"); 142 | sqlite3_result_error_nomem(context); 143 | return; 144 | } 145 | 146 | sqlite3_result_text(context, chars, -1, SQLITE_TRANSIENT); 147 | env->ReleaseStringUTFChars(result, chars); 148 | } 149 | 150 | static void nativeSetResultLong(JNIEnv* env, jclass clazz, 151 | jlong contextPtr, jlong result) { 152 | sqlite3_context *context = tocontext(env, contextPtr); 153 | if (context) sqlite3_result_int64(context, result); 154 | } 155 | 156 | static void nativeSetResultDouble(JNIEnv* env, jclass clazz, 157 | jlong contextPtr, jdouble result) { 158 | sqlite3_context *context = tocontext(env, contextPtr); 159 | if (context) sqlite3_result_double(context, result); 160 | } 161 | 162 | static void nativeSetResultInt(JNIEnv* env, jclass clazz, 163 | jlong contextPtr, jint result) { 164 | sqlite3_context *context = tocontext(env, contextPtr); 165 | if (context) sqlite3_result_int(context, result); 166 | } 167 | 168 | static void nativeSetResultError(JNIEnv* env, jclass clazz, 169 | jlong contextPtr, jstring error) { 170 | sqlite3_context *context = tocontext(env, contextPtr); 171 | if (error == NULL) { 172 | sqlite3_result_null(context); 173 | return; 174 | } 175 | 176 | const char* chars = env->GetStringUTFChars(error, NULL); 177 | if (!chars) { 178 | ALOGE("result value can't be transferred to UTFChars"); 179 | sqlite3_result_error_nomem(context); 180 | return; 181 | } 182 | 183 | sqlite3_result_error(context, chars, -1); 184 | env->ReleaseStringUTFChars(error, chars); 185 | } 186 | 187 | static void nativeSetResultNull(JNIEnv* env, jclass clazz, jlong contextPtr) { 188 | sqlite3_context *context = tocontext(env, contextPtr); 189 | if (context) sqlite3_result_null(context); 190 | } 191 | 192 | 193 | static const JNINativeMethod sMethods[] = 194 | { 195 | /* name, signature, funcPtr */ 196 | { "nativeGetArgBlob", "(JI)[B", 197 | (void*)nativeGetArgBlob }, 198 | { "nativeGetArgString", "(JI)Ljava/lang/String;", 199 | (void*)nativeGetArgString }, 200 | { "nativeGetArgLong", "(JI)J", 201 | (void*)nativeGetArgLong }, 202 | { "nativeGetArgDouble", "(JI)D", 203 | (void*)nativeGetArgDouble }, 204 | { "nativeGetArgInt", "(JI)I", 205 | (void*)nativeGetArgInt }, 206 | 207 | { "nativeSetResultBlob", "(J[B)V", 208 | (void*)nativeSetResultBlob }, 209 | { "nativeSetResultString", "(JLjava/lang/String;)V", 210 | (void*)nativeSetResultString }, 211 | { "nativeSetResultLong", "(JJ)V", 212 | (void*)nativeSetResultLong }, 213 | { "nativeSetResultDouble", "(JD)V", 214 | (void*)nativeSetResultDouble }, 215 | { "nativeSetResultInt", "(JI)V", 216 | (void*)nativeSetResultInt }, 217 | { "nativeSetResultError", "(JLjava/lang/String;)V", 218 | (void*)nativeSetResultError }, 219 | { "nativeSetResultNull", "(J)V", 220 | (void*)nativeSetResultNull }, 221 | }; 222 | 223 | int register_android_database_SQLiteFunction(JNIEnv* env) 224 | { 225 | return jniRegisterNativeMethods(env, 226 | "io/requery/android/database/sqlite/SQLiteFunction", sMethods, NELEM(sMethods)); 227 | } 228 | 229 | } // namespace android 230 | -------------------------------------------------------------------------------- /sqlite-android/src/main/jni/sqlite/android_database_SQLiteGlobal.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 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 | // modified from original source see README at the top level of this project 17 | 18 | #define LOG_TAG "SQLiteGlobal" 19 | 20 | #include 21 | #include "JNIHelp.h" 22 | #include "ALog-priv.h" 23 | 24 | #include 25 | //#include 26 | 27 | #include "android_database_SQLiteCommon.h" 28 | 29 | namespace android { 30 | 31 | // Limit heap to 8MB for now. This is 4 times the maximum cursor window 32 | // size, as has been used by the original code in SQLiteDatabase for 33 | // a long time. 34 | static const int SOFT_HEAP_LIMIT = 8 * 1024 * 1024; 35 | 36 | 37 | // Called each time a message is logged. 38 | static void sqliteLogCallback(void* data, int iErrCode, const char* zMsg) { 39 | bool verboseLog = !!data; 40 | if (iErrCode == 0 || iErrCode == SQLITE_CONSTRAINT || iErrCode == SQLITE_SCHEMA) { 41 | if (verboseLog) { 42 | ALOG(LOG_VERBOSE, SQLITE_LOG_TAG, "(%d) %s\n", iErrCode, zMsg); 43 | } 44 | } else { 45 | ALOG(LOG_ERROR, SQLITE_LOG_TAG, "(%d) %s\n", iErrCode, zMsg); 46 | } 47 | } 48 | 49 | // Sets the global SQLite configuration. 50 | // This must be called before any other SQLite functions are called. 51 | static void sqliteInitialize() { 52 | // Enable multi-threaded mode. In this mode, SQLite is safe to use by multiple 53 | // threads as long as no two threads use the same database connection at the same 54 | // time (which we guarantee in the SQLite database wrappers). 55 | sqlite3_config(SQLITE_CONFIG_MULTITHREAD); 56 | 57 | // Redirect SQLite log messages to the Android log. 58 | #if 0 59 | bool verboseLog = android_util_Log_isVerboseLogEnabled(SQLITE_LOG_TAG); 60 | #endif 61 | bool verboseLog = false; 62 | sqlite3_config(SQLITE_CONFIG_LOG, &sqliteLogCallback, verboseLog ? (void*)1 : NULL); 63 | 64 | // The soft heap limit prevents the page cache allocations from growing 65 | // beyond the given limit, no matter what the max page cache sizes are 66 | // set to. The limit does not, as of 3.5.0, affect any other allocations. 67 | sqlite3_soft_heap_limit(SOFT_HEAP_LIMIT); 68 | 69 | // Initialize SQLite. 70 | sqlite3_initialize(); 71 | } 72 | 73 | static jint nativeReleaseMemory(JNIEnv* env, jclass clazz) { 74 | return sqlite3_release_memory(SOFT_HEAP_LIMIT); 75 | } 76 | 77 | static JNINativeMethod sMethods[] = 78 | { 79 | /* name, signature, funcPtr */ 80 | { "nativeReleaseMemory", "()I", 81 | (void*)nativeReleaseMemory }, 82 | }; 83 | 84 | int register_android_database_SQLiteGlobal(JNIEnv *env) 85 | { 86 | sqliteInitialize(); 87 | 88 | return jniRegisterNativeMethods(env, "io/requery/android/database/sqlite/SQLiteGlobal", 89 | sMethods, NELEM(sMethods)); 90 | } 91 | 92 | } // namespace android 93 | --------------------------------------------------------------------------------