SQLite Android Bindings
Check-in [9c4a073c3b]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:In SQLITE_HAS_CODEC builds, do not initialize the LOCALIZED collation automatically (as if the SQLiteDatabase.NO_LOCALIZED_COLLATORS flag was set). Require apps to call the enableLocalizedCollators() method to explicitly initialize it. This gives the app an opportunity to execute a PRAGMA statement to configure an encryption key before the database is first accessed.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 9c4a073c3bc186182f0ca952a32591894c0e9bba
User & Date: dan 2013-12-24 12:07:09
Context
2013-12-24
18:51
Add extra SEE tests. And fix problems revealed by the same. check-in: 0b5fd0b0d3 user: dan tags: trunk
12:07
In SQLITE_HAS_CODEC builds, do not initialize the LOCALIZED collation automatically (as if the SQLiteDatabase.NO_LOCALIZED_COLLATORS flag was set). Require apps to call the enableLocalizedCollators() method to explicitly initialize it. This gives the app an opportunity to execute a PRAGMA statement to configure an encryption key before the database is first accessed. check-in: 9c4a073c3b user: dan tags: trunk
2013-12-23
07:33
Add extra required utility functions to ExtraUtils.java. check-in: be6acc5363 user: dan tags: trunk
Changes
Hide Diffs Side-by-Side Diffs Ignore Whitespace Patch

Changes to jni/Android.mk.

     3      3   
     4      4   LOCAL_CFLAGS += -DHAVE_CONFIG_H -DKHTML_NO_EXCEPTIONS -DGKWQ_NO_JAVA
     5      5   LOCAL_CFLAGS += -DNO_SUPPORT_JS_BINDING -DQT_NO_WHEELEVENT -DKHTML_NO_XBL
     6      6   LOCAL_CFLAGS += -U__APPLE__
     7      7   LOCAL_CFLAGS += -Wno-unused-parameter -Wno-int-to-pointer-cast
     8      8   LOCAL_CFLAGS += -Wno-maybe-uninitialized -Wno-parentheses
     9      9   LOCAL_CPPFLAGS += -Wno-conversion-null
           10  +
    10     11   
    11     12   ifeq ($(TARGET_ARCH), arm)
    12     13   	LOCAL_CFLAGS += -DPACKED="__attribute__ ((packed))"
    13     14   else
    14     15   	LOCAL_CFLAGS += -DPACKED=""
    15     16   endif
    16     17   
    17     18   LOCAL_SRC_FILES:=                             \
    18     19   	android_database_SQLiteCommon.cpp     \
    19     20   	android_database_SQLiteConnection.cpp \
    20     21   	android_database_SQLiteGlobal.cpp     \
    21     22   	android_database_SQLiteDebug.cpp      \
    22         -	JNIHelp.cpp JniConstants.cpp          \
    23         -	sqlite3.c
           23  +	JNIHelp.cpp JniConstants.cpp
           24  +
           25  +#
           26  +# For a SEE build, add the SEE sources to the tree and uncomment the first
           27  +# two of the following three lines.
           28  +#
           29  +LOCAL_SRC_FILES += sqlite3-see.c
           30  +LOCAL_CFLAGS    += -DSQLITE_HAS_CODEC
           31  +# LOCAL_SRC_FILES += sqlite3.c
    24     32   
    25     33   LOCAL_C_INCLUDES += nativehelper/
    26     34   
    27     35   LOCAL_MODULE:= libsqliteX
    28     36   LOCAL_LDLIBS += -ldl -llog 
    29     37   
    30     38   include $(BUILD_SHARED_LIBRARY)
    31     39   

