SQLite Android Bindings

Changes On Branch openseedatabase
Login

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

Changes In Branch openseedatabase Excluding Merge-Ins

This is equivalent to a diff from be6acc5363 to 6f17178a68

2013-12-24
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)
10:14
Add extra SEE tests. (Leaf check-in: 6f17178a68 user: dan tags: openseedatabase)
2013-12-23
18:23
Add support for SEE, SQLite's encryption extension. (check-in: 409082dd02 user: dan tags: openseedatabase)
07:33
Add extra required utility functions to ExtraUtils.java. (check-in: be6acc5363 user: dan tags: trunk)
2013-12-21
19:32
Update jni/README. (check-in: 1b80ccf163 user: dan tags: trunk)

Changes to jni/Android.mk.

1
2
3
4
5
6
7
8
9

10
11
12
13
14
15
16
17
18
19
20
21
22








23
24
25
26
27
28
29
30
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_CFLAGS += -DHAVE_CONFIG_H -DKHTML_NO_EXCEPTIONS -DGKWQ_NO_JAVA
LOCAL_CFLAGS += -DNO_SUPPORT_JS_BINDING -DQT_NO_WHEELEVENT -DKHTML_NO_XBL
LOCAL_CFLAGS += -U__APPLE__
LOCAL_CFLAGS += -Wno-unused-parameter -Wno-int-to-pointer-cast
LOCAL_CFLAGS += -Wno-maybe-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_SQLiteGlobal.cpp     \
	android_database_SQLiteDebug.cpp      \
	JNIHelp.cpp JniConstants.cpp          \








	sqlite3.c

LOCAL_C_INCLUDES += nativehelper/

LOCAL_MODULE:= libsqliteX
LOCAL_LDLIBS += -ldl -llog 

include $(BUILD_SHARED_LIBRARY)









>












|
>
>
>
>
>
>
>
>
|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)

LOCAL_CFLAGS += -DHAVE_CONFIG_H -DKHTML_NO_EXCEPTIONS -DGKWQ_NO_JAVA
LOCAL_CFLAGS += -DNO_SUPPORT_JS_BINDING -DQT_NO_WHEELEVENT -DKHTML_NO_XBL
LOCAL_CFLAGS += -U__APPLE__
LOCAL_CFLAGS += -Wno-unused-parameter -Wno-int-to-pointer-cast
LOCAL_CFLAGS += -Wno-maybe-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_SQLiteGlobal.cpp     \
	android_database_SQLiteDebug.cpp      \
	JNIHelp.cpp JniConstants.cpp

#
# For a SEE build, add the SEE sources to the tree and replace the line 
# below with:
#
#  LOCAL_SRC_FILES += sqlite3-see.c
#  LOCAL_CFLAGS    += -DSQLITE_HAS_CODEC
#
LOCAL_SRC_FILES += sqlite3.c

LOCAL_C_INCLUDES += nativehelper/

LOCAL_MODULE:= libsqliteX
LOCAL_LDLIBS += -ldl -llog 

include $(BUILD_SHARED_LIBRARY)

Changes to jni/android_database_SQLiteConnection.cpp.

849
850
851
852
853
854
855
























856
857
858
859
860
861
862
        sqlite3_progress_handler(connection->db, 4, sqliteProgressHandlerCallback,
                connection);
    } else {
        sqlite3_progress_handler(connection->db, 0, NULL, NULL);
    }
}


























static JNINativeMethod sMethods[] =
{
    /* name, signature, funcPtr */
    { "nativeOpen", "(Ljava/lang/String;ILjava/lang/String;ZZ)I",
            (void*)nativeOpen },
    { "nativeClose", "(I)V",







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
        sqlite3_progress_handler(connection->db, 4, sqliteProgressHandlerCallback,
                connection);
    } else {
        sqlite3_progress_handler(connection->db, 0, NULL, NULL);
    }
}

