SQLite Android Bindings

Check-in [e8a9b149f7]
Login

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

Overview
Comment:Restore standard behaviours of (a) activating a connection pool in wal mode and (b) switching into wal mode automatically if the flag is set even if SQLITE_HAS_CODEC is defined (they were previously disabled in this case). Strip any URI parameters from the database name before it is included in any log messages. Always build with SQLITE_USE_URI=1 defined.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: e8a9b149f74f517b80ad0b2fc723a7cb7ccf8d2b
User & Date: dan 2017-05-03 18:18:33
References
2018-09-24
19:06
Partly revert [e8a9b149f7] so that for SEE-enabled builds, the journal mode of the database is not changed to match the configuration flags as soon as it is opened. (check-in: 8a027aa451 user: dan tags: trunk)
Context
2017-05-03
19:59
Update see.wiki to advise use of a URI parameter instead of "PRAGMA key = ?". (check-in: 7a62c59e53 user: dan tags: trunk)
18:18
Restore standard behaviours of (a) activating a connection pool in wal mode and (b) switching into wal mode automatically if the flag is set even if SQLITE_HAS_CODEC is defined (they were previously disabled in this case). Strip any URI parameters from the database name before it is included in any log messages. Always build with SQLITE_USE_URI=1 defined. (check-in: e8a9b149f7 user: dan tags: trunk)
2017-05-02
19:54
Add a new test that uses AndroidJUnit4. And related gradle changes. (check-in: 40f79eca30 user: dan tags: trunk)
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to sqlite3/src/androidTest/java/org/sqlite/database/SeeTest1.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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53





















54
55
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
package org.sqlite.database;


import android.content.Context;
import android.database.Cursor;
import android.os.AsyncTask;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;


import junit.framework.Assert;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;


import org.sqlite.database.sqlite.SQLiteDatabase;
import org.sqlite.database.sqlite.SQLiteOpenHelper;

import java.io.File;



import static org.junit.Assert.*;


class MyHelper extends SQLiteOpenHelper {

    public static final String DATABASE_NAME = "mydb.db";

    public MyHelper(Context ctx){
        super(ctx, ctx.getDatabasePath(DATABASE_NAME).getAbsolutePath(), null, 1);
    }
    public void onConfigure(SQLiteDatabase db){
        db.execSQL("PRAGMA key = 'secret'");

        db.enableWriteAheadLogging();

        final Cursor pragmaCursor = db.rawQuery("PRAGMA journal_mode = WAL", null);
        pragmaCursor.moveToFirst();
        pragmaCursor.close();
    }
    public void onCreate(SQLiteDatabase db){
        db.execSQL("CREATE TABLE t1(x)");
    }
    public void onUpgrade(SQLiteDatabase db, int iOld, int iNew){
    }
}


/**
 * Created by dan on 5/3/17.
 */
@RunWith(AndroidJUnit4.class)
public class SeeTest1 {
        private Context mContext;






















    @Before
    public void setup() throws Exception {

        System.loadLibrary("sqliteX");

        mContext = InstrumentationRegistry.getTargetContext();

        // delete any existing database
        File databaseFile = mContext.getDatabasePath(MyHelper.DATABASE_NAME);
        databaseFile.mkdirs();
        if (databaseFile.exists()) {
            databaseFile.delete();
        }
    }