Changes to jni/android_database_SQLiteConnection.cpp.

   849    849           sqlite3_progress_handler(connection->db, 4, sqliteProgressHandlerCallback,
   850    850                   connection);
   851    851       } else {
   852    852           sqlite3_progress_handler(connection->db, 0, NULL, NULL);
   853    853       }
   854    854   }
   855    855   
          856  +static jboolean nativeHasCodec(JNIEnv* env, jobject clazz){
          857  +#ifdef SQLITE_HAS_CODEC
          858  +  return true;
          859  +#else
          860  +  return false;
          861  +#endif
          862  +}
          863  +
   856    864   
   857    865   static JNINativeMethod sMethods[] =
   858    866   {
   859    867       /* name, signature, funcPtr */
   860    868       { "nativeOpen", "(Ljava/lang/String;ILjava/lang/String;ZZ)I",
   861    869               (void*)nativeOpen },
   862    870       { "nativeClose", "(I)V",
................................................................................
   905    913               (void*)nativeExecuteForCursorWindow },
   906    914       { "nativeGetDbLookaside", "(I)I",
   907    915               (void*)nativeGetDbLookaside },
   908    916       { "nativeCancel", "(I)V",
   909    917               (void*)nativeCancel },
   910    918       { "nativeResetCancel", "(IZ)V",
   911    919               (void*)nativeResetCancel },
          920  +
          921  +    { "nativeHasCodec", "()Z", (void*)nativeHasCodec },
   912    922   };
   913    923   
   914    924   #define FIND_CLASS(var, className) \
   915    925           var = env->FindClass(className); \
   916    926           LOG_FATAL_IF(! var, "Unable to find class " className);
   917    927   
   918    928   #define GET_METHOD_ID(var, clazz, methodName, fieldDescriptor) \

Changes to src/org/sqlite/app/customsqlite/CustomSqlite.java.

     3      3   
     4      4   import android.app.Activity;
     5      5   import android.os.Bundle;
     6      6   import android.util.Log;
     7      7   import android.view.View;
     8      8   import android.widget.TextView;
     9      9   
           10  +import java.io.File;
           11  +
           12  +import java.lang.InterruptedException;
           13  +
    10     14   import org.sqlite.database.sqlite.SQLiteDatabase;
    11     15   import org.sqlite.database.sqlite.SQLiteStatement;
    12     16   
    13     17   import android.database.Cursor;
    14     18   
    15     19   /*
    16     20   import android.database.sqlite.SQLiteDatabase;
................................................................................
    18     22   */
    19     23   
    20     24   public class CustomSqlite extends Activity
    21     25   {
    22     26     private TextView myTV;          /* Text view widget */
    23     27     private int myNTest;            /* Number of tests attempted */
    24     28     private int myNErr;             /* Number of tests failed */
           29  +
           30  +  File DB_PATH;
    25     31   
    26     32     /** Called when the activity is first created. */
    27     33     @Override
    28     34     public void onCreate(Bundle savedInstanceState){
    29     35       super.onCreate(savedInstanceState);
    30     36       setContentView(R.layout.main);
    31     37       myTV = (TextView)findViewById(R.id.tv_widget);
................................................................................
    56     62       } else {
    57     63         myNErr++;
    58     64         myTV.append("FAILED\n");
    59     65         myTV.append("   res=     \"" + res + "\"\n");
    60     66         myTV.append("   expected=\"" + expected + "\"\n");
    61     67       }
    62     68     }
           69  +
           70  +  /*
           71  +  ** Test that a database connection may be accessed from a second thread.
           72  +  */
           73  +  public void thread_test_1(){
           74  +    SQLiteDatabase.deleteDatabase(DB_PATH);
           75  +    final SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(DB_PATH, null);
           76  +
           77  +    String db_path2 = DB_PATH.toString() + "2";
           78  +
           79  +    db.execSQL("CREATE TABLE t1(x, y)");
           80  +    db.execSQL("INSERT INTO t1 VALUES (1, 2), (3, 4)");
           81  +
           82  +    Thread t = new Thread( new Runnable() {
           83  +      public void run() {
           84  +        SQLiteStatement st = db.compileStatement("SELECT sum(x+y) FROM t1");
           85  +        String res = st.simpleQueryForString();
           86  +        test_result("thread_test_1", res, "10");
           87  +      }
           88  +    });
           89  +
           90  +    t.start();
           91  +    try {
           92  +      t.join();
           93  +    } catch (InterruptedException e) {
           94  +    }
           95  +  }
    63     96   
    64     97     /*
    65     98     ** Use a Cursor to loop through the results of a SELECT query.
    66     99     */
    67    100     public void csr_test_1(){
    68         -    SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(":memory:", null);
          101  +    SQLiteDatabase.deleteDatabase(DB_PATH);
          102  +    SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(DB_PATH, null);
    69    103       String res = "";
    70    104   
    71    105       db.execSQL("CREATE TABLE t1(x)");
    72    106       db.execSQL("INSERT INTO t1 VALUES ('one'), ('two'), ('three')");
    73    107       
    74    108       Cursor c = db.rawQuery("SELECT x FROM t1", null);
    75    109       if( c!=null ){
................................................................................
    80    114         }
    81    115       }else{
    82    116         test_warning("csr_test_1", "c==NULL");
    83    117       }
    84    118   
    85    119       test_result("csr_test_1", res, ".one.two.three");
    86    120     }
          121  +
          122  +  /*
          123  +  ** Check that using openSeeDatabase() creates encrypted databases. 
          124  +  */
          125  +  public void see_test_1(){
          126  +    SQLiteDatabase.deleteDatabase(DB_PATH);
          127  +    String res = "";
          128  +    
          129  +    SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(DB_PATH, null);
          130  +    db.execSQL("PRAGMA key = 'secretkey'");
          131  +
          132  +    db.execSQL("CREATE TABLE t1(x)");
          133  +    db.execSQL("INSERT INTO t1 VALUES ('one'), ('two'), ('three')");
          134  +    
          135  +    Cursor c = db.rawQuery("SELECT x FROM t1", null);
          136  +    if( c!=null ){
          137  +      boolean bRes;
          138  +      for(bRes=c.moveToFirst(); bRes; bRes=c.moveToNext()){
          139  +        String x = c.getString(0);
          140  +        res = res + "." + x;
          141  +      }
          142  +    }else{
          143  +      test_warning("see_test_1", "c==NULL");
          144  +    }
          145  +
          146  +    test_result("see_test_1", res, ".one.two.three");
          147  +  }
    87    148   
    88    149     public void run_the_tests(View view){
    89    150       System.loadLibrary("sqliteX");
          151  +    DB_PATH = getApplicationContext().getDatabasePath("test.db");
          152  +    DB_PATH.mkdirs();
    90    153   
    91    154       myTV.setText("");
    92    155       myNErr = 0;
    93    156       myNTest = 0;
    94    157   
    95    158       try {
    96    159         report_version();
    97    160         csr_test_1();
          161  +      thread_test_1();
          162  +      see_test_1();
    98    163   
    99    164         myTV.append("\n" + myNErr + " errors from " + myNTest + " tests\n");
   100    165       } catch(Exception e) {
   101    166         myTV.append("Exception: " + e.toString() + "\n");
   102    167         myTV.append(android.util.Log.getStackTraceString(e) + "\n");
   103    168       }
   104    169     }
   105    170   }
   106    171   
   107    172   