static void nativeSetSeeKey(JNIEnv *pEnv, jobject clazz, jint connectionPtr, jstring zKey){
  SQLiteConnection* p = reinterpret_cast<SQLiteConnection*>(connectionPtr);
  sqlite3_stmt *pStmt = 0;
  int rc;

  const char *zUtf8 = pEnv->GetStringUTFChars(zKey, 0);
  char *zSql = sqlite3_mprintf("PRAGMA key = '%q'", zUtf8);

  ALOG(LOG_ERROR, SQLITE_TRACE_TAG, "nativeSetSeeKey: %s", zSql);

  rc = sqlite3_prepare_v2(p->db, zSql, -1, &pStmt, 0);
  if( rc==SQLITE_OK ){
    sqlite3_step(pStmt);
    rc = sqlite3_finalize(pStmt);
  }
  
  pEnv->ReleaseStringUTFChars(zKey, zUtf8);
  sqlite3_free(zSql);

  if( rc!=SQLITE_OK ){
    char *zErr = sqlite3_mprintf("nativeSetSeeKey %d \"%s\"", rc, sqlite3_errmsg(p->db));
    throw_sqlite3_exception(pEnv, p->db, zErr);
  }
}

static JNINativeMethod sMethods[] =
{
    /* name, signature, funcPtr */
    { "nativeOpen", "(Ljava/lang/String;ILjava/lang/String;ZZ)I",
            (void*)nativeOpen },
    { "nativeClose", "(I)V",
905
906
907
908
909
910
911


912
913
914
915
916
917
918
            (void*)nativeExecuteForCursorWindow },
    { "nativeGetDbLookaside", "(I)I",
            (void*)nativeGetDbLookaside },
    { "nativeCancel", "(I)V",
            (void*)nativeCancel },
    { "nativeResetCancel", "(IZ)V",
            (void*)nativeResetCancel },


};

#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) \







>
>







929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
            (void*)nativeExecuteForCursorWindow },
    { "nativeGetDbLookaside", "(I)I",
            (void*)nativeGetDbLookaside },
    { "nativeCancel", "(I)V",
            (void*)nativeCancel },
    { "nativeResetCancel", "(IZ)V",
            (void*)nativeResetCancel },

    { "nativeSetSeeKey", "(ILjava/lang/String;)V", (void*)nativeSetSeeKey },
};

#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) \

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

1
2
3
4
5
6
7
8
9






10
11

12
13
14
15
16
17
18








19
20
21
22
23
24


25
26
27
28
29
30
31

package org.sqlite.app.customsqlite;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;







import org.sqlite.database.sqlite.SQLiteDatabase;
import org.sqlite.database.sqlite.SQLiteStatement;


import android.database.Cursor;

/*
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;
*/









public class CustomSqlite extends Activity
{
  private TextView myTV;          /* Text view widget */
  private int myNTest;            /* Number of tests attempted */
  private int myNErr;             /* Number of tests failed */



  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    myTV = (TextView)findViewById(R.id.tv_widget);









>
>
>
>
>
>


>







>
>
>
>
>
>
>
>






>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

package org.sqlite.app.customsqlite;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

import java.io.File;
import java.io.FileInputStream;
import java.util.Arrays;

import java.lang.InterruptedException;

import org.sqlite.database.sqlite.SQLiteDatabase;
import org.sqlite.database.sqlite.SQLiteStatement;
import org.sqlite.database.sqlite.SQLiteDatabaseCorruptException;

import android.database.Cursor;

/*
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;
*/

import org.sqlite.database.DatabaseErrorHandler;
class DoNotDeleteErrorHandler implements DatabaseErrorHandler {
  private static final String TAG = "DoNotDeleteErrorHandler";
  public void onCorruption(SQLiteDatabase dbObj) {
    Log.e(TAG, "Corruption reported by sqlite on database: " + dbObj.getPath());
  }
}

public class CustomSqlite extends Activity
{
  private TextView myTV;          /* Text view widget */
  private int myNTest;            /* Number of tests attempted */
  private int myNErr;             /* Number of tests failed */

  File DB_PATH;