    @Test
    public void testAndroidDefaultWalMode() throws Exception {
        // create database
        final MyHelper helper = new MyHelper(mContext);
        helper.getWritableDatabase();

        // verify that WAL journal mode is set
        final Cursor pragmaCursor = helper.getWritableDatabase().rawQuery("PRAGMA journal_mode", null);
        pragmaCursor.moveToFirst();
        Assert.assertEquals(pragmaCursor.getString(pragmaCursor.getColumnIndex("journal_mode")), "wal");
        pragmaCursor.close();

        // start long running transaction
        AsyncTask.execute(new Runnable() {
            @Override
            public void run() {
                helper.getWritableDatabase().beginTransactionNonExclusive();


<





>







>




>
>









|


<
<

<
<
<
<














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

















|







|







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
49
50
51
52
53
54
55
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
package org.sqlite.database;


import android.content.Context;
import android.database.Cursor;
import android.os.AsyncTask;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.util.Log;

import junit.framework.Assert;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.sqlite.database.sqlite.SQLiteConnection;
import org.sqlite.database.sqlite.SQLiteDatabase;
import org.sqlite.database.sqlite.SQLiteOpenHelper;

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

import static org.junit.Assert.*;


class MyHelper extends SQLiteOpenHelper {

    public static final String DATABASE_NAME = "mydb.db";

    public MyHelper(Context ctx){
        super(ctx, "file:" + ctx.getDatabasePath(DATABASE_NAME).getAbsolutePath() + "?key=secret", null, 1);
    }
    public void onConfigure(SQLiteDatabase db){


        db.enableWriteAheadLogging();




    }
    public void onCreate(SQLiteDatabase db){
        db.execSQL("CREATE TABLE t1(x)");
    }
    public void onUpgrade(SQLiteDatabase db, int iOld, int iNew){
    }
}


/**
 * Created by dan on 5/3/17.
 */
@RunWith(AndroidJUnit4.class)
public class SeeTest1 {
    private Context mContext;

    /*
    ** Test if the database at 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(String path) throws Exception {
      FileInputStream in = new FileInputStream(mContext.getDatabasePath(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;
    }

    @Before
    public void setup() throws Exception {

        System.loadLibrary("sqliteX");

        mContext = InstrumentationRegistry.getTargetContext();

        // delete any existing database
        File databaseFile = mContext.getDatabasePath(MyHelper.DATABASE_NAME);
        databaseFile.mkdirs();
        if (databaseFile.exists()) {
            databaseFile.delete();
        }
    }

    @Test
    public void testEncryptedWalMode() throws Exception {
        // create database
        final MyHelper helper = new MyHelper(mContext);
        helper.getWritableDatabase();

        // verify that WAL journal mode is set
        final Cursor pragmaCursor = helper.getWritableDatabase().rawQuery("PRAGMA journal_mode", null);
        pragmaCursor.moveToFirst();
        Assert.assertEquals("wal", pragmaCursor.getString(pragmaCursor.getColumnIndex("journal_mode")));
        pragmaCursor.close();

        // start long running transaction
        AsyncTask.execute(new Runnable() {
            @Override
            public void run() {
                helper.getWritableDatabase().beginTransactionNonExclusive();
105
106
107
108
109
110
111
112




113


        //try to read something from the database while the slow transaction is running
        helper.getWritableDatabase().execSQL("SELECT * FROM t1");

        //verify that the operation didn't wait until the 3000ms long operation finished
        if (System.currentTimeMillis() - startTime > 3000) {
            throw new Exception("WAL mode isn't working corectly - read operation was blocked");
        }
    }




}









|
>
>
>
>
|
>
>
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
        //try to read something from the database while the slow transaction is running
        helper.getWritableDatabase().execSQL("SELECT * FROM t1");

        //verify that the operation didn't wait until the 3000ms long operation finished
        if (System.currentTimeMillis() - startTime > 3000) {
            throw new Exception("WAL mode isn't working corectly - read operation was blocked");
        }

        if( SQLiteConnection.hasCodec() ){
          Assert.assertEquals("encrypted", db_is_encrypted(MyHelper.DATABASE_NAME));
        } else {
          Assert.assertEquals("unencrypted", db_is_encrypted(MyHelper.DATABASE_NAME));
        }
    }
}

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

218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
                mConfiguration.label,
                SQLiteDebug.DEBUG_SQL_STATEMENTS, SQLiteDebug.DEBUG_SQL_TIME);

        setPageSize();
        setForeignKeyModeFromConfiguration();
        setJournalSizeLimit();
	setAutoCheckpointInterval();
	if( !nativeHasCodec() ){
	  setWalModeFromConfiguration();
          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);
        }







<
|
|
<







218
219
220
221
222
223
224

225
226

227
228
229
230
231
232
233
                mConfiguration.label,
                SQLiteDebug.DEBUG_SQL_STATEMENTS, SQLiteDebug.DEBUG_SQL_TIME);

        setPageSize();
        setForeignKeyModeFromConfiguration();
        setJournalSizeLimit();
	setAutoCheckpointInterval();

        setWalModeFromConfiguration();
        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);
        }

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

946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
    }

    private static int getPriority(int connectionFlags) {
        return (connectionFlags & CONNECTION_FLAG_INTERACTIVE) != 0 ? 1 : 0;
    }

    private void setMaxConnectionPoolSizeLocked() {
        if( !SQLiteDatabase.hasCodec()
	 && (mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0
	) {
            mMaxConnectionPoolSize = SQLiteGlobal.getWALConnectionPoolSize();
        } else {
            // TODO: We don't actually need to restrict the connection pool size to 1
            // for non-WAL databases.  There might be reasons to use connection pooling
            // with other journal modes.  For now, enabling connection pooling and
            // using WAL are the same thing in the API.
            mMaxConnectionPoolSize = 1;







<
|
<







946
947
948
949
950
951
952

953

954
955
956
957
958
959
960
    }

    private static int getPriority(int connectionFlags) {
        return (connectionFlags & CONNECTION_FLAG_INTERACTIVE) != 0 ? 1 : 0;
    }

    private void setMaxConnectionPoolSizeLocked() {

        if ((mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0) {

            mMaxConnectionPoolSize = SQLiteGlobal.getWALConnectionPoolSize();
        } else {
            // TODO: We don't actually need to restrict the connection pool size to 1
            // for non-WAL databases.  There might be reasons to use connection pooling
            // with other journal modes.  For now, enabling connection pooling and
            // using WAL are the same thing in the API.
            mMaxConnectionPoolSize = 1;

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

157
158
159
160
161
162
163






164
165
166
167
168
169
     * @return True if the database is in-memory.
     */
    public boolean isInMemoryDb() {
        return path.equalsIgnoreCase(MEMORY_DB_PATH);
    }

    private static String stripPathForLogs(String path) {






        if (path.indexOf('@') == -1) {
            return path;
        }
        return EMAIL_IN_DB_PATTERN.matcher(path).replaceAll("XX@YY");
    }
}







>
>
>
>
>
>






157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
     * @return True if the database is in-memory.
     */
    public boolean isInMemoryDb() {
        return path.equalsIgnoreCase(MEMORY_DB_PATH);
    }

    private static String stripPathForLogs(String path) {
        /* Strip off all URI parameters. */
        int iIdx = path.indexOf('?');
        if( iIdx>=0 ){
            path = (String) path.subSequence(0, iIdx);
        }

        if (path.indexOf('@') == -1) {
            return path;
        }
        return EMAIL_IN_DB_PATTERN.matcher(path).replaceAll("XX@YY");
    }
}

Changes to sqlite3/src/main/jni/sqlite/Android.mk.

20
21
22
23
24
25
26

27
28
29
30
31
32
33
34
35
#
LOCAL_CFLAGS += -DSQLITE_TEMP_STORE=3

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 += -DHAVE_STRCHRNUL=0

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=""







>

|







20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#
LOCAL_CFLAGS += -DSQLITE_TEMP_STORE=3

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 += -DHAVE_STRCHRNUL=0
LOCAL_CFLAGS += -DSQLITE_USE_URI=1
LOCAL_CFLAGS += -Wno-unused-parameter -Wno-int-to-pointer-cast
LOCAL_CFLAGS += -Wno-uninitialized -Wno-parentheses
LOCAL_CPPFLAGS += -Wno-conversion-null


ifeq ($(TARGET_ARCH), arm)
	LOCAL_CFLAGS += -DPACKED="__attribute__ ((packed))"
else
	LOCAL_CFLAGS += -DPACKED=""