Changes to src/org/sqlite/database/sqlite/SQLiteConnection.java.

   154    154       private static native long nativeExecuteForCursorWindow(
   155    155               int connectionPtr, int statementPtr, CursorWindow win,
   156    156               int startPos, int requiredPos, boolean countAllRows);
   157    157       private static native int nativeGetDbLookaside(int connectionPtr);
   158    158       private static native void nativeCancel(int connectionPtr);
   159    159       private static native void nativeResetCancel(int connectionPtr, boolean cancelable);
   160    160   
          161  +    private static native boolean nativeHasCodec();
          162  +    public static boolean hasCodec(){ return nativeHasCodec(); }
          163  +
   161    164       private SQLiteConnection(SQLiteConnectionPool pool,
   162    165               SQLiteDatabaseConfiguration configuration,
   163    166               int connectionId, boolean primaryConnection) {
   164    167           mPool = pool;
   165    168           mConfiguration = new SQLiteDatabaseConfiguration(configuration);
   166    169           mConnectionId = connectionId;
   167    170           mIsPrimaryConnection = primaryConnection;
................................................................................
   212    215                   SQLiteDebug.DEBUG_SQL_STATEMENTS, SQLiteDebug.DEBUG_SQL_TIME);
   213    216   
   214    217           setPageSize();
   215    218           setForeignKeyModeFromConfiguration();
   216    219           setWalModeFromConfiguration();
   217    220           setJournalSizeLimit();
   218    221           setAutoCheckpointInterval();
   219         -        setLocaleFromConfiguration();
          222  +	if( !nativeHasCodec() ){
          223  +          setLocaleFromConfiguration();
          224  +	}
   220    225   
   221    226           // Register custom functions.
   222    227           final int functionCount = mConfiguration.customFunctions.size();
   223    228           for (int i = 0; i < functionCount; i++) {
   224    229               SQLiteCustomFunction function = mConfiguration.customFunctions.get(i);
   225    230               nativeRegisterCustomFunction(mConnectionPtr, function);
   226    231           }