  /** Called when the activity is first created. */
  @Override
  public void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);
    setContentView(R.layout.main);
    myTV = (TextView)findViewById(R.id.tv_widget);
56
57
58
59
60
61
62
63
64
















































65
66
67

68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83

84































85






















86
87
88
89


90
91
92
93
94
95
96
97


98
99
100
101
102
103
104
105
106
107
    } else {
      myNErr++;
      myTV.append("FAILED\n");
      myTV.append("   res=     \"" + res + "\"\n");
      myTV.append("   expected=\"" + expected + "\"\n");
    }
  }

  /*
















































  ** Use a Cursor to loop through the results of a SELECT query.
  */
  public void csr_test_1(){

    SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(":memory:", null);
    String res = "";

    db.execSQL("CREATE TABLE t1(x)");
    db.execSQL("INSERT INTO t1 VALUES ('one'), ('two'), ('three')");
    
    Cursor c = db.rawQuery("SELECT x FROM t1", null);
    if( c!=null ){
      boolean bRes;
      for(bRes=c.moveToFirst(); bRes; bRes=c.moveToNext()){
        String x = c.getString(0);
        res = res + "." + x;
      }
    }else{
      test_warning("csr_test_1", "c==NULL");
    }

































    test_result("csr_test_1", res, ".one.two.three");






















  }

  public void run_the_tests(View view){
    System.loadLibrary("sqliteX");



    myTV.setText("");
    myNErr = 0;
    myNTest = 0;

    try {
      report_version();
      csr_test_1();



      myTV.append("\n" + myNErr + " errors from " + myNTest + " tests\n");
    } catch(Exception e) {
      myTV.append("Exception: " + e.toString() + "\n");
      myTV.append(android.util.Log.getStackTraceString(e) + "\n");
    }
  }
}











>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


|
>
|















>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>




>
>








>
>










73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
    } else {
      myNErr++;
      myTV.append("FAILED\n");
      myTV.append("   res=     \"" + res + "\"\n");
      myTV.append("   expected=\"" + expected + "\"\n");
    }
  }

  /*
  ** Test if the database at DB_PATH is encrypted or not. The db
  ** is assumed to be encrypted if the first 6 bytes are anything
  ** other than "SQLite".
  **
  ** If the test reveals that the db is encrypted, return the string
  ** "encrypted". Otherwise, "unencrypted".
  */
  public String db_is_encrypted() throws Exception {
    FileInputStream in = new FileInputStream(DB_PATH);

    byte[] buffer = new byte[6];
    in.read(buffer, 0, 6);

    String res = "encrypted";
    if( Arrays.equals(buffer, (new String("SQLite")).getBytes()) ){
      res = "unencrypted";
    }
    return res;
  }

  /*
  ** Test that a database connection may be accessed from a second thread.
  */
  public void thread_test_1(){
    SQLiteDatabase.deleteDatabase(DB_PATH);
    final SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(DB_PATH, null);

    String db_path2 = DB_PATH.toString() + "2";

    db.execSQL("CREATE TABLE t1(x, y)");
    db.execSQL("INSERT INTO t1 VALUES (1, 2), (3, 4)");

    Thread t = new Thread( new Runnable() {
      public void run() {
        SQLiteStatement st = db.compileStatement("SELECT sum(x+y) FROM t1");
        String res = st.simpleQueryForString();
        test_result("thread_test_1", res, "10");
      }
    });

    t.start();
    try {
      t.join();
    } catch (InterruptedException e) {
    }
  }

  /*
  ** Use a Cursor to loop through the results of a SELECT query.
  */
  public void csr_test_1() throws Exception {
    SQLiteDatabase.deleteDatabase(DB_PATH);
    SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(DB_PATH, null);
    String res = "";

    db.execSQL("CREATE TABLE t1(x)");
    db.execSQL("INSERT INTO t1 VALUES ('one'), ('two'), ('three')");
    
    Cursor c = db.rawQuery("SELECT x FROM t1", null);
    if( c!=null ){
      boolean bRes;
      for(bRes=c.moveToFirst(); bRes; bRes=c.moveToNext()){
        String x = c.getString(0);
        res = res + "." + x;
      }
    }else{
      test_warning("csr_test_1", "c==NULL");
    }
    test_result("csr_test_1.1", res, ".one.two.three");

    db.close();
    test_result("csr_test_1.2", db_is_encrypted(), "unencrypted");
  }

  public String string_from_t1_x(SQLiteDatabase db){
    String res = "";

    Cursor c = db.rawQuery("SELECT x FROM t1", null);
    boolean bRes;
    for(bRes=c.moveToFirst(); bRes; bRes=c.moveToNext()){
      String x = c.getString(0);
      res = res + "." + x;
    }

    return res;
  }

  /*
  ** Check that using openSeeDatabase() creates encrypted databases. 
  */
  public void see_test_1() throws Exception {
    SQLiteDatabase.deleteDatabase(DB_PATH);
    String res = "";
    SQLiteDatabase db = SQLiteDatabase.openSeeDatabase(
        DB_PATH.getPath(), "secretkey", null, SQLiteDatabase.CREATE_IF_NECESSARY, null
    );

    db.execSQL("CREATE TABLE t1(x)");
    db.execSQL("INSERT INTO t1 VALUES ('one'), ('two'), ('three')");
    
    res = string_from_t1_x(db);
    test_result("see_test_1.1", res, ".one.two.three");
    db.close();

    test_result("see_test_1.2", db_is_encrypted(), "encrypted");

    db = SQLiteDatabase.openSeeDatabase(
        DB_PATH.getPath(), "secretkey", null, SQLiteDatabase.CREATE_IF_NECESSARY, null
    );
    res = string_from_t1_x(db);
    test_result("see_test_1.3", res, ".one.two.three");
    db.close();

    res = "unencrypted";
    try {
      db = SQLiteDatabase.openDatabase(
        DB_PATH.getPath(), null, SQLiteDatabase.CREATE_IF_NECESSARY, new DoNotDeleteErrorHandler()
      );
      db.close();
    } catch ( SQLiteDatabaseCorruptException e ){
      res = "encrypted";
    }
    test_result("see_test_1.4", res, "encrypted");

  }

  public void run_the_tests(View view){
    System.loadLibrary("sqliteX");
    DB_PATH = getApplicationContext().getDatabasePath("test.db");
    DB_PATH.mkdirs();

    myTV.setText("");
    myNErr = 0;
    myNTest = 0;

    try {
      report_version();
      csr_test_1();
      thread_test_1();
      see_test_1();

      myTV.append("\n" + myNErr + " errors from " + myNTest + " tests\n");
    } catch(Exception e) {
      myTV.append("Exception: " + e.toString() + "\n");
      myTV.append(android.util.Log.getStackTraceString(e) + "\n");
    }
  }
}


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

