summaryrefslogtreecommitdiff
path: root/core/java/android
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android')
-rw-r--r--core/java/android/database/sqlite/SQLiteConnection.java50
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabase.java89
-rw-r--r--core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java23
3 files changed, 120 insertions, 42 deletions
diff --git a/core/java/android/database/sqlite/SQLiteConnection.java b/core/java/android/database/sqlite/SQLiteConnection.java
index f7222750b89b..796cfdce2c0d 100644
--- a/core/java/android/database/sqlite/SQLiteConnection.java
+++ b/core/java/android/database/sqlite/SQLiteConnection.java
@@ -39,6 +39,8 @@ import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Map;
+import java.util.function.BinaryOperator;
+import java.util.function.UnaryOperator;
/**
* Represents a SQLite database connection.
@@ -123,8 +125,10 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
boolean enableTrace, boolean enableProfile, int lookasideSlotSize,
int lookasideSlotCount);
private static native void nativeClose(long connectionPtr);
- private static native void nativeRegisterCustomFunction(long connectionPtr,
- SQLiteCustomFunction function);
+ private static native void nativeRegisterCustomScalarFunction(long connectionPtr,
+ String name, UnaryOperator<String> function);
+ private static native void nativeRegisterCustomAggregateFunction(long connectionPtr,
+ String name, BinaryOperator<String> 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);
@@ -225,13 +229,7 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
setJournalSizeLimit();
setAutoCheckpointInterval();
setLocaleFromConfiguration();
-
- // Register custom functions.
- final int functionCount = mConfiguration.customFunctions.size();
- for (int i = 0; i < functionCount; i++) {
- SQLiteCustomFunction function = mConfiguration.customFunctions.get(i);
- nativeRegisterCustomFunction(mConnectionPtr, function);
- }
+ setCustomFunctionsFromConfiguration();
}
private void dispose(boolean finalized) {
@@ -457,6 +455,19 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
}
}
+ private void setCustomFunctionsFromConfiguration() {
+ for (int i = 0; i < mConfiguration.customScalarFunctions.size(); i++) {
+ nativeRegisterCustomScalarFunction(mConnectionPtr,
+ mConfiguration.customScalarFunctions.keyAt(i),
+ mConfiguration.customScalarFunctions.valueAt(i));
+ }
+ for (int i = 0; i < mConfiguration.customAggregateFunctions.size(); i++) {
+ nativeRegisterCustomAggregateFunction(mConnectionPtr,
+ mConfiguration.customAggregateFunctions.keyAt(i),
+ mConfiguration.customAggregateFunctions.valueAt(i));
+ }
+ }
+
private void checkDatabaseWiped() {
if (!SQLiteGlobal.checkDbWipe()) {
return;
@@ -491,15 +502,6 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
void reconfigure(SQLiteDatabaseConfiguration configuration) {
mOnlyAllowReadOnlyOperations = false;
- // Register custom functions.
- final int functionCount = configuration.customFunctions.size();
- for (int i = 0; i < functionCount; i++) {
- SQLiteCustomFunction function = configuration.customFunctions.get(i);
- if (!mConfiguration.customFunctions.contains(function)) {
- nativeRegisterCustomFunction(mConnectionPtr, function);
- }
- }
-
// Remember what changed.
boolean foreignKeyModeChanged = configuration.foreignKeyConstraintsEnabled
!= mConfiguration.foreignKeyConstraintsEnabled;
@@ -507,6 +509,10 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
& (SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING
| SQLiteDatabase.ENABLE_LEGACY_COMPATIBILITY_WAL)) != 0;
boolean localeChanged = !configuration.locale.equals(mConfiguration.locale);
+ boolean customScalarFunctionsChanged = !configuration.customScalarFunctions
+ .equals(mConfiguration.customScalarFunctions);
+ boolean customAggregateFunctionsChanged = !configuration.customAggregateFunctions
+ .equals(mConfiguration.customAggregateFunctions);
// Update configuration parameters.
mConfiguration.updateParametersFrom(configuration);
@@ -514,20 +520,18 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
// 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();
}
+ if (customScalarFunctionsChanged || customAggregateFunctionsChanged) {
+ setCustomFunctionsFromConfiguration();
+ }
}
// Called by SQLiteConnectionPool only.
diff --git a/core/java/android/database/sqlite/SQLiteDatabase.java b/core/java/android/database/sqlite/SQLiteDatabase.java
index 44c78aa783a7..458914efcbbd 100644
--- a/core/java/android/database/sqlite/SQLiteDatabase.java
+++ b/core/java/android/database/sqlite/SQLiteDatabase.java
@@ -62,6 +62,8 @@ import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.WeakHashMap;
+import java.util.function.BinaryOperator;
+import java.util.function.UnaryOperator;
/**
* Exposes methods to manage a SQLite database.
@@ -958,26 +960,87 @@ public final class SQLiteDatabase extends SQLiteClosable {
}
/**
- * 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
- */
- public void addCustomFunction(String name, int numArgs, CustomFunction function) {
- // Create wrapper (also validates arguments).
- SQLiteCustomFunction wrapper = new SQLiteCustomFunction(name, numArgs, function);
+ * Register a custom scalar function that can be called from SQL
+ * expressions.
+ * <p>
+ * For example, registering a custom scalar function named {@code REVERSE}
+ * could be used in a query like
+ * {@code SELECT REVERSE(name) FROM employees}.
+ * <p>
+ * When attempting to register multiple functions with the same function
+ * name, SQLite will replace any previously defined functions with the
+ * latest definition, regardless of what function type they are. SQLite does
+ * not support unregistering functions.
+ *
+ * @param functionName Case-insensitive name to register this function
+ * under, limited to 255 UTF-8 bytes in length.
+ * @param scalarFunction Functional interface that will be invoked when the
+ * function name is used by a SQL statement. The argument values
+ * from the SQL statement are passed to the functional interface,
+ * and the return values from the functional interface are
+ * returned back into the SQL statement.
+ * @throws SQLiteException if the custom function could not be registered.
+ * @see #setCustomAggregateFunction(String, BinaryOperator)
+ */
+ public void setCustomScalarFunction(@NonNull String functionName,
+ @NonNull UnaryOperator<String> scalarFunction) throws SQLiteException {
+ Objects.requireNonNull(functionName);
+ Objects.requireNonNull(scalarFunction);
+
+ synchronized (mLock) {
+ throwIfNotOpenLocked();
+
+ mConfigurationLocked.customScalarFunctions.put(functionName, scalarFunction);
+ try {
+ mConnectionPoolLocked.reconfigure(mConfigurationLocked);
+ } catch (RuntimeException ex) {
+ mConfigurationLocked.customScalarFunctions.remove(functionName);
+ throw ex;
+ }
+ }
+ }
+
+ /**
+ * Register a custom aggregate function that can be called from SQL
+ * expressions.
+ * <p>
+ * For example, registering a custom aggregation function named
+ * {@code LONGEST} could be used in a query like
+ * {@code SELECT LONGEST(name) FROM employees}.
+ * <p>
+ * The implementation of this method follows the reduction flow outlined in
+ * {@link java.util.stream.Stream#reduce(BinaryOperator)}, and the custom
+ * aggregation function is expected to be an associative accumulation
+ * function, as defined by that class.
+ * <p>
+ * When attempting to register multiple functions with the same function
+ * name, SQLite will replace any previously defined functions with the
+ * latest definition, regardless of what function type they are. SQLite does
+ * not support unregistering functions.
+ *
+ * @param functionName Case-insensitive name to register this function
+ * under, limited to 255 UTF-8 bytes in length.
+ * @param aggregateFunction Functional interface that will be invoked when
+ * the function name is used by a SQL statement. The argument
+ * values from the SQL statement are passed to the functional
+ * interface, and the return values from the functional interface
+ * are returned back into the SQL statement.
+ * @throws SQLiteException if the custom function could not be registered.
+ * @see #setCustomScalarFunction(String, UnaryOperator)
+ */
+ public void setCustomAggregateFunction(@NonNull String functionName,
+ @NonNull BinaryOperator<String> aggregateFunction) throws SQLiteException {
+ Objects.requireNonNull(functionName);
+ Objects.requireNonNull(aggregateFunction);
synchronized (mLock) {
throwIfNotOpenLocked();
- mConfigurationLocked.customFunctions.add(wrapper);
+ mConfigurationLocked.customAggregateFunctions.put(functionName, aggregateFunction);
try {
mConnectionPoolLocked.reconfigure(mConfigurationLocked);
} catch (RuntimeException ex) {
- mConfigurationLocked.customFunctions.remove(wrapper);
+ mConfigurationLocked.customAggregateFunctions.remove(functionName);
throw ex;
}
}
diff --git a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
index 6a52b72a9e1c..b11942abe0c7 100644
--- a/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
+++ b/core/java/android/database/sqlite/SQLiteDatabaseConfiguration.java
@@ -17,9 +17,12 @@
package android.database.sqlite;
import android.compat.annotation.UnsupportedAppUsage;
+import android.util.ArrayMap;
-import java.util.ArrayList;
import java.util.Locale;
+import java.util.Map;
+import java.util.function.BinaryOperator;
+import java.util.function.UnaryOperator;
import java.util.regex.Pattern;
/**
@@ -87,10 +90,16 @@ public final class SQLiteDatabaseConfiguration {
public boolean foreignKeyConstraintsEnabled;
/**
- * The custom functions to register.
+ * The custom scalar functions to register.
*/
- public final ArrayList<SQLiteCustomFunction> customFunctions =
- new ArrayList<SQLiteCustomFunction>();
+ public final ArrayMap<String, UnaryOperator<String>> customScalarFunctions
+ = new ArrayMap<>();
+
+ /**
+ * The custom aggregate functions to register.
+ */
+ public final ArrayMap<String, BinaryOperator<String>> customAggregateFunctions
+ = new ArrayMap<>();
/**
* The size in bytes of each lookaside slot
@@ -181,8 +190,10 @@ public final class SQLiteDatabaseConfiguration {
maxSqlCacheSize = other.maxSqlCacheSize;
locale = other.locale;
foreignKeyConstraintsEnabled = other.foreignKeyConstraintsEnabled;
- customFunctions.clear();
- customFunctions.addAll(other.customFunctions);
+ customScalarFunctions.clear();
+ customScalarFunctions.putAll(other.customScalarFunctions);
+ customAggregateFunctions.clear();
+ customAggregateFunctions.putAll(other.customAggregateFunctions);
lookasideSlotSize = other.lookasideSlotSize;
lookasideSlotCount = other.lookasideSlotCount;
idleConnectionTimeoutMs = other.idleConnectionTimeoutMs;