@@ -99,6 +99,7 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
9999 private final SQLiteDatabaseConfiguration mConfiguration ;
100100 private final int mConnectionId ;
101101 private final boolean mIsPrimaryConnection ;
102+ private final boolean mIsReadOnlyConnection ;
102103 private final PreparedStatementCache mPreparedStatementCache ;
103104 private PreparedStatement mPreparedStatementPool ;
104105
@@ -111,7 +112,7 @@ public final class SQLiteConnection implements CancellationSignal.OnCancelListen
111112 private boolean mOnlyAllowReadOnlyOperations ;
112113
113114 // The number of times attachCancellationSignal has been called.
114- // Because SQLite statement execution can be re-entrant , we keep track of how many
115+ // Because SQLite statement execution can be reentrant , we keep track of how many
115116 // times we have attempted to attach a cancellation signal to the connection so that
116117 // we can ensure that we detach the signal at the right time.
117118 private int mCancellationSignalAttachCount ;
@@ -121,7 +122,7 @@ private static native int nativeOpen(String path, int openFlags, String label,
121122 private static native void nativeClose (int connectionPtr );
122123 private static native void nativeRegisterCustomFunction (int connectionPtr ,
123124 SQLiteCustomFunction function );
124- private static native void nativeSetLocale (int connectionPtr , String locale );
125+ private static native void nativeRegisterLocalizedCollators (int connectionPtr , String locale );
125126 private static native int nativePrepareStatement (int connectionPtr , String sql );
126127 private static native void nativeFinalizeStatement (int connectionPtr , int statementPtr );
127128 private static native int nativeGetParameterCount (int connectionPtr , int statementPtr );
@@ -163,6 +164,7 @@ private SQLiteConnection(SQLiteConnectionPool pool,
163164 mConfiguration = new SQLiteDatabaseConfiguration (configuration );
164165 mConnectionId = connectionId ;
165166 mIsPrimaryConnection = primaryConnection ;
167+ mIsReadOnlyConnection = (configuration .openFlags & SQLiteDatabase .OPEN_READONLY ) != 0 ;
166168 mPreparedStatementCache = new PreparedStatementCache (
167169 mConfiguration .maxSqlCacheSize );
168170 mCloseGuard .open ("close" );
@@ -237,45 +239,102 @@ private void dispose(boolean finalized) {
237239 }
238240
239241 private void setPageSize () {
240- if (!mConfiguration .isInMemoryDb ()) {
241- execute ("PRAGMA page_size=" + SQLiteGlobal .getDefaultPageSize (), null , null );
242+ if (!mConfiguration .isInMemoryDb () && !mIsReadOnlyConnection ) {
243+ final long newValue = SQLiteGlobal .getDefaultPageSize ();
244+ long value = executeForLong ("PRAGMA page_size" , null , null );
245+ if (value != newValue ) {
246+ execute ("PRAGMA page_size=" + newValue , null , null );
247+ }
242248 }
243249 }
244250
245251 private void setAutoCheckpointInterval () {
246- if (!mConfiguration .isInMemoryDb ()) {
247- executeForLong ("PRAGMA wal_autocheckpoint=" + SQLiteGlobal .getWALAutoCheckpoint (),
248- null , null );
252+ if (!mConfiguration .isInMemoryDb () && !mIsReadOnlyConnection ) {
253+ final long newValue = SQLiteGlobal .getWALAutoCheckpoint ();
254+ long value = executeForLong ("PRAGMA wal_autocheckpoint" , null , null );
255+ if (value != newValue ) {
256+ executeForLong ("PRAGMA wal_autocheckpoint=" + newValue , null , null );
257+ }
249258 }
250259 }
251260
252261 private void setJournalSizeLimit () {
253- if (!mConfiguration .isInMemoryDb ()) {
254- executeForLong ("PRAGMA journal_size_limit=" + SQLiteGlobal .getJournalSizeLimit (),
255- null , null );
262+ if (!mConfiguration .isInMemoryDb () && !mIsReadOnlyConnection ) {
263+ final long newValue = SQLiteGlobal .getJournalSizeLimit ();
264+ long value = executeForLong ("PRAGMA journal_size_limit" , null , null );
265+ if (value != newValue ) {
266+ executeForLong ("PRAGMA journal_size_limit=" + newValue , null , null );
267+ }
256268 }
257269 }
258270
259271 private void setSyncModeFromConfiguration () {
260- if (!mConfiguration .isInMemoryDb ()) {
261- execute ("PRAGMA synchronous=" + mConfiguration .syncMode , null , null );
272+ if (!mConfiguration .isInMemoryDb () && !mIsReadOnlyConnection ) {
273+ final String newValue = mConfiguration .syncMode ;
274+ String value = executeForString ("PRAGMA synchronous" , null , null );
275+ if (!value .equalsIgnoreCase (newValue )) {
276+ execute ("PRAGMA synchronous=" + newValue , null , null );
277+ }
262278 }
263279 }
264280
265281 private void setJournalModeFromConfiguration () {
266- if (!mConfiguration .isInMemoryDb ()) {
267- String result = executeForString ("PRAGMA journal_mode=" + mConfiguration .journalMode ,
268- null , null );
269- if (!result .equalsIgnoreCase (mConfiguration .journalMode )) {
270- Log .e (TAG , "setting journal_mode to " + mConfiguration .journalMode
271- + " failed for db: " + mConfiguration .label
272- + " (on pragma set journal_mode, sqlite returned:" + result );
282+ if (!mConfiguration .isInMemoryDb () && !mIsReadOnlyConnection ) {
283+ final String newValue = mConfiguration .journalMode ;
284+ String value = executeForString ("PRAGMA journal_mode" , null , null );
285+ if (!value .equalsIgnoreCase (newValue )) {
286+ value = executeForString ("PRAGMA journal_mode=" + newValue , null , null );
287+ if (!value .equalsIgnoreCase (newValue )) {
288+ Log .e (TAG , "setting journal_mode to " + newValue
289+ + " failed for db: " + mConfiguration .label
290+ + " (on pragma set journal_mode, sqlite returned:" + value );
291+ }
273292 }
274293 }
275294 }
276295
277296 private void setLocaleFromConfiguration () {
278- nativeSetLocale (mConnectionPtr , mConfiguration .locale .toString ());
297+ if ((mConfiguration .openFlags & SQLiteDatabase .NO_LOCALIZED_COLLATORS ) != 0 ) {
298+ return ;
299+ }
300+
301+ // Register the localized collators.
302+ final String newLocale = mConfiguration .locale .toString ();
303+ nativeRegisterLocalizedCollators (mConnectionPtr , newLocale );
304+
305+ // If the database is read-only, we cannot modify the android metadata table
306+ // or existing indexes.
307+ if (mIsReadOnlyConnection ) {
308+ return ;
309+ }
310+
311+ try {
312+ // Ensure the android metadata table exists.
313+ execute ("CREATE TABLE IF NOT EXISTS android_metadata (locale TEXT)" , null , null );
314+
315+ // Check whether the locale was actually changed.
316+ final String oldLocale = executeForString ("SELECT locale FROM android_metadata "
317+ + "UNION SELECT NULL ORDER BY locale DESC LIMIT 1" , null , null );
318+ if (oldLocale != null && oldLocale .equals (newLocale )) {
319+ return ;
320+ }
321+
322+ // Go ahead and update the indexes using the new locale.
323+ execute ("BEGIN" , null , null );
324+ boolean success = false ;
325+ try {
326+ execute ("DELETE FROM android_metadata" , null , null );
327+ execute ("INSERT INTO android_metadata (locale) VALUES(?)" ,
328+ new Object [] { newLocale }, null );
329+ execute ("REINDEX LOCALIZED" , null , null );
330+ success = true ;
331+ } finally {
332+ execute (success ? "COMMIT" : "ROLLBACK" , null , null );
333+ }
334+ } catch (RuntimeException ex ) {
335+ throw new SQLiteException ("Failed to change locale for db '" + mConfiguration .label
336+ + "' to '" + newLocale + "'." , ex );
337+ }
279338 }
280339
281340 // Called by SQLiteConnectionPool only.
0 commit comments