diff --git a/app/build.gradle b/app/build.gradle
index 4f0350c03f..33c9e49def 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -469,7 +469,7 @@ dependencies {
// https://github.com/requery/sqlite-android/
// https://jitpack.io/#requery/sqlite-android
//implementation "com.github.requery:sqlite-android:$requery_version"
- implementation project(':sqlite-android')
+ //implementation project(':sqlite-android')
// https://mvnrepository.com/artifact/androidx.paging/paging-runtime
// https://developer.android.com/jetpack/androidx/releases/paging
diff --git a/app/src/main/java/eu/faircode/email/BoundaryCallbackMessages.java b/app/src/main/java/eu/faircode/email/BoundaryCallbackMessages.java
index 853dfc7c4b..f218258124 100644
--- a/app/src/main/java/eu/faircode/email/BoundaryCallbackMessages.java
+++ b/app/src/main/java/eu/faircode/email/BoundaryCallbackMessages.java
@@ -21,6 +21,7 @@ package eu.faircode.email;
import android.content.Context;
import android.content.SharedPreferences;
+import android.database.sqlite.SQLiteDatabase;
import android.text.TextUtils;
import androidx.annotation.NonNull;
@@ -83,8 +84,6 @@ import javax.mail.search.SearchTerm;
import javax.mail.search.SizeTerm;
import javax.mail.search.SubjectTerm;
-import io.requery.android.database.sqlite.SQLiteDatabase;
-
public class BoundaryCallbackMessages extends PagedList.BoundaryCallback {
private Context context;
private AdapterMessage.ViewType viewType;
diff --git a/app/src/main/java/eu/faircode/email/DB.java b/app/src/main/java/eu/faircode/email/DB.java
index 65fd360fd7..602e0326b6 100644
--- a/app/src/main/java/eu/faircode/email/DB.java
+++ b/app/src/main/java/eu/faircode/email/DB.java
@@ -6,6 +6,7 @@ import android.app.ActivityManager;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabaseCorruptException;
import android.net.Uri;
import android.os.Build;
@@ -47,9 +48,6 @@ import java.util.concurrent.ExecutorService;
import javax.mail.Address;
import javax.mail.internet.InternetAddress;
-import io.requery.android.database.sqlite.RequerySQLiteOpenHelperFactory;
-import io.requery.android.database.sqlite.SQLiteDatabase;
-
/*
This file is part of FairEmail.
@@ -422,7 +420,7 @@ public abstract class DB extends RoomDatabase {
return Room
.databaseBuilder(context, DB.class, DB_NAME)
- .openHelperFactory(new RequerySQLiteOpenHelperFactory())
+ //.openHelperFactory(new RequerySQLiteOpenHelperFactory())
.setQueryExecutor(executorQuery)
.setTransactionExecutor(executorTransaction)
.setJournalMode(wal ? JournalMode.WRITE_AHEAD_LOGGING : JournalMode.TRUNCATE) // using the latest sqlite
@@ -2561,7 +2559,14 @@ public abstract class DB extends RoomDatabase {
@Override
@SuppressWarnings("deprecation")
public void endTransaction() {
- super.endTransaction();
+ try {
+ super.endTransaction();
+ } catch (IllegalStateException ex) {
+ if ("Cannot perform this operation because there is no current transaction.".equals(ex.getMessage()))
+ Log.w(ex);
+ else
+ throw ex;
+ }
}
public static class Converters {
diff --git a/app/src/main/java/eu/faircode/email/FragmentDialogSearch.java b/app/src/main/java/eu/faircode/email/FragmentDialogSearch.java
index 5bbf91a029..2ec95e78b9 100644
--- a/app/src/main/java/eu/faircode/email/FragmentDialogSearch.java
+++ b/app/src/main/java/eu/faircode/email/FragmentDialogSearch.java
@@ -27,6 +27,7 @@ import android.content.SharedPreferences;
import android.content.res.ColorStateList;
import android.database.Cursor;
import android.database.MatrixCursor;
+import android.database.sqlite.SQLiteDatabase;
import android.graphics.Typeface;
import android.os.Bundle;
import android.text.TextUtils;
@@ -58,8 +59,6 @@ import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
-import io.requery.android.database.sqlite.SQLiteDatabase;
-
public class FragmentDialogSearch extends FragmentDialogBase {
private ImageButton ibMore;
private TextView tvMore;
diff --git a/app/src/main/java/eu/faircode/email/FragmentOptionsMisc.java b/app/src/main/java/eu/faircode/email/FragmentOptionsMisc.java
index 2dc25d2de8..c52a28ecf9 100644
--- a/app/src/main/java/eu/faircode/email/FragmentOptionsMisc.java
+++ b/app/src/main/java/eu/faircode/email/FragmentOptionsMisc.java
@@ -34,6 +34,7 @@ import android.content.pm.PackageManager;
import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.res.Resources;
+import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabaseCorruptException;
import android.graphics.Paint;
import android.graphics.Typeface;
@@ -86,6 +87,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Field;
+import java.lang.reflect.Method;
import java.nio.charset.Charset;
import java.text.NumberFormat;
import java.util.ArrayList;
@@ -98,8 +100,6 @@ import java.util.Map;
import java.util.Objects;
import java.util.SortedMap;
-import io.requery.android.database.sqlite.SQLiteDatabase;
-
public class FragmentOptionsMisc extends FragmentBase implements SharedPreferences.OnSharedPreferenceChangeListener {
private boolean resumed = false;
private List> languages = new ArrayList<>();
@@ -2115,16 +2115,16 @@ public class FragmentOptionsMisc extends FragmentBase implements SharedPreferenc
tvFingerprint.setText(Helper.getFingerprint(getContext()));
- int cursorWindowSize = -1;
+ Integer cursorWindowSize = null;
try {
- Field fCursorWindowSize = io.requery.android.database.CursorWindow.class.getDeclaredField("sDefaultCursorWindowSize");
- fCursorWindowSize.setAccessible(true);
- cursorWindowSize = fCursorWindowSize.getInt(null);
+ //Field fCursorWindowSize = android.database.CursorWindow.class.getDeclaredField("sDefaultCursorWindowSize");
+ //fCursorWindowSize.setAccessible(true);
+ //cursorWindowSize = fCursorWindowSize.getInt(null);
} catch (Throwable ex) {
Log.w(ex);
}
tvCursorWindow.setText(getString(R.string.title_advanced_cursor_window,
- Helper.humanReadableByteCount(cursorWindowSize, false)));
+ cursorWindowSize == null ? "?" : Helper.humanReadableByteCount(cursorWindowSize, false)));
cardDebug.setVisibility(swDebug.isChecked() || BuildConfig.DEBUG ? View.VISIBLE : View.GONE);
}
diff --git a/app/src/main/java/eu/faircode/email/Fts4DbHelper.java b/app/src/main/java/eu/faircode/email/Fts4DbHelper.java
index b98aed42aa..a3e54957c7 100644
--- a/app/src/main/java/eu/faircode/email/Fts4DbHelper.java
+++ b/app/src/main/java/eu/faircode/email/Fts4DbHelper.java
@@ -23,6 +23,8 @@ import android.annotation.SuppressLint;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
import android.text.TextUtils;
import java.util.ArrayList;
@@ -31,9 +33,6 @@ import java.util.List;
import javax.mail.Address;
-import io.requery.android.database.sqlite.SQLiteDatabase;
-import io.requery.android.database.sqlite.SQLiteOpenHelper;
-
// https://www.sqlite.org/fts3.html
// fts4 requires sqlite version 3.7.4, API 21 Android
public class Fts4DbHelper extends SQLiteOpenHelper {
@@ -121,7 +120,7 @@ public class Fts4DbHelper extends SQLiteOpenHelper {
cv.put("keyword", TextUtils.join(", ", message.keywords));
cv.put("text", text);
cv.put("notes", message.notes);
- db.insert("message", SQLiteDatabase.CONFLICT_FAIL, cv);
+ db.insertWithOnConflict("message", null, cv, SQLiteDatabase.CONFLICT_FAIL);
}
static void delete(SQLiteDatabase db) {
@@ -129,18 +128,18 @@ public class Fts4DbHelper extends SQLiteOpenHelper {
}
static void delete(SQLiteDatabase db, long id) {
- db.delete("message", "rowid = ?", new Object[]{id});
+ db.delete("message", "rowid = ?", new String[]{Long.toString(id)});
}
static List getSuggestions(SQLiteDatabase db, String query, int max) {
List result = new ArrayList<>();
- try (Cursor cursor = db.query(
+ try (Cursor cursor = db.rawQuery(
"SELECT term FROM message_terms" +
" WHERE term LIKE ?" +
" ORDER BY occurrences DESC" +
" LIMIT " + max,
- new Object[]{query})) {
+ new String[]{query})) {
while (cursor != null && cursor.moveToNext())
result.add(cursor.getString(0));
}
@@ -232,7 +231,7 @@ public class Fts4DbHelper extends SQLiteOpenHelper {
try (Cursor cursor = db.query(
"message", new String[]{"rowid"},
select + "message MATCH ?",
- new Object[]{search},
+ new String[]{search},
null, null, "time DESC", null)) {
while (cursor != null && cursor.moveToNext())
result.add(cursor.getLong(0));
diff --git a/app/src/main/java/eu/faircode/email/Fts5DbHelper.java b/app/src/main/java/eu/faircode/email/Fts5DbHelper.java
index 16f8b76c50..7b13e9ed9b 100644
--- a/app/src/main/java/eu/faircode/email/Fts5DbHelper.java
+++ b/app/src/main/java/eu/faircode/email/Fts5DbHelper.java
@@ -23,6 +23,8 @@ import android.annotation.SuppressLint;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
import android.text.TextUtils;
import java.util.ArrayList;
@@ -31,9 +33,6 @@ import java.util.List;
import javax.mail.Address;
-import io.requery.android.database.sqlite.SQLiteDatabase;
-import io.requery.android.database.sqlite.SQLiteOpenHelper;
-
// https://www.sqlite.org/fts5.html
public class Fts5DbHelper extends SQLiteOpenHelper {
private Context context;
@@ -112,7 +111,7 @@ public class Fts5DbHelper extends SQLiteOpenHelper {
cv.put("keyword", TextUtils.join(", ", message.keywords));
cv.put("text", text);
cv.put("notes", message.notes);
- db.insert("message", SQLiteDatabase.CONFLICT_FAIL, cv);
+ db.insertWithOnConflict("message", null, cv, SQLiteDatabase.CONFLICT_FAIL);
}
static void delete(SQLiteDatabase db) {
@@ -120,18 +119,18 @@ public class Fts5DbHelper extends SQLiteOpenHelper {
}
static void delete(SQLiteDatabase db, long id) {
- db.delete("message", "rowid = ?", new Object[]{id});
+ db.delete("message", "rowid = ?", new String[]{Long.toString(id)});
}
static List getSuggestions(SQLiteDatabase db, String query, int max) {
List result = new ArrayList<>();
- try (Cursor cursor = db.query(
+ try (Cursor cursor = db.rawQuery(
"SELECT term FROM message_terms" +
" WHERE term LIKE ?" +
" ORDER BY cnt" +
" LIMIT " + max,
- new Object[]{query})) {
+ new String[]{query})) {
while (cursor != null && cursor.moveToNext())
result.add(cursor.getString(0));
}
@@ -223,7 +222,7 @@ public class Fts5DbHelper extends SQLiteOpenHelper {
try (Cursor cursor = db.query(
"message", new String[]{"rowid"},
select + "message MATCH ?",
- new Object[]{search},
+ new String[]{search},
null, null, "time DESC", null)) {
while (cursor != null && cursor.moveToNext())
result.add(cursor.getLong(0));
diff --git a/app/src/main/java/eu/faircode/email/Log.java b/app/src/main/java/eu/faircode/email/Log.java
index 3fdc1da0c5..542c7eec9c 100644
--- a/app/src/main/java/eu/faircode/email/Log.java
+++ b/app/src/main/java/eu/faircode/email/Log.java
@@ -39,6 +39,7 @@ import android.content.pm.PermissionGroupInfo;
import android.content.pm.PermissionInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
+import android.database.CursorWindowAllocationException;
import android.database.sqlite.SQLiteFullException;
import android.graphics.Point;
import android.graphics.Typeface;
@@ -164,8 +165,6 @@ import javax.net.ssl.SSLSocket;
import javax.net.ssl.TrustManager;
import javax.net.ssl.TrustManagerFactory;
-import io.requery.android.database.CursorWindowAllocationException;
-
public class Log {
private static Context ctx;
diff --git a/app/src/main/java/eu/faircode/email/WorkerCleanup.java b/app/src/main/java/eu/faircode/email/WorkerCleanup.java
index 4deb2b5f9f..b27750461a 100644
--- a/app/src/main/java/eu/faircode/email/WorkerCleanup.java
+++ b/app/src/main/java/eu/faircode/email/WorkerCleanup.java
@@ -26,6 +26,7 @@ import android.app.NotificationManager;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
import android.os.Build;
import android.text.TextUtils;
@@ -45,8 +46,6 @@ import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
-import io.requery.android.database.sqlite.SQLiteDatabase;
-
public class WorkerCleanup extends Worker {
private static final int CLEANUP_INTERVAL = 4; // hours
private static final long KEEP_FILES_DURATION = 3600 * 1000L; // milliseconds
diff --git a/app/src/main/java/eu/faircode/email/WorkerFts.java b/app/src/main/java/eu/faircode/email/WorkerFts.java
index 3020841c89..398df2318a 100644
--- a/app/src/main/java/eu/faircode/email/WorkerFts.java
+++ b/app/src/main/java/eu/faircode/email/WorkerFts.java
@@ -24,6 +24,7 @@ import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
+import android.database.sqlite.SQLiteDatabase;
import androidx.annotation.NonNull;
import androidx.preference.PreferenceManager;
@@ -38,8 +39,6 @@ import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
-import io.requery.android.database.sqlite.SQLiteDatabase;
-
public class WorkerFts extends Worker {
private static final int INDEX_DELAY = 30; // seconds
private static final int INDEX_BATCH_SIZE = 100;
diff --git a/settings.gradle b/settings.gradle
index ab1f6a7344..fd78ac7fec 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,3 +1,2 @@
-include ':app', ':openpgp-api', ':sqlite-android'
+include ':app', ':openpgp-api'
project(':openpgp-api').projectDir = new File('openpgp-api')
-project(':sqlite-android').projectDir = new File('sqlite-android')
diff --git a/sqlite-android/.gitignore b/sqlite-android/.gitignore
deleted file mode 100644
index 054fce48a8..0000000000
--- a/sqlite-android/.gitignore
+++ /dev/null
@@ -1,7 +0,0 @@
-/build
-/src/main/jniLibs
-/src/main/jni/sqlite/sqlite3.h
-/src/main/jni/sqlite/sqlite3.c
-/src/main/jni/sqlite.zip
-/src/main/obj
-.externalNativeBuild/
diff --git a/sqlite-android/build.gradle b/sqlite-android/build.gradle
deleted file mode 100644
index 5357b8d23d..0000000000
--- a/sqlite-android/build.gradle
+++ /dev/null
@@ -1,150 +0,0 @@
-plugins {
- id 'de.undercouch.download'
- id 'com.android.library'
- id 'maven-publish'
-}
-
-group = 'io.requery'
-version = '3.39.3'
-description = 'Android SQLite compatibility library'
-
-android {
- compileSdkVersion 32
- buildToolsVersion "33.0.0"
- ndkVersion '25.0.8775105'
-
- defaultConfig {
- minSdkVersion 14
- versionName project.version
- testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
- consumerProguardFiles 'proguard-rules.pro'
- ndk {
- abiFilters 'armeabi-v7a', 'arm64-v8a', 'x86', 'x86_64'
- }
- }
-
- buildTypes {
- release {
- minifyEnabled false
- proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
- }
- }
- lintOptions {
- abortOnError false
- }
- compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
- }
- externalNativeBuild {
- ndkBuild {
- path 'src/main/jni/Android.mk'
- }
- }
-
- libraryVariants.all {
- it.generateBuildConfigProvider.configure { enabled = false }
- }
-}
-
-dependencies {
- api 'androidx.sqlite:sqlite:2.2.0'
- api 'androidx.core:core:1.8.0'
- androidTestImplementation 'androidx.test:core:1.4.0'
- androidTestImplementation 'androidx.test:runner:1.4.0'
- androidTestImplementation 'androidx.test:rules:1.4.0'
- androidTestImplementation 'androidx.test.ext:junit:1.1.3'
-}
-
-ext {
- sqliteDistributionUrl = 'https://www.sqlite.org/2022/sqlite-amalgamation-3390300.zip'
-}
-
-task downloadSqlite(type: Download) {
- src project.sqliteDistributionUrl
- dest 'src/main/jni/sqlite.zip'
- overwrite false
-}
-
-task installSqlite(dependsOn: downloadSqlite, type: Copy) {
- from zipTree(downloadSqlite.dest).matching {
- include '*/sqlite3.*'
- eachFile { it.setPath(it.getName()) }
- }
- into 'src/main/jni/sqlite'
-}
-
-preBuild.dependsOn installSqlite
-
-Properties properties = new Properties()
-File localProperties = project.rootProject.file('local.properties')
-if (localProperties.exists()) {
- properties.load(localProperties.newDataInputStream())
-}
-
-task sourceJar(type: Jar) {
- archiveClassifier.set('sources')
- from android.sourceSets.main.java.srcDirs
-}
-
-task javadoc(type: Javadoc) {
- source = android.sourceSets.main.java.srcDirs
- classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
- android.libraryVariants.all { variant ->
- if (variant.name == 'release') {
- owner.classpath += variant.javaCompileProvider.get().classpath
- }
- }
- exclude '**/R.html', '**/R.*.html', '**/index.html'
- if (JavaVersion.current().isJava9Compatible()) {
- options.addBooleanOption('html5', true)
- }
-
- failOnError false
-}
-
-task javadocJar(type: Jar, dependsOn: javadoc) {
- archiveClassifier.set('javadoc')
- from javadoc.destinationDir
-}
-
-preBuild.dependsOn installSqlite
-// https://issuetracker.google.com/issues/207403732
-tasks.whenTaskAdded { task ->
- if (task.name.startsWith("configureNdkBuildDebug")
- || task.name.startsWith("configureNdkBuildRelease")) {
- task.dependsOn preBuild
- }
-}
-
-publishing {
- publications {
- maven(MavenPublication) {
- groupId project.group
- artifactId project.name
- version project.version
- pom {
- name = project.name
- description = project.description
- url = 'https://github.com/requery/sqlite-android'
- licenses {
- license {
- name = 'The Apache Software License, Version 2.0'
- url = 'http://www.apache.org/licenses/LICENSE-2.0.txt'
- distribution = 'repo'
- }
- }
- scm {
- url = 'https://github.com/requery/sqlite-android.git'
- connection = 'scm:git:git://github.com/requery/sqlite-android.git'
- developerConnection = 'scm:git:git@github.com/requery/sqlite-android.git'
- }
- }
- afterEvaluate {
- artifact bundleReleaseAar
- artifact sourceJar
- artifact javadocJar
- }
- }
- }
-}
diff --git a/sqlite-android/proguard-rules.pro b/sqlite-android/proguard-rules.pro
deleted file mode 100644
index 02c5d5af92..0000000000
--- a/sqlite-android/proguard-rules.pro
+++ /dev/null
@@ -1,14 +0,0 @@
--keepclasseswithmembers class io.requery.android.database.** {
- native ;
- public (...);
-}
--keepnames class io.requery.android.database.** { *; }
--keep public class io.requery.android.database.sqlite.SQLiteFunction { *; }
--keep public class io.requery.android.database.sqlite.SQLiteCustomFunction { *; }
--keep public class io.requery.android.database.sqlite.SQLiteCursor { *; }
--keep public class io.requery.android.database.sqlite.SQLiteDebug** { *; }
--keep public class io.requery.android.database.sqlite.SQLiteDatabase { *; }
--keep public class io.requery.android.database.sqlite.SQLiteOpenHelper { *; }
--keep public class io.requery.android.database.sqlite.SQLiteStatement { *; }
--keep public class io.requery.android.database.CursorWindow { *; }
--keepattributes Exceptions,InnerClasses
diff --git a/sqlite-android/src/main/AndroidManifest.xml b/sqlite-android/src/main/AndroidManifest.xml
deleted file mode 100644
index 795975fcb4..0000000000
--- a/sqlite-android/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
diff --git a/sqlite-android/src/main/java/io/requery/android/database/AbstractCursor.java b/sqlite-android/src/main/java/io/requery/android/database/AbstractCursor.java
deleted file mode 100644
index 27a5e1c3c7..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/AbstractCursor.java
+++ /dev/null
@@ -1,421 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-
-package io.requery.android.database;
-
-import android.content.ContentResolver;
-import android.database.CharArrayBuffer;
-import android.database.ContentObservable;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.database.CursorIndexOutOfBoundsException;
-import android.database.DataSetObservable;
-import android.database.DataSetObserver;
-import android.net.Uri;
-import android.os.Build;
-import android.os.Bundle;
-import android.util.Log;
-
-import java.lang.ref.WeakReference;
-
-/**
- * This is an abstract cursor class that handles a lot of the common code
- * that all cursors need to deal with and is provided for convenience reasons.
- */
-public abstract class AbstractCursor implements Cursor {
-
- private static final String TAG = "Cursor";
-
- protected int mPos;
-
- protected boolean mClosed;
-
- //@Deprecated // deprecated in AOSP but still used for non-deprecated methods
- protected ContentResolver mContentResolver;
-
- private Uri mNotifyUri;
-
- private final Object mSelfObserverLock = new Object();
- private ContentObserver mSelfObserver;
- private boolean mSelfObserverRegistered;
- private final DataSetObservable mDataSetObservable = new DataSetObservable();
- private final ContentObservable mContentObservable = new ContentObservable();
-
- private Bundle mExtras = Bundle.EMPTY;
-
- @Override
- abstract public int getCount();
-
- @Override
- abstract public String[] getColumnNames();
-
- @Override
- abstract public String getString(int column);
- @Override
- abstract public short getShort(int column);
- @Override
- abstract public int getInt(int column);
- @Override
- abstract public long getLong(int column);
- @Override
- abstract public float getFloat(int column);
- @Override
- abstract public double getDouble(int column);
- @Override
- abstract public boolean isNull(int column);
-
- @Override
- public abstract int getType(int column);
-
- @Override
- public byte[] getBlob(int column) {
- throw new UnsupportedOperationException("getBlob is not supported");
- }
-
- @Override
- public int getColumnCount() {
- return getColumnNames().length;
- }
-
- @Override
- public void deactivate() {
- onDeactivateOrClose();
- }
-
- /** @hide */
- protected void onDeactivateOrClose() {
- if (mSelfObserver != null) {
- mContentResolver.unregisterContentObserver(mSelfObserver);
- mSelfObserverRegistered = false;
- }
- mDataSetObservable.notifyInvalidated();
- }
-
- @Override
- public boolean requery() {
- if (mSelfObserver != null && !mSelfObserverRegistered) {
- mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver);
- mSelfObserverRegistered = true;
- }
- mDataSetObservable.notifyChanged();
- return true;
- }
-
- @Override
- public boolean isClosed() {
- return mClosed;
- }
-
- @Override
- public void close() {
- mClosed = true;
- mContentObservable.unregisterAll();
- onDeactivateOrClose();
- }
-
- @Override
- public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) {
- // Default implementation, uses getString
- String result = getString(columnIndex);
- if (result != null) {
- char[] data = buffer.data;
- if (data == null || data.length < result.length()) {
- buffer.data = result.toCharArray();
- } else {
- result.getChars(0, result.length(), data, 0);
- }
- buffer.sizeCopied = result.length();
- } else {
- buffer.sizeCopied = 0;
- }
- }
-
- public AbstractCursor() {
- mPos = -1;
- }
-
- @Override
- public final int getPosition() {
- return mPos;
- }
-
- @Override
- public final boolean moveToPosition(int position) {
- // Make sure position isn't past the end of the cursor
- final int count = getCount();
- if (position >= count) {
- mPos = count;
- return false;
- }
-
- // Make sure position isn't before the beginning of the cursor
- if (position < 0) {
- mPos = -1;
- return false;
- }
-
- // Check for no-op moves, and skip the rest of the work for them
- if (position == mPos) {
- return true;
- }
-
- boolean result = onMove(mPos, position);
- if (!result) {
- mPos = -1;
- } else {
- mPos = position;
- }
-
- return result;
- }
-
- /**
- * This function is called every time the cursor is successfully scrolled
- * to a new position, giving the subclass a chance to update any state it
- * may have. If it returns false the move function will also do so and the
- * cursor will scroll to the beforeFirst position.
- *
- * This function should be called by methods such as {@link #moveToPosition(int)},
- * so it will typically not be called from outside of the cursor class itself.
- *
- *
- * @param oldPosition The position that we're moving from.
- * @param newPosition The position that we're moving to.
- * @return True if the move is successful, false otherwise.
- */
- public abstract boolean onMove(int oldPosition, int newPosition);
-
- @Override
- public final boolean move(int offset) {
- return moveToPosition(mPos + offset);
- }
-
- @Override
- public final boolean moveToFirst() {
- return moveToPosition(0);
- }
-
- @Override
- public final boolean moveToLast() {
- return moveToPosition(getCount() - 1);
- }
-
- @Override
- public final boolean moveToNext() {
- return moveToPosition(mPos + 1);
- }
-
- @Override
- public final boolean moveToPrevious() {
- return moveToPosition(mPos - 1);
- }
-
- @Override
- public final boolean isFirst() {
- return mPos == 0 && getCount() != 0;
- }
-
- @Override
- public final boolean isLast() {
- int cnt = getCount();
- return mPos == (cnt - 1) && cnt != 0;
- }
-
- @Override
- public final boolean isBeforeFirst() {
- return getCount() == 0 || mPos == -1;
- }
-
- @Override
- public final boolean isAfterLast() {
- return getCount() == 0 || mPos == getCount();
- }
-
- @Override
- public int getColumnIndex(String columnName) {
- // Hack according to bug 903852
- final int periodIndex = columnName.lastIndexOf('.');
- if (periodIndex != -1) {
- Exception e = new Exception();
- Log.e(TAG, "requesting column name with table name -- " + columnName, e);
- columnName = columnName.substring(periodIndex + 1);
- }
-
- String columnNames[] = getColumnNames();
- int length = columnNames.length;
- for (int i = 0; i < length; i++) {
- if (columnNames[i].equalsIgnoreCase(columnName)) {
- return i;
- }
- }
- return -1;
- }
-
- @Override
- public int getColumnIndexOrThrow(String columnName) {
- final int index = getColumnIndex(columnName);
- if (index < 0) {
- throw new IllegalArgumentException("column '" + columnName + "' does not exist");
- }
- return index;
- }
-
- @Override
- public String getColumnName(int columnIndex) {
- return getColumnNames()[columnIndex];
- }
-
- @Override
- public void registerContentObserver(ContentObserver observer) {
- mContentObservable.registerObserver(observer);
- }
-
- @Override
- public void unregisterContentObserver(ContentObserver observer) {
- // cursor will unregister all observers when it close
- if (!mClosed) {
- mContentObservable.unregisterObserver(observer);
- }
- }
-
- @Override
- public void registerDataSetObserver(DataSetObserver observer) {
- mDataSetObservable.registerObserver(observer);
- }
-
- @Override
- public void unregisterDataSetObserver(DataSetObserver observer) {
- mDataSetObservable.unregisterObserver(observer);
- }
-
- /**
- * Subclasses must call this method when they finish committing updates to notify all
- * observers.
- *
- * @param selfChange value
- */
- @SuppressWarnings("deprecation")
- protected void onChange(boolean selfChange) {
- synchronized (mSelfObserverLock) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- mContentObservable.dispatchChange(selfChange, null);
- } else {
- mContentObservable.dispatchChange(selfChange);
- }
- if (mNotifyUri != null && selfChange) {
- mContentResolver.notifyChange(mNotifyUri, mSelfObserver);
- }
- }
- }
-
- /**
- * Specifies a content URI to watch for changes.
- *
- * @param cr The content resolver from the caller's context.
- * @param notifyUri The URI to watch for changes. This can be a
- * specific row URI, or a base URI for a whole class of content.
- */
- @Override
- public void setNotificationUri(ContentResolver cr, Uri notifyUri) {
- synchronized (mSelfObserverLock) {
- mNotifyUri = notifyUri;
- mContentResolver = cr;
- if (mSelfObserver != null) {
- mContentResolver.unregisterContentObserver(mSelfObserver);
- }
- mSelfObserver = new SelfContentObserver(this);
- mContentResolver.registerContentObserver(mNotifyUri, true, mSelfObserver);
- mSelfObserverRegistered = true;
- }
- }
-
- @Override
- public Uri getNotificationUri() {
- synchronized (mSelfObserverLock) {
- return mNotifyUri;
- }
- }
-
- @Override
- public boolean getWantsAllOnMoveCalls() {
- return false;
- }
-
- @Override
- public void setExtras(Bundle extras) {
- mExtras = (extras == null) ? Bundle.EMPTY : extras;
- }
-
- @Override
- public Bundle getExtras() {
- return mExtras;
- }
-
- @Override
- public Bundle respond(Bundle extras) {
- return Bundle.EMPTY;
- }
-
- /**
- * This function throws CursorIndexOutOfBoundsException if the cursor position is out of bounds.
- * Subclass implementations of the get functions should call this before attempting to
- * retrieve data.
- *
- * @throws CursorIndexOutOfBoundsException
- */
- protected void checkPosition() {
- if (-1 == mPos || getCount() == mPos) {
- throw new CursorIndexOutOfBoundsException(mPos, getCount());
- }
- }
-
- @SuppressWarnings("FinalizeDoesntCallSuperFinalize")
- @Override
- protected void finalize() {
- if (mSelfObserver != null && mSelfObserverRegistered) {
- mContentResolver.unregisterContentObserver(mSelfObserver);
- }
- try {
- if (!mClosed) close();
- } catch(Exception ignored) { }
- }
-
- /**
- * Cursors use this class to track changes others make to their URI.
- */
- protected static class SelfContentObserver extends ContentObserver {
- WeakReference mCursor;
-
- public SelfContentObserver(AbstractCursor cursor) {
- super(null);
- mCursor = new WeakReference<>(cursor);
- }
-
- @Override
- public boolean deliverSelfNotifications() {
- return false;
- }
-
- @Override
- public void onChange(boolean selfChange) {
- AbstractCursor cursor = mCursor.get();
- if (cursor != null) {
- cursor.onChange(false);
- }
- }
- }
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/AbstractWindowedCursor.java b/sqlite-android/src/main/java/io/requery/android/database/AbstractWindowedCursor.java
deleted file mode 100644
index 1a850138c6..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/AbstractWindowedCursor.java
+++ /dev/null
@@ -1,177 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-
-package io.requery.android.database;
-
-import android.database.CharArrayBuffer;
-import android.database.Cursor;
-import android.database.StaleDataException;
-
-/**
- * A base class for Cursors that store their data in {@link android.database.CursorWindow}s.
- *
- * The cursor owns the cursor window it uses. When the cursor is closed,
- * its window is also closed. Likewise, when the window used by the cursor is
- * changed, its old window is closed. This policy of strict ownership ensures
- * that cursor windows are not leaked.
- *
- * Subclasses are responsible for filling the cursor window with data during
- * {@link #onMove(int, int)}, allocating a new cursor window if necessary.
- * During {@link #requery()}, the existing cursor window should be cleared and
- * filled with new data.
- *
- * If the contents of the cursor change or become invalid, the old window must be closed
- * (because it is owned by the cursor) and set to null.
- *
- */
-@SuppressWarnings("unused")
-public abstract class AbstractWindowedCursor extends AbstractCursor {
- /**
- * The cursor window owned by this cursor.
- */
- protected CursorWindow mWindow;
-
- @Override
- public byte[] getBlob(int columnIndex) {
- checkPosition();
- return mWindow.getBlob(mPos, columnIndex);
- }
-
- @Override
- public String getString(int columnIndex) {
- checkPosition();
- return mWindow.getString(mPos, columnIndex);
- }
-
- @Override
- public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) {
- mWindow.copyStringToBuffer(mPos, columnIndex, buffer);
- }
-
- @Override
- public short getShort(int columnIndex) {
- checkPosition();
- return mWindow.getShort(mPos, columnIndex);
- }
-
- @Override
- public int getInt(int columnIndex) {
- checkPosition();
- return mWindow.getInt(mPos, columnIndex);
- }
-
- @Override
- public long getLong(int columnIndex) {
- checkPosition();
- return mWindow.getLong(mPos, columnIndex);
- }
-
- @Override
- public float getFloat(int columnIndex) {
- checkPosition();
- return mWindow.getFloat(mPos, columnIndex);
- }
-
- @Override
- public double getDouble(int columnIndex) {
- checkPosition();
- return mWindow.getDouble(mPos, columnIndex);
- }
-
- @Override
- public boolean isNull(int columnIndex) {
- return mWindow.getType(mPos, columnIndex) == Cursor.FIELD_TYPE_NULL;
- }
-
- @Override
- public int getType(int columnIndex) {
- return mWindow.getType(mPos, columnIndex);
- }
-
- @Override
- protected void checkPosition() {
- super.checkPosition();
- if (mWindow == null) {
- throw new StaleDataException("Attempting to access a closed CursorWindow." +
- "Most probable cause: cursor is deactivated prior to calling this method.");
- }
- }
-
- public CursorWindow getWindow() {
- return mWindow;
- }
-
- /**
- * Sets a new cursor window for the cursor to use.
- *
- * The cursor takes ownership of the provided cursor window; the cursor window
- * will be closed when the cursor is closed or when the cursor adopts a new
- * cursor window.
- *
- * If the cursor previously had a cursor window, then it is closed when the
- * new cursor window is assigned.
- *
- *
- * @param window The new cursor window, typically a remote cursor window.
- */
- public void setWindow(CursorWindow window) {
- if (window != mWindow) {
- closeWindow();
- mWindow = window;
- }
- }
-
- /**
- * Returns true if the cursor has an associated cursor window.
- *
- * @return True if the cursor has an associated cursor window.
- */
- public boolean hasWindow() {
- return mWindow != null;
- }
-
- /**
- * Closes the cursor window and sets {@link #mWindow} to null.
- * @hide
- */
- protected void closeWindow() {
- if (mWindow != null) {
- mWindow.close();
- mWindow = null;
- }
- }
-
- /**
- * If there is a window, clear it. Otherwise, creates a new window.
- *
- * @param name The window name.
- * @hide
- */
- protected void clearOrCreateWindow(String name) {
- if (mWindow == null) {
- mWindow = new CursorWindow(name);
- } else {
- mWindow.clear();
- }
- }
-
- @Override
- protected void onDeactivateOrClose() {
- super.onDeactivateOrClose();
- closeWindow();
- }
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/CursorWindow.java b/sqlite-android/src/main/java/io/requery/android/database/CursorWindow.java
deleted file mode 100644
index b7af4238fc..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/CursorWindow.java
+++ /dev/null
@@ -1,507 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-
-package io.requery.android.database;
-
-import android.database.CharArrayBuffer;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteException;
-import io.requery.android.database.sqlite.SQLiteClosable;
-
-/**
- * A buffer containing multiple cursor rows.
- */
-@SuppressWarnings("unused")
-public class CursorWindow extends SQLiteClosable {
-
- private static final int WINDOW_SIZE_KB = 2048;
-
- /** The cursor window size. resource xml file specifies the value in kB.
- * convert it to bytes here by multiplying with 1024.
- */
- private static final int sDefaultCursorWindowSize =
- WINDOW_SIZE_KB * 1024;
- private final int mWindowSizeBytes;
-
- /**
- * The native CursorWindow object pointer. (FOR INTERNAL USE ONLY)
- */
- public long mWindowPtr;
-
- private int mStartPos;
- private final String mName;
-
- private static native long nativeCreate(String name, int cursorWindowSize);
- private static native void nativeDispose(long windowPtr);
-
- private static native void nativeClear(long windowPtr);
-
- private static native int nativeGetNumRows(long windowPtr);
- private static native boolean nativeSetNumColumns(long windowPtr, int columnNum);
- private static native boolean nativeAllocRow(long windowPtr);
- private static native void nativeFreeLastRow(long windowPtr);
-
- private static native int nativeGetType(long windowPtr, int row, int column);
- private static native byte[] nativeGetBlob(long windowPtr, int row, int column);
- private static native String nativeGetString(long windowPtr, int row, int column);
- private static native long nativeGetLong(long windowPtr, int row, int column);
- private static native double nativeGetDouble(long windowPtr, int row, int column);
-
- private static native boolean nativePutBlob(long windowPtr, byte[] value, int row, int column);
- private static native boolean nativePutString(long windowPtr, String value, int row, int column);
- private static native boolean nativePutLong(long windowPtr, long value, int row, int column);
- private static native boolean nativePutDouble(long windowPtr, double value, int row, int column);
- private static native boolean nativePutNull(long windowPtr, int row, int column);
-
- private static native String nativeGetName(long windowPtr);
-
- /**
- * Creates a new empty cursor with default cursor size (currently 2MB)
- */
- public CursorWindow(String name) {
- this(name, sDefaultCursorWindowSize);
- }
-
-
- /**
- * Creates a new empty cursor window and gives it a name.
- *
- * The cursor initially has no rows or columns. Call {@link #setNumColumns(int)} to
- * set the number of columns before adding any rows to the cursor.
- *
- *
- * @param name The name of the cursor window, or null if none.
- * @param windowSizeBytes Size of cursor window in bytes.
- *
- * Note: Memory is dynamically allocated as data rows are added to
- * the window. Depending on the amount of data stored, the actual
- * amount of memory allocated can be lower than specified size,
- * but cannot exceed it. Value is a non-negative number of bytes.
- */
- public CursorWindow(String name, int windowSizeBytes) {
- /* In
- https://developer.android.com/reference/android/database/CursorWindow#CursorWindow(java.lang.String,%20long)
- windowSizeBytes is long. However windowSizeBytes is
- eventually transformed into a size_t in cpp, and I can not
- guarantee that long->size_t would be possible. I thus keep
- int. This means that we can create cursor of size up to 4GiB
- while upstream can theoretically create cursor of size up to
- 16 EiB. It is probably an acceptable restriction.*/
- mStartPos = 0;
- mWindowSizeBytes = windowSizeBytes;
- mName = name != null && name.length() != 0 ? name : "";
- mWindowPtr = nativeCreate(mName, windowSizeBytes);
- if (mWindowPtr == 0) {
- throw new CursorWindowAllocationException("Cursor window allocation of " +
- (windowSizeBytes / 1024) + " kb failed. ");
- }
- }
-
- @SuppressWarnings("ThrowFromFinallyBlock")
- @Override
- protected void finalize() throws Throwable {
- try {
- dispose();
- } finally {
- super.finalize();
- }
- }
-
- private void dispose() {
- if (mWindowPtr != 0) {
- nativeDispose(mWindowPtr);
- mWindowPtr = 0;
- }
- }
-
- /**
- * Gets the name of this cursor window, never null.
- */
- public String getName() {
- return mName;
- }
-
- /**
- * Clears out the existing contents of the window, making it safe to reuse
- * for new data.
- *
- * The start position ({@link #getStartPosition()}), number of rows ({@link #getNumRows()}),
- * and number of columns in the cursor are all reset to zero.
- *
- */
- public void clear() {
- mStartPos = 0;
- nativeClear(mWindowPtr);
- }
-
- /**
- * Gets the start position of this cursor window.
- *
- * The start position is the zero-based index of the first row that this window contains
- * relative to the entire result set of the {@link Cursor}.
- *
- *
- * @return The zero-based start position.
- */
- public int getStartPosition() {
- return mStartPos;
- }
-
- /**
- * Sets the start position of this cursor window.
- *
- * The start position is the zero-based index of the first row that this window contains
- * relative to the entire result set of the {@link Cursor}.
- *
- *
- * @param pos The new zero-based start position.
- */
- public void setStartPosition(int pos) {
- mStartPos = pos;
- }
-
- /**
- * Gets the number of rows in this window.
- *
- * @return The number of rows in this cursor window.
- */
- public int getNumRows() {
- return nativeGetNumRows(mWindowPtr);
- }
-
- /**
- * Sets the number of columns in this window.
- *
- * This method must be called before any rows are added to the window, otherwise
- * it will fail to set the number of columns if it differs from the current number
- * of columns.
- *
- *
- * @param columnNum The new number of columns.
- * @return True if successful.
- */
- public boolean setNumColumns(int columnNum) {
- return nativeSetNumColumns(mWindowPtr, columnNum);
- }
-
- /**
- * Allocates a new row at the end of this cursor window.
- *
- * @return True if successful, false if the cursor window is out of memory.
- */
- public boolean allocRow(){
- return nativeAllocRow(mWindowPtr);
- }
-
- /**
- * Frees the last row in this cursor window.
- */
- public void freeLastRow(){
- nativeFreeLastRow(mWindowPtr);
- }
-
- /**
- * Returns the type of the field at the specified row and column index.
- *
- * The returned field types are:
- *
- * {@link Cursor#FIELD_TYPE_NULL}
- * {@link Cursor#FIELD_TYPE_INTEGER}
- * {@link Cursor#FIELD_TYPE_FLOAT}
- * {@link Cursor#FIELD_TYPE_STRING}
- * {@link Cursor#FIELD_TYPE_BLOB}
- *
- *
- *
- * @param row The zero-based row index.
- * @param column The zero-based column index.
- * @return The field type.
- */
- public int getType(int row, int column) {
- return nativeGetType(mWindowPtr, row - mStartPos, column);
- }
-
- /**
- * Gets the value of the field at the specified row and column index as a byte array.
- *
- * The result is determined as follows:
- *
- * If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the result
- * is null.
- * If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then the result
- * is the blob value.
- * If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the result
- * is the array of bytes that make up the internal representation of the
- * string value.
- * If the field is of type {@link Cursor#FIELD_TYPE_INTEGER} or
- * {@link Cursor#FIELD_TYPE_FLOAT}, then a {@link SQLiteException} is thrown.
- *
- *
- *
- * @param row The zero-based row index.
- * @param column The zero-based column index.
- * @return The value of the field as a byte array.
- */
- public byte[] getBlob(int row, int column) {
- return nativeGetBlob(mWindowPtr, row - mStartPos, column);
- }
-
- /**
- * Gets the value of the field at the specified row and column index as a string.
- *
- * The result is determined as follows:
- *
- * If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the result
- * is null.
- * If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the result
- * is the string value.
- * If the field is of type {@link Cursor#FIELD_TYPE_INTEGER}, then the result
- * is a string representation of the integer in decimal, obtained by formatting the
- * value with the printf family of functions using
- * format specifier %lld.
- * If the field is of type {@link Cursor#FIELD_TYPE_FLOAT}, then the result
- * is a string representation of the floating-point value in decimal, obtained by
- * formatting the value with the printf family of functions using
- * format specifier %g.
- * If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then a
- * {@link SQLiteException} is thrown.
- *
- *
- *
- * @param row The zero-based row index.
- * @param column The zero-based column index.
- * @return The value of the field as a string.
- */
- public String getString(int row, int column) {
- return nativeGetString(mWindowPtr, row - mStartPos, column);
- }
-
- /**
- * Copies the text of the field at the specified row and column index into
- * a {@link CharArrayBuffer}.
- *
- * The buffer is populated as follows:
- *
- * If the buffer is too small for the value to be copied, then it is
- * automatically resized.
- * If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the buffer
- * is set to an empty string.
- * If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the buffer
- * is set to the contents of the string.
- * If the field is of type {@link Cursor#FIELD_TYPE_INTEGER}, then the buffer
- * is set to a string representation of the integer in decimal, obtained by formatting the
- * value with the printf family of functions using
- * format specifier %lld.
- * If the field is of type {@link Cursor#FIELD_TYPE_FLOAT}, then the buffer is
- * set to a string representation of the floating-point value in decimal, obtained by
- * formatting the value with the printf family of functions using
- * format specifier %g.
- * If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then a
- * {@link SQLiteException} is thrown.
- *
- *
- *
- * @param row The zero-based row index.
- * @param column The zero-based column index.
- * @param buffer The {@link CharArrayBuffer} to hold the string. It is automatically
- * resized if the requested string is larger than the buffer's current capacity.
- */
- public void copyStringToBuffer(int row, int column, CharArrayBuffer buffer) {
- if (buffer == null) {
- throw new IllegalArgumentException("CharArrayBuffer should not be null");
- }
- // TODO not as optimal as the original code
- char[] chars = getString(row, column).toCharArray();
- buffer.data = chars;
- buffer.sizeCopied = chars.length;
- }
-
- /**
- * Gets the value of the field at the specified row and column index as a long.
- *
- * The result is determined as follows:
- *
- * If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the result
- * is 0L.
- * If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the result
- * is the value obtained by parsing the string value with strtoll.
- * If the field is of type {@link Cursor#FIELD_TYPE_INTEGER}, then the result
- * is the long value.
- * If the field is of type {@link Cursor#FIELD_TYPE_FLOAT}, then the result
- * is the floating-point value converted to a long.
- * If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then a
- * {@link SQLiteException} is thrown.
- *
- *
- *
- * @param row The zero-based row index.
- * @param column The zero-based column index.
- * @return The value of the field as a long.
- */
- public long getLong(int row, int column) {
- return nativeGetLong(mWindowPtr, row - mStartPos, column);
- }
-
- /**
- * Gets the value of the field at the specified row and column index as a
- * double.
- *
- * The result is determined as follows:
- *
- * If the field is of type {@link Cursor#FIELD_TYPE_NULL}, then the result
- * is 0.0.
- * If the field is of type {@link Cursor#FIELD_TYPE_STRING}, then the result
- * is the value obtained by parsing the string value with strtod.
- * If the field is of type {@link Cursor#FIELD_TYPE_INTEGER}, then the result
- * is the integer value converted to a double.
- * If the field is of type {@link Cursor#FIELD_TYPE_FLOAT}, then the result
- * is the double value.
- * If the field is of type {@link Cursor#FIELD_TYPE_BLOB}, then a
- * {@link SQLiteException} is thrown.
- *
- *
- *
- * @param row The zero-based row index.
- * @param column The zero-based column index.
- * @return The value of the field as a double.
- */
- public double getDouble(int row, int column) {
- return nativeGetDouble(mWindowPtr, row - mStartPos, column);
- }
-
- /**
- * Gets the value of the field at the specified row and column index as a
- * short.
- *
- * The result is determined by invoking {@link #getLong} and converting the
- * result to short.
- *
- *
- * @param row The zero-based row index.
- * @param column The zero-based column index.
- * @return The value of the field as a short.
- */
- public short getShort(int row, int column) {
- return (short) getLong(row, column);
- }
-
- /**
- * Gets the value of the field at the specified row and column index as an
- * int.
- *
- * The result is determined by invoking {@link #getLong} and converting the
- * result to int.
- *
- *
- * @param row The zero-based row index.
- * @param column The zero-based column index.
- * @return The value of the field as an int.
- */
- public int getInt(int row, int column) {
- return (int) getLong(row, column);
- }
-
- /**
- * Gets the value of the field at the specified row and column index as a
- * float.
- *
- * The result is determined by invoking {@link #getDouble} and converting the
- * result to float.
- *
- *
- * @param row The zero-based row index.
- * @param column The zero-based column index.
- * @return The value of the field as an float.
- */
- public float getFloat(int row, int column) {
- return (float) getDouble(row, column);
- }
-
- /**
- * Copies a byte array into the field at the specified row and column index.
- *
- * @param value The value to store.
- * @param row The zero-based row index.
- * @param column The zero-based column index.
- * @return True if successful.
- */
- public boolean putBlob(byte[] value, int row, int column) {
- return nativePutBlob(mWindowPtr, value, row - mStartPos, column);
- }
-
- /**
- * Copies a string into the field at the specified row and column index.
- *
- * @param value The value to store.
- * @param row The zero-based row index.
- * @param column The zero-based column index.
- * @return True if successful.
- */
- public boolean putString(String value, int row, int column) {
- return nativePutString(mWindowPtr, value, row - mStartPos, column);
- }
-
- /**
- * Puts a long integer into the field at the specified row and column index.
- *
- * @param value The value to store.
- * @param row The zero-based row index.
- * @param column The zero-based column index.
- * @return True if successful.
- */
- public boolean putLong(long value, int row, int column) {
- return nativePutLong(mWindowPtr, value, row - mStartPos, column);
- }
-
- /**
- * Puts a double-precision floating point value into the field at the
- * specified row and column index.
- *
- * @param value The value to store.
- * @param row The zero-based row index.
- * @param column The zero-based column index.
- * @return True if successful.
- */
- public boolean putDouble(double value, int row, int column) {
- return nativePutDouble(mWindowPtr, value, row - mStartPos, column);
- }
-
- /**
- * Puts a null value into the field at the specified row and column index.
- *
- * @param row The zero-based row index.
- * @param column The zero-based column index.
- * @return True if successful.
- */
- public boolean putNull(int row, int column) {
- return nativePutNull(mWindowPtr, row - mStartPos, column);
- }
-
- @Override
- protected void onAllReferencesReleased() {
- dispose();
- }
-
- @Override
- public String toString() {
- return getName() + " {" + Long.toHexString(mWindowPtr) + "}";
- }
-
- public int getWindowSizeBytes() {
- return mWindowSizeBytes;
- }
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/CursorWindowAllocationException.java b/sqlite-android/src/main/java/io/requery/android/database/CursorWindowAllocationException.java
deleted file mode 100644
index 3ac59bac02..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/CursorWindowAllocationException.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package io.requery.android.database;
-
-/**
- * This exception is thrown when a CursorWindow couldn't be allocated,
- * most probably due to memory not being available.
- *
- * @hide
- */
-public class CursorWindowAllocationException extends RuntimeException {
- public CursorWindowAllocationException(String description) {
- super(description);
- }
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/DatabaseErrorHandler.java b/sqlite-android/src/main/java/io/requery/android/database/DatabaseErrorHandler.java
deleted file mode 100644
index e27fd9badd..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/DatabaseErrorHandler.java
+++ /dev/null
@@ -1,33 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-
-package io.requery.android.database;
-
-import io.requery.android.database.sqlite.SQLiteDatabase;
-
-/**
- * An interface to let apps define an action to take when database corruption is detected.
- */
-public interface DatabaseErrorHandler {
-
- /**
- * The method invoked when database corruption is detected.
- * @param dbObj the {@link SQLiteDatabase} object representing the database on which corruption
- * is detected.
- */
- void onCorruption(SQLiteDatabase dbObj);
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/DefaultDatabaseErrorHandler.java b/sqlite-android/src/main/java/io/requery/android/database/DefaultDatabaseErrorHandler.java
deleted file mode 100755
index 5225d15ff2..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/DefaultDatabaseErrorHandler.java
+++ /dev/null
@@ -1,106 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-
-package io.requery.android.database;
-
-import android.database.sqlite.SQLiteException;
-import android.util.Log;
-import android.util.Pair;
-import io.requery.android.database.sqlite.SQLiteDatabase;
-
-import java.io.File;
-import java.util.List;
-
-/**
- * Default class used to define the actions to take when the database corruption is reported
- * by sqlite.
- *
- * An application can specify an implementation of {@link DatabaseErrorHandler} on the
- * following:
- *
- * {@link SQLiteDatabase#openOrCreateDatabase(String,
- * SQLiteDatabase.CursorFactory, DatabaseErrorHandler)}
- * {@link SQLiteDatabase#openDatabase(String,
- * SQLiteDatabase.CursorFactory, int, DatabaseErrorHandler)}
- *
- * The specified {@link DatabaseErrorHandler} is used to handle database corruption errors, if they
- * occur.
- *
- * If null is specified for DatabaeErrorHandler param in the above calls, then this class is used
- * as the default {@link DatabaseErrorHandler}.
- */
-public final class DefaultDatabaseErrorHandler implements DatabaseErrorHandler {
-
- private static final String TAG = "DefaultDatabaseError";
-
- @Override
- public void onCorruption(SQLiteDatabase dbObj) {
- Log.e(TAG, "Corruption reported by sqlite on database: " + dbObj.getPath());
-
- // is the corruption detected even before database could be 'opened'?
- if (!dbObj.isOpen()) {
- // database files are not even openable. delete this database file.
- // NOTE if the database has attached databases, then any of them could be corrupt.
- // and not deleting all of them could cause corrupted database file to remain and
- // make the application crash on database open operation. To avoid this problem,
- // the application should provide its own {@link DatabaseErrorHandler} impl class
- // to delete ALL files of the database (including the attached databases).
- deleteDatabaseFile(dbObj.getPath());
- return;
- }
-
- List> attachedDbs = null;
- try {
- // Close the database, which will cause subsequent operations to fail.
- // before that, get the attached database list first.
- try {
- attachedDbs = dbObj.getAttachedDbs();
- } catch (SQLiteException e) {
- /* ignore */
- }
- try {
- dbObj.close();
- } catch (SQLiteException e) {
- /* ignore */
- }
- } finally {
- // Delete all files of this corrupt database and/or attached databases
- if (attachedDbs != null) {
- for (Pair p : attachedDbs) {
- deleteDatabaseFile(p.second);
- }
- } else {
- // attachedDbs = null is possible when the database is so corrupt that even
- // "PRAGMA database_list;" also fails. delete the main database file
- deleteDatabaseFile(dbObj.getPath());
- }
- }
- }
-
- private void deleteDatabaseFile(String fileName) {
- if (fileName.equalsIgnoreCase(":memory:") || fileName.trim().length() == 0) {
- return;
- }
- Log.e(TAG, "deleting the database file: " + fileName);
- try {
- SQLiteDatabase.deleteDatabase(new File(fileName));
- } catch (Exception e) {
- /* print warning and ignore exception */
- Log.w(TAG, "delete failed: " + e.getMessage());
- }
- }
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/sqlite/CloseGuard.java b/sqlite-android/src/main/java/io/requery/android/database/sqlite/CloseGuard.java
deleted file mode 100644
index 790ca594da..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/sqlite/CloseGuard.java
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-
-package io.requery.android.database.sqlite;
-
-import android.util.Log;
-
-/**
- * CloseGuard is a mechanism for flagging implicit finalizer cleanup of
- * resources that should have been cleaned up by explicit close
- * methods (aka "explicit termination methods" in Effective Java).
- *
- * A simple example:
{@code
- * class Foo {
- *
- * private final CloseGuard guard = CloseGuard.get();
- *
- * ...
- *
- * public Foo() {
- * ...;
- * guard.open("cleanup");
- * }
- *
- * public void cleanup() {
- * guard.close();
- * ...;
- * }
- *
- * protected void finalize() throws Throwable {
- * try {
- * if (guard != null) {
- * guard.warnIfOpen();
- * }
- * cleanup();
- * } finally {
- * super.finalize();
- * }
- * }
- * }
- * }
- *
- * In usage where the resource to be explicitly cleaned up are
- * allocated after object construction, CloseGuard protection can
- * be deferred. For example: {@code
- * class Bar {
- *
- * private final CloseGuard guard = CloseGuard.get();
- *
- * ...
- *
- * public Bar() {
- * ...;
- * }
- *
- * public void connect() {
- * ...;
- * guard.open("cleanup");
- * }
- *
- * public void cleanup() {
- * guard.close();
- * ...;
- * }
- *
- * protected void finalize() throws Throwable {
- * try {
- * if (guard != null) {
- * guard.warnIfOpen();
- * }
- * cleanup();
- * } finally {
- * super.finalize();
- * }
- * }
- * }
- * }
- *
- * When used in a constructor calls to {@code open} should occur at
- * the end of the constructor since an exception that would cause
- * abrupt termination of the constructor will mean that the user will
- * not have a reference to the object to cleanup explicitly. When used
- * in a method, the call to {@code open} should occur just after
- * resource acquisition.
- *
- *
- *
- * Note that the null check on {@code guard} in the finalizer is to
- * cover cases where a constructor throws an exception causing the
- * {@code guard} to be uninitialized.
- *
- * @hide
- */
-@SuppressWarnings("unused")
-public final class CloseGuard {
-
- /**
- * Instance used when CloseGuard is disabled to avoid allocation.
- */
- private static final CloseGuard NOOP = new CloseGuard();
-
- /**
- * Enabled by default so we can catch issues early in VM startup.
- * Note, however, that Android disables this early in its startup,
- * but enables it with DropBoxing for system apps on debug builds.
- */
- private static volatile boolean ENABLED = true;
-
- /**
- * Hook for customizing how CloseGuard issues are reported.
- */
- private static volatile Reporter REPORTER = new DefaultReporter();
-
- /**
- * Returns a CloseGuard instance. If CloseGuard is enabled, {@code
- * #open(String)} can be used to set up the instance to warn on
- * failure to close. If CloseGuard is disabled, a non-null no-op
- * instance is returned.
- */
- public static CloseGuard get() {
- if (!ENABLED) {
- return NOOP;
- }
- return new CloseGuard();
- }
-
- /**
- * Used to enable or disable CloseGuard. Note that CloseGuard only
- * warns if it is enabled for both allocation and finalization.
- */
- public static void setEnabled(boolean enabled) {
- ENABLED = enabled;
- }
-
- /**
- * Used to replace default Reporter used to warn of CloseGuard
- * violations. Must be non-null.
- */
- public static void setReporter(Reporter reporter) {
- if (reporter == null) {
- throw new NullPointerException("reporter == null");
- }
- REPORTER = reporter;
- }
-
- /**
- * Returns non-null CloseGuard.Reporter.
- */
- public static Reporter getReporter() {
- return REPORTER;
- }
-
- private CloseGuard() {}
-
- /**
- * If CloseGuard is enabled, {@code open} initializes the instance
- * with a warning that the caller should have explicitly called the
- * {@code closer} method instead of relying on finalization.
- *
- * @param closer non-null name of explicit termination method
- * @throws NullPointerException if closer is null, regardless of
- * whether or not CloseGuard is enabled
- */
- public void open(String closer) {
- // always perform the check for valid API usage...
- if (closer == null) {
- throw new NullPointerException("closer == null");
- }
- // ...but avoid allocating an allocationSite if disabled
- if (this == NOOP || !ENABLED) {
- return;
- }
- String message = "Explicit termination method '" + closer + "' not called";
- allocationSite = new Throwable(message);
- }
-
- private Throwable allocationSite;
-
- /**
- * Marks this CloseGuard instance as closed to avoid warnings on
- * finalization.
- */
- public void close() {
- allocationSite = null;
- }
-
- /**
- * If CloseGuard is enabled, logs a warning if the caller did not
- * properly cleanup by calling an explicit close method
- * before finalization. If CloseGuard is disabled, no action is
- * performed.
- */
- public void warnIfOpen() {
- if (allocationSite == null || !ENABLED) {
- return;
- }
-
- String message =
- ("A resource was acquired at attached stack trace but never released. "
- + "See java.io.Closeable for information on avoiding resource leaks.");
-
- REPORTER.report(message, allocationSite);
- }
-
- /**
- * Interface to allow customization of reporting behavior.
- */
- public interface Reporter {
- void report(String message, Throwable allocationSite);
- }
-
- /**
- * Default Reporter which reports CloseGuard violations to the log.
- */
- private static final class DefaultReporter implements Reporter {
- @Override public void report (String message, Throwable allocationSite) {
- Log.w("SQLite", message, allocationSite);
- }
- }
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/sqlite/RequerySQLiteOpenHelperFactory.java b/sqlite-android/src/main/java/io/requery/android/database/sqlite/RequerySQLiteOpenHelperFactory.java
deleted file mode 100644
index 408a5da5ce..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/sqlite/RequerySQLiteOpenHelperFactory.java
+++ /dev/null
@@ -1,95 +0,0 @@
-package io.requery.android.database.sqlite;
-
-import android.content.Context;
-import androidx.sqlite.db.SupportSQLiteOpenHelper;
-import io.requery.android.database.DatabaseErrorHandler;
-
-import java.util.Collections;
-
-/**
- * Implements {@link SupportSQLiteOpenHelper.Factory} using the SQLite implementation shipped in
- * this library.
- */
-@SuppressWarnings("unused")
-public final class RequerySQLiteOpenHelperFactory implements SupportSQLiteOpenHelper.Factory {
- private final Iterable configurationOptions;
-
- @SuppressWarnings("WeakerAccess")
- public RequerySQLiteOpenHelperFactory(Iterable configurationOptions) {
- this.configurationOptions = configurationOptions;
- }
-
- public RequerySQLiteOpenHelperFactory() {
- this(Collections.emptyList());
- }
-
- @Override
- public SupportSQLiteOpenHelper create(SupportSQLiteOpenHelper.Configuration config) {
- return new CallbackSQLiteOpenHelper(config.context, config.name, config.callback, configurationOptions);
- }
-
- private static final class CallbackSQLiteOpenHelper extends SQLiteOpenHelper {
-
- private final SupportSQLiteOpenHelper.Callback callback;
- private final Iterable configurationOptions;
-
- CallbackSQLiteOpenHelper(Context context, String name, SupportSQLiteOpenHelper.Callback cb, Iterable ops) {
- super(context, name, null, cb.version, new CallbackDatabaseErrorHandler(cb));
- this.callback = cb;
- this.configurationOptions = ops;
- }
-
- @Override
- public void onConfigure(SQLiteDatabase db) {
- callback.onConfigure(db);
- }
-
- @Override
- public void onCreate(SQLiteDatabase db) {
- callback.onCreate(db);
- }
-
- @Override
- public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- callback.onUpgrade(db, oldVersion, newVersion);
- }
-
- @Override
- public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- callback.onDowngrade(db, oldVersion, newVersion);
- }
-
- @Override
- public void onOpen(SQLiteDatabase db) {
- callback.onOpen(db);
- }
-
- @Override protected SQLiteDatabaseConfiguration createConfiguration(String path, int openFlags) {
- SQLiteDatabaseConfiguration config = super.createConfiguration(path, openFlags);
-
- for (ConfigurationOptions option : configurationOptions) {
- config = option.apply(config);
- }
-
- return config;
- }
- }
-
- private static final class CallbackDatabaseErrorHandler implements DatabaseErrorHandler {
-
- private final SupportSQLiteOpenHelper.Callback callback;
-
- CallbackDatabaseErrorHandler(SupportSQLiteOpenHelper.Callback callback) {
- this.callback = callback;
- }
-
- @Override
- public void onCorruption(SQLiteDatabase db) {
- callback.onCorruption(db);
- }
- }
-
- public interface ConfigurationOptions {
- SQLiteDatabaseConfiguration apply(SQLiteDatabaseConfiguration configuration);
- }
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteClosable.java b/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteClosable.java
deleted file mode 100644
index 0edc17d4e0..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteClosable.java
+++ /dev/null
@@ -1,80 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-
-package io.requery.android.database.sqlite;
-
-import java.io.Closeable;
-
-/**
- * An object created from a SQLiteDatabase that can be closed.
- *
- * This class implements a primitive reference counting scheme for database objects.
- */
-public abstract class SQLiteClosable implements Closeable {
- private int mReferenceCount = 1;
-
- /**
- * Called when the last reference to the object was released by
- * a call to {@link #releaseReference()} or {@link #close()}.
- */
- protected abstract void onAllReferencesReleased();
-
- /**
- * Acquires a reference to the object.
- *
- * @throws IllegalStateException if the last reference to the object has already
- * been released.
- */
- public void acquireReference() {
- synchronized(this) {
- if (mReferenceCount <= 0) {
- throw new IllegalStateException(
- "attempt to re-open an already-closed object: " + this);
- }
- mReferenceCount++;
- }
- }
-
- /**
- * Releases a reference to the object, closing the object if the last reference
- * was released.
- *
- * @see #onAllReferencesReleased()
- */
- public void releaseReference() {
- boolean refCountIsZero;
- synchronized(this) {
- refCountIsZero = --mReferenceCount == 0;
- }
- if (refCountIsZero) {
- onAllReferencesReleased();
- }
- }
-
- /**
- * Releases a reference to the object, closing the object if the last reference
- * was released.
- *
- * Calling this method is equivalent to calling {@link #releaseReference}.
- *
- * @see #releaseReference()
- * @see #onAllReferencesReleased()
- */
- public void close() {
- releaseReference();
- }
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteConnection.java b/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteConnection.java
deleted file mode 100644
index c408b86522..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteConnection.java
+++ /dev/null
@@ -1,1585 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-/*
-** Modified to support SQLite extensions by the SQLite developers:
-** sqlite-dev@sqlite.org.
-*/
-
-package io.requery.android.database.sqlite;
-
-import android.annotation.SuppressLint;
-import android.annotation.TargetApi;
-import android.database.Cursor;
-import android.database.sqlite.SQLiteBindOrColumnIndexOutOfRangeException;
-import android.database.sqlite.SQLiteDatabaseLockedException;
-import android.database.sqlite.SQLiteException;
-import android.os.Build;
-import android.os.Looper;
-import android.os.ParcelFileDescriptor;
-import android.util.Log;
-import android.util.Printer;
-import androidx.collection.LruCache;
-import androidx.core.os.CancellationSignal;
-import androidx.core.os.OperationCanceledException;
-import io.requery.android.database.CursorWindow;
-
-import java.text.SimpleDateFormat;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.Map;
-import java.util.regex.Pattern;
-
-/**
- * Represents a SQLite database connection.
- * Each connection wraps an instance of a native sqlite3 object.
- *
- * When database connection pooling is enabled, there can be multiple active
- * connections to the same database. Otherwise there is typically only one
- * connection per database.
- *
- * When the SQLite WAL feature is enabled, multiple readers and one writer
- * can concurrently access the database. Without WAL, readers and writers
- * are mutually exclusive.
- *
- *
- * Ownership and concurrency guarantees
- *
- * Connection objects are not thread-safe. They are acquired as needed to
- * perform a database operation and are then returned to the pool. At any
- * given time, a connection is either owned and used by a {@link SQLiteSession}
- * object or the {@link SQLiteConnectionPool}. Those classes are
- * responsible for serializing operations to guard against concurrent
- * use of a connection.
- *
- * The guarantee of having a single owner allows this class to be implemented
- * without locks and greatly simplifies resource management.
- *
- *
- * Encapsulation guarantees
- *
- * The connection object object owns *all* of the SQLite related native
- * objects that are associated with the connection. What's more, there are
- * no other objects in the system that are capable of obtaining handles to
- * those native objects. Consequently, when the connection is closed, we do
- * not have to worry about what other components might have references to
- * its associated SQLite state -- there are none.
- *
- * Encapsulation is what ensures that the connection object's
- * lifecycle does not become a tortured mess of finalizers and reference
- * queues.
- *
- *
- * Reentrance
- *
- * This class must tolerate reentrant execution of SQLite operations because
- * triggers may call custom SQLite functions that perform additional queries.
- *
- *
- * @hide
- */
-@SuppressWarnings("TryFinallyCanBeTryWithResources")
-public final class SQLiteConnection implements CancellationSignal.OnCancelListener {
- private static final String TAG = "SQLiteConnection";
- private static final boolean DEBUG = false;
-
- private static final String[] EMPTY_STRING_ARRAY = new String[0];
- private static final byte[] EMPTY_BYTE_ARRAY = new byte[0];
-
- private static final Pattern TRIM_SQL_PATTERN = Pattern.compile("[\\s]*\\n+[\\s]*");
-
- private final CloseGuard mCloseGuard = CloseGuard.get();
-
- private final SQLiteConnectionPool mPool;
- private final SQLiteDatabaseConfiguration mConfiguration;
- private final int mConnectionId;
- private final boolean mIsPrimaryConnection;
- private final boolean mIsReadOnlyConnection;
- private final PreparedStatementCache mPreparedStatementCache;
- private PreparedStatement mPreparedStatementPool;
-
- // The recent operations log.
- private final OperationLog mRecentOperations = new OperationLog();
-
- // The native SQLiteConnection pointer. (FOR INTERNAL USE ONLY)
- private long mConnectionPtr;
-
- private boolean mOnlyAllowReadOnlyOperations;
-
- // The number of times attachCancellationSignal has been called.
- // Because SQLite statement execution can be reentrant, we keep track of how many
- // times we have attempted to attach a cancellation signal to the connection so that
- // we can ensure that we detach the signal at the right time.
- private int mCancellationSignalAttachCount;
-
- private static native long nativeOpen(String path, int openFlags, String label,
- boolean enableTrace, boolean enableProfile);
- private static native void nativeClose(long connectionPtr);
- private static native void nativeRegisterCustomFunction(long connectionPtr,
- SQLiteCustomFunction function);
- private static native void nativeRegisterFunction(long connectionPtr,
- SQLiteFunction function);
- private static native void nativeRegisterLocalizedCollators(long connectionPtr, String locale);
- private static native long nativePrepareStatement(long connectionPtr, String sql);
- private static native void nativeFinalizeStatement(long connectionPtr, long statementPtr);
- private static native int nativeGetParameterCount(long connectionPtr, long statementPtr);
- private static native boolean nativeIsReadOnly(long connectionPtr, long statementPtr);
- private static native int nativeGetColumnCount(long connectionPtr, long statementPtr);
- private static native String nativeGetColumnName(long connectionPtr, long statementPtr,
- int index);
- private static native void nativeBindNull(long connectionPtr, long statementPtr,
- int index);
- private static native void nativeBindLong(long connectionPtr, long statementPtr,
- int index, long value);
- private static native void nativeBindDouble(long connectionPtr, long statementPtr,
- int index, double value);
- private static native void nativeBindString(long connectionPtr, long statementPtr,
- int index, String value);
- private static native void nativeBindBlob(long connectionPtr, long statementPtr,
- int index, byte[] value);
- private static native void nativeResetStatementAndClearBindings(
- long connectionPtr, long statementPtr);
- private static native void nativeExecute(long connectionPtr, long statementPtr);
- private static native long nativeExecuteForLong(long connectionPtr, long statementPtr);
- private static native String nativeExecuteForString(long connectionPtr, long statementPtr);
- private static native int nativeExecuteForBlobFileDescriptor(
- long connectionPtr, long statementPtr);
- private static native int nativeExecuteForChangedRowCount(long connectionPtr, long statementPtr);
- private static native long nativeExecuteForLastInsertedRowId(
- long connectionPtr, long statementPtr);
- private static native long nativeExecuteForCursorWindow(
- long connectionPtr, long statementPtr, long winPtr,
- int startPos, int requiredPos, boolean countAllRows);
- private static native int nativeGetDbLookaside(long connectionPtr);
- private static native void nativeCancel(long connectionPtr);
- private static native void nativeResetCancel(long connectionPtr, boolean cancelable);
-
- private static native boolean nativeHasCodec();
- private static native void nativeLoadExtension(long connectionPtr, String file, String proc);
-
- public static boolean hasCodec(){ return nativeHasCodec(); }
-
- private SQLiteConnection(SQLiteConnectionPool pool,
- SQLiteDatabaseConfiguration configuration,
- int connectionId, boolean primaryConnection) {
- mPool = pool;
- mConfiguration = new SQLiteDatabaseConfiguration(configuration);
- mConnectionId = connectionId;
- mIsPrimaryConnection = primaryConnection;
- mIsReadOnlyConnection = (configuration.openFlags & SQLiteDatabase.OPEN_READONLY) != 0;
- mPreparedStatementCache = new PreparedStatementCache(
- mConfiguration.maxSqlCacheSize);
- mCloseGuard.open("close");
- }
-
- @SuppressWarnings("ThrowFromFinallyBlock")
- @Override
- protected void finalize() throws Throwable {
- try {
- if (mPool != null && mConnectionPtr != 0) {
- mPool.onConnectionLeaked();
- }
-
- dispose(true);
- } finally {
- super.finalize();
- }
- }
-
- // Called by SQLiteConnectionPool only.
- static SQLiteConnection open(SQLiteConnectionPool pool,
- SQLiteDatabaseConfiguration configuration,
- int connectionId, boolean primaryConnection) {
- SQLiteConnection connection = new SQLiteConnection(pool, configuration,
- connectionId, primaryConnection);
- try {
- connection.open();
- return connection;
- } catch (SQLiteException ex) {
- connection.dispose(false);
- throw ex;
- }
- }
-
- // Called by SQLiteConnectionPool only.
- // Closes the database closes and releases all of its associated resources.
- // Do not call methods on the connection after it is closed. It will probably crash.
- void close() {
- dispose(false);
- }
-
- private void open() {
- mConnectionPtr = nativeOpen(mConfiguration.path,
- // remove the wal flag as its a custom flag not supported by sqlite3_open_v2
- mConfiguration.openFlags & ~SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING,
- mConfiguration.label,
- SQLiteDebug.DEBUG_SQL_STATEMENTS, SQLiteDebug.DEBUG_SQL_TIME);
-
- setPageSize();
- setForeignKeyModeFromConfiguration();
- setJournalSizeLimit();
- setAutoCheckpointInterval();
- if (!nativeHasCodec()) {
- setWalModeFromConfiguration();
- setLocaleFromConfiguration();
- }
-
- // Register (deprecated) custom functions.
- final int customFunctionCount = mConfiguration.customFunctions.size();
- for (int i = 0; i < customFunctionCount; i++) {
- SQLiteCustomFunction function = mConfiguration.customFunctions.get(i);
- nativeRegisterCustomFunction(mConnectionPtr, function);
- }
-
- // Register functions
- final int functionCount = mConfiguration.functions.size();
- for (int i = 0; i < functionCount; i++) {
- SQLiteFunction function = mConfiguration.functions.get(i);
- nativeRegisterFunction(mConnectionPtr, function);
- }
-
- // Register custom extensions
- for (SQLiteCustomExtension extension : mConfiguration.customExtensions) {
- nativeLoadExtension(mConnectionPtr, extension.path, extension.entryPoint);
- }
- }
-
- private void dispose(boolean finalized) {
- if (mCloseGuard != null) {
- if (finalized) {
- mCloseGuard.warnIfOpen();
- }
- mCloseGuard.close();
- }
-
- if (mConnectionPtr != 0) {
- final int cookie = mRecentOperations.beginOperation("close", null, null);
- try {
- mPreparedStatementCache.evictAll();
- nativeClose(mConnectionPtr);
- mConnectionPtr = 0;
- } finally {
- mRecentOperations.endOperation(cookie);
- }
- }
- }
-
- private void setPageSize() {
- if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
- final long newValue = SQLiteGlobal.getDefaultPageSize();
- long value = executeForLong("PRAGMA page_size", null, null);
- if (value != newValue) {
- execute("PRAGMA page_size=" + newValue, null, null);
- }
- }
- }
-
- private void setAutoCheckpointInterval() {
- if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
- final long newValue = SQLiteGlobal.getWALAutoCheckpoint();
- long value = executeForLong("PRAGMA wal_autocheckpoint", null, null);
- if (value != newValue) {
- executeForLong("PRAGMA wal_autocheckpoint=" + newValue, null, null);
- }
- }
- }
-
- private void setJournalSizeLimit() {
- if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
- final long newValue = SQLiteGlobal.getJournalSizeLimit();
- long value = executeForLong("PRAGMA journal_size_limit", null, null);
- if (value != newValue) {
- executeForLong("PRAGMA journal_size_limit=" + newValue, null, null);
- }
- }
- }
-
- private void setForeignKeyModeFromConfiguration() {
- if (!mIsReadOnlyConnection) {
- final long newValue = mConfiguration.foreignKeyConstraintsEnabled ? 1 : 0;
- long value = executeForLong("PRAGMA foreign_keys", null, null);
- if (value != newValue) {
- execute("PRAGMA foreign_keys=" + newValue, null, null);
- }
- }
- }
-
- private void setWalModeFromConfiguration() {
- if (!mConfiguration.isInMemoryDb() && !mIsReadOnlyConnection) {
- if ((mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0) {
- setJournalMode("WAL");
- setSyncMode(SQLiteGlobal.getWALSyncMode());
- } else {
- setJournalMode(SQLiteGlobal.getDefaultJournalMode());
- setSyncMode(SQLiteGlobal.getDefaultSyncMode());
- }
- }
- }
-
- private void setSyncMode(String newValue) {
- String value = executeForString("PRAGMA synchronous", null, null);
- if (!canonicalizeSyncMode(value).equalsIgnoreCase(
- canonicalizeSyncMode(newValue))) {
- execute("PRAGMA synchronous=" + newValue, null, null);
- }
- }
-
- private static String canonicalizeSyncMode(String value) {
- switch (value) {
- case "0":
- return "OFF";
- case "1":
- return "NORMAL";
- case "2":
- return "FULL";
- }
- return value;
- }
-
- private void setJournalMode(String newValue) {
- String value = executeForString("PRAGMA journal_mode", null, null);
- if (!value.equalsIgnoreCase(newValue)) {
- try {
- String result = executeForString("PRAGMA journal_mode=" + newValue, null, null);
- if (result.equalsIgnoreCase(newValue)) {
- return;
- }
- // PRAGMA journal_mode silently fails and returns the original journal
- // mode in some cases if the journal mode could not be changed.
- } catch (SQLiteException ex) {
- // This error (SQLITE_BUSY) occurs if one connection has the database
- // open in WAL mode and another tries to change it to non-WAL.
- if (!(ex instanceof SQLiteDatabaseLockedException)) {
- throw ex;
- }
- }
-
- // Because we always disable WAL mode when a database is first opened
- // (even if we intend to re-enable it), we can encounter problems if
- // there is another open connection to the database somewhere.
- // This can happen for a variety of reasons such as an application opening
- // the same database in multiple processes at the same time or if there is a
- // crashing content provider service that the ActivityManager has
- // removed from its registry but whose process hasn't quite died yet
- // by the time it is restarted in a new process.
- //
- // If we don't change the journal mode, nothing really bad happens.
- // In the worst case, an application that enables WAL might not actually
- // get it, although it can still use connection pooling.
- Log.w(TAG, "Could not change the database journal mode of '"
- + mConfiguration.label + "' from '" + value + "' to '" + newValue
- + "' because the database is locked. This usually means that "
- + "there are other open connections to the database which prevents "
- + "the database from enabling or disabling write-ahead logging mode. "
- + "Proceeding without changing the journal mode.");
- }
- }
-
- private void setLocaleFromConfiguration() {
- // Register the localized collators.
- final String newLocale = mConfiguration.locale.toString();
- nativeRegisterLocalizedCollators(mConnectionPtr, newLocale);
-
- // If the database is read-only, we cannot modify the android metadata table
- // or existing indexes.
- if (mIsReadOnlyConnection) {
- return;
- }
-
- try {
- // Ensure the android metadata table exists.
- execute("CREATE TABLE IF NOT EXISTS android_metadata (locale TEXT)", null, null);
-
- // Check whether the locale was actually changed.
- final String oldLocale = executeForString("SELECT locale FROM android_metadata "
- + "UNION SELECT NULL ORDER BY locale DESC LIMIT 1", null, null);
- if (oldLocale != null && oldLocale.equals(newLocale)) {
- return;
- }
-
- // Go ahead and update the indexes using the new locale.
- execute("BEGIN", null, null);
- boolean success = false;
- try {
- execute("DELETE FROM android_metadata", null, null);
- execute("INSERT INTO android_metadata (locale) VALUES(?)",
- new Object[] { newLocale }, null);
- execute("REINDEX LOCALIZED", null, null);
- success = true;
- } finally {
- execute(success ? "COMMIT" : "ROLLBACK", null, null);
- }
- } catch (RuntimeException ex) {
- throw new SQLiteException("Failed to change locale for db '" + mConfiguration.label
- + "' to '" + newLocale + "'.");
- }
- }
-
- public void enableLocalizedCollators() {
- if (nativeHasCodec()) {
- setLocaleFromConfiguration();
- }
- }
-
- // Called by SQLiteConnectionPool only.
- void reconfigure(SQLiteDatabaseConfiguration configuration) {
- mOnlyAllowReadOnlyOperations = false;
-
- // Register (deprecated) custom functions.
- final int customFunctionCount = configuration.customFunctions.size();
- for (int i = 0; i < customFunctionCount; i++) {
- SQLiteCustomFunction function = configuration.customFunctions.get(i);
- if (!mConfiguration.customFunctions.contains(function)) {
- nativeRegisterCustomFunction(mConnectionPtr, function);
- }
- }
-
- // Register Functions
- final int functionCount = configuration.functions.size();
- for (int i = 0; i < functionCount; i++) {
- SQLiteFunction function = configuration.functions.get(i);
- if (!mConfiguration.functions.contains(function)) {
- nativeRegisterFunction(mConnectionPtr, function);
- }
- }
-
- // Remember what changed.
- boolean foreignKeyModeChanged = configuration.foreignKeyConstraintsEnabled
- != mConfiguration.foreignKeyConstraintsEnabled;
- boolean walModeChanged = ((configuration.openFlags ^ mConfiguration.openFlags)
- & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
- boolean localeChanged = !configuration.locale.equals(mConfiguration.locale);
-
- // Update configuration parameters.
- mConfiguration.updateParametersFrom(configuration);
-
- // Update prepared statement cache size.
- /* mPreparedStatementCache.resize(configuration.maxSqlCacheSize); */
-
- // Update foreign key mode.
- if (foreignKeyModeChanged) {
- setForeignKeyModeFromConfiguration();
- }
-
- // Update WAL.
- if (walModeChanged) {
- setWalModeFromConfiguration();
- }
-
- // Update locale.
- if (localeChanged) {
- setLocaleFromConfiguration();
- }
- }
-
- // Called by SQLiteConnectionPool only.
- // When set to true, executing write operations will throw SQLiteException.
- // Preparing statements that might write is ok, just don't execute them.
- void setOnlyAllowReadOnlyOperations(boolean readOnly) {
- mOnlyAllowReadOnlyOperations = readOnly;
- }
-
- // Called by SQLiteConnectionPool only.
- // Returns true if the prepared statement cache contains the specified SQL.
- boolean isPreparedStatementInCache(String sql) {
- return mPreparedStatementCache.get(sql) != null;
- }
-
- /**
- * Returns true if this is the primary database connection.
- * @return True if this is the primary database connection.
- */
- public boolean isPrimaryConnection() {
- return mIsPrimaryConnection;
- }
-
- /**
- * Prepares a statement for execution but does not bind its parameters or execute it.
- *
- * This method can be used to check for syntax errors during compilation
- * prior to execution of the statement. If the {@code outStatementInfo} argument
- * is not null, the provided {@link SQLiteStatementInfo} object is populated
- * with information about the statement.
- *
- * A prepared statement makes no reference to the arguments that may eventually
- * be bound to it, consequently it it possible to cache certain prepared statements
- * such as SELECT or INSERT/UPDATE statements. If the statement is cacheable,
- * then it will be stored in the cache for later.
- *
- * To take advantage of this behavior as an optimization, the connection pool
- * provides a method to acquire a connection that already has a given SQL statement
- * in its prepared statement cache so that it is ready for execution.
- *
- *
- * @param sql The SQL statement to prepare.
- * @param outStatementInfo The {@link SQLiteStatementInfo} object to populate
- * with information about the statement, or null if none.
- *
- * @throws SQLiteException if an error occurs, such as a syntax error.
- */
- public void prepare(String sql, SQLiteStatementInfo outStatementInfo) {
- if (sql == null) {
- throw new IllegalArgumentException("sql must not be null.");
- }
-
- final int cookie = mRecentOperations.beginOperation("prepare", sql, null);
- try {
- final PreparedStatement statement = acquirePreparedStatement(sql);
- try {
- if (outStatementInfo != null) {
- outStatementInfo.numParameters = statement.mNumParameters;
- outStatementInfo.readOnly = statement.mReadOnly;
-
- final int columnCount = nativeGetColumnCount(
- mConnectionPtr, statement.mStatementPtr);
- if (columnCount == 0) {
- outStatementInfo.columnNames = EMPTY_STRING_ARRAY;
- } else {
- outStatementInfo.columnNames = new String[columnCount];
- for (int i = 0; i < columnCount; i++) {
- outStatementInfo.columnNames[i] = nativeGetColumnName(
- mConnectionPtr, statement.mStatementPtr, i);
- }
- }
- }
- } finally {
- releasePreparedStatement(statement);
- }
- } catch (RuntimeException ex) {
- mRecentOperations.failOperation(cookie, ex);
- throw ex;
- } finally {
- mRecentOperations.endOperation(cookie);
- }
- }
-
- /**
- * Executes a statement that does not return a result.
- *
- * @param sql The SQL statement to execute.
- * @param bindArgs The arguments to bind, or null if none.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- *
- * @throws SQLiteException if an error occurs, such as a syntax error
- * or invalid number of bind arguments.
- * @throws OperationCanceledException if the operation was canceled.
- */
- public void execute(String sql, Object[] bindArgs,
- CancellationSignal cancellationSignal) {
- if (sql == null) {
- throw new IllegalArgumentException("sql must not be null.");
- }
-
- final int cookie = mRecentOperations.beginOperation("execute", sql, bindArgs);
- try {
- final PreparedStatement statement = acquirePreparedStatement(sql);
- try {
- throwIfStatementForbidden(statement);
- bindArguments(statement, bindArgs);
- applyBlockGuardPolicy(statement);
- attachCancellationSignal(cancellationSignal);
- try {
- nativeExecute(mConnectionPtr, statement.mStatementPtr);
- } finally {
- detachCancellationSignal(cancellationSignal);
- }
- } finally {
- releasePreparedStatement(statement);
- }
- } catch (RuntimeException ex) {
- mRecentOperations.failOperation(cookie, ex);
- throw ex;
- } finally {
- mRecentOperations.endOperation(cookie);
- }
- }
-
- /**
- * Executes a statement that returns a single long result.
- *
- * @param sql The SQL statement to execute.
- * @param bindArgs The arguments to bind, or null if none.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * @return The value of the first column in the first row of the result set
- * as a long, or zero if none.
- *
- * @throws SQLiteException if an error occurs, such as a syntax error
- * or invalid number of bind arguments.
- * @throws OperationCanceledException if the operation was canceled.
- */
- public long executeForLong(String sql, Object[] bindArgs,
- CancellationSignal cancellationSignal) {
- if (sql == null) {
- throw new IllegalArgumentException("sql must not be null.");
- }
-
- final int cookie = mRecentOperations.beginOperation("executeForLong", sql, bindArgs);
- try {
- final PreparedStatement statement = acquirePreparedStatement(sql);
- try {
- throwIfStatementForbidden(statement);
- bindArguments(statement, bindArgs);
- applyBlockGuardPolicy(statement);
- attachCancellationSignal(cancellationSignal);
- try {
- return nativeExecuteForLong(mConnectionPtr, statement.mStatementPtr);
- } finally {
- detachCancellationSignal(cancellationSignal);
- }
- } finally {
- releasePreparedStatement(statement);
- }
- } catch (RuntimeException ex) {
- mRecentOperations.failOperation(cookie, ex);
- throw ex;
- } finally {
- mRecentOperations.endOperation(cookie);
- }
- }
-
- /**
- * Executes a statement that returns a single {@link String} result.
- *
- * @param sql The SQL statement to execute.
- * @param bindArgs The arguments to bind, or null if none.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * @return The value of the first column in the first row of the result set
- * as a String, or null if none.
- *
- * @throws SQLiteException if an error occurs, such as a syntax error
- * or invalid number of bind arguments.
- * @throws OperationCanceledException if the operation was canceled.
- */
- public String executeForString(String sql, Object[] bindArgs,
- CancellationSignal cancellationSignal) {
- if (sql == null) {
- throw new IllegalArgumentException("sql must not be null.");
- }
-
- final int cookie = mRecentOperations.beginOperation("executeForString", sql, bindArgs);
- try {
- final PreparedStatement statement = acquirePreparedStatement(sql);
- try {
- throwIfStatementForbidden(statement);
- bindArguments(statement, bindArgs);
- applyBlockGuardPolicy(statement);
- attachCancellationSignal(cancellationSignal);
- try {
- return nativeExecuteForString(mConnectionPtr, statement.mStatementPtr);
- } finally {
- detachCancellationSignal(cancellationSignal);
- }
- } finally {
- releasePreparedStatement(statement);
- }
- } catch (RuntimeException ex) {
- mRecentOperations.failOperation(cookie, ex);
- throw ex;
- } finally {
- mRecentOperations.endOperation(cookie);
- }
- }
-
- /**
- * Executes a statement that returns a single BLOB result as a
- * file descriptor to a shared memory region.
- *
- * @param sql The SQL statement to execute.
- * @param bindArgs The arguments to bind, or null if none.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * @return The file descriptor for a shared memory region that contains
- * the value of the first column in the first row of the result set as a BLOB,
- * or null if none.
- *
- * @throws SQLiteException if an error occurs, such as a syntax error
- * or invalid number of bind arguments.
- * @throws OperationCanceledException if the operation was canceled.
- */
- public ParcelFileDescriptor executeForBlobFileDescriptor(String sql, Object[] bindArgs,
- CancellationSignal cancellationSignal) {
- if (sql == null) {
- throw new IllegalArgumentException("sql must not be null.");
- }
-
- final int cookie = mRecentOperations.beginOperation("executeForBlobFileDescriptor",
- sql, bindArgs);
- try {
- final PreparedStatement statement = acquirePreparedStatement(sql);
- try {
- throwIfStatementForbidden(statement);
- bindArguments(statement, bindArgs);
- applyBlockGuardPolicy(statement);
- attachCancellationSignal(cancellationSignal);
- try {
- int fd = nativeExecuteForBlobFileDescriptor(
- mConnectionPtr, statement.mStatementPtr);
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB_MR2) {
- return fd >= 0 ? ParcelFileDescriptor.adoptFd(fd) : null;
- } else {
- throw new UnsupportedOperationException();
- }
- } finally {
- detachCancellationSignal(cancellationSignal);
- }
- } finally {
- releasePreparedStatement(statement);
- }
- } catch (RuntimeException ex) {
- mRecentOperations.failOperation(cookie, ex);
- throw ex;
- } finally {
- mRecentOperations.endOperation(cookie);
- }
- }
-
- /**
- * Executes a statement that returns a count of the number of rows
- * that were changed. Use for UPDATE or DELETE SQL statements.
- *
- * @param sql The SQL statement to execute.
- * @param bindArgs The arguments to bind, or null if none.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * @return The number of rows that were changed.
- *
- * @throws SQLiteException if an error occurs, such as a syntax error
- * or invalid number of bind arguments.
- * @throws OperationCanceledException if the operation was canceled.
- */
- public int executeForChangedRowCount(String sql, Object[] bindArgs,
- CancellationSignal cancellationSignal) {
- if (sql == null) {
- throw new IllegalArgumentException("sql must not be null.");
- }
-
- int changedRows = 0;
- final int cookie = mRecentOperations.beginOperation("executeForChangedRowCount",
- sql, bindArgs);
- try {
- final PreparedStatement statement = acquirePreparedStatement(sql);
- try {
- throwIfStatementForbidden(statement);
- bindArguments(statement, bindArgs);
- applyBlockGuardPolicy(statement);
- attachCancellationSignal(cancellationSignal);
- try {
- changedRows = nativeExecuteForChangedRowCount(
- mConnectionPtr, statement.mStatementPtr);
- return changedRows;
- } finally {
- detachCancellationSignal(cancellationSignal);
- }
- } finally {
- releasePreparedStatement(statement);
- }
- } catch (RuntimeException ex) {
- mRecentOperations.failOperation(cookie, ex);
- throw ex;
- } finally {
- if (mRecentOperations.endOperationDeferLog(cookie)) {
- mRecentOperations.logOperation(cookie, "changedRows=" + changedRows);
- }
- }
- }
-
- /**
- * Executes a statement that returns the row id of the last row inserted
- * by the statement. Use for INSERT SQL statements.
- *
- * @param sql The SQL statement to execute.
- * @param bindArgs The arguments to bind, or null if none.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * @return The row id of the last row that was inserted, or 0 if none.
- *
- * @throws SQLiteException if an error occurs, such as a syntax error
- * or invalid number of bind arguments.
- * @throws OperationCanceledException if the operation was canceled.
- */
- public long executeForLastInsertedRowId(String sql, Object[] bindArgs,
- CancellationSignal cancellationSignal) {
- if (sql == null) {
- throw new IllegalArgumentException("sql must not be null.");
- }
-
- final int cookie = mRecentOperations.beginOperation("executeForLastInsertedRowId",
- sql, bindArgs);
- try {
- final PreparedStatement statement = acquirePreparedStatement(sql);
- try {
- throwIfStatementForbidden(statement);
- bindArguments(statement, bindArgs);
- applyBlockGuardPolicy(statement);
- attachCancellationSignal(cancellationSignal);
- try {
- return nativeExecuteForLastInsertedRowId(
- mConnectionPtr, statement.mStatementPtr);
- } finally {
- detachCancellationSignal(cancellationSignal);
- }
- } finally {
- releasePreparedStatement(statement);
- }
- } catch (RuntimeException ex) {
- mRecentOperations.failOperation(cookie, ex);
- throw ex;
- } finally {
- mRecentOperations.endOperation(cookie);
- }
- }
-
- /**
- * Executes a statement and populates the specified {@link CursorWindow}
- * with a range of results. Returns the number of rows that were counted
- * during query execution.
- *
- * @param sql The SQL statement to execute.
- * @param bindArgs The arguments to bind, or null if none.
- * @param window The cursor window to clear and fill.
- * @param startPos The start position for filling the window.
- * @param requiredPos The position of a row that MUST be in the window.
- * If it won't fit, then the query should discard part of what it filled
- * so that it does. Must be greater than or equal to startPos.
- * @param countAllRows True to count all rows that the query would return
- * regagless of whether they fit in the window.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * @return The number of rows that were counted during query execution. Might
- * not be all rows in the result set unless countAllRows is true.
- *
- * @throws SQLiteException if an error occurs, such as a syntax error
- * or invalid number of bind arguments.
- * @throws OperationCanceledException if the operation was canceled.
- */
- public int executeForCursorWindow(String sql,
- Object[] bindArgs,
- CursorWindow window,
- int startPos,
- int requiredPos,
- boolean countAllRows,
- CancellationSignal cancellationSignal) {
- if (sql == null) {
- throw new IllegalArgumentException("sql must not be null.");
- }
- if (window == null) {
- throw new IllegalArgumentException("window must not be null.");
- }
-
- window.acquireReference();
- try {
- int actualPos = -1;
- int countedRows = -1;
- int filledRows = -1;
- final int cookie = mRecentOperations.beginOperation("executeForCursorWindow",
- sql, bindArgs);
- try {
- final PreparedStatement statement = acquirePreparedStatement(sql);
- try {
- throwIfStatementForbidden(statement);
- bindArguments(statement, bindArgs);
- applyBlockGuardPolicy(statement);
- attachCancellationSignal(cancellationSignal);
- try {
- final long result = nativeExecuteForCursorWindow(
- mConnectionPtr, statement.mStatementPtr, window.mWindowPtr,
- startPos, requiredPos, countAllRows);
- actualPos = (int)(result >> 32);
- countedRows = (int)result;
- filledRows = window.getNumRows();
- window.setStartPosition(actualPos);
- return countedRows;
- } finally {
- detachCancellationSignal(cancellationSignal);
- }
- } finally {
- releasePreparedStatement(statement);
- }
- } catch (RuntimeException ex) {
- mRecentOperations.failOperation(cookie, ex);
- throw ex;
- } finally {
- if (mRecentOperations.endOperationDeferLog(cookie)) {
- mRecentOperations.logOperation(cookie, "window='" + window
- + "', startPos=" + startPos
- + ", actualPos=" + actualPos
- + ", filledRows=" + filledRows
- + ", countedRows=" + countedRows);
- }
- }
- } finally {
- window.releaseReference();
- }
- }
-
- private PreparedStatement acquirePreparedStatement(String sql) {
- PreparedStatement statement = mPreparedStatementCache.get(sql);
- boolean skipCache = false;
- if (statement != null) {
- if (!statement.mInUse) {
- return statement;
- }
- // The statement is already in the cache but is in use (this statement appears
- // to be not only re-entrant but recursive!). So prepare a new copy of the
- // statement but do not cache it.
- skipCache = true;
- }
-
- final long statementPtr = nativePrepareStatement(mConnectionPtr, sql);
- try {
- final int numParameters = nativeGetParameterCount(mConnectionPtr, statementPtr);
- final int type = SQLiteStatementType.getSqlStatementType(sql);
- final boolean readOnly = nativeIsReadOnly(mConnectionPtr, statementPtr);
- statement = obtainPreparedStatement(sql, statementPtr, numParameters, type, readOnly);
- if (!skipCache && isCacheable(type)) {
- mPreparedStatementCache.put(sql, statement);
- statement.mInCache = true;
- }
- } catch (RuntimeException ex) {
- // Finalize the statement if an exception occurred and we did not add
- // it to the cache. If it is already in the cache, then leave it there.
- if (statement == null || !statement.mInCache) {
- nativeFinalizeStatement(mConnectionPtr, statementPtr);
- }
- throw ex;
- }
- statement.mInUse = true;
- return statement;
- }
-
- private void releasePreparedStatement(PreparedStatement statement) {
- statement.mInUse = false;
- if (statement.mInCache) {
- try {
- nativeResetStatementAndClearBindings(mConnectionPtr, statement.mStatementPtr);
- } catch (SQLiteException ex) {
- // The statement could not be reset due to an error. Remove it from the cache.
- // When remove() is called, the cache will invoke its entryRemoved() callback,
- // which will in turn call finalizePreparedStatement() to finalize and
- // recycle the statement.
- if (DEBUG) {
- Log.d(TAG, "Could not reset prepared statement due to an exception. "
- + "Removing it from the cache. SQL: "
- + trimSqlForDisplay(statement.mSql), ex);
- }
-
- mPreparedStatementCache.remove(statement.mSql);
- }
- } else {
- finalizePreparedStatement(statement);
- }
- }
-
- private void finalizePreparedStatement(PreparedStatement statement) {
- nativeFinalizeStatement(mConnectionPtr, statement.mStatementPtr);
- recyclePreparedStatement(statement);
- }
-
- private void attachCancellationSignal(CancellationSignal cancellationSignal) {
- if (cancellationSignal != null) {
- cancellationSignal.throwIfCanceled();
-
- mCancellationSignalAttachCount += 1;
- if (mCancellationSignalAttachCount == 1) {
- // Reset cancellation flag before executing the statement.
- nativeResetCancel(mConnectionPtr, true /*cancelable*/);
-
- // After this point, onCancel() may be called concurrently.
- cancellationSignal.setOnCancelListener(this);
- }
- }
- }
-
- @SuppressLint("Assert")
- private void detachCancellationSignal(CancellationSignal cancellationSignal) {
- if (cancellationSignal != null) {
- assert mCancellationSignalAttachCount > 0;
-
- mCancellationSignalAttachCount -= 1;
- if (mCancellationSignalAttachCount == 0) {
- // After this point, onCancel() cannot be called concurrently.
- cancellationSignal.setOnCancelListener(null);
-
- // Reset cancellation flag after executing the statement.
- nativeResetCancel(mConnectionPtr, false /*cancelable*/);
- }
- }
- }
-
- // CancellationSignal.OnCancelListener callback.
- // This method may be called on a different thread than the executing statement.
- // However, it will only be called between calls to attachCancellationSignal and
- // detachCancellationSignal, while a statement is executing. We can safely assume
- // that the SQLite connection is still alive.
- @Override
- public void onCancel() {
- nativeCancel(mConnectionPtr);
- }
-
- private void bindArguments(PreparedStatement statement, Object[] bindArgs) {
- final int count = bindArgs != null ? bindArgs.length : 0;
- if (count != statement.mNumParameters) {
- String message = "Expected " + statement.mNumParameters + " bind arguments but "
- + count + " were provided.";
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- throw new SQLiteBindOrColumnIndexOutOfRangeException(message);
- } else {
- throw new SQLiteException(message);
- }
- }
- if (count == 0) {
- return;
- }
-
- final long statementPtr = statement.mStatementPtr;
- for (int i = 0; i < count; i++) {
- final Object arg = bindArgs[i];
- switch (getTypeOfObject(arg)) {
- case Cursor.FIELD_TYPE_NULL:
- nativeBindNull(mConnectionPtr, statementPtr, i + 1);
- break;
- case Cursor.FIELD_TYPE_INTEGER:
- nativeBindLong(mConnectionPtr, statementPtr, i + 1,
- ((Number)arg).longValue());
- break;
- case Cursor.FIELD_TYPE_FLOAT:
- nativeBindDouble(mConnectionPtr, statementPtr, i + 1,
- ((Number)arg).doubleValue());
- break;
- case Cursor.FIELD_TYPE_BLOB:
- nativeBindBlob(mConnectionPtr, statementPtr, i + 1, (byte[])arg);
- break;
- case Cursor.FIELD_TYPE_STRING:
- default:
- if (arg instanceof Boolean) {
- // Provide compatibility with legacy applications which may pass
- // Boolean values in bind args.
- nativeBindLong(mConnectionPtr, statementPtr, i + 1, (Boolean) arg ? 1 : 0);
- } else {
- nativeBindString(mConnectionPtr, statementPtr, i + 1, arg.toString());
- }
- break;
- }
- }
- }
-
- /**
- * Returns data type of the given object's value.
- *
- * Returned values are
- *
- * {@link Cursor#FIELD_TYPE_NULL}
- * {@link Cursor#FIELD_TYPE_INTEGER}
- * {@link Cursor#FIELD_TYPE_FLOAT}
- * {@link Cursor#FIELD_TYPE_STRING}
- * {@link Cursor#FIELD_TYPE_BLOB}
- *
- *
- *
- * @param obj the object whose value type is to be returned
- * @return object value type
- */
- @TargetApi(Build.VERSION_CODES.HONEYCOMB)
- private static int getTypeOfObject(Object obj) {
- if (obj == null) {
- return Cursor.FIELD_TYPE_NULL;
- } else if (obj instanceof byte[]) {
- return Cursor.FIELD_TYPE_BLOB;
- } else if (obj instanceof Float || obj instanceof Double) {
- return Cursor.FIELD_TYPE_FLOAT;
- } else if (obj instanceof Long || obj instanceof Integer
- || obj instanceof Short || obj instanceof Byte) {
- return Cursor.FIELD_TYPE_INTEGER;
- } else {
- return Cursor.FIELD_TYPE_STRING;
- }
- }
-
- private void throwIfStatementForbidden(PreparedStatement statement) {
- if (mOnlyAllowReadOnlyOperations && !statement.mReadOnly) {
- throw new SQLiteException("Cannot execute this statement because it "
- + "might modify the database but the connection is read-only.");
- }
- }
-
- private static boolean isCacheable(int statementType) {
- return statementType == SQLiteStatementType.STATEMENT_UPDATE
- || statementType == SQLiteStatementType.STATEMENT_SELECT;
- }
-
- private void applyBlockGuardPolicy(PreparedStatement statement) {
- if (!mConfiguration.isInMemoryDb() && SQLiteDebug.DEBUG_SQL_LOG) {
- // don't have access to the policy, so just log
- if (Looper.myLooper() == Looper.getMainLooper()) {
- if (statement.mReadOnly) {
- Log.w(TAG, "Reading from disk on main thread");
- } else {
- Log.w(TAG, "Writing to disk on main thread");
- }
- }
- }
- }
-
- /**
- * Dumps debugging information about this connection.
- *
- * @param printer The printer to receive the dump, not null.
- * @param verbose True to dump more verbose information.
- */
- public void dump(Printer printer, boolean verbose) {
- dumpUnsafe(printer, verbose);
- }
-
- /**
- * Dumps debugging information about this connection, in the case where the
- * caller might not actually own the connection.
- *
- * This function is written so that it may be called by a thread that does not
- * own the connection. We need to be very careful because the connection state is
- * not synchronized.
- *
- * At worst, the method may return stale or slightly wrong data, however
- * it should not crash. This is ok as it is only used for diagnostic purposes.
- *
- * @param printer The printer to receive the dump, not null.
- * @param verbose True to dump more verbose information.
- */
- void dumpUnsafe(Printer printer, boolean verbose) {
- printer.println("Connection #" + mConnectionId + ":");
- if (verbose) {
- printer.println(" connectionPtr: 0x" + Long.toHexString(mConnectionPtr));
- }
- printer.println(" isPrimaryConnection: " + mIsPrimaryConnection);
- printer.println(" onlyAllowReadOnlyOperations: " + mOnlyAllowReadOnlyOperations);
-
- mRecentOperations.dump(printer, verbose);
-
- if (verbose) {
- mPreparedStatementCache.dump(printer);
- }
- }
-
- /**
- * Describes the currently executing operation, in the case where the
- * caller might not actually own the connection.
- *
- * This function is written so that it may be called by a thread that does not
- * own the connection. We need to be very careful because the connection state is
- * not synchronized.
- *
- * At worst, the method may return stale or slightly wrong data, however
- * it should not crash. This is ok as it is only used for diagnostic purposes.
- *
- * @return A description of the current operation including how long it has been running,
- * or null if none.
- */
- String describeCurrentOperationUnsafe() {
- return mRecentOperations.describeCurrentOperation();
- }
-
- /**
- * Collects statistics about database connection memory usage.
- *
- * @param dbStatsList The list to populate.
- */
- void collectDbStats(ArrayList dbStatsList) {
- // Get information about the main database.
- int lookaside = nativeGetDbLookaside(mConnectionPtr);
- long pageCount = 0;
- long pageSize = 0;
- try {
- pageCount = executeForLong("PRAGMA page_count;", null, null);
- pageSize = executeForLong("PRAGMA page_size;", null, null);
- } catch (SQLiteException ex) {
- // Ignore.
- }
- dbStatsList.add(getMainDbStatsUnsafe(lookaside, pageCount, pageSize));
-
- // Get information about attached databases.
- // We ignore the first row in the database list because it corresponds to
- // the main database which we have already described.
- CursorWindow window = new CursorWindow("collectDbStats");
- try {
- executeForCursorWindow("PRAGMA database_list;", null, window, 0, 0, false, null);
- for (int i = 1; i < window.getNumRows(); i++) {
- String name = window.getString(i, 1);
- String path = window.getString(i, 2);
- pageCount = 0;
- pageSize = 0;
- try {
- pageCount = executeForLong("PRAGMA " + name + ".page_count;", null, null);
- pageSize = executeForLong("PRAGMA " + name + ".page_size;", null, null);
- } catch (SQLiteException ex) {
- // Ignore.
- }
- String label = " (attached) " + name;
- if (!path.isEmpty()) {
- label += ": " + path;
- }
- dbStatsList.add(new SQLiteDebug.DbStats(label, pageCount, pageSize, 0, 0, 0, 0));
- }
- } catch (SQLiteException ex) {
- // Ignore.
- } finally {
- window.close();
- }
- }
-
- /**
- * Collects statistics about database connection memory usage, in the case where the
- * caller might not actually own the connection.
- */
- void collectDbStatsUnsafe(ArrayList dbStatsList) {
- dbStatsList.add(getMainDbStatsUnsafe(0, 0, 0));
- }
-
- private SQLiteDebug.DbStats getMainDbStatsUnsafe(int lookaside, long pageCount, long pageSize) {
- // The prepared statement cache is thread-safe so we can access its statistics
- // even if we do not own the database connection.
- String label = mConfiguration.path;
- if (!mIsPrimaryConnection) {
- label += " (" + mConnectionId + ")";
- }
- return new SQLiteDebug.DbStats(label, pageCount, pageSize, lookaside,
- mPreparedStatementCache.hitCount(),
- mPreparedStatementCache.missCount(),
- mPreparedStatementCache.size());
- }
-
- @Override
- public String toString() {
- return "SQLiteConnection: " + mConfiguration.path + " (" + mConnectionId + ")";
- }
-
- private PreparedStatement obtainPreparedStatement(String sql, long statementPtr,
- int numParameters, int type, boolean readOnly) {
- PreparedStatement statement = mPreparedStatementPool;
- if (statement != null) {
- mPreparedStatementPool = statement.mPoolNext;
- statement.mPoolNext = null;
- statement.mInCache = false;
- } else {
- statement = new PreparedStatement();
- }
- statement.mSql = sql;
- statement.mStatementPtr = statementPtr;
- statement.mNumParameters = numParameters;
- statement.mType = type;
- statement.mReadOnly = readOnly;
- return statement;
- }
-
- private void recyclePreparedStatement(PreparedStatement statement) {
- statement.mSql = null;
- statement.mPoolNext = mPreparedStatementPool;
- mPreparedStatementPool = statement;
- }
-
- private static String trimSqlForDisplay(String sql) {
- return TRIM_SQL_PATTERN.matcher(sql).replaceAll(" ");
- }
-
- /**
- * Holder type for a prepared statement.
- *
- * Although this object holds a pointer to a native statement object, it
- * does not have a finalizer. This is deliberate. The {@link SQLiteConnection}
- * owns the statement object and will take care of freeing it when needed.
- * In particular, closing the connection requires a guarantee of deterministic
- * resource disposal because all native statement objects must be freed before
- * the native database object can be closed. So no finalizers here.
- */
- private static final class PreparedStatement {
- // Next item in pool.
- public PreparedStatement mPoolNext;
-
- // The SQL from which the statement was prepared.
- public String mSql;
-
- // The native sqlite3_stmt object pointer.
- // Lifetime is managed explicitly by the connection.
- public long mStatementPtr;
-
- // The number of parameters that the prepared statement has.
- public int mNumParameters;
-
- // The statement type.
- public int mType;
-
- // True if the statement is read-only.
- public boolean mReadOnly;
-
- // True if the statement is in the cache.
- public boolean mInCache;
-
- // True if the statement is in use (currently executing).
- // We need this flag because due to the use of custom functions in triggers, it's
- // possible for SQLite calls to be re-entrant. Consequently we need to prevent
- // in use statements from being finalized until they are no longer in use.
- public boolean mInUse;
- }
-
- private final class PreparedStatementCache
- extends LruCache {
- public PreparedStatementCache(int size) {
- super(size);
- }
-
- @Override
- protected void entryRemoved(boolean evicted, String key,
- PreparedStatement oldValue, PreparedStatement newValue) {
- oldValue.mInCache = false;
- if (!oldValue.mInUse) {
- finalizePreparedStatement(oldValue);
- }
- }
-
- public void dump(Printer printer) {
- printer.println(" Prepared statement cache:");
- Map cache = snapshot();
- if (!cache.isEmpty()) {
- int i = 0;
- for (Map.Entry entry : cache.entrySet()) {
- PreparedStatement statement = entry.getValue();
- if (statement.mInCache) { // might be false due to a race with entryRemoved
- String sql = entry.getKey();
- printer.println(" " + i + ": statementPtr=0x"
- + Long.toHexString(statement.mStatementPtr)
- + ", numParameters=" + statement.mNumParameters
- + ", type=" + statement.mType
- + ", readOnly=" + statement.mReadOnly
- + ", sql=\"" + trimSqlForDisplay(sql) + "\"");
- }
- i += 1;
- }
- } else {
- printer.println(" ");
- }
- }
- }
-
- private static final class OperationLog {
- private static final int MAX_RECENT_OPERATIONS = 20;
- private static final int COOKIE_GENERATION_SHIFT = 8;
- private static final int COOKIE_INDEX_MASK = 0xff;
-
- private final Operation[] mOperations = new Operation[MAX_RECENT_OPERATIONS];
- private int mIndex;
- private int mGeneration;
-
- public int beginOperation(String kind, String sql, Object[] bindArgs) {
- synchronized (mOperations) {
- final int index = (mIndex + 1) % MAX_RECENT_OPERATIONS;
- Operation operation = mOperations[index];
- if (operation == null) {
- operation = new Operation();
- mOperations[index] = operation;
- } else {
- operation.mFinished = false;
- operation.mException = null;
- if (operation.mBindArgs != null) {
- operation.mBindArgs.clear();
- }
- }
- operation.mStartTime = System.currentTimeMillis();
- operation.mKind = kind;
- operation.mSql = sql;
- if (bindArgs != null) {
- if (operation.mBindArgs == null) {
- operation.mBindArgs = new ArrayList<>();
- } else {
- operation.mBindArgs.clear();
- }
- for (final Object arg : bindArgs) {
- if (arg != null && arg instanceof byte[]) {
- // Don't hold onto the real byte array longer than necessary.
- operation.mBindArgs.add(EMPTY_BYTE_ARRAY);
- } else {
- operation.mBindArgs.add(arg);
- }
- }
- }
- operation.mCookie = newOperationCookieLocked(index);
- mIndex = index;
- return operation.mCookie;
- }
- }
-
- public void failOperation(int cookie, Exception ex) {
- synchronized (mOperations) {
- final Operation operation = getOperationLocked(cookie);
- if (operation != null) {
- operation.mException = ex;
- }
- }
- }
-
- public void endOperation(int cookie) {
- synchronized (mOperations) {
- if (endOperationDeferLogLocked(cookie)) {
- logOperationLocked(cookie, null);
- }
- }
- }
-
- public boolean endOperationDeferLog(int cookie) {
- synchronized (mOperations) {
- return endOperationDeferLogLocked(cookie);
- }
- }
-
- public void logOperation(int cookie, String detail) {
- synchronized (mOperations) {
- logOperationLocked(cookie, detail);
- }
- }
-
- private boolean endOperationDeferLogLocked(int cookie) {
- final Operation operation = getOperationLocked(cookie);
- if (operation != null) {
- operation.mEndTime = System.currentTimeMillis();
- operation.mFinished = true;
- return SQLiteDebug.DEBUG_LOG_SLOW_QUERIES && SQLiteDebug.shouldLogSlowQuery(
- operation.mEndTime - operation.mStartTime);
- }
- return false;
- }
-
- private void logOperationLocked(int cookie, String detail) {
- final Operation operation = getOperationLocked(cookie);
- if (operation == null) {
- return;
- }
- StringBuilder msg = new StringBuilder();
- operation.describe(msg, false);
- if (detail != null) {
- msg.append(", ").append(detail);
- }
- Log.d(TAG, msg.toString());
- }
-
- private int newOperationCookieLocked(int index) {
- final int generation = mGeneration++;
- return generation << COOKIE_GENERATION_SHIFT | index;
- }
-
- private Operation getOperationLocked(int cookie) {
- final int index = cookie & COOKIE_INDEX_MASK;
- final Operation operation = mOperations[index];
- return operation.mCookie == cookie ? operation : null;
- }
-
- public String describeCurrentOperation() {
- synchronized (mOperations) {
- final Operation operation = mOperations[mIndex];
- if (operation != null && !operation.mFinished) {
- StringBuilder msg = new StringBuilder();
- operation.describe(msg, false);
- return msg.toString();
- }
- return null;
- }
- }
-
- public void dump(Printer printer, boolean verbose) {
- synchronized (mOperations) {
- printer.println(" Most recently executed operations:");
- int index = mIndex;
- Operation operation = mOperations[index];
- if (operation != null) {
- int n = 0;
- do {
- StringBuilder msg = new StringBuilder();
- msg.append(" ").append(n).append(": [");
- msg.append(operation.getFormattedStartTime());
- msg.append("] ");
- operation.describe(msg, verbose);
- printer.println(msg.toString());
-
- if (index > 0) {
- index -= 1;
- } else {
- index = MAX_RECENT_OPERATIONS - 1;
- }
- n += 1;
- operation = mOperations[index];
- } while (operation != null && n < MAX_RECENT_OPERATIONS);
- } else {
- printer.println(" ");
- }
- }
- }
- }
-
- private static final class Operation {
- @SuppressLint("SimpleDateFormat")
- private static final SimpleDateFormat sDateFormat =
- new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
-
- public long mStartTime;
- public long mEndTime;
- public String mKind;
- public String mSql;
- public ArrayList mBindArgs;
- public boolean mFinished;
- public Exception mException;
- public int mCookie;
-
- public void describe(StringBuilder msg, boolean verbose) {
- msg.append(mKind);
- if (mFinished) {
- msg.append(" took ").append(mEndTime - mStartTime).append("ms");
- } else {
- msg.append(" started ").append(System.currentTimeMillis() - mStartTime)
- .append("ms ago");
- }
- msg.append(" - ").append(getStatus());
- if (mSql != null) {
- msg.append(", sql=\"").append(trimSqlForDisplay(mSql)).append("\"");
- }
- if (verbose && mBindArgs != null && mBindArgs.size() != 0) {
- msg.append(", bindArgs=[");
- final int count = mBindArgs.size();
- for (int i = 0; i < count; i++) {
- final Object arg = mBindArgs.get(i);
- if (i != 0) {
- msg.append(", ");
- }
- if (arg == null) {
- msg.append("null");
- } else if (arg instanceof byte[]) {
- msg.append("");
- } else if (arg instanceof String) {
- msg.append("\"").append((String)arg).append("\"");
- } else {
- msg.append(arg);
- }
- }
- msg.append("]");
- }
- if (mException != null) {
- msg.append(", exception=\"").append(mException.getMessage()).append("\"");
- }
- }
-
- private String getStatus() {
- if (!mFinished) {
- return "running";
- }
- return mException != null ? "failed" : "succeeded";
- }
-
- private String getFormattedStartTime() {
- return sDateFormat.format(new Date(mStartTime));
- }
- }
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteConnectionPool.java b/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteConnectionPool.java
deleted file mode 100644
index fdfe5b455f..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteConnectionPool.java
+++ /dev/null
@@ -1,1084 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-/*
-** Modified to support SQLite extensions by the SQLite developers:
-** sqlite-dev@sqlite.org.
-*/
-
-package io.requery.android.database.sqlite;
-
-import android.annotation.SuppressLint;
-import android.database.sqlite.SQLiteException;
-import android.os.SystemClock;
-import android.util.Log;
-import android.util.Printer;
-import androidx.core.os.CancellationSignal;
-import androidx.core.os.OperationCanceledException;
-
-import java.io.Closeable;
-import java.util.ArrayList;
-import java.util.Map;
-import java.util.WeakHashMap;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.locks.LockSupport;
-
-/**
- * Maintains a pool of active SQLite database connections.
- *
- * At any given time, a connection is either owned by the pool, or it has been
- * acquired by a {@link SQLiteSession}. When the {@link SQLiteSession} is
- * finished with the connection it is using, it must return the connection
- * back to the pool.
- *
- * The pool holds strong references to the connections it owns. However,
- * it only holds weak references to the connections that sessions
- * have acquired from it. Using weak references in the latter case ensures
- * that the connection pool can detect when connections have been improperly
- * abandoned so that it can create new connections to replace them if needed.
- *
- * The connection pool is thread-safe (but the connections themselves are not).
- *
- *
- * Exception safety
- *
- * This code attempts to maintain the invariant that opened connections are
- * always owned. Unfortunately that means it needs to handle exceptions
- * all over to ensure that broken connections get cleaned up. Most
- * operations invokving SQLite can throw {@link SQLiteException} or other
- * runtime exceptions. This is a bit of a pain to deal with because the compiler
- * cannot help us catch missing exception handling code.
- *
- * The general rule for this file: If we are making calls out to
- * {@link SQLiteConnection} then we must be prepared to handle any
- * runtime exceptions it might throw at us. Note that out-of-memory
- * is an {@link Error}, not a {@link RuntimeException}. We don't trouble ourselves
- * handling out of memory because it is hard to do anything at all sensible then
- * and most likely the VM is about to crash.
- *
- *
- * @hide
- */
-public final class SQLiteConnectionPool implements Closeable {
- private static final String TAG = "SQLiteConnectionPool";
-
- // Amount of time to wait in milliseconds before unblocking acquireConnection
- // and logging a message about the connection pool being busy.
- private static final long CONNECTION_POOL_BUSY_MILLIS = 30 * 1000; // 30 seconds
-
- private final CloseGuard mCloseGuard = CloseGuard.get();
-
- private final Object mLock = new Object();
- private final AtomicBoolean mConnectionLeaked = new AtomicBoolean();
- private final SQLiteDatabaseConfiguration mConfiguration;
- private int mMaxConnectionPoolSize;
- private boolean mIsOpen;
- private int mNextConnectionId;
-
- private ConnectionWaiter mConnectionWaiterPool;
- private ConnectionWaiter mConnectionWaiterQueue;
-
- // Strong references to all available connections.
- private final ArrayList mAvailableNonPrimaryConnections = new ArrayList<>();
- private SQLiteConnection mAvailablePrimaryConnection;
-
- // Describes what should happen to an acquired connection when it is returned to the pool.
- enum AcquiredConnectionStatus {
- // The connection should be returned to the pool as usual.
- NORMAL,
-
- // The connection must be reconfigured before being returned.
- RECONFIGURE,
-
- // The connection must be closed and discarded.
- DISCARD,
- }
-
- // Weak references to all acquired connections. The associated value
- // indicates whether the connection must be reconfigured before being
- // returned to the available connection list or discarded.
- // For example, the prepared statement cache size may have changed and
- // need to be updated in preparation for the next client.
- private final WeakHashMap mAcquiredConnections =
- new WeakHashMap<>();
-
- /**
- * Connection flag: Read-only.
- *
- * This flag indicates that the connection will only be used to
- * perform read-only operations.
- *
- */
- public static final int CONNECTION_FLAG_READ_ONLY = 1;
-
- /**
- * Connection flag: Primary connection affinity.
- *
- * This flag indicates that the primary connection is required.
- * This flag helps support legacy applications that expect most data modifying
- * operations to be serialized by locking the primary database connection.
- * Setting this flag essentially implements the old "db lock" concept by preventing
- * an operation from being performed until it can obtain exclusive access to
- * the primary connection.
- *
- */
- public static final int CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY = 1 << 1;
-
- /**
- * Connection flag: Connection is being used interactively.
- *
- * This flag indicates that the connection is needed by the UI thread.
- * The connection pool can use this flag to elevate the priority
- * of the database connection request.
- *
- */
- public static final int CONNECTION_FLAG_INTERACTIVE = 1 << 2;
-
- private SQLiteConnectionPool(SQLiteDatabaseConfiguration configuration) {
- mConfiguration = new SQLiteDatabaseConfiguration(configuration);
- setMaxConnectionPoolSizeLocked();
- }
-
- @SuppressWarnings("ThrowFromFinallyBlock")
- @Override
- protected void finalize() throws Throwable {
- try {
- dispose(true);
- } finally {
- super.finalize();
- }
- }
-
- /**
- * Opens a connection pool for the specified database.
- *
- * @param configuration The database configuration.
- * @return The connection pool.
- *
- * @throws SQLiteException if a database error occurs.
- */
- public static SQLiteConnectionPool open(SQLiteDatabaseConfiguration configuration) {
- if (configuration == null) {
- throw new IllegalArgumentException("configuration must not be null.");
- }
-
- // Create the pool.
- SQLiteConnectionPool pool = new SQLiteConnectionPool(configuration);
- pool.open(); // might throw
- return pool;
- }
-
- // Might throw
- private void open() {
- // Open the primary connection.
- // This might throw if the database is corrupt.
- mAvailablePrimaryConnection = openConnectionLocked(mConfiguration,
- true /*primaryConnection*/); // might throw
-
- // Mark the pool as being open for business.
- mIsOpen = true;
- mCloseGuard.open("close");
- }
-
- /**
- * Closes the connection pool.
- *
- * When the connection pool is closed, it will refuse all further requests
- * to acquire connections. All connections that are currently available in
- * the pool are closed immediately. Any connections that are still in use
- * will be closed as soon as they are returned to the pool.
- *
- *
- * @throws IllegalStateException if the pool has been closed.
- */
- public void close() {
- dispose(false);
- }
-
- private void dispose(boolean finalized) {
- if (mCloseGuard != null) {
- if (finalized) {
- mCloseGuard.warnIfOpen();
- }
- mCloseGuard.close();
- }
-
- if (!finalized) {
- // Close all connections. We don't need (or want) to do this
- // when finalized because we don't know what state the connections
- // themselves will be in. The finalizer is really just here for CloseGuard.
- // The connections will take care of themselves when their own finalizers run.
- synchronized (mLock) {
- throwIfClosedLocked();
-
- mIsOpen = false;
-
- closeAvailableConnectionsAndLogExceptionsLocked();
-
- final int pendingCount = mAcquiredConnections.size();
- if (pendingCount != 0) {
- Log.i(TAG, "The connection pool for " + mConfiguration.label
- + " has been closed but there are still "
- + pendingCount + " connections in use. They will be closed "
- + "as they are released back to the pool.");
- }
-
- wakeConnectionWaitersLocked();
- }
- }
- }
-
- /**
- * Reconfigures the database configuration of the connection pool and all of its
- * connections.
- *
- * Configuration changes are propagated down to connections immediately if
- * they are available or as soon as they are released. This includes changes
- * that affect the size of the pool.
- *
- *
- * @param configuration The new configuration.
- *
- * @throws IllegalStateException if the pool has been closed.
- */
- @SuppressLint("Assert")
- public void reconfigure(SQLiteDatabaseConfiguration configuration) {
- if (configuration == null) {
- throw new IllegalArgumentException("configuration must not be null.");
- }
-
- synchronized (mLock) {
- throwIfClosedLocked();
-
- boolean walModeChanged = ((configuration.openFlags ^ mConfiguration.openFlags)
- & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
- if (walModeChanged) {
- // WAL mode can only be changed if there are no acquired connections
- // because we need to close all but the primary connection first.
- if (!mAcquiredConnections.isEmpty()) {
- throw new IllegalStateException("Write Ahead Logging (WAL) mode cannot "
- + "be enabled or disabled while there are transactions in "
- + "progress. Finish all transactions and release all active "
- + "database connections first.");
- }
-
- // Close all non-primary connections. This should happen immediately
- // because none of them are in use.
- closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked();
- assert mAvailableNonPrimaryConnections.isEmpty();
- }
-
- boolean foreignKeyModeChanged = configuration.foreignKeyConstraintsEnabled
- != mConfiguration.foreignKeyConstraintsEnabled;
- if (foreignKeyModeChanged) {
- // Foreign key constraints can only be changed if there are no transactions
- // in progress. To make this clear, we throw an exception if there are
- // any acquired connections.
- if (!mAcquiredConnections.isEmpty()) {
- throw new IllegalStateException("Foreign Key Constraints cannot "
- + "be enabled or disabled while there are transactions in "
- + "progress. Finish all transactions and release all active "
- + "database connections first.");
- }
- }
-
- if (mConfiguration.openFlags != configuration.openFlags) {
- // If we are changing open flags and WAL mode at the same time, then
- // we have no choice but to close the primary connection beforehand
- // because there can only be one connection open when we change WAL mode.
- if (walModeChanged) {
- closeAvailableConnectionsAndLogExceptionsLocked();
- }
-
- // Try to reopen the primary connection using the new open flags then
- // close and discard all existing connections.
- // This might throw if the database is corrupt or cannot be opened in
- // the new mode in which case existing connections will remain untouched.
- SQLiteConnection newPrimaryConnection = openConnectionLocked(configuration,
- true /*primaryConnection*/); // might throw
-
- closeAvailableConnectionsAndLogExceptionsLocked();
- discardAcquiredConnectionsLocked();
-
- mAvailablePrimaryConnection = newPrimaryConnection;
- mConfiguration.updateParametersFrom(configuration);
- setMaxConnectionPoolSizeLocked();
- } else {
- // Reconfigure the database connections in place.
- mConfiguration.updateParametersFrom(configuration);
- setMaxConnectionPoolSizeLocked();
-
- closeExcessConnectionsAndLogExceptionsLocked();
- reconfigureAllConnectionsLocked();
- }
-
- wakeConnectionWaitersLocked();
- }
- }
-
- /**
- * Acquires a connection from the pool.
- *
- * The caller must call {@link #releaseConnection} to release the connection
- * back to the pool when it is finished. Failure to do so will result
- * in much unpleasantness.
- *
- *
- * @param sql If not null, try to find a connection that already has
- * the specified SQL statement in its prepared statement cache.
- * @param connectionFlags The connection request flags.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * @return The connection that was acquired, never null.
- *
- * @throws IllegalStateException if the pool has been closed.
- * @throws SQLiteException if a database error occurs.
- * @throws OperationCanceledException if the operation was canceled.
- */
- public SQLiteConnection acquireConnection(String sql, int connectionFlags,
- CancellationSignal cancellationSignal) {
- return waitForConnection(sql, connectionFlags, cancellationSignal);
- }
-
- /**
- * Releases a connection back to the pool.
- *
- * It is ok to call this method after the pool has closed, to release
- * connections that were still in use at the time of closure.
- *
- *
- * @param connection The connection to release. Must not be null.
- *
- * @throws IllegalStateException if the connection was not acquired
- * from this pool or if it has already been released.
- */
- public void releaseConnection(SQLiteConnection connection) {
- synchronized (mLock) {
- AcquiredConnectionStatus status = mAcquiredConnections.remove(connection);
- if (status == null) {
- throw new IllegalStateException("Cannot perform this operation "
- + "because the specified connection was not acquired "
- + "from this pool or has already been released.");
- }
-
- if (!mIsOpen) {
- closeConnectionAndLogExceptionsLocked(connection);
- } else if (connection.isPrimaryConnection()) {
- if (recycleConnectionLocked(connection, status)) {
- assert mAvailablePrimaryConnection == null;
- mAvailablePrimaryConnection = connection;
- }
- wakeConnectionWaitersLocked();
- } else if (mAvailableNonPrimaryConnections.size() >= mMaxConnectionPoolSize - 1) {
- closeConnectionAndLogExceptionsLocked(connection);
- } else {
- if (recycleConnectionLocked(connection, status)) {
- mAvailableNonPrimaryConnections.add(connection);
- }
- wakeConnectionWaitersLocked();
- }
- }
- }
-
- // Can't throw.
- private boolean recycleConnectionLocked(SQLiteConnection connection,
- AcquiredConnectionStatus status) {
- if (status == AcquiredConnectionStatus.RECONFIGURE) {
- try {
- connection.reconfigure(mConfiguration); // might throw
- } catch (RuntimeException ex) {
- Log.e(TAG, "Failed to reconfigure released connection, closing it: "
- + connection, ex);
- status = AcquiredConnectionStatus.DISCARD;
- }
- }
- if (status == AcquiredConnectionStatus.DISCARD) {
- closeConnectionAndLogExceptionsLocked(connection);
- return false;
- }
- return true;
- }
-
- /**
- * Returns true if the session should yield the connection due to
- * contention over available database connections.
- *
- * @param connection The connection owned by the session.
- * @param connectionFlags The connection request flags.
- * @return True if the session should yield its connection.
- *
- * @throws IllegalStateException if the connection was not acquired
- * from this pool or if it has already been released.
- */
- public boolean shouldYieldConnection(SQLiteConnection connection, int connectionFlags) {
- synchronized (mLock) {
- if (!mAcquiredConnections.containsKey(connection)) {
- throw new IllegalStateException("Cannot perform this operation "
- + "because the specified connection was not acquired "
- + "from this pool or has already been released.");
- }
-
- if (!mIsOpen) {
- return false;
- }
-
- return isSessionBlockingImportantConnectionWaitersLocked(
- connection.isPrimaryConnection(), connectionFlags);
- }
- }
-
- /**
- * Collects statistics about database connection memory usage.
- *
- * @param dbStatsList The list to populate.
- */
- public void collectDbStats(ArrayList dbStatsList) {
- synchronized (mLock) {
- if (mAvailablePrimaryConnection != null) {
- mAvailablePrimaryConnection.collectDbStats(dbStatsList);
- }
-
- for (SQLiteConnection connection : mAvailableNonPrimaryConnections) {
- connection.collectDbStats(dbStatsList);
- }
-
- for (SQLiteConnection connection : mAcquiredConnections.keySet()) {
- connection.collectDbStatsUnsafe(dbStatsList);
- }
- }
- }
-
- // Might throw.
- private SQLiteConnection openConnectionLocked(SQLiteDatabaseConfiguration configuration,
- boolean primaryConnection) {
- final int connectionId = mNextConnectionId++;
- return SQLiteConnection.open(this, configuration,
- connectionId, primaryConnection); // might throw
- }
-
- void onConnectionLeaked() {
- // This code is running inside of the SQLiteConnection finalizer.
- //
- // We don't know whether it is just the connection that has been finalized (and leaked)
- // or whether the connection pool has also been or is about to be finalized.
- // Consequently, it would be a bad idea to try to grab any locks or to
- // do any significant work here. So we do the simplest possible thing and
- // set a flag. waitForConnection() periodically checks this flag (when it
- // times out) so that it can recover from leaked connections and wake
- // itself or other threads up if necessary.
- //
- // You might still wonder why we don't try to do more to wake up the waiters
- // immediately. First, as explained above, it would be hard to do safely
- // unless we started an extra Thread to function as a reference queue. Second,
- // this is never supposed to happen in normal operation. Third, there is no
- // guarantee that the GC will actually detect the leak in a timely manner so
- // it's not all that important that we recover from the leak in a timely manner
- // either. Fourth, if a badly behaved application finds itself hung waiting for
- // several seconds while waiting for a leaked connection to be detected and recreated,
- // then perhaps its authors will have added incentive to fix the problem!
-
- Log.w(TAG, "A SQLiteConnection object for database '"
- + mConfiguration.label + "' was leaked! Please fix your application "
- + "to end transactions in progress properly and to close the database "
- + "when it is no longer needed.");
-
- mConnectionLeaked.set(true);
- }
-
- // Can't throw.
- private void closeAvailableConnectionsAndLogExceptionsLocked() {
- closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked();
-
- if (mAvailablePrimaryConnection != null) {
- closeConnectionAndLogExceptionsLocked(mAvailablePrimaryConnection);
- mAvailablePrimaryConnection = null;
- }
- }
-
- // Can't throw.
- private void closeAvailableNonPrimaryConnectionsAndLogExceptionsLocked() {
- for (SQLiteConnection connection : mAvailableNonPrimaryConnections) {
- closeConnectionAndLogExceptionsLocked(connection);
- }
- mAvailableNonPrimaryConnections.clear();
- }
-
- // Can't throw.
- private void closeExcessConnectionsAndLogExceptionsLocked() {
- int availableCount = mAvailableNonPrimaryConnections.size();
- while (availableCount-- > mMaxConnectionPoolSize - 1) {
- SQLiteConnection connection =
- mAvailableNonPrimaryConnections.remove(availableCount);
- closeConnectionAndLogExceptionsLocked(connection);
- }
- }
-
- // Can't throw.
- private void closeConnectionAndLogExceptionsLocked(SQLiteConnection connection) {
- try {
- connection.close(); // might throw
- } catch (RuntimeException ex) {
- Log.e(TAG, "Failed to close connection, its fate is now in the hands "
- + "of the merciful GC: " + connection, ex);
- }
- }
-
- // Can't throw.
- private void discardAcquiredConnectionsLocked() {
- markAcquiredConnectionsLocked(AcquiredConnectionStatus.DISCARD);
- }
-
- // Can't throw.
- private void reconfigureAllConnectionsLocked() {
- if (mAvailablePrimaryConnection != null) {
- try {
- mAvailablePrimaryConnection.reconfigure(mConfiguration); // might throw
- } catch (RuntimeException ex) {
- Log.e(TAG, "Failed to reconfigure available primary connection, closing it: "
- + mAvailablePrimaryConnection, ex);
- closeConnectionAndLogExceptionsLocked(mAvailablePrimaryConnection);
- mAvailablePrimaryConnection = null;
- }
- }
-
- int count = mAvailableNonPrimaryConnections.size();
- for (int i = 0; i < count; i++) {
- final SQLiteConnection connection = mAvailableNonPrimaryConnections.get(i);
- try {
- connection.reconfigure(mConfiguration); // might throw
- } catch (RuntimeException ex) {
- Log.e(TAG, "Failed to reconfigure available non-primary connection, closing it: "
- + connection, ex);
- closeConnectionAndLogExceptionsLocked(connection);
- mAvailableNonPrimaryConnections.remove(i--);
- count -= 1;
- }
- }
-
- markAcquiredConnectionsLocked(AcquiredConnectionStatus.RECONFIGURE);
- }
-
- // Can't throw.
- private void markAcquiredConnectionsLocked(AcquiredConnectionStatus status) {
- if (!mAcquiredConnections.isEmpty()) {
- ArrayList keysToUpdate = new ArrayList<>(
- mAcquiredConnections.size());
- for (Map.Entry entry
- : mAcquiredConnections.entrySet()) {
- AcquiredConnectionStatus oldStatus = entry.getValue();
- if (status != oldStatus
- && oldStatus != AcquiredConnectionStatus.DISCARD) {
- keysToUpdate.add(entry.getKey());
- }
- }
- for (SQLiteConnection key : keysToUpdate) {
- mAcquiredConnections.put(key, status);
- }
- }
- }
-
- // Might throw.
- private SQLiteConnection waitForConnection(String sql, int connectionFlags,
- CancellationSignal cancellationSignal) {
- final boolean wantPrimaryConnection =
- (connectionFlags & CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY) != 0;
-
- final ConnectionWaiter waiter;
- final int nonce;
- synchronized (mLock) {
- throwIfClosedLocked();
-
- // Abort if canceled.
- if (cancellationSignal != null) {
- cancellationSignal.throwIfCanceled();
- }
-
- // Try to acquire a connection.
- SQLiteConnection connection = null;
- if (!wantPrimaryConnection) {
- connection = tryAcquireNonPrimaryConnectionLocked(
- sql, connectionFlags); // might throw
- }
- if (connection == null) {
- connection = tryAcquirePrimaryConnectionLocked(connectionFlags); // might throw
- }
- if (connection != null) {
- return connection;
- }
-
- // No connections available. Enqueue a waiter in priority order.
- final int priority = getPriority(connectionFlags);
- final long startTime = SystemClock.uptimeMillis();
- waiter = obtainConnectionWaiterLocked(Thread.currentThread(), startTime,
- priority, wantPrimaryConnection, sql, connectionFlags);
- ConnectionWaiter predecessor = null;
- ConnectionWaiter successor = mConnectionWaiterQueue;
- while (successor != null) {
- if (priority > successor.mPriority) {
- waiter.mNext = successor;
- break;
- }
- predecessor = successor;
- successor = successor.mNext;
- }
- if (predecessor != null) {
- predecessor.mNext = waiter;
- } else {
- mConnectionWaiterQueue = waiter;
- }
-
- nonce = waiter.mNonce;
- }
-
- // Set up the cancellation listener.
- if (cancellationSignal != null) {
- cancellationSignal.setOnCancelListener(new CancellationSignal.OnCancelListener() {
- @Override
- public void onCancel() {
- synchronized (mLock) {
- if (waiter.mNonce == nonce) {
- cancelConnectionWaiterLocked(waiter);
- }
- }
- }
- });
- }
- try {
- // Park the thread until a connection is assigned or the pool is closed.
- // Rethrow an exception from the wait, if we got one.
- long busyTimeoutMillis = CONNECTION_POOL_BUSY_MILLIS;
- long nextBusyTimeoutTime = waiter.mStartTime + busyTimeoutMillis;
- for (;;) {
- // Detect and recover from connection leaks.
- if (mConnectionLeaked.compareAndSet(true, false)) {
- synchronized (mLock) {
- wakeConnectionWaitersLocked();
- }
- }
-
- // Wait to be unparked (may already have happened), a timeout, or interruption.
- LockSupport.parkNanos(this, busyTimeoutMillis * 1000000L);
-
- // Clear the interrupted flag, just in case.
- Thread.interrupted();
-
- // Check whether we are done waiting yet.
- synchronized (mLock) {
- throwIfClosedLocked();
-
- final SQLiteConnection connection = waiter.mAssignedConnection;
- final RuntimeException ex = waiter.mException;
- if (connection != null || ex != null) {
- recycleConnectionWaiterLocked(waiter);
- if (connection != null) {
- return connection;
- }
- throw ex; // rethrow!
- }
-
- final long now = SystemClock.uptimeMillis();
- if (now < nextBusyTimeoutTime) {
- busyTimeoutMillis = now - nextBusyTimeoutTime;
- } else {
- logConnectionPoolBusyLocked(now - waiter.mStartTime, connectionFlags);
- busyTimeoutMillis = CONNECTION_POOL_BUSY_MILLIS;
- nextBusyTimeoutTime = now + busyTimeoutMillis;
- }
- }
- }
- } finally {
- // Remove the cancellation listener.
- if (cancellationSignal != null) {
- cancellationSignal.setOnCancelListener(null);
- }
- }
- }
-
- // Can't throw.
- private void cancelConnectionWaiterLocked(ConnectionWaiter waiter) {
- if (waiter.mAssignedConnection != null || waiter.mException != null) {
- // Waiter is done waiting but has not woken up yet.
- return;
- }
-
- // Waiter must still be waiting. Dequeue it.
- ConnectionWaiter predecessor = null;
- ConnectionWaiter current = mConnectionWaiterQueue;
- while (current != waiter) {
- assert current != null;
- predecessor = current;
- current = current.mNext;
- }
- if (predecessor != null) {
- predecessor.mNext = waiter.mNext;
- } else {
- mConnectionWaiterQueue = waiter.mNext;
- }
-
- // Send the waiter an exception and unpark it.
- waiter.mException = new OperationCanceledException();
- LockSupport.unpark(waiter.mThread);
-
- // Check whether removing this waiter will enable other waiters to make progress.
- wakeConnectionWaitersLocked();
- }
-
- // Can't throw.
- private void logConnectionPoolBusyLocked(long waitMillis, int connectionFlags) {
- final Thread thread = Thread.currentThread();
- StringBuilder msg = new StringBuilder();
- msg.append("The connection pool for database '").append(mConfiguration.label);
- msg.append("' has been unable to grant a connection to thread ");
- msg.append(thread.getId()).append(" (").append(thread.getName()).append(") ");
- msg.append("with flags 0x").append(Integer.toHexString(connectionFlags));
- msg.append(" for ").append(waitMillis * 0.001f).append(" seconds.\n");
-
- ArrayList requests = new ArrayList<>();
- int activeConnections = 0;
- int idleConnections = 0;
- if (!mAcquiredConnections.isEmpty()) {
- for (SQLiteConnection connection : mAcquiredConnections.keySet()) {
- String description = connection.describeCurrentOperationUnsafe();
- if (description != null) {
- requests.add(description);
- activeConnections += 1;
- } else {
- idleConnections += 1;
- }
- }
- }
- int availableConnections = mAvailableNonPrimaryConnections.size();
- if (mAvailablePrimaryConnection != null) {
- availableConnections += 1;
- }
-
- msg.append("Connections: ").append(activeConnections).append(" active, ");
- msg.append(idleConnections).append(" idle, ");
- msg.append(availableConnections).append(" available.\n");
-
- if (!requests.isEmpty()) {
- msg.append("\nRequests in progress:\n");
- for (String request : requests) {
- msg.append(" ").append(request).append("\n");
- }
- }
-
- Log.w(TAG, msg.toString());
- }
-
- // Can't throw.
- private void wakeConnectionWaitersLocked() {
- // Unpark all waiters that have requests that we can fulfill.
- // This method is designed to not throw runtime exceptions, although we might send
- // a waiter an exception for it to rethrow.
- ConnectionWaiter predecessor = null;
- ConnectionWaiter waiter = mConnectionWaiterQueue;
- boolean primaryConnectionNotAvailable = false;
- boolean nonPrimaryConnectionNotAvailable = false;
- while (waiter != null) {
- boolean unpark = false;
- if (!mIsOpen) {
- unpark = true;
- } else {
- try {
- SQLiteConnection connection = null;
- if (!waiter.mWantPrimaryConnection && !nonPrimaryConnectionNotAvailable) {
- connection = tryAcquireNonPrimaryConnectionLocked(
- waiter.mSql, waiter.mConnectionFlags); // might throw
- if (connection == null) {
- nonPrimaryConnectionNotAvailable = true;
- }
- }
- if (connection == null && !primaryConnectionNotAvailable) {
- connection = tryAcquirePrimaryConnectionLocked(
- waiter.mConnectionFlags); // might throw
- if (connection == null) {
- primaryConnectionNotAvailable = true;
- }
- }
- if (connection != null) {
- waiter.mAssignedConnection = connection;
- unpark = true;
- } else if (nonPrimaryConnectionNotAvailable && primaryConnectionNotAvailable) {
- // There are no connections available and the pool is still open.
- // We cannot fulfill any more connection requests, so stop here.
- break;
- }
- } catch (RuntimeException ex) {
- // Let the waiter handle the exception from acquiring a connection.
- waiter.mException = ex;
- unpark = true;
- }
- }
-
- final ConnectionWaiter successor = waiter.mNext;
- if (unpark) {
- if (predecessor != null) {
- predecessor.mNext = successor;
- } else {
- mConnectionWaiterQueue = successor;
- }
- waiter.mNext = null;
-
- LockSupport.unpark(waiter.mThread);
- } else {
- predecessor = waiter;
- }
- waiter = successor;
- }
- }
-
- // Might throw.
- private SQLiteConnection tryAcquirePrimaryConnectionLocked(int connectionFlags) {
- // If the primary connection is available, acquire it now.
- SQLiteConnection connection = mAvailablePrimaryConnection;
- if (connection != null) {
- mAvailablePrimaryConnection = null;
- finishAcquireConnectionLocked(connection, connectionFlags); // might throw
- return connection;
- }
-
- // Make sure that the primary connection actually exists and has just been acquired.
- for (SQLiteConnection acquiredConnection : mAcquiredConnections.keySet()) {
- if (acquiredConnection.isPrimaryConnection()) {
- return null;
- }
- }
-
- // Uhoh. No primary connection! Either this is the first time we asked
- // for it, or maybe it leaked?
- connection = openConnectionLocked(mConfiguration,
- true /*primaryConnection*/); // might throw
- finishAcquireConnectionLocked(connection, connectionFlags); // might throw
- return connection;
- }
-
- // Might throw.
- private SQLiteConnection tryAcquireNonPrimaryConnectionLocked(
- String sql, int connectionFlags) {
- // Try to acquire the next connection in the queue.
- SQLiteConnection connection;
- final int availableCount = mAvailableNonPrimaryConnections.size();
- if (availableCount > 1 && sql != null) {
- // If we have a choice, then prefer a connection that has the
- // prepared statement in its cache.
- for (int i = 0; i < availableCount; i++) {
- connection = mAvailableNonPrimaryConnections.get(i);
- if (connection.isPreparedStatementInCache(sql)) {
- mAvailableNonPrimaryConnections.remove(i);
- finishAcquireConnectionLocked(connection, connectionFlags); // might throw
- return connection;
- }
- }
- }
- if (availableCount > 0) {
- // Otherwise, just grab the next one.
- connection = mAvailableNonPrimaryConnections.remove(availableCount - 1);
- finishAcquireConnectionLocked(connection, connectionFlags); // might throw
- return connection;
- }
-
- // Expand the pool if needed.
- int openConnections = mAcquiredConnections.size();
- if (mAvailablePrimaryConnection != null) {
- openConnections += 1;
- }
- if (openConnections >= mMaxConnectionPoolSize) {
- return null;
- }
- connection = openConnectionLocked(mConfiguration,
- false /*primaryConnection*/); // might throw
- finishAcquireConnectionLocked(connection, connectionFlags); // might throw
- return connection;
- }
-
- // Might throw.
- private void finishAcquireConnectionLocked(SQLiteConnection connection, int connectionFlags) {
- try {
- final boolean readOnly = (connectionFlags & CONNECTION_FLAG_READ_ONLY) != 0;
- connection.setOnlyAllowReadOnlyOperations(readOnly);
-
- mAcquiredConnections.put(connection, AcquiredConnectionStatus.NORMAL);
- } catch (RuntimeException ex) {
- Log.e(TAG, "Failed to prepare acquired connection for session, closing it: "
- + connection +", connectionFlags=" + connectionFlags);
- closeConnectionAndLogExceptionsLocked(connection);
- throw ex; // rethrow!
- }
- }
-
- private boolean isSessionBlockingImportantConnectionWaitersLocked(
- boolean holdingPrimaryConnection, int connectionFlags) {
- ConnectionWaiter waiter = mConnectionWaiterQueue;
- if (waiter != null) {
- final int priority = getPriority(connectionFlags);
- do {
- // Only worry about blocked connections that have same or lower priority.
- if (priority > waiter.mPriority) {
- break;
- }
-
- // If we are holding the primary connection then we are blocking the waiter.
- // Likewise, if we are holding a non-primary connection and the waiter
- // would accept a non-primary connection, then we are blocking the waier.
- if (holdingPrimaryConnection || !waiter.mWantPrimaryConnection) {
- return true;
- }
-
- waiter = waiter.mNext;
- } while (waiter != null);
- }
- return false;
- }
-
- private static int getPriority(int connectionFlags) {
- return (connectionFlags & CONNECTION_FLAG_INTERACTIVE) != 0 ? 1 : 0;
- }
-
- private void setMaxConnectionPoolSizeLocked() {
- if (!SQLiteDatabase.hasCodec()
- && (mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0) {
- mMaxConnectionPoolSize = SQLiteGlobal.getWALConnectionPoolSize();
- } else {
- // TODO: We don't actually need to restrict the connection pool size to 1
- // for non-WAL databases. There might be reasons to use connection pooling
- // with other journal modes. For now, enabling connection pooling and
- // using WAL are the same thing in the API.
- mMaxConnectionPoolSize = 1;
- }
- }
-
- private void throwIfClosedLocked() {
- if (!mIsOpen) {
- throw new IllegalStateException("Cannot perform this operation "
- + "because the connection pool has been closed.");
- }
- }
-
- private ConnectionWaiter obtainConnectionWaiterLocked(Thread thread, long startTime,
- int priority, boolean wantPrimaryConnection, String sql, int connectionFlags) {
- ConnectionWaiter waiter = mConnectionWaiterPool;
- if (waiter != null) {
- mConnectionWaiterPool = waiter.mNext;
- waiter.mNext = null;
- } else {
- waiter = new ConnectionWaiter();
- }
- waiter.mThread = thread;
- waiter.mStartTime = startTime;
- waiter.mPriority = priority;
- waiter.mWantPrimaryConnection = wantPrimaryConnection;
- waiter.mSql = sql;
- waiter.mConnectionFlags = connectionFlags;
- return waiter;
- }
-
- private void recycleConnectionWaiterLocked(ConnectionWaiter waiter) {
- waiter.mNext = mConnectionWaiterPool;
- waiter.mThread = null;
- waiter.mSql = null;
- waiter.mAssignedConnection = null;
- waiter.mException = null;
- waiter.mNonce += 1;
- mConnectionWaiterPool = waiter;
- }
-
- public void enableLocalizedCollators() {
- synchronized (mLock) {
- if (!mAcquiredConnections.isEmpty() || mAvailablePrimaryConnection == null) {
- throw new IllegalStateException(
- "Cannot enable localized collators while database is in use"
- );
- }
- mAvailablePrimaryConnection.enableLocalizedCollators();
- }
- }
-
- /**
- * Dumps debugging information about this connection pool.
- *
- * @param printer The printer to receive the dump, not null.
- * @param verbose True to dump more verbose information.
- */
- public void dump(Printer printer, boolean verbose) {
- synchronized (mLock) {
- printer.println("Connection pool for " + mConfiguration.path + ":");
- printer.println(" Open: " + mIsOpen);
- printer.println(" Max connections: " + mMaxConnectionPoolSize);
-
- printer.println(" Available primary connection:");
- if (mAvailablePrimaryConnection != null) {
- mAvailablePrimaryConnection.dump(printer, verbose);
- } else {
- printer.println("");
- }
-
- printer.println(" Available non-primary connections:");
- if (!mAvailableNonPrimaryConnections.isEmpty()) {
- for (SQLiteConnection connection : mAvailableNonPrimaryConnections) {
- connection.dump(printer, verbose);
- }
- } else {
- printer.println("");
- }
-
- printer.println(" Acquired connections:");
- if (!mAcquiredConnections.isEmpty()) {
- for (Map.Entry entry :
- mAcquiredConnections.entrySet()) {
- final SQLiteConnection connection = entry.getKey();
- connection.dumpUnsafe(printer, verbose);
- printer.println(" Status: " + entry.getValue());
- }
- } else {
- printer.println("");
- }
-
- printer.println(" Connection waiters:");
- if (mConnectionWaiterQueue != null) {
- int i = 0;
- final long now = SystemClock.uptimeMillis();
- for (ConnectionWaiter waiter = mConnectionWaiterQueue; waiter != null;
- waiter = waiter.mNext, i++) {
- printer.println(i + ": waited for "
- + ((now - waiter.mStartTime) * 0.001f)
- + " ms - thread=" + waiter.mThread
- + ", priority=" + waiter.mPriority
- + ", sql='" + waiter.mSql + "'");
- }
- } else {
- printer.println("");
- }
- }
- }
-
- @Override
- public String toString() {
- return "SQLiteConnectionPool: " + mConfiguration.path;
- }
-
- private static final class ConnectionWaiter {
- public ConnectionWaiter mNext;
- public Thread mThread;
- public long mStartTime;
- public int mPriority;
- public boolean mWantPrimaryConnection;
- public String mSql;
- public int mConnectionFlags;
- public SQLiteConnection mAssignedConnection;
- public RuntimeException mException;
- public int mNonce;
- }
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteCursor.java b/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteCursor.java
deleted file mode 100644
index 226689eae5..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteCursor.java
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-
-package io.requery.android.database.sqlite;
-
-import android.util.Log;
-import android.util.SparseIntArray;
-import io.requery.android.database.AbstractWindowedCursor;
-import io.requery.android.database.CursorWindow;
-
-import java.util.HashMap;
-
-/**
- * A Cursor implementation that exposes results from a query on a {@link SQLiteDatabase}.
- *
- * SQLiteCursor is not internally synchronized so code using a SQLiteCursor from multiple
- * threads should perform its own synchronization when using the SQLiteCursor.
- */
-public class SQLiteCursor extends AbstractWindowedCursor {
- static final String TAG = "SQLiteCursor";
- static final int NO_COUNT = -1;
-
- /** The names of the columns in the rows */
- private final String[] mColumns;
-
- /** The query object for the cursor */
- private final SQLiteQuery mQuery;
-
- /** The compiled query this cursor came from */
- private final SQLiteCursorDriver mDriver;
-
- /** The number of rows in the cursor */
- private int mCount = NO_COUNT;
-
- /** The number of rows that can fit in the cursor window, 0 if unknown */
- private int mCursorWindowCapacity;
-
- /** A mapping of column names to column indices, to speed up lookups */
- private SparseIntArray mColumnNameArray;
- private HashMap mColumnNameMap;
-
- /** Used to find out where a cursor was allocated in case it never got released. */
- private final CloseGuard mCloseGuard;
-
- /**
- * Execute a query and provide access to its result set through a Cursor
- * interface. For a query such as: {@code SELECT name, birth, phone FROM
- * myTable WHERE ... LIMIT 1,20 ORDER BY...} the column names (name, birth,
- * phone) would be in the projection argument and everything from
- * {@code FROM} onward would be in the params argument.
- *
- * @param editTable not used, present only for compatibility with
- * {@link android.database.sqlite.SQLiteCursor}
- * @param query the {@link SQLiteQuery} object associated with this cursor object.
- */
- @SuppressWarnings("unused")
- public SQLiteCursor(SQLiteCursorDriver driver, String editTable, SQLiteQuery query) {
- if (query == null) {
- throw new IllegalArgumentException("query object cannot be null");
- }
- mDriver = driver;
- mQuery = query;
- mCloseGuard = CloseGuard.get();
- mColumns = query.getColumnNames();
- }
-
- /**
- * Get the database that this cursor is associated with.
- * @return the SQLiteDatabase that this cursor is associated with.
- */
- public SQLiteDatabase getDatabase() {
- return mQuery.getDatabase();
- }
-
- @Override
- public boolean onMove(int oldPosition, int newPosition) {
- // Make sure the row at newPosition is present in the window
- if (mWindow == null || newPosition < mWindow.getStartPosition() ||
- newPosition >= (mWindow.getStartPosition() + mWindow.getNumRows())) {
- fillWindow(newPosition);
- }
-
- return true;
- }
-
- @Override
- public int getCount() {
- if (mCount == NO_COUNT) {
- fillWindow(0);
- }
- return mCount;
- }
-
- public static int cursorPickFillWindowStartPosition(
- int cursorPosition, int cursorWindowCapacity) {
- return Math.max(cursorPosition - cursorWindowCapacity / 3, 0);
- }
-
- private void fillWindow(int requiredPos) {
- clearOrCreateWindow(getDatabase().getPath());
-
- try {
- if (mCount == NO_COUNT) {
- int startPos = cursorPickFillWindowStartPosition(requiredPos, 0);
- mCount = mQuery.fillWindow(mWindow, startPos, requiredPos, true);
- mCursorWindowCapacity = mWindow.getNumRows();
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "received count(*) from native_fill_window: " + mCount);
- }
- } else {
- int startPos = cursorPickFillWindowStartPosition(requiredPos,
- mCursorWindowCapacity);
- mQuery.fillWindow(mWindow, startPos, requiredPos, false);
- }
- } catch (RuntimeException ex) {
- // Close the cursor window if the query failed and therefore will
- // not produce any results. This helps to avoid accidentally leaking
- // the cursor window if the client does not correctly handle exceptions
- // and fails to close the cursor.
- setWindow(null);
- throw ex;
- }
- }
-
- @Override
- public int getColumnIndex(String columnName) {
- // Create mColumnNameMap on demand
- if (mColumnNameArray == null && mColumnNameMap == null) {
- String[] columns = mColumns;
- int columnCount = columns.length;
- SparseIntArray map = new SparseIntArray(columnCount);
- boolean collision = false;
- for (int i = 0; i < columnCount; i++) {
- int key = columns[i].hashCode();
- // check for hashCode collision
- if (map.get(key, -1) != -1) {
- collision = true;
- break;
- }
- map.put(key, i);
- }
-
- if (collision) {
- mColumnNameMap = new HashMap<>();
- for (int i = 0; i < columnCount; i++) {
- mColumnNameMap.put(columns[i], i);
- }
- } else {
- mColumnNameArray = map;
- }
- }
-
- // Hack according to bug 903852
- final int periodIndex = columnName.lastIndexOf('.');
- if (periodIndex != -1) {
- Exception e = new Exception();
- Log.e(TAG, "requesting column name with table name -- " + columnName, e);
- columnName = columnName.substring(periodIndex + 1);
- }
-
- if (mColumnNameMap != null) {
- Integer i = mColumnNameMap.get(columnName);
- return i == null ? -1 : i;
- } else {
- return mColumnNameArray.get(columnName.hashCode(), -1);
- }
- }
-
- @Override
- public String[] getColumnNames() {
- return mColumns;
- }
-
- @Override
- public void deactivate() {
- super.deactivate();
- mDriver.cursorDeactivated();
- }
-
- @Override
- public void close() {
- super.close();
- synchronized (this) {
- mQuery.close();
- mDriver.cursorClosed();
- }
- }
-
- @Override
- public boolean requery() {
- if (isClosed()) {
- return false;
- }
-
- synchronized (this) {
- if (!mQuery.getDatabase().isOpen()) {
- return false;
- }
-
- if (mWindow != null) {
- mWindow.clear();
- }
- mPos = -1;
- mCount = NO_COUNT;
-
- mDriver.cursorRequeried(this);
- }
-
- try {
- return super.requery();
- } catch (IllegalStateException e) {
- // for backwards compatibility, just return false
- Log.w(TAG, "requery() failed " + e.getMessage(), e);
- return false;
- }
- }
-
- @Override
- public void setWindow(CursorWindow window) {
- super.setWindow(window);
- mCount = NO_COUNT;
- }
-
- /**
- * Changes the selection arguments. The new values take effect after a call to requery().
- */
- public void setSelectionArguments(String[] selectionArgs) {
- mDriver.setBindArguments(selectionArgs);
- }
-
- /**
- * Release the native resources, if they haven't been released yet.
- */
- @Override
- protected void finalize() {
- try {
- // if the cursor hasn't been closed yet, close it first
- if (mWindow != null) {
- mCloseGuard.warnIfOpen();
- close();
- }
- } finally {
- super.finalize();
- }
- }
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteCursorDriver.java b/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteCursorDriver.java
deleted file mode 100644
index 0a3c653cb3..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteCursorDriver.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-
-package io.requery.android.database.sqlite;
-
-import android.database.Cursor;
-
-/**
- * A driver for SQLiteCursors that is used to create them and gets notified
- * by the cursors it creates on significant events in their lifetimes.
- */
-public interface SQLiteCursorDriver {
- /**
- * Executes the query returning a Cursor over the result set.
- *
- * @param factory The CursorFactory to use when creating the Cursors, or
- * null if standard SQLiteCursors should be returned.
- * @return a Cursor over the result set
- */
- Cursor query(SQLiteDatabase.CursorFactory factory, Object[] bindArgs);
-
- /**
- * Called by a SQLiteCursor when it is released.
- */
- void cursorDeactivated();
-
- /**
- * Called by a SQLiteCursor when it is requeried.
- */
- void cursorRequeried(Cursor cursor);
-
- /**
- * Called by a SQLiteCursor when it it closed to destroy this object as well.
- */
- void cursorClosed();
-
- /**
- * Set new bind arguments. These will take effect in cursorRequeried().
- * @param bindArgs the new arguments
- */
- void setBindArguments(String[] bindArgs);
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteCustomExtension.java b/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteCustomExtension.java
deleted file mode 100644
index 86d8efaa77..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteCustomExtension.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2016 requery.io
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package io.requery.android.database.sqlite;
-
-/**
- * Describes a SQLite extension entry point.
- */
-public final class SQLiteCustomExtension {
-
- public final String path;
- public final String entryPoint;
-
- /**
- * Creates a SQLite extension description.
- *
- * @param path path to the loadable extension shared library
- * e.g. "/data/data/(package)/lib/libSqliteICU.so"
- * @param entryPoint extension entry point (optional)
- * e.g. "sqlite3_icu_init"
- */
- public SQLiteCustomExtension(String path, String entryPoint) {
- if (path == null) {
- throw new IllegalArgumentException("null path");
- }
- this.path = path;
- this.entryPoint = entryPoint;
- }
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteCustomFunction.java b/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteCustomFunction.java
deleted file mode 100644
index 6814875b49..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteCustomFunction.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-
-package io.requery.android.database.sqlite;
-
-/**
- * Describes a custom SQL function.
- *
- * @hide
- */
-public final class SQLiteCustomFunction {
- public final String name;
- public final int numArgs;
- public final SQLiteDatabase.CustomFunction callback;
-
- /**
- * Create custom function.
- *
- * @param name The name of the sqlite3 function.
- * @param numArgs The number of arguments for the function, or -1 to
- * support any number of arguments.
- * @param callback The callback to invoke when the function is executed.
- */
- public SQLiteCustomFunction(String name, int numArgs,
- SQLiteDatabase.CustomFunction callback) {
- if (name == null) {
- throw new IllegalArgumentException("name must not be null.");
- }
-
- this.name = name;
- this.numArgs = numArgs;
- this.callback = callback;
- }
-
- // Called from native.
- @SuppressWarnings("unused")
- private String dispatchCallback(String[] args) {
- return callback.callback(args);
- }
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteDatabase.java b/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteDatabase.java
deleted file mode 100644
index aca107f4a6..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteDatabase.java
+++ /dev/null
@@ -1,2603 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-/*
-** Modified to support SQLite extensions by the SQLite developers:
-** sqlite-dev@sqlite.org.
-*/
-
-package io.requery.android.database.sqlite;
-
-import android.annotation.SuppressLint;
-import android.content.ContentValues;
-import android.database.Cursor;
-import android.database.SQLException;
-import android.database.sqlite.SQLiteDatabaseCorruptException;
-import android.database.sqlite.SQLiteException;
-import android.database.sqlite.SQLiteTransactionListener;
-import android.os.Build;
-import android.os.Looper;
-import android.os.ParcelFileDescriptor;
-import android.text.TextUtils;
-import android.util.EventLog;
-import android.util.Log;
-import android.util.Pair;
-import android.util.Printer;
-import androidx.annotation.IntDef;
-import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.annotation.RequiresApi;
-import androidx.core.os.CancellationSignal;
-import androidx.core.os.OperationCanceledException;
-import androidx.sqlite.db.SupportSQLiteDatabase;
-import androidx.sqlite.db.SupportSQLiteQuery;
-import io.requery.android.database.DatabaseErrorHandler;
-import io.requery.android.database.DefaultDatabaseErrorHandler;
-
-import java.io.File;
-import java.io.FileFilter;
-import java.io.IOException;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.WeakHashMap;
-
-/**
- * Exposes methods to manage a SQLite database.
- *
- *
- * SQLiteDatabase has methods to create, delete, execute SQL commands, and
- * perform other common database management tasks.
- *
- * See the Notepad sample application in the SDK for an example of creating
- * and managing a database.
- *
- * Database names must be unique within an application, not across all applications.
- *
- *
- * Localized Collation - ORDER BY
- *
- * In addition to SQLite's default BINARY collator, Android supplies
- * two more, LOCALIZED, which changes with the system's current locale,
- * and UNICODE, which is the Unicode Collation Algorithm and not tailored
- * to the current locale.
- *
- */
-@SuppressWarnings({"unused", "JavaDoc", "TryFinallyCanBeTryWithResources"})
-@SuppressLint("ShiftFlags") // suppressed for readability with native code
-public final class SQLiteDatabase extends SQLiteClosable implements SupportSQLiteDatabase {
-
- /**
- * Name of the compiled native library.
- */
- public static final String LIBRARY_NAME = "sqlite3x";
- static {
- System.loadLibrary(LIBRARY_NAME);
- }
-
- private static final String TAG = "SQLiteDatabase";
-
- private static final int EVENT_DB_CORRUPT = 75004;
-
- // Stores reference to all databases opened in the current process.
- // (The referent Object is not used at this time.)
- // INVARIANT: Guarded by sActiveDatabases.
- private static final WeakHashMap sActiveDatabases = new WeakHashMap<>();
-
- // Thread-local for database sessions that belong to this database.
- // Each thread has its own database session.
- // INVARIANT: Immutable.
- private final ThreadLocal mThreadSession = new ThreadLocal() {
- @Override
- protected SQLiteSession initialValue() {
- return createSession();
- }
- };
-
- // The optional factory to use when creating new Cursors. May be null.
- // INVARIANT: Immutable.
- private final CursorFactory mCursorFactory;
-
- // Error handler to be used when SQLite returns corruption errors.
- // INVARIANT: Immutable.
- private final DatabaseErrorHandler mErrorHandler;
-
- // Shared database state lock.
- // This lock guards all of the shared state of the database, such as its
- // configuration, whether it is open or closed, and so on. This lock should
- // be held for as little time as possible.
- //
- // The lock MUST NOT be held while attempting to acquire database connections or
- // while executing SQL statements on behalf of the client as it can lead to deadlock.
- //
- // It is ok to hold the lock while reconfiguring the connection pool or dumping
- // statistics because those operations are non-reentrant and do not try to acquire
- // connections that might be held by other threads.
- //
- // Basic rule: grab the lock, access or modify global state, release the lock, then
- // do the required SQL work.
- private final Object mLock = new Object();
-
- // Warns if the database is finalized without being closed properly.
- // INVARIANT: Guarded by mLock.
- private final CloseGuard mCloseGuardLocked = CloseGuard.get();
-
- // The database configuration.
- // INVARIANT: Guarded by mLock.
- private final SQLiteDatabaseConfiguration mConfigurationLocked;
-
- // The connection pool for the database, null when closed.
- // The pool itself is thread-safe, but the reference to it can only be acquired
- // when the lock is held.
- // INVARIANT: Guarded by mLock.
- private SQLiteConnectionPool mConnectionPoolLocked;
-
- /**
- * When a constraint violation occurs, an immediate ROLLBACK occurs,
- * thus ending the current transaction, and the command aborts with a
- * return code of SQLITE_CONSTRAINT. If no transaction is active
- * (other than the implied transaction that is created on every command)
- * then this algorithm works the same as ABORT.
- */
- public static final int CONFLICT_ROLLBACK = 1;
-
- /**
- * When a constraint violation occurs,no ROLLBACK is executed
- * so changes from prior commands within the same transaction
- * are preserved. This is the default behavior.
- */
- public static final int CONFLICT_ABORT = 2;
-
- /**
- * When a constraint violation occurs, the command aborts with a return
- * code SQLITE_CONSTRAINT. But any changes to the database that
- * the command made prior to encountering the constraint violation
- * are preserved and are not backed out.
- */
- public static final int CONFLICT_FAIL = 3;
-
- /**
- * When a constraint violation occurs, the one row that contains
- * the constraint violation is not inserted or changed.
- * But the command continues executing normally. Other rows before and
- * after the row that contained the constraint violation continue to be
- * inserted or updated normally. No error is returned.
- */
- public static final int CONFLICT_IGNORE = 4;
-
- /**
- * When a UNIQUE constraint violation occurs, the pre-existing rows that
- * are causing the constraint violation are removed prior to inserting
- * or updating the current row. Thus the insert or update always occurs.
- * The command continues executing normally. No error is returned.
- * If a NOT NULL constraint violation occurs, the NULL value is replaced
- * by the default value for that column. If the column has no default
- * value, then the ABORT algorithm is used. If a CHECK constraint
- * violation occurs then the IGNORE algorithm is used. When this conflict
- * resolution strategy deletes rows in order to satisfy a constraint,
- * it does not invoke delete triggers on those rows.
- * This behavior might change in a future release.
- */
- public static final int CONFLICT_REPLACE = 5;
-
- /**
- * Use the following when no conflict action is specified.
- */
- public static final int CONFLICT_NONE = 0;
-
- /** Conflict options integer enumeration definition */
- @IntDef({
- CONFLICT_ABORT,
- CONFLICT_FAIL,
- CONFLICT_IGNORE,
- CONFLICT_NONE,
- CONFLICT_REPLACE,
- CONFLICT_ROLLBACK})
- @Retention(RetentionPolicy.SOURCE)
- public @interface ConflictAlgorithm {
- }
-
- private static final String[] CONFLICT_VALUES = new String[]
- {"", " OR ROLLBACK ", " OR ABORT ", " OR FAIL ", " OR IGNORE ", " OR REPLACE "};
-
- /** Open flag to open in the database in read only mode */
- public static final int OPEN_READONLY = 0x00000001;
-
- /** Open flag to open in the database in read/write mode */
- public static final int OPEN_READWRITE = 0x00000002;
-
- /** Open flag to create the database if it does not exist */
- public static final int OPEN_CREATE = 0x00000004;
-
- /** Open flag to support URI filenames */
- public static final int OPEN_URI = 0x00000040;
-
- /** Open flag opens the database in multi-thread threading mode */
- public static final int OPEN_NOMUTEX = 0x00008000;
-
- /** Open flag opens the database in serialized threading mode */
- public static final int OPEN_FULLMUTEX = 0x00010000;
-
- /** Open flag opens the database in shared cache mode */
- public static final int OPEN_SHAREDCACHE = 0x00020000;
-
- /** Open flag opens the database in private cache mode */
- public static final int OPEN_PRIVATECACHE = 0x00040000;
-
- /** Open flag equivalent to {@link #OPEN_READWRITE} | {@link #OPEN_CREATE} */
- public static final int CREATE_IF_NECESSARY = OPEN_READWRITE | OPEN_CREATE;
-
- /** Open flag to enable write-ahead logging */ // custom flag remove for sqlite3_open_v2
- public static final int ENABLE_WRITE_AHEAD_LOGGING = 0x20000000;
-
- /** Integer flag definition for the database open options */
- @SuppressLint("UniqueConstants") // duplicate values provided for compatibility
- @IntDef(flag = true, value = {
- OPEN_READONLY,
- OPEN_READWRITE,
- OPEN_CREATE,
- OPEN_URI,
- OPEN_NOMUTEX,
- OPEN_FULLMUTEX,
- OPEN_SHAREDCACHE,
- OPEN_PRIVATECACHE,
- CREATE_IF_NECESSARY,
- ENABLE_WRITE_AHEAD_LOGGING})
- @Retention(RetentionPolicy.SOURCE)
- public @interface OpenFlags {
- }
-
- /**
- * Absolute max value that can be set by {@link #setMaxSqlCacheSize(int)}.
- *
- * Each prepared-statement is between 1K - 6K, depending on the complexity of the
- * SQL statement & schema. A large SQL cache may use a significant amount of memory.
- */
- public static final int MAX_SQL_CACHE_SIZE = 100;
-
- private SQLiteDatabase(SQLiteDatabaseConfiguration configuration,
- CursorFactory cursorFactory,
- DatabaseErrorHandler errorHandler) {
- mCursorFactory = cursorFactory;
- mErrorHandler = errorHandler != null ? errorHandler : new DefaultDatabaseErrorHandler();
- mConfigurationLocked = configuration;
- }
-
- @SuppressWarnings("ThrowFromFinallyBlock")
- @Override
- protected void finalize() throws Throwable {
- try {
- dispose(true);
- } finally {
- super.finalize();
- }
- }
-
- @Override
- protected void onAllReferencesReleased() {
- dispose(false);
- }
-
- private void dispose(boolean finalized) {
- final SQLiteConnectionPool pool;
- synchronized (mLock) {
- if (mCloseGuardLocked != null) {
- if (finalized) {
- mCloseGuardLocked.warnIfOpen();
- }
- mCloseGuardLocked.close();
- }
-
- pool = mConnectionPoolLocked;
- mConnectionPoolLocked = null;
- }
-
- if (!finalized) {
- synchronized (sActiveDatabases) {
- sActiveDatabases.remove(this);
- }
-
- if (pool != null) {
- pool.close();
- }
- }
- }
-
- /**
- * Attempts to release memory that SQLite holds but does not require to
- * operate properly. Typically this memory will come from the page cache.
- *
- * @return the number of bytes actually released
- */
- public static int releaseMemory() {
- return SQLiteGlobal.releaseMemory();
- }
-
- /**
- * Gets a label to use when describing the database in log messages.
- * @return The label.
- */
- String getLabel() {
- synchronized (mLock) {
- return mConfigurationLocked.label;
- }
- }
-
- /**
- * Sends a corruption message to the database error handler.
- */
- void onCorruption() {
- EventLog.writeEvent(EVENT_DB_CORRUPT, getLabel());
- mErrorHandler.onCorruption(this);
- }
-
- /**
- * Gets the {@link SQLiteSession} that belongs to this thread for this database.
- * Once a thread has obtained a session, it will continue to obtain the same
- * session even after the database has been closed (although the session will not
- * be usable). However, a thread that does not already have a session cannot
- * obtain one after the database has been closed.
- *
- * The idea is that threads that have active connections to the database may still
- * have work to complete even after the call to {@link #close}. Active database
- * connections are not actually disposed until they are released by the threads
- * that own them.
- *
- * @return The session, never null.
- *
- * @throws IllegalStateException if the thread does not yet have a session and
- * the database is not open.
- */
- SQLiteSession getThreadSession() {
- return mThreadSession.get(); // initialValue() throws if database closed
- }
-
- SQLiteSession createSession() {
- final SQLiteConnectionPool pool;
- synchronized (mLock) {
- throwIfNotOpenLocked();
- pool = mConnectionPoolLocked;
- }
- return new SQLiteSession(pool);
- }
-
- /**
- * Gets default connection flags that are appropriate for this thread, taking into
- * account whether the thread is acting on behalf of the UI.
- *
- * @param readOnly True if the connection should be read-only.
- * @return The connection flags.
- */
- int getThreadDefaultConnectionFlags(boolean readOnly) {
- int flags = readOnly ? SQLiteConnectionPool.CONNECTION_FLAG_READ_ONLY :
- SQLiteConnectionPool.CONNECTION_FLAG_PRIMARY_CONNECTION_AFFINITY;
- if (isMainThread()) {
- flags |= SQLiteConnectionPool.CONNECTION_FLAG_INTERACTIVE;
- }
- return flags;
- }
-
- private static boolean isMainThread() {
- // FIXME: There should be a better way to do this.
- // Would also be nice to have something that would work across Binder calls.
- Looper looper = Looper.myLooper();
- return looper != null && looper == Looper.getMainLooper();
- }
-
- /**
- * Begins a transaction in EXCLUSIVE mode.
- *
- * Transactions can be nested.
- * When the outer transaction is ended all of
- * the work done in that transaction and all of the nested transactions will be committed or
- * rolled back. The changes will be rolled back if any transaction is ended without being
- * marked as clean (by calling setTransactionSuccessful). Otherwise they will be committed.
- *
- * Here is the standard idiom for transactions:
- *
- *
- * db.beginTransaction();
- * try {
- * ...
- * db.setTransactionSuccessful();
- * } finally {
- * db.endTransaction();
- * }
- *
- */
- @Override
- public void beginTransaction() {
- beginTransaction(null, SQLiteSession.TRANSACTION_MODE_EXCLUSIVE);
- }
-
- /**
- * Begins a transaction in IMMEDIATE mode. Transactions can be nested. When
- * the outer transaction is ended all of the work done in that transaction
- * and all of the nested transactions will be committed or rolled back. The
- * changes will be rolled back if any transaction is ended without being
- * marked as clean (by calling setTransactionSuccessful). Otherwise they
- * will be committed.
- *
- * Here is the standard idiom for transactions:
- *
- *
- * db.beginTransactionNonExclusive();
- * try {
- * ...
- * db.setTransactionSuccessful();
- * } finally {
- * db.endTransaction();
- * }
- *
- */
- @Override
- public void beginTransactionNonExclusive() {
- beginTransaction(null, SQLiteSession.TRANSACTION_MODE_IMMEDIATE);
- }
-
- /**
- * Begins a transaction in DEFERRED mode.
- */
- public void beginTransactionDeferred() {
- beginTransaction(null, SQLiteSession.TRANSACTION_MODE_DEFERRED);
- }
-
- /**
- * Begins a transaction in DEFERRED mode.
- *
- * @param transactionListener listener that should be notified when the transaction begins,
- * commits, or is rolled back, either explicitly or by a call to
- * {@link #yieldIfContendedSafely}.
- */
- public void beginTransactionWithListenerDeferred(
- SQLiteTransactionListener transactionListener) {
- beginTransaction(transactionListener, SQLiteSession.TRANSACTION_MODE_DEFERRED);
- }
-
- /**
- * Begins a transaction in EXCLUSIVE mode.
- *
- * Transactions can be nested.
- * When the outer transaction is ended all of
- * the work done in that transaction and all of the nested transactions will be committed or
- * rolled back. The changes will be rolled back if any transaction is ended without being
- * marked as clean (by calling setTransactionSuccessful). Otherwise they will be committed.
- *
- * Here is the standard idiom for transactions:
- *
- *
- * db.beginTransactionWithListener(listener);
- * try {
- * ...
- * db.setTransactionSuccessful();
- * } finally {
- * db.endTransaction();
- * }
- *
- *
- * @param transactionListener listener that should be notified when the transaction begins,
- * commits, or is rolled back, either explicitly or by a call to
- * {@link #yieldIfContendedSafely}.
- */
- @Override
- public void beginTransactionWithListener(SQLiteTransactionListener transactionListener) {
- beginTransaction(transactionListener, SQLiteSession.TRANSACTION_MODE_EXCLUSIVE);
- }
-
- /**
- * Begins a transaction in IMMEDIATE mode. Transactions can be nested. When
- * the outer transaction is ended all of the work done in that transaction
- * and all of the nested transactions will be committed or rolled back. The
- * changes will be rolled back if any transaction is ended without being
- * marked as clean (by calling setTransactionSuccessful). Otherwise they
- * will be committed.
- *
- * Here is the standard idiom for transactions:
- *
- *
- * db.beginTransactionWithListenerNonExclusive(listener);
- * try {
- * ...
- * db.setTransactionSuccessful();
- * } finally {
- * db.endTransaction();
- * }
- *
- *
- * @param transactionListener listener that should be notified when the
- * transaction begins, commits, or is rolled back, either
- * explicitly or by a call to {@link #yieldIfContendedSafely}.
- */
- @Override
- public void beginTransactionWithListenerNonExclusive(
- SQLiteTransactionListener transactionListener) {
- beginTransaction(transactionListener, SQLiteSession.TRANSACTION_MODE_IMMEDIATE);
- }
-
- private void beginTransaction(SQLiteTransactionListener transactionListener, int mode) {
- acquireReference();
- try {
- getThreadSession().beginTransaction(mode, transactionListener,
- getThreadDefaultConnectionFlags(false /*readOnly*/), null);
- } finally {
- releaseReference();
- }
- }
-
- /**
- * End a transaction. See beginTransaction for notes about how to use this and when transactions
- * are committed and rolled back.
- */
- @Override
- public void endTransaction() {
- acquireReference();
- try {
- getThreadSession().endTransaction(null);
- } finally {
- releaseReference();
- }
- }
-
- /**
- * Marks the current transaction as successful. Do not do any more database work between
- * calling this and calling endTransaction. Do as little non-database work as possible in that
- * situation too. If any errors are encountered between this and endTransaction the transaction
- * will still be committed.
- *
- * @throws IllegalStateException if the current thread is not in a transaction or the
- * transaction is already marked as successful.
- */
- @Override
- public void setTransactionSuccessful() {
- acquireReference();
- try {
- getThreadSession().setTransactionSuccessful();
- } finally {
- releaseReference();
- }
- }
-
- /**
- * Returns true if the current thread has a transaction pending.
- *
- * @return True if the current thread is in a transaction.
- */
- @Override
- public boolean inTransaction() {
- acquireReference();
- try {
- return getThreadSession().hasTransaction();
- } finally {
- releaseReference();
- }
- }
-
- /**
- * Returns true if the current thread is holding an active connection to the database.
- *
- * The name of this method comes from a time when having an active connection
- * to the database meant that the thread was holding an actual lock on the
- * database. Nowadays, there is no longer a true "database lock" although threads
- * may block if they cannot acquire a database connection to perform a
- * particular operation.
- *
- *
- * @return True if the current thread is holding an active connection to the database.
- */
- @Override
- public boolean isDbLockedByCurrentThread() {
- acquireReference();
- try {
- return getThreadSession().hasConnection();
- } finally {
- releaseReference();
- }
- }
-
- /**
- * Temporarily end the transaction to let other threads run. The transaction is assumed to be
- * successful so far. Do not call setTransactionSuccessful before calling this. When this
- * returns a new transaction will have been created but not marked as successful. This assumes
- * that there are no nested transactions (beginTransaction has only been called once) and will
- * throw an exception if that is not the case.
- * @return true if the transaction was yielded
- */
- @Override
- public boolean yieldIfContendedSafely() {
- return yieldIfContendedHelper(true /* check yielding */, -1 /* sleepAfterYieldDelay*/);
- }
-
- /**
- * Temporarily end the transaction to let other threads run. The transaction is assumed to be
- * successful so far. Do not call setTransactionSuccessful before calling this. When this
- * returns a new transaction will have been created but not marked as successful. This assumes
- * that there are no nested transactions (beginTransaction has only been called once) and will
- * throw an exception if that is not the case.
- * @param sleepAfterYieldDelay if > 0, sleep this long before starting a new transaction if
- * the lock was actually yielded. This will allow other background threads to make some
- * more progress than they would if we started the transaction immediately.
- * @return true if the transaction was yielded
- */
- @Override
- public boolean yieldIfContendedSafely(long sleepAfterYieldDelay) {
- return yieldIfContendedHelper(true /* check yielding */, sleepAfterYieldDelay);
- }
-
- private boolean yieldIfContendedHelper(boolean throwIfUnsafe, long sleepAfterYieldDelay) {
- acquireReference();
- try {
- return getThreadSession().yieldTransaction(sleepAfterYieldDelay, throwIfUnsafe, null);
- } finally {
- releaseReference();
- }
- }
-
- /**
- * Open the database according to the flags {@link OpenFlags}
- *
- * Sets the locale of the database to the the system's current locale.
- * Call {@link #setLocale} if you would like something else.
- *
- * @param path to database file to open and/or create
- * @param factory an optional factory class that is called to instantiate a
- * cursor when query is called, or null for default
- * @param flags to control database access mode
- * @return the newly opened database
- * @throws SQLiteException if the database cannot be opened
- */
- public static SQLiteDatabase openDatabase(String path,
- CursorFactory factory,
- @OpenFlags int flags) {
- return openDatabase(path, factory, flags, null);
- }
-
- /**
- * Open the database according to the flags {@link OpenFlags}
- *
- * Sets the locale of the database to the the system's current locale.
- * Call {@link #setLocale} if you would like something else.
- *
- * Accepts input param: a concrete instance of {@link DatabaseErrorHandler} to be
- * used to handle corruption when sqlite reports database corruption.
- *
- * @param path to database file to open and/or create
- * @param factory an optional factory class that is called to instantiate a
- * cursor when query is called, or null for default
- * @param flags to control database access mode
- * @param errorHandler the {@link DatabaseErrorHandler} obj to be used to handle corruption
- * when sqlite reports database corruption
- * @return the newly opened database
- * @throws SQLiteException if the database cannot be opened
- */
- public static SQLiteDatabase openDatabase(String path,
- CursorFactory factory,
- @OpenFlags int flags,
- DatabaseErrorHandler errorHandler) {
- SQLiteDatabaseConfiguration configuration = new SQLiteDatabaseConfiguration(path, flags);
- SQLiteDatabase db = new SQLiteDatabase(configuration, factory, errorHandler);
- db.open();
- return db;
- }
-
- /**
- * Open the database according to the given configuration.
- *
- * Sets the locale of the database to the the system's current locale.
- * Call {@link #setLocale} if you would like something else.
- *
- * Accepts input param: a concrete instance of {@link DatabaseErrorHandler} to be
- * used to handle corruption when sqlite reports database corruption.
- *
- * @param configuration to database configuration to use
- * @param factory an optional factory class that is called to instantiate a
- * cursor when query is called, or null for default
- * @param errorHandler the {@link DatabaseErrorHandler} obj to be used to handle corruption
- * when sqlite reports database corruption
- * @return the newly opened database
- * @throws SQLiteException if the database cannot be opened
- */
- public static SQLiteDatabase openDatabase(SQLiteDatabaseConfiguration configuration,
- CursorFactory factory,
- DatabaseErrorHandler errorHandler) {
- SQLiteDatabase db = new SQLiteDatabase(configuration, factory, errorHandler);
- db.open();
- return db;
- }
-
- /**
- * Equivalent to openDatabase(file.getPath(), factory, CREATE_IF_NECESSARY).
- */
- public static SQLiteDatabase openOrCreateDatabase(File file, CursorFactory factory) {
- return openOrCreateDatabase(file.getPath(), factory);
- }
-
- /**
- * Equivalent to openDatabase(path, factory, CREATE_IF_NECESSARY).
- */
- public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory) {
- return openDatabase(path, factory, CREATE_IF_NECESSARY, null);
- }
-
- /**
- * Equivalent to openDatabase(path, factory, CREATE_IF_NECESSARY, errorHandler).
- */
- public static SQLiteDatabase openOrCreateDatabase(String path, CursorFactory factory,
- DatabaseErrorHandler errorHandler) {
- return openDatabase(path, factory, CREATE_IF_NECESSARY, errorHandler);
- }
-
- /**
- * Deletes a database including its journal file and other auxiliary files
- * that may have been created by the database engine.
- *
- * @param file The database file path.
- * @return True if the database was successfully deleted.
- */
- public static boolean deleteDatabase(File file) {
- if (file == null) {
- throw new IllegalArgumentException("file must not be null");
- }
-
- boolean deleted;
- deleted = file.delete();
- deleted |= new File(file.getPath() + "-journal").delete();
- deleted |= new File(file.getPath() + "-shm").delete();
- deleted |= new File(file.getPath() + "-wal").delete();
-
- File dir = file.getParentFile();
- if (dir != null) {
- final String prefix = file.getName() + "-mj";
- final FileFilter filter = new FileFilter() {
- @Override
- public boolean accept(File candidate) {
- return candidate.getName().startsWith(prefix);
- }
- };
- for (File masterJournal : dir.listFiles(filter)) {
- deleted |= masterJournal.delete();
- }
- }
- return deleted;
- }
-
- /**
- * Reopens the database in read-write mode.
- * If the database is already read-write, does nothing.
- *
- * @throws SQLiteException if the database could not be reopened as requested, in which
- * case it remains open in read only mode.
- * @throws IllegalStateException if the database is not open.
- *
- * @see #isReadOnly()
- * @hide
- */
- public void reopenReadWrite() {
- synchronized (mLock) {
- throwIfNotOpenLocked();
-
- if (!isReadOnlyLocked()) {
- return; // nothing to do
- }
-
- // Reopen the database in read-write mode.
- final int oldOpenFlags = mConfigurationLocked.openFlags;
- mConfigurationLocked.openFlags = (mConfigurationLocked.openFlags & ~OPEN_READONLY);
- try {
- mConnectionPoolLocked.reconfigure(mConfigurationLocked);
- } catch (RuntimeException ex) {
- mConfigurationLocked.openFlags = oldOpenFlags;
- throw ex;
- }
- }
- }
-
- private void open() {
- try {
- if (!mConfigurationLocked.isInMemoryDb()
- && (mConfigurationLocked.openFlags & OPEN_CREATE) != 0) {
- ensureFile(mConfigurationLocked.path);
- }
- try {
- openInner();
- } catch (SQLiteDatabaseCorruptException ex) {
- onCorruption();
- openInner();
- }
- } catch (SQLiteException ex) {
- Log.e(TAG, "Failed to open database '" + getLabel() + "'.", ex);
- close();
- throw ex;
- }
- }
-
- private static void ensureFile(String path) {
- File file = new File(path);
- if (!file.exists()) {
- try {
- if (!file.getParentFile().exists() && !file.getParentFile().mkdirs()) {
- // Fixes #103: Check parent directory's existence before
- // attempting to create.
- Log.e(TAG, "Couldn't mkdirs " + file);
- }
- if (!file.createNewFile()) {
- Log.e(TAG, "Couldn't create " + file);
- }
- } catch (IOException e) {
- Log.e(TAG, "Couldn't ensure file " + file, e);
- }
- }
- }
-
- private void openInner() {
- synchronized (mLock) {
- assert mConnectionPoolLocked == null;
- mConnectionPoolLocked = SQLiteConnectionPool.open(mConfigurationLocked);
- mCloseGuardLocked.open("close");
- }
-
- synchronized (sActiveDatabases) {
- sActiveDatabases.put(this, null);
- }
- }
-
- /**
- * Create a memory backed SQLite database. Its contents will be destroyed
- * when the database is closed.
- *
- * Sets the locale of the database to the the system's current locale.
- * Call {@link #setLocale} if you would like something else.
- *
- * @param factory an optional factory class that is called to instantiate a
- * cursor when query is called
- * @return a SQLiteDatabase object, or null if the database can't be created
- */
- public static SQLiteDatabase create(CursorFactory factory) {
- // This is a magic string with special meaning for SQLite.
- return openDatabase(SQLiteDatabaseConfiguration.MEMORY_DB_PATH,
- factory, CREATE_IF_NECESSARY);
- }
-
- /**
- * Registers a CustomFunction callback as a function that can be called from
- * SQLite database triggers.
- *
- * @param name the name of the sqlite3 function
- * @param numArgs the number of arguments for the function
- * @param function callback to call when the function is executed
- * @hide
- */
- @Deprecated
- public void addCustomFunction(String name, int numArgs, CustomFunction function) {
- // Create wrapper (also validates arguments).
- SQLiteCustomFunction wrapper = new SQLiteCustomFunction(name, numArgs, function);
-
- synchronized (mLock) {
- throwIfNotOpenLocked();
-
- mConfigurationLocked.customFunctions.add(wrapper);
- try {
- mConnectionPoolLocked.reconfigure(mConfigurationLocked);
- } catch (RuntimeException ex) {
- mConfigurationLocked.customFunctions.remove(wrapper);
- throw ex;
- }
- }
- }
-
- /**
- * Registers a Function callback as a function that can be called from
- * SQLite database triggers.
- *
- * @param name the name of the sqlite3 function
- * @param numArgs the number of arguments for the function
- * @param function callback to call when the function is executed
- * @hide
- */
- public void addFunction(String name, int numArgs, Function function) {
- addFunction(name, numArgs, function, 0);
- }
-
- /**
- * Registers a Function callback as a function that can be called from
- * SQLite database triggers.
- *
- * @param name the name of the sqlite3 function
- * @param numArgs the number of arguments for the function
- * @param function callback to call when the function is executed
- * @param flags
- * @hide
- */
- public void addFunction(String name, int numArgs, Function function, int flags) {
- // Create wrapper (also validates arguments).
- SQLiteFunction wrapper = new SQLiteFunction(name, numArgs, function, flags);
-
- synchronized (mLock) {
- throwIfNotOpenLocked();
-
- mConfigurationLocked.functions.add(wrapper);
- try {
- mConnectionPoolLocked.reconfigure(mConfigurationLocked);
- } catch (RuntimeException ex) {
- mConfigurationLocked.functions.remove(wrapper);
- throw ex;
- }
- }
- }
-
- /**
- * Gets the database version.
- *
- * @return the database version
- */
- @Override
- public int getVersion() {
- return ((Long) longForQuery("PRAGMA user_version;", null)).intValue();
- }
-
- /**
- * Sets the database version.
- *
- * @param version the new database version
- */
- @Override
- public void setVersion(int version) {
- execSQL("PRAGMA user_version = " + version);
- }
-
- /**
- * Returns the maximum size the database may grow to.
- *
- * @return the new maximum database size
- */
- @Override
- public long getMaximumSize() {
- long pageCount = longForQuery("PRAGMA max_page_count;", null);
- return pageCount * getPageSize();
- }
-
- /**
- * Sets the maximum size the database will grow to. The maximum size cannot
- * be set below the current size.
- *
- * @param numBytes the maximum database size, in bytes
- * @return the new maximum database size
- */
- @Override
- public long setMaximumSize(long numBytes) {
- long pageSize = getPageSize();
- long numPages = numBytes / pageSize;
- // If numBytes isn't a multiple of pageSize, bump up a page
- if ((numBytes % pageSize) != 0) {
- numPages++;
- }
- long newPageCount = longForQuery("PRAGMA max_page_count = " + numPages, null);
- return newPageCount * pageSize;
- }
-
- /**
- * Returns the current database page size, in bytes.
- *
- * @return the database page size, in bytes
- */
- @Override
- public long getPageSize() {
- return longForQuery("PRAGMA page_size;", null);
- }
-
- /**
- * Sets the database page size. The page size must be a power of two. This
- * method does not work if any data has been written to the database file,
- * and must be called right after the database has been created.
- *
- * @param numBytes the database page size, in bytes
- */
- @Override
- public void setPageSize(long numBytes) {
- execSQL("PRAGMA page_size = " + numBytes);
- }
-
- /**
- * Finds the name of the first table, which is editable.
- *
- * @param tables a list of tables
- * @return the first table listed
- */
- public static String findEditTable(String tables) {
- if (!TextUtils.isEmpty(tables)) {
- // find the first word terminated by either a space or a comma
- int spacepos = tables.indexOf(' ');
- int commapos = tables.indexOf(',');
-
- if (spacepos > 0 && (spacepos < commapos || commapos < 0)) {
- return tables.substring(0, spacepos);
- } else if (commapos > 0 && (commapos < spacepos || spacepos < 0) ) {
- return tables.substring(0, commapos);
- }
- return tables;
- } else {
- throw new IllegalStateException("Invalid tables");
- }
- }
-
- /**
- * Compiles an SQL statement into a reusable pre-compiled statement object.
- * The parameters are identical to {@link #execSQL(String)}. You may put ?s in the
- * statement and fill in those values with {@link SQLiteProgram#bindString}
- * and {@link SQLiteProgram#bindLong} each time you want to run the
- * statement. Statements may not return result sets larger than 1x1.
- *
- * No two threads should be using the same {@link SQLiteStatement} at the same time.
- *
- * @param sql The raw SQL statement, may contain ? for unknown values to be
- * bound later.
- * @return A pre-compiled {@link SQLiteStatement} object. Note that
- * {@link SQLiteStatement}s are not synchronized, see the documentation for more details.
- */
- @Override
- public SQLiteStatement compileStatement(String sql) throws SQLException {
- acquireReference();
- try {
- return new SQLiteStatement(this, sql, null);
- } finally {
- releaseReference();
- }
- }
-
- /**
- * Query the given URL, returning a {@link Cursor} over the result set.
- *
- * @param distinct true if you want each row to be unique, false otherwise.
- * @param table The table name to compile the query against.
- * @param columns A list of which columns to return. Passing null will
- * return all columns, which is discouraged to prevent reading
- * data from storage that isn't going to be used.
- * @param selection A filter declaring which rows to return, formatted as an
- * SQL WHERE clause (excluding the WHERE itself). Passing null
- * will return all rows for the given table.
- * @param selectionArgs You may include ?s in selection, which will be
- * replaced by the values from selectionArgs, in order that they
- * appear in the selection.
- * @param groupBy A filter declaring how to group rows, formatted as an SQL
- * GROUP BY clause (excluding the GROUP BY itself). Passing null
- * will cause the rows to not be grouped.
- * @param having A filter declare which row groups to include in the cursor,
- * if row grouping is being used, formatted as an SQL HAVING
- * clause (excluding the HAVING itself). Passing null will cause
- * all row groups to be included, and is required when row
- * grouping is not being used.
- * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
- * (excluding the ORDER BY itself). Passing null will use the
- * default sort order, which may be unordered.
- * @param limit Limits the number of rows returned by the query,
- * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
- * @return A {@link Cursor} object, which is positioned before the first entry. Note that
- * {@link Cursor}s are not synchronized, see the documentation for more details.
- * @see Cursor
- */
- public Cursor query(boolean distinct, String table, String[] columns,
- String selection, Object[] selectionArgs, String groupBy,
- String having, String orderBy, String limit) {
- return queryWithFactory(null, distinct, table, columns, selection, selectionArgs,
- groupBy, having, orderBy, limit, null);
- }
-
- /**
- * Query the given URL, returning a {@link Cursor} over the result set.
- *
- * @param distinct true if you want each row to be unique, false otherwise.
- * @param table The table name to compile the query against.
- * @param columns A list of which columns to return. Passing null will
- * return all columns, which is discouraged to prevent reading
- * data from storage that isn't going to be used.
- * @param selection A filter declaring which rows to return, formatted as an
- * SQL WHERE clause (excluding the WHERE itself). Passing null
- * will return all rows for the given table.
- * @param selectionArgs You may include ?s in selection, which will be
- * replaced by the values from selectionArgs, in order that they
- * appear in the selection.
- * @param groupBy A filter declaring how to group rows, formatted as an SQL
- * GROUP BY clause (excluding the GROUP BY itself). Passing null
- * will cause the rows to not be grouped.
- * @param having A filter declare which row groups to include in the cursor,
- * if row grouping is being used, formatted as an SQL HAVING
- * clause (excluding the HAVING itself). Passing null will cause
- * all row groups to be included, and is required when row
- * grouping is not being used.
- * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
- * (excluding the ORDER BY itself). Passing null will use the
- * default sort order, which may be unordered.
- * @param limit Limits the number of rows returned by the query,
- * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * If the operation is canceled, then {@link OperationCanceledException} will be thrown
- * when the query is executed.
- * @return A {@link Cursor} object, which is positioned before the first entry. Note that
- * {@link Cursor}s are not synchronized, see the documentation for more details.
- * @see Cursor
- */
- public Cursor query(boolean distinct, String table, String[] columns,
- String selection, Object[] selectionArgs, String groupBy,
- String having, String orderBy, String limit, CancellationSignal cancellationSignal) {
- return queryWithFactory(null, distinct, table, columns, selection, selectionArgs,
- groupBy, having, orderBy, limit, cancellationSignal);
- }
-
- /**
- * Query the given URL, returning a {@link Cursor} over the result set.
- *
- * @param cursorFactory the cursor factory to use, or null for the default factory
- * @param distinct true if you want each row to be unique, false otherwise.
- * @param table The table name to compile the query against.
- * @param columns A list of which columns to return. Passing null will
- * return all columns, which is discouraged to prevent reading
- * data from storage that isn't going to be used.
- * @param selection A filter declaring which rows to return, formatted as an
- * SQL WHERE clause (excluding the WHERE itself). Passing null
- * will return all rows for the given table.
- * @param selectionArgs You may include ?s in selection, which will be
- * replaced by the values from selectionArgs, in order that they
- * appear in the selection.
- * @param groupBy A filter declaring how to group rows, formatted as an SQL
- * GROUP BY clause (excluding the GROUP BY itself). Passing null
- * will cause the rows to not be grouped.
- * @param having A filter declare which row groups to include in the cursor,
- * if row grouping is being used, formatted as an SQL HAVING
- * clause (excluding the HAVING itself). Passing null will cause
- * all row groups to be included, and is required when row
- * grouping is not being used.
- * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
- * (excluding the ORDER BY itself). Passing null will use the
- * default sort order, which may be unordered.
- * @param limit Limits the number of rows returned by the query,
- * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
- * @return A {@link Cursor} object, which is positioned before the first entry. Note that
- * {@link Cursor}s are not synchronized, see the documentation for more details.
- * @see Cursor
- */
- public Cursor queryWithFactory(CursorFactory cursorFactory,
- boolean distinct, String table, String[] columns,
- String selection, Object[] selectionArgs, String groupBy,
- String having, String orderBy, String limit) {
- return queryWithFactory(cursorFactory, distinct, table, columns, selection,
- selectionArgs, groupBy, having, orderBy, limit, null);
- }
-
- /**
- * Query the given URL, returning a {@link Cursor} over the result set.
- *
- * @param cursorFactory the cursor factory to use, or null for the default factory
- * @param distinct true if you want each row to be unique, false otherwise.
- * @param table The table name to compile the query against.
- * @param columns A list of which columns to return. Passing null will
- * return all columns, which is discouraged to prevent reading
- * data from storage that isn't going to be used.
- * @param selection A filter declaring which rows to return, formatted as an
- * SQL WHERE clause (excluding the WHERE itself). Passing null
- * will return all rows for the given table.
- * @param selectionArgs You may include ?s in selection, which will be
- * replaced by the values from selectionArgs, in order that they
- * appear in the selection.
- * @param groupBy A filter declaring how to group rows, formatted as an SQL
- * GROUP BY clause (excluding the GROUP BY itself). Passing null
- * will cause the rows to not be grouped.
- * @param having A filter declare which row groups to include in the cursor,
- * if row grouping is being used, formatted as an SQL HAVING
- * clause (excluding the HAVING itself). Passing null will cause
- * all row groups to be included, and is required when row
- * grouping is not being used.
- * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
- * (excluding the ORDER BY itself). Passing null will use the
- * default sort order, which may be unordered.
- * @param limit Limits the number of rows returned by the query,
- * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * If the operation is canceled, then {@link OperationCanceledException} will be thrown
- * when the query is executed.
- * @return A {@link Cursor} object, which is positioned before the first entry. Note that
- * {@link Cursor}s are not synchronized, see the documentation for more details.
- * @see Cursor
- */
- public Cursor queryWithFactory(CursorFactory cursorFactory,
- boolean distinct, String table, String[] columns,
- String selection, Object[] selectionArgs, String groupBy,
- String having, String orderBy, String limit, CancellationSignal cancellationSignal) {
- acquireReference();
- try {
- String sql = SQLiteQueryBuilder.buildQueryString(
- distinct, table, columns, selection, groupBy, having, orderBy, limit);
-
- return rawQueryWithFactory(cursorFactory, sql, selectionArgs,
- findEditTable(table), cancellationSignal);
- } finally {
- releaseReference();
- }
- }
-
- /**
- * Query the given table, returning a {@link Cursor} over the result set.
- *
- * @param table The table name to compile the query against.
- * @param columns A list of which columns to return. Passing null will
- * return all columns, which is discouraged to prevent reading
- * data from storage that isn't going to be used.
- * @param selection A filter declaring which rows to return, formatted as an
- * SQL WHERE clause (excluding the WHERE itself). Passing null
- * will return all rows for the given table.
- * @param selectionArgs You may include ?s in selection, which will be
- * replaced by the values from selectionArgs, in order that they
- * appear in the selection.
- * @param groupBy A filter declaring how to group rows, formatted as an SQL
- * GROUP BY clause (excluding the GROUP BY itself). Passing null
- * will cause the rows to not be grouped.
- * @param having A filter declare which row groups to include in the cursor,
- * if row grouping is being used, formatted as an SQL HAVING
- * clause (excluding the HAVING itself). Passing null will cause
- * all row groups to be included, and is required when row
- * grouping is not being used.
- * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
- * (excluding the ORDER BY itself). Passing null will use the
- * default sort order, which may be unordered.
- * @return A {@link Cursor} object, which is positioned before the first entry. Note that
- * {@link Cursor}s are not synchronized, see the documentation for more details.
- * @see Cursor
- */
- public Cursor query(String table, String[] columns, String selection,
- Object[] selectionArgs, String groupBy, String having,
- String orderBy) {
-
- return query(false, table, columns, selection, selectionArgs, groupBy,
- having, orderBy, null /* limit */);
- }
-
- /**
- * Query the given table, returning a {@link Cursor} over the result set.
- *
- * @param table The table name to compile the query against.
- * @param columns A list of which columns to return. Passing null will
- * return all columns, which is discouraged to prevent reading
- * data from storage that isn't going to be used.
- * @param selection A filter declaring which rows to return, formatted as an
- * SQL WHERE clause (excluding the WHERE itself). Passing null
- * will return all rows for the given table.
- * @param selectionArgs You may include ?s in selection, which will be
- * replaced by the values from selectionArgs, in order that they
- * appear in the selection.
- * @param groupBy A filter declaring how to group rows, formatted as an SQL
- * GROUP BY clause (excluding the GROUP BY itself). Passing null
- * will cause the rows to not be grouped.
- * @param having A filter declare which row groups to include in the cursor,
- * if row grouping is being used, formatted as an SQL HAVING
- * clause (excluding the HAVING itself). Passing null will cause
- * all row groups to be included, and is required when row
- * grouping is not being used.
- * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
- * (excluding the ORDER BY itself). Passing null will use the
- * default sort order, which may be unordered.
- * @param limit Limits the number of rows returned by the query,
- * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
- * @return A {@link Cursor} object, which is positioned before the first entry. Note that
- * {@link Cursor}s are not synchronized, see the documentation for more details.
- * @see Cursor
- */
- public Cursor query(String table, String[] columns, String selection,
- Object[] selectionArgs, String groupBy, String having,
- String orderBy, String limit) {
-
- return query(false, table, columns, selection, selectionArgs, groupBy,
- having, orderBy, limit);
- }
-
- /**
- * Runs the provided SQL and returns a {@link Cursor} over the result set.
- *
- * @param query the SQL query. The SQL string must not be ; terminated
- * @return A {@link Cursor} object, which is positioned before the first entry. Note that
- * {@link Cursor}s are not synchronized, see the documentation for more details.
- */
- @Override
- public Cursor query(String query) {
- return rawQueryWithFactory(null, query, null, null, null);
- }
-
- /**
- * Runs the provided SQL and returns a {@link Cursor} over the result set.
- *
- * @param query the SQL query. The SQL string must not be ; terminated
- * @param selectionArgs You may include ?s in where clause in the query,
- * which will be replaced by the values from selectionArgs.
- * @return A {@link Cursor} object, which is positioned before the first entry. Note that
- * {@link Cursor}s are not synchronized, see the documentation for more details.
- */
- @Override
- public Cursor query(String query, Object[] selectionArgs) {
- return rawQueryWithFactory(null, query, selectionArgs, null, null);
- }
-
- /**
- * Runs the provided SQL and returns a {@link Cursor} over the result set.
- *
- * @param supportQuery the SQL query.
- * @return A {@link Cursor} object, which is positioned before the first entry. Note that
- * {@link Cursor}s are not synchronized, see the documentation for more details.
- */
- @Override
- public Cursor query(final SupportSQLiteQuery supportQuery) {
- return query(supportQuery, (CancellationSignal) null);
- }
-
- /**
- * Runs the provided SQL and returns a {@link Cursor} over the result set.
- *
- * @param supportQuery the SQL query. The SQL string must not be ; terminated
- * @param signal A signal to cancel the operation in progress, or null if none.
- * If the operation is canceled, then {@link OperationCanceledException} will be thrown
- * when the query is executed.
- * @return A {@link Cursor} object, which is positioned before the first entry. Note that
- * {@link Cursor}s are not synchronized, see the documentation for more details.
- */
- @Override
- @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
- public Cursor query(SupportSQLiteQuery supportQuery, android.os.CancellationSignal signal) {
- if (signal != null) {
- final CancellationSignal supportCancellationSignal = new CancellationSignal();
- signal.setOnCancelListener(new android.os.CancellationSignal.OnCancelListener() {
- @Override
- public void onCancel() {
- supportCancellationSignal.cancel();
- }
- });
- return query(supportQuery, supportCancellationSignal);
- } else {
- return query(supportQuery, (CancellationSignal) null);
- }
- }
-
- /**
- * Runs the provided SQL and returns a {@link Cursor} over the result set.
- *
- * @param supportQuery the SQL query. The SQL string must not be ; terminated
- * @param signal A signal to cancel the operation in progress, or null if none.
- * If the operation is canceled, then {@link OperationCanceledException} will be thrown
- * when the query is executed.
- * @return A {@link Cursor} object, which is positioned before the first entry. Note that
- * {@link Cursor}s are not synchronized, see the documentation for more details.
- */
- public Cursor query(final SupportSQLiteQuery supportQuery, CancellationSignal signal) {
- return rawQueryWithFactory(new CursorFactory() {
- @Override
- public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery,
- String editTable, SQLiteQuery query) {
- supportQuery.bindTo(query);
- if (mCursorFactory == null) {
- return new SQLiteCursor(masterQuery, editTable, query);
- } else {
- return mCursorFactory.newCursor(db, masterQuery, editTable, query);
- }
- }
- }, supportQuery.getSql(), new String[0], null, signal);
- }
-
- /**
- * Runs the provided SQL and returns a {@link Cursor} over the result set.
- *
- * @param sql the SQL query. The SQL string must not be ; terminated
- * @param selectionArgs You may include ?s in where clause in the query,
- * which will be replaced by the values from selectionArgs.
- * @return A {@link Cursor} object, which is positioned before the first entry. Note that
- * {@link Cursor}s are not synchronized, see the documentation for more details.
- */
- public Cursor rawQuery(String sql, Object[] selectionArgs) {
- return rawQueryWithFactory(null, sql, selectionArgs, null, null);
- }
-
- /**
- * Runs the provided SQL and returns a {@link Cursor} over the result set.
- *
- * @param sql the SQL query. The SQL string must not be ; terminated
- * @param selectionArgs You may include ?s in where clause in the query,
- * which will be replaced by the values from selectionArgs.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * If the operation is canceled, then {@link OperationCanceledException} will be thrown
- * when the query is executed.
- * @return A {@link Cursor} object, which is positioned before the first entry. Note that
- * {@link Cursor}s are not synchronized, see the documentation for more details.
- */
- public Cursor rawQuery(String sql, Object[] selectionArgs,
- CancellationSignal cancellationSignal) {
- return rawQueryWithFactory(null, sql, selectionArgs, null, cancellationSignal);
- }
-
- /**
- * Runs the provided SQL and returns a cursor over the result set.
- *
- * @param cursorFactory the cursor factory to use, or null for the default factory
- * @param sql the SQL query. The SQL string must not be ; terminated
- * @param selectionArgs You may include ?s in where clause in the query,
- * which will be replaced by the values from selectionArgs.
- * @param editTable the name of the first table, which is editable
- * @return A {@link Cursor} object, which is positioned before the first entry. Note that
- * {@link Cursor}s are not synchronized, see the documentation for more details.
- */
- public Cursor rawQueryWithFactory(
- CursorFactory cursorFactory, String sql, Object[] selectionArgs,
- String editTable) {
- return rawQueryWithFactory(cursorFactory, sql, selectionArgs, editTable, null);
- }
-
- /**
- * Runs the provided SQL and returns a cursor over the result set.
- *
- * @param cursorFactory the cursor factory to use, or null for the default factory
- * @param sql the SQL query. The SQL string must not be ; terminated
- * @param selectionArgs You may include ?s in where clause in the query,
- * which will be replaced by the values from selectionArgs.
- * @param editTable the name of the first table, which is editable
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * If the operation is canceled, then {@link OperationCanceledException} will be thrown
- * when the query is executed.
- * @return A {@link Cursor} object, which is positioned before the first entry. Note that
- * {@link Cursor}s are not synchronized, see the documentation for more details.
- */
- public Cursor rawQueryWithFactory(
- CursorFactory cursorFactory, String sql, Object[] selectionArgs,
- String editTable, CancellationSignal cancellationSignal) {
- acquireReference();
- try {
- SQLiteCursorDriver driver = new SQLiteDirectCursorDriver(this, sql, editTable,
- cancellationSignal);
- return driver.query(cursorFactory != null ? cursorFactory : mCursorFactory,
- selectionArgs);
- } finally {
- releaseReference();
- }
- }
-
- /**
- * Convenience method for inserting a row into the database.
- *
- * @param table the table to insert the row into
- * @param nullColumnHack optional; may be null.
- * SQL doesn't allow inserting a completely empty row without
- * naming at least one column name. If your provided values is
- * empty, no column names are known and an empty row can't be inserted.
- * If not set to null, the nullColumnHack parameter
- * provides the name of nullable column name to explicitly insert a NULL into
- * in the case where your values is empty.
- * @param values this map contains the initial column values for the
- * row. The keys should be the column names and the values the
- * column values
- * @return the row ID of the newly inserted row, or -1 if an error occurred
- */
- public long insert(String table, String nullColumnHack, ContentValues values) {
- try {
- return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
- } catch (SQLException e) {
- Log.e(TAG, "Error inserting " + values, e);
- return -1;
- }
- }
-
- /**
- * Convenience method for inserting a row into the database.
- *
- * @param table the table to insert the row into
- * @param nullColumnHack optional; may be null.
- * SQL doesn't allow inserting a completely empty row without
- * naming at least one column name. If your provided values is
- * empty, no column names are known and an empty row can't be inserted.
- * If not set to null, the nullColumnHack parameter
- * provides the name of nullable column name to explicitly insert a NULL into
- * in the case where your values is empty.
- * @param values this map contains the initial column values for the
- * row. The keys should be the column names and the values the
- * column values
- * @throws SQLException
- * @return the row ID of the newly inserted row, or -1 if an error occurred
- */
- public long insertOrThrow(String table, String nullColumnHack, ContentValues values)
- throws SQLException {
- return insertWithOnConflict(table, nullColumnHack, values, CONFLICT_NONE);
- }
-
- /**
- * Convenience method for replacing a row in the database.
- *
- * @param table the table in which to replace the row
- * @param nullColumnHack optional; may be null.
- * SQL doesn't allow inserting a completely empty row without
- * naming at least one column name. If your provided initialValues is
- * empty, no column names are known and an empty row can't be inserted.
- * If not set to null, the nullColumnHack parameter
- * provides the name of nullable column name to explicitly insert a NULL into
- * in the case where your initialValues is empty.
- * @param initialValues this map contains the initial column values for
- * the row.
- * @return the row ID of the newly inserted row, or -1 if an error occurred
- */
- public long replace(String table, String nullColumnHack, ContentValues initialValues) {
- try {
- return insertWithOnConflict(table, nullColumnHack, initialValues,
- CONFLICT_REPLACE);
- } catch (SQLException e) {
- Log.e(TAG, "Error inserting " + initialValues, e);
- return -1;
- }
- }
-
- /**
- * Convenience method for replacing a row in the database.
- *
- * @param table the table in which to replace the row
- * @param nullColumnHack optional; may be null.
- * SQL doesn't allow inserting a completely empty row without
- * naming at least one column name. If your provided initialValues is
- * empty, no column names are known and an empty row can't be inserted.
- * If not set to null, the nullColumnHack parameter
- * provides the name of nullable column name to explicitly insert a NULL into
- * in the case where your initialValues is empty.
- * @param initialValues this map contains the initial column values for
- * the row. The key
- * @throws SQLException
- * @return the row ID of the newly inserted row, or -1 if an error occurred
- */
- public long replaceOrThrow(String table, String nullColumnHack,
- ContentValues initialValues) throws SQLException {
- return insertWithOnConflict(table, nullColumnHack, initialValues,
- CONFLICT_REPLACE);
- }
-
- /**
- * General method for inserting a row into the database.
- *
- * @param table the table to insert the row into
- * @param conflictAlgorithm for insert conflict resolver
- * @param values this map contains the initial column values for the
- * row. The keys should be the column names and the values the
- * column values
- * @return the row ID of the newly inserted row
- * OR the primary key of the existing row if the input param 'conflictAlgorithm' =
- * {@link #CONFLICT_IGNORE}
- * OR -1 if any error
- */
- @Override
- public long insert(String table, @ConflictAlgorithm int conflictAlgorithm,
- ContentValues values) throws SQLException {
- return insertWithOnConflict(table, null, values, conflictAlgorithm);
- }
-
- /**
- * General method for inserting a row into the database.
- *
- * @param table the table to insert the row into
- * @param nullColumnHack optional; may be null.
- * SQL doesn't allow inserting a completely empty row without
- * naming at least one column name. If your provided initialValues is
- * empty, no column names are known and an empty row can't be inserted.
- * If not set to null, the nullColumnHack parameter
- * provides the name of nullable column name to explicitly insert a NULL into
- * in the case where your initialValues is empty.
- * @param initialValues this map contains the initial column values for the
- * row. The keys should be the column names and the values the
- * column values
- * @param conflictAlgorithm for insert conflict resolver
- * @return the row ID of the newly inserted row
- * OR the primary key of the existing row if the input param 'conflictAlgorithm' =
- * {@link #CONFLICT_IGNORE}
- * OR -1 if any error
- */
- @SuppressWarnings("StringConcatenationInsideStringBufferAppend")
- public long insertWithOnConflict(String table, String nullColumnHack,
- ContentValues initialValues, @ConflictAlgorithm int conflictAlgorithm) {
- acquireReference();
- try {
- StringBuilder sql = new StringBuilder();
- sql.append("INSERT");
- sql.append(CONFLICT_VALUES[conflictAlgorithm]);
- sql.append(" INTO ");
- sql.append(table);
- sql.append('(');
-
- Object[] bindArgs = null;
- int size = (initialValues != null && initialValues.size() > 0)
- ? initialValues.size() : 0;
- if (size > 0) {
- bindArgs = new Object[size];
- int i = 0;
- for (Map.Entry entry : initialValues.valueSet()) {
- sql.append((i > 0) ? "," : "");
- sql.append(entry.getKey());
- bindArgs[i++] = entry.getValue();
- }
- sql.append(')');
- sql.append(" VALUES (");
- for (i = 0; i < size; i++) {
- sql.append((i > 0) ? ",?" : "?");
- }
- } else {
- sql.append(nullColumnHack + ") VALUES (NULL");
- }
- sql.append(')');
-
- SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
- try {
- return statement.executeInsert();
- } finally {
- statement.close();
- }
- } finally {
- releaseReference();
- }
- }
-
- /**
- * Convenience method for deleting rows in the database.
- *
- * @param table the table to delete from
- * @param whereClause the optional WHERE clause to apply when deleting.
- * Passing null will delete all rows.
- * @param whereArgs You may include ?s in the where clause, which
- * will be replaced by the values from whereArgs. The values
- * will be bound as Strings.
- * @return the number of rows affected if a whereClause is passed in, 0
- * otherwise. To remove all rows and get a count pass "1" as the
- * whereClause.
- */
- public int delete(String table, String whereClause, String[] whereArgs) {
- acquireReference();
- try {
- SQLiteStatement statement = new SQLiteStatement(this, "DELETE FROM " + table +
- (!TextUtils.isEmpty(whereClause) ? " WHERE " + whereClause : ""), whereArgs);
- try {
- return statement.executeUpdateDelete();
- } finally {
- statement.close();
- }
- } finally {
- releaseReference();
- }
- }
-
- /**
- * Convenience method for deleting rows in the database.
- *
- * @param table the table to delete from
- * @param whereClause the optional WHERE clause to apply when deleting.
- * Passing null will delete all rows.
- * @param whereArgs You may include ?s in the where clause, which
- * will be replaced by the values from whereArgs. The values
- * will be bound as Strings.
- * @return the number of rows affected if a whereClause is passed in, 0
- * otherwise. To remove all rows and get a count pass "1" as the
- * whereClause.
- */
- @Override
- public int delete(String table, String whereClause, Object[] whereArgs) {
- acquireReference();
- try {
- SQLiteStatement statement = new SQLiteStatement(this, "DELETE FROM " + table +
- (!TextUtils.isEmpty(whereClause) ? " WHERE " + whereClause : ""), whereArgs);
- try {
- return statement.executeUpdateDelete();
- } finally {
- statement.close();
- }
- } finally {
- releaseReference();
- }
- }
-
- /**
- * Convenience method for updating rows in the database.
- *
- * @param table the table to update in
- * @param values a map from column names to new column values. null is a
- * valid value that will be translated to NULL.
- * @param whereClause the optional WHERE clause to apply when updating.
- * Passing null will update all rows.
- * @param whereArgs You may include ?s in the where clause, which
- * will be replaced by the values from whereArgs. The values
- * will be bound as Strings.
- * @return the number of rows affected
- */
- public int update(String table, ContentValues values, String whereClause, String[] whereArgs) {
- return updateWithOnConflict(table, values, whereClause, whereArgs, CONFLICT_NONE);
- }
-
- /**
- * Convenience method for updating rows in the database.
- *
- * @param table the table to update in
- * @param values a map from column names to new column values. null is a
- * valid value that will be translated to NULL.
- * @param whereClause the optional WHERE clause to apply when updating.
- * Passing null will update all rows.
- * @param whereArgs You may include ?s in the where clause, which
- * will be replaced by the values from whereArgs. The values
- * will be bound as Strings.
- * @param conflictAlgorithm for update conflict resolver
- * @return the number of rows affected
- */
- @Override
- public int update(String table, @ConflictAlgorithm int conflictAlgorithm, ContentValues values,
- String whereClause, Object[] whereArgs) {
- if (values == null || values.size() == 0) {
- throw new IllegalArgumentException("Empty values");
- }
-
- acquireReference();
- try {
- StringBuilder sql = new StringBuilder(120);
- sql.append("UPDATE ");
- sql.append(CONFLICT_VALUES[conflictAlgorithm]);
- sql.append(table);
- sql.append(" SET ");
-
- // move all bind args to one array
- int setValuesSize = values.size();
- int bindArgsSize = (whereArgs == null) ? setValuesSize : (setValuesSize + whereArgs.length);
- Object[] bindArgs = new Object[bindArgsSize];
- int i = 0;
- for (Map.Entry entry : values.valueSet()) {
- sql.append((i > 0) ? "," : "");
- sql.append(entry.getKey());
- bindArgs[i++] = entry.getValue();
- sql.append("=?");
- }
- if (whereArgs != null) {
- for (i = setValuesSize; i < bindArgsSize; i++) {
- bindArgs[i] = whereArgs[i - setValuesSize];
- }
- }
- if (!TextUtils.isEmpty(whereClause)) {
- sql.append(" WHERE ");
- sql.append(whereClause);
- }
-
- SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
- try {
- return statement.executeUpdateDelete();
- } finally {
- statement.close();
- }
- } finally {
- releaseReference();
- }
- }
-
- /**
- * Convenience method for updating rows in the database.
- *
- * @param table the table to update in
- * @param values a map from column names to new column values. null is a
- * valid value that will be translated to NULL.
- * @param whereClause the optional WHERE clause to apply when updating.
- * Passing null will update all rows.
- * @param whereArgs You may include ?s in the where clause, which
- * will be replaced by the values from whereArgs. The values
- * will be bound as Strings.
- * @param conflictAlgorithm for update conflict resolver
- * @return the number of rows affected
- */
- public int updateWithOnConflict(String table, ContentValues values,
- String whereClause, String[] whereArgs, @ConflictAlgorithm int conflictAlgorithm) {
- if (values == null || values.size() == 0) {
- throw new IllegalArgumentException("Empty values");
- }
-
- acquireReference();
- try {
- StringBuilder sql = new StringBuilder(120);
- sql.append("UPDATE ");
- sql.append(CONFLICT_VALUES[conflictAlgorithm]);
- sql.append(table);
- sql.append(" SET ");
-
- // move all bind args to one array
- int setValuesSize = values.size();
- int bindArgsSize = (whereArgs == null) ? setValuesSize : (setValuesSize + whereArgs.length);
- Object[] bindArgs = new Object[bindArgsSize];
- int i = 0;
- for (Map.Entry entry : values.valueSet()) {
- sql.append((i > 0) ? "," : "");
- sql.append(entry.getKey());
- bindArgs[i++] = entry.getValue();
- sql.append("=?");
- }
- if (whereArgs != null) {
- for (i = setValuesSize; i < bindArgsSize; i++) {
- bindArgs[i] = whereArgs[i - setValuesSize];
- }
- }
- if (!TextUtils.isEmpty(whereClause)) {
- sql.append(" WHERE ");
- sql.append(whereClause);
- }
-
- SQLiteStatement statement = new SQLiteStatement(this, sql.toString(), bindArgs);
- try {
- return statement.executeUpdateDelete();
- } finally {
- statement.close();
- }
- } finally {
- releaseReference();
- }
- }
-
- /**
- * Execute a single SQL statement that is NOT a SELECT
- * or any other SQL statement that returns data.
- *
- * It has no means to return any data (such as the number of affected rows).
- * Instead, you're encouraged to use {@link #insert(String, String, ContentValues)},
- * {@link #update(String, ContentValues, String, String[])}, et al, when possible.
- *
- *
- * When using {@link #enableWriteAheadLogging()}, journal_mode is
- * automatically managed by this class. So, do not set journal_mode
- * using "PRAGMA journal_mode'" statement if your app is using
- * {@link #enableWriteAheadLogging()}
- *
- *
- * @param sql the SQL statement to be executed. Multiple statements separated by semicolons are
- * not supported.
- * @throws SQLException if the SQL string is invalid
- */
- @Override
- public void execSQL(String sql) throws SQLException {
- executeSql(sql, null);
- }
-
- /**
- * Execute a single SQL statement that is NOT a SELECT/INSERT/UPDATE/DELETE.
- *
- * For INSERT statements, use any of the following instead.
- *
- * {@link #insert(String, String, ContentValues)}
- * {@link #insertOrThrow(String, String, ContentValues)}
- * {@link #insertWithOnConflict(String, String, ContentValues, int)}
- *
- *
- * For UPDATE statements, use any of the following instead.
- *
- * {@link #update(String, ContentValues, String, String[])}
- * {@link #updateWithOnConflict(String, ContentValues, String, String[], int)}
- *
- *
- * For DELETE statements, use any of the following instead.
- *
- * {@link #delete(String, String, String[])}
- *
- *
- * For example, the following are good candidates for using this method:
- *
- * ALTER TABLE
- * CREATE or DROP table / trigger / view / index / virtual table
- * REINDEX
- * RELEASE
- * SAVEPOINT
- * PRAGMA that returns no data
- *
- *
- *
- * When using {@link #enableWriteAheadLogging()}, journal_mode is
- * automatically managed by this class. So, do not set journal_mode
- * using "PRAGMA journal_mode'" statement if your app is using
- * {@link #enableWriteAheadLogging()}
- *
- *
- * @param sql the SQL statement to be executed. Multiple statements separated by semicolons are
- * not supported.
- * @param bindArgs only byte[], String, Long and Double are supported in bindArgs.
- * @throws SQLException if the SQL string is invalid
- */
- @Override
- public void execSQL(String sql, Object[] bindArgs) throws SQLException {
- if (bindArgs == null) {
- throw new IllegalArgumentException("Empty bindArgs");
- }
- executeSql(sql, bindArgs);
- }
-
- private int executeSql(String sql, Object[] bindArgs) throws SQLException {
- acquireReference();
- try {
- SQLiteStatement statement = new SQLiteStatement(this, sql, bindArgs);
- try {
- return statement.executeUpdateDelete();
- } finally {
- statement.close();
- }
- } finally {
- releaseReference();
- }
- }
-
- /**
- * Verifies that a SQL SELECT statement is valid by compiling it.
- * If the SQL statement is not valid, this method will throw a {@link SQLiteException}.
- *
- * @param sql SQL to be validated
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * If the operation is canceled, then {@link OperationCanceledException} will be thrown
- * when the query is executed.
- * @throws SQLiteException if {@code sql} is invalid
- */
- public void validateSql(@NonNull String sql, @Nullable CancellationSignal cancellationSignal) {
- getThreadSession().prepare(sql,
- getThreadDefaultConnectionFlags(true), cancellationSignal, null);
- }
-
- /**
- * Returns true if the database is opened as read only.
- *
- * @return True if database is opened as read only.
- */
- @Override
- public boolean isReadOnly() {
- synchronized (mLock) {
- return isReadOnlyLocked();
- }
- }
-
- private boolean isReadOnlyLocked() {
- return (mConfigurationLocked.openFlags & OPEN_READONLY) == OPEN_READONLY;
- }
-
- /**
- * Returns true if the database is in-memory db.
- *
- * @return True if the database is in-memory.
- * @hide
- */
- public boolean isInMemoryDatabase() {
- synchronized (mLock) {
- return mConfigurationLocked.isInMemoryDb();
- }
- }
-
- /**
- * Returns true if the database is currently open.
- *
- * @return True if the database is currently open (has not been closed).
- */
- @Override
- public boolean isOpen() {
- synchronized (mLock) {
- return mConnectionPoolLocked != null;
- }
- }
-
- /**
- * Returns true if the new version code is greater than the current database version.
- *
- * @param newVersion The new version code.
- * @return True if the new version code is greater than the current database version.
- */
- @Override
- public boolean needUpgrade(int newVersion) {
- return newVersion > getVersion();
- }
-
- /**
- * Gets the path to the database file.
- *
- * @return The path to the database file.
- */
- @Override
- public final String getPath() {
- synchronized (mLock) {
- return mConfigurationLocked.path;
- }
- }
-
- /**
- * Sets the locale for this database.
- *
- * @param locale The new locale.
- *
- * @throws SQLException if the locale could not be set. The most common reason
- * for this is that there is no collator available for the locale you requested.
- * In this case the database remains unchanged.
- */
- @Override
- public void setLocale(Locale locale) {
- if (locale == null) {
- throw new IllegalArgumentException("locale must not be null.");
- }
-
- synchronized (mLock) {
- throwIfNotOpenLocked();
-
- final Locale oldLocale = mConfigurationLocked.locale;
- mConfigurationLocked.locale = locale;
- try {
- mConnectionPoolLocked.reconfigure(mConfigurationLocked);
- } catch (RuntimeException ex) {
- mConfigurationLocked.locale = oldLocale;
- throw ex;
- }
- }
- }
-
- /**
- * Sets the maximum size of the prepared-statement cache for this database.
- * (size of the cache = number of compiled-sql-statements stored in the cache).
- *
- * Maximum cache size can ONLY be increased from its current size (default = 10).
- * If this method is called with smaller size than the current maximum value,
- * then IllegalStateException is thrown.
- *
- * This method is thread-safe.
- *
- * @param cacheSize the size of the cache. can be (0 to {@link #MAX_SQL_CACHE_SIZE})
- * @throws IllegalStateException if input cacheSize > {@link #MAX_SQL_CACHE_SIZE}.
- */
- @Override
- public void setMaxSqlCacheSize(int cacheSize) {
- if (cacheSize > MAX_SQL_CACHE_SIZE || cacheSize < 0) {
- throw new IllegalStateException(
- "expected value between 0 and " + MAX_SQL_CACHE_SIZE);
- }
-
- synchronized (mLock) {
- throwIfNotOpenLocked();
-
- final int oldMaxSqlCacheSize = mConfigurationLocked.maxSqlCacheSize;
- mConfigurationLocked.maxSqlCacheSize = cacheSize;
- try {
- mConnectionPoolLocked.reconfigure(mConfigurationLocked);
- } catch (RuntimeException ex) {
- mConfigurationLocked.maxSqlCacheSize = oldMaxSqlCacheSize;
- throw ex;
- }
- }
- }
-
- /**
- * Sets whether foreign key constraints are enabled for the database.
- *
- * By default, foreign key constraints are not enforced by the database.
- * This method allows an application to enable foreign key constraints.
- * It must be called each time the database is opened to ensure that foreign
- * key constraints are enabled for the session.
- *
- * A good time to call this method is right after calling {@link #openOrCreateDatabase}
- * or in the {@link SQLiteOpenHelper#onConfigure} callback.
- *
- * When foreign key constraints are disabled, the database does not check whether
- * changes to the database will violate foreign key constraints. Likewise, when
- * foreign key constraints are disabled, the database will not execute cascade
- * delete or update triggers. As a result, it is possible for the database
- * state to become inconsistent. To perform a database integrity check,
- * call {@link #isDatabaseIntegrityOk}.
- *
- * This method must not be called while a transaction is in progress.
- *
- * See also SQLite Foreign Key Constraints
- * for more details about foreign key constraint support.
- *
- *
- * @param enable True to enable foreign key constraints, false to disable them.
- *
- * @throws IllegalStateException if the are transactions is in progress
- * when this method is called.
- */
- @Override
- public void setForeignKeyConstraintsEnabled(boolean enable) {
- synchronized (mLock) {
- throwIfNotOpenLocked();
-
- if (mConfigurationLocked.foreignKeyConstraintsEnabled == enable) {
- return;
- }
-
- mConfigurationLocked.foreignKeyConstraintsEnabled = enable;
- try {
- mConnectionPoolLocked.reconfigure(mConfigurationLocked);
- } catch (RuntimeException ex) {
- mConfigurationLocked.foreignKeyConstraintsEnabled = !enable;
- throw ex;
- }
- }
- }
-
- /**
- * This method enables parallel execution of queries from multiple threads on the
- * same database. It does this by opening multiple connections to the database
- * and using a different database connection for each query. The database
- * journal mode is also changed to enable writes to proceed concurrently with reads.
- *
- * When write-ahead logging is not enabled (the default), it is not possible for
- * reads and writes to occur on the database at the same time. Before modifying the
- * database, the writer implicitly acquires an exclusive lock on the database which
- * prevents readers from accessing the database until the write is completed.
- *
- * In contrast, when write-ahead logging is enabled (by calling this method), write
- * operations occur in a separate log file which allows reads to proceed concurrently.
- * While a write is in progress, readers on other threads will perceive the state
- * of the database as it was before the write began. When the write completes, readers
- * on other threads will then perceive the new state of the database.
- *
- * It is a good idea to enable write-ahead logging whenever a database will be
- * concurrently accessed and modified by multiple threads at the same time.
- * However, write-ahead logging uses significantly more memory than ordinary
- * journaling because there are multiple connections to the same database.
- * So if a database will only be used by a single thread, or if optimizing
- * concurrency is not very important, then write-ahead logging should be disabled.
- *
- * After calling this method, execution of queries in parallel is enabled as long as
- * the database remains open. To disable execution of queries in parallel, either
- * call {@link #disableWriteAheadLogging} or close the database and reopen it.
- *
- * The maximum number of connections used to execute queries in parallel is
- * dependent upon the device memory and possibly other properties.
- *
- * If a query is part of a transaction, then it is executed on the same database handle the
- * transaction was begun.
- *
- * Writers should use {@link #beginTransactionNonExclusive()} or
- * {@link #beginTransactionWithListenerNonExclusive(SQLiteTransactionListener)}
- * to start a transaction. Non-exclusive mode allows database file to be in readable
- * by other threads executing queries.
- *
- * If the database has any attached databases, then execution of queries in parallel is NOT
- * possible. Likewise, write-ahead logging is not supported for read-only databases
- * or memory databases. In such cases, {@link #enableWriteAheadLogging} returns false.
- *
- * The best way to enable write-ahead logging is to pass the
- * {@link #ENABLE_WRITE_AHEAD_LOGGING} flag to {@link #openDatabase}. This is
- * more efficient than calling {@link #enableWriteAheadLogging}.
- *
- * SQLiteDatabase db = SQLiteDatabase.openDatabase("db_filename", cursorFactory,
- * SQLiteDatabase.CREATE_IF_NECESSARY | SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING,
- * myDatabaseErrorHandler);
- * db.enableWriteAheadLogging();
- *
- *
- * Another way to enable write-ahead logging is to call {@link #enableWriteAheadLogging}
- * after opening the database.
- *
- * SQLiteDatabase db = SQLiteDatabase.openDatabase("db_filename", cursorFactory,
- * SQLiteDatabase.CREATE_IF_NECESSARY, myDatabaseErrorHandler);
- * db.enableWriteAheadLogging();
- *
- *
- * See also SQLite Write-Ahead Logging for
- * more details about how write-ahead logging works.
- *
- *
- * @return True if write-ahead logging is enabled.
- *
- * @throws IllegalStateException if there are transactions in progress at the
- * time this method is called. WAL mode can only be changed when there are no
- * transactions in progress.
- *
- * @see #ENABLE_WRITE_AHEAD_LOGGING
- * @see #disableWriteAheadLogging
- */
- @Override
- public boolean enableWriteAheadLogging() {
- synchronized (mLock) {
- throwIfNotOpenLocked();
-
- if ((mConfigurationLocked.openFlags & ENABLE_WRITE_AHEAD_LOGGING) != 0) {
- return true;
- }
-
- if (isReadOnlyLocked()) {
- // WAL doesn't make sense for readonly-databases.
- // TODO: True, but connection pooling does still make sense...
- return false;
- }
-
- if (mConfigurationLocked.isInMemoryDb()) {
- Log.i(TAG, "can't enable WAL for memory databases.");
- return false;
- }
-
- mConfigurationLocked.openFlags |= ENABLE_WRITE_AHEAD_LOGGING;
- try {
- mConnectionPoolLocked.reconfigure(mConfigurationLocked);
- } catch (RuntimeException ex) {
- mConfigurationLocked.openFlags &= ~ENABLE_WRITE_AHEAD_LOGGING;
- throw ex;
- }
- }
- return true;
- }
-
- /**
- * This method disables the features enabled by {@link #enableWriteAheadLogging()}.
- *
- * @throws IllegalStateException if there are transactions in progress at the
- * time this method is called. WAL mode can only be changed when there are no
- * transactions in progress.
- *
- * @see #enableWriteAheadLogging
- */
- @Override
- public void disableWriteAheadLogging() {
- synchronized (mLock) {
- throwIfNotOpenLocked();
-
- if ((mConfigurationLocked.openFlags & ENABLE_WRITE_AHEAD_LOGGING) == 0) {
- return;
- }
-
- mConfigurationLocked.openFlags &= ~ENABLE_WRITE_AHEAD_LOGGING;
- try {
- mConnectionPoolLocked.reconfigure(mConfigurationLocked);
- } catch (RuntimeException ex) {
- mConfigurationLocked.openFlags |= ENABLE_WRITE_AHEAD_LOGGING;
- throw ex;
- }
- }
- }
-
- /**
- * Returns true if write-ahead logging has been enabled for this database.
- *
- * @return True if write-ahead logging has been enabled for this database.
- *
- * @see #enableWriteAheadLogging
- * @see #ENABLE_WRITE_AHEAD_LOGGING
- */
- @Override
- public boolean isWriteAheadLoggingEnabled() {
- synchronized (mLock) {
- throwIfNotOpenLocked();
-
- return (mConfigurationLocked.openFlags & ENABLE_WRITE_AHEAD_LOGGING) != 0;
- }
- }
-
- /**
- * Collect statistics about all open databases in the current process.
- * Used by bug report.
- */
- static ArrayList getDbStats() {
- ArrayList dbStatsList = new ArrayList<>();
- for (SQLiteDatabase db : getActiveDatabases()) {
- db.collectDbStats(dbStatsList);
- }
- return dbStatsList;
- }
-
- private void collectDbStats(ArrayList dbStatsList) {
- synchronized (mLock) {
- if (mConnectionPoolLocked != null) {
- mConnectionPoolLocked.collectDbStats(dbStatsList);
- }
- }
- }
-
- private static ArrayList getActiveDatabases() {
- ArrayList databases = new ArrayList<>();
- synchronized (sActiveDatabases) {
- databases.addAll(sActiveDatabases.keySet());
- }
- return databases;
- }
-
- /**
- * Dump detailed information about all open databases in the current process.
- * Used by bug report.
- */
- static void dumpAll(Printer printer, boolean verbose) {
- for (SQLiteDatabase db : getActiveDatabases()) {
- db.dump(printer, verbose);
- }
- }
-
- private void dump(Printer printer, boolean verbose) {
- synchronized (mLock) {
- if (mConnectionPoolLocked != null) {
- printer.println("");
- mConnectionPoolLocked.dump(printer, verbose);
- }
- }
- }
-
- /**
- * Returns list of full pathnames of all attached databases including the main database
- * by executing 'pragma database_list' on the database.
- *
- * @return ArrayList of pairs of (database name, database file path) or null if the database
- * is not open.
- */
- @Override
- public List> getAttachedDbs() {
- ArrayList> attachedDbs = new ArrayList<>();
- synchronized (mLock) {
- if (mConnectionPoolLocked == null) {
- return null; // not open
- }
-
- acquireReference();
- }
-
- try {
- // has attached databases. query sqlite to get the list of attached databases.
- Cursor c = null;
- try {
- c = rawQuery("pragma database_list;", null);
- while (c.moveToNext()) {
- // sqlite returns a row for each database in the returned list of databases.
- // in each row,
- // 1st column is the database name such as main, or the database
- // name specified on the "ATTACH" command
- // 2nd column is the database file path.
- attachedDbs.add(new Pair<>(c.getString(1), c.getString(2)));
- }
- } finally {
- if (c != null) {
- c.close();
- }
- }
- return attachedDbs;
- } finally {
- releaseReference();
- }
- }
-
- /**
- * Runs 'pragma integrity_check' on the given database (and all the attached databases)
- * and returns true if the given database (and all its attached databases) pass integrity_check,
- * false otherwise.
- *
- * If the result is false, then this method logs the errors reported by the integrity_check
- * command execution.
- *
- * Note that 'pragma integrity_check' on a database can take a long time.
- *
- * @return true if the given database (and all its attached databases) pass integrity_check,
- * false otherwise.
- */
- @Override
- public boolean isDatabaseIntegrityOk() {
- acquireReference();
- try {
- List> attachedDbs;
- try {
- attachedDbs = getAttachedDbs();
- if (attachedDbs == null) {
- throw new IllegalStateException("databaselist for: " + getPath() + " couldn't " +
- "be retrieved. probably because the database is closed");
- }
- } catch (SQLiteException e) {
- // can't get attachedDb list. do integrity check on the main database
- attachedDbs = new ArrayList<>();
- attachedDbs.add(new Pair<>("main", getPath()));
- }
-
- for (Pair p : attachedDbs) {
- SQLiteStatement prog = null;
- try {
- prog = compileStatement("PRAGMA " + p.first + ".integrity_check(1);");
- String rslt = prog.simpleQueryForString();
- if (!rslt.equalsIgnoreCase("ok")) {
- // integrity_checker failed on main or attached databases
- Log.e(TAG, "PRAGMA integrity_check on " + p.second + " returned: " + rslt);
- return false;
- }
- } finally {
- if (prog != null) prog.close();
- }
- }
- } finally {
- releaseReference();
- }
- return true;
- }
-
- @Override
- public String toString() {
- return "SQLiteDatabase: " + getPath();
- }
-
- private void throwIfNotOpenLocked() {
- if (mConnectionPoolLocked == null) {
- throw new IllegalStateException("The database '" + mConfigurationLocked.label
- + "' is not open.");
- }
- }
-
- /**
- * Used to allow returning sub-classes of {@link Cursor} when calling query.
- */
- public interface CursorFactory {
- /**
- * See {@link SQLiteCursor#SQLiteCursor(SQLiteCursorDriver, String, SQLiteQuery)}.
- */
- Cursor newCursor(SQLiteDatabase db,
- SQLiteCursorDriver masterQuery, String editTable,
- SQLiteQuery query);
- }
-
- /**
- * A callback interface for a custom sqlite3 function. This can be used to create a function
- * that can be called from sqlite3 database triggers.
- *
- * This interface is deprecated; new code should prefer {@link Function}
- */
- @Deprecated
- public interface CustomFunction {
- /**
- * Invoked whenever the function is called.
- * @param args function arguments
- * @return String value of the result or null
- */
- String callback(String[] args);
- }
-
- /**
- * A callback interface for a custom sqlite3 function. This can be used to create a function
- * that can be called from sqlite3 database triggers, or used in queries.
- */
- public interface Function {
- /**
- * Flag that declares this function to be "deterministic,"
- * which means it may be used with Indexes on Expressions.
- */
- public static final int FLAG_DETERMINISTIC = 0x800;
-
- interface Args {
- byte[] getBlob(int arg);
- String getString(int arg);
- double getDouble(int arg);
- int getInt(int arg);
- long getLong(int arg);
- }
-
- interface Result {
- void set(byte[] value);
- void set(double value);
- void set(int value);
- void set(long value);
- void set(String value);
- void setError(String error);
- void setNull();
- }
-
- /**
- * Invoked whenever the function is called.
- * @param args function arguments
- * @return String value of the result or null
- */
- void callback(Args args, Result result);
- }
-
- static boolean hasCodec() {
- return SQLiteConnection.hasCodec();
- }
-
- void enableLocalizedCollators() {
- mConnectionPoolLocked.enableLocalizedCollators();
- }
-
- /**
- * Query the table for the number of rows in the table.
- * @param table the name of the table to query
- * @return the number of rows in the table
- */
- public long queryNumEntries(String table) {
- return queryNumEntries(table, null, null);
- }
-
- /**
- * Query the table for the number of rows in the table.
- * @param table the name of the table to query
- * @param selection A filter declaring which rows to return,
- * formatted as an SQL WHERE clause (excluding the WHERE itself).
- * Passing null will count all rows for the given table
- * @return the number of rows in the table filtered by the selection
- */
- public long queryNumEntries(String table, String selection) {
- return queryNumEntries(table, selection, null);
- }
-
- /**
- * Query the table for the number of rows in the table.
- * @param table the name of the table to query
- * @param selection A filter declaring which rows to return,
- * formatted as an SQL WHERE clause (excluding the WHERE itself).
- * Passing null will count all rows for the given table
- * @param selectionArgs You may include ?s in selection,
- * which will be replaced by the values from selectionArgs,
- * in order that they appear in the selection.
- * The values will be bound as Strings.
- * @return the number of rows in the table filtered by the selection
- */
- public long queryNumEntries(String table, String selection, String[] selectionArgs) {
- String s = (!TextUtils.isEmpty(selection)) ? " where " + selection : "";
- return longForQuery("select count(*) from " + table + s, selectionArgs);
- }
-
- /**
- * Utility method to run the query on the db and return the value in the
- * first column of the first row.
- */
- public long longForQuery(String query, String[] selectionArgs) {
- SQLiteStatement prog = compileStatement(query);
- try {
- return longForQuery(prog, selectionArgs);
- } finally {
- prog.close();
- }
- }
-
- /**
- * Utility method to run the pre-compiled query and return the value in the
- * first column of the first row.
- */
- private static long longForQuery(SQLiteStatement prog, String[] selectionArgs) {
- prog.bindAllArgsAsStrings(selectionArgs);
- return prog.simpleQueryForLong();
- }
-
- /**
- * Utility method to run the query on the db and return the value in the
- * first column of the first row.
- */
- public String stringForQuery(String query, String[] selectionArgs) {
- SQLiteStatement prog = compileStatement(query);
- try {
- return stringForQuery(prog, selectionArgs);
- } finally {
- prog.close();
- }
- }
-
- /**
- * Utility method to run the pre-compiled query and return the value in the
- * first column of the first row.
- */
- public static String stringForQuery(SQLiteStatement prog, String[] selectionArgs) {
- prog.bindAllArgsAsStrings(selectionArgs);
- return prog.simpleQueryForString();
- }
-
- /**
- * Utility method to run the query on the db and return the blob value in the
- * first column of the first row.
- *
- * @return A read-only file descriptor for a copy of the blob value.
- */
- public ParcelFileDescriptor blobFileDescriptorForQuery(String query, String[] selectionArgs) {
- SQLiteStatement prog = compileStatement(query);
- try {
- return blobFileDescriptorForQuery(prog, selectionArgs);
- } finally {
- prog.close();
- }
- }
-
- /**
- * Utility method to run the pre-compiled query and return the blob value in the
- * first column of the first row.
- *
- * @return A read-only file descriptor for a copy of the blob value.
- */
- public static ParcelFileDescriptor blobFileDescriptorForQuery(SQLiteStatement prog,
- String[] selectionArgs) {
- prog.bindAllArgsAsStrings(selectionArgs);
- return prog.simpleQueryForBlobFileDescriptor();
- }
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteDatabaseConfiguration.java b/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteDatabaseConfiguration.java
deleted file mode 100644
index 2087f2bb8e..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteDatabaseConfiguration.java
+++ /dev/null
@@ -1,203 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-
-package io.requery.android.database.sqlite;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-import java.util.regex.Pattern;
-
-/**
- * Describes how to configure a database.
- *
- * The purpose of this object is to keep track of all of the little
- * configuration settings that are applied to a database after it
- * is opened so that they can be applied to all connections in the
- * connection pool uniformly.
- *
- * Each connection maintains its own copy of this object so it can
- * keep track of which settings have already been applied.
- *
- *
- * @hide
- */
-public final class SQLiteDatabaseConfiguration {
- // The pattern we use to strip email addresses from database paths
- // when constructing a label to use in log messages.
- private static final Pattern EMAIL_IN_DB_PATTERN =
- Pattern.compile("[\\w\\.\\-]+@[\\w\\.\\-]+");
-
- /**
- * Special path used by in-memory databases.
- */
- public static final String MEMORY_DB_PATH = ":memory:";
-
- /**
- * The database path.
- */
- public final String path;
-
- /**
- * The label to use to describe the database when it appears in logs.
- * This is derived from the path but is stripped to remove PII.
- */
- public final String label;
-
- /**
- * The flags used to open the database.
- */
- public @SQLiteDatabase.OpenFlags int openFlags;
-
- /**
- * The maximum size of the prepared statement cache for each database connection.
- * Must be non-negative.
- *
- * Default is 25.
- */
- public int maxSqlCacheSize;
-
- /**
- * The database locale.
- *
- * Default is the value returned by {@link Locale#getDefault()}.
- */
- public Locale locale;
-
- /**
- * True if foreign key constraints are enabled.
- *
- * Default is false.
- */
- public boolean foreignKeyConstraintsEnabled;
-
- /**
- * The custom functions to register.
- *
- * This interface is deprecated; see {@link SQLiteFunction}
- */
- @Deprecated
- public final List customFunctions = new ArrayList<>();
-
- /**
- * The {@link SQLiteFunction}s to register.
- */
- public final List functions = new ArrayList<>();
-
- /**
- * The custom extensions to register.
- */
- public final List customExtensions = new ArrayList<>();
-
- /**
- * Creates a database configuration with the required parameters for opening a
- * database and default values for all other parameters.
- *
- * @param path The database path.
- * @param openFlags Open flags for the database, such as {@link SQLiteDatabase#OPEN_READWRITE}.
- */
- public SQLiteDatabaseConfiguration(String path, @SQLiteDatabase.OpenFlags int openFlags) {
- if (path == null) {
- throw new IllegalArgumentException("path must not be null.");
- }
-
- this.path = path;
- label = stripPathForLogs(path);
- this.openFlags = openFlags;
-
- // Set default values for optional parameters.
- maxSqlCacheSize = 25;
- locale = Locale.getDefault();
- }
-
- /**
- * Creates a database configuration with the required parameters for opening a
- * database and default values for all other parameters.
- *
- * @param path The database path.
- * @param openFlags Open flags for the database, such as {@link SQLiteDatabase#OPEN_READWRITE}.
- * @param functions custom functions to use.
- * @param extensions custom extensions to use.
- */
- public SQLiteDatabaseConfiguration(String path,
- @SQLiteDatabase.OpenFlags int openFlags,
- List customFunctions,
- List functions,
- List extensions) {
- this(path, openFlags);
- this.customFunctions.addAll(customFunctions);
- this.customExtensions.addAll(extensions);
- this.functions.addAll(functions);
- }
-
- /**
- * Creates a database configuration as a copy of another configuration.
- *
- * @param other The other configuration.
- */
- SQLiteDatabaseConfiguration(SQLiteDatabaseConfiguration other) {
- if (other == null) {
- throw new IllegalArgumentException("other must not be null.");
- }
-
- this.path = other.path;
- this.label = other.label;
- updateParametersFrom(other);
- }
-
- /**
- * Updates the non-immutable parameters of this configuration object
- * from the other configuration object.
- *
- * @param other The object from which to copy the parameters.
- */
- void updateParametersFrom(SQLiteDatabaseConfiguration other) {
- if (other == null) {
- throw new IllegalArgumentException("other must not be null.");
- }
- if (!path.equals(other.path)) {
- throw new IllegalArgumentException("other configuration must refer to "
- + "the same database.");
- }
-
- openFlags = other.openFlags;
- maxSqlCacheSize = other.maxSqlCacheSize;
- locale = other.locale;
- foreignKeyConstraintsEnabled = other.foreignKeyConstraintsEnabled;
- customFunctions.clear();
- customFunctions.addAll(other.customFunctions);
- customExtensions.clear();
- customExtensions.addAll(other.customExtensions);
- functions.clear();
- functions.addAll(other.functions);
- }
-
- /**
- * Returns true if the database is in-memory.
- * @return True if the database is in-memory.
- */
- public boolean isInMemoryDb() {
- return path.equalsIgnoreCase(MEMORY_DB_PATH);
- }
-
- private static String stripPathForLogs(String path) {
- if (path.indexOf('@') == -1) {
- return path;
- }
- return EMAIL_IN_DB_PATTERN.matcher(path).replaceAll("XX@YY");
- }
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteDebug.java b/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteDebug.java
deleted file mode 100644
index 7a398894bb..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteDebug.java
+++ /dev/null
@@ -1,173 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-
-package io.requery.android.database.sqlite;
-
-import android.util.Log;
-import android.util.Printer;
-
-import java.util.ArrayList;
-
-/**
- * Provides debugging info about all SQLite databases running in the current process.
- *
- * {@hide}
- */
-@SuppressWarnings("unused")
-public final class SQLiteDebug {
- private static native void nativeGetPagerStats(PagerStats stats);
-
- /**
- * Controls the printing of informational SQL log messages.
- *
- * Enable using "adb shell setprop log.tag.SQLiteLog VERBOSE".
- */
- public static final boolean DEBUG_SQL_LOG =
- Log.isLoggable("SQLiteLog", Log.VERBOSE);
-
- /**
- * Controls the printing of SQL statements as they are executed.
- *
- * Enable using "adb shell setprop log.tag.SQLiteStatements VERBOSE".
- */
- public static final boolean DEBUG_SQL_STATEMENTS =
- Log.isLoggable("SQLiteStatements", Log.VERBOSE);
-
- /**
- * Controls the printing of wall-clock time taken to execute SQL statements
- * as they are executed.
- *
- * Enable using "adb shell setprop log.tag.SQLiteTime VERBOSE".
- */
- public static final boolean DEBUG_SQL_TIME =
- Log.isLoggable("SQLiteTime", Log.VERBOSE);
-
- /**
- * True to enable database performance testing instrumentation.
- * @hide
- */
- public static final boolean DEBUG_LOG_SLOW_QUERIES = false;
-
- private SQLiteDebug() {
- }
-
- /**
- * Determines whether a query should be logged.
- *
- * Reads the "db.log.slow_query_threshold" system property, which can be changed
- * by the user at any time. If the value is zero, then all queries will
- * be considered slow. If the value does not exist or is negative, then no queries will
- * be considered slow.
- *
- * This value can be changed dynamically while the system is running.
- * For example, "adb shell setprop db.log.slow_query_threshold 200" will
- * log all queries that take 200ms or longer to run.
- * @hide
- */
- public static boolean shouldLogSlowQuery(long elapsedTimeMillis) {
- int slowQueryMillis = Integer.parseInt(
- System.getProperty("db.log.slow_query_threshold", "-1"));
- return slowQueryMillis >= 0 && elapsedTimeMillis >= slowQueryMillis;
- }
-
- /**
- * Contains statistics about the active pagers in the current process.
- *
- * @see #nativeGetPagerStats(PagerStats)
- */
- public static class PagerStats {
- /** the current amount of memory checked out by sqlite using sqlite3_malloc().
- * documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html
- */
- public int memoryUsed;
-
- /** the number of bytes of page cache allocation which could not be sattisfied by the
- * SQLITE_CONFIG_PAGECACHE buffer and where forced to overflow to sqlite3_malloc().
- * The returned value includes allocations that overflowed because they where too large
- * (they were larger than the "sz" parameter to SQLITE_CONFIG_PAGECACHE) and allocations
- * that overflowed because no space was left in the page cache.
- * documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html
- */
- public int pageCacheOverflow;
-
- /** records the largest memory allocation request handed to sqlite3.
- * documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html
- */
- public int largestMemAlloc;
-
- /** a list of {@link DbStats} - one for each main database opened by the applications
- * running on the android device
- */
- public ArrayList dbStats;
- }
-
- /**
- * contains statistics about a database
- */
- public static class DbStats {
- /** name of the database */
- public String dbName;
-
- /** the page size for the database */
- public long pageSize;
-
- /** the database size */
- public long dbSize;
-
- /** documented here http://www.sqlite.org/c3ref/c_dbstatus_lookaside_used.html */
- public int lookaside;
-
- /** statement cache stats: hits/misses/cachesize */
- public String cache;
-
- public DbStats(String dbName, long pageCount, long pageSize, int lookaside,
- int hits, int misses, int cachesize) {
- this.dbName = dbName;
- this.pageSize = pageSize / 1024;
- dbSize = (pageCount * pageSize) / 1024;
- this.lookaside = lookaside;
- this.cache = hits + "/" + misses + "/" + cachesize;
- }
- }
-
- /**
- * return all pager and database stats for the current process.
- * @return {@link PagerStats}
- */
- public static PagerStats getDatabaseInfo() {
- PagerStats stats = new PagerStats();
- nativeGetPagerStats(stats);
- stats.dbStats = SQLiteDatabase.getDbStats();
- return stats;
- }
-
- /**
- * Dumps detailed information about all databases used by the process.
- * @param printer The printer for dumping database state.
- * @param args Command-line arguments supplied to dumpsys dbinfo
- */
- public static void dump(Printer printer, String[] args) {
- boolean verbose = false;
- for (String arg : args) {
- if (arg.equals("-v")) {
- verbose = true;
- }
- }
-
- SQLiteDatabase.dumpAll(printer, verbose);
- }
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteDirectCursorDriver.java b/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteDirectCursorDriver.java
deleted file mode 100644
index 1b69858c10..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteDirectCursorDriver.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-
-package io.requery.android.database.sqlite;
-
-import android.database.Cursor;
-import androidx.core.os.CancellationSignal;
-
-/**
- * A cursor driver that uses the given query directly.
- *
- * @hide
- */
-public final class SQLiteDirectCursorDriver implements SQLiteCursorDriver {
- private final SQLiteDatabase mDatabase;
- private final String mEditTable;
- private final String mSql;
- private final CancellationSignal mCancellationSignal;
- private SQLiteQuery mQuery;
-
- public SQLiteDirectCursorDriver(SQLiteDatabase db, String sql, String editTable,
- CancellationSignal cancellationSignal) {
- mDatabase = db;
- mEditTable = editTable;
- mSql = sql;
- mCancellationSignal = cancellationSignal;
- }
-
- public Cursor query(SQLiteDatabase.CursorFactory factory, Object[] selectionArgs) {
- SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, selectionArgs, mCancellationSignal);
- final Cursor cursor;
- try {
- if (factory == null) {
- cursor = new SQLiteCursor(this, mEditTable, query);
- } else {
- cursor = factory.newCursor(mDatabase, this, mEditTable, query);
- }
- } catch (RuntimeException ex) {
- query.close();
- throw ex;
- }
-
- mQuery = query;
- return cursor;
- }
-
- @Override
- public void cursorClosed() {
- // Do nothing
- }
-
- @Override
- public void setBindArguments(String[] bindArgs) {
- mQuery.bindAllArgsAsStrings(bindArgs);
- }
-
- @Override
- public void cursorDeactivated() {
- // Do nothing
- }
-
- @Override
- public void cursorRequeried(Cursor cursor) {
- // Do nothing
- }
-
- @Override
- public String toString() {
- return "SQLiteDirectCursorDriver: " + mSql;
- }
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteFunction.java b/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteFunction.java
deleted file mode 100644
index d9fbcb6b18..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteFunction.java
+++ /dev/null
@@ -1,185 +0,0 @@
-package io.requery.android.database.sqlite;
-
-/**
- * @author dhleong
- */
-public class SQLiteFunction {
- public final String name;
- public final int numArgs;
- public final SQLiteDatabase.Function callback;
-
- // accessed from native code
- final int flags;
-
- // NOTE: from a single database connection, all calls to
- // functions are serialized by SQLITE-internal mutexes,
- // so we save on GC churn by reusing a single, shared instance
- private final MyArgs args = new MyArgs();
- private final MyResult result = new MyResult();
-
- /**
- * Create custom function.
- *
- * @param name The name of the sqlite3 function.
- * @param numArgs The number of arguments for the function, or -1 to
- * support any number of arguments.
- * @param callback The callback to invoke when the function is executed.
- * @param flags Extra SQLITE flags to pass when creating the function
- * in native code.
- */
- public SQLiteFunction(String name, int numArgs,
- SQLiteDatabase.Function callback) {
- this(name, numArgs, callback, 0);
- }
-
- /**
- * Create custom function.
- *
- * @param name The name of the sqlite3 function.
- * @param numArgs The number of arguments for the function, or -1 to
- * support any number of arguments.
- * @param callback The callback to invoke when the function is executed.
- * @param flags Extra SQLITE flags to pass when creating the function
- * in native code.
- */
- public SQLiteFunction(String name, int numArgs,
- SQLiteDatabase.Function callback,
- int flags) {
- if (name == null) {
- throw new IllegalArgumentException("name must not be null.");
- }
-
- this.name = name;
- this.numArgs = numArgs;
- this.callback = callback;
- this.flags = flags;
- }
-
- // Called from native.
- @SuppressWarnings("unused")
- private void dispatchCallback(long contextPtr, long argsPtr, int argsCount) {
- result.contextPtr = contextPtr;
- args.argsPtr = argsPtr;
- args.argsCount = argsCount;
-
- try {
- callback.callback(args, result);
-
- if (!result.isSet) {
- result.setNull();
- }
-
- } finally {
- result.contextPtr = 0;
- result.isSet = false;
- args.argsPtr = 0;
- args.argsCount = 0;
- }
- }
-
- static native byte[] nativeGetArgBlob(long argsPtr, int arg);
- static native String nativeGetArgString(long argsPtr, int arg);
- static native double nativeGetArgDouble(long argsPtr, int arg);
- static native int nativeGetArgInt(long argsPtr, int arg);
- static native long nativeGetArgLong(long argsPtr, int arg);
-
- static native void nativeSetResultBlob(long contextPtr, byte[] result);
- static native void nativeSetResultString(long contextPtr, String result);
- static native void nativeSetResultDouble(long contextPtr, double result);
- static native void nativeSetResultInt(long contextPtr, int result);
- static native void nativeSetResultLong(long contextPtr, long result);
- static native void nativeSetResultError(long contextPtr, String error);
- static native void nativeSetResultNull(long contextPtr);
-
- private static class MyArgs implements SQLiteDatabase.Function.Args {
- long argsPtr;
- int argsCount;
-
- @Override
- public byte[] getBlob(int arg) {
- return nativeGetArgBlob(argsPtr, checkArg(arg));
- }
-
- @Override
- public String getString(int arg) {
- return nativeGetArgString(argsPtr, checkArg(arg));
- }
-
- @Override
- public double getDouble(int arg) {
- return nativeGetArgDouble(argsPtr, checkArg(arg));
- }
-
- @Override
- public int getInt(int arg) {
- return nativeGetArgInt(argsPtr, checkArg(arg));
- }
-
- @Override
- public long getLong(int arg) {
- return nativeGetArgLong(argsPtr, checkArg(arg));
- }
-
- private int checkArg(int arg) {
- if (arg < 0 || arg >= argsCount) {
- throw new IllegalArgumentException(
- "Requested arg " + arg + " but had " + argsCount
- );
- }
-
- return arg;
- }
- }
-
- private static class MyResult implements SQLiteDatabase.Function.Result {
- long contextPtr;
- boolean isSet;
-
- @Override
- public void set(byte[] value) {
- checkSet();
- nativeSetResultBlob(contextPtr, value);
- }
-
- @Override
- public void set(double value) {
- checkSet();
- nativeSetResultDouble(contextPtr, value);
- }
-
- @Override
- public void set(int value) {
- checkSet();
- nativeSetResultInt(contextPtr, value);
- }
-
- @Override
- public void set(long value) {
- checkSet();
- nativeSetResultLong(contextPtr, value);
- }
-
- @Override
- public void set(String value) {
- checkSet();
- nativeSetResultString(contextPtr, value);
- }
-
- @Override
- public void setError(String error) {
- checkSet();
- nativeSetResultError(contextPtr, error);
- }
-
- @Override
- public void setNull() {
- checkSet();
- nativeSetResultNull(contextPtr);
- }
-
- private void checkSet() {
- if (isSet) throw new IllegalStateException("Result is already set");
- isSet = true;
- }
- }
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteGlobal.java b/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteGlobal.java
deleted file mode 100644
index 6e1281affe..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteGlobal.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-/*
-** Modified to support SQLite extensions by the SQLite developers:
-** sqlite-dev@sqlite.org.
-*/
-
-package io.requery.android.database.sqlite;
-
-import android.os.StatFs;
-
-/**
- * Provides access to SQLite functions that affect all database connection,
- * such as memory management.
- *
- * The native code associated with SQLiteGlobal is also sets global configuration options
- * using sqlite3_config() then calls sqlite3_initialize() to ensure that the SQLite
- * library is properly initialized exactly once before any other framework or application
- * code has a chance to run.
- *
- * Verbose SQLite logging is enabled if the "log.tag.SQLiteLog" property is set to "V".
- * (per {@link SQLiteDebug#DEBUG_SQL_LOG}).
- *
- * @hide
- */
-public final class SQLiteGlobal {
- private static final Object sLock = new Object();
- private static int sDefaultPageSize;
-
- private static native int nativeReleaseMemory();
-
- private SQLiteGlobal() {
- }
-
- /**
- * Attempts to release memory by pruning the SQLite page cache and other
- * internal data structures.
- *
- * @return The number of bytes that were freed.
- */
- public static int releaseMemory() {
- return nativeReleaseMemory();
- }
-
- // values derived from:
- // https://android.googlesource.com/platform/frameworks/base.git/+/master/core/res/res/values/config.xml
-
- /**
- * Gets the default page size to use when creating a database.
- */
- @SuppressWarnings("deprecation")
- public static int getDefaultPageSize() {
- synchronized (sLock) {
- if (sDefaultPageSize == 0) {
- sDefaultPageSize = new StatFs("/data").getBlockSize();
- }
- return 1024;
- }
- }
-
- /**
- * Gets the default journal mode when WAL is not in use.
- */
- public static String getDefaultJournalMode() {
- return "TRUNCATE";
- }
-
- /**
- * Gets the journal size limit in bytes.
- */
- public static int getJournalSizeLimit() {
- return 524288;
- }
-
- /**
- * Gets the default database synchronization mode when WAL is not in use.
- */
- public static String getDefaultSyncMode() {
- return "FULL";
- }
-
- /**
- * Gets the database synchronization mode when in WAL mode.
- */
- public static String getWALSyncMode() {
- return "normal";
- }
-
- /**
- * Gets the WAL auto-checkpoint integer in database pages.
- */
- public static int getWALAutoCheckpoint() {
- return 1000;
- }
-
- /**
- * Gets the connection pool size when in WAL mode.
- */
- public static int getWALConnectionPoolSize() {
- return 10;
- }
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteOpenHelper.java b/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteOpenHelper.java
deleted file mode 100644
index dd5e4d0fcb..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteOpenHelper.java
+++ /dev/null
@@ -1,410 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-
-package io.requery.android.database.sqlite;
-
-import android.content.Context;
-import android.database.sqlite.SQLiteException;
-import android.util.Log;
-import androidx.sqlite.db.SupportSQLiteOpenHelper;
-import io.requery.android.database.DatabaseErrorHandler;
-
-/**
- * A helper class to manage database creation and version management.
- *
- * You create a subclass implementing {@link #onCreate}, {@link #onUpgrade} and
- * optionally {@link #onOpen}, and this class takes care of opening the database
- * if it exists, creating it if it does not, and upgrading it as necessary.
- * Transactions are used to make sure the database is always in a sensible state.
- *
- *
This class makes it easy for {@link android.content.ContentProvider}
- * implementations to defer opening and upgrading the database until first use,
- * to avoid blocking application startup with long-running database upgrades.
- *
- *
For an example, see the NotePadProvider class in the NotePad sample application,
- * in the samples/ directory of the SDK.
- *
- * Note: this class assumes
- * monotonically increasing version numbers for upgrades.
- */
-@SuppressWarnings("unused")
-public abstract class SQLiteOpenHelper implements SupportSQLiteOpenHelper {
- private static final String TAG = SQLiteOpenHelper.class.getSimpleName();
-
- // When true, getReadableDatabase returns a read-only database if it is just being opened.
- // The database handle is reopened in read/write mode when getWritableDatabase is called.
- // We leave this behavior disabled in production because it is inefficient and breaks
- // many applications. For debugging purposes it can be useful to turn on strict
- // read-only semantics to catch applications that call getReadableDatabase when they really
- // wanted getWritableDatabase.
- private static final boolean DEBUG_STRICT_READONLY = false;
-
- private final Context mContext;
- private final String mName;
- private final SQLiteDatabase.CursorFactory mFactory;
- private final int mNewVersion;
-
- private SQLiteDatabase mDatabase;
- private boolean mIsInitializing;
- private boolean mEnableWriteAheadLogging;
- private final DatabaseErrorHandler mErrorHandler;
-
- /**
- * Create a helper object to create, open, and/or manage a database.
- * This method always returns very quickly. The database is not actually
- * created or opened until one of {@link #getWritableDatabase} or
- * {@link #getReadableDatabase} is called.
- *
- * @param context to use to open or create the database
- * @param name of the database file, or null for an in-memory database
- * @param factory to use for creating cursor objects, or null for the default
- * @param version number of the database (starting at 1); if the database is older,
- * {@link #onUpgrade} will be used to upgrade the database; if the database is
- * newer, {@link #onDowngrade} will be used to downgrade the database
- */
- public SQLiteOpenHelper(Context context,
- String name,
- SQLiteDatabase.CursorFactory factory,
- int version) {
- this(context, name, factory, version, null);
- }
-
- /**
- * Create a helper object to create, open, and/or manage a database.
- * The database is not actually created or opened until one of
- * {@link #getWritableDatabase} or {@link #getReadableDatabase} is called.
- *
- * Accepts input param: a concrete instance of {@link DatabaseErrorHandler} to be
- * used to handle corruption when sqlite reports database corruption.
- *
- * @param context to use to open or create the database
- * @param name of the database file, or null for an in-memory database
- * @param factory to use for creating cursor objects, or null for the default
- * @param version number of the database (starting at 1); if the database is older,
- * {@link #onUpgrade} will be used to upgrade the database; if the database is
- * newer, {@link #onDowngrade} will be used to downgrade the database
- * @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database
- * corruption, or null to use the default error handler.
- */
- public SQLiteOpenHelper(Context context, String name,
- SQLiteDatabase.CursorFactory factory,
- int version,
- DatabaseErrorHandler errorHandler) {
- if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version);
-
- mContext = context;
- mName = name;
- mFactory = factory;
- mNewVersion = version;
- mErrorHandler = errorHandler;
- }
-
- /**
- * Return the name of the SQLite database being opened, as given to
- * the constructor.
- */
- @Override
- public String getDatabaseName() {
- return mName;
- }
-
- /**
- * Enables or disables the use of write-ahead logging for the database.
- *
- * Write-ahead logging cannot be used with read-only databases so the value of
- * this flag is ignored if the database is opened read-only.
- *
- * @param enabled True if write-ahead logging should be enabled, false if it
- * should be disabled.
- *
- * @see SQLiteDatabase#enableWriteAheadLogging()
- */
- @Override
- public void setWriteAheadLoggingEnabled(boolean enabled) {
- synchronized (this) {
- if (mEnableWriteAheadLogging != enabled) {
- if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {
- if (enabled) {
- mDatabase.enableWriteAheadLogging();
- } else {
- mDatabase.disableWriteAheadLogging();
- }
- }
- mEnableWriteAheadLogging = enabled;
- }
- }
- }
-
- /**
- * Create and/or open a database that will be used for reading and writing.
- * The first time this is called, the database will be opened and
- * {@link #onCreate}, {@link #onUpgrade} and/or {@link #onOpen} will be
- * called.
- *
- * Once opened successfully, the database is cached, so you can
- * call this method every time you need to write to the database.
- * (Make sure to call {@link #close} when you no longer need the database.)
- * Errors such as bad permissions or a full disk may cause this method
- * to fail, but future attempts may succeed if the problem is fixed.
- *
- * Database upgrade may take a long time, you
- * should not call this method from the application main thread, including
- * from {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
- *
- * @throws SQLiteException if the database cannot be opened for writing
- * @return a read/write database object valid until {@link #close} is called
- */
- @Override
- public SQLiteDatabase getWritableDatabase() {
- synchronized (this) {
- return getDatabaseLocked(true);
- }
- }
-
- /**
- * Create and/or open a database. This will be the same object returned by
- * {@link #getWritableDatabase} unless some problem, such as a full disk,
- * requires the database to be opened read-only. In that case, a read-only
- * database object will be returned. If the problem is fixed, a future call
- * to {@link #getWritableDatabase} may succeed, in which case the read-only
- * database object will be closed and the read/write object will be returned
- * in the future.
- *
- *
Like {@link #getWritableDatabase}, this method may
- * take a long time to return, so you should not call it from the
- * application main thread, including from
- * {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
- *
- * @throws SQLiteException if the database cannot be opened
- * @return a database object valid until {@link #getWritableDatabase}
- * or {@link #close} is called.
- */
- @Override
- public SQLiteDatabase getReadableDatabase() {
- synchronized (this) {
- return getDatabaseLocked(false);
- }
- }
-
- private SQLiteDatabase getDatabaseLocked(boolean writable) {
- if (mDatabase != null) {
- if (!mDatabase.isOpen()) {
- // Darn! The user closed the database by calling mDatabase.close().
- mDatabase = null;
- } else if (!writable || !mDatabase.isReadOnly()) {
- // The database is already open for business.
- return mDatabase;
- }
- }
-
- if (mIsInitializing) {
- throw new IllegalStateException("getDatabase called recursively");
- }
-
- SQLiteDatabase db = mDatabase;
- try {
- mIsInitializing = true;
-
- if (db != null) {
- if (db.isReadOnly()) {
- db.reopenReadWrite();
- }
- } else if (mName == null) {
- db = SQLiteDatabase.create(null);
- } else {
- try {
- final String path = mContext.getDatabasePath(mName).getPath();
- if (DEBUG_STRICT_READONLY && !writable) {
- SQLiteDatabaseConfiguration configuration =
- createConfiguration(path, SQLiteDatabase.OPEN_READONLY);
- db = SQLiteDatabase.openDatabase(configuration, mFactory, mErrorHandler);
- } else {
- int flags = mEnableWriteAheadLogging ?
- SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING : 0;
- flags |= SQLiteDatabase.CREATE_IF_NECESSARY;
- SQLiteDatabaseConfiguration configuration =
- createConfiguration(path, flags);
- db = SQLiteDatabase.openDatabase(configuration, mFactory, mErrorHandler);
- }
- } catch (SQLiteException ex) {
- if (writable) {
- throw ex;
- }
- Log.e(TAG, "Couldn't open " + mName
- + " for writing (will try read-only):", ex);
- final String path = mContext.getDatabasePath(mName).getPath();
- SQLiteDatabaseConfiguration configuration =
- createConfiguration(path, SQLiteDatabase.OPEN_READONLY);
- db = SQLiteDatabase.openDatabase(configuration, mFactory, mErrorHandler);
- }
- }
-
- onConfigure(db);
-
- final int version = db.getVersion();
- if (version != mNewVersion) {
- if (db.isReadOnly()) {
- throw new SQLiteException("Can't upgrade read-only database from version " +
- db.getVersion() + " to " + mNewVersion + ": " + mName);
- }
-
- db.beginTransaction();
- try {
- if (version == 0) {
- onCreate(db);
- } else {
- if (version > mNewVersion) {
- onDowngrade(db, version, mNewVersion);
- } else {
- onUpgrade(db, version, mNewVersion);
- }
- }
- db.setVersion(mNewVersion);
- db.setTransactionSuccessful();
- } finally {
- db.endTransaction();
- }
- }
-
- onOpen(db);
-
- if (db.isReadOnly()) {
- Log.w(TAG, "Opened " + mName + " in read-only mode");
- }
-
- mDatabase = db;
- return db;
- } finally {
- mIsInitializing = false;
- if (db != null && db != mDatabase) {
- db.close();
- }
- }
- }
-
- /**
- * Close any open database object.
- */
- @Override
- public synchronized void close() {
- if (mIsInitializing) throw new IllegalStateException("Closed during initialization");
-
- if (mDatabase != null && mDatabase.isOpen()) {
- mDatabase.close();
- mDatabase = null;
- }
- }
-
- /**
- * Called when the database connection is being configured, to enable features
- * such as write-ahead logging or foreign key support.
- *
- * This method is called before {@link #onCreate}, {@link #onUpgrade},
- * {@link #onDowngrade}, or {@link #onOpen} are called. It should not modify
- * the database except to configure the database connection as required.
- *
- * This method should only call methods that configure the parameters of the
- * database connection, such as {@link SQLiteDatabase#enableWriteAheadLogging}
- * {@link SQLiteDatabase#setForeignKeyConstraintsEnabled},
- * {@link SQLiteDatabase#setLocale}, {@link SQLiteDatabase#setMaximumSize},
- * or executing PRAGMA statements.
- *
- *
- * @param db The database.
- */
- public void onConfigure(SQLiteDatabase db) {}
-
- /**
- * Called when the database is created for the first time. This is where the
- * creation of tables and the initial population of the tables should happen.
- *
- * @param db The database.
- */
- public abstract void onCreate(SQLiteDatabase db);
-
- /**
- * Called when the database needs to be upgraded. The implementation
- * should use this method to drop tables, add tables, or do anything else it
- * needs to upgrade to the new schema version.
- *
- *
- * The SQLite ALTER TABLE documentation can be found
- * here . If you add new columns
- * you can use ALTER TABLE to insert them into a live table. If you rename or remove columns
- * you can use ALTER TABLE to rename the old table, then create the new table and then
- * populate the new table with the contents of the old table.
- *
- * This method executes within a transaction. If an exception is thrown, all changes
- * will automatically be rolled back.
- *
- *
- * @param db The database.
- * @param oldVersion The old database version.
- * @param newVersion The new database version.
- */
- public abstract void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);
-
- /**
- * Called when the database needs to be downgraded. This is strictly similar to
- * {@link #onUpgrade} method, but is called whenever current version is newer than requested one.
- * However, this method is not abstract, so it is not mandatory for a customer to
- * implement it. If not overridden, default implementation will reject downgrade and
- * throws SQLiteException
- *
- *
- * This method executes within a transaction. If an exception is thrown, all changes
- * will automatically be rolled back.
- *
- *
- * @param db The database.
- * @param oldVersion The old database version.
- * @param newVersion The new database version.
- */
- public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
- throw new SQLiteException("Can't downgrade database from version " +
- oldVersion + " to " + newVersion);
- }
-
- /**
- * Called when the database has been opened. The implementation
- * should check {@link SQLiteDatabase#isReadOnly} before updating the
- * database.
- *
- * This method is called after the database connection has been configured
- * and after the database schema has been created, upgraded or downgraded as necessary.
- * If the database connection must be configured in some way before the schema
- * is created, upgraded, or downgraded, do it in {@link #onConfigure} instead.
- *
- *
- * @param db The database.
- */
- public void onOpen(SQLiteDatabase db) {}
-
- /**
- * Called before the database is opened. Provides the {@link SQLiteDatabaseConfiguration}
- * instance that is used to initialize the database. Override this to create a configuration
- * that has custom functions or extensions.
- *
- * @param path to database file to open and/or create
- * @param openFlags to control database access mode
- * @return {@link SQLiteDatabaseConfiguration} instance, cannot be null.
- */
- protected SQLiteDatabaseConfiguration createConfiguration(String path,
- @SQLiteDatabase.OpenFlags int openFlags) {
- return new SQLiteDatabaseConfiguration(path, openFlags);
- }
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteProgram.java b/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteProgram.java
deleted file mode 100644
index 2399023ed5..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteProgram.java
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-
-package io.requery.android.database.sqlite;
-
-import androidx.core.os.CancellationSignal;
-import androidx.sqlite.db.SupportSQLiteProgram;
-
-import java.util.Arrays;
-
-/**
- * A base class for compiled SQLite programs.
- *
- * This class is not thread-safe.
- *
- */
-@SuppressWarnings("unused")
-public abstract class SQLiteProgram extends SQLiteClosable implements SupportSQLiteProgram {
- private static final String[] EMPTY_STRING_ARRAY = new String[0];
-
- private final SQLiteDatabase mDatabase;
- private final String mSql;
- private final boolean mReadOnly;
- private final String[] mColumnNames;
- private final int mNumParameters;
- private final Object[] mBindArgs;
-
- SQLiteProgram(SQLiteDatabase db, String sql, Object[] bindArgs,
- CancellationSignal cancellationSignalForPrepare) {
- mDatabase = db;
- mSql = sql.trim();
-
- int n = SQLiteStatementType.getSqlStatementType(mSql);
- switch (n) {
- case SQLiteStatementType.STATEMENT_BEGIN:
- case SQLiteStatementType.STATEMENT_COMMIT:
- case SQLiteStatementType.STATEMENT_ABORT:
- mReadOnly = false;
- mColumnNames = EMPTY_STRING_ARRAY;
- mNumParameters = 0;
- break;
-
- default:
- boolean assumeReadOnly = (n == SQLiteStatementType.STATEMENT_SELECT);
- SQLiteStatementInfo info = new SQLiteStatementInfo();
- db.getThreadSession().prepare(mSql,
- db.getThreadDefaultConnectionFlags(assumeReadOnly),
- cancellationSignalForPrepare, info);
- mReadOnly = info.readOnly;
- mColumnNames = info.columnNames;
- mNumParameters = info.numParameters;
- break;
- }
-
- if (bindArgs != null && bindArgs.length > mNumParameters) {
- throw new IllegalArgumentException("Too many bind arguments. "
- + bindArgs.length + " arguments were provided but the statement needs "
- + mNumParameters + " arguments.");
- }
-
- if (mNumParameters != 0) {
- mBindArgs = new Object[mNumParameters];
- if (bindArgs != null) {
- System.arraycopy(bindArgs, 0, mBindArgs, 0, bindArgs.length);
- }
- } else {
- mBindArgs = null;
- }
- }
-
- final SQLiteDatabase getDatabase() {
- return mDatabase;
- }
-
- final String getSql() {
- return mSql;
- }
-
- final Object[] getBindArgs() {
- return mBindArgs;
- }
-
- final String[] getColumnNames() {
- return mColumnNames;
- }
-
- /** @hide */
- protected final SQLiteSession getSession() {
- return mDatabase.getThreadSession();
- }
-
- /** @hide */
- protected final int getConnectionFlags() {
- return mDatabase.getThreadDefaultConnectionFlags(mReadOnly);
- }
-
- /** @hide */
- protected final void onCorruption() {
- mDatabase.onCorruption();
- }
-
- /**
- * Bind a NULL value to this statement. The value remains bound until
- * {@link #clearBindings} is called.
- *
- * @param index The 1-based index to the parameter to bind null to
- */
- @Override
- public void bindNull(int index) {
- bind(index, null);
- }
-
- /**
- * Bind a long value to this statement. The value remains bound until
- * {@link #clearBindings} is called.
- *addToBindArgs
- * @param index The 1-based index to the parameter to bind
- * @param value The value to bind
- */
- @Override
- public void bindLong(int index, long value) {
- bind(index, value);
- }
-
- /**
- * Bind a double value to this statement. The value remains bound until
- * {@link #clearBindings} is called.
- *
- * @param index The 1-based index to the parameter to bind
- * @param value The value to bind
- */
- @Override
- public void bindDouble(int index, double value) {
- bind(index, value);
- }
-
- /**
- * Bind a String value to this statement. The value remains bound until
- * {@link #clearBindings} is called.
- *
- * @param index The 1-based index to the parameter to bind
- * @param value The value to bind, must not be null
- */
- @Override
- public void bindString(int index, String value) {
- if (value == null) {
- throw new IllegalArgumentException("the bind value at index " + index + " is null");
- }
- bind(index, value);
- }
-
- /**
- * Bind a byte array value to this statement. The value remains bound until
- * {@link #clearBindings} is called.
- *
- * @param index The 1-based index to the parameter to bind
- * @param value The value to bind, must not be null
- */
- @Override
- public void bindBlob(int index, byte[] value) {
- if (value == null) {
- throw new IllegalArgumentException("the bind value at index " + index + " is null");
- }
- bind(index, value);
- }
-
- /**
- * Binds the given Object to the given SQLiteProgram using the proper
- * typing. For example, bind numbers as longs/doubles, and everything else
- * as a string by call toString() on it.
- *
- * @param index the 1-based index to bind at
- * @param value the value to bind
- */
- public void bindObject(int index, Object value) {
- if (value == null) {
- bindNull(index);
- } else if (value instanceof Double || value instanceof Float) {
- bindDouble(index, ((Number)value).doubleValue());
- } else if (value instanceof Number) {
- bindLong(index, ((Number)value).longValue());
- } else if (value instanceof Boolean) {
- Boolean bool = (Boolean)value;
- if (bool) {
- bindLong(index, 1);
- } else {
- bindLong(index, 0);
- }
- } else if (value instanceof byte[]){
- bindBlob(index, (byte[]) value);
- } else {
- bindString(index, value.toString());
- }
- }
-
- /**
- * Clears all existing bindings. Unset bindings are treated as NULL.
- */
- @Override
- public void clearBindings() {
- if (mBindArgs != null) {
- Arrays.fill(mBindArgs, null);
- }
- }
-
- /**
- * Given an array of String bindArgs, this method binds all of them in one single call.
- *
- * @param bindArgs the String array of bind args, none of which must be null.
- */
- public void bindAllArgsAsStrings(String[] bindArgs) {
- if (bindArgs != null) {
- for (int i = bindArgs.length; i != 0; i--) {
- bindString(i, bindArgs[i - 1]);
- }
- }
- }
-
- @Override
- protected void onAllReferencesReleased() {
- clearBindings();
- }
-
- private void bind(int index, Object value) {
- if (index < 1 || index > mNumParameters) {
- throw new IllegalArgumentException("Cannot bind argument at index "
- + index + " because the index is out of range. "
- + "The statement has " + mNumParameters + " parameters.");
- }
- mBindArgs[index - 1] = value;
- }
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteQuery.java b/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteQuery.java
deleted file mode 100644
index 3632ad10c0..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteQuery.java
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-
-package io.requery.android.database.sqlite;
-
-import android.database.sqlite.SQLiteDatabaseCorruptException;
-import android.database.sqlite.SQLiteException;
-import android.util.Log;
-import androidx.core.os.CancellationSignal;
-import androidx.core.os.OperationCanceledException;
-import io.requery.android.database.CursorWindow;
-
-/**
- * Represents a query that reads the resulting rows into a {@link SQLiteQuery}.
- * This class is used by {@link SQLiteCursor} and isn't useful itself.
- *
- * This class is not thread-safe.
- *
- */
-public final class SQLiteQuery extends SQLiteProgram {
- private static final String TAG = "SQLiteQuery";
-
- private final CancellationSignal mCancellationSignal;
-
- SQLiteQuery(SQLiteDatabase db, String query, Object[] bindArgs,
- CancellationSignal cancellationSignal) {
- super(db, query, bindArgs, cancellationSignal);
- mCancellationSignal = cancellationSignal;
- }
-
- /**
- * Reads rows into a buffer.
- *
- * @param window The window to fill into
- * @param startPos The start position for filling the window.
- * @param requiredPos The position of a row that MUST be in the window.
- * If it won't fit, then the query should discard part of what it filled.
- * @param countAllRows True to count all rows that the query would
- * return regardless of whether they fit in the window.
- * @return Number of rows that were enumerated. Might not be all rows
- * unless countAllRows is true.
- *
- * @throws SQLiteException if an error occurs.
- * @throws OperationCanceledException if the operation was canceled.
- */
- int fillWindow(CursorWindow window, int startPos, int requiredPos, boolean countAllRows) {
- acquireReference();
- try {
- window.acquireReference();
- try {
- return getSession().executeForCursorWindow(getSql(), getBindArgs(),
- window, startPos, requiredPos, countAllRows, getConnectionFlags(),
- mCancellationSignal);
- } catch (SQLiteDatabaseCorruptException ex) {
- onCorruption();
- throw ex;
- } catch (SQLiteException ex) {
- Log.e(TAG, "exception: " + ex.getMessage() + "; query: " + getSql());
- throw ex;
- } finally {
- window.releaseReference();
- }
- } finally {
- releaseReference();
- }
- }
-
- @Override
- public String toString() {
- return "SQLiteQuery: " + getSql();
- }
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteQueryBuilder.java b/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteQueryBuilder.java
deleted file mode 100644
index a22c413262..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteQueryBuilder.java
+++ /dev/null
@@ -1,612 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-
-package io.requery.android.database.sqlite;
-
-import android.database.Cursor;
-import android.database.DatabaseUtils;
-import android.os.OperationCanceledException;
-import android.provider.BaseColumns;
-import android.text.TextUtils;
-import android.util.Log;
-import androidx.core.os.CancellationSignal;
-
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Map.Entry;
-import java.util.Set;
-import java.util.regex.Pattern;
-
-/**
- * This is a convience class that helps build SQL queries to be sent to
- * {@link SQLiteDatabase} objects.
- */
-@SuppressWarnings("unused")
-public class SQLiteQueryBuilder {
- private static final String TAG = "SQLiteQueryBuilder";
- private static final Pattern sLimitPattern =
- Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?");
-
- private Map mProjectionMap = null;
- private String mTables = "";
- private StringBuilder mWhereClause = null; // lazily created
- private boolean mDistinct;
- private SQLiteDatabase.CursorFactory mFactory;
- private boolean mStrict;
-
- public SQLiteQueryBuilder() {
- mDistinct = false;
- mFactory = null;
- }
-
- /**
- * Mark the query as DISTINCT.
- *
- * @param distinct if true the query is DISTINCT, otherwise it isn't
- */
- public void setDistinct(boolean distinct) {
- mDistinct = distinct;
- }
-
- /**
- * Returns the list of tables being queried
- *
- * @return the list of tables being queried
- */
- public String getTables() {
- return mTables;
- }
-
- /**
- * Sets the list of tables to query. Multiple tables can be specified to perform a join.
- * For example:
- * setTables("foo, bar")
- * setTables("foo LEFT OUTER JOIN bar ON (foo.id = bar.foo_id)")
- *
- * @param inTables the list of tables to query on
- */
- public void setTables(String inTables) {
- mTables = inTables;
- }
-
- /**
- * Append a chunk to the WHERE clause of the query. All chunks appended are surrounded
- * by parenthesis and ANDed with the selection passed to {@link #query}. The final
- * WHERE clause looks like:
- *
- * WHERE (<append chunk 1><append chunk2>) AND (<query() selection parameter>)
- *
- * @param inWhere the chunk of text to append to the WHERE clause.
- */
- public void appendWhere(CharSequence inWhere) {
- if (mWhereClause == null) {
- mWhereClause = new StringBuilder(inWhere.length() + 16);
- }
- if (mWhereClause.length() == 0) {
- mWhereClause.append('(');
- }
- mWhereClause.append(inWhere);
- }
-
- /**
- * Append a chunk to the WHERE clause of the query. All chunks appended are surrounded
- * by parenthesis and ANDed with the selection passed to {@link #query}. The final
- * WHERE clause looks like:
- *
- * WHERE (<append chunk 1><append chunk2>) AND (<query() selection parameter>)
- *
- * @param inWhere the chunk of text to append to the WHERE clause. it will be escaped
- * to avoid SQL injection attacks
- */
- public void appendWhereEscapeString(String inWhere) {
- if (mWhereClause == null) {
- mWhereClause = new StringBuilder(inWhere.length() + 16);
- }
- if (mWhereClause.length() == 0) {
- mWhereClause.append('(');
- }
- DatabaseUtils.appendEscapedSQLString(mWhereClause, inWhere);
- }
-
- /**
- * Sets the projection map for the query. The projection map maps
- * from column names that the caller passes into query to database
- * column names. This is useful for renaming columns as well as
- * disambiguating column names when doing joins. For example you
- * could map "name" to "people.name". If a projection map is set
- * it must contain all column names the user may request, even if
- * the key and value are the same.
- *
- * @param columnMap maps from the user column names to the database column names
- */
- public void setProjectionMap(Map columnMap) {
- mProjectionMap = columnMap;
- }
-
- /**
- * Sets the cursor factory to be used for the query. You can use
- * one factory for all queries on a database but it is normally
- * easier to specify the factory when doing this query.
- *
- * @param factory the factory to use.
- */
- public void setCursorFactory(SQLiteDatabase.CursorFactory factory) {
- mFactory = factory;
- }
-
- /**
- * When set, the selection is verified against malicious arguments.
- * When using this class to create a statement using
- * {@link #buildQueryString(boolean, String, String[], String, String, String, String, String)},
- * non-numeric limits will raise an exception. If a projection map is specified, fields
- * not in that map will be ignored.
- * If this class is used to execute the statement directly using
- * {@link #query(SQLiteDatabase, String[], String, String[], String, String, String)}
- * or
- * {@link #query(SQLiteDatabase, String[], String, String[], String, String, String, String)},
- * additionally also parenthesis escaping selection are caught.
- *
- * To summarize: To get maximum protection against malicious third party apps (for example
- * content provider consumers), make sure to do the following:
- *
- * Set this value to true
- * Use a projection map
- * Use one of the query overloads instead of getting the statement as a sql string
- *
- * By default, this value is false.
- */
- public void setStrict(boolean flag) {
- mStrict = flag;
- }
-
- /**
- * Build an SQL query string from the given clauses.
- *
- * @param distinct true if you want each row to be unique, false otherwise.
- * @param tables The table names to compile the query against.
- * @param columns A list of which columns to return. Passing null will
- * return all columns, which is discouraged to prevent reading
- * data from storage that isn't going to be used.
- * @param where A filter declaring which rows to return, formatted as an SQL
- * WHERE clause (excluding the WHERE itself). Passing null will
- * return all rows for the given URL.
- * @param groupBy A filter declaring how to group rows, formatted as an SQL
- * GROUP BY clause (excluding the GROUP BY itself). Passing null
- * will cause the rows to not be grouped.
- * @param having A filter declare which row groups to include in the cursor,
- * if row grouping is being used, formatted as an SQL HAVING
- * clause (excluding the HAVING itself). Passing null will cause
- * all row groups to be included, and is required when row
- * grouping is not being used.
- * @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
- * (excluding the ORDER BY itself). Passing null will use the
- * default sort order, which may be unordered.
- * @param limit Limits the number of rows returned by the query,
- * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
- * @return the SQL query string
- */
- public static String buildQueryString(
- boolean distinct, String tables, String[] columns, String where,
- String groupBy, String having, String orderBy, String limit) {
- if (TextUtils.isEmpty(groupBy) && !TextUtils.isEmpty(having)) {
- throw new IllegalArgumentException(
- "HAVING clauses are only permitted when using a groupBy clause");
- }
- if (!TextUtils.isEmpty(limit) && !sLimitPattern.matcher(limit).matches()) {
- throw new IllegalArgumentException("invalid LIMIT clauses:" + limit);
- }
-
- StringBuilder query = new StringBuilder(120);
-
- query.append("SELECT ");
- if (distinct) {
- query.append("DISTINCT ");
- }
- if (columns != null && columns.length != 0) {
- appendColumns(query, columns);
- } else {
- query.append("* ");
- }
- query.append("FROM ");
- query.append(tables);
- appendClause(query, " WHERE ", where);
- appendClause(query, " GROUP BY ", groupBy);
- appendClause(query, " HAVING ", having);
- appendClause(query, " ORDER BY ", orderBy);
- appendClause(query, " LIMIT ", limit);
-
- return query.toString();
- }
-
- private static void appendClause(StringBuilder s, String name, String clause) {
- if (!TextUtils.isEmpty(clause)) {
- s.append(name);
- s.append(clause);
- }
- }
-
- /**
- * Add the names that are non-null in columns to s, separating
- * them with commas.
- */
- public static void appendColumns(StringBuilder s, String[] columns) {
- int n = columns.length;
-
- for (int i = 0; i < n; i++) {
- String column = columns[i];
-
- if (column != null) {
- if (i > 0) {
- s.append(", ");
- }
- s.append(column);
- }
- }
- s.append(' ');
- }
-
- /**
- * Perform a query by combining all current settings and the
- * information passed into this method.
- *
- * @param db the database to query on
- * @param projectionIn A list of which columns to return. Passing
- * null will return all columns, which is discouraged to prevent
- * reading data from storage that isn't going to be used.
- * @param selection A filter declaring which rows to return,
- * formatted as an SQL WHERE clause (excluding the WHERE
- * itself). Passing null will return all rows for the given URL.
- * @param selectionArgs You may include ?s in selection, which
- * will be replaced by the values from selectionArgs, in order
- * that they appear in the selection. The values will be bound
- * as Strings.
- * @param groupBy A filter declaring how to group rows, formatted
- * as an SQL GROUP BY clause (excluding the GROUP BY
- * itself). Passing null will cause the rows to not be grouped.
- * @param having A filter declare which row groups to include in
- * the cursor, if row grouping is being used, formatted as an
- * SQL HAVING clause (excluding the HAVING itself). Passing
- * null will cause all row groups to be included, and is
- * required when row grouping is not being used.
- * @param sortOrder How to order the rows, formatted as an SQL
- * ORDER BY clause (excluding the ORDER BY itself). Passing null
- * will use the default sort order, which may be unordered.
- * @return a cursor over the result set
- * @see android.content.ContentResolver#query(android.net.Uri, String[],
- * String, String[], String)
- */
- public Cursor query(SQLiteDatabase db, String[] projectionIn,
- String selection, String[] selectionArgs, String groupBy,
- String having, String sortOrder) {
- return query(db, projectionIn, selection, selectionArgs, groupBy, having, sortOrder,
- null /* limit */, null /* cancellationSignal */);
- }
-
- /**
- * Perform a query by combining all current settings and the
- * information passed into this method.
- *
- * @param db the database to query on
- * @param projectionIn A list of which columns to return. Passing
- * null will return all columns, which is discouraged to prevent
- * reading data from storage that isn't going to be used.
- * @param selection A filter declaring which rows to return,
- * formatted as an SQL WHERE clause (excluding the WHERE
- * itself). Passing null will return all rows for the given URL.
- * @param selectionArgs You may include ?s in selection, which
- * will be replaced by the values from selectionArgs, in order
- * that they appear in the selection. The values will be bound
- * as Strings.
- * @param groupBy A filter declaring how to group rows, formatted
- * as an SQL GROUP BY clause (excluding the GROUP BY
- * itself). Passing null will cause the rows to not be grouped.
- * @param having A filter declare which row groups to include in
- * the cursor, if row grouping is being used, formatted as an
- * SQL HAVING clause (excluding the HAVING itself). Passing
- * null will cause all row groups to be included, and is
- * required when row grouping is not being used.
- * @param sortOrder How to order the rows, formatted as an SQL
- * ORDER BY clause (excluding the ORDER BY itself). Passing null
- * will use the default sort order, which may be unordered.
- * @param limit Limits the number of rows returned by the query,
- * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
- * @return a cursor over the result set
- * @see android.content.ContentResolver#query(android.net.Uri, String[],
- * String, String[], String)
- */
- public Cursor query(SQLiteDatabase db, String[] projectionIn,
- String selection, String[] selectionArgs, String groupBy,
- String having, String sortOrder, String limit) {
- return query(db, projectionIn, selection, selectionArgs,
- groupBy, having, sortOrder, limit, null);
- }
-
- /**
- * Perform a query by combining all current settings and the
- * information passed into this method.
- *
- * @param db the database to query on
- * @param projectionIn A list of which columns to return. Passing
- * null will return all columns, which is discouraged to prevent
- * reading data from storage that isn't going to be used.
- * @param selection A filter declaring which rows to return,
- * formatted as an SQL WHERE clause (excluding the WHERE
- * itself). Passing null will return all rows for the given URL.
- * @param selectionArgs You may include ?s in selection, which
- * will be replaced by the values from selectionArgs, in order
- * that they appear in the selection. The values will be bound
- * as Strings.
- * @param groupBy A filter declaring how to group rows, formatted
- * as an SQL GROUP BY clause (excluding the GROUP BY
- * itself). Passing null will cause the rows to not be grouped.
- * @param having A filter declare which row groups to include in
- * the cursor, if row grouping is being used, formatted as an
- * SQL HAVING clause (excluding the HAVING itself). Passing
- * null will cause all row groups to be included, and is
- * required when row grouping is not being used.
- * @param sortOrder How to order the rows, formatted as an SQL
- * ORDER BY clause (excluding the ORDER BY itself). Passing null
- * will use the default sort order, which may be unordered.
- * @param limit Limits the number of rows returned by the query,
- * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * If the operation is canceled, then {@link OperationCanceledException} will be thrown
- * when the query is executed.
- * @return a cursor over the result set
- * @see android.content.ContentResolver#query(android.net.Uri, String[],
- * String, String[], String)
- */
- public Cursor query(SQLiteDatabase db, String[] projectionIn,
- String selection, String[] selectionArgs, String groupBy,
- String having, String sortOrder, String limit, CancellationSignal cancellationSignal) {
- if (mTables == null) {
- return null;
- }
-
- if (mStrict && selection != null && selection.length() > 0) {
- // Validate the user-supplied selection to detect syntactic anomalies
- // in the selection string that could indicate a SQL injection attempt.
- // The idea is to ensure that the selection clause is a valid SQL expression
- // by compiling it twice: once wrapped in parentheses and once as
- // originally specified. An attacker cannot create an expression that
- // would escape the SQL expression while maintaining balanced parentheses
- // in both the wrapped and original forms.
- String sqlForValidation = buildQuery(projectionIn, "(" + selection + ")", groupBy,
- having, sortOrder, limit);
- db.validateSql(sqlForValidation, cancellationSignal); // will throw if query is invalid
- }
-
- String sql = buildQuery(
- projectionIn, selection, groupBy, having,
- sortOrder, limit);
-
- if (Log.isLoggable(TAG, Log.DEBUG)) {
- Log.d(TAG, "Performing query: " + sql);
- }
- return db.rawQueryWithFactory(
- mFactory, sql, selectionArgs,
- SQLiteDatabase.findEditTable(mTables),
- cancellationSignal); // will throw if query is invalid
- }
-
- /**
- * Construct a SELECT statement suitable for use in a group of
- * SELECT statements that will be joined through UNION operators
- * in buildUnionQuery.
- *
- * @param projectionIn A list of which columns to return. Passing
- * null will return all columns, which is discouraged to
- * prevent reading data from storage that isn't going to be
- * used.
- * @param selection A filter declaring which rows to return,
- * formatted as an SQL WHERE clause (excluding the WHERE
- * itself). Passing null will return all rows for the given
- * URL.
- * @param groupBy A filter declaring how to group rows, formatted
- * as an SQL GROUP BY clause (excluding the GROUP BY itself).
- * Passing null will cause the rows to not be grouped.
- * @param having A filter declare which row groups to include in
- * the cursor, if row grouping is being used, formatted as an
- * SQL HAVING clause (excluding the HAVING itself). Passing
- * null will cause all row groups to be included, and is
- * required when row grouping is not being used.
- * @param sortOrder How to order the rows, formatted as an SQL
- * ORDER BY clause (excluding the ORDER BY itself). Passing null
- * will use the default sort order, which may be unordered.
- * @param limit Limits the number of rows returned by the query,
- * formatted as LIMIT clause. Passing null denotes no LIMIT clause.
- * @return the resulting SQL SELECT statement
- */
- public String buildQuery(
- String[] projectionIn, String selection, String groupBy,
- String having, String sortOrder, String limit) {
- String[] projection = computeProjection(projectionIn);
-
- StringBuilder where = new StringBuilder();
- boolean hasBaseWhereClause = mWhereClause != null && mWhereClause.length() > 0;
-
- if (hasBaseWhereClause) {
- where.append(mWhereClause.toString());
- where.append(')');
- }
-
- // Tack on the user's selection, if present.
- if (selection != null && selection.length() > 0) {
- if (hasBaseWhereClause) {
- where.append(" AND ");
- }
-
- where.append('(');
- where.append(selection);
- where.append(')');
- }
-
- return buildQueryString(
- mDistinct, mTables, projection, where.toString(),
- groupBy, having, sortOrder, limit);
- }
-
- /**
- * Construct a SELECT statement suitable for use in a group of
- * SELECT statements that will be joined through UNION operators
- * in buildUnionQuery.
- *
- * @param typeDiscriminatorColumn the name of the result column
- * whose cells will contain the name of the table from which
- * each row was drawn.
- * @param unionColumns the names of the columns to appear in the
- * result. This may include columns that do not appear in the
- * table this SELECT is querying (i.e. mTables), but that do
- * appear in one of the other tables in the UNION query that we
- * are constructing.
- * @param columnsPresentInTable a Set of the names of the columns
- * that appear in this table (i.e. in the table whose name is
- * mTables). Since columns in unionColumns include columns that
- * appear only in other tables, we use this array to distinguish
- * which ones actually are present. Other columns will have
- * NULL values for results from this subquery.
- * @param computedColumnsOffset all columns in unionColumns before
- * this index are included under the assumption that they're
- * computed and therefore won't appear in columnsPresentInTable,
- * e.g. "date * 1000 as normalized_date"
- * @param typeDiscriminatorValue the value used for the
- * type-discriminator column in this subquery
- * @param selection A filter declaring which rows to return,
- * formatted as an SQL WHERE clause (excluding the WHERE
- * itself). Passing null will return all rows for the given
- * URL.
- * @param groupBy A filter declaring how to group rows, formatted
- * as an SQL GROUP BY clause (excluding the GROUP BY itself).
- * Passing null will cause the rows to not be grouped.
- * @param having A filter declare which row groups to include in
- * the cursor, if row grouping is being used, formatted as an
- * SQL HAVING clause (excluding the HAVING itself). Passing
- * null will cause all row groups to be included, and is
- * required when row grouping is not being used.
- * @return the resulting SQL SELECT statement
- */
- public String buildUnionSubQuery(
- String typeDiscriminatorColumn,
- String[] unionColumns,
- Set columnsPresentInTable,
- int computedColumnsOffset,
- String typeDiscriminatorValue,
- String selection,
- String groupBy,
- String having) {
- int unionColumnsCount = unionColumns.length;
- String[] projectionIn = new String[unionColumnsCount];
-
- for (int i = 0; i < unionColumnsCount; i++) {
- String unionColumn = unionColumns[i];
-
- if (unionColumn.equals(typeDiscriminatorColumn)) {
- projectionIn[i] = "'" + typeDiscriminatorValue + "' AS "
- + typeDiscriminatorColumn;
- } else if (i <= computedColumnsOffset
- || columnsPresentInTable.contains(unionColumn)) {
- projectionIn[i] = unionColumn;
- } else {
- projectionIn[i] = "NULL AS " + unionColumn;
- }
- }
- return buildQuery(
- projectionIn, selection, groupBy, having,
- null /* sortOrder */,
- null /* limit */);
- }
-
- /**
- * Given a set of subqueries, all of which are SELECT statements,
- * construct a query that returns the union of what those
- * subqueries return.
- * @param subQueries an array of SQL SELECT statements, all of
- * which must have the same columns as the same positions in
- * their results
- * @param sortOrder How to order the rows, formatted as an SQL
- * ORDER BY clause (excluding the ORDER BY itself). Passing
- * null will use the default sort order, which may be unordered.
- * @param limit The limit clause, which applies to the entire union result set
- *
- * @return the resulting SQL SELECT statement
- */
- public String buildUnionQuery(String[] subQueries, String sortOrder, String limit) {
- StringBuilder query = new StringBuilder(128);
- int subQueryCount = subQueries.length;
- String unionOperator = mDistinct ? " UNION " : " UNION ALL ";
-
- for (int i = 0; i < subQueryCount; i++) {
- if (i > 0) {
- query.append(unionOperator);
- }
- query.append(subQueries[i]);
- }
- appendClause(query, " ORDER BY ", sortOrder);
- appendClause(query, " LIMIT ", limit);
- return query.toString();
- }
-
- private String[] computeProjection(String[] projectionIn) {
- if (projectionIn != null && projectionIn.length > 0) {
- if (mProjectionMap != null) {
- String[] projection = new String[projectionIn.length];
- int length = projectionIn.length;
-
- for (int i = 0; i < length; i++) {
- String userColumn = projectionIn[i];
- String column = mProjectionMap.get(userColumn);
-
- if (column != null) {
- projection[i] = column;
- continue;
- }
-
- if (!mStrict &&
- ( userColumn.contains(" AS ") || userColumn.contains(" as "))) {
- /* A column alias already exist */
- projection[i] = userColumn;
- continue;
- }
-
- throw new IllegalArgumentException("Invalid column "
- + projectionIn[i]);
- }
- return projection;
- } else {
- return projectionIn;
- }
- } else if (mProjectionMap != null) {
- // Return all columns in projection map.
- Set> entrySet = mProjectionMap.entrySet();
- String[] projection = new String[entrySet.size()];
- Iterator> entryIter = entrySet.iterator();
- int i = 0;
-
- while (entryIter.hasNext()) {
- Entry entry = entryIter.next();
-
- // Don't include the _count column when people ask for no projection.
- if (entry.getKey().equals(BaseColumns._COUNT)) {
- continue;
- }
- projection[i++] = entry.getValue();
- }
- return projection;
- }
- return null;
- }
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteSession.java b/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteSession.java
deleted file mode 100644
index 1f3201d3a2..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteSession.java
+++ /dev/null
@@ -1,975 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-/*
-** Modified to support SQLite extensions by the SQLite developers:
-** sqlite-dev@sqlite.org.
-*/
-
-package io.requery.android.database.sqlite;
-
-import android.annotation.SuppressLint;
-import android.database.sqlite.SQLiteException;
-import android.database.sqlite.SQLiteTransactionListener;
-import android.os.ParcelFileDescriptor;
-import androidx.core.os.CancellationSignal;
-import androidx.core.os.OperationCanceledException;
-import io.requery.android.database.CursorWindow;
-
-/**
- * Provides a single client the ability to use a database.
- *
- * About database sessions
- *
- * Database access is always performed using a session. The session
- * manages the lifecycle of transactions and database connections.
- *
- * Sessions can be used to perform both read-only and read-write operations.
- * There is some advantage to knowing when a session is being used for
- * read-only purposes because the connection pool can optimize the use
- * of the available connections to permit multiple read-only operations
- * to execute in parallel whereas read-write operations may need to be serialized.
- *
- * When Write Ahead Logging (WAL) is enabled, the database can
- * execute simultaneous read-only and read-write transactions, provided that
- * at most one read-write transaction is performed at a time. When WAL is not
- * enabled, read-only transactions can execute in parallel but read-write
- * transactions are mutually exclusive.
- *
- *
- * Ownership and concurrency guarantees
- *
- * Session objects are not thread-safe. In fact, session objects are thread-bound.
- * The {@link SQLiteDatabase} uses a thread-local variable to associate a session
- * with each thread for the use of that thread alone. Consequently, each thread
- * has its own session object and therefore its own transaction state independent
- * of other threads.
- *
- * A thread has at most one session per database. This constraint ensures that
- * a thread can never use more than one database connection at a time for a
- * given database. As the number of available database connections is limited,
- * if a single thread tried to acquire multiple connections for the same database
- * at the same time, it might deadlock. Therefore we allow there to be only
- * one session (so, at most one connection) per thread per database.
- *
- *
- * Transactions
- *
- * There are two kinds of transaction: implicit transactions and explicit
- * transactions.
- *
- * An implicit transaction is created whenever a database operation is requested
- * and there is no explicit transaction currently in progress. An implicit transaction
- * only lasts for the duration of the database operation in question and then it
- * is ended. If the database operation was successful, then its changes are committed.
- *
- * An explicit transaction is started by calling {@link #beginTransaction} and
- * specifying the desired transaction mode. Once an explicit transaction has begun,
- * all subsequent database operations will be performed as part of that transaction.
- * To end an explicit transaction, first call {@link #setTransactionSuccessful} if the
- * transaction was successful, then call {@link #endTransaction}. If the transaction was
- * marked successful, its changes will be committed, otherwise they will be rolled back.
- *
- * Explicit transactions can also be nested. A nested explicit transaction is
- * started with {@link #beginTransaction}, marked successful with
- * {@link #setTransactionSuccessful}and ended with {@link #endTransaction}.
- * If any nested transaction is not marked successful, then the entire transaction
- * including all of its nested transactions will be rolled back
- * when the outermost transaction is ended.
- *
- * To improve concurrency, an explicit transaction can be yielded by calling
- * {@link #yieldTransaction}. If there is contention for use of the database,
- * then yielding ends the current transaction, commits its changes, releases the
- * database connection for use by another session for a little while, and starts a
- * new transaction with the same properties as the original one.
- * Changes committed by {@link #yieldTransaction} cannot be rolled back.
- *
- * When a transaction is started, the client can provide a {@link SQLiteTransactionListener}
- * to listen for notifications of transaction-related events.
- *
- * Recommended usage:
- *
- * // First, begin the transaction.
- * session.beginTransaction(SQLiteSession.TRANSACTION_MODE_DEFERRED, 0);
- * try {
- * // Then do stuff...
- * session.execute("INSERT INTO ...", null, 0);
- *
- * // As the very last step before ending the transaction, mark it successful.
- * session.setTransactionSuccessful();
- * } finally {
- * // Finally, end the transaction.
- * // This statement will commit the transaction if it was marked successful or
- * // roll it back otherwise.
- * session.endTransaction();
- * }
- *
- *
- *
- * Database connections
- *
- * A {@link SQLiteDatabase} can have multiple active sessions at the same
- * time. Each session acquires and releases connections to the database
- * as needed to perform each requested database transaction. If all connections
- * are in use, then database transactions on some sessions will block until a
- * connection becomes available.
- *
- * The session acquires a single database connection only for the duration
- * of a single (implicit or explicit) database transaction, then releases it.
- * This characteristic allows a small pool of database connections to be shared
- * efficiently by multiple sessions as long as they are not all trying to perform
- * database transactions at the same time.
- *
- *
- * Responsiveness
- *
- * Because there are a limited number of database connections and the session holds
- * a database connection for the entire duration of a database transaction,
- * it is important to keep transactions short. This is especially important
- * for read-write transactions since they may block other transactions
- * from executing. Consider calling {@link #yieldTransaction} periodically
- * during long-running transactions.
- *
- * Another important consideration is that transactions that take too long to
- * run may cause the application UI to become unresponsive. Even if the transaction
- * is executed in a background thread, the user will get bored and
- * frustrated if the application shows no data for several seconds while
- * a transaction runs.
- *
- * Guidelines:
- *
- * Do not perform database transactions on the UI thread.
- * Keep database transactions as short as possible.
- * Simple queries often run faster than complex queries.
- * Measure the performance of your database transactions.
- * Consider what will happen when the size of the data set grows.
- * A query that works well on 100 rows may struggle with 10,000.
- *
- *
- * Reentrance
- *
- * This class must tolerate reentrant execution of SQLite operations because
- * triggers may call custom SQLite functions that perform additional queries.
- *
- *
- * @hide
- */
-@SuppressWarnings({"unused", "JavaDoc"})
-@SuppressLint("Assert")
-public final class SQLiteSession {
- private final SQLiteConnectionPool mConnectionPool;
-
- private SQLiteConnection mConnection;
- private int mConnectionFlags;
- private int mConnectionUseCount;
- private Transaction mTransactionPool;
- private Transaction mTransactionStack;
-
- /**
- * Transaction mode: Deferred.
- *
- * In a deferred transaction, no locks are acquired on the database
- * until the first operation is performed. If the first operation is
- * read-only, then a SHARED lock is acquired, otherwise
- * a RESERVED lock is acquired.
- *
- * While holding a SHARED lock, this session is only allowed to
- * read but other sessions are allowed to read or write.
- * While holding a RESERVED lock, this session is allowed to read
- * or write but other sessions are only allowed to read.
- *
- * Because the lock is only acquired when needed in a deferred transaction,
- * it is possible for another session to write to the database first before
- * this session has a chance to do anything.
- *
- * Corresponds to the SQLite BEGIN DEFERRED transaction mode.
- *
- */
- public static final int TRANSACTION_MODE_DEFERRED = 0;
-
- /**
- * Transaction mode: Immediate.
- *
- * When an immediate transaction begins, the session acquires a
- * RESERVED lock.
- *
- * While holding a RESERVED lock, this session is allowed to read
- * or write but other sessions are only allowed to read.
- *
- * Corresponds to the SQLite BEGIN IMMEDIATE transaction mode.
- *
- */
- public static final int TRANSACTION_MODE_IMMEDIATE = 1;
-
- /**
- * Transaction mode: Exclusive.
- *
- * When an exclusive transaction begins, the session acquires an
- * EXCLUSIVE lock.
- *
- * While holding an EXCLUSIVE lock, this session is allowed to read
- * or write but no other sessions are allowed to access the database.
- *
- * Corresponds to the SQLite BEGIN EXCLUSIVE transaction mode.
- *
- */
- public static final int TRANSACTION_MODE_EXCLUSIVE = 2;
-
- /**
- * Creates a session bound to the specified connection pool.
- *
- * @param connectionPool The connection pool.
- */
- public SQLiteSession(SQLiteConnectionPool connectionPool) {
- if (connectionPool == null) {
- throw new IllegalArgumentException("connectionPool must not be null");
- }
-
- mConnectionPool = connectionPool;
- }
-
- /**
- * Returns true if the session has a transaction in progress.
- *
- * @return True if the session has a transaction in progress.
- */
- public boolean hasTransaction() {
- return mTransactionStack != null;
- }
-
- /**
- * Returns true if the session has a nested transaction in progress.
- *
- * @return True if the session has a nested transaction in progress.
- */
- public boolean hasNestedTransaction() {
- return mTransactionStack != null && mTransactionStack.mParent != null;
- }
-
- /**
- * Returns true if the session has an active database connection.
- *
- * @return True if the session has an active database connection.
- */
- public boolean hasConnection() {
- return mConnection != null;
- }
-
- /**
- * Begins a transaction.
- *
- * Transactions may nest. If the transaction is not in progress,
- * then a database connection is obtained and a new transaction is started.
- * Otherwise, a nested transaction is started.
- *
- * Each call to {@link #beginTransaction} must be matched exactly by a call
- * to {@link #endTransaction}. To mark a transaction as successful,
- * call {@link #setTransactionSuccessful} before calling {@link #endTransaction}.
- * If the transaction is not successful, or if any of its nested
- * transactions were not successful, then the entire transaction will
- * be rolled back when the outermost transaction is ended.
- *
- *
- * @param transactionMode The transaction mode. One of: {@link #TRANSACTION_MODE_DEFERRED},
- * {@link #TRANSACTION_MODE_IMMEDIATE}, or {@link #TRANSACTION_MODE_EXCLUSIVE}.
- * Ignored when creating a nested transaction.
- * @param transactionListener The transaction listener, or null if none.
- * @param connectionFlags The connection flags to use if a connection must be
- * acquired by this operation. Refer to {@link SQLiteConnectionPool}.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- *
- * @throws IllegalStateException if {@link #setTransactionSuccessful} has already been
- * called for the current transaction.
- * @throws SQLiteException if an error occurs.
- * @throws OperationCanceledException if the operation was canceled.
- *
- * @see #setTransactionSuccessful
- * @see #yieldTransaction
- * @see #endTransaction
- */
- public void beginTransaction(int transactionMode,
- SQLiteTransactionListener transactionListener,
- int connectionFlags,
- CancellationSignal cancellationSignal) {
- throwIfTransactionMarkedSuccessful();
- beginTransactionUnchecked(transactionMode, transactionListener, connectionFlags,
- cancellationSignal);
- }
-
- private void beginTransactionUnchecked(int transactionMode,
- SQLiteTransactionListener transactionListener, int connectionFlags,
- CancellationSignal cancellationSignal) {
- if (cancellationSignal != null) {
- cancellationSignal.throwIfCanceled();
- }
-
- if (mTransactionStack == null) {
- acquireConnection(null, connectionFlags, cancellationSignal); // might throw
- }
- try {
- // Set up the transaction such that we can back out safely
- // in case we fail part way.
- if (mTransactionStack == null) {
- // Execute SQL might throw a runtime exception.
- switch (transactionMode) {
- case TRANSACTION_MODE_IMMEDIATE:
- mConnection.execute("BEGIN IMMEDIATE;", null,
- cancellationSignal); // might throw
- break;
- case TRANSACTION_MODE_EXCLUSIVE:
- mConnection.execute("BEGIN EXCLUSIVE;", null,
- cancellationSignal); // might throw
- break;
- default:
- mConnection.execute("BEGIN;", null, cancellationSignal); // might throw
- break;
- }
- }
-
- // Listener might throw a runtime exception.
- if (transactionListener != null) {
- try {
- transactionListener.onBegin(); // might throw
- } catch (RuntimeException ex) {
- if (mTransactionStack == null) {
- mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw
- }
- throw ex;
- }
- }
-
- // Bookkeeping can't throw, except an OOM, which is just too bad...
- Transaction transaction = obtainTransaction(transactionMode, transactionListener);
- transaction.mParent = mTransactionStack;
- mTransactionStack = transaction;
- } finally {
- if (mTransactionStack == null) {
- releaseConnection(); // might throw
- }
- }
- }
-
- /**
- * Marks the current transaction as having completed successfully.
- *
- * This method can be called at most once between {@link #beginTransaction} and
- * {@link #endTransaction} to indicate that the changes made by the transaction should be
- * committed. If this method is not called, the changes will be rolled back
- * when the transaction is ended.
- *
- *
- * @throws IllegalStateException if there is no current transaction, or if
- * {@link #setTransactionSuccessful} has already been called for the current transaction.
- *
- * @see #beginTransaction
- * @see #endTransaction
- */
- public void setTransactionSuccessful() {
- throwIfNoTransaction();
- throwIfTransactionMarkedSuccessful();
-
- mTransactionStack.mMarkedSuccessful = true;
- }
-
- /**
- * Ends the current transaction and commits or rolls back changes.
- *
- * If this is the outermost transaction (not nested within any other
- * transaction), then the changes are committed if {@link #setTransactionSuccessful}
- * was called or rolled back otherwise.
- *
- * This method must be called exactly once for each call to {@link #beginTransaction}.
- *
- *
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- *
- * @throws IllegalStateException if there is no current transaction.
- * @throws SQLiteException if an error occurs.
- * @throws OperationCanceledException if the operation was canceled.
- *
- * @see #beginTransaction
- * @see #setTransactionSuccessful
- * @see #yieldTransaction
- */
- public void endTransaction(CancellationSignal cancellationSignal) {
- throwIfNoTransaction();
- assert mConnection != null;
-
- endTransactionUnchecked(cancellationSignal, false);
- }
-
- private void endTransactionUnchecked(CancellationSignal cancellationSignal, boolean yielding) {
- if (cancellationSignal != null) {
- cancellationSignal.throwIfCanceled();
- }
-
- final Transaction top = mTransactionStack;
- boolean successful = (top.mMarkedSuccessful || yielding) && !top.mChildFailed;
-
- RuntimeException listenerException = null;
- final SQLiteTransactionListener listener = top.mListener;
- if (listener != null) {
- try {
- if (successful) {
- listener.onCommit(); // might throw
- } else {
- listener.onRollback(); // might throw
- }
- } catch (RuntimeException ex) {
- listenerException = ex;
- successful = false;
- }
- }
-
- mTransactionStack = top.mParent;
- recycleTransaction(top);
-
- if (mTransactionStack != null) {
- if (!successful) {
- mTransactionStack.mChildFailed = true;
- }
- } else {
- try {
- if (successful) {
- mConnection.execute("COMMIT;", null, cancellationSignal); // might throw
- } else {
- mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw
- }
- } finally {
- releaseConnection(); // might throw
- }
- }
-
- if (listenerException != null) {
- throw listenerException;
- }
- }
-
- /**
- * Temporarily ends a transaction to let other threads have use of
- * the database. Begins a new transaction after a specified delay.
- *
- * If there are other threads waiting to acquire connections,
- * then the current transaction is committed and the database
- * connection is released. After a short delay, a new transaction
- * is started.
- *
- * The transaction is assumed to be successful so far. Do not call
- * {@link #setTransactionSuccessful()} before calling this method.
- * This method will fail if the transaction has already been marked
- * successful.
- *
- * The changes that were committed by a yield cannot be rolled back later.
- *
- * Before this method was called, there must already have been
- * a transaction in progress. When this method returns, there will
- * still be a transaction in progress, either the same one as before
- * or a new one if the transaction was actually yielded.
- *
- * This method should not be called when there is a nested transaction
- * in progress because it is not possible to yield a nested transaction.
- * If throwIfNested is true, then attempting to yield
- * a nested transaction will throw {@link IllegalStateException}, otherwise
- * the method will return false in that case.
- *
- * If there is no nested transaction in progress but a previous nested
- * transaction failed, then the transaction is not yielded (because it
- * must be rolled back) and this method returns false.
- *
- *
- * @param sleepAfterYieldDelayMillis A delay time to wait after yielding
- * the database connection to allow other threads some time to run.
- * If the value is less than or equal to zero, there will be no additional
- * delay beyond the time it will take to begin a new transaction.
- * @param throwIfUnsafe If true, then instead of returning false when no
- * transaction is in progress, a nested transaction is in progress, or when
- * the transaction has already been marked successful, throws {@link IllegalStateException}.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * @return True if the transaction was actually yielded.
- *
- * @throws IllegalStateException if throwIfNested is true and
- * there is no current transaction, there is a nested transaction in progress or
- * if {@link #setTransactionSuccessful} has already been called for the current transaction.
- * @throws SQLiteException if an error occurs.
- * @throws OperationCanceledException if the operation was canceled.
- *
- * @see #beginTransaction
- * @see #endTransaction
- */
- public boolean yieldTransaction(long sleepAfterYieldDelayMillis, boolean throwIfUnsafe,
- CancellationSignal cancellationSignal) {
- if (throwIfUnsafe) {
- throwIfNoTransaction();
- throwIfTransactionMarkedSuccessful();
- throwIfNestedTransaction();
- } else {
- if (mTransactionStack == null || mTransactionStack.mMarkedSuccessful
- || mTransactionStack.mParent != null) {
- return false;
- }
- }
- assert mConnection != null;
-
- if (mTransactionStack.mChildFailed) {
- return false;
- }
-
- return yieldTransactionUnchecked(sleepAfterYieldDelayMillis,
- cancellationSignal); // might throw
- }
-
- private boolean yieldTransactionUnchecked(long sleepAfterYieldDelayMillis,
- CancellationSignal cancellationSignal) {
- if (cancellationSignal != null) {
- cancellationSignal.throwIfCanceled();
- }
-
- if (!mConnectionPool.shouldYieldConnection(mConnection, mConnectionFlags)) {
- return false;
- }
-
- final int transactionMode = mTransactionStack.mMode;
- final SQLiteTransactionListener listener = mTransactionStack.mListener;
- final int connectionFlags = mConnectionFlags;
- endTransactionUnchecked(cancellationSignal, true); // might throw
-
- if (sleepAfterYieldDelayMillis > 0) {
- try {
- Thread.sleep(sleepAfterYieldDelayMillis);
- } catch (InterruptedException ex) {
- // we have been interrupted, that's all we need to do
- }
- }
-
- beginTransactionUnchecked(transactionMode, listener, connectionFlags,
- cancellationSignal); // might throw
- return true;
- }
-
- /**
- * Prepares a statement for execution but does not bind its parameters or execute it.
- *
- * This method can be used to check for syntax errors during compilation
- * prior to execution of the statement. If the {@code outStatementInfo} argument
- * is not null, the provided {@link SQLiteStatementInfo} object is populated
- * with information about the statement.
- *
- * A prepared statement makes no reference to the arguments that may eventually
- * be bound to it, consequently it it possible to cache certain prepared statements
- * such as SELECT or INSERT/UPDATE statements. If the statement is cacheable,
- * then it will be stored in the cache for later and reused if possible.
- *
- *
- * @param sql The SQL statement to prepare.
- * @param connectionFlags The connection flags to use if a connection must be
- * acquired by this operation. Refer to {@link SQLiteConnectionPool}.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * @param outStatementInfo The {@link SQLiteStatementInfo} object to populate
- * with information about the statement, or null if none.
- *
- * @throws SQLiteException if an error occurs, such as a syntax error.
- * @throws OperationCanceledException if the operation was canceled.
- */
- public void prepare(String sql, int connectionFlags, CancellationSignal cancellationSignal,
- SQLiteStatementInfo outStatementInfo) {
- if (sql == null) {
- throw new IllegalArgumentException("sql must not be null.");
- }
-
- if (cancellationSignal != null) {
- cancellationSignal.throwIfCanceled();
- }
-
- acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
- try {
- mConnection.prepare(sql, outStatementInfo); // might throw
- } finally {
- releaseConnection(); // might throw
- }
- }
-
- /**
- * Executes a statement that does not return a result.
- *
- * @param sql The SQL statement to execute.
- * @param bindArgs The arguments to bind, or null if none.
- * @param connectionFlags The connection flags to use if a connection must be
- * acquired by this operation. Refer to {@link SQLiteConnectionPool}.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- *
- * @throws SQLiteException if an error occurs, such as a syntax error
- * or invalid number of bind arguments.
- * @throws OperationCanceledException if the operation was canceled.
- */
- public void execute(String sql, Object[] bindArgs, int connectionFlags,
- CancellationSignal cancellationSignal) {
- if (sql == null) {
- throw new IllegalArgumentException("sql must not be null.");
- }
-
- if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
- return;
- }
-
- acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
- try {
- mConnection.execute(sql, bindArgs, cancellationSignal); // might throw
- } finally {
- releaseConnection(); // might throw
- }
- }
-
- /**
- * Executes a statement that returns a single long result.
- *
- * @param sql The SQL statement to execute.
- * @param bindArgs The arguments to bind, or null if none.
- * @param connectionFlags The connection flags to use if a connection must be
- * acquired by this operation. Refer to {@link SQLiteConnectionPool}.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * @return The value of the first column in the first row of the result set
- * as a long, or zero if none.
- *
- * @throws SQLiteException if an error occurs, such as a syntax error
- * or invalid number of bind arguments.
- * @throws OperationCanceledException if the operation was canceled.
- */
- public long executeForLong(String sql, Object[] bindArgs, int connectionFlags,
- CancellationSignal cancellationSignal) {
- if (sql == null) {
- throw new IllegalArgumentException("sql must not be null.");
- }
-
- if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
- return 0;
- }
-
- acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
- try {
- return mConnection.executeForLong(sql, bindArgs, cancellationSignal); // might throw
- } finally {
- releaseConnection(); // might throw
- }
- }
-
- /**
- * Executes a statement that returns a single {@link String} result.
- *
- * @param sql The SQL statement to execute.
- * @param bindArgs The arguments to bind, or null if none.
- * @param connectionFlags The connection flags to use if a connection must be
- * acquired by this operation. Refer to {@link SQLiteConnectionPool}.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * @return The value of the first column in the first row of the result set
- * as a String, or null if none.
- *
- * @throws SQLiteException if an error occurs, such as a syntax error
- * or invalid number of bind arguments.
- * @throws OperationCanceledException if the operation was canceled.
- */
- public String executeForString(String sql, Object[] bindArgs, int connectionFlags,
- CancellationSignal cancellationSignal) {
- if (sql == null) {
- throw new IllegalArgumentException("sql must not be null.");
- }
-
- if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
- return null;
- }
-
- acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
- try {
- return mConnection.executeForString(sql, bindArgs, cancellationSignal); // might throw
- } finally {
- releaseConnection(); // might throw
- }
- }
-
- /**
- * Executes a statement that returns a single BLOB result as a
- * file descriptor to a shared memory region.
- *
- * @param sql The SQL statement to execute.
- * @param bindArgs The arguments to bind, or null if none.
- * @param connectionFlags The connection flags to use if a connection must be
- * acquired by this operation. Refer to {@link SQLiteConnectionPool}.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * @return The file descriptor for a shared memory region that contains
- * the value of the first column in the first row of the result set as a BLOB,
- * or null if none.
- *
- * @throws SQLiteException if an error occurs, such as a syntax error
- * or invalid number of bind arguments.
- * @throws OperationCanceledException if the operation was canceled.
- */
- public ParcelFileDescriptor executeForBlobFileDescriptor(String sql, Object[] bindArgs,
- int connectionFlags, CancellationSignal cancellationSignal) {
- if (sql == null) {
- throw new IllegalArgumentException("sql must not be null.");
- }
-
- if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
- return null;
- }
-
- acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
- try {
- return mConnection.executeForBlobFileDescriptor(sql, bindArgs,
- cancellationSignal); // might throw
- } finally {
- releaseConnection(); // might throw
- }
- }
-
- /**
- * Executes a statement that returns a count of the number of rows
- * that were changed. Use for UPDATE or DELETE SQL statements.
- *
- * @param sql The SQL statement to execute.
- * @param bindArgs The arguments to bind, or null if none.
- * @param connectionFlags The connection flags to use if a connection must be
- * acquired by this operation. Refer to {@link SQLiteConnectionPool}.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * @return The number of rows that were changed.
- *
- * @throws SQLiteException if an error occurs, such as a syntax error
- * or invalid number of bind arguments.
- * @throws OperationCanceledException if the operation was canceled.
- */
- public int executeForChangedRowCount(String sql, Object[] bindArgs, int connectionFlags,
- CancellationSignal cancellationSignal) {
- if (sql == null) {
- throw new IllegalArgumentException("sql must not be null.");
- }
-
- if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
- return 0;
- }
-
- acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
- try {
- return mConnection.executeForChangedRowCount(sql, bindArgs,
- cancellationSignal); // might throw
- } finally {
- releaseConnection(); // might throw
- }
- }
-
- /**
- * Executes a statement that returns the row id of the last row inserted
- * by the statement. Use for INSERT SQL statements.
- *
- * @param sql The SQL statement to execute.
- * @param bindArgs The arguments to bind, or null if none.
- * @param connectionFlags The connection flags to use if a connection must be
- * acquired by this operation. Refer to {@link SQLiteConnectionPool}.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * @return The row id of the last row that was inserted, or 0 if none.
- *
- * @throws SQLiteException if an error occurs, such as a syntax error
- * or invalid number of bind arguments.
- * @throws OperationCanceledException if the operation was canceled.
- */
- public long executeForLastInsertedRowId(String sql, Object[] bindArgs, int connectionFlags,
- CancellationSignal cancellationSignal) {
- if (sql == null) {
- throw new IllegalArgumentException("sql must not be null.");
- }
-
- if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
- return 0;
- }
-
- acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
- try {
- return mConnection.executeForLastInsertedRowId(sql, bindArgs,
- cancellationSignal); // might throw
- } finally {
- releaseConnection(); // might throw
- }
- }
-
- /**
- * Executes a statement and populates the specified {@link CursorWindow}
- * with a range of results. Returns the number of rows that were counted
- * during query execution.
- *
- * @param sql The SQL statement to execute.
- * @param bindArgs The arguments to bind, or null if none.
- * @param window The cursor window to clear and fill.
- * @param startPos The start position for filling the window.
- * @param requiredPos The position of a row that MUST be in the window.
- * If it won't fit, then the query should discard part of what it filled
- * so that it does. Must be greater than or equal to startPos.
- * @param countAllRows True to count all rows that the query would return
- * regagless of whether they fit in the window.
- * @param connectionFlags The connection flags to use if a connection must be
- * acquired by this operation. Refer to {@link SQLiteConnectionPool}.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * @return The number of rows that were counted during query execution. Might
- * not be all rows in the result set unless countAllRows is true.
- *
- * @throws SQLiteException if an error occurs, such as a syntax error
- * or invalid number of bind arguments.
- * @throws OperationCanceledException if the operation was canceled.
- */
- public int executeForCursorWindow(String sql, Object[] bindArgs,
- CursorWindow window, int startPos, int requiredPos,
- boolean countAllRows,
- int connectionFlags,
- CancellationSignal cancellationSignal) {
- if (sql == null) {
- throw new IllegalArgumentException("sql must not be null.");
- }
- if (window == null) {
- throw new IllegalArgumentException("window must not be null.");
- }
-
- if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
- window.clear();
- return 0;
- }
-
- acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
- try {
- return mConnection.executeForCursorWindow(sql, bindArgs,
- window, startPos, requiredPos, countAllRows,
- cancellationSignal); // might throw
- } finally {
- releaseConnection(); // might throw
- }
- }
-
- /**
- * Performs special reinterpretation of certain SQL statements such as "BEGIN",
- * "COMMIT" and "ROLLBACK" to ensure that transaction state invariants are
- * maintained.
- *
- * This function is mainly used to support legacy apps that perform their
- * own transactions by executing raw SQL rather than calling {@link #beginTransaction}
- * and the like.
- *
- * @param sql The SQL statement to execute.
- * @param bindArgs The arguments to bind, or null if none.
- * @param connectionFlags The connection flags to use if a connection must be
- * acquired by this operation. Refer to {@link SQLiteConnectionPool}.
- * @param cancellationSignal A signal to cancel the operation in progress, or null if none.
- * @return True if the statement was of a special form that was handled here,
- * false otherwise.
- *
- * @throws SQLiteException if an error occurs, such as a syntax error
- * or invalid number of bind arguments.
- * @throws OperationCanceledException if the operation was canceled.
- */
- private boolean executeSpecial(String sql, Object[] bindArgs, int connectionFlags,
- CancellationSignal cancellationSignal) {
- if (cancellationSignal != null) {
- cancellationSignal.throwIfCanceled();
- }
-
- final int type = SQLiteStatementType.getSqlStatementType(sql);
- switch (type) {
- case SQLiteStatementType.STATEMENT_BEGIN:
- beginTransaction(TRANSACTION_MODE_EXCLUSIVE, null, connectionFlags,
- cancellationSignal);
- return true;
-
- case SQLiteStatementType.STATEMENT_COMMIT:
- setTransactionSuccessful();
- endTransaction(cancellationSignal);
- return true;
-
- case SQLiteStatementType.STATEMENT_ABORT:
- endTransaction(cancellationSignal);
- return true;
- }
- return false;
- }
-
- private void acquireConnection(String sql, int connectionFlags,
- CancellationSignal cancellationSignal) {
- if (mConnection == null) {
- assert mConnectionUseCount == 0;
- mConnection = mConnectionPool.acquireConnection(sql, connectionFlags,
- cancellationSignal); // might throw
- mConnectionFlags = connectionFlags;
- }
- mConnectionUseCount += 1;
- }
-
- private void releaseConnection() {
- assert mConnection != null;
- assert mConnectionUseCount > 0;
- if (--mConnectionUseCount == 0) {
- try {
- mConnectionPool.releaseConnection(mConnection); // might throw
- } finally {
- mConnection = null;
- }
- }
- }
-
- private void throwIfNoTransaction() {
- if (mTransactionStack == null) {
- throw new IllegalStateException("Cannot perform this operation because "
- + "there is no current transaction.");
- }
- }
-
- private void throwIfTransactionMarkedSuccessful() {
- if (mTransactionStack != null && mTransactionStack.mMarkedSuccessful) {
- throw new IllegalStateException("Cannot perform this operation because "
- + "the transaction has already been marked successful. The only "
- + "thing you can do now is call endTransaction().");
- }
- }
-
- private void throwIfNestedTransaction() {
- if (hasNestedTransaction()) {
- throw new IllegalStateException("Cannot perform this operation because "
- + "a nested transaction is in progress.");
- }
- }
-
- private Transaction obtainTransaction(int mode, SQLiteTransactionListener listener) {
- Transaction transaction = mTransactionPool;
- if (transaction != null) {
- mTransactionPool = transaction.mParent;
- transaction.mParent = null;
- transaction.mMarkedSuccessful = false;
- transaction.mChildFailed = false;
- } else {
- transaction = new Transaction();
- }
- transaction.mMode = mode;
- transaction.mListener = listener;
- return transaction;
- }
-
- private void recycleTransaction(Transaction transaction) {
- transaction.mParent = mTransactionPool;
- transaction.mListener = null;
- mTransactionPool = transaction;
- }
-
- private static final class Transaction {
- public Transaction mParent;
- public int mMode;
- public SQLiteTransactionListener mListener;
- public boolean mMarkedSuccessful;
- public boolean mChildFailed;
- }
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteStatement.java b/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteStatement.java
deleted file mode 100644
index 34edd8ec9c..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteStatement.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-
-package io.requery.android.database.sqlite;
-
-import android.database.SQLException;
-import android.database.sqlite.SQLiteDatabaseCorruptException;
-import android.database.sqlite.SQLiteDoneException;
-import android.os.ParcelFileDescriptor;
-import androidx.sqlite.db.SupportSQLiteStatement;
-
-/**
- * Represents a statement that can be executed against a database. The statement
- * cannot return multiple rows or columns, but single value (1 x 1) result sets
- * are supported.
- *
- * This class is not thread-safe.
- *
- */
-@SuppressWarnings("unused")
-public final class SQLiteStatement extends SQLiteProgram implements SupportSQLiteStatement {
-
- SQLiteStatement(SQLiteDatabase db, String sql, Object[] bindArgs) {
- super(db, sql, bindArgs, null);
- }
-
- /**
- * Execute this SQL statement, if it is not a SELECT / INSERT / DELETE / UPDATE, for example
- * CREATE / DROP table, view, trigger, index etc.
- *
- * @throws SQLException If the SQL string is invalid for some reason
- */
- @Override
- public void execute() {
- acquireReference();
- try {
- getSession().execute(getSql(), getBindArgs(), getConnectionFlags(), null);
- } catch (SQLiteDatabaseCorruptException ex) {
- onCorruption();
- throw ex;
- } finally {
- releaseReference();
- }
- }
-
- /**
- * Execute this SQL statement, if the the number of rows affected by execution of this SQL
- * statement is of any importance to the caller - for example, UPDATE / DELETE SQL statements.
- *
- * @return the number of rows affected by this SQL statement execution.
- * @throws SQLException If the SQL string is invalid for some reason
- */
- @Override
- public int executeUpdateDelete() {
- acquireReference();
- try {
- return getSession().executeForChangedRowCount(
- getSql(), getBindArgs(), getConnectionFlags(), null);
- } catch (SQLiteDatabaseCorruptException ex) {
- onCorruption();
- throw ex;
- } finally {
- releaseReference();
- }
- }
-
- /**
- * Execute this SQL statement and return the ID of the row inserted due to this call.
- * The SQL statement should be an INSERT for this to be a useful call.
- *
- * @return the row ID of the last row inserted, if this insert is successful. -1 otherwise.
- *
- * @throws SQLException If the SQL string is invalid for some reason
- */
- @Override
- public long executeInsert() {
- acquireReference();
- try {
- return getSession().executeForLastInsertedRowId(
- getSql(), getBindArgs(), getConnectionFlags(), null);
- } catch (SQLiteDatabaseCorruptException ex) {
- onCorruption();
- throw ex;
- } finally {
- releaseReference();
- }
- }
-
- /**
- * Execute a statement that returns a 1 by 1 table with a numeric value.
- * For example, SELECT COUNT(*) FROM table;
- *
- * @return The result of the query.
- *
- * @throws SQLiteDoneException if the query returns zero rows
- */
- @Override
- public long simpleQueryForLong() {
- acquireReference();
- try {
- return getSession().executeForLong(
- getSql(), getBindArgs(), getConnectionFlags(), null);
- } catch (SQLiteDatabaseCorruptException ex) {
- onCorruption();
- throw ex;
- } finally {
- releaseReference();
- }
- }
-
- /**
- * Execute a statement that returns a 1 by 1 table with a text value.
- * For example, SELECT COUNT(*) FROM table;
- *
- * @return The result of the query.
- *
- * @throws SQLiteDoneException if the query returns zero rows
- */
- @Override
- public String simpleQueryForString() {
- acquireReference();
- try {
- return getSession().executeForString(
- getSql(), getBindArgs(), getConnectionFlags(), null);
- } catch (SQLiteDatabaseCorruptException ex) {
- onCorruption();
- throw ex;
- } finally {
- releaseReference();
- }
- }
-
- /**
- * Executes a statement that returns a 1 by 1 table with a blob value.
- *
- * @return A read-only file descriptor for a copy of the blob value, or {@code null}
- * if the value is null or could not be read for some reason.
- *
- * @throws SQLiteDoneException if the query returns zero rows
- */
- public ParcelFileDescriptor simpleQueryForBlobFileDescriptor() {
- acquireReference();
- try {
- return getSession().executeForBlobFileDescriptor(
- getSql(), getBindArgs(), getConnectionFlags(), null);
- } catch (SQLiteDatabaseCorruptException ex) {
- onCorruption();
- throw ex;
- } finally {
- releaseReference();
- }
- }
-
- @Override
- public String toString() {
- return "SQLiteProgram: " + getSql();
- }
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteStatementInfo.java b/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteStatementInfo.java
deleted file mode 100644
index c8d50bc395..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteStatementInfo.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-
-package io.requery.android.database.sqlite;
-
-/**
- * Describes a SQLite statement.
- *
- * @hide
- */
-public final class SQLiteStatementInfo {
- /**
- * The number of parameters that the statement has.
- */
- public int numParameters;
-
- /**
- * The names of all columns in the result set of the statement.
- */
- public String[] columnNames;
-
- /**
- * True if the statement is read-only.
- */
- public boolean readOnly;
-}
diff --git a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteStatementType.java b/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteStatementType.java
deleted file mode 100644
index 5cb412e751..0000000000
--- a/sqlite-android/src/main/java/io/requery/android/database/sqlite/SQLiteStatementType.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-
-package io.requery.android.database.sqlite;
-
-import java.util.Locale;
-
-class SQLiteStatementType {
-
- /** One of the values returned by {@link #getSqlStatementType(String)}. */
- public static final int STATEMENT_SELECT = 1;
- /** One of the values returned by {@link #getSqlStatementType(String)}. */
- public static final int STATEMENT_UPDATE = 2;
- /** One of the values returned by {@link #getSqlStatementType(String)}. */
- public static final int STATEMENT_ATTACH = 3;
- /** One of the values returned by {@link #getSqlStatementType(String)}. */
- public static final int STATEMENT_BEGIN = 4;
- /** One of the values returned by {@link #getSqlStatementType(String)}. */
- public static final int STATEMENT_COMMIT = 5;
- /** One of the values returned by {@link #getSqlStatementType(String)}. */
- public static final int STATEMENT_ABORT = 6;
- /** One of the values returned by {@link #getSqlStatementType(String)}. */
- public static final int STATEMENT_PRAGMA = 7;
- /** One of the values returned by {@link #getSqlStatementType(String)}. */
- public static final int STATEMENT_DDL = 8;
- /** One of the values returned by {@link #getSqlStatementType(String)}. */
- public static final int STATEMENT_UNPREPARED = 9;
- /** One of the values returned by {@link #getSqlStatementType(String)}. */
- public static final int STATEMENT_OTHER = 99;
-
- private SQLiteStatementType() {
- }
-
- /**
- * Returns one of the following which represent the type of the given SQL statement.
- *
- * {@link #STATEMENT_SELECT}
- * {@link #STATEMENT_UPDATE}
- * {@link #STATEMENT_ATTACH}
- * {@link #STATEMENT_BEGIN}
- * {@link #STATEMENT_COMMIT}
- * {@link #STATEMENT_ABORT}
- * {@link #STATEMENT_OTHER}
- *
- * @param sql the SQL statement whose type is returned by this method
- * @return one of the values listed above
- */
- public static int getSqlStatementType(String sql) {
- sql = sql.trim();
- if (sql.length() < 3) {
- return STATEMENT_OTHER;
- }
- String prefixSql = sql.substring(0, 3);
-
- if (prefixSql.equalsIgnoreCase("SEL")
- || prefixSql.equalsIgnoreCase("WIT")) {
- return STATEMENT_SELECT;
- }
- if (prefixSql.equalsIgnoreCase("INS")
- || prefixSql.equalsIgnoreCase("UPD")
- || prefixSql.equalsIgnoreCase("REP")
- || prefixSql.equalsIgnoreCase("DEL")) {
- return STATEMENT_UPDATE;
- }
- if (prefixSql.equalsIgnoreCase("ATT")) {
- return STATEMENT_ATTACH;
- }
- if (prefixSql.equalsIgnoreCase("COM")
- || prefixSql.equalsIgnoreCase("END")) {
- return STATEMENT_COMMIT;
- }
- if (prefixSql.equalsIgnoreCase("ROL")) {
- return STATEMENT_ABORT;
- }
- if (prefixSql.equalsIgnoreCase("BEG")) {
- return STATEMENT_BEGIN;
- }
- if (prefixSql.equalsIgnoreCase("PRA")) {
- return STATEMENT_PRAGMA;
- }
- if (prefixSql.equalsIgnoreCase("CRE")
- || prefixSql.equalsIgnoreCase("DRO")
- || prefixSql.equalsIgnoreCase("ALT")) {
- return STATEMENT_DDL;
- }
-
- if (prefixSql.equalsIgnoreCase("ANA") || prefixSql.equalsIgnoreCase("DET")) {
- return STATEMENT_UNPREPARED;
- }
-
- return STATEMENT_OTHER;
- }
-}
diff --git a/sqlite-android/src/main/jni/Android.mk b/sqlite-android/src/main/jni/Android.mk
deleted file mode 100644
index e372509cbb..0000000000
--- a/sqlite-android/src/main/jni/Android.mk
+++ /dev/null
@@ -1,4 +0,0 @@
-
-LOCAL_PATH:= $(call my-dir)
-include $(LOCAL_PATH)/sqlite/Android.mk
-
diff --git a/sqlite-android/src/main/jni/Application.mk b/sqlite-android/src/main/jni/Application.mk
deleted file mode 100644
index 89c88ffbf3..0000000000
--- a/sqlite-android/src/main/jni/Application.mk
+++ /dev/null
@@ -1,5 +0,0 @@
-APP_STL:=none
-APP_OPTIM := release
-APP_ABI := armeabi-v7a,arm64-v8a,x86,x86_64
-NDK_TOOLCHAIN_VERSION := clang
-NDK_APP_LIBS_OUT=../jniLibs
diff --git a/sqlite-android/src/main/jni/sqlite/ALog-priv.h b/sqlite-android/src/main/jni/sqlite/ALog-priv.h
deleted file mode 100644
index 04a0abf398..0000000000
--- a/sqlite-android/src/main/jni/sqlite/ALog-priv.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Copyright 2013 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef NATIVEHELPER_ALOGPRIV_H_
-#define NATIVEHELPER_ALOGPRIV_H_
-
-#include
-
-#ifndef LOG_NDEBUG
-#ifdef NDEBUG
-#define LOG_NDEBUG 1
-#else
-#define LOG_NDEBUG 0
-#endif
-#endif
-
-
-/*
- * Basic log message macros intended to emulate the behavior of log/log.h
- * in system core. This should be dependent only on ndk exposed logging
- * functionality.
- */
-
-#ifndef ALOG
-#define ALOG(priority, tag, fmt...) \
- __android_log_print(ANDROID_##priority, tag, fmt)
-#endif
-
-#ifndef ALOGV
-#if LOG_NDEBUG
-#define ALOGV(...) ((void)0)
-#else
-#define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
-#endif
-#endif
-
-#ifndef ALOGD
-#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
-#endif
-
-#ifndef ALOGI
-#define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
-#endif
-
-#ifndef ALOGW
-#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
-#endif
-
-#ifndef ALOGE
-#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
-#endif
-
-/*
-** Not quite the same as the core android LOG_FATAL_IF (which also
-** sends a SIGTRAP), but close enough.
-*/
-#define LOG_FATAL_IF(bCond, zErr) if( bCond ) ALOGE(zErr);
-
-#endif
diff --git a/sqlite-android/src/main/jni/sqlite/Android.mk b/sqlite-android/src/main/jni/sqlite/Android.mk
deleted file mode 100644
index 5d995ff030..0000000000
--- a/sqlite-android/src/main/jni/sqlite/Android.mk
+++ /dev/null
@@ -1,67 +0,0 @@
-LOCAL_PATH:= $(call my-dir)
-include $(CLEAR_VARS)
-
-# NOTE the following flags,
-# SQLITE_TEMP_STORE=3 causes all TEMP files to go into RAM. and thats the behavior we want
-# SQLITE_ENABLE_FTS3 enables usage of FTS3 - NOT FTS1 or 2.
-# SQLITE_DEFAULT_AUTOVACUUM=1 causes the databases to be subject to auto-vacuum
-sqlite_flags := \
- -DNDEBUG=1 \
- -DHAVE_USLEEP=1 \
- -DSQLITE_HAVE_ISNAN \
- -DSQLITE_DEFAULT_JOURNAL_SIZE_LIMIT=1048576 \
- -DSQLITE_THREADSAFE=2 \
- -DSQLITE_TEMP_STORE=3 \
- -DSQLITE_POWERSAFE_OVERWRITE=1 \
- -DSQLITE_DEFAULT_FILE_FORMAT=4 \
- -DSQLITE_DEFAULT_AUTOVACUUM=1 \
- -DSQLITE_ENABLE_MEMORY_MANAGEMENT=1 \
- -DSQLITE_ENABLE_FTS3 \
- -DSQLITE_ENABLE_FTS3_PARENTHESIS \
- -DSQLITE_ENABLE_FTS4 \
- -DSQLITE_ENABLE_FTS4_PARENTHESIS \
- -DSQLITE_ENABLE_FTS5 \
- -DSQLITE_ENABLE_FTS5_PARENTHESIS \
- -D_SQLITE_ENABLE_JSON1 \
- -D_SQLITE_ENABLE_RTREE=1 \
- -DSQLITE_OMIT_BUILTIN_TEST \
- -D_SQLITE_OMIT_COMPILEOPTION_DIAGS \
- -DSQLITE_DEFAULT_FILE_PERMISSIONS=0600 \
- -DSQLITE_DEFAULT_MEMSTATUS=0 \
- -DSQLITE_MAX_EXPR_DEPTH=0 \
- -D_SQLITE_USE_ALLOCA \
- -DSQLITE_ENABLE_BATCH_ATOMIC_WRITE \
- -O3
-
-LOCAL_CFLAGS += $(sqlite_flags)
-LOCAL_CFLAGS += -Wno-unused-parameter -Wno-int-to-pointer-cast
-LOCAL_CFLAGS += -Wno-uninitialized -Wno-parentheses
-LOCAL_CPPFLAGS += -Wno-conversion-null
-
-
-ifeq ($(TARGET_ARCH), arm)
- LOCAL_CFLAGS += -DPACKED="__attribute__ ((packed))"
-else
- LOCAL_CFLAGS += -DPACKED=""
-endif
-
-LOCAL_SRC_FILES:= \
- android_database_SQLiteCommon.cpp \
- android_database_SQLiteConnection.cpp \
- android_database_SQLiteFunction.cpp \
- android_database_SQLiteGlobal.cpp \
- android_database_SQLiteDebug.cpp \
- android_database_CursorWindow.cpp \
- CursorWindow.cpp \
- JNIHelp.cpp \
- JNIString.cpp
-
-LOCAL_SRC_FILES += sqlite3.c
-
-LOCAL_C_INCLUDES += $(LOCAL_PATH)
-
-LOCAL_MODULE:= libsqlite3x
-LOCAL_LDLIBS += -ldl -llog -latomic
-
-include $(BUILD_SHARED_LIBRARY)
-
diff --git a/sqlite-android/src/main/jni/sqlite/CursorWindow.cpp b/sqlite-android/src/main/jni/sqlite/CursorWindow.cpp
deleted file mode 100644
index 3e57543914..0000000000
--- a/sqlite-android/src/main/jni/sqlite/CursorWindow.cpp
+++ /dev/null
@@ -1,283 +0,0 @@
-/*
- * Copyright (C) 2006-2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- // modified from original source see README at the top level of this project
-
-#undef LOG_TAG
-#define LOG_TAG "CursorWindow"
-
-#include "CursorWindow.h"
-#include "ALog-priv.h"
-
-#include
-#include
-#include
-
-namespace android {
-
-CursorWindow::CursorWindow(const char* name, void* data, size_t size, bool readOnly) :
- mData(data), mSize(size), mReadOnly(readOnly) {
- mName = strdup(name);
- mHeader = static_cast(mData);
-}
-
-CursorWindow::~CursorWindow() {
- free(mName);
- free(mData);
-}
-
-status_t CursorWindow::create(const char* name, size_t size, CursorWindow** outWindow) {
- status_t result;
- void* data = malloc(size);
- if (!data) {
- return NO_MEMORY;
- }
- CursorWindow* window = new CursorWindow(name, data, size, false);
- result = window->clear();
- if (!result) {
- LOG_WINDOW("Created new CursorWindow: freeOffset=%d, "
- "numRows=%d, numColumns=%d, mSize=%d, mData=%p",
- window->mHeader->freeOffset,
- window->mHeader->numRows,
- window->mHeader->numColumns,
- window->mSize, window->mData);
- *outWindow = window;
- return OK;
- }
- delete window;
- return result;
-}
-
-status_t CursorWindow::clear() {
- if (mReadOnly) {
- return INVALID_OPERATION;
- }
-
- mHeader->freeOffset = sizeof(Header) + sizeof(RowSlotChunk);
- mHeader->firstChunkOffset = sizeof(Header);
- mHeader->numRows = 0;
- mHeader->numColumns = 0;
-
- RowSlotChunk* firstChunk = static_cast(offsetToPtr(mHeader->firstChunkOffset));
- firstChunk->nextChunkOffset = 0;
- return OK;
-}
-
-status_t CursorWindow::setNumColumns(uint32_t numColumns) {
- if (mReadOnly) {
- return INVALID_OPERATION;
- }
-
- uint32_t cur = mHeader->numColumns;
- if ((cur > 0 || mHeader->numRows > 0) && cur != numColumns) {
- ALOGE("Trying to go from %d columns to %d", cur, numColumns);
- return INVALID_OPERATION;
- }
- mHeader->numColumns = numColumns;
- return OK;
-}
-
-status_t CursorWindow::allocRow() {
- if (mReadOnly) {
- return INVALID_OPERATION;
- }
-
- // Fill in the row slot
- RowSlot* rowSlot = allocRowSlot();
- if (rowSlot == NULL) {
- return NO_MEMORY;
- }
-
- // Allocate the slots for the field directory
- size_t fieldDirSize = mHeader->numColumns * sizeof(FieldSlot);
- uint32_t fieldDirOffset = alloc(fieldDirSize, true /*aligned*/);
- if (!fieldDirOffset) {
- mHeader->numRows--;
- LOG_WINDOW("The row failed, so back out the new row accounting "
- "from allocRowSlot %d", mHeader->numRows);
- return NO_MEMORY;
- }
- FieldSlot* fieldDir = static_cast(offsetToPtr(fieldDirOffset));
- memset(fieldDir, 0, fieldDirSize);
-
- //LOG_WINDOW("Allocated row %u, rowSlot is at offset %u, fieldDir is %d bytes at offset %u\n",
- // mHeader->numRows - 1, offsetFromPtr(rowSlot), fieldDirSize, fieldDirOffset);
- rowSlot->offset = fieldDirOffset;
- return OK;
-}
-
-status_t CursorWindow::freeLastRow() {
- if (mReadOnly) {
- return INVALID_OPERATION;
- }
-
- if (mHeader->numRows > 0) {
- mHeader->numRows--;
- }
- return OK;
-}
-
-uint32_t CursorWindow::alloc(size_t size, bool aligned) {
- uint32_t padding;
- if (aligned) {
- // 4 byte alignment
- padding = (~mHeader->freeOffset + 1) & 3;
- } else {
- padding = 0;
- }
-
- uint32_t offset = mHeader->freeOffset + padding;
- uint32_t nextFreeOffset = offset + size;
- if (nextFreeOffset > mSize) {
- ALOGW("Window is full: requested allocation %zu bytes, "
- "free space %zu bytes, window size %zu bytes",
- size, freeSpace(), mSize);
- return 0;
- }
-
- mHeader->freeOffset = nextFreeOffset;
- return offset;
-}
-
-CursorWindow::RowSlot* CursorWindow::getRowSlot(uint32_t row) {
- uint32_t chunkPos = row;
- RowSlotChunk* chunk = static_cast(
- offsetToPtr(mHeader->firstChunkOffset));
- while (chunkPos >= ROW_SLOT_CHUNK_NUM_ROWS) {
- chunk = static_cast(offsetToPtr(chunk->nextChunkOffset));
- chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS;
- }
- return &chunk->slots[chunkPos];
-}
-
-CursorWindow::RowSlot* CursorWindow::allocRowSlot() {
- uint32_t chunkPos = mHeader->numRows;
- RowSlotChunk* chunk = static_cast(
- offsetToPtr(mHeader->firstChunkOffset));
- while (chunkPos > ROW_SLOT_CHUNK_NUM_ROWS) {
- chunk = static_cast(offsetToPtr(chunk->nextChunkOffset));
- chunkPos -= ROW_SLOT_CHUNK_NUM_ROWS;
- }
- if (chunkPos == ROW_SLOT_CHUNK_NUM_ROWS) {
- if (!chunk->nextChunkOffset) {
- chunk->nextChunkOffset = alloc(sizeof(RowSlotChunk), true /*aligned*/);
- if (!chunk->nextChunkOffset) {
- return NULL;
- }
- }
- chunk = static_cast(offsetToPtr(chunk->nextChunkOffset));
- chunk->nextChunkOffset = 0;
- chunkPos = 0;
- }
- mHeader->numRows += 1;
- return &chunk->slots[chunkPos];
-}
-
-CursorWindow::FieldSlot* CursorWindow::getFieldSlot(uint32_t row, uint32_t column) {
- if (row >= mHeader->numRows || column >= mHeader->numColumns) {
- ALOGE("Failed to read row %d, column %d from a CursorWindow which "
- "has %d rows, %d columns.",
- row, column, mHeader->numRows, mHeader->numColumns);
- return NULL;
- }
- RowSlot* rowSlot = getRowSlot(row);
- if (!rowSlot) {
- ALOGE("Failed to find rowSlot for row %d.", row);
- return NULL;
- }
- FieldSlot* fieldDir = static_cast(offsetToPtr(rowSlot->offset));
- return &fieldDir[column];
-}
-
-status_t CursorWindow::putBlob(uint32_t row, uint32_t column, const void* value, size_t size) {
- return putBlobOrString(row, column, value, size, FIELD_TYPE_BLOB);
-}
-
-status_t CursorWindow::putString(uint32_t row, uint32_t column, const char* value,
- size_t sizeIncludingNull) {
- return putBlobOrString(row, column, value, sizeIncludingNull, FIELD_TYPE_STRING);
-}
-
-status_t CursorWindow::putBlobOrString(uint32_t row, uint32_t column,
- const void* value, size_t size, int32_t type) {
- if (mReadOnly) {
- return INVALID_OPERATION;
- }
-
- FieldSlot* fieldSlot = getFieldSlot(row, column);
- if (!fieldSlot) {
- return BAD_VALUE;
- }
-
- uint32_t offset = alloc(size);
- if (!offset) {
- return NO_MEMORY;
- }
-
- memcpy(offsetToPtr(offset), value, size);
-
- fieldSlot->type = type;
- fieldSlot->data.buffer.offset = offset;
- fieldSlot->data.buffer.size = size;
- return OK;
-}
-
-status_t CursorWindow::putLong(uint32_t row, uint32_t column, int64_t value) {
- if (mReadOnly) {
- return INVALID_OPERATION;
- }
-
- FieldSlot* fieldSlot = getFieldSlot(row, column);
- if (!fieldSlot) {
- return BAD_VALUE;
- }
-
- fieldSlot->type = FIELD_TYPE_INTEGER;
- fieldSlot->data.l = value;
- return OK;
-}
-
-status_t CursorWindow::putDouble(uint32_t row, uint32_t column, double value) {
- if (mReadOnly) {
- return INVALID_OPERATION;
- }
-
- FieldSlot* fieldSlot = getFieldSlot(row, column);
- if (!fieldSlot) {
- return BAD_VALUE;
- }
-
- fieldSlot->type = FIELD_TYPE_FLOAT;
- fieldSlot->data.d = value;
- return OK;
-}
-
-status_t CursorWindow::putNull(uint32_t row, uint32_t column) {
- if (mReadOnly) {
- return INVALID_OPERATION;
- }
-
- FieldSlot* fieldSlot = getFieldSlot(row, column);
- if (!fieldSlot) {
- return BAD_VALUE;
- }
-
- fieldSlot->type = FIELD_TYPE_NULL;
- fieldSlot->data.buffer.offset = 0;
- fieldSlot->data.buffer.size = 0;
- return OK;
-}
-
-}; // namespace android
diff --git a/sqlite-android/src/main/jni/sqlite/CursorWindow.h b/sqlite-android/src/main/jni/sqlite/CursorWindow.h
deleted file mode 100644
index aceeb6347e..0000000000
--- a/sqlite-android/src/main/jni/sqlite/CursorWindow.h
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- // modified from original source see README at the top level of this project
-
-#ifndef _ANDROID__DATABASE_WINDOW_H
-#define _ANDROID__DATABASE_WINDOW_H
-
-#include "ALog-priv.h"
-#include
-#include
-
-#include "Errors.h"
-
-#if LOG_NDEBUG
-
-#define IF_LOG_WINDOW() if (false)
-#define LOG_WINDOW(...)
-
-#else
-
-#define IF_LOG_WINDOW() IF_ALOG(LOG_DEBUG, "CursorWindow")
-#define LOG_WINDOW(...) ALOG(LOG_DEBUG, "CursorWindow", __VA_ARGS__)
-
-#endif
-
-namespace android {
-
-/**
- * This class stores a set of rows from a database in a buffer. The beginning of the
- * window has first chunk of RowSlots, which are offsets to the row directory, followed by
- * an offset to the next chunk in a linked-list of additional chunk of RowSlots in case
- * the pre-allocated chunk isn't big enough to refer to all rows. Each row directory has a
- * FieldSlot per column, which has the size, offset, and type of the data for that field.
- * Note that the data types come from sqlite3.h.
- *
- * Strings are stored in UTF-8.
- */
-class CursorWindow {
- CursorWindow(const char* name, void* data, size_t size, bool readOnly);
-
-public:
- /* Field types. */
- enum {
- FIELD_TYPE_NULL = 0,
- FIELD_TYPE_INTEGER = 1,
- FIELD_TYPE_FLOAT = 2,
- FIELD_TYPE_STRING = 3,
- FIELD_TYPE_BLOB = 4,
- };
-
- /* Opaque type that describes a field slot. */
- struct FieldSlot {
- private:
- int32_t type;
- union {
- double d;
- int64_t l;
- struct {
- uint32_t offset;
- uint32_t size;
- } buffer;
- } data;
-
- friend class CursorWindow;
- } __attribute((packed));
-
- ~CursorWindow();
-
- static status_t create(const char* name, size_t size, CursorWindow** outCursorWindow);
-
- inline const char* name() { return mName; }
- inline size_t size() { return mSize; }
- inline size_t freeSpace() { return mSize - mHeader->freeOffset; }
- inline uint32_t getNumRows() { return mHeader->numRows; }
- inline uint32_t getNumColumns() { return mHeader->numColumns; }
-
- status_t clear();
- status_t setNumColumns(uint32_t numColumns);
-
- /**
- * Allocate a row slot and its directory.
- * The row is initialized will null entries for each field.
- */
- status_t allocRow();
- status_t freeLastRow();
-
- status_t putBlob(uint32_t row, uint32_t column, const void* value, size_t size);
- status_t putString(uint32_t row, uint32_t column, const char* value, size_t sizeIncludingNull);
- status_t putLong(uint32_t row, uint32_t column, int64_t value);
- status_t putDouble(uint32_t row, uint32_t column, double value);
- status_t putNull(uint32_t row, uint32_t column);
-
- /**
- * Gets the field slot at the specified row and column.
- * Returns null if the requested row or column is not in the window.
- */
- FieldSlot* getFieldSlot(uint32_t row, uint32_t column);
-
- inline int32_t getFieldSlotType(FieldSlot* fieldSlot) {
- return fieldSlot->type;
- }
-
- inline int64_t getFieldSlotValueLong(FieldSlot* fieldSlot) {
- return fieldSlot->data.l;
- }
-
- inline double getFieldSlotValueDouble(FieldSlot* fieldSlot) {
- return fieldSlot->data.d;
- }
-
- inline const char* getFieldSlotValueString(FieldSlot* fieldSlot,
- size_t* outSizeIncludingNull) {
- *outSizeIncludingNull = fieldSlot->data.buffer.size;
- return static_cast(offsetToPtr(fieldSlot->data.buffer.offset));
- }
-
- inline const void* getFieldSlotValueBlob(FieldSlot* fieldSlot, size_t* outSize) {
- *outSize = fieldSlot->data.buffer.size;
- return offsetToPtr(fieldSlot->data.buffer.offset);
- }
-
-private:
- static const size_t ROW_SLOT_CHUNK_NUM_ROWS = 100;
-
- struct Header {
- // Offset of the lowest unused byte in the window.
- uint32_t freeOffset;
-
- // Offset of the first row slot chunk.
- uint32_t firstChunkOffset;
-
- uint32_t numRows;
- uint32_t numColumns;
- };
-
- struct RowSlot {
- uint32_t offset;
- };
-
- struct RowSlotChunk {
- RowSlot slots[ROW_SLOT_CHUNK_NUM_ROWS];
- uint32_t nextChunkOffset;
- };
-
- char* mName;
- void* mData;
- size_t mSize;
- bool mReadOnly;
- Header* mHeader;
-
- inline void* offsetToPtr(uint32_t offset) {
- return static_cast(mData) + offset;
- }
-
- inline uint32_t offsetFromPtr(void* ptr) {
- return static_cast(ptr) - static_cast(mData);
- }
-
- /**
- * Allocate a portion of the window. Returns the offset
- * of the allocation, or 0 if there isn't enough space.
- * If aligned is true, the allocation gets 4 byte alignment.
- */
- uint32_t alloc(size_t size, bool aligned = false);
-
- RowSlot* getRowSlot(uint32_t row);
- RowSlot* allocRowSlot();
-
- status_t putBlobOrString(uint32_t row, uint32_t column,
- const void* value, size_t size, int32_t type);
-};
-
-}; // namespace android
-
-#endif
diff --git a/sqlite-android/src/main/jni/sqlite/Errors.h b/sqlite-android/src/main/jni/sqlite/Errors.h
deleted file mode 100644
index 0b75b1926c..0000000000
--- a/sqlite-android/src/main/jni/sqlite/Errors.h
+++ /dev/null
@@ -1,88 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ANDROID_ERRORS_H
-#define ANDROID_ERRORS_H
-
-#include
-#include
-
-namespace android {
-
-// use this type to return error codes
-#ifdef HAVE_MS_C_RUNTIME
-typedef int status_t;
-#else
-typedef int32_t status_t;
-#endif
-
-/* the MS C runtime lacks a few error codes */
-
-/*
- * Error codes.
- * All error codes are negative values.
- */
-
-// Win32 #defines NO_ERROR as well. It has the same value, so there's no
-// real conflict, though it's a bit awkward.
-#ifdef _WIN32
-# undef NO_ERROR
-#endif
-
-enum {
- OK = 0, // Everything's swell.
- NO_ERROR = 0, // No errors.
-
- UNKNOWN_ERROR = 0x80000000,
-
- NO_MEMORY = -ENOMEM,
- INVALID_OPERATION = -ENOSYS,
- BAD_VALUE = -EINVAL,
- BAD_TYPE = 0x80000001,
- NAME_NOT_FOUND = -ENOENT,
- PERMISSION_DENIED = -EPERM,
- NO_INIT = -ENODEV,
- ALREADY_EXISTS = -EEXIST,
- DEAD_OBJECT = -EPIPE,
- FAILED_TRANSACTION = 0x80000002,
- JPARKS_BROKE_IT = -EPIPE,
-#if !defined(HAVE_MS_C_RUNTIME)
- BAD_INDEX = -EOVERFLOW,
- NOT_ENOUGH_DATA = -ENODATA,
- WOULD_BLOCK = -EWOULDBLOCK,
- TIMED_OUT = -ETIMEDOUT,
- UNKNOWN_TRANSACTION = -EBADMSG,
-#else
- BAD_INDEX = -E2BIG,
- NOT_ENOUGH_DATA = 0x80000003,
- WOULD_BLOCK = 0x80000004,
- TIMED_OUT = 0x80000005,
- UNKNOWN_TRANSACTION = 0x80000006,
-#endif
- FDS_NOT_ALLOWED = 0x80000007,
-};
-
-// Restore define; enumeration is in "android" namespace, so the value defined
-// there won't work for Win32 code in a different namespace.
-#ifdef _WIN32
-# define NO_ERROR 0L
-#endif
-
-}; // namespace android
-
-// ---------------------------------------------------------------------------
-
-#endif // ANDROID_ERRORS_H
diff --git a/sqlite-android/src/main/jni/sqlite/JNIHelp.cpp b/sqlite-android/src/main/jni/sqlite/JNIHelp.cpp
deleted file mode 100644
index c153429616..0000000000
--- a/sqlite-android/src/main/jni/sqlite/JNIHelp.cpp
+++ /dev/null
@@ -1,217 +0,0 @@
-/*
- * Copyright (C) 2006 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#define LOG_TAG "JNIHelp"
-
-#include "JNIHelp.h"
-#include "ALog-priv.h"
-
-#include
-#include
-#include
-#include
-
-/**
- * Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.)
- */
-template
-class scoped_local_ref {
-public:
- scoped_local_ref(C_JNIEnv* env, T localRef = NULL)
- : mEnv(env), mLocalRef(localRef)
- {
- }
-
- ~scoped_local_ref() {
- reset();
- }
-
- void reset(T localRef = NULL) {
- if (mLocalRef != NULL) {
- (*mEnv)->DeleteLocalRef(reinterpret_cast(mEnv), mLocalRef);
- mLocalRef = localRef;
- }
- }
-
- T get() const {
- return mLocalRef;
- }
-
-private:
- C_JNIEnv* mEnv;
- T mLocalRef;
-
- // Disallow copy and assignment.
- scoped_local_ref(const scoped_local_ref&);
- void operator=(const scoped_local_ref&);
-};
-
-static jclass findClass(C_JNIEnv* env, const char* className) {
- JNIEnv* e = reinterpret_cast(env);
- return (*env)->FindClass(e, className);
-}
-
-extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
- const JNINativeMethod* gMethods, int numMethods)
-{
- JNIEnv* e = reinterpret_cast(env);
-
- ALOGV("Registering %s's %d native methods...", className, numMethods);
-
- scoped_local_ref c(env, findClass(env, className));
- if (c.get() == NULL) {
- char* msg;
- asprintf(&msg, "Native registration unable to find class '%s'; aborting...", className);
- e->FatalError(msg);
- }
-
- if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
- char* msg;
- asprintf(&msg, "RegisterNatives failed for '%s'; aborting...", className);
- e->FatalError(msg);
- }
-
- return 0;
-}
-
-/*
- * Returns a human-readable summary of an exception object. The buffer will
- * be populated with the "binary" class name and, if present, the
- * exception message.
- */
-static bool logExceptionSummary(C_JNIEnv *env, jthrowable exception,
- const char* exceptionClassName) {
- JNIEnv* e = reinterpret_cast(env);
-
- /* get the name of the exception's class */
- scoped_local_ref exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
- scoped_local_ref classClass(env,
- (*env)->GetObjectClass(e, exceptionClass.get())); // java.lang.Class, can't fail
- jmethodID classGetNameMethod =
- (*env)->GetMethodID(e, classClass.get(), "getName", "()Ljava/lang/String;");
- scoped_local_ref classNameStr(env,
- (jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod));
- if (classNameStr.get() == NULL) {
- (*env)->ExceptionClear(e);
- ALOGW("Discarding pending exception (%s) to throw %s", "",
- exceptionClassName);
- return false;
- }
- const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL);
- if (classNameChars == NULL) {
- (*env)->ExceptionClear(e);
- ALOGW("Discarding pending exception (%s) to throw %s", "",
- exceptionClassName);
- return false;
- }
- (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars);
-
- /* if the exception has a detail message, get that */
- jmethodID getMessage =
- (*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;");
- scoped_local_ref messageStr(env,
- (jstring) (*env)->CallObjectMethod(e, exception, getMessage));
- if (messageStr.get() == NULL) {
- return true;
- }
-
- const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
- if (messageChars != NULL) {
- ALOGW("Discarding pending exception (%s: %s) to throw %s",
- classNameChars,
- messageChars,
- exceptionClassName);
- (*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars);
- } else {
- ALOGW("Discarding pending exception (%s: ) to throw %s",
- classNameChars,
- exceptionClassName);
- (*env)->ExceptionClear(e); // clear OOM
- }
-
- return true;
-}
-
-extern "C" int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) {
- JNIEnv* e = reinterpret_cast(env);
-
- if ((*env)->ExceptionCheck(e)) {
- /* TODO: consider creating the new exception with this as "cause" */
- scoped_local_ref exception(env, (*env)->ExceptionOccurred(e));
- (*env)->ExceptionClear(e);
-
- if (exception.get() != NULL) {
- logExceptionSummary(env, exception.get(), className);
- }
- }
-
- scoped_local_ref exceptionClass(env, findClass(env, className));
- if (exceptionClass.get() == NULL) {
- ALOGE("Unable to find exception class %s", className);
- /* ClassNotFoundException now pending */
- return -1;
- }
-
- if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) {
- ALOGE("Failed throwing '%s' '%s'", className, msg);
- /* an exception, most likely OOM, will now be pending */
- return -1;
- }
-
- return 0;
-}
-
-int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) {
- char msgBuf[512];
- vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
- return jniThrowException(env, className, msgBuf);
-}
-
-int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) {
- return jniThrowException(env, "java/lang/NullPointerException", msg);
-}
-
-int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) {
- return jniThrowException(env, "java/lang/RuntimeException", msg);
-}
-
-int jniThrowIOException(C_JNIEnv* env, int errnum) {
- char buffer[80];
- const char* message = jniStrError(errnum, buffer, sizeof(buffer));
- return jniThrowException(env, "java/io/IOException", message);
-}
-
-const char* jniStrError(int errnum, char* buf, size_t buflen) {
-#if __GLIBC__
- // Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int.
- // char *strerror_r(int errnum, char *buf, size_t n);
- return strerror_r(errnum, buf, buflen);
-#else
- int rc = strerror_r(errnum, buf, buflen);
- if (rc != 0) {
- // (POSIX only guarantees a value other than 0. The safest
- // way to implement this function is to use C++ and overload on the
- // type of strerror_r to accurately distinguish GNU from POSIX.)
- snprintf(buf, buflen, "errno %d", errnum);
- }
- return buf;
-#endif
-}
-
-void* operator new (size_t size) { return malloc(size); }
-void* operator new [] (size_t size) { return malloc(size); }
-void operator delete (void* pointer) { free(pointer); }
-void operator delete [] (void* pointer) { free(pointer); }
diff --git a/sqlite-android/src/main/jni/sqlite/JNIHelp.h b/sqlite-android/src/main/jni/sqlite/JNIHelp.h
deleted file mode 100644
index 33a836fe83..0000000000
--- a/sqlite-android/src/main/jni/sqlite/JNIHelp.h
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-/*
- * JNI helper functions.
- *
- * This file may be included by C or C++ code, which is trouble because jni.h
- * uses different typedefs for JNIEnv in each language.
- *
- * TODO: remove C support.
- */
-#ifndef NATIVEHELPER_JNIHELP_H_
-#define NATIVEHELPER_JNIHELP_H_
-
-#include "jni.h"
-#include
-
-#ifndef NELEM
-# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/*
- * Register one or more native methods with a particular class.
- * "className" looks like "java/lang/String". Aborts on failure.
- * TODO: fix all callers and change the return type to void.
- */
-int jniRegisterNativeMethods(C_JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods);
-
-/*
- * Throw an exception with the specified class and an optional message.
- *
- * The "className" argument will be passed directly to FindClass, which
- * takes strings with slashes (e.g. "java/lang/Object").
- *
- * If an exception is currently pending, we log a warning message and
- * clear it.
- *
- * Returns 0 on success, nonzero if something failed (e.g. the exception
- * class couldn't be found, so *an* exception will still be pending).
- *
- * Currently aborts the VM if it can't throw the exception.
- */
-int jniThrowException(C_JNIEnv* env, const char* className, const char* msg);
-
-/*
- * Throw a java.lang.NullPointerException, with an optional message.
- */
-int jniThrowNullPointerException(C_JNIEnv* env, const char* msg);
-
-/*
- * Throw a java.lang.RuntimeException, with an optional message.
- */
-int jniThrowRuntimeException(C_JNIEnv* env, const char* msg);
-
-/*
- * Throw a java.io.IOException, generating the message from errno.
- */
-int jniThrowIOException(C_JNIEnv* env, int errnum);
-
-/*
- * Return a pointer to a locale-dependent error string explaining errno
- * value 'errnum'. The returned pointer may or may not be equal to 'buf'.
- * This function is thread-safe (unlike strerror) and portable (unlike
- * strerror_r).
- */
-const char* jniStrError(int errnum, char* buf, size_t buflen);
-
-/*
- * Returns a new java.io.FileDescriptor for the given int fd.
- */
-jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd);
-
-/*
- * Returns the int fd from a java.io.FileDescriptor.
- */
-int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor);
-
-/*
- * Sets the int fd in a java.io.FileDescriptor.
- */
-void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value);
-
-/*
- * Returns the reference from a java.lang.ref.Reference.
- */
-jobject jniGetReferent(C_JNIEnv* env, jobject ref);
-
-#ifdef __cplusplus
-}
-#endif
-
-
-/*
- * For C++ code, we provide inlines that map to the C functions. g++ always
- * inlines these, even on non-optimized builds.
- */
-#if defined(__cplusplus)
-inline int jniRegisterNativeMethods(JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods) {
- return jniRegisterNativeMethods(&env->functions, className, gMethods, numMethods);
-}
-
-inline int jniThrowException(JNIEnv* env, const char* className, const char* msg) {
- return jniThrowException(&env->functions, className, msg);
-}
-
-extern "C" int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args);
-
-/*
- * Equivalent to jniThrowException but with a printf-like format string and
- * variable-length argument list. This is only available in C++.
- */
-inline int jniThrowExceptionFmt(JNIEnv* env, const char* className, const char* fmt, ...) {
- va_list args;
- va_start(args, fmt);
- return jniThrowExceptionFmt(&env->functions, className, fmt, args);
- va_end(args);
-}
-
-inline int jniThrowNullPointerException(JNIEnv* env, const char* msg) {
- return jniThrowNullPointerException(&env->functions, msg);
-}
-
-inline int jniThrowRuntimeException(JNIEnv* env, const char* msg) {
- return jniThrowRuntimeException(&env->functions, msg);
-}
-
-inline int jniThrowIOException(JNIEnv* env, int errnum) {
- return jniThrowIOException(&env->functions, errnum);
-}
-
-inline jobject jniCreateFileDescriptor(JNIEnv* env, int fd) {
- return jniCreateFileDescriptor(&env->functions, fd);
-}
-
-inline int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor) {
- return jniGetFDFromFileDescriptor(&env->functions, fileDescriptor);
-}
-
-inline void jniSetFileDescriptorOfFD(JNIEnv* env, jobject fileDescriptor, int value) {
- jniSetFileDescriptorOfFD(&env->functions, fileDescriptor, value);
-}
-
-inline jobject jniGetReferent(JNIEnv* env, jobject ref) {
- return jniGetReferent(&env->functions, ref);
-}
-
-#endif
-
-#define FIND_CLASS(var, className) \
- var = env->FindClass(className); \
- LOG_FATAL_IF(! var, "Unable to find class " className);
-
-#define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \
- var = env->GetMethodID(clazz, methodName, fieldDescriptor); \
- LOG_FATAL_IF(! var, "Unable to find method" methodName);
-
-#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
- var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
- LOG_FATAL_IF(! var, "Unable to find field " fieldName);
-
-#endif /* NATIVEHELPER_JNIHELP_H_ */
diff --git a/sqlite-android/src/main/jni/sqlite/JNIString.cpp b/sqlite-android/src/main/jni/sqlite/JNIString.cpp
deleted file mode 100644
index ca758b1e96..0000000000
--- a/sqlite-android/src/main/jni/sqlite/JNIString.cpp
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements. See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-// Note this code is adapted from AOSP implementation of String, now located at
-// https://android.googlesource.com/platform/libcore/+/master/libart/src/main/java/java/lang/StringFactory.java
-
-#include
-
-#define REPLACEMENT_CHAR 0xfffd;
-
-namespace android {
-
-jsize utf8ToJavaCharArray(const char* d, jchar v[], jint byteCount) {
- jint idx = 0;
- jint last = byteCount;
- jint s = 0;
-outer:
- while (idx < last) {
- jbyte b0 = d[idx++];
- if ((b0 & 0x80) == 0) {
- // 0xxxxxxx
- // Range: U-00000000 - U-0000007F
- jint val = b0 & 0xff;
- v[s++] = (jchar) val;
- } else if (((b0 & 0xe0) == 0xc0) || ((b0 & 0xf0) == 0xe0) ||
- ((b0 & 0xf8) == 0xf0) || ((b0 & 0xfc) == 0xf8) || ((b0 & 0xfe) == 0xfc)) {
- jint utfCount = 1;
- if ((b0 & 0xf0) == 0xe0) utfCount = 2;
- else if ((b0 & 0xf8) == 0xf0) utfCount = 3;
- else if ((b0 & 0xfc) == 0xf8) utfCount = 4;
- else if ((b0 & 0xfe) == 0xfc) utfCount = 5;
-
- // 110xxxxx (10xxxxxx)+
- // Range: U-00000080 - U-000007FF (count == 1)
- // Range: U-00000800 - U-0000FFFF (count == 2)
- // Range: U-00010000 - U-001FFFFF (count == 3)
- // Range: U-00200000 - U-03FFFFFF (count == 4)
- // Range: U-04000000 - U-7FFFFFFF (count == 5)
-
- if (idx + utfCount > last) {
- v[s++] = REPLACEMENT_CHAR;
- continue;
- }
-
- // Extract usable bits from b0
- jint val = b0 & (0x1f >> (utfCount - 1));
- for (int i = 0; i < utfCount; ++i) {
- jbyte b = d[idx++];
- if ((b & 0xc0) != 0x80) {
- v[s++] = REPLACEMENT_CHAR;
- idx--; // Put the input char back
- goto outer;
- }
- // Push new bits in from the right side
- val <<= 6;
- val |= b & 0x3f;
- }
-
- // Note: Java allows overlong char
- // specifications To disallow, check that val
- // is greater than or equal to the minimum
- // value for each count:
- //
- // count min value
- // ----- ----------
- // 1 0x80
- // 2 0x800
- // 3 0x10000
- // 4 0x200000
- // 5 0x4000000
-
- // Allow surrogate values (0xD800 - 0xDFFF) to
- // be specified using 3-byte UTF values only
- if ((utfCount != 2) && (val >= 0xD800) && (val <= 0xDFFF)) {
- v[s++] = REPLACEMENT_CHAR;
- continue;
- }
-
- // Reject chars greater than the Unicode maximum of U+10FFFF.
- if (val > 0x10FFFF) {
- v[s++] = REPLACEMENT_CHAR;
- continue;
- }
-
- // Encode chars from U+10000 up as surrogate pairs
- if (val < 0x10000) {
- v[s++] = (jchar) val;
- } else {
- int x = val & 0xffff;
- int u = (val >> 16) & 0x1f;
- int w = (u - 1) & 0xffff;
- int hi = 0xd800 | (w << 6) | (x >> 10);
- int lo = 0xdc00 | (x & 0x3ff);
- v[s++] = (jchar) hi;
- v[s++] = (jchar) lo;
- }
- } else {
- // Illegal values 0x8*, 0x9*, 0xa*, 0xb*, 0xfd-0xff
- v[s++] = REPLACEMENT_CHAR;
- }
- }
- return s;
-}
-}
\ No newline at end of file
diff --git a/sqlite-android/src/main/jni/sqlite/README b/sqlite-android/src/main/jni/sqlite/README
deleted file mode 100644
index da09588fda..0000000000
--- a/sqlite-android/src/main/jni/sqlite/README
+++ /dev/null
@@ -1,32 +0,0 @@
-
-All the files in this directory are copied from stock android. The following
-files:
-
-JNIHelp.cpp
-ALog-priv.h
-
-are copied in from Android's libnativehelper module (altogether less than 1000
-lines of code). The remainder are from the core framework (directory
-/frameworks/base/core/jni).
-
-Notes on changes:
-
-The ashmem_XXX() interfaces are used for the various "xxxForBlobDescriptor()"
-API functions. The code in libcutils for this seems to be platform
-dependent - some platforms have kernel support, others have a user space
-implementation. So these functions are not supported for now.
-
-The original SQLiteConnection.cpp uses AndroidRuntime::genJNIEnv() to obtain a
-pointer to the current threads environment. Changed to store a pointer to the
-process JavaVM (Android allows only one) as a global variable. Then retrieve
-the JNIEnv as needed using GetEnv().
-
-Replaced uses of class String8 with std::string in SQLiteConnection.cpp and a
-few other places.
-
-The "LOCALIZED" collation and some miscellaneous user-functions added by the
-sqlite3_android.cpp module are not included. A collation called LOCALIZED
-that is equivalent to BINARY is added instead to keep various things working.
-This should not cause serious problems - class SQLiteConnection always
-runs "REINDEX LOCALIZED" immediately after opening a connection.
-
diff --git a/sqlite-android/src/main/jni/sqlite/android_database_CursorWindow.cpp b/sqlite-android/src/main/jni/sqlite/android_database_CursorWindow.cpp
deleted file mode 100644
index cda6093037..0000000000
--- a/sqlite-android/src/main/jni/sqlite/android_database_CursorWindow.cpp
+++ /dev/null
@@ -1,412 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- // modified from original source see README at the top level of this project
-
-#undef LOG_TAG
-#define LOG_TAG "CursorWindow"
-#define __STDC_FORMAT_MACROS
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "CursorWindow.h"
-#include "android_database_SQLiteCommon.h"
-
-namespace android {
-
-static struct {
- jfieldID data;
- jfieldID sizeCopied;
-} gCharArrayBufferClassInfo;
-
-static jstring gEmptyString = NULL;
-
-static void throwExceptionWithRowCol(JNIEnv* env, jint row, jint column) {
- char buf[64];
- snprintf(buf, sizeof(buf), "Couldn't read row %d column %d", row, column);
- jniThrowException(env, "java/lang/IllegalStateException", buf);
-}
-
-static void throwUnknownTypeException(JNIEnv * env, jint type) {
- char buf[32];
- snprintf(buf, sizeof(buf), "UNKNOWN type %d", type);
- jniThrowException(env, "java/lang/IllegalStateException", buf);
-}
-
-static jlong nativeCreate(JNIEnv* env, jclass clazz, jstring nameObj, jint cursorWindowSize) {
- CursorWindow* window;
- const char* nameStr = env->GetStringUTFChars(nameObj, NULL);
- status_t status = CursorWindow::create(nameStr, cursorWindowSize, &window);
- env->ReleaseStringUTFChars(nameObj, nameStr);
-
- if (status || !window) {
- ALOGE("Could not allocate CursorWindow of size %d due to error %d.",
- cursorWindowSize, status);
- return 0;
- }
-
- LOG_WINDOW("nativeInitializeEmpty: window = %p", window);
- return reinterpret_cast(window);
-}
-
-static void nativeDispose(JNIEnv* env, jclass clazz, jlong windowPtr) {
- CursorWindow* window = reinterpret_cast(windowPtr);
- if (window) {
- LOG_WINDOW("Closing window %p", window);
- delete window;
- }
-}
-
-static jstring nativeGetName(JNIEnv* env, jclass clazz, jlong windowPtr) {
- CursorWindow* window = reinterpret_cast(windowPtr);
- return env->NewStringUTF(window->name());
-}
-
-static void nativeClear(JNIEnv * env, jclass clazz, jlong windowPtr) {
- CursorWindow* window = reinterpret_cast(windowPtr);
- LOG_WINDOW("Clearing window %p", window);
- status_t status = window->clear();
- if (status) {
- LOG_WINDOW("Could not clear window. error=%d", status);
- }
-}
-
-static jint nativeGetNumRows(JNIEnv* env, jclass clazz, jlong windowPtr) {
- CursorWindow* window = reinterpret_cast(windowPtr);
- return window->getNumRows();
-}
-
-static jboolean nativeSetNumColumns(JNIEnv* env, jclass clazz, jlong windowPtr,
- jint columnNum) {
- CursorWindow* window = reinterpret_cast(windowPtr);
- status_t status = window->setNumColumns(columnNum);
- return status == OK;
-}
-
-static jboolean nativeAllocRow(JNIEnv* env, jclass clazz, jlong windowPtr) {
- CursorWindow* window = reinterpret_cast(windowPtr);
- status_t status = window->allocRow();
- return status == OK;
-}
-
-static void nativeFreeLastRow(JNIEnv* env, jclass clazz, jlong windowPtr) {
- CursorWindow* window = reinterpret_cast(windowPtr);
- window->freeLastRow();
-}
-
-static jint nativeGetType(JNIEnv* env, jclass clazz, jlong windowPtr,
- jint row, jint column) {
- CursorWindow* window = reinterpret_cast(windowPtr);
- LOG_WINDOW("returning column type affinity for %d,%d from %p", row, column, window);
-
- CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
- if (!fieldSlot) {
- return CursorWindow::FIELD_TYPE_NULL;
- }
- return window->getFieldSlotType(fieldSlot);
-}
-
-static jbyteArray nativeGetBlob(JNIEnv* env, jclass clazz, jlong windowPtr,
- jint row, jint column) {
- CursorWindow* window = reinterpret_cast(windowPtr);
- //LOG_WINDOW("Getting blob for %d,%d from %p", row, column, window);
-
- CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
- if (!fieldSlot) {
- throwExceptionWithRowCol(env, row, column);
- return NULL;
- }
-
- int32_t type = window->getFieldSlotType(fieldSlot);
- if (type == CursorWindow::FIELD_TYPE_BLOB || type == CursorWindow::FIELD_TYPE_STRING) {
- size_t size;
- const void* value = window->getFieldSlotValueBlob(fieldSlot, &size);
- jbyteArray byteArray = env->NewByteArray(size);
- if (!byteArray) {
- env->ExceptionClear();
- throw_sqlite3_exception(env, "Native could not create new byte[]");
- return NULL;
- }
- env->SetByteArrayRegion(byteArray, 0, size, static_cast(value));
- return byteArray;
- } else if (type == CursorWindow::FIELD_TYPE_INTEGER) {
- throw_sqlite3_exception(env, "INTEGER data in nativeGetBlob ");
- } else if (type == CursorWindow::FIELD_TYPE_FLOAT) {
- throw_sqlite3_exception(env, "FLOAT data in nativeGetBlob ");
- } else if (type == CursorWindow::FIELD_TYPE_NULL) {
- // do nothing
- } else {
- throwUnknownTypeException(env, type);
- }
- return NULL;
-}
-
-extern int utf8ToJavaCharArray(const char* d, jchar v[], jint byteCount);
-
-static jstring nativeGetString(JNIEnv* env, jclass clazz, jlong windowPtr,
- jint row, jint column) {
- CursorWindow* window = reinterpret_cast(windowPtr);
- //LOG_WINDOW("Getting string for %d,%d from %p", row, column, window);
-
- CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
- if (!fieldSlot) {
- throwExceptionWithRowCol(env, row, column);
- return NULL;
- }
-
- int32_t type = window->getFieldSlotType(fieldSlot);
- if (type == CursorWindow::FIELD_TYPE_STRING) {
- size_t sizeIncludingNull;
- const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull);
- if (sizeIncludingNull <= 1) {
- return gEmptyString;
- }
- const size_t MaxStackStringSize = 65536; // max size for a stack char array
- if (sizeIncludingNull > MaxStackStringSize) {
- jchar* chars = new jchar[sizeIncludingNull - 1];
- jint size = utf8ToJavaCharArray(value, chars, sizeIncludingNull - 1);
- jstring string = env->NewString(chars, size);
- delete[] chars;
- return string;
- } else {
- jchar chars[sizeIncludingNull - 1];
- jint size = utf8ToJavaCharArray(value, chars, sizeIncludingNull - 1);
- return env->NewString(chars, size);
- }
- } else if (type == CursorWindow::FIELD_TYPE_INTEGER) {
- int64_t value = window->getFieldSlotValueLong(fieldSlot);
- char buf[32];
- snprintf(buf, sizeof(buf), "%" PRId64, value);
- return env->NewStringUTF(buf);
- } else if (type == CursorWindow::FIELD_TYPE_FLOAT) {
- double value = window->getFieldSlotValueDouble(fieldSlot);
- char buf[32];
- snprintf(buf, sizeof(buf), "%g", value);
- return env->NewStringUTF(buf);
- } else if (type == CursorWindow::FIELD_TYPE_NULL) {
- return NULL;
- } else if (type == CursorWindow::FIELD_TYPE_BLOB) {
- throw_sqlite3_exception(env, "Unable to convert BLOB to string");
- return NULL;
- } else {
- throwUnknownTypeException(env, type);
- return NULL;
- }
-}
-
-static jlong nativeGetLong(JNIEnv* env, jclass clazz, jlong windowPtr,
- jint row, jint column) {
- CursorWindow* window = reinterpret_cast(windowPtr);
- //LOG_WINDOW("Getting long for %d,%d from %p", row, column, window);
-
- CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
- if (!fieldSlot) {
- throwExceptionWithRowCol(env, row, column);
- return 0;
- }
-
- int32_t type = window->getFieldSlotType(fieldSlot);
- if (type == CursorWindow::FIELD_TYPE_INTEGER) {
- return window->getFieldSlotValueLong(fieldSlot);
- } else if (type == CursorWindow::FIELD_TYPE_STRING) {
- size_t sizeIncludingNull;
- const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull);
- return sizeIncludingNull > 1 ? strtoll(value, NULL, 0) : 0L;
- } else if (type == CursorWindow::FIELD_TYPE_FLOAT) {
- return jlong(window->getFieldSlotValueDouble(fieldSlot));
- } else if (type == CursorWindow::FIELD_TYPE_NULL) {
- return 0;
- } else if (type == CursorWindow::FIELD_TYPE_BLOB) {
- throw_sqlite3_exception(env, "Unable to convert BLOB to long");
- return 0;
- } else {
- throwUnknownTypeException(env, type);
- return 0;
- }
-}
-
-static jdouble nativeGetDouble(JNIEnv* env, jclass clazz, jlong windowPtr,
- jint row, jint column) {
- CursorWindow* window = reinterpret_cast(windowPtr);
- //LOG_WINDOW("Getting double for %d,%d from %p", row, column, window);
-
- CursorWindow::FieldSlot* fieldSlot = window->getFieldSlot(row, column);
- if (!fieldSlot) {
- throwExceptionWithRowCol(env, row, column);
- return 0.0;
- }
-
- int32_t type = window->getFieldSlotType(fieldSlot);
- if (type == CursorWindow::FIELD_TYPE_FLOAT) {
- return window->getFieldSlotValueDouble(fieldSlot);
- } else if (type == CursorWindow::FIELD_TYPE_STRING) {
- size_t sizeIncludingNull;
- const char* value = window->getFieldSlotValueString(fieldSlot, &sizeIncludingNull);
- return sizeIncludingNull > 1 ? strtod(value, NULL) : 0.0;
- } else if (type == CursorWindow::FIELD_TYPE_INTEGER) {
- return jdouble(window->getFieldSlotValueLong(fieldSlot));
- } else if (type == CursorWindow::FIELD_TYPE_NULL) {
- return 0.0;
- } else if (type == CursorWindow::FIELD_TYPE_BLOB) {
- throw_sqlite3_exception(env, "Unable to convert BLOB to double");
- return 0.0;
- } else {
- throwUnknownTypeException(env, type);
- return 0.0;
- }
-}
-
-static jboolean nativePutBlob(JNIEnv* env, jclass clazz, jlong windowPtr,
- jbyteArray valueObj, jint row, jint column) {
- CursorWindow* window = reinterpret_cast(windowPtr);
- jsize len = env->GetArrayLength(valueObj);
-
- void* value = env->GetPrimitiveArrayCritical(valueObj, NULL);
- status_t status = window->putBlob(row, column, value, len);
- env->ReleasePrimitiveArrayCritical(valueObj, value, JNI_ABORT);
-
- if (status) {
- LOG_WINDOW("Failed to put blob. error=%d", status);
- return false;
- }
-
- LOG_WINDOW("%d,%d is BLOB with %u bytes", row, column, len);
- return true;
-}
-
-static jboolean nativePutString(JNIEnv* env, jclass clazz, jlong windowPtr,
- jstring valueObj, jint row, jint column) {
- CursorWindow* window = reinterpret_cast(windowPtr);
-
- size_t sizeIncludingNull = env->GetStringUTFLength(valueObj) + 1;
- const char* valueStr = env->GetStringUTFChars(valueObj, NULL);
- if (!valueStr) {
- LOG_WINDOW("value can't be transferred to UTFChars");
- return false;
- }
- status_t status = window->putString(row, column, valueStr, sizeIncludingNull);
- env->ReleaseStringUTFChars(valueObj, valueStr);
-
- if (status) {
- LOG_WINDOW("Failed to put string. error=%d", status);
- return false;
- }
-
- LOG_WINDOW("%d,%d is TEXT with %u bytes", row, column, sizeIncludingNull);
- return true;
-}
-
-static jboolean nativePutLong(JNIEnv* env, jclass clazz, jlong windowPtr,
- jlong value, jint row, jint column) {
- CursorWindow* window = reinterpret_cast(windowPtr);
- status_t status = window->putLong(row, column, value);
-
- if (status) {
- LOG_WINDOW("Failed to put long. error=%d", status);
- return false;
- }
-
- LOG_WINDOW("%d,%d is INTEGER 0x%016llx", row, column, value);
- return true;
-}
-
-static jboolean nativePutDouble(JNIEnv* env, jclass clazz, jlong windowPtr,
- jdouble value, jint row, jint column) {
- CursorWindow* window = reinterpret_cast(windowPtr);
- status_t status = window->putDouble(row, column, value);
-
- if (status) {
- LOG_WINDOW("Failed to put double. error=%d", status);
- return false;
- }
-
- LOG_WINDOW("%d,%d is FLOAT %lf", row, column, value);
- return true;
-}
-
-static jboolean nativePutNull(JNIEnv* env, jclass clazz, jlong windowPtr,
- jint row, jint column) {
- CursorWindow* window = reinterpret_cast(windowPtr);
- status_t status = window->putNull(row, column);
-
- if (status) {
- LOG_WINDOW("Failed to put null. error=%d", status);
- return false;
- }
-
- LOG_WINDOW("%d,%d is NULL", row, column);
- return true;
-}
-
-static const JNINativeMethod sMethods[] =
-{
- /* name, signature, funcPtr */
- { "nativeCreate", "(Ljava/lang/String;I)J",
- (void*)nativeCreate },
- { "nativeDispose", "(J)V",
- (void*)nativeDispose },
- { "nativeGetName", "(J)Ljava/lang/String;",
- (void*)nativeGetName },
- { "nativeClear", "(J)V",
- (void*)nativeClear },
- { "nativeGetNumRows", "(J)I",
- (void*)nativeGetNumRows },
- { "nativeSetNumColumns", "(JI)Z",
- (void*)nativeSetNumColumns },
- { "nativeAllocRow", "(J)Z",
- (void*)nativeAllocRow },
- { "nativeFreeLastRow", "(J)V",
- (void*)nativeFreeLastRow },
- { "nativeGetType", "(JII)I",
- (void*)nativeGetType },
- { "nativeGetBlob", "(JII)[B",
- (void*)nativeGetBlob },
- { "nativeGetString", "(JII)Ljava/lang/String;",
- (void*)nativeGetString },
- { "nativeGetLong", "(JII)J",
- (void*)nativeGetLong },
- { "nativeGetDouble", "(JII)D",
- (void*)nativeGetDouble },
- { "nativePutBlob", "(J[BII)Z",
- (void*)nativePutBlob },
- { "nativePutString", "(JLjava/lang/String;II)Z",
- (void*)nativePutString },
- { "nativePutLong", "(JJII)Z",
- (void*)nativePutLong },
- { "nativePutDouble", "(JDII)Z",
- (void*)nativePutDouble },
- { "nativePutNull", "(JII)Z",
- (void*)nativePutNull },
-};
-
-int register_android_database_CursorWindow(JNIEnv* env)
-{
- jclass clazz;
- FIND_CLASS(clazz, "android/database/CharArrayBuffer");
-
- GET_FIELD_ID(gCharArrayBufferClassInfo.data, clazz, "data", "[C");
- GET_FIELD_ID(gCharArrayBufferClassInfo.sizeCopied, clazz, "sizeCopied", "I");
-
- gEmptyString = static_cast(env->NewGlobalRef(env->NewStringUTF("")));
- return jniRegisterNativeMethods(env,
- "io/requery/android/database/CursorWindow", sMethods, NELEM(sMethods));
-}
-
-} // namespace android
diff --git a/sqlite-android/src/main/jni/sqlite/android_database_SQLiteCommon.cpp b/sqlite-android/src/main/jni/sqlite/android_database_SQLiteCommon.cpp
deleted file mode 100644
index 705343efb8..0000000000
--- a/sqlite-android/src/main/jni/sqlite/android_database_SQLiteCommon.cpp
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-
-#include "android_database_SQLiteCommon.h"
-
-namespace android {
-
-/* throw a SQLiteException with a message appropriate for the error in handle */
-void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle) {
- throw_sqlite3_exception(env, handle, NULL);
-}
-
-/* throw a SQLiteException with the given message */
-void throw_sqlite3_exception(JNIEnv* env, const char* message) {
- throw_sqlite3_exception(env, NULL, message);
-}
-
-/* throw a SQLiteException with a message appropriate for the error in handle
- concatenated with the given message
- */
-void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle, const char* message) {
- if (handle) {
- // get the error code and message from the SQLite connection
- // the error message may contain more information than the error code
- // because it is based on the extended error code rather than the simplified
- // error code that SQLite normally returns.
- throw_sqlite3_exception(env, sqlite3_extended_errcode(handle),
- sqlite3_errmsg(handle), message);
- } else {
- // we use SQLITE_OK so that a generic SQLiteException is thrown;
- // any code not specified in the switch statement below would do.
- throw_sqlite3_exception(env, SQLITE_OK, "unknown error", message);
- }
-}
-
-/* throw a SQLiteException for a given error code
- * should only be used when the database connection is not available because the
- * error information will not be quite as rich */
-void throw_sqlite3_exception_errcode(JNIEnv* env, int errcode, const char* message) {
- throw_sqlite3_exception(env, errcode, "unknown error", message);
-}
-
-/* throw a SQLiteException for a given error code, sqlite3message, and
- user message
- */
-void throw_sqlite3_exception(JNIEnv* env, int errcode,
- const char* sqlite3Message, const char* message) {
- const char* exceptionClass;
- switch (errcode & 0xff) { /* mask off extended error code */
- case SQLITE_IOERR:
- exceptionClass = "android/database/sqlite/SQLiteDiskIOException";
- break;
- case SQLITE_CORRUPT:
- case SQLITE_NOTADB: // treat "unsupported file format" error as corruption also
- exceptionClass = "android/database/sqlite/SQLiteDatabaseCorruptException";
- break;
- case SQLITE_CONSTRAINT:
- exceptionClass = "android/database/sqlite/SQLiteConstraintException";
- break;
- case SQLITE_ABORT:
- exceptionClass = "android/database/sqlite/SQLiteAbortException";
- break;
- case SQLITE_DONE:
- exceptionClass = "android/database/sqlite/SQLiteDoneException";
- sqlite3Message = NULL; // SQLite error message is irrelevant in this case
- break;
- case SQLITE_FULL:
- exceptionClass = "android/database/sqlite/SQLiteFullException";
- break;
- case SQLITE_MISUSE:
- exceptionClass = "android/database/sqlite/SQLiteMisuseException";
- break;
- case SQLITE_PERM:
- exceptionClass = "android/database/sqlite/SQLiteAccessPermException";
- break;
- case SQLITE_BUSY:
- exceptionClass = "android/database/sqlite/SQLiteDatabaseLockedException";
- break;
- case SQLITE_LOCKED:
- exceptionClass = "android/database/sqlite/SQLiteTableLockedException";
- break;
- case SQLITE_READONLY:
- exceptionClass = "android/database/sqlite/SQLiteReadOnlyDatabaseException";
- break;
- case SQLITE_CANTOPEN:
- exceptionClass = "android/database/sqlite/SQLiteCantOpenDatabaseException";
- break;
- case SQLITE_TOOBIG:
- exceptionClass = "android/database/sqlite/SQLiteBlobTooBigException";
- break;
- case SQLITE_RANGE:
- exceptionClass = "android/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException";
- break;
- case SQLITE_NOMEM:
- exceptionClass = "android/database/sqlite/SQLiteOutOfMemoryException";
- break;
- case SQLITE_MISMATCH:
- exceptionClass = "android/database/sqlite/SQLiteDatatypeMismatchException";
- break;
- case SQLITE_INTERRUPT:
- exceptionClass = "androidx/core/os/OperationCanceledException";
- break;
- default:
- exceptionClass = "android/database/sqlite/SQLiteException";
- break;
- }
-
- // check this exception class exists otherwise just default to SQLiteException
- if (env->FindClass(exceptionClass) == NULL) {
- exceptionClass = "android/database/sqlite/SQLiteException";
- }
-
- if (sqlite3Message) {
- char *zFullmsg = sqlite3_mprintf(
- "%s (code %d)%s%s", sqlite3Message, errcode,
- (message ? ": " : ""), (message ? message : "")
- );
- jniThrowException(env, exceptionClass, zFullmsg);
- sqlite3_free(zFullmsg);
- } else {
- jniThrowException(env, exceptionClass, message);
- }
-}
-
-
-} // namespace android
diff --git a/sqlite-android/src/main/jni/sqlite/android_database_SQLiteCommon.h b/sqlite-android/src/main/jni/sqlite/android_database_SQLiteCommon.h
deleted file mode 100644
index 8c684dec49..0000000000
--- a/sqlite-android/src/main/jni/sqlite/android_database_SQLiteCommon.h
+++ /dev/null
@@ -1,52 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-
-#ifndef _ANDROID_DATABASE_SQLITE_COMMON_H
-#define _ANDROID_DATABASE_SQLITE_COMMON_H
-
-#include
-#include
-
-#include
-
-// Special log tags defined in SQLiteDebug.java.
-#define SQLITE_LOG_TAG "SQLiteLog"
-#define SQLITE_TRACE_TAG "SQLiteStatements"
-#define SQLITE_PROFILE_TAG "SQLiteTime"
-
-namespace android {
-
-/* throw a SQLiteException with a message appropriate for the error in handle */
-void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle);
-
-/* throw a SQLiteException with the given message */
-void throw_sqlite3_exception(JNIEnv* env, const char* message);
-
-/* throw a SQLiteException with a message appropriate for the error in handle
- concatenated with the given message
- */
-void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle, const char* message);
-
-/* throw a SQLiteException for a given error code */
-void throw_sqlite3_exception_errcode(JNIEnv* env, int errcode, const char* message);
-
-void throw_sqlite3_exception(JNIEnv* env, int errcode,
- const char* sqlite3Message, const char* message);
-
-}
-
-#endif // _ANDROID_DATABASE_SQLITE_COMMON_H
diff --git a/sqlite-android/src/main/jni/sqlite/android_database_SQLiteConnection.cpp b/sqlite-android/src/main/jni/sqlite/android_database_SQLiteConnection.cpp
deleted file mode 100644
index bd7ff7b8e9..0000000000
--- a/sqlite-android/src/main/jni/sqlite/android_database_SQLiteConnection.cpp
+++ /dev/null
@@ -1,1048 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-
-#define LOG_TAG "SQLiteConnection"
-
-#include
-#include
-#include
-#include
-#include
-#include
-
-#include "sqlite3.h"
-#include "JNIHelp.h"
-#include "ALog-priv.h"
-#include "android_database_SQLiteCommon.h"
-#include "CursorWindow.h"
-
-// Set to 1 to use UTF16 storage for localized indexes.
-#define UTF16_STORAGE 0
-
-namespace android {
-
-/* Busy timeout in milliseconds.
- * If another connection (possibly in another process) has the database locked for
- * longer than this amount of time then SQLite will generate a SQLITE_BUSY error.
- * The SQLITE_BUSY error is then raised as a SQLiteDatabaseLockedException.
- *
- * In ordinary usage, busy timeouts are quite rare. Most databases only ever
- * have a single open connection at a time unless they are using WAL. When using
- * WAL, a timeout could occur if one connection is busy performing an auto-checkpoint
- * operation. The busy timeout needs to be long enough to tolerate slow I/O write
- * operations but not so long as to cause the application to hang indefinitely if
- * there is a problem acquiring a database lock.
- */
-static const int BUSY_TIMEOUT_MS = 2500;
-
-static JavaVM *gpJavaVM = 0;
-
-static struct {
- jfieldID name;
- jfieldID numArgs;
- jmethodID dispatchCallback;
-} gSQLiteCustomFunctionClassInfo;
-
-static struct {
- jfieldID name;
- jfieldID numArgs;
- jfieldID flags;
- jmethodID dispatchCallback;
-} gSQLiteFunctionClassInfo;
-
-static struct {
- jclass clazz;
-} gStringClassInfo;
-
-struct SQLiteConnection {
- sqlite3* const db;
- const int openFlags;
- char* path;
- char* label;
-
- volatile bool canceled;
-
- SQLiteConnection(sqlite3* db, int openFlags, const char* path_, const char* label_) :
- db(db), openFlags(openFlags), canceled(false) {
- path = strdup(path_);
- label = strdup(label_);
- }
-
- ~SQLiteConnection() {
- free(path);
- free(label);
- }
-};
-
-// Called each time a statement begins execution, when tracing is enabled.
-static void sqliteTraceCallback(void *data, const char *sql) {
- SQLiteConnection* connection = static_cast(data);
- ALOG(LOG_VERBOSE, SQLITE_TRACE_TAG, "%s: \"%s\"\n",
- connection->label, sql);
-}
-
-// Called each time a statement finishes execution, when profiling is enabled.
-static void sqliteProfileCallback(void *data, const char *sql, sqlite3_uint64 tm) {
- SQLiteConnection* connection = static_cast(data);
- ALOG(LOG_VERBOSE, SQLITE_PROFILE_TAG, "%s: \"%s\" took %0.3f ms\n",
- connection->label, sql, tm * 0.000001f);
-}
-
-// Called after each SQLite VM instruction when cancelation is enabled.
-static int sqliteProgressHandlerCallback(void* data) {
- SQLiteConnection* connection = static_cast(data);
- return connection->canceled;
-}
-
-/*
-** This function is a collation sequence callback equivalent to the built-in
-** BINARY sequence.
-**
-** Stock Android uses a modified version of sqlite3.c that calls out to a module
-** named "sqlite3_android" to add extra built-in collations and functions to
-** all database handles. Specifically, collation sequence "LOCALIZED". For now,
-** this module does not include sqlite3_android (since it is difficult to build
-** with the NDK only). Instead, this function is registered as "LOCALIZED" for all
-** new database handles.
-*/
-static int coll_localized(
- void *not_used,
- int nKey1, const void *pKey1,
- int nKey2, const void *pKey2
-){
- int rc, n;
- n = nKey1GetStringUTFChars(pathStr, NULL);
- const char* labelChars = env->GetStringUTFChars(labelStr, NULL);
-
- sqlite3* db;
- int err = sqlite3_open_v2(pathChars, &db, openFlags, NULL);
- if (err != SQLITE_OK) {
- env->ReleaseStringUTFChars(pathStr, pathChars);
- env->ReleaseStringUTFChars(labelStr, labelChars);
- throw_sqlite3_exception_errcode(env, err, "Could not open database");
- return 0;
- }
- err = sqlite3_create_collation(db, "localized", SQLITE_UTF8, 0, coll_localized);
- if (err != SQLITE_OK) {
- env->ReleaseStringUTFChars(pathStr, pathChars);
- env->ReleaseStringUTFChars(labelStr, labelChars);
- throw_sqlite3_exception_errcode(env, err, "Could not register collation");
- sqlite3_close(db);
- return 0;
- }
-
- // Check that the database is really read/write when that is what we asked for.
- if ((openFlags & SQLITE_OPEN_READWRITE) && sqlite3_db_readonly(db, NULL)) {
- env->ReleaseStringUTFChars(pathStr, pathChars);
- env->ReleaseStringUTFChars(labelStr, labelChars);
- throw_sqlite3_exception(env, db, "Could not open the database in read/write mode.");
- sqlite3_close(db);
- return 0;
- }
-
- // Set the default busy handler to retry automatically before returning SQLITE_BUSY.
- err = sqlite3_busy_timeout(db, BUSY_TIMEOUT_MS);
- if (err != SQLITE_OK) {
- env->ReleaseStringUTFChars(pathStr, pathChars);
- env->ReleaseStringUTFChars(labelStr, labelChars);
- throw_sqlite3_exception(env, db, "Could not set busy timeout");
- sqlite3_close(db);
- return 0;
- }
-
- // Register custom Android functions.
-#if 0
- err = register_android_functions(db, UTF16_STORAGE);
- if (err) {
- env->ReleaseStringUTFChars(pathStr, pathChars);
- env->ReleaseStringUTFChars(labelStr, labelChars);
- throw_sqlite3_exception(env, db, "Could not register Android SQL functions.");
- sqlite3_close(db);
- return 0;
- }
-#endif
-
- // Create wrapper object.
- SQLiteConnection* connection = new SQLiteConnection(db, openFlags, pathChars, labelChars);
- ALOGV("Opened connection %p with label '%s'", db, labelChars);
- env->ReleaseStringUTFChars(pathStr, pathChars);
- env->ReleaseStringUTFChars(labelStr, labelChars);
-
- // Enable tracing and profiling if requested.
- if (enableTrace) {
- sqlite3_trace(db, &sqliteTraceCallback, connection);
- }
- if (enableProfile) {
- sqlite3_profile(db, &sqliteProfileCallback, connection);
- }
-
- return reinterpret_cast(connection);
-}
-
-static void nativeClose(JNIEnv* env, jclass clazz, jlong connectionPtr) {
- SQLiteConnection* connection = reinterpret_cast(connectionPtr);
-
- if (connection) {
- ALOGV("Closing connection %p", connection->db);
- int err = sqlite3_close(connection->db);
- if (err != SQLITE_OK) {
- // This can happen if sub-objects aren't closed first. Make sure the caller knows.
- ALOGE("sqlite3_close(%p) failed: %d", connection->db, err);
- throw_sqlite3_exception(env, connection->db, "Count not close db.");
- return;
- }
-
- delete connection;
- }
-}
-
-// Called each time a custom function is evaluated.
-static void sqliteCustomFunctionCallback(sqlite3_context *context,
- int argc, sqlite3_value **argv) {
-
- JNIEnv* env = 0;
- gpJavaVM->GetEnv((void**)&env, JNI_VERSION_1_4);
-
- // Get the callback function object.
- // Create a new local reference to it in case the callback tries to do something
- // dumb like unregister the function (thereby destroying the global ref) while it is running.
- jobject functionObjGlobal = reinterpret_cast(sqlite3_user_data(context));
- jobject functionObj = env->NewLocalRef(functionObjGlobal);
-
- jobjectArray argsArray = env->NewObjectArray(argc, gStringClassInfo.clazz, NULL);
- if (argsArray) {
- for (int i = 0; i < argc; i++) {
- const jchar* arg = static_cast(sqlite3_value_text16(argv[i]));
- if (!arg) {
- ALOGW("NULL argument in custom_function_callback. This should not happen.");
- } else {
- size_t argLen = sqlite3_value_bytes16(argv[i]) / sizeof(jchar);
- jstring argStr = env->NewString(arg, argLen);
- if (!argStr) {
- goto error; // out of memory error
- }
- env->SetObjectArrayElement(argsArray, i, argStr);
- env->DeleteLocalRef(argStr);
- }
- }
-
- {
- jobject result = env->CallObjectMethod(functionObj,
- gSQLiteCustomFunctionClassInfo.dispatchCallback, argsArray);
- if (env->ExceptionCheck()) {
- sqlite3_result_error(context, "Custom function exception", -1);
- } else if (result == NULL) {
- sqlite3_result_null(context);
- } else {
- jstring str = static_cast(result);
- const char* chars = env->GetStringUTFChars(str, NULL);
- sqlite3_result_text(context, chars, -1, SQLITE_TRANSIENT);
- env->ReleaseStringUTFChars(str, chars);
- }
- env->DeleteLocalRef(result);
- }
-error:
- env->DeleteLocalRef(argsArray);
- }
-
- env->DeleteLocalRef(functionObj);
-
- if (env->ExceptionCheck()) {
- ALOGE("An exception was thrown by custom SQLite function.");
- /* LOGE_EX(env); */
- env->ExceptionClear();
- }
-}
-
-// Called each time a Function is evaluated.
-static void sqliteFunctionCallback(sqlite3_context *context,
- int argc, sqlite3_value **argv) {
-
- JNIEnv* env = 0;
- gpJavaVM->GetEnv((void**)&env, JNI_VERSION_1_4);
-
- // Get the callback function object.
- // Create a new local reference to it in case the callback tries to do something
- // dumb like unregister the function (thereby destroying the global ref) while it is running.
- jobject functionObjGlobal = reinterpret_cast(sqlite3_user_data(context));
- jobject functionObj = env->NewLocalRef(functionObjGlobal);
-
- jlong contextPtr = jlong(context);
- jlong argsPtr = jlong(argv);
- jint argsCount = jint(argc);
-
- env->CallVoidMethod(functionObj,
- gSQLiteFunctionClassInfo.dispatchCallback,
- contextPtr,
- argsPtr,
- argsCount
- );
- if (env->ExceptionCheck()) {
- sqlite3_result_error(context, "Custom function exception", -1);
- }
-
- env->DeleteLocalRef(functionObj);
-
- if (env->ExceptionCheck()) {
- ALOGE("An exception was thrown by custom SQLite function.");
- /* LOGE_EX(env); */
- env->ExceptionClear();
- }
-}
-
-// Called when a custom function is destroyed.
-static void sqliteCustomFunctionDestructor(void* data) {
- jobject functionObjGlobal = reinterpret_cast(data);
- JNIEnv* env = 0;
- gpJavaVM->GetEnv((void**)&env, JNI_VERSION_1_4);
- env->DeleteGlobalRef(functionObjGlobal);
-}
-
-static void nativeRegisterCustomFunction(JNIEnv* env, jclass clazz, jlong connectionPtr,
- jobject functionObj) {
- SQLiteConnection* connection = reinterpret_cast(connectionPtr);
-
- jstring nameStr = jstring(env->GetObjectField(
- functionObj, gSQLiteCustomFunctionClassInfo.name));
- jint numArgs = env->GetIntField(functionObj, gSQLiteCustomFunctionClassInfo.numArgs);
-
- jobject functionObjGlobal = env->NewGlobalRef(functionObj);
-
- const char* name = env->GetStringUTFChars(nameStr, NULL);
- int err = sqlite3_create_function_v2(connection->db, name, numArgs, SQLITE_UTF16,
- reinterpret_cast(functionObjGlobal),
- &sqliteCustomFunctionCallback, NULL, NULL, &sqliteCustomFunctionDestructor);
- env->ReleaseStringUTFChars(nameStr, name);
-
- if (err != SQLITE_OK) {
- ALOGE("sqlite3_create_function returned %d", err);
- env->DeleteGlobalRef(functionObjGlobal);
- throw_sqlite3_exception(env, connection->db);
- return;
- }
-}
-
-static void nativeRegisterFunction(JNIEnv *env, jclass clazz, jlong connectionPtr,
- jobject functionObj) {
- SQLiteConnection* connection = reinterpret_cast(connectionPtr);
-
- jstring nameStr = jstring(env->GetObjectField(
- functionObj, gSQLiteFunctionClassInfo.name));
- jint numArgs = env->GetIntField(functionObj, gSQLiteFunctionClassInfo.numArgs);
- jint flags = env->GetIntField(functionObj, gSQLiteFunctionClassInfo.flags);
-
- jobject functionObjGlobal = env->NewGlobalRef(functionObj);
-
- const char* name = env->GetStringUTFChars(nameStr, NULL);
- int err = sqlite3_create_function_v2(connection->db, name, numArgs,
- SQLITE_UTF16 | flags,
- reinterpret_cast(functionObjGlobal),
- &sqliteFunctionCallback, NULL, NULL, &sqliteCustomFunctionDestructor);
- env->ReleaseStringUTFChars(nameStr, name);
-
- if (err != SQLITE_OK) {
- ALOGE("sqlite3_create_function returned %d", err);
- env->DeleteGlobalRef(functionObjGlobal);
- throw_sqlite3_exception(env, connection->db);
- return;
- }
-}
-
-static void nativeRegisterLocalizedCollators(JNIEnv* env, jclass clazz, jlong connectionPtr,
- jstring localeStr) {
- SQLiteConnection* connection = reinterpret_cast(connectionPtr);
-#if 0
- const char* locale = env->GetStringUTFChars(localeStr, NULL);
-
- int err = register_localized_collators(connection->db, locale, UTF16_STORAGE);
- env->ReleaseStringUTFChars(localeStr, locale);
-
- if (err != SQLITE_OK) {
- throw_sqlite3_exception(env, connection->db);
- }
-#endif
-}
-
-static jlong nativePrepareStatement(JNIEnv* env, jclass clazz, jlong connectionPtr,
- jstring sqlString) {
- SQLiteConnection* connection = reinterpret_cast(connectionPtr);
-
- jsize sqlLength = env->GetStringLength(sqlString);
- const jchar* sql = env->GetStringCritical(sqlString, NULL);
- sqlite3_stmt* statement;
- int err = sqlite3_prepare16_v2(connection->db,
- sql, sqlLength * sizeof(jchar), &statement, NULL);
- env->ReleaseStringCritical(sqlString, sql);
-
- if (err != SQLITE_OK) {
- // Error messages like 'near ")": syntax error' are not
- // always helpful enough, so construct an error string that
- // includes the query itself.
- const char *query = env->GetStringUTFChars(sqlString, NULL);
- char *message = (char*) malloc(strlen(query) + 50);
- if (message) {
- strcpy(message, ", while compiling: "); // less than 50 chars
- strcat(message, query);
- }
- env->ReleaseStringUTFChars(sqlString, query);
- throw_sqlite3_exception(env, connection->db, message);
- free(message);
- return 0;
- }
-
- ALOGV("Prepared statement %p on connection %p", statement, connection->db);
- return reinterpret_cast(statement);
-}
-
-static void nativeFinalizeStatement(JNIEnv* env, jclass clazz, jlong connectionPtr,
- jlong statementPtr) {
- SQLiteConnection* connection = reinterpret_cast(connectionPtr);
- sqlite3_stmt* statement = reinterpret_cast(statementPtr);
-
- // We ignore the result of sqlite3_finalize because it is really telling us about
- // whether any errors occurred while executing the statement. The statement itself
- // is always finalized regardless.
- ALOGV("Finalized statement %p on connection %p", statement, connection->db);
- sqlite3_finalize(statement);
-}
-
-static jint nativeGetParameterCount(JNIEnv* env, jclass clazz, jlong connectionPtr,
- jlong statementPtr) {
- SQLiteConnection* connection = reinterpret_cast(connectionPtr);
- sqlite3_stmt* statement = reinterpret_cast(statementPtr);
-
- return sqlite3_bind_parameter_count(statement);
-}
-
-static jboolean nativeIsReadOnly(JNIEnv* env, jclass clazz, jlong connectionPtr,
- jlong statementPtr) {
- SQLiteConnection* connection = reinterpret_cast(connectionPtr);
- sqlite3_stmt* statement = reinterpret_cast(statementPtr);
-
- return sqlite3_stmt_readonly(statement) != 0;
-}
-
-static jint nativeGetColumnCount(JNIEnv* env, jclass clazz, jlong connectionPtr,
- jlong statementPtr) {
- SQLiteConnection* connection = reinterpret_cast(connectionPtr);
- sqlite3_stmt* statement = reinterpret_cast(statementPtr);
-
- return sqlite3_column_count(statement);
-}
-
-static jstring nativeGetColumnName(JNIEnv* env, jclass clazz, jlong connectionPtr,
- jlong statementPtr, jint index) {
- SQLiteConnection* connection = reinterpret_cast(connectionPtr);
- sqlite3_stmt* statement = reinterpret_cast(statementPtr);
-
- const jchar* name = static_cast(sqlite3_column_name16(statement, index));
- if (name) {
- size_t length = 0;
- while (name[length]) {
- length += 1;
- }
- return env->NewString(name, length);
- }
- return NULL;
-}
-
-static void nativeBindNull(JNIEnv* env, jclass clazz, jlong connectionPtr,
- jlong statementPtr, jint index) {
- SQLiteConnection* connection = reinterpret_cast(connectionPtr);
- sqlite3_stmt* statement = reinterpret_cast(statementPtr);
-
- int err = sqlite3_bind_null(statement, index);
- if (err != SQLITE_OK) {
- throw_sqlite3_exception(env, connection->db, NULL);
- }
-}
-
-static void nativeBindLong(JNIEnv* env, jclass clazz, jlong connectionPtr,
- jlong statementPtr, jint index, jlong value) {
- SQLiteConnection* connection = reinterpret_cast(connectionPtr);
- sqlite3_stmt* statement = reinterpret_cast(statementPtr);
-
- int err = sqlite3_bind_int64(statement, index, value);
- if (err != SQLITE_OK) {
- throw_sqlite3_exception(env, connection->db, NULL);
- }
-}
-
-static void nativeBindDouble(JNIEnv* env, jclass clazz, jlong connectionPtr,
- jlong statementPtr, jint index, jdouble value) {
- SQLiteConnection* connection = reinterpret_cast(connectionPtr);
- sqlite3_stmt* statement = reinterpret_cast(statementPtr);
-
- int err = sqlite3_bind_double(statement, index, value);
- if (err != SQLITE_OK) {
- throw_sqlite3_exception(env, connection->db, NULL);
- }
-}
-
-static void nativeBindString(JNIEnv* env, jclass clazz, jlong connectionPtr,
- jlong statementPtr, jint index, jstring valueString) {
- SQLiteConnection* connection = reinterpret_cast(connectionPtr);
- sqlite3_stmt* statement = reinterpret_cast(statementPtr);
-
- jsize valueLength = env->GetStringLength(valueString);
- const jchar* value = env->GetStringCritical(valueString, NULL);
- int err = sqlite3_bind_text16(statement, index, value, valueLength * sizeof(jchar),
- SQLITE_TRANSIENT);
- env->ReleaseStringCritical(valueString, value);
- if (err != SQLITE_OK) {
- throw_sqlite3_exception(env, connection->db, NULL);
- }
-}
-
-static void nativeBindBlob(JNIEnv* env, jclass clazz, jlong connectionPtr,
- jlong statementPtr, jint index, jbyteArray valueArray) {
- SQLiteConnection* connection = reinterpret_cast(connectionPtr);
- sqlite3_stmt* statement = reinterpret_cast(statementPtr);
-
- jsize valueLength = env->GetArrayLength(valueArray);
- jbyte* value = static_cast(env->GetPrimitiveArrayCritical(valueArray, NULL));
- int err = sqlite3_bind_blob(statement, index, value, valueLength, SQLITE_TRANSIENT);
- env->ReleasePrimitiveArrayCritical(valueArray, value, JNI_ABORT);
- if (err != SQLITE_OK) {
- throw_sqlite3_exception(env, connection->db, NULL);
- }
-}
-
-static void nativeResetStatementAndClearBindings(JNIEnv* env, jclass clazz, jlong connectionPtr,
- jlong statementPtr) {
- SQLiteConnection* connection = reinterpret_cast(connectionPtr);
- sqlite3_stmt* statement = reinterpret_cast(statementPtr);
-
- int err = sqlite3_reset(statement);
- if (err == SQLITE_OK) {
- err = sqlite3_clear_bindings(statement);
- }
- if (err != SQLITE_OK) {
- throw_sqlite3_exception(env, connection->db, NULL);
- }
-}
-
-static int executeNonQuery(JNIEnv* env, SQLiteConnection* connection, sqlite3_stmt* statement) {
- int err = sqlite3_step(statement);
- if (err == SQLITE_ROW) {
- throw_sqlite3_exception(env,
- "Queries can be performed using SQLiteDatabase query or rawQuery methods only.");
- } else if (err != SQLITE_DONE) {
- throw_sqlite3_exception(env, connection->db);
- }
- return err;
-}
-
-static void nativeExecute(JNIEnv* env, jclass clazz, jlong connectionPtr,
- jlong statementPtr) {
- SQLiteConnection* connection = reinterpret_cast(connectionPtr);
- sqlite3_stmt* statement = reinterpret_cast(statementPtr);
-
- executeNonQuery(env, connection, statement);
-}
-
-static jint nativeExecuteForChangedRowCount(JNIEnv* env, jclass clazz,
- jlong connectionPtr, jlong statementPtr) {
- SQLiteConnection* connection = reinterpret_cast(connectionPtr);
- sqlite3_stmt* statement = reinterpret_cast(statementPtr);
-
- int err = executeNonQuery(env, connection, statement);
- return err == SQLITE_DONE ? sqlite3_changes(connection->db) : -1;
-}
-
-static jlong nativeExecuteForLastInsertedRowId(JNIEnv* env, jclass clazz,
- jlong connectionPtr, jlong statementPtr) {
- SQLiteConnection* connection = reinterpret_cast(connectionPtr);
- sqlite3_stmt* statement = reinterpret_cast(statementPtr);
-
- int err = executeNonQuery(env, connection, statement);
- return err == SQLITE_DONE && sqlite3_changes(connection->db) > 0
- ? sqlite3_last_insert_rowid(connection->db) : -1;
-}
-
-static int executeOneRowQuery(JNIEnv* env, SQLiteConnection* connection, sqlite3_stmt* statement) {
- int err = sqlite3_step(statement);
- if (err != SQLITE_ROW) {
- throw_sqlite3_exception(env, connection->db);
- }
- return err;
-}
-
-static jlong nativeExecuteForLong(JNIEnv* env, jclass clazz,
- jlong connectionPtr, jlong statementPtr) {
- SQLiteConnection* connection = reinterpret_cast(connectionPtr);
- sqlite3_stmt* statement = reinterpret_cast(statementPtr);
-
- int err = executeOneRowQuery(env, connection, statement);
- if (err == SQLITE_ROW && sqlite3_column_count(statement) >= 1) {
- return sqlite3_column_int64(statement, 0);
- }
- return -1;
-}
-
-static jstring nativeExecuteForString(JNIEnv* env, jclass clazz,
- jlong connectionPtr, jlong statementPtr) {
- SQLiteConnection* connection = reinterpret_cast(connectionPtr);
- sqlite3_stmt* statement = reinterpret_cast(statementPtr);
-
- int err = executeOneRowQuery(env, connection, statement);
- if (err == SQLITE_ROW && sqlite3_column_count(statement) >= 1) {
- const jchar* text = static_cast(sqlite3_column_text16(statement, 0));
- if (text) {
- size_t length = sqlite3_column_bytes16(statement, 0) / sizeof(jchar);
- return env->NewString(text, length);
- }
- }
- return NULL;
-}
-
-static int createAshmemRegionWithData(JNIEnv* env, const void* data, size_t length) {
-#if 0
- int error = 0;
- int fd = ashmem_create_region(NULL, length);
- if (fd < 0) {
- error = errno;
- ALOGE("ashmem_create_region failed: %s", strerror(error));
- } else {
- if (length > 0) {
- void* ptr = mmap(NULL, length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
- if (ptr == MAP_FAILED) {
- error = errno;
- ALOGE("mmap failed: %s", strerror(error));
- } else {
- memcpy(ptr, data, length);
- munmap(ptr, length);
- }
- }
-
- if (!error) {
- if (ashmem_set_prot_region(fd, PROT_READ) < 0) {
- error = errno;
- ALOGE("ashmem_set_prot_region failed: %s", strerror(errno));
- } else {
- return fd;
- }
- }
-
- close(fd);
- }
-
-#endif
- jniThrowIOException(env, -1);
- return -1;
-}
-
-static jint nativeExecuteForBlobFileDescriptor(JNIEnv* env, jclass clazz,
- jlong connectionPtr, jlong statementPtr) {
- SQLiteConnection* connection = reinterpret_cast(connectionPtr);
- sqlite3_stmt* statement = reinterpret_cast(statementPtr);
-
- int err = executeOneRowQuery(env, connection, statement);
- if (err == SQLITE_ROW && sqlite3_column_count(statement) >= 1) {
- const void* blob = sqlite3_column_blob(statement, 0);
- if (blob) {
- int length = sqlite3_column_bytes(statement, 0);
- if (length >= 0) {
- return createAshmemRegionWithData(env, blob, length);
- }
- }
- }
- return -1;
-}
-
-enum CopyRowResult {
- CPR_OK,
- CPR_FULL,
- CPR_ERROR,
-};
-
-static CopyRowResult copyRow(JNIEnv* env, CursorWindow* window,
- sqlite3_stmt* statement, int numColumns, int startPos, int addedRows) {
- // Allocate a new field directory for the row.
- status_t status = window->allocRow();
- if (status) {
- LOG_WINDOW("Failed allocating fieldDir at startPos %d row %d, error=%d",
- startPos, addedRows, status);
- return CPR_FULL;
- }
-
- // Pack the row into the window.
- CopyRowResult result = CPR_OK;
- for (int i = 0; i < numColumns; i++) {
- int type = sqlite3_column_type(statement, i);
- if (type == SQLITE_TEXT) {
- // TEXT data
- const char* text = reinterpret_cast(
- sqlite3_column_text(statement, i));
- // SQLite does not include the NULL terminator in size, but does
- // ensure all strings are NULL terminated, so increase size by
- // one to make sure we store the terminator.
- size_t sizeIncludingNull = sqlite3_column_bytes(statement, i) + 1;
- status = window->putString(addedRows, i, text, sizeIncludingNull);
- if (status) {
- LOG_WINDOW("Failed allocating %u bytes for text at %d,%d, error=%d",
- sizeIncludingNull, startPos + addedRows, i, status);
- result = CPR_FULL;
- break;
- }
- LOG_WINDOW("%d,%d is TEXT with %u bytes",
- startPos + addedRows, i, sizeIncludingNull);
- } else if (type == SQLITE_INTEGER) {
- // INTEGER data
- int64_t value = sqlite3_column_int64(statement, i);
- status = window->putLong(addedRows, i, value);
- if (status) {
- LOG_WINDOW("Failed allocating space for a long in column %d, error=%d",
- i, status);
- result = CPR_FULL;
- break;
- }
- LOG_WINDOW("%d,%d is INTEGER 0x%016llx", startPos + addedRows, i, value);
- } else if (type == SQLITE_FLOAT) {
- // FLOAT data
- double value = sqlite3_column_double(statement, i);
- status = window->putDouble(addedRows, i, value);
- if (status) {
- LOG_WINDOW("Failed allocating space for a double in column %d, error=%d",
- i, status);
- result = CPR_FULL;
- break;
- }
- LOG_WINDOW("%d,%d is FLOAT %lf", startPos + addedRows, i, value);
- } else if (type == SQLITE_BLOB) {
- // BLOB data
- const void* blob = sqlite3_column_blob(statement, i);
- size_t size = sqlite3_column_bytes(statement, i);
- status = window->putBlob(addedRows, i, blob, size);
- if (status) {
- LOG_WINDOW("Failed allocating %u bytes for blob at %d,%d, error=%d",
- size, startPos + addedRows, i, status);
- result = CPR_FULL;
- break;
- }
- LOG_WINDOW("%d,%d is Blob with %u bytes",
- startPos + addedRows, i, size);
- } else if (type == SQLITE_NULL) {
- // NULL field
- status = window->putNull(addedRows, i);
- if (status) {
- LOG_WINDOW("Failed allocating space for a null in column %d, error=%d",
- i, status);
- result = CPR_FULL;
- break;
- }
-
- LOG_WINDOW("%d,%d is NULL", startPos + addedRows, i);
- } else {
- // Unknown data
- ALOGE("Unknown column type when filling database window");
- throw_sqlite3_exception(env, "Unknown column type when filling window");
- result = CPR_ERROR;
- break;
- }
- }
-
- // Free the last row if if was not successfully copied.
- if (result != CPR_OK) {
- window->freeLastRow();
- }
- return result;
-}
-
-static jlong nativeExecuteForCursorWindow(JNIEnv* env, jclass clazz,
- jlong connectionPtr, jlong statementPtr, jlong windowPtr,
- jint startPos, jint requiredPos, jboolean countAllRows) {
- SQLiteConnection* connection = reinterpret_cast(connectionPtr);
- sqlite3_stmt* statement = reinterpret_cast(statementPtr);
- CursorWindow* window = reinterpret_cast(windowPtr);
-
- status_t status = window->clear();
- if (status) {
- throw_sqlite3_exception(env, connection->db, "Failed to clear the cursor window");
- return 0;
- }
-
- int numColumns = sqlite3_column_count(statement);
- status = window->setNumColumns(numColumns);
- if (status) {
- throw_sqlite3_exception(env, connection->db, "Failed to set the cursor window column count");
- return 0;
- }
-
- int retryCount = 0;
- int totalRows = 0;
- int addedRows = 0;
- bool windowFull = false;
- bool gotException = false;
- while (!gotException && (!windowFull || countAllRows)) {
- int err = sqlite3_step(statement);
- if (err == SQLITE_ROW) {
- LOG_WINDOW("Stepped statement %p to row %d", statement, totalRows);
- retryCount = 0;
- totalRows += 1;
-
- // Skip the row if the window is full or we haven't reached the start position yet.
- if (startPos >= totalRows || windowFull) {
- continue;
- }
-
- CopyRowResult cpr = copyRow(env, window, statement, numColumns, startPos, addedRows);
- if (cpr == CPR_FULL && addedRows && startPos + addedRows <= requiredPos) {
- // We filled the window before we got to the one row that we really wanted.
- // Clear the window and start filling it again from here.
- // TODO: Would be nicer if we could progressively replace earlier rows.
- window->clear();
- window->setNumColumns(numColumns);
- startPos += addedRows;
- addedRows = 0;
- cpr = copyRow(env, window, statement, numColumns, startPos, addedRows);
- }
-
- if (cpr == CPR_OK) {
- addedRows += 1;
- } else if (cpr == CPR_FULL) {
- windowFull = true;
- } else {
- gotException = true;
- }
- } else if (err == SQLITE_DONE) {
- // All rows processed, bail
- LOG_WINDOW("Processed all rows");
- break;
- } else if (err == SQLITE_LOCKED || err == SQLITE_BUSY) {
- // The table is locked, retry
- LOG_WINDOW("Database locked, retrying");
- if (retryCount > 50) {
- ALOGE("Bailing on database busy retry");
- throw_sqlite3_exception(env, connection->db, "retrycount exceeded");
- gotException = true;
- } else {
- // Sleep to give the thread holding the lock a chance to finish
- usleep(1000);
- retryCount++;
- }
- } else {
- throw_sqlite3_exception(env, connection->db);
- gotException = true;
- }
- }
-
- LOG_WINDOW("Resetting statement %p after fetching %d rows and adding %d rows"
- "to the window in %d bytes",
- statement, totalRows, addedRows, window->size() - window->freeSpace());
- sqlite3_reset(statement);
-
- // Report the total number of rows on request.
- if (startPos > totalRows) {
- ALOGE("startPos %d > actual rows %d", startPos, totalRows);
- }
- jlong result = jlong(startPos) << 32 | jlong(totalRows);
- return result;
-}
-
-static jint nativeGetDbLookaside(JNIEnv* env, jobject clazz, jlong connectionPtr) {
- SQLiteConnection* connection = reinterpret_cast(connectionPtr);
-
- int cur = -1;
- int unused;
- sqlite3_db_status(connection->db, SQLITE_DBSTATUS_LOOKASIDE_USED, &cur, &unused, 0);
- return cur;
-}
-
-static void nativeCancel(JNIEnv* env, jobject clazz, jlong connectionPtr) {
- SQLiteConnection* connection = reinterpret_cast(connectionPtr);
- connection->canceled = true;
-}
-
-static void nativeResetCancel(JNIEnv* env, jobject clazz, jlong connectionPtr,
- jboolean cancelable) {
- SQLiteConnection* connection = reinterpret_cast(connectionPtr);
- connection->canceled = false;
-
- if (cancelable) {
- sqlite3_progress_handler(connection->db, 4, sqliteProgressHandlerCallback,
- connection);
- } else {
- sqlite3_progress_handler(connection->db, 0, NULL, NULL);
- }
-}
-
-static jboolean nativeHasCodec(JNIEnv* env, jobject clazz){
-#ifdef SQLITE_HAS_CODEC
- return true;
-#else
- return false;
-#endif
-}
-
-static void nativeLoadExtension(JNIEnv* env, jobject clazz,
- jlong connectionPtr, jstring file, jstring proc) {
- char* errorMessage;
-
- SQLiteConnection* connection = reinterpret_cast(connectionPtr);
- int result = sqlite3_enable_load_extension(connection->db, 1);
- if (result == SQLITE_OK) {
- const char* fileChars = env->GetStringUTFChars(file, NULL);
- const char* procChars = NULL;
- if (proc) {
- procChars = env->GetStringUTFChars(proc, NULL);
- }
- result = sqlite3_load_extension(connection->db, fileChars, procChars, &errorMessage);
- env->ReleaseStringUTFChars(file, fileChars);
- if (proc) {
- env->ReleaseStringUTFChars(proc, procChars);
- }
- }
- if (result != SQLITE_OK) {
- char* formattedError = sqlite3_mprintf("Could not register extension: %s", errorMessage);
- sqlite3_free(errorMessage);
-
- throw_sqlite3_exception_errcode(env, result, formattedError);
- sqlite3_free(formattedError);
- }
-}
-
-static JNINativeMethod sMethods[] =
-{
- /* name, signature, funcPtr */
- { "nativeOpen", "(Ljava/lang/String;ILjava/lang/String;ZZ)J",
- (void*)nativeOpen },
- { "nativeClose", "(J)V",
- (void*)nativeClose },
- { "nativeRegisterCustomFunction", "(JLio/requery/android/database/sqlite/SQLiteCustomFunction;)V",
- (void*)nativeRegisterCustomFunction },
- { "nativeRegisterFunction", "(JLio/requery/android/database/sqlite/SQLiteFunction;)V",
- (void*)nativeRegisterFunction },
- { "nativeRegisterLocalizedCollators", "(JLjava/lang/String;)V",
- (void*)nativeRegisterLocalizedCollators },
- { "nativePrepareStatement", "(JLjava/lang/String;)J",
- (void*)nativePrepareStatement },
- { "nativeFinalizeStatement", "(JJ)V",
- (void*)nativeFinalizeStatement },
- { "nativeGetParameterCount", "(JJ)I",
- (void*)nativeGetParameterCount },
- { "nativeIsReadOnly", "(JJ)Z",
- (void*)nativeIsReadOnly },
- { "nativeGetColumnCount", "(JJ)I",
- (void*)nativeGetColumnCount },
- { "nativeGetColumnName", "(JJI)Ljava/lang/String;",
- (void*)nativeGetColumnName },
- { "nativeBindNull", "(JJI)V",
- (void*)nativeBindNull },
- { "nativeBindLong", "(JJIJ)V",
- (void*)nativeBindLong },
- { "nativeBindDouble", "(JJID)V",
- (void*)nativeBindDouble },
- { "nativeBindString", "(JJILjava/lang/String;)V",
- (void*)nativeBindString },
- { "nativeBindBlob", "(JJI[B)V",
- (void*)nativeBindBlob },
- { "nativeResetStatementAndClearBindings", "(JJ)V",
- (void*)nativeResetStatementAndClearBindings },
- { "nativeExecute", "(JJ)V",
- (void*)nativeExecute },
- { "nativeExecuteForLong", "(JJ)J",
- (void*)nativeExecuteForLong },
- { "nativeExecuteForString", "(JJ)Ljava/lang/String;",
- (void*)nativeExecuteForString },
- { "nativeExecuteForBlobFileDescriptor", "(JJ)I",
- (void*)nativeExecuteForBlobFileDescriptor },
- { "nativeExecuteForChangedRowCount", "(JJ)I",
- (void*)nativeExecuteForChangedRowCount },
- { "nativeExecuteForLastInsertedRowId", "(JJ)J",
- (void*)nativeExecuteForLastInsertedRowId },
- { "nativeExecuteForCursorWindow", "(JJJIIZ)J",
- (void*)nativeExecuteForCursorWindow },
- { "nativeGetDbLookaside", "(J)I",
- (void*)nativeGetDbLookaside },
- { "nativeCancel", "(J)V",
- (void*)nativeCancel },
- { "nativeResetCancel", "(JZ)V",
- (void*)nativeResetCancel },
- { "nativeHasCodec", "()Z",
- (void*)nativeHasCodec },
- { "nativeLoadExtension", "(JLjava/lang/String;Ljava/lang/String;)V",
- (void*)nativeLoadExtension },
-};
-
-int register_android_database_SQLiteConnection(JNIEnv *env)
-{
- jclass clazz;
- FIND_CLASS(clazz, "io/requery/android/database/sqlite/SQLiteCustomFunction");
-
- GET_FIELD_ID(gSQLiteCustomFunctionClassInfo.name, clazz,
- "name", "Ljava/lang/String;");
- GET_FIELD_ID(gSQLiteCustomFunctionClassInfo.numArgs, clazz,
- "numArgs", "I");
- GET_METHOD_ID(gSQLiteCustomFunctionClassInfo.dispatchCallback,
- clazz, "dispatchCallback", "([Ljava/lang/String;)Ljava/lang/String;");
-
- FIND_CLASS(clazz, "io/requery/android/database/sqlite/SQLiteFunction");
-
- GET_FIELD_ID(gSQLiteFunctionClassInfo.name, clazz,
- "name", "Ljava/lang/String;");
- GET_FIELD_ID(gSQLiteFunctionClassInfo.numArgs, clazz,
- "numArgs", "I");
- GET_FIELD_ID(gSQLiteFunctionClassInfo.flags, clazz,
- "flags", "I");
- GET_METHOD_ID(gSQLiteFunctionClassInfo.dispatchCallback,
- clazz, "dispatchCallback", "(JJI)V");
-
- FIND_CLASS(clazz, "java/lang/String");
- gStringClassInfo.clazz = jclass(env->NewGlobalRef(clazz));
-
- return jniRegisterNativeMethods(env,
- "io/requery/android/database/sqlite/SQLiteConnection",
- sMethods, NELEM(sMethods)
- );
-}
-
-extern int register_android_database_SQLiteGlobal(JNIEnv *env);
-extern int register_android_database_SQLiteDebug(JNIEnv *env);
-extern int register_android_database_SQLiteFunction(JNIEnv *env);
-extern int register_android_database_CursorWindow(JNIEnv *env);
-
-} // namespace android
-
-extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
- JNIEnv *env = 0;
-
- android::gpJavaVM = vm;
- vm->GetEnv((void**)&env, JNI_VERSION_1_4);
-
- android::register_android_database_SQLiteConnection(env);
- android::register_android_database_SQLiteDebug(env);
- android::register_android_database_SQLiteGlobal(env);
- android::register_android_database_CursorWindow(env);
- android::register_android_database_SQLiteFunction(env);
-
- return JNI_VERSION_1_4;
-}
-
-
-
diff --git a/sqlite-android/src/main/jni/sqlite/android_database_SQLiteDebug.cpp b/sqlite-android/src/main/jni/sqlite/android_database_SQLiteDebug.cpp
deleted file mode 100644
index 371b321f02..0000000000
--- a/sqlite-android/src/main/jni/sqlite/android_database_SQLiteDebug.cpp
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2007 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-
-#define LOG_TAG "SQLiteDebug"
-
-#include
-#include "JNIHelp.h"
-#include "ALog-priv.h"
-
-#include
-#include
-#include
-#include
-
-#include
-
-namespace android {
-
-static struct {
- jfieldID memoryUsed;
- jfieldID pageCacheOverflow;
- jfieldID largestMemAlloc;
-} gSQLiteDebugPagerStatsClassInfo;
-
-static void nativeGetPagerStats(JNIEnv *env, jobject clazz, jobject statsObj)
-{
- int memoryUsed;
- int pageCacheOverflow;
- int largestMemAlloc;
- int unused;
-
- sqlite3_status(SQLITE_STATUS_MEMORY_USED, &memoryUsed, &unused, 0);
- sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &unused, &largestMemAlloc, 0);
- sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &pageCacheOverflow, &unused, 0);
- env->SetIntField(statsObj, gSQLiteDebugPagerStatsClassInfo.memoryUsed, memoryUsed);
- env->SetIntField(statsObj, gSQLiteDebugPagerStatsClassInfo.pageCacheOverflow,
- pageCacheOverflow);
- env->SetIntField(statsObj, gSQLiteDebugPagerStatsClassInfo.largestMemAlloc, largestMemAlloc);
-}
-
-/*
- * JNI registration.
- */
-
-static JNINativeMethod gMethods[] =
-{
- { "nativeGetPagerStats", "(Lio/requery/android/database/sqlite/SQLiteDebug$PagerStats;)V",
- (void*) nativeGetPagerStats },
-};
-
-int register_android_database_SQLiteDebug(JNIEnv *env)
-{
- jclass clazz;
- FIND_CLASS(clazz, "io/requery/android/database/sqlite/SQLiteDebug$PagerStats");
-
- GET_FIELD_ID(gSQLiteDebugPagerStatsClassInfo.memoryUsed, clazz,
- "memoryUsed", "I");
- GET_FIELD_ID(gSQLiteDebugPagerStatsClassInfo.largestMemAlloc, clazz,
- "largestMemAlloc", "I");
- GET_FIELD_ID(gSQLiteDebugPagerStatsClassInfo.pageCacheOverflow, clazz,
- "pageCacheOverflow", "I");
-
- return jniRegisterNativeMethods(env, "io/requery/android/database/sqlite/SQLiteDebug",
- gMethods, NELEM(gMethods));
-}
-
-} // namespace android
diff --git a/sqlite-android/src/main/jni/sqlite/android_database_SQLiteFunction.cpp b/sqlite-android/src/main/jni/sqlite/android_database_SQLiteFunction.cpp
deleted file mode 100644
index f376593dda..0000000000
--- a/sqlite-android/src/main/jni/sqlite/android_database_SQLiteFunction.cpp
+++ /dev/null
@@ -1,229 +0,0 @@
-#define LOG_TAG "SQLiteFunction"
-
-#include
-#include
-#include
-#include
-#include
-
-#include "sqlite3.h"
-#include "JNIHelp.h"
-#include "ALog-priv.h"
-#include "android_database_SQLiteCommon.h"
-
-namespace android {
-
-/* Returns the sqlite3_value for the given arg of the given function.
- * If 0 is returned, an exception has been thrown to report the reason. */
-static sqlite3_value *tovalue(JNIEnv *env, jlong argsPtr, jint arg) {
- if (arg < 0) {
- throw_sqlite3_exception(env, "Invalid arg index");
- return 0;
- }
- if (!argsPtr) {
- throw_sqlite3_exception(env, "Invalid argsPtr");
- return 0;
- }
-
- sqlite3_value **args = reinterpret_cast(argsPtr);
- return args[arg];
-}
-
-static sqlite3_context *tocontext(JNIEnv *env, jlong contextPtr) {
- if (!contextPtr) {
- throw_sqlite3_exception(env, "Invalid contextPtr");
- return 0;
- }
-
- return reinterpret_cast(contextPtr);
-}
-
-/*
- * Getters
- */
-
-static jbyteArray nativeGetArgBlob(JNIEnv* env, jclass clazz, jlong argsPtr,
- jint arg) {
- int length;
- jbyteArray byteArray;
- const void *blob;
-
- sqlite3_value *value = tovalue(env, argsPtr, arg);
- if (!value) return NULL;
-
- blob = sqlite3_value_blob(value);
- if (!blob) return NULL;
-
- length = sqlite3_value_bytes(value);
- byteArray = env->NewByteArray(length);
- if (!byteArray) {
- env->ExceptionClear();
- throw_sqlite3_exception(env, "Native could not create new byte[]");
- return NULL;
- }
-
- env->SetByteArrayRegion(byteArray, 0, length, static_cast(blob));
- return byteArray;
-}
-
-static jstring nativeGetArgString(JNIEnv* env, jclass clazz, jlong argsPtr,
- jint arg) {
- sqlite3_value *value = tovalue(env, argsPtr, arg);
- if (!value) return NULL;
-
- const jchar* chars = static_cast(sqlite3_value_text16(value));
- if (!chars) return NULL;
-
- size_t len = sqlite3_value_bytes16(value) / sizeof(jchar);
- jstring str = env->NewString(chars, len);
- if (!str) {
- env->ExceptionClear();
- throw_sqlite3_exception(env, "Native could not allocate string");
- return NULL;
- }
-
- return str;
-}
-
-static jlong nativeGetArgLong(JNIEnv* env, jclass clazz, jlong argsPtr,
- jint arg) {
- sqlite3_value *value = tovalue(env, argsPtr, arg);
- return value ? sqlite3_value_int64(value) : 0;
-}
-
-static jdouble nativeGetArgDouble(JNIEnv* env, jclass clazz, jlong argsPtr,
- jint arg) {
- sqlite3_value *value = tovalue(env, argsPtr, arg);
- return value ? sqlite3_value_double(value) : 0;
-}
-
-static jint nativeGetArgInt(JNIEnv* env, jclass clazz, jlong argsPtr,
- jint arg) {
- sqlite3_value *value = tovalue(env, argsPtr, arg);
- return value ? sqlite3_value_int(value) : 0;
-}
-
-/*
- * Setters
- */
-
-static void nativeSetResultBlob(JNIEnv* env, jclass clazz,
- jlong contextPtr, jbyteArray result) {
- sqlite3_context *context = tocontext(env, contextPtr);
- if (!context) return;
- if (result == NULL) {
- sqlite3_result_null(context);
- return;
- }
-
- jsize len = env->GetArrayLength(result);
- void *bytes = env->GetPrimitiveArrayCritical(result, NULL);
- if (!bytes) {
- env->ExceptionClear();
- throw_sqlite3_exception(env, "Out of memory accepting blob");
- return;
- }
-
- sqlite3_result_blob(context, bytes, len, SQLITE_TRANSIENT);
- env->ReleasePrimitiveArrayCritical(result, bytes, JNI_ABORT);
-}
-
-static void nativeSetResultString(JNIEnv* env, jclass clazz,
- jlong contextPtr, jstring result) {
- sqlite3_context *context = tocontext(env, contextPtr);
- if (result == NULL) {
- sqlite3_result_null(context);
- return;
- }
-
- const char* chars = env->GetStringUTFChars(result, NULL);
- if (!chars) {
- ALOGE("result value can't be transferred to UTFChars");
- sqlite3_result_error_nomem(context);
- return;
- }
-
- sqlite3_result_text(context, chars, -1, SQLITE_TRANSIENT);
- env->ReleaseStringUTFChars(result, chars);
-}
-
-static void nativeSetResultLong(JNIEnv* env, jclass clazz,
- jlong contextPtr, jlong result) {
- sqlite3_context *context = tocontext(env, contextPtr);
- if (context) sqlite3_result_int64(context, result);
-}
-
-static void nativeSetResultDouble(JNIEnv* env, jclass clazz,
- jlong contextPtr, jdouble result) {
- sqlite3_context *context = tocontext(env, contextPtr);
- if (context) sqlite3_result_double(context, result);
-}
-
-static void nativeSetResultInt(JNIEnv* env, jclass clazz,
- jlong contextPtr, jint result) {
- sqlite3_context *context = tocontext(env, contextPtr);
- if (context) sqlite3_result_int(context, result);
-}
-
-static void nativeSetResultError(JNIEnv* env, jclass clazz,
- jlong contextPtr, jstring error) {
- sqlite3_context *context = tocontext(env, contextPtr);
- if (error == NULL) {
- sqlite3_result_null(context);
- return;
- }
-
- const char* chars = env->GetStringUTFChars(error, NULL);
- if (!chars) {
- ALOGE("result value can't be transferred to UTFChars");
- sqlite3_result_error_nomem(context);
- return;
- }
-
- sqlite3_result_error(context, chars, -1);
- env->ReleaseStringUTFChars(error, chars);
-}
-
-static void nativeSetResultNull(JNIEnv* env, jclass clazz, jlong contextPtr) {
- sqlite3_context *context = tocontext(env, contextPtr);
- if (context) sqlite3_result_null(context);
-}
-
-
-static const JNINativeMethod sMethods[] =
-{
- /* name, signature, funcPtr */
- { "nativeGetArgBlob", "(JI)[B",
- (void*)nativeGetArgBlob },
- { "nativeGetArgString", "(JI)Ljava/lang/String;",
- (void*)nativeGetArgString },
- { "nativeGetArgLong", "(JI)J",
- (void*)nativeGetArgLong },
- { "nativeGetArgDouble", "(JI)D",
- (void*)nativeGetArgDouble },
- { "nativeGetArgInt", "(JI)I",
- (void*)nativeGetArgInt },
-
- { "nativeSetResultBlob", "(J[B)V",
- (void*)nativeSetResultBlob },
- { "nativeSetResultString", "(JLjava/lang/String;)V",
- (void*)nativeSetResultString },
- { "nativeSetResultLong", "(JJ)V",
- (void*)nativeSetResultLong },
- { "nativeSetResultDouble", "(JD)V",
- (void*)nativeSetResultDouble },
- { "nativeSetResultInt", "(JI)V",
- (void*)nativeSetResultInt },
- { "nativeSetResultError", "(JLjava/lang/String;)V",
- (void*)nativeSetResultError },
- { "nativeSetResultNull", "(J)V",
- (void*)nativeSetResultNull },
-};
-
-int register_android_database_SQLiteFunction(JNIEnv* env)
-{
- return jniRegisterNativeMethods(env,
- "io/requery/android/database/sqlite/SQLiteFunction", sMethods, NELEM(sMethods));
-}
-
-} // namespace android
diff --git a/sqlite-android/src/main/jni/sqlite/android_database_SQLiteGlobal.cpp b/sqlite-android/src/main/jni/sqlite/android_database_SQLiteGlobal.cpp
deleted file mode 100644
index a07b48e272..0000000000
--- a/sqlite-android/src/main/jni/sqlite/android_database_SQLiteGlobal.cpp
+++ /dev/null
@@ -1,92 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-// modified from original source see README at the top level of this project
-
-#define LOG_TAG "SQLiteGlobal"
-
-#include
-#include
-#include "ALog-priv.h"
-
-#include
-//#include
-
-#include "android_database_SQLiteCommon.h"
-
-namespace android {
-
-// Limit heap to 8MB for now. This is 4 times the maximum cursor window
-// size, as has been used by the original code in SQLiteDatabase for
-// a long time.
-static const int SOFT_HEAP_LIMIT = 8 * 1024 * 1024;
-
-
-// Called each time a message is logged.
-static void sqliteLogCallback(void* data, int iErrCode, const char* zMsg) {
- bool verboseLog = !!data;
- if (iErrCode == 0 || iErrCode == SQLITE_CONSTRAINT || iErrCode == SQLITE_SCHEMA) {
- if (verboseLog) {
- ALOG(LOG_VERBOSE, SQLITE_LOG_TAG, "(%d) %s\n", iErrCode, zMsg);
- }
- } else {
- ALOG(LOG_ERROR, SQLITE_LOG_TAG, "(%d) %s\n", iErrCode, zMsg);
- }
-}
-
-// Sets the global SQLite configuration.
-// This must be called before any other SQLite functions are called.
-static void sqliteInitialize() {
- // Enable multi-threaded mode. In this mode, SQLite is safe to use by multiple
- // threads as long as no two threads use the same database connection at the same
- // time (which we guarantee in the SQLite database wrappers).
- sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
-
- // Redirect SQLite log messages to the Android log.
-#if 0
- bool verboseLog = android_util_Log_isVerboseLogEnabled(SQLITE_LOG_TAG);
-#endif
- bool verboseLog = false;
- sqlite3_config(SQLITE_CONFIG_LOG, &sqliteLogCallback, verboseLog ? (void*)1 : NULL);
-
- // The soft heap limit prevents the page cache allocations from growing
- // beyond the given limit, no matter what the max page cache sizes are
- // set to. The limit does not, as of 3.5.0, affect any other allocations.
- sqlite3_soft_heap_limit(SOFT_HEAP_LIMIT);
-
- // Initialize SQLite.
- sqlite3_initialize();
-}
-
-static jint nativeReleaseMemory(JNIEnv* env, jclass clazz) {
- return sqlite3_release_memory(SOFT_HEAP_LIMIT);
-}
-
-static JNINativeMethod sMethods[] =
-{
- /* name, signature, funcPtr */
- { "nativeReleaseMemory", "()I",
- (void*)nativeReleaseMemory },
-};
-
-int register_android_database_SQLiteGlobal(JNIEnv *env)
-{
- sqliteInitialize();
-
- return jniRegisterNativeMethods(env, "io/requery/android/database/sqlite/SQLiteGlobal",
- sMethods, NELEM(sMethods));
-}
-
-} // namespace android