153
154
155
156
157
158
159

160
161
162
163
164
165
166
            int connectionPtr, int statementPtr);
    private static native long nativeExecuteForCursorWindow(
            int connectionPtr, int statementPtr, CursorWindow win,
            int startPos, int requiredPos, boolean countAllRows);
    private static native int nativeGetDbLookaside(int connectionPtr);
    private static native void nativeCancel(int connectionPtr);
    private static native void nativeResetCancel(int connectionPtr, boolean cancelable);


    private SQLiteConnection(SQLiteConnectionPool pool,
            SQLiteDatabaseConfiguration configuration,
            int connectionId, boolean primaryConnection) {
        mPool = pool;
        mConfiguration = new SQLiteDatabaseConfiguration(configuration);
        mConnectionId = connectionId;







>







153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
            int connectionPtr, int statementPtr);
    private static native long nativeExecuteForCursorWindow(
            int connectionPtr, int statementPtr, CursorWindow win,
            int startPos, int requiredPos, boolean countAllRows);
    private static native int nativeGetDbLookaside(int connectionPtr);
    private static native void nativeCancel(int connectionPtr);
    private static native void nativeResetCancel(int connectionPtr, boolean cancelable);
    private static native void nativeSetSeeKey(int connectionPtr, String key);

    private SQLiteConnection(SQLiteConnectionPool pool,
            SQLiteDatabaseConfiguration configuration,
            int connectionId, boolean primaryConnection) {
        mPool = pool;
        mConfiguration = new SQLiteDatabaseConfiguration(configuration);
        mConnectionId = connectionId;
206
207
208
209
210
211
212





213
214
215
216
217
218
219
        dispose(false);
    }

    private void open() {
        mConnectionPtr = nativeOpen(mConfiguration.path, mConfiguration.openFlags,
                mConfiguration.label,
                SQLiteDebug.DEBUG_SQL_STATEMENTS, SQLiteDebug.DEBUG_SQL_TIME);






        setPageSize();
        setForeignKeyModeFromConfiguration();
        setWalModeFromConfiguration();
        setJournalSizeLimit();
        setAutoCheckpointInterval();
        setLocaleFromConfiguration();







>
>
>
>
>







207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
        dispose(false);
    }

    private void open() {
        mConnectionPtr = nativeOpen(mConfiguration.path, mConfiguration.openFlags,
                mConfiguration.label,
                SQLiteDebug.DEBUG_SQL_STATEMENTS, SQLiteDebug.DEBUG_SQL_TIME);

        if( mConfiguration.bSee ){
          nativeSetSeeKey(mConnectionPtr, mConfiguration.seekey);
          mConfiguration.setSeeKey(null);
        }

        setPageSize();
        setForeignKeyModeFromConfiguration();
        setWalModeFromConfiguration();
        setJournalSizeLimit();
        setAutoCheckpointInterval();
        setLocaleFromConfiguration();
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
                & 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.







|







422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
                & 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.

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

180
181
182
183
184
185
186


187
188
189
190
191
192
193

    // 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");
    }

    /**







>
>







180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195

    // Might throw
    private void open() {
        // Open the primary connection.
        // This might throw if the database is corrupt.
        mAvailablePrimaryConnection = openConnectionLocked(mConfiguration,
                true /*primaryConnection*/); // might throw

        mConfiguration.setSeeKey(null);

        // Mark the pool as being open for business.
        mIsOpen = true;
        mCloseGuard.open("close");
    }

    /**

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

691
692
693
694
695
696
697














698
699
700
701
702
703
704
     */
    public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags,
            DatabaseErrorHandler errorHandler) {
        SQLiteDatabase db = new SQLiteDatabase(path, flags, 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);
    }







