• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.database.sqlite;
18 
19 import android.compat.annotation.UnsupportedAppUsage;
20 import android.util.ArrayMap;
21 import android.util.Pair;
22 
23 import java.util.ArrayList;
24 import java.util.Locale;
25 import java.util.function.BinaryOperator;
26 import java.util.function.UnaryOperator;
27 import java.util.regex.Pattern;
28 
29 /**
30  * Describes how to configure a database.
31  * <p>
32  * The purpose of this object is to keep track of all of the little
33  * configuration settings that are applied to a database after it
34  * is opened so that they can be applied to all connections in the
35  * connection pool uniformly.
36  * </p><p>
37  * Each connection maintains its own copy of this object so it can
38  * keep track of which settings have already been applied.
39  * </p>
40  *
41  * @hide
42  */
43 public final class SQLiteDatabaseConfiguration {
44     // The pattern we use to strip email addresses from database paths
45     // when constructing a label to use in log messages.
46     private static final Pattern EMAIL_IN_DB_PATTERN =
47             Pattern.compile("[\\w\\.\\-]+@[\\w\\.\\-]+");
48 
49     /**
50      * Special path used by in-memory databases.
51      */
52     public static final String MEMORY_DB_PATH = ":memory:";
53 
54     /**
55      * The database path.
56      */
57     public final String path;
58 
59     /**
60      * The label to use to describe the database when it appears in logs.
61      * This is derived from the path but is stripped to remove PII.
62      */
63     public final String label;
64 
65     /**
66      * The flags used to open the database.
67      */
68     public int openFlags;
69 
70     /**
71      * The maximum size of the prepared statement cache for each database connection.
72      * Must be non-negative.
73      *
74      * Default is 25.
75      */
76     @UnsupportedAppUsage
77     public int maxSqlCacheSize;
78 
79     /**
80      * The database locale.
81      *
82      * Default is the value returned by {@link Locale#getDefault()}.
83      */
84     public Locale locale;
85 
86     /**
87      * True if foreign key constraints are enabled.
88      *
89      * Default is false.
90      */
91     public boolean foreignKeyConstraintsEnabled;
92 
93     /**
94      * The custom scalar functions to register.
95      */
96     public final ArrayMap<String, UnaryOperator<String>> customScalarFunctions
97             = new ArrayMap<>();
98 
99     /**
100      * The custom aggregate functions to register.
101      */
102     public final ArrayMap<String, BinaryOperator<String>> customAggregateFunctions
103             = new ArrayMap<>();
104 
105     /**
106      * The statements to execute to initialize each connection.
107      */
108     public final ArrayList<Pair<String, Object[]>> perConnectionSql = new ArrayList<>();
109 
110     /**
111      * The size in bytes of each lookaside slot
112      *
113      * <p>If negative, the default lookaside configuration will be used
114      */
115     public int lookasideSlotSize = -1;
116 
117     /**
118      * The total number of lookaside memory slots per database connection
119      *
120      * <p>If negative, the default lookaside configuration will be used
121      */
122     public int lookasideSlotCount = -1;
123 
124     /**
125      * The number of milliseconds that SQLite connection is allowed to be idle before it
126      * is closed and removed from the pool.
127      * <p>By default, idle connections are not closed
128      */
129     public long idleConnectionTimeoutMs = Long.MAX_VALUE;
130 
131     /**
132      * Journal mode to use when {@link SQLiteDatabase#ENABLE_WRITE_AHEAD_LOGGING} is not set.
133      * <p>Default is returned by {@link SQLiteGlobal#getDefaultJournalMode()}
134      */
135     public String journalMode;
136 
137     /**
138      * Synchronous mode to use.
139      * <p>Default is returned by {@link SQLiteGlobal#getDefaultSyncMode()}
140      * or {@link SQLiteGlobal#getWALSyncMode()} depending on journal mode
141      */
142     public String syncMode;
143 
144     /**
145      * Creates a database configuration with the required parameters for opening a
146      * database and default values for all other parameters.
147      *
148      * @param path The database path.
149      * @param openFlags Open flags for the database, such as {@link SQLiteDatabase#OPEN_READWRITE}.
150      */
SQLiteDatabaseConfiguration(String path, int openFlags)151     public SQLiteDatabaseConfiguration(String path, int openFlags) {
152         if (path == null) {
153             throw new IllegalArgumentException("path must not be null.");
154         }
155 
156         this.path = path;
157         label = stripPathForLogs(path);
158         this.openFlags = openFlags;
159 
160         // Set default values for optional parameters.
161         maxSqlCacheSize = 25;
162         locale = Locale.getDefault();
163     }
164 
165     /**
166      * Creates a database configuration as a copy of another configuration.
167      *
168      * @param other The other configuration.
169      */
SQLiteDatabaseConfiguration(SQLiteDatabaseConfiguration other)170     public SQLiteDatabaseConfiguration(SQLiteDatabaseConfiguration other) {
171         if (other == null) {
172             throw new IllegalArgumentException("other must not be null.");
173         }
174 
175         this.path = other.path;
176         this.label = other.label;
177         updateParametersFrom(other);
178     }
179 
180     /**
181      * Updates the non-immutable parameters of this configuration object
182      * from the other configuration object.
183      *
184      * @param other The object from which to copy the parameters.
185      */
updateParametersFrom(SQLiteDatabaseConfiguration other)186     public void updateParametersFrom(SQLiteDatabaseConfiguration other) {
187         if (other == null) {
188             throw new IllegalArgumentException("other must not be null.");
189         }
190         if (!path.equals(other.path)) {
191             throw new IllegalArgumentException("other configuration must refer to "
192                     + "the same database.");
193         }
194 
195         openFlags = other.openFlags;
196         maxSqlCacheSize = other.maxSqlCacheSize;
197         locale = other.locale;
198         foreignKeyConstraintsEnabled = other.foreignKeyConstraintsEnabled;
199         customScalarFunctions.clear();
200         customScalarFunctions.putAll(other.customScalarFunctions);
201         customAggregateFunctions.clear();
202         customAggregateFunctions.putAll(other.customAggregateFunctions);
203         perConnectionSql.clear();
204         perConnectionSql.addAll(other.perConnectionSql);
205         lookasideSlotSize = other.lookasideSlotSize;
206         lookasideSlotCount = other.lookasideSlotCount;
207         idleConnectionTimeoutMs = other.idleConnectionTimeoutMs;
208         journalMode = other.journalMode;
209         syncMode = other.syncMode;
210     }
211 
212     /**
213      * Returns true if the database is in-memory.
214      * @return True if the database is in-memory.
215      */
isInMemoryDb()216     public boolean isInMemoryDb() {
217         return path.equalsIgnoreCase(MEMORY_DB_PATH);
218     }
219 
isLegacyCompatibilityWalEnabled()220     boolean isLegacyCompatibilityWalEnabled() {
221         return journalMode == null && syncMode == null
222                 && (openFlags & SQLiteDatabase.ENABLE_LEGACY_COMPATIBILITY_WAL) != 0;
223     }
224 
stripPathForLogs(String path)225     private static String stripPathForLogs(String path) {
226         if (path.indexOf('@') == -1) {
227             return path;
228         }
229         return EMAIL_IN_DB_PATTERN.matcher(path).replaceAll("XX@YY");
230     }
231 
isLookasideConfigSet()232     boolean isLookasideConfigSet() {
233         return lookasideSlotCount >= 0 && lookasideSlotSize >= 0;
234     }
235 }
236