SQLite Android Bindings

Check-in Differences
Login

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

Difference From 40f79eca30e59f72 To e8a9b149f74f517b

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)
2017-05-01
15:14
Define HAVE_USLEEP to avoid 1 second delays when sleep() is called (check-in: efde9e0e44 user: pjw tags: trunk)

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
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, ctx.getDatabasePath(DATABASE_NAME).getAbsolutePath(), null, 1);
        super(ctx, "file:" + ctx.getDatabasePath(DATABASE_NAME).getAbsolutePath() + "?key=secret", 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;
    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 testAndroidDefaultWalMode() throws Exception {
    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(pragmaCursor.getString(pragmaCursor.getColumnIndex("journal_mode")), "wal");
        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








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
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();
	if( !nativeHasCodec() ){
	  setWalModeFromConfiguration();
          setLocaleFromConfiguration();
        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
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( !SQLiteDatabase.hasCodec()
	 && (mConfiguration.openFlags & SQLiteDatabase.ENABLE_WRITE_AHEAD_LOGGING) != 0
        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
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
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-maybe-uninitialized -Wno-parentheses
LOCAL_CFLAGS += -Wno-uninitialized -Wno-parentheses
LOCAL_CPPFLAGS += -Wno-conversion-null


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