>
>
>
>
>
>
>
>
>
>
>
>
>
>







691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
     */
    public static SQLiteDatabase openDatabase(String path, CursorFactory factory, int flags,
            DatabaseErrorHandler errorHandler) {
        SQLiteDatabase db = new SQLiteDatabase(path, flags, factory, errorHandler);
        db.open();
        return db;
    }

    public static SQLiteDatabase openSeeDatabase(
      String path, 
      String key, 
      CursorFactory factory, 
      int flags, 
      DatabaseErrorHandler errorHandler
    ){
      SQLiteDatabase db = new SQLiteDatabase(path, flags, factory, errorHandler);
      db.mConfigurationLocked.setSeeKey(key);
      db.open();
      db.mConfigurationLocked.setSeeKey(null);
      return db;
    }

    /**
     * Equivalent to openDatabase(file.getPath(), factory, CREATE_IF_NECESSARY).
     */
    public static SQLiteDatabase openOrCreateDatabase(File file, CursorFactory factory) {
        return openOrCreateDatabase(file.getPath(), factory);
    }
713
714
715
716
717
718
719

720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738


739
740
741
742
743
744
745
746
747
748
749
750
751

752
753
754
755
756
757
758
    /**
     * 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 = false;
        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.
     *







>



















>
>













>







727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
    /**
     * 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 = false;
        deleted |= file.delete();
        deleted |= new File(file.getPath() + "-journal").delete();
        deleted |= new File(file.getPath() + "-shm").delete();
        deleted |= new File(file.getPath() + "-wal").delete();

        /* TODO: This is throwing a NullPointerException. Do not know why. */
        /*
        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.
     *

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

85
86
87
88
89
90
91















92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110




111
112
113
114
115
116
117
118
119
120
121
122
123
124
125



126
127
128
129
130
131
132

    /**
     * The custom functions to register.
     */
    public final ArrayList<SQLiteCustomFunction> customFunctions =
            new ArrayList<SQLiteCustomFunction>();
















    /**
     * 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, 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 as a copy of another configuration.
     *
     * @param other The other configuration.
     */
    public 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.







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>



















>
>
>
>















>
>
>







85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154

    /**
     * The custom functions to register.
     */
    public final ArrayList<SQLiteCustomFunction> customFunctions =
            new ArrayList<SQLiteCustomFunction>();


    /*
    ** The following two variables are used by the SEE extension.
    */
    public boolean bSee;
    public String seekey;

    /*
    ** Function to set SEE related variables.
    */
    public void setSeeKey(String key){
      bSee = true;
      seekey = key;
    }

    /**
     * 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, 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();

        // Set default values for SEE parameters (default is unencrypted).
        bSee = false;
        seekey = null;
    }

    /**
     * Creates a database configuration as a copy of another configuration.
     *
     * @param other The other configuration.
     */
    public SQLiteDatabaseConfiguration(SQLiteDatabaseConfiguration other) {
        if (other == null) {
            throw new IllegalArgumentException("other must not be null.");
        }

        this.path = other.path;
        this.label = other.label;
        updateParametersFrom(other);

        this.bSee = other.bSee;
        this.seekey = other.seekey;
    }

    /**
     * Updates the non-immutable parameters of this configuration object
     * from the other configuration object.
     *
     * @param other The object from which to copy the parameters.