................................................................................
   391    396                   execute(success ? "COMMIT" : "ROLLBACK", null, null);
   392    397               }
   393    398           } catch (RuntimeException ex) {
   394    399               throw new SQLiteException("Failed to change locale for db '" + mConfiguration.label
   395    400                       + "' to '" + newLocale + "'.", ex);
   396    401           }
   397    402       }
          403  +
          404  +    public void enableLocalizedCollators(){
          405  +      if( nativeHasCodec() ){
          406  +	setLocaleFromConfiguration();
          407  +      }
          408  +    }
   398    409   
   399    410       // Called by SQLiteConnectionPool only.
   400    411       void reconfigure(SQLiteDatabaseConfiguration configuration) {
   401    412           mOnlyAllowReadOnlyOperations = false;
   402    413   
   403    414           // Register custom functions.
   404    415           final int functionCount = configuration.customFunctions.size();
................................................................................
   416    427                   & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0;
   417    428           boolean localeChanged = !configuration.locale.equals(mConfiguration.locale);
   418    429   
   419    430           // Update configuration parameters.
   420    431           mConfiguration.updateParametersFrom(configuration);
   421    432   
   422    433           // Update prepared statement cache size.
   423         -        mPreparedStatementCache.resize(configuration.maxSqlCacheSize);
          434  +        /* mPreparedStatementCache.resize(configuration.maxSqlCacheSize); */
   424    435   
   425    436           // Update foreign key mode.
   426    437           if (foreignKeyModeChanged) {
   427    438               setForeignKeyModeFromConfiguration();
   428    439           }
   429    440   
   430    441           // Update WAL.

Changes to src/org/sqlite/database/sqlite/SQLiteConnectionPool.java.

   987    987           waiter.mThread = null;
   988    988           waiter.mSql = null;
   989    989           waiter.mAssignedConnection = null;
   990    990           waiter.mException = null;
   991    991           waiter.mNonce += 1;
   992    992           mConnectionWaiterPool = waiter;
   993    993       }
          994  +
          995  +    public void enableLocalizedCollators() {
          996  +      synchronized (mLock) {
          997  +	if( !mAcquiredConnections.isEmpty() || mAvailablePrimaryConnection==null ) {
          998  +	  throw new IllegalStateException(
          999  +	      "Cannot enable localized collators while database is in use"
         1000  +	  );
         1001  +	}
         1002  +	mAvailablePrimaryConnection.enableLocalizedCollators();
         1003  +      }
         1004  +    }
   994   1005   
   995   1006       /**
   996   1007        * Dumps debugging information about this connection pool.
   997   1008        *
   998   1009        * @param printer The printer to receive the dump, not null.
   999   1010        * @param verbose True to dump more verbose information.
  1000   1011        */

Changes to src/org/sqlite/database/sqlite/SQLiteDatabase.java.

  2187   2187        * This can be used to create a function that can be called from
  2188   2188        * sqlite3 database triggers.
  2189   2189        * @hide
  2190   2190        */
  2191   2191       public interface CustomFunction {
  2192   2192           public void callback(String[] args);
  2193   2193       }
         2194  +
         2195  +    public static boolean hasCodec() {
         2196  +      return SQLiteConnection.hasCodec();
         2197  +    }
         2198  +
         2199  +    public void enableLocalizedCollators() {
         2200  +      mConnectionPoolLocked.enableLocalizedCollators();
         2201  +    }
  2194   2202   }