droid
This commit is contained in:
parent
29af01c288
commit
e3ac349a0d
@ -68,7 +68,7 @@ open_asset(VFS_t *me, const char *fname, const char *io_mode, int sno) {
|
|||||||
__android_log_print(ANDROID_LOG_INFO, "YAPDroid", "open %s-%s <%s>", fname, me->prefix,io_mode);
|
__android_log_print(ANDROID_LOG_INFO, "YAPDroid", "open %s-%s <%s>", fname, me->prefix,io_mode);
|
||||||
if (strchr(io_mode, 'B')) {
|
if (strchr(io_mode, 'B')) {
|
||||||
mode = AASSET_MODE_BUFFER;
|
mode = AASSET_MODE_BUFFER;
|
||||||
} else {[lib]
|
} else {
|
||||||
mode = AASSET_MODE_UNKNOWN;
|
mode = AASSET_MODE_UNKNOWN;
|
||||||
}
|
}
|
||||||
GLOBAL_Stream[sno].name = Yap_LookupAtom(fname);
|
GLOBAL_Stream[sno].name = Yap_LookupAtom(fname);
|
||||||
|
@ -182,7 +182,7 @@ DBMS(show_databases)(Connection,database(Databases)) :-
|
|||||||
DBMS(result_set)(Mode),
|
DBMS(result_set)(Mode),
|
||||||
'$write_or_not'('SHOW DATABASES'),
|
'$write_or_not'('SHOW DATABASES'),
|
||||||
c_DBMS(query)('SHOW DATABASES',ResultSet,Conn,Mode,_),
|
c_DBMS(query)('SHOW DATABASES',ResultSet,Conn,Mode,_),
|
||||||
!,c_DBMS(row)(ResultSet,1,[Databases]).
|
!,DBMS(row)(ResultSet,1,[Databases]).
|
||||||
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||||
|
|
||||||
|
|
||||||
|
@ -174,6 +174,7 @@
|
|||||||
nonvar(Module).
|
nonvar(Module).
|
||||||
% Prevent the error of given an atom that has no value
|
% Prevent the error of given an atom that has no value
|
||||||
'$error_checks'(get_value(Connection,Con)) :- !,
|
'$error_checks'(get_value(Connection,Con)) :- !,
|
||||||
|
writeln(user_error,get_value(Connection,Con)),
|
||||||
% This also prevents the case of giving the number of the connection
|
% This also prevents the case of giving the number of the connection
|
||||||
% as an argument
|
% as an argument
|
||||||
atom(Connection),
|
atom(Connection),
|
||||||
@ -181,11 +182,3 @@
|
|||||||
get_value(Connection,Value),
|
get_value(Connection,Value),
|
||||||
Value \== [].
|
Value \== [].
|
||||||
|
|
||||||
% Prevent the error of given an atom that has no value
|
|
||||||
'$error_checks'(get_value(Conn,Connection)) :- !,
|
|
||||||
% This also prevents the case of giving the number of the connection
|
|
||||||
% as an argument
|
|
||||||
atom(Conn),
|
|
||||||
var(Connection),
|
|
||||||
get_value(Conn,Value),
|
|
||||||
Value \== [].
|
|
||||||
|
@ -393,14 +393,14 @@
|
|||||||
% Only for making the error tests in all of the calls to
|
% Only for making the error tests in all of the calls to
|
||||||
% get_value/2
|
% get_value/2
|
||||||
'$get_value'(Connection,Con) :-
|
'$get_value'(Connection,Con) :-
|
||||||
'$error_checks'(get_value(Connection,Con)),
|
%'$error_checks'(get_value(Connection,Con)),
|
||||||
get_value(Connection,Con).
|
get_value(Connection,Con).
|
||||||
|
|
||||||
|
|
||||||
'$check_fields'([],[]).
|
'$check_fields'([],[]).
|
||||||
'$check_fields'(['$const$'(_)|TAtt],[_|TFields]):-
|
'$check_fields'(['$const$'(_)|TAtt],[_|TFields]):-
|
||||||
'$check_fields'(TAtt,TFields).
|
'$check_fields'(TAtt,TFields).
|
||||||
% um campo auto_incrementavel, é sempre parte da chave, e como é auto
|
% um campo auto_incrementavel, <EFBFBD> sempre parte da chave, e como <20> auto
|
||||||
% pode-se dar valores NULOS
|
% pode-se dar valores NULOS
|
||||||
'$check_fields'([att(_,Name)|TAtt],[property(Name,_,1,1)|TFields]):-!,
|
'$check_fields'([att(_,Name)|TAtt],[property(Name,_,1,1)|TFields]):-!,
|
||||||
'$check_fields'(TAtt,TFields).
|
'$check_fields'(TAtt,TFields).
|
||||||
|
@ -77,7 +77,6 @@ static Int c_sqlite3_get_database(USES_REGS1);
|
|||||||
static Int c_sqlite3_change_database(USES_REGS1);
|
static Int c_sqlite3_change_database(USES_REGS1);
|
||||||
|
|
||||||
static Int c_sqlite3_connect(USES_REGS1) {
|
static Int c_sqlite3_connect(USES_REGS1) {
|
||||||
printf("hello darkness\n");
|
|
||||||
Term arg_file = Deref(ARG1);
|
Term arg_file = Deref(ARG1);
|
||||||
Term arg_db = ARG4;
|
Term arg_db = ARG4;
|
||||||
|
|
||||||
@ -85,11 +84,13 @@ static Int c_sqlite3_connect(USES_REGS1) {
|
|||||||
sqlite3 *db;
|
sqlite3 *db;
|
||||||
|
|
||||||
const char *file = AtomName(AtomOfTerm(arg_file));
|
const char *file = AtomName(AtomOfTerm(arg_file));
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "YAPDroid", "connect sqlite3 %s",file);
|
||||||
|
|
||||||
CALL_SQLITE(ARG1, open(file, &db));
|
CALL_SQLITE(ARG1, open(file, &db));
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "YAPDroid", "connect sqlite3 %p",db);
|
||||||
|
|
||||||
if (!Yap_unify(arg_db, MkAddressTerm(db))) {
|
if (!Yap_unify(arg_db, MkAddressTerm(db))) {
|
||||||
return FALSE;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
/* Criar um novo no na lista de ligacoes*/
|
/* Criar um novo no na lista de ligacoes*/
|
||||||
new = myddas_util_add_connection(db, NULL, API_SQLITE3);
|
new = myddas_util_add_connection(db, NULL, API_SQLITE3);
|
||||||
@ -98,9 +99,9 @@ static Int c_sqlite3_connect(USES_REGS1) {
|
|||||||
#ifdef DEBUG
|
#ifdef DEBUG
|
||||||
fprintf(stderr, "ERROR: ** c_db_my_connect ** Error allocating memory\n");
|
fprintf(stderr, "ERROR: ** c_db_my_connect ** Error allocating memory\n");
|
||||||
#endif
|
#endif
|
||||||
return FALSE;
|
return false;
|
||||||
}
|
}
|
||||||
return TRUE;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -276,6 +277,7 @@ static Int c_sqlite3_number_of_fields(USES_REGS1) {
|
|||||||
const char *relation = AtomName(AtomOfTerm(arg_relation));
|
const char *relation = AtomName(AtomOfTerm(arg_relation));
|
||||||
sqlite3 *db = AddressOfTerm(arg_db);
|
sqlite3 *db = AddressOfTerm(arg_db);
|
||||||
sqlite3_stmt *stmt;
|
sqlite3_stmt *stmt;
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "YAPDroid", " sqlite3 relation %s",relation);
|
||||||
|
|
||||||
char sql[256];
|
char sql[256];
|
||||||
|
|
||||||
@ -288,7 +290,11 @@ static Int c_sqlite3_number_of_fields(USES_REGS1) {
|
|||||||
|
|
||||||
CALL_SQLITE(ARG1, finalize(stmt));
|
CALL_SQLITE(ARG1, finalize(stmt));
|
||||||
|
|
||||||
return Yap_unify(arg_fields, MkIntegerTerm(fields));
|
__android_log_print(ANDROID_LOG_INFO, "YAPDroid", " sqlite3 fields %d",fields);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return Yap_unify(arg_fields, MkIntegerTerm(fields));
|
||||||
}
|
}
|
||||||
|
|
||||||
/* db_get_attributes_types: RelName x connection -> TypesList */
|
/* db_get_attributes_types: RelName x connection -> TypesList */
|
||||||
@ -301,6 +307,7 @@ static Int c_sqlite3_get_attributes_types(USES_REGS1) {
|
|||||||
sqlite3 *db = (sqlite3 *)IntegerOfTerm(arg_db);
|
sqlite3 *db = (sqlite3 *)IntegerOfTerm(arg_db);
|
||||||
char sql[256];
|
char sql[256];
|
||||||
int row;
|
int row;
|
||||||
|
__android_log_print(ANDROID_LOG_INFO, "YAPDroid", " sqlite3 reelation %s get_attributes",relation);
|
||||||
|
|
||||||
sqlite3_stmt *stmt;
|
sqlite3_stmt *stmt;
|
||||||
|
|
||||||
@ -341,7 +348,8 @@ static Int c_sqlite3_get_attributes_types(USES_REGS1) {
|
|||||||
tm = "?";
|
tm = "?";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
list = Yap_MkNewPairTerm();
|
__android_log_print(ANDROID_LOG_INFO, "YAPDroid", " sqlite3 attributes %s:%s ",sqlite3_column_name(stmt, row),tm);
|
||||||
|
list = Yap_MkNewPairTerm();
|
||||||
*tfp = list;
|
*tfp = list;
|
||||||
RepPair(list)[0] = MkAtomTerm(Yap_LookupAtom(tm));
|
RepPair(list)[0] = MkAtomTerm(Yap_LookupAtom(tm));
|
||||||
tfp = RepPair(list) + 1;
|
tfp = RepPair(list) + 1;
|
||||||
@ -664,9 +672,14 @@ static void Yap_InitBackMYDDAS_SQLITE3Preds(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
X_API void init_sqlite3(void) {
|
X_API void init_sqlite3(void) {
|
||||||
|
Term cm = CurrentModule;
|
||||||
|
CurrentModule = MkAtomTerm(Yap_LookupAtom("myddas_sqlite3"));
|
||||||
|
|
||||||
Yap_InitMYDDAS_SQLITE3Preds();
|
Yap_InitMYDDAS_SQLITE3Preds();
|
||||||
|
|
||||||
Yap_InitBackMYDDAS_SQLITE3Preds();
|
Yap_InitBackMYDDAS_SQLITE3Preds();
|
||||||
|
|
||||||
|
CurrentModule = cm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
package="org.sqlite.app.customsqlite"
|
|
||||||
android:versionCode="1"
|
|
||||||
android:versionName="1.0">
|
|
||||||
<application android:label="@string/app_name" android:icon="@drawable/ic_launcher">
|
|
||||||
<activity android:name="CustomSqlite"
|
|
||||||
android:label="@string/app_name">
|
|
||||||
<intent-filter>
|
|
||||||
<action android:name="android.intent.action.MAIN" />
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
</application>
|
|
||||||
</manifest>
|
|
@ -1,17 +0,0 @@
|
|||||||
# This file is used to override default values used by the Ant build system.
|
|
||||||
#
|
|
||||||
# This file must be checked into Version Control Systems, as it is
|
|
||||||
# integral to the build system of your project.
|
|
||||||
|
|
||||||
# This file is only used by the Ant script.
|
|
||||||
|
|
||||||
# You can use this to override default values such as
|
|
||||||
# 'source.dir' for the location of your java source folder and
|
|
||||||
# 'out.dir' for the location of your output folder.
|
|
||||||
|
|
||||||
# You can also use it define how the release builds are signed by declaring
|
|
||||||
# the following properties:
|
|
||||||
# 'key.store' for the location of your keystore and
|
|
||||||
# 'key.alias' for the name of the key to use.
|
|
||||||
# The password will be asked during the build when you use the 'release' target.
|
|
||||||
|
|
@ -1,92 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project name="CustomSqlite" default="help">
|
|
||||||
|
|
||||||
<!-- The local.properties file is created and updated by the 'android' tool.
|
|
||||||
It contains the path to the SDK. It should *NOT* be checked into
|
|
||||||
Version Control Systems. -->
|
|
||||||
<property file="local.properties" />
|
|
||||||
|
|
||||||
<!-- The ant.properties file can be created by you. It is only edited by the
|
|
||||||
'android' tool to add properties to it.
|
|
||||||
This is the place to change some Ant specific build properties.
|
|
||||||
Here are some properties you may want to change/update:
|
|
||||||
|
|
||||||
source.dir
|
|
||||||
The name of the source directory. Default is 'src'.
|
|
||||||
out.dir
|
|
||||||
The name of the output directory. Default is 'bin'.
|
|
||||||
|
|
||||||
For other overridable properties, look at the beginning of the rules
|
|
||||||
files in the SDK, at tools/ant/build.xml
|
|
||||||
|
|
||||||
Properties related to the SDK location or the project target should
|
|
||||||
be updated using the 'android' tool with the 'update' action.
|
|
||||||
|
|
||||||
This file is an integral part of the build system for your
|
|
||||||
application and should be checked into Version Control Systems.
|
|
||||||
|
|
||||||
-->
|
|
||||||
<property file="ant.properties" />
|
|
||||||
|
|
||||||
<!-- if sdk.dir was not set from one of the property file, then
|
|
||||||
get it from the ANDROID_HOME env var.
|
|
||||||
This must be done before we load project.properties since
|
|
||||||
the proguard config can use sdk.dir -->
|
|
||||||
<property environment="env" />
|
|
||||||
<condition property="sdk.dir" value="${env.ANDROID_HOME}">
|
|
||||||
<isset property="env.ANDROID_HOME" />
|
|
||||||
</condition>
|
|
||||||
|
|
||||||
<!-- The project.properties file is created and updated by the 'android'
|
|
||||||
tool, as well as ADT.
|
|
||||||
|
|
||||||
This contains project specific properties such as project target, and library
|
|
||||||
dependencies. Lower level build properties are stored in ant.properties
|
|
||||||
(or in .classpath for Eclipse projects).
|
|
||||||
|
|
||||||
This file is an integral part of the build system for your
|
|
||||||
application and should be checked into Version Control Systems. -->
|
|
||||||
<loadproperties srcFile="project.properties" />
|
|
||||||
|
|
||||||
<!-- quick check on sdk.dir -->
|
|
||||||
<fail
|
|
||||||
message="sdk.dir is missing. Make sure to generate local.properties using 'android update project' or to inject it through the ANDROID_HOME environment variable."
|
|
||||||
unless="sdk.dir"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<!--
|
|
||||||
Import per project custom build rules if present at the root of the project.
|
|
||||||
This is the place to put custom intermediary targets such as:
|
|
||||||
-pre-build
|
|
||||||
-pre-compile
|
|
||||||
-post-compile (This is typically used for code obfuscation.
|
|
||||||
Compiled code location: ${out.classes.absolute.dir}
|
|
||||||
If this is not done in place, override ${out.dex.input.absolute.dir})
|
|
||||||
-post-package
|
|
||||||
-post-build
|
|
||||||
-pre-clean
|
|
||||||
-->
|
|
||||||
<import file="custom_rules.xml" optional="true" />
|
|
||||||
|
|
||||||
<!-- Import the actual build file.
|
|
||||||
|
|
||||||
To customize existing targets, there are two options:
|
|
||||||
- Customize only one target:
|
|
||||||
- copy/paste the target into this file, *before* the
|
|
||||||
<import> task.
|
|
||||||
- customize it to your needs.
|
|
||||||
- Customize the whole content of build.xml
|
|
||||||
- copy/paste the content of the rules files (minus the top node)
|
|
||||||
into this file, replacing the <import> task.
|
|
||||||
- customize to your needs.
|
|
||||||
|
|
||||||
***********************
|
|
||||||
****** IMPORTANT ******
|
|
||||||
***********************
|
|
||||||
In all cases you must update the value of version-tag below to read 'custom' instead of an integer,
|
|
||||||
in order to avoid having your file be overridden by tools such as "android update project"
|
|
||||||
-->
|
|
||||||
<!-- version-tag: 1 -->
|
|
||||||
<import file="${sdk.dir}/tools/ant/build.xml" />
|
|
||||||
|
|
||||||
</project>
|
|
@ -1,4 +0,0 @@
|
|||||||
|
|
||||||
LOCAL_PATH:= $(call my-dir)
|
|
||||||
include $(LOCAL_PATH)/sqlite/Android.mk
|
|
||||||
|
|
@ -1 +0,0 @@
|
|||||||
APP_STL:=stlport_static
|
|
@ -1,76 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2013 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef NATIVEHELPER_ALOGPRIV_H_
|
|
||||||
#define NATIVEHELPER_ALOGPRIV_H_
|
|
||||||
|
|
||||||
#include <android/log.h>
|
|
||||||
|
|
||||||
#ifndef LOG_NDEBUG
|
|
||||||
#ifdef NDEBUG
|
|
||||||
#define LOG_NDEBUG 1
|
|
||||||
#else
|
|
||||||
#define LOG_NDEBUG 0
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Basic log message macros intended to emulate the behavior of log/log.h
|
|
||||||
* in system core. This should be dependent only on ndk exposed logging
|
|
||||||
* functionality.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef ALOG
|
|
||||||
#define ALOG(priority, tag, fmt...) \
|
|
||||||
__android_log_print(ANDROID_##priority, tag, fmt)
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef ALOGV
|
|
||||||
#if LOG_NDEBUG
|
|
||||||
#define ALOGV(...) ((void)0)
|
|
||||||
#else
|
|
||||||
#define ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__))
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef ALOGD
|
|
||||||
#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef ALOGI
|
|
||||||
#define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef ALOGW
|
|
||||||
#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef ALOGE
|
|
||||||
#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Not quite the same as the core android LOG_FATAL_IF (which also
|
|
||||||
** sends a SIGTRAP), but close enough.
|
|
||||||
*/
|
|
||||||
#define LOG_FATAL_IF(bCond, zErr) if( bCond ) ALOGE(zErr);
|
|
||||||
|
|
||||||
#endif
|
|
@ -1,44 +0,0 @@
|
|||||||
|
|
||||||
LOCAL_PATH:= $(call my-dir)
|
|
||||||
include $(CLEAR_VARS)
|
|
||||||
|
|
||||||
# If using SEE, uncomment the following:
|
|
||||||
# LOCAL_CFLAGS += -DSQLITE_HAS_CODEC
|
|
||||||
|
|
||||||
# This is important - it causes SQLite to use memory for temp files. Since
|
|
||||||
# Android has no globally writable temp directory, if this is not defined the
|
|
||||||
# application throws an exception when it tries to create a temp file.
|
|
||||||
#
|
|
||||||
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=""
|
|
||||||
endif
|
|
||||||
|
|
||||||
LOCAL_SRC_FILES:= \
|
|
||||||
android_database_SQLiteCommon.cpp \
|
|
||||||
android_database_SQLiteConnection.cpp \
|
|
||||||
android_database_SQLiteGlobal.cpp \
|
|
||||||
android_database_SQLiteDebug.cpp \
|
|
||||||
JNIHelp.cpp JniConstants.cpp
|
|
||||||
|
|
||||||
LOCAL_SRC_FILES += sqlite3.c
|
|
||||||
|
|
||||||
LOCAL_C_INCLUDES += $(LOCAL_PATH) $(LOCAL_PATH)/nativehelper/
|
|
||||||
|
|
||||||
LOCAL_MODULE:= libsqliteX
|
|
||||||
LOCAL_LDLIBS += -ldl -llog
|
|
||||||
|
|
||||||
include $(BUILD_SHARED_LIBRARY)
|
|
||||||
|
|
@ -1,341 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2006 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define LOG_TAG "JNIHelp"
|
|
||||||
|
|
||||||
#include "JniConstants.h"
|
|
||||||
#include "JNIHelp.h"
|
|
||||||
#include "ALog-priv.h"
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <assert.h>
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.)
|
|
||||||
*/
|
|
||||||
template<typename T>
|
|
||||||
class scoped_local_ref {
|
|
||||||
public:
|
|
||||||
scoped_local_ref(C_JNIEnv* env, T localRef = NULL)
|
|
||||||
: mEnv(env), mLocalRef(localRef)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
~scoped_local_ref() {
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset(T localRef = NULL) {
|
|
||||||
if (mLocalRef != NULL) {
|
|
||||||
(*mEnv)->DeleteLocalRef(reinterpret_cast<JNIEnv*>(mEnv), mLocalRef);
|
|
||||||
mLocalRef = localRef;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
T get() const {
|
|
||||||
return mLocalRef;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
C_JNIEnv* mEnv;
|
|
||||||
T mLocalRef;
|
|
||||||
|
|
||||||
// Disallow copy and assignment.
|
|
||||||
scoped_local_ref(const scoped_local_ref&);
|
|
||||||
void operator=(const scoped_local_ref&);
|
|
||||||
};
|
|
||||||
|
|
||||||
static jclass findClass(C_JNIEnv* env, const char* className) {
|
|
||||||
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
|
|
||||||
return (*env)->FindClass(e, className);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
|
|
||||||
const JNINativeMethod* gMethods, int numMethods)
|
|
||||||
{
|
|
||||||
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
|
|
||||||
|
|
||||||
ALOGV("Registering %s's %d native methods...", className, numMethods);
|
|
||||||
|
|
||||||
scoped_local_ref<jclass> c(env, findClass(env, className));
|
|
||||||
if (c.get() == NULL) {
|
|
||||||
char* msg;
|
|
||||||
asprintf(&msg, "Native registration unable to find class '%s'; aborting...", className);
|
|
||||||
e->FatalError(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
|
|
||||||
char* msg;
|
|
||||||
asprintf(&msg, "RegisterNatives failed for '%s'; aborting...", className);
|
|
||||||
e->FatalError(msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns a human-readable summary of an exception object. The buffer will
|
|
||||||
* be populated with the "binary" class name and, if present, the
|
|
||||||
* exception message.
|
|
||||||
*/
|
|
||||||
static bool getExceptionSummary(C_JNIEnv* env, jthrowable exception, std::string& result) {
|
|
||||||
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
|
|
||||||
|
|
||||||
/* get the name of the exception's class */
|
|
||||||
scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
|
|
||||||
scoped_local_ref<jclass> classClass(env,
|
|
||||||
(*env)->GetObjectClass(e, exceptionClass.get())); // java.lang.Class, can't fail
|
|
||||||
jmethodID classGetNameMethod =
|
|
||||||
(*env)->GetMethodID(e, classClass.get(), "getName", "()Ljava/lang/String;");
|
|
||||||
scoped_local_ref<jstring> classNameStr(env,
|
|
||||||
(jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod));
|
|
||||||
if (classNameStr.get() == NULL) {
|
|
||||||
(*env)->ExceptionClear(e);
|
|
||||||
result = "<error getting class name>";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL);
|
|
||||||
if (classNameChars == NULL) {
|
|
||||||
(*env)->ExceptionClear(e);
|
|
||||||
result = "<error getting class name UTF-8>";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
result += classNameChars;
|
|
||||||
(*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars);
|
|
||||||
|
|
||||||
/* if the exception has a detail message, get that */
|
|
||||||
jmethodID getMessage =
|
|
||||||
(*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;");
|
|
||||||
scoped_local_ref<jstring> messageStr(env,
|
|
||||||
(jstring) (*env)->CallObjectMethod(e, exception, getMessage));
|
|
||||||
if (messageStr.get() == NULL) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
result += ": ";
|
|
||||||
|
|
||||||
const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
|
|
||||||
if (messageChars != NULL) {
|
|
||||||
result += messageChars;
|
|
||||||
(*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars);
|
|
||||||
} else {
|
|
||||||
result += "<error getting message>";
|
|
||||||
(*env)->ExceptionClear(e); // clear OOM
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns an exception (with stack trace) as a string.
|
|
||||||
*/
|
|
||||||
static bool getStackTrace(C_JNIEnv* env, jthrowable exception, std::string& result) {
|
|
||||||
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
|
|
||||||
|
|
||||||
scoped_local_ref<jclass> stringWriterClass(env, findClass(env, "java/io/StringWriter"));
|
|
||||||
if (stringWriterClass.get() == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
jmethodID stringWriterCtor = (*env)->GetMethodID(e, stringWriterClass.get(), "<init>", "()V");
|
|
||||||
jmethodID stringWriterToStringMethod =
|
|
||||||
(*env)->GetMethodID(e, stringWriterClass.get(), "toString", "()Ljava/lang/String;");
|
|
||||||
|
|
||||||
scoped_local_ref<jclass> printWriterClass(env, findClass(env, "java/io/PrintWriter"));
|
|
||||||
if (printWriterClass.get() == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
jmethodID printWriterCtor =
|
|
||||||
(*env)->GetMethodID(e, printWriterClass.get(), "<init>", "(Ljava/io/Writer;)V");
|
|
||||||
|
|
||||||
scoped_local_ref<jobject> stringWriter(env,
|
|
||||||
(*env)->NewObject(e, stringWriterClass.get(), stringWriterCtor));
|
|
||||||
if (stringWriter.get() == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
jobject printWriter =
|
|
||||||
(*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get());
|
|
||||||
if (printWriter == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
|
|
||||||
jmethodID printStackTraceMethod =
|
|
||||||
(*env)->GetMethodID(e, exceptionClass.get(), "printStackTrace", "(Ljava/io/PrintWriter;)V");
|
|
||||||
(*env)->CallVoidMethod(e, exception, printStackTraceMethod, printWriter);
|
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(e)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
scoped_local_ref<jstring> messageStr(env,
|
|
||||||
(jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod));
|
|
||||||
if (messageStr.get() == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
|
|
||||||
if (utfChars == NULL) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
result = utfChars;
|
|
||||||
|
|
||||||
(*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) {
|
|
||||||
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
|
|
||||||
|
|
||||||
if ((*env)->ExceptionCheck(e)) {
|
|
||||||
/* TODO: consider creating the new exception with this as "cause" */
|
|
||||||
scoped_local_ref<jthrowable> exception(env, (*env)->ExceptionOccurred(e));
|
|
||||||
(*env)->ExceptionClear(e);
|
|
||||||
|
|
||||||
if (exception.get() != NULL) {
|
|
||||||
std::string text;
|
|
||||||
getExceptionSummary(env, exception.get(), text);
|
|
||||||
ALOGW("Discarding pending exception (%s) to throw %s", text.c_str(), className);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
scoped_local_ref<jclass> exceptionClass(env, findClass(env, className));
|
|
||||||
if (exceptionClass.get() == NULL) {
|
|
||||||
ALOGE("Unable to find exception class %s", className);
|
|
||||||
/* ClassNotFoundException now pending */
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) {
|
|
||||||
ALOGE("Failed throwing '%s' '%s'", className, msg);
|
|
||||||
/* an exception, most likely OOM, will now be pending */
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) {
|
|
||||||
char msgBuf[512];
|
|
||||||
vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
|
|
||||||
return jniThrowException(env, className, msgBuf);
|
|
||||||
}
|
|
||||||
|
|
||||||
int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) {
|
|
||||||
return jniThrowException(env, "java/lang/NullPointerException", msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) {
|
|
||||||
return jniThrowException(env, "java/lang/RuntimeException", msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
int jniThrowIOException(C_JNIEnv* env, int errnum) {
|
|
||||||
char buffer[80];
|
|
||||||
const char* message = jniStrError(errnum, buffer, sizeof(buffer));
|
|
||||||
return jniThrowException(env, "java/io/IOException", message);
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string jniGetStackTrace(C_JNIEnv* env, jthrowable exception) {
|
|
||||||
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
|
|
||||||
|
|
||||||
scoped_local_ref<jthrowable> currentException(env, (*env)->ExceptionOccurred(e));
|
|
||||||
if (exception == NULL) {
|
|
||||||
exception = currentException.get();
|
|
||||||
if (exception == NULL) {
|
|
||||||
return "<no pending exception>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentException.get() != NULL) {
|
|
||||||
(*env)->ExceptionClear(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string trace;
|
|
||||||
if (!getStackTrace(env, exception, trace)) {
|
|
||||||
(*env)->ExceptionClear(e);
|
|
||||||
getExceptionSummary(env, exception, trace);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentException.get() != NULL) {
|
|
||||||
(*env)->Throw(e, currentException.get()); // rethrow
|
|
||||||
}
|
|
||||||
|
|
||||||
return trace;
|
|
||||||
}
|
|
||||||
|
|
||||||
void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) {
|
|
||||||
std::string trace(jniGetStackTrace(env, exception));
|
|
||||||
__android_log_write(priority, tag, trace.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* jniStrError(int errnum, char* buf, size_t buflen) {
|
|
||||||
#if __GLIBC__
|
|
||||||
// Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int.
|
|
||||||
// char *strerror_r(int errnum, char *buf, size_t n);
|
|
||||||
return strerror_r(errnum, buf, buflen);
|
|
||||||
#else
|
|
||||||
char *rc = strerror_r(errnum, buf, buflen);
|
|
||||||
if (rc != NULL) {
|
|
||||||
// (POSIX only guarantees a value other than 0. The safest
|
|
||||||
// way to implement this function is to use C++ and overload on the
|
|
||||||
// type of strerror_r to accurately distinguish GNU from POSIX.)
|
|
||||||
snprintf(buf, buflen, "errno %d", errnum);
|
|
||||||
}
|
|
||||||
return buf;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) {
|
|
||||||
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
|
|
||||||
static jmethodID ctor = e->GetMethodID(JniConstants::fileDescriptorClass, "<init>", "()V");
|
|
||||||
jobject fileDescriptor = (*env)->NewObject(e, JniConstants::fileDescriptorClass, ctor);
|
|
||||||
// NOTE: NewObject ensures that an OutOfMemoryError will be seen by the Java
|
|
||||||
// caller if the alloc fails, so we just return NULL when that happens.
|
|
||||||
if (fileDescriptor != NULL) {
|
|
||||||
jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
|
|
||||||
}
|
|
||||||
return fileDescriptor;
|
|
||||||
}
|
|
||||||
|
|
||||||
int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
|
|
||||||
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
|
|
||||||
static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I");
|
|
||||||
if (fileDescriptor != NULL) {
|
|
||||||
return (*env)->GetIntField(e, fileDescriptor, fid);
|
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) {
|
|
||||||
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
|
|
||||||
static jfieldID fid = e->GetFieldID(JniConstants::fileDescriptorClass, "descriptor", "I");
|
|
||||||
(*env)->SetIntField(e, fileDescriptor, fid, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
jobject jniGetReferent(C_JNIEnv* env, jobject ref) {
|
|
||||||
JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
|
|
||||||
static jmethodID get = e->GetMethodID(JniConstants::referenceClass, "get", "()Ljava/lang/Object;");
|
|
||||||
return (*env)->CallObjectMethod(e, ref, get);
|
|
||||||
}
|
|
||||||
|
|
@ -1,139 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define LOG_TAG "JniConstants"
|
|
||||||
|
|
||||||
#include "ALog-priv.h"
|
|
||||||
#include "JniConstants.h"
|
|
||||||
#include "ScopedLocalRef.h"
|
|
||||||
|
|
||||||
#include <stdlib.h>
|
|
||||||
|
|
||||||
jclass JniConstants::bidiRunClass;
|
|
||||||
jclass JniConstants::bigDecimalClass;
|
|
||||||
jclass JniConstants::booleanClass;
|
|
||||||
jclass JniConstants::byteArrayClass;
|
|
||||||
jclass JniConstants::byteClass;
|
|
||||||
jclass JniConstants::calendarClass;
|
|
||||||
jclass JniConstants::characterClass;
|
|
||||||
jclass JniConstants::charsetICUClass;
|
|
||||||
jclass JniConstants::constructorClass;
|
|
||||||
jclass JniConstants::deflaterClass;
|
|
||||||
jclass JniConstants::doubleClass;
|
|
||||||
jclass JniConstants::errnoExceptionClass;
|
|
||||||
jclass JniConstants::fieldClass;
|
|
||||||
jclass JniConstants::fieldPositionIteratorClass;
|
|
||||||
jclass JniConstants::fileDescriptorClass;
|
|
||||||
jclass JniConstants::floatClass;
|
|
||||||
jclass JniConstants::gaiExceptionClass;
|
|
||||||
jclass JniConstants::inet6AddressClass;
|
|
||||||
jclass JniConstants::inetAddressClass;
|
|
||||||
jclass JniConstants::inetSocketAddressClass;
|
|
||||||
jclass JniConstants::inetUnixAddressClass;
|
|
||||||
jclass JniConstants::inflaterClass;
|
|
||||||
jclass JniConstants::inputStreamClass;
|
|
||||||
jclass JniConstants::integerClass;
|
|
||||||
jclass JniConstants::localeDataClass;
|
|
||||||
jclass JniConstants::longClass;
|
|
||||||
jclass JniConstants::methodClass;
|
|
||||||
jclass JniConstants::mutableIntClass;
|
|
||||||
jclass JniConstants::mutableLongClass;
|
|
||||||
jclass JniConstants::objectClass;
|
|
||||||
jclass JniConstants::objectArrayClass;
|
|
||||||
jclass JniConstants::outputStreamClass;
|
|
||||||
jclass JniConstants::parsePositionClass;
|
|
||||||
jclass JniConstants::patternSyntaxExceptionClass;
|
|
||||||
jclass JniConstants::realToStringClass;
|
|
||||||
jclass JniConstants::referenceClass;
|
|
||||||
jclass JniConstants::shortClass;
|
|
||||||
jclass JniConstants::socketClass;
|
|
||||||
jclass JniConstants::socketImplClass;
|
|
||||||
jclass JniConstants::stringClass;
|
|
||||||
jclass JniConstants::structAddrinfoClass;
|
|
||||||
jclass JniConstants::structFlockClass;
|
|
||||||
jclass JniConstants::structGroupReqClass;
|
|
||||||
jclass JniConstants::structLingerClass;
|
|
||||||
jclass JniConstants::structPasswdClass;
|
|
||||||
jclass JniConstants::structPollfdClass;
|
|
||||||
jclass JniConstants::structStatClass;
|
|
||||||
jclass JniConstants::structStatVfsClass;
|
|
||||||
jclass JniConstants::structTimevalClass;
|
|
||||||
jclass JniConstants::structUcredClass;
|
|
||||||
jclass JniConstants::structUtsnameClass;
|
|
||||||
|
|
||||||
static jclass findClass(JNIEnv* env, const char* name) {
|
|
||||||
ScopedLocalRef<jclass> localClass(env, env->FindClass(name));
|
|
||||||
jclass result = reinterpret_cast<jclass>(env->NewGlobalRef(localClass.get()));
|
|
||||||
if (result == NULL) {
|
|
||||||
ALOGE("failed to find class '%s'", name);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void JniConstants::init(JNIEnv* env) {
|
|
||||||
bidiRunClass = findClass(env, "java/text/Bidi$Run");
|
|
||||||
bigDecimalClass = findClass(env, "java/math/BigDecimal");
|
|
||||||
booleanClass = findClass(env, "java/lang/Boolean");
|
|
||||||
byteClass = findClass(env, "java/lang/Byte");
|
|
||||||
byteArrayClass = findClass(env, "[B");
|
|
||||||
calendarClass = findClass(env, "java/util/Calendar");
|
|
||||||
characterClass = findClass(env, "java/lang/Character");
|
|
||||||
charsetICUClass = findClass(env, "java/nio/charset/CharsetICU");
|
|
||||||
constructorClass = findClass(env, "java/lang/reflect/Constructor");
|
|
||||||
floatClass = findClass(env, "java/lang/Float");
|
|
||||||
deflaterClass = findClass(env, "java/util/zip/Deflater");
|
|
||||||
doubleClass = findClass(env, "java/lang/Double");
|
|
||||||
errnoExceptionClass = findClass(env, "libcore/io/ErrnoException");
|
|
||||||
fieldClass = findClass(env, "java/lang/reflect/Field");
|
|
||||||
fieldPositionIteratorClass = findClass(env, "libcore/icu/NativeDecimalFormat$FieldPositionIterator");
|
|
||||||
fileDescriptorClass = findClass(env, "java/io/FileDescriptor");
|
|
||||||
gaiExceptionClass = findClass(env, "libcore/io/GaiException");
|
|
||||||
inet6AddressClass = findClass(env, "java/net/Inet6Address");
|
|
||||||
inetAddressClass = findClass(env, "java/net/InetAddress");
|
|
||||||
inetSocketAddressClass = findClass(env, "java/net/InetSocketAddress");
|
|
||||||
inetUnixAddressClass = findClass(env, "java/net/InetUnixAddress");
|
|
||||||
inflaterClass = findClass(env, "java/util/zip/Inflater");
|
|
||||||
inputStreamClass = findClass(env, "java/io/InputStream");
|
|
||||||
integerClass = findClass(env, "java/lang/Integer");
|
|
||||||
localeDataClass = findClass(env, "libcore/icu/LocaleData");
|
|
||||||
longClass = findClass(env, "java/lang/Long");
|
|
||||||
methodClass = findClass(env, "java/lang/reflect/Method");
|
|
||||||
mutableIntClass = findClass(env, "libcore/util/MutableInt");
|
|
||||||
mutableLongClass = findClass(env, "libcore/util/MutableLong");
|
|
||||||
objectClass = findClass(env, "java/lang/Object");
|
|
||||||
objectArrayClass = findClass(env, "[Ljava/lang/Object;");
|
|
||||||
outputStreamClass = findClass(env, "java/io/OutputStream");
|
|
||||||
parsePositionClass = findClass(env, "java/text/ParsePosition");
|
|
||||||
patternSyntaxExceptionClass = findClass(env, "java/util/regex/PatternSyntaxException");
|
|
||||||
realToStringClass = findClass(env, "java/lang/RealToString");
|
|
||||||
referenceClass = findClass(env, "java/lang/ref/Reference");
|
|
||||||
shortClass = findClass(env, "java/lang/Short");
|
|
||||||
socketClass = findClass(env, "java/net/Socket");
|
|
||||||
socketImplClass = findClass(env, "java/net/SocketImpl");
|
|
||||||
stringClass = findClass(env, "java/lang/String");
|
|
||||||
structAddrinfoClass = findClass(env, "libcore/io/StructAddrinfo");
|
|
||||||
structFlockClass = findClass(env, "libcore/io/StructFlock");
|
|
||||||
structGroupReqClass = findClass(env, "libcore/io/StructGroupReq");
|
|
||||||
structLingerClass = findClass(env, "libcore/io/StructLinger");
|
|
||||||
structPasswdClass = findClass(env, "libcore/io/StructPasswd");
|
|
||||||
structPollfdClass = findClass(env, "libcore/io/StructPollfd");
|
|
||||||
structStatClass = findClass(env, "libcore/io/StructStat");
|
|
||||||
structStatVfsClass = findClass(env, "libcore/io/StructStatVfs");
|
|
||||||
structTimevalClass = findClass(env, "libcore/io/StructTimeval");
|
|
||||||
structUcredClass = findClass(env, "libcore/io/StructUcred");
|
|
||||||
structUtsnameClass = findClass(env, "libcore/io/StructUtsname");
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
|
|
||||||
All the files in this directory are copied from stock android. The following
|
|
||||||
files:
|
|
||||||
|
|
||||||
JniConstants.cpp
|
|
||||||
JNIHelp.cpp
|
|
||||||
ALog-priv.h
|
|
||||||
|
|
||||||
are copied in from Android's libnativehelper module (altogether less than 1000
|
|
||||||
lines of code). The remainder are from the core framework (directory
|
|
||||||
/frameworks/base/core/jni).
|
|
||||||
|
|
||||||
Notes on changes:
|
|
||||||
|
|
||||||
The ashmem_XXX() interfaces are used for the various "xxxForBlobDescriptor()"
|
|
||||||
API functions. The code in libcutils for this seems to be platform
|
|
||||||
dependent - some platforms have kernel support, others have a user space
|
|
||||||
implementation. So these functions are not supported for now.
|
|
||||||
|
|
||||||
The original SQLiteConnection.cpp uses AndroidRuntime::genJNIEnv() to obtain a
|
|
||||||
pointer to the current threads environment. Changed to store a pointer to the
|
|
||||||
process JavaVM (Android allows only one) as a global variable. Then retrieve
|
|
||||||
the JNIEnv as needed using GetEnv().
|
|
||||||
|
|
||||||
Replaced uses of class String8 with std::string in SQLiteConnection.cpp and a
|
|
||||||
few other places.
|
|
||||||
|
|
||||||
The stock Android code to populate CursorWindow containers with the results of
|
|
||||||
a SELECT statement uses a C++ interface that is not available to NDK builds. So
|
|
||||||
this code is rewritten to call the CursorWindow java interface via JNI methods.
|
|
||||||
This is the largest source code change. See function
|
|
||||||
nativeExecuteForCursorWindow() in file android_database_SQLiteConnection.cpp
|
|
||||||
for details.
|
|
||||||
|
|
||||||
The "LOCALIZED" collation and some miscellaneous user-functions added by the
|
|
||||||
sqlite3_android.cpp module are not included. A collation called LOCALIZED
|
|
||||||
that is equivalent to BINARY is added instead to keep various things working.
|
|
||||||
This should not cause serious problems - class SQLiteConnection always
|
|
||||||
runs "REINDEX LOCALIZED" immediately after opening a connection.
|
|
||||||
|
|
@ -1,138 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2011 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "android_database_SQLiteCommon.h"
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
|
|
||||||
/* throw a SQLiteException with a message appropriate for the error in handle */
|
|
||||||
void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle) {
|
|
||||||
throw_sqlite3_exception(env, handle, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* throw a SQLiteException with the given message */
|
|
||||||
void throw_sqlite3_exception(JNIEnv* env, const char* message) {
|
|
||||||
throw_sqlite3_exception(env, NULL, message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* throw a SQLiteException with a message appropriate for the error in handle
|
|
||||||
concatenated with the given message
|
|
||||||
*/
|
|
||||||
void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle, const char* message) {
|
|
||||||
if (handle) {
|
|
||||||
// get the error code and message from the SQLite connection
|
|
||||||
// the error message may contain more information than the error code
|
|
||||||
// because it is based on the extended error code rather than the simplified
|
|
||||||
// error code that SQLite normally returns.
|
|
||||||
throw_sqlite3_exception(env, sqlite3_extended_errcode(handle),
|
|
||||||
sqlite3_errmsg(handle), message);
|
|
||||||
} else {
|
|
||||||
// we use SQLITE_OK so that a generic SQLiteException is thrown;
|
|
||||||
// any code not specified in the switch statement below would do.
|
|
||||||
throw_sqlite3_exception(env, SQLITE_OK, "unknown error", message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* throw a SQLiteException for a given error code
|
|
||||||
* should only be used when the database connection is not available because the
|
|
||||||
* error information will not be quite as rich */
|
|
||||||
void throw_sqlite3_exception_errcode(JNIEnv* env, int errcode, const char* message) {
|
|
||||||
throw_sqlite3_exception(env, errcode, "unknown error", message);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* throw a SQLiteException for a given error code, sqlite3message, and
|
|
||||||
user message
|
|
||||||
*/
|
|
||||||
void throw_sqlite3_exception(JNIEnv* env, int errcode,
|
|
||||||
const char* sqlite3Message, const char* message) {
|
|
||||||
const char* exceptionClass;
|
|
||||||
switch (errcode & 0xff) { /* mask off extended error code */
|
|
||||||
case SQLITE_IOERR:
|
|
||||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteDiskIOException";
|
|
||||||
break;
|
|
||||||
case SQLITE_CORRUPT:
|
|
||||||
case SQLITE_NOTADB: // treat "unsupported file format" error as corruption also
|
|
||||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteDatabaseCorruptException";
|
|
||||||
break;
|
|
||||||
case SQLITE_CONSTRAINT:
|
|
||||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteConstraintException";
|
|
||||||
break;
|
|
||||||
case SQLITE_ABORT:
|
|
||||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteAbortException";
|
|
||||||
break;
|
|
||||||
case SQLITE_DONE:
|
|
||||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteDoneException";
|
|
||||||
sqlite3Message = NULL; // SQLite error message is irrelevant in this case
|
|
||||||
break;
|
|
||||||
case SQLITE_FULL:
|
|
||||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteFullException";
|
|
||||||
break;
|
|
||||||
case SQLITE_MISUSE:
|
|
||||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteMisuseException";
|
|
||||||
break;
|
|
||||||
case SQLITE_PERM:
|
|
||||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteAccessPermException";
|
|
||||||
break;
|
|
||||||
case SQLITE_BUSY:
|
|
||||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteDatabaseLockedException";
|
|
||||||
break;
|
|
||||||
case SQLITE_LOCKED:
|
|
||||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteTableLockedException";
|
|
||||||
break;
|
|
||||||
case SQLITE_READONLY:
|
|
||||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteReadOnlyDatabaseException";
|
|
||||||
break;
|
|
||||||
case SQLITE_CANTOPEN:
|
|
||||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteCantOpenDatabaseException";
|
|
||||||
break;
|
|
||||||
case SQLITE_TOOBIG:
|
|
||||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteBlobTooBigException";
|
|
||||||
break;
|
|
||||||
case SQLITE_RANGE:
|
|
||||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteBindOrColumnIndexOutOfRangeException";
|
|
||||||
break;
|
|
||||||
case SQLITE_NOMEM:
|
|
||||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteOutOfMemoryException";
|
|
||||||
break;
|
|
||||||
case SQLITE_MISMATCH:
|
|
||||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteDatatypeMismatchException";
|
|
||||||
break;
|
|
||||||
case SQLITE_INTERRUPT:
|
|
||||||
exceptionClass = "android/os/OperationCanceledException";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
exceptionClass = "org/sqlite/database/sqlite/SQLiteException";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sqlite3Message) {
|
|
||||||
char *zFullmsg = sqlite3_mprintf(
|
|
||||||
"%s (code %d)%s%s", sqlite3Message, errcode,
|
|
||||||
(message ? ": " : ""), (message ? message : "")
|
|
||||||
);
|
|
||||||
jniThrowException(env, exceptionClass, zFullmsg);
|
|
||||||
sqlite3_free(zFullmsg);
|
|
||||||
} else {
|
|
||||||
jniThrowException(env, exceptionClass, message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
} // namespace android
|
|
@ -1,55 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2007 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef _ANDROID_DATABASE_SQLITE_COMMON_H
|
|
||||||
#define _ANDROID_DATABASE_SQLITE_COMMON_H
|
|
||||||
|
|
||||||
#include <jni.h>
|
|
||||||
#include <JNIHelp.h>
|
|
||||||
|
|
||||||
#include <sqlite3.h>
|
|
||||||
|
|
||||||
// Special log tags defined in SQLiteDebug.java.
|
|
||||||
#define SQLITE_LOG_TAG "SQLiteLog"
|
|
||||||
#define SQLITE_TRACE_TAG "SQLiteStatements"
|
|
||||||
#define SQLITE_PROFILE_TAG "SQLiteTime"
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
|
|
||||||
/* throw a SQLiteException with a message appropriate for the error in handle */
|
|
||||||
void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle);
|
|
||||||
|
|
||||||
/* throw a SQLiteException with the given message */
|
|
||||||
void throw_sqlite3_exception(JNIEnv* env, const char* message);
|
|
||||||
|
|
||||||
/* throw a SQLiteException with a message appropriate for the error in handle
|
|
||||||
concatenated with the given message
|
|
||||||
*/
|
|
||||||
void throw_sqlite3_exception(JNIEnv* env, sqlite3* handle, const char* message);
|
|
||||||
|
|
||||||
/* throw a SQLiteException for a given error code */
|
|
||||||
void throw_sqlite3_exception_errcode(JNIEnv* env, int errcode, const char* message);
|
|
||||||
|
|
||||||
void throw_sqlite3_exception(JNIEnv* env, int errcode,
|
|
||||||
const char* sqlite3Message, const char* message);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif // _ANDROID_DATABASE_SQLITE_COMMON_H
|
|
File diff suppressed because it is too large
Load Diff
@ -1,92 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2007 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define LOG_TAG "SQLiteDebug"
|
|
||||||
|
|
||||||
#include <jni.h>
|
|
||||||
#include <JNIHelp.h>
|
|
||||||
#include <ALog-priv.h>
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include <sqlite3.h>
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
|
|
||||||
static struct {
|
|
||||||
jfieldID memoryUsed;
|
|
||||||
jfieldID pageCacheOverflow;
|
|
||||||
jfieldID largestMemAlloc;
|
|
||||||
} gSQLiteDebugPagerStatsClassInfo;
|
|
||||||
|
|
||||||
static void nativeGetPagerStats(JNIEnv *env, jobject clazz, jobject statsObj)
|
|
||||||
{
|
|
||||||
int memoryUsed;
|
|
||||||
int pageCacheOverflow;
|
|
||||||
int largestMemAlloc;
|
|
||||||
int unused;
|
|
||||||
|
|
||||||
sqlite3_status(SQLITE_STATUS_MEMORY_USED, &memoryUsed, &unused, 0);
|
|
||||||
sqlite3_status(SQLITE_STATUS_MALLOC_SIZE, &unused, &largestMemAlloc, 0);
|
|
||||||
sqlite3_status(SQLITE_STATUS_PAGECACHE_OVERFLOW, &pageCacheOverflow, &unused, 0);
|
|
||||||
env->SetIntField(statsObj, gSQLiteDebugPagerStatsClassInfo.memoryUsed, memoryUsed);
|
|
||||||
env->SetIntField(statsObj, gSQLiteDebugPagerStatsClassInfo.pageCacheOverflow,
|
|
||||||
pageCacheOverflow);
|
|
||||||
env->SetIntField(statsObj, gSQLiteDebugPagerStatsClassInfo.largestMemAlloc, largestMemAlloc);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JNI registration.
|
|
||||||
*/
|
|
||||||
|
|
||||||
static JNINativeMethod gMethods[] =
|
|
||||||
{
|
|
||||||
{ "nativeGetPagerStats", "(Lorg/sqlite/database/sqlite/SQLiteDebug$PagerStats;)V",
|
|
||||||
(void*) nativeGetPagerStats },
|
|
||||||
};
|
|
||||||
|
|
||||||
#define FIND_CLASS(var, className) \
|
|
||||||
var = env->FindClass(className); \
|
|
||||||
LOG_FATAL_IF(! var, "Unable to find class " className);
|
|
||||||
|
|
||||||
#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
|
|
||||||
var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
|
|
||||||
LOG_FATAL_IF(! var, "Unable to find field " fieldName);
|
|
||||||
|
|
||||||
int register_android_database_SQLiteDebug(JNIEnv *env)
|
|
||||||
{
|
|
||||||
jclass clazz;
|
|
||||||
FIND_CLASS(clazz, "org/sqlite/database/sqlite/SQLiteDebug$PagerStats");
|
|
||||||
|
|
||||||
GET_FIELD_ID(gSQLiteDebugPagerStatsClassInfo.memoryUsed, clazz,
|
|
||||||
"memoryUsed", "I");
|
|
||||||
GET_FIELD_ID(gSQLiteDebugPagerStatsClassInfo.largestMemAlloc, clazz,
|
|
||||||
"largestMemAlloc", "I");
|
|
||||||
GET_FIELD_ID(gSQLiteDebugPagerStatsClassInfo.pageCacheOverflow, clazz,
|
|
||||||
"pageCacheOverflow", "I");
|
|
||||||
|
|
||||||
return jniRegisterNativeMethods(env, "org/sqlite/database/sqlite/SQLiteDebug",
|
|
||||||
gMethods, NELEM(gMethods));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace android
|
|
@ -1,99 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2011 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define LOG_TAG "SQLiteGlobal"
|
|
||||||
|
|
||||||
#include <jni.h>
|
|
||||||
#include <JNIHelp.h>
|
|
||||||
#include "ALog-priv.h"
|
|
||||||
|
|
||||||
|
|
||||||
#include <sqlite3.h>
|
|
||||||
#if 0
|
|
||||||
#include <sqlite3_android.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
#include "android_database_SQLiteCommon.h"
|
|
||||||
|
|
||||||
namespace android {
|
|
||||||
|
|
||||||
// Limit heap to 8MB for now. This is 4 times the maximum cursor window
|
|
||||||
// size, as has been used by the original code in SQLiteDatabase for
|
|
||||||
// a long time.
|
|
||||||
static const int SOFT_HEAP_LIMIT = 8 * 1024 * 1024;
|
|
||||||
|
|
||||||
|
|
||||||
// Called each time a message is logged.
|
|
||||||
static void sqliteLogCallback(void* data, int iErrCode, const char* zMsg) {
|
|
||||||
bool verboseLog = !!data;
|
|
||||||
if (iErrCode == 0 || iErrCode == SQLITE_CONSTRAINT || iErrCode == SQLITE_SCHEMA) {
|
|
||||||
if (verboseLog) {
|
|
||||||
ALOG(LOG_VERBOSE, SQLITE_LOG_TAG, "(%d) %s\n", iErrCode, zMsg);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ALOG(LOG_ERROR, SQLITE_LOG_TAG, "(%d) %s\n", iErrCode, zMsg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets the global SQLite configuration.
|
|
||||||
// This must be called before any other SQLite functions are called.
|
|
||||||
static void sqliteInitialize() {
|
|
||||||
// Enable multi-threaded mode. In this mode, SQLite is safe to use by multiple
|
|
||||||
// threads as long as no two threads use the same database connection at the same
|
|
||||||
// time (which we guarantee in the SQLite database wrappers).
|
|
||||||
sqlite3_config(SQLITE_CONFIG_MULTITHREAD);
|
|
||||||
|
|
||||||
// Redirect SQLite log messages to the Android log.
|
|
||||||
#if 0
|
|
||||||
bool verboseLog = android_util_Log_isVerboseLogEnabled(SQLITE_LOG_TAG);
|
|
||||||
#endif
|
|
||||||
bool verboseLog = false;
|
|
||||||
sqlite3_config(SQLITE_CONFIG_LOG, &sqliteLogCallback, verboseLog ? (void*)1 : NULL);
|
|
||||||
|
|
||||||
// The soft heap limit prevents the page cache allocations from growing
|
|
||||||
// beyond the given limit, no matter what the max page cache sizes are
|
|
||||||
// set to. The limit does not, as of 3.5.0, affect any other allocations.
|
|
||||||
sqlite3_soft_heap_limit(SOFT_HEAP_LIMIT);
|
|
||||||
|
|
||||||
// Initialize SQLite.
|
|
||||||
sqlite3_initialize();
|
|
||||||
}
|
|
||||||
|
|
||||||
static jint nativeReleaseMemory(JNIEnv* env, jclass clazz) {
|
|
||||||
return sqlite3_release_memory(SOFT_HEAP_LIMIT);
|
|
||||||
}
|
|
||||||
|
|
||||||
static JNINativeMethod sMethods[] =
|
|
||||||
{
|
|
||||||
/* name, signature, funcPtr */
|
|
||||||
{ "nativeReleaseMemory", "()I",
|
|
||||||
(void*)nativeReleaseMemory },
|
|
||||||
};
|
|
||||||
|
|
||||||
int register_android_database_SQLiteGlobal(JNIEnv *env)
|
|
||||||
{
|
|
||||||
sqliteInitialize();
|
|
||||||
|
|
||||||
return jniRegisterNativeMethods(env, "org/sqlite/database/sqlite/SQLiteGlobal",
|
|
||||||
sMethods, NELEM(sMethods));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace android
|
|
@ -1,191 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2007 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* JNI helper functions.
|
|
||||||
*
|
|
||||||
* This file may be included by C or C++ code, which is trouble because jni.h
|
|
||||||
* uses different typedefs for JNIEnv in each language.
|
|
||||||
*
|
|
||||||
* TODO: remove C support.
|
|
||||||
*/
|
|
||||||
#ifndef NATIVEHELPER_JNIHELP_H_
|
|
||||||
#define NATIVEHELPER_JNIHELP_H_
|
|
||||||
|
|
||||||
#include "jni.h"
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#ifndef NELEM
|
|
||||||
# define NELEM(x) ((int) (sizeof(x) / sizeof((x)[0])))
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
extern "C" {
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Register one or more native methods with a particular class.
|
|
||||||
* "className" looks like "java/lang/String". Aborts on failure.
|
|
||||||
* TODO: fix all callers and change the return type to void.
|
|
||||||
*/
|
|
||||||
int jniRegisterNativeMethods(C_JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Throw an exception with the specified class and an optional message.
|
|
||||||
*
|
|
||||||
* The "className" argument will be passed directly to FindClass, which
|
|
||||||
* takes strings with slashes (e.g. "java/lang/Object").
|
|
||||||
*
|
|
||||||
* If an exception is currently pending, we log a warning message and
|
|
||||||
* clear it.
|
|
||||||
*
|
|
||||||
* Returns 0 on success, nonzero if something failed (e.g. the exception
|
|
||||||
* class couldn't be found, so *an* exception will still be pending).
|
|
||||||
*
|
|
||||||
* Currently aborts the VM if it can't throw the exception.
|
|
||||||
*/
|
|
||||||
int jniThrowException(C_JNIEnv* env, const char* className, const char* msg);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Throw a java.lang.NullPointerException, with an optional message.
|
|
||||||
*/
|
|
||||||
int jniThrowNullPointerException(C_JNIEnv* env, const char* msg);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Throw a java.lang.RuntimeException, with an optional message.
|
|
||||||
*/
|
|
||||||
int jniThrowRuntimeException(C_JNIEnv* env, const char* msg);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Throw a java.io.IOException, generating the message from errno.
|
|
||||||
*/
|
|
||||||
int jniThrowIOException(C_JNIEnv* env, int errnum);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Return a pointer to a locale-dependent error string explaining errno
|
|
||||||
* value 'errnum'. The returned pointer may or may not be equal to 'buf'.
|
|
||||||
* This function is thread-safe (unlike strerror) and portable (unlike
|
|
||||||
* strerror_r).
|
|
||||||
*/
|
|
||||||
const char* jniStrError(int errnum, char* buf, size_t buflen);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns a new java.io.FileDescriptor for the given int fd.
|
|
||||||
*/
|
|
||||||
jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns the int fd from a java.io.FileDescriptor.
|
|
||||||
*/
|
|
||||||
int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Sets the int fd in a java.io.FileDescriptor.
|
|
||||||
*/
|
|
||||||
void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Returns the reference from a java.lang.ref.Reference.
|
|
||||||
*/
|
|
||||||
jobject jniGetReferent(C_JNIEnv* env, jobject ref);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Log a message and an exception.
|
|
||||||
* If exception is NULL, logs the current exception in the JNI environment.
|
|
||||||
*/
|
|
||||||
void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception);
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* For C++ code, we provide inlines that map to the C functions. g++ always
|
|
||||||
* inlines these, even on non-optimized builds.
|
|
||||||
*/
|
|
||||||
#if defined(__cplusplus)
|
|
||||||
inline int jniRegisterNativeMethods(JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods) {
|
|
||||||
return jniRegisterNativeMethods(&env->functions, className, gMethods, numMethods);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int jniThrowException(JNIEnv* env, const char* className, const char* msg) {
|
|
||||||
return jniThrowException(&env->functions, className, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
extern "C" int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args);
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Equivalent to jniThrowException but with a printf-like format string and
|
|
||||||
* variable-length argument list. This is only available in C++.
|
|
||||||
*/
|
|
||||||
inline int jniThrowExceptionFmt(JNIEnv* env, const char* className, const char* fmt, ...) {
|
|
||||||
va_list args;
|
|
||||||
va_start(args, fmt);
|
|
||||||
return jniThrowExceptionFmt(&env->functions, className, fmt, args);
|
|
||||||
va_end(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int jniThrowNullPointerException(JNIEnv* env, const char* msg) {
|
|
||||||
return jniThrowNullPointerException(&env->functions, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int jniThrowRuntimeException(JNIEnv* env, const char* msg) {
|
|
||||||
return jniThrowRuntimeException(&env->functions, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int jniThrowIOException(JNIEnv* env, int errnum) {
|
|
||||||
return jniThrowIOException(&env->functions, errnum);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline jobject jniCreateFileDescriptor(JNIEnv* env, int fd) {
|
|
||||||
return jniCreateFileDescriptor(&env->functions, fd);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor) {
|
|
||||||
return jniGetFDFromFileDescriptor(&env->functions, fileDescriptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void jniSetFileDescriptorOfFD(JNIEnv* env, jobject fileDescriptor, int value) {
|
|
||||||
jniSetFileDescriptorOfFD(&env->functions, fileDescriptor, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline jobject jniGetReferent(JNIEnv* env, jobject ref) {
|
|
||||||
return jniGetReferent(&env->functions, ref);
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void jniLogException(JNIEnv* env, int priority, const char* tag, jthrowable exception = NULL) {
|
|
||||||
jniLogException(&env->functions, priority, tag, exception);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
|
||||||
* TEMP_FAILURE_RETRY is defined by some, but not all, versions of
|
|
||||||
* <unistd.h>. (Alas, it is not as standard as we'd hoped!) So, if it's
|
|
||||||
* not already defined, then define it here.
|
|
||||||
*/
|
|
||||||
#ifndef TEMP_FAILURE_RETRY
|
|
||||||
/* Used to retry syscalls that can return EINTR. */
|
|
||||||
#define TEMP_FAILURE_RETRY(exp) ({ \
|
|
||||||
typeof (exp) _rc; \
|
|
||||||
do { \
|
|
||||||
_rc = (exp); \
|
|
||||||
} while (_rc == -1 && errno == EINTR); \
|
|
||||||
_rc; })
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif /* NATIVEHELPER_JNIHELP_H_ */
|
|
@ -1,99 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef JNI_CONSTANTS_H_included
|
|
||||||
#define JNI_CONSTANTS_H_included
|
|
||||||
|
|
||||||
#include "JNIHelp.h"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A cache to avoid calling FindClass at runtime.
|
|
||||||
*
|
|
||||||
* Class lookup is relatively expensive (2.5us on passion-eng at the time of writing), so we do
|
|
||||||
* all such lookups eagerly at startup. This means that code that never uses, say,
|
|
||||||
* java.util.zip.Deflater still has to pay for the lookup, but it means that on a device the cost
|
|
||||||
* is definitely paid during boot and amortized. A central cache also removes the temptation to
|
|
||||||
* dynamically call FindClass rather than add a small cache to each file that needs one. Another
|
|
||||||
* cost is that each class cached here requires a global reference, though in practice we save
|
|
||||||
* enough by not having a global reference for each file that uses a class such as java.lang.String
|
|
||||||
* which is used in several files.
|
|
||||||
*
|
|
||||||
* FindClass is still called in a couple of situations: when throwing exceptions, and in some of
|
|
||||||
* the serialization code. The former is clearly not a performance case, and we're currently
|
|
||||||
* assuming that neither is the latter.
|
|
||||||
*
|
|
||||||
* TODO: similar arguments hold for field and method IDs; we should cache them centrally too.
|
|
||||||
*/
|
|
||||||
struct JniConstants {
|
|
||||||
static void init(JNIEnv* env);
|
|
||||||
|
|
||||||
static jclass bidiRunClass;
|
|
||||||
static jclass bigDecimalClass;
|
|
||||||
static jclass booleanClass;
|
|
||||||
static jclass byteArrayClass;
|
|
||||||
static jclass byteClass;
|
|
||||||
static jclass calendarClass;
|
|
||||||
static jclass characterClass;
|
|
||||||
static jclass charsetICUClass;
|
|
||||||
static jclass constructorClass;
|
|
||||||
static jclass deflaterClass;
|
|
||||||
static jclass doubleClass;
|
|
||||||
static jclass errnoExceptionClass;
|
|
||||||
static jclass fieldClass;
|
|
||||||
static jclass fieldPositionIteratorClass;
|
|
||||||
static jclass fileDescriptorClass;
|
|
||||||
static jclass floatClass;
|
|
||||||
static jclass gaiExceptionClass;
|
|
||||||
static jclass inet6AddressClass;
|
|
||||||
static jclass inetAddressClass;
|
|
||||||
static jclass inetSocketAddressClass;
|
|
||||||
static jclass inetUnixAddressClass;
|
|
||||||
static jclass inflaterClass;
|
|
||||||
static jclass inputStreamClass;
|
|
||||||
static jclass integerClass;
|
|
||||||
static jclass localeDataClass;
|
|
||||||
static jclass longClass;
|
|
||||||
static jclass methodClass;
|
|
||||||
static jclass mutableIntClass;
|
|
||||||
static jclass mutableLongClass;
|
|
||||||
static jclass objectClass;
|
|
||||||
static jclass objectArrayClass;
|
|
||||||
static jclass outputStreamClass;
|
|
||||||
static jclass parsePositionClass;
|
|
||||||
static jclass patternSyntaxExceptionClass;
|
|
||||||
static jclass realToStringClass;
|
|
||||||
static jclass referenceClass;
|
|
||||||
static jclass shortClass;
|
|
||||||
static jclass socketClass;
|
|
||||||
static jclass socketImplClass;
|
|
||||||
static jclass stringClass;
|
|
||||||
static jclass structAddrinfoClass;
|
|
||||||
static jclass structFlockClass;
|
|
||||||
static jclass structGroupReqClass;
|
|
||||||
static jclass structLingerClass;
|
|
||||||
static jclass structPasswdClass;
|
|
||||||
static jclass structPollfdClass;
|
|
||||||
static jclass structStatClass;
|
|
||||||
static jclass structStatVfsClass;
|
|
||||||
static jclass structTimevalClass;
|
|
||||||
static jclass structUcredClass;
|
|
||||||
static jclass structUtsnameClass;
|
|
||||||
};
|
|
||||||
|
|
||||||
#define NATIVE_METHOD(className, functionName, signature) \
|
|
||||||
{ #functionName, signature, reinterpret_cast<void*>(className ## _ ## functionName) }
|
|
||||||
|
|
||||||
#endif // JNI_CONSTANTS_H_included
|
|
@ -1,63 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef SCOPED_LOCAL_REF_H_included
|
|
||||||
#define SCOPED_LOCAL_REF_H_included
|
|
||||||
|
|
||||||
#include "jni.h"
|
|
||||||
|
|
||||||
#include <stddef.h>
|
|
||||||
|
|
||||||
// A smart pointer that deletes a JNI local reference when it goes out of scope.
|
|
||||||
template<typename T>
|
|
||||||
class ScopedLocalRef {
|
|
||||||
public:
|
|
||||||
ScopedLocalRef(JNIEnv* env, T localRef) : mEnv(env), mLocalRef(localRef) {
|
|
||||||
}
|
|
||||||
|
|
||||||
~ScopedLocalRef() {
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset(T ptr = NULL) {
|
|
||||||
if (ptr != mLocalRef) {
|
|
||||||
if (mLocalRef != NULL) {
|
|
||||||
mEnv->DeleteLocalRef(mLocalRef);
|
|
||||||
}
|
|
||||||
mLocalRef = ptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
T release() __attribute__((warn_unused_result)) {
|
|
||||||
T localRef = mLocalRef;
|
|
||||||
mLocalRef = NULL;
|
|
||||||
return localRef;
|
|
||||||
}
|
|
||||||
|
|
||||||
T get() const {
|
|
||||||
return mLocalRef;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
JNIEnv* mEnv;
|
|
||||||
T mLocalRef;
|
|
||||||
|
|
||||||
// Disallow copy and assignment.
|
|
||||||
ScopedLocalRef(const ScopedLocalRef&);
|
|
||||||
void operator=(const ScopedLocalRef&);
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // SCOPED_LOCAL_REF_H_included
|
|
File diff suppressed because it is too large
Load Diff
@ -1,10 +0,0 @@
|
|||||||
# This file is automatically generated by Android Tools.
|
|
||||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
|
||||||
#
|
|
||||||
# This file must *NOT* be checked into Version Control Systems,
|
|
||||||
# as it contains information specific to your local configuration.
|
|
||||||
|
|
||||||
# location of the SDK. This is only used by Ant
|
|
||||||
# For customization when using a Version Control System, please read the
|
|
||||||
# header note.
|
|
||||||
sdk.dir=/home/dan/adt-bundle-linux-x86-20131030/sdk/
|
|
@ -1,20 +0,0 @@
|
|||||||
# To enable ProGuard in your project, edit project.properties
|
|
||||||
# to define the proguard.config property as described in that file.
|
|
||||||
#
|
|
||||||
# Add project specific ProGuard rules here.
|
|
||||||
# By default, the flags in this file are appended to flags specified
|
|
||||||
# in ${sdk.dir}/tools/proguard/proguard-android.txt
|
|
||||||
# You can edit the include path and order by changing the ProGuard
|
|
||||||
# include property in project.properties.
|
|
||||||
#
|
|
||||||
# For more details, see
|
|
||||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
|
||||||
|
|
||||||
# Add any project specific keep options here:
|
|
||||||
|
|
||||||
# If your project uses WebView with JS, uncomment the following
|
|
||||||
# and specify the fully qualified class name to the JavaScript interface
|
|
||||||
# class:
|
|
||||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
|
||||||
# public *;
|
|
||||||
#}
|
|
@ -1,14 +0,0 @@
|
|||||||
# This file is automatically generated by Android Tools.
|
|
||||||
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
|
||||||
#
|
|
||||||
# This file must be checked in Version Control Systems.
|
|
||||||
#
|
|
||||||
# To customize properties used by the Ant build system edit
|
|
||||||
# "ant.properties", and override values to adapt the script to your
|
|
||||||
# project structure.
|
|
||||||
#
|
|
||||||
# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home):
|
|
||||||
#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt
|
|
||||||
|
|
||||||
# Project target.
|
|
||||||
target=android-19
|
|
Binary file not shown.
Before Width: | Height: | Size: 9.2 KiB |
Binary file not shown.
Before Width: | Height: | Size: 2.7 KiB |
Binary file not shown.
Before Width: | Height: | Size: 5.1 KiB |
Binary file not shown.
Before Width: | Height: | Size: 14 KiB |
@ -1,36 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<ScrollView
|
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="fill_parent">
|
|
||||||
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="fill_parent"
|
|
||||||
>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="CustomSqlite Tests"
|
|
||||||
android:typeface="monospace"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Button
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="Run the tests"
|
|
||||||
android:onClick="run_the_tests"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/tv_widget"
|
|
||||||
android:layout_width="fill_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:text="<this text should be replaced by the test output>"
|
|
||||||
android:typeface="monospace"
|
|
||||||
/>
|
|
||||||
</LinearLayout>
|
|
||||||
</ScrollView>
|
|
||||||
|
|
@ -1,4 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<resources>
|
|
||||||
<string name="app_name">CustomSqlite</string>
|
|
||||||
</resources>
|
|
@ -1,416 +0,0 @@
|
|||||||
|
|
||||||
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 org.sqlite.database.sqlite.SQLiteOpenHelper;
|
|
||||||
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
/*
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void report_version(){
|
|
||||||
SQLiteDatabase db = null;
|
|
||||||
SQLiteStatement st;
|
|
||||||
String res;
|
|
||||||
|
|
||||||
db = SQLiteDatabase.openOrCreateDatabase(":memory:", null);
|
|
||||||
st = db.compileStatement("SELECT sqlite_version()");
|
|
||||||
res = st.simpleQueryForString();
|
|
||||||
|
|
||||||
myTV.append("SQLite version " + res + "\n\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void test_warning(String name, String warning){
|
|
||||||
myTV.append("WARNING:" + name + ": " + warning + "\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void test_result(String name, String res, String expected){
|
|
||||||
myTV.append(name + "... ");
|
|
||||||
myNTest++;
|
|
||||||
|
|
||||||
if( res.equals(expected) ){
|
|
||||||
myTV.append("ok\n");
|
|
||||||
} 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) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Test that a database connection may be accessed from a second thread.
|
|
||||||
*/
|
|
||||||
public void thread_test_2(){
|
|
||||||
SQLiteDatabase.deleteDatabase(DB_PATH);
|
|
||||||
final SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(DB_PATH, null);
|
|
||||||
|
|
||||||
db.execSQL("CREATE TABLE t1(x, y)");
|
|
||||||
db.execSQL("INSERT INTO t1 VALUES (1, 2), (3, 4)");
|
|
||||||
|
|
||||||
db.enableWriteAheadLogging();
|
|
||||||
db.beginTransactionNonExclusive();
|
|
||||||
db.execSQL("INSERT INTO t1 VALUES (5, 6)");
|
|
||||||
|
|
||||||
Thread t = new Thread( new Runnable() {
|
|
||||||
public void run() {
|
|
||||||
SQLiteStatement st = db.compileStatement("SELECT sum(x+y) FROM t1");
|
|
||||||
String res = st.simpleQueryForString();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
t.start();
|
|
||||||
String res = "concurrent";
|
|
||||||
|
|
||||||
int i;
|
|
||||||
for(i=0; i<20 && t.isAlive(); i++){
|
|
||||||
try { Thread.sleep(100); } catch(InterruptedException e) {}
|
|
||||||
}
|
|
||||||
if( t.isAlive() ){ res = "blocked"; }
|
|
||||||
|
|
||||||
db.endTransaction();
|
|
||||||
try { t.join(); } catch(InterruptedException e) {}
|
|
||||||
if( SQLiteDatabase.hasCodec() ){
|
|
||||||
test_result("thread_test_2", res, "blocked");
|
|
||||||
} else {
|
|
||||||
test_result("thread_test_2", res, "concurrent");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Use a Cursor to loop through the results of a SELECT query.
|
|
||||||
*/
|
|
||||||
public void csr_test_2() throws Exception {
|
|
||||||
SQLiteDatabase.deleteDatabase(DB_PATH);
|
|
||||||
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(DB_PATH, null);
|
|
||||||
String res = "";
|
|
||||||
String expect = "";
|
|
||||||
int i;
|
|
||||||
int nRow = 0;
|
|
||||||
|
|
||||||
db.execSQL("CREATE TABLE t1(x)");
|
|
||||||
db.execSQL("BEGIN");
|
|
||||||
for(i=0; i<1000; i++){
|
|
||||||
db.execSQL("INSERT INTO t1 VALUES ('one'), ('two'), ('three')");
|
|
||||||
expect += ".one.two.three";
|
|
||||||
}
|
|
||||||
db.execSQL("COMMIT");
|
|
||||||
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_2.1", res, expect);
|
|
||||||
|
|
||||||
db.execSQL("BEGIN");
|
|
||||||
for(i=0; i<1000; i++){
|
|
||||||
db.execSQL("INSERT INTO t1 VALUES (X'123456'), (X'789ABC'), (X'DEF012')");
|
|
||||||
db.execSQL("INSERT INTO t1 VALUES (45), (46), (47)");
|
|
||||||
db.execSQL("INSERT INTO t1 VALUES (8.1), (8.2), (8.3)");
|
|
||||||
db.execSQL("INSERT INTO t1 VALUES (NULL), (NULL), (NULL)");
|
|
||||||
}
|
|
||||||
db.execSQL("COMMIT");
|
|
||||||
|
|
||||||
c = db.rawQuery("SELECT x FROM t1", null);
|
|
||||||
if( c!=null ){
|
|
||||||
boolean bRes;
|
|
||||||
for(bRes=c.moveToFirst(); bRes; bRes=c.moveToNext()) nRow++;
|
|
||||||
}else{
|
|
||||||
test_warning("csr_test_1", "c==NULL");
|
|
||||||
}
|
|
||||||
test_result("csr_test_2.2", "" + nRow, "15000");
|
|
||||||
|
|
||||||
db.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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')");
|
|
||||||
|
|
||||||
res = string_from_t1_x(db);
|
|
||||||
test_result("csr_test_1.1", res, ".one.two.three");
|
|
||||||
|
|
||||||
db.close();
|
|
||||||
test_result("csr_test_1.2", db_is_encrypted(), "unencrypted");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void stmt_jrnl_test_1() throws Exception {
|
|
||||||
SQLiteDatabase.deleteDatabase(DB_PATH);
|
|
||||||
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(DB_PATH, null);
|
|
||||||
String res = "";
|
|
||||||
|
|
||||||
db.execSQL("CREATE TABLE t1(x, y UNIQUE)");
|
|
||||||
db.execSQL("BEGIN");
|
|
||||||
db.execSQL("INSERT INTO t1 VALUES(1, 1), (2, 2), (3, 3)");
|
|
||||||
db.execSQL("UPDATE t1 SET y=y+3");
|
|
||||||
db.execSQL("COMMIT");
|
|
||||||
db.close();
|
|
||||||
test_result("stmt_jrnl_test_1.1", "did not crash", "did not crash");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void supp_char_test_1() throws Exception {
|
|
||||||
SQLiteDatabase.deleteDatabase(DB_PATH);
|
|
||||||
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(DB_PATH, null);
|
|
||||||
String res = "";
|
|
||||||
String smiley = new String( Character.toChars(0x10000) );
|
|
||||||
|
|
||||||
db.execSQL("CREATE TABLE t1(x)");
|
|
||||||
db.execSQL("INSERT INTO t1 VALUES ('a" + smiley + "b')");
|
|
||||||
|
|
||||||
res = string_from_t1_x(db);
|
|
||||||
|
|
||||||
test_result("supp_char_test1." + smiley, res, ".a" + smiley + "b");
|
|
||||||
|
|
||||||
db.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
** If this is a SEE build, check that encrypted databases work.
|
|
||||||
*/
|
|
||||||
public void see_test_1() throws Exception {
|
|
||||||
if( !SQLiteDatabase.hasCodec() ) return;
|
|
||||||
|
|
||||||
SQLiteDatabase.deleteDatabase(DB_PATH);
|
|
||||||
String res = "";
|
|
||||||
|
|
||||||
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(DB_PATH, null);
|
|
||||||
db.execSQL("PRAGMA key = 'secretkey'");
|
|
||||||
|
|
||||||
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.openOrCreateDatabase(DB_PATH, null);
|
|
||||||
db.execSQL("PRAGMA key = 'secretkey'");
|
|
||||||
res = string_from_t1_x(db);
|
|
||||||
test_result("see_test_1.3", res, ".one.two.three");
|
|
||||||
db.close();
|
|
||||||
|
|
||||||
res = "unencrypted";
|
|
||||||
try {
|
|
||||||
db = SQLiteDatabase.openOrCreateDatabase(DB_PATH.getPath(), null);
|
|
||||||
string_from_t1_x(db);
|
|
||||||
} catch ( SQLiteDatabaseCorruptException e ){
|
|
||||||
res = "encrypted";
|
|
||||||
} finally {
|
|
||||||
db.close();
|
|
||||||
}
|
|
||||||
test_result("see_test_1.4", res, "encrypted");
|
|
||||||
|
|
||||||
res = "unencrypted";
|
|
||||||
try {
|
|
||||||
db = SQLiteDatabase.openOrCreateDatabase(DB_PATH.getPath(), null);
|
|
||||||
db.execSQL("PRAGMA key = 'otherkey'");
|
|
||||||
string_from_t1_x(db);
|
|
||||||
} catch ( SQLiteDatabaseCorruptException e ){
|
|
||||||
res = "encrypted";
|
|
||||||
} finally {
|
|
||||||
db.close();
|
|
||||||
}
|
|
||||||
test_result("see_test_1.5", res, "encrypted");
|
|
||||||
}
|
|
||||||
|
|
||||||
class MyHelper extends SQLiteOpenHelper {
|
|
||||||
public MyHelper(Context ctx){
|
|
||||||
super(ctx, DB_PATH.getPath(), null, 1);
|
|
||||||
}
|
|
||||||
public void onConfigure(SQLiteDatabase db){
|
|
||||||
db.execSQL("PRAGMA key = 'secret'");
|
|
||||||
}
|
|
||||||
public void onCreate(SQLiteDatabase db){
|
|
||||||
db.execSQL("CREATE TABLE t1(x)");
|
|
||||||
}
|
|
||||||
public void onUpgrade(SQLiteDatabase db, int iOld, int iNew){
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
** Check that SQLiteOpenHelper works.
|
|
||||||
*/
|
|
||||||
public void helper_test_1() throws Exception {
|
|
||||||
/* SQLiteDatabase.deleteDatabase(DB_PATH); */
|
|
||||||
|
|
||||||
MyHelper helper = new MyHelper(this);
|
|
||||||
SQLiteDatabase db = helper.getWritableDatabase();
|
|
||||||
db.execSQL("INSERT INTO t1 VALUES ('x'), ('y'), ('z')");
|
|
||||||
|
|
||||||
String res = string_from_t1_x(db);
|
|
||||||
test_result("helper.1", res, ".x.y.z");
|
|
||||||
|
|
||||||
helper.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
** If this is a SEE build, check that SQLiteOpenHelper still works.
|
|
||||||
*/
|
|
||||||
public void see_test_2() throws Exception {
|
|
||||||
if( !SQLiteDatabase.hasCodec() ) return;
|
|
||||||
SQLiteDatabase.deleteDatabase(DB_PATH);
|
|
||||||
|
|
||||||
MyHelper helper = new MyHelper(this);
|
|
||||||
SQLiteDatabase db = helper.getWritableDatabase();
|
|
||||||
db.execSQL("INSERT INTO t1 VALUES ('x'), ('y'), ('z')");
|
|
||||||
|
|
||||||
String res = string_from_t1_x(db);
|
|
||||||
test_result("see_test_2.1", res, ".x.y.z");
|
|
||||||
test_result("see_test_2.2", db_is_encrypted(), "encrypted");
|
|
||||||
|
|
||||||
helper.close();
|
|
||||||
helper = new MyHelper(this);
|
|
||||||
db = helper.getReadableDatabase();
|
|
||||||
test_result("see_test_2.3", res, ".x.y.z");
|
|
||||||
|
|
||||||
db = helper.getWritableDatabase();
|
|
||||||
test_result("see_test_2.4", res, ".x.y.z");
|
|
||||||
|
|
||||||
test_result("see_test_2.5", db_is_encrypted(), "encrypted");
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run_the_tests(View view){
|
|
||||||
System.loadLibrary("sqliteX");
|
|
||||||
DB_PATH = getApplicationContext().getDatabasePath("test.db");
|
|
||||||
DB_PATH.getParentFile().mkdirs();
|
|
||||||
|
|
||||||
myTV.setText("");
|
|
||||||
myNErr = 0;
|
|
||||||
myNTest = 0;
|
|
||||||
|
|
||||||
try {
|
|
||||||
report_version();
|
|
||||||
helper_test_1();
|
|
||||||
supp_char_test_1();
|
|
||||||
csr_test_1();
|
|
||||||
csr_test_2();
|
|
||||||
thread_test_1();
|
|
||||||
thread_test_2();
|
|
||||||
see_test_1();
|
|
||||||
see_test_2();
|
|
||||||
stmt_jrnl_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");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database;
|
|
||||||
|
|
||||||
import org.sqlite.database.sqlite.SQLiteDatabase;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An interface to let the apps define the actions to take when the following errors are detected
|
|
||||||
* database corruption
|
|
||||||
*/
|
|
||||||
public interface DatabaseErrorHandler {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* defines the method to be invoked when database corruption is detected.
|
|
||||||
* @param dbObj the {@link SQLiteDatabase} object representing the database on which corruption
|
|
||||||
* is detected.
|
|
||||||
*/
|
|
||||||
void onCorruption(SQLiteDatabase dbObj);
|
|
||||||
}
|
|
@ -1,117 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database;
|
|
||||||
|
|
||||||
import java.io.File;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.sqlite.database.sqlite.SQLiteDatabase;
|
|
||||||
import org.sqlite.database.sqlite.SQLiteException;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.util.Pair;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default class used to define the actions to take when the database corruption is reported
|
|
||||||
* by sqlite.
|
|
||||||
* <p>
|
|
||||||
* An application can specify an implementation of {@link DatabaseErrorHandler} on the
|
|
||||||
* following:
|
|
||||||
* <ul>
|
|
||||||
* <li>{@link SQLiteDatabase#openOrCreateDatabase(String,
|
|
||||||
* org.sqlite.database.sqlite.SQLiteDatabase.CursorFactory, DatabaseErrorHandler)}</li>
|
|
||||||
* <li>{@link SQLiteDatabase#openDatabase(String,
|
|
||||||
* org.sqlite.database.sqlite.SQLiteDatabase.CursorFactory, int, DatabaseErrorHandler)}</li>
|
|
||||||
* </ul>
|
|
||||||
* The specified {@link DatabaseErrorHandler} is used to handle database corruption errors, if they
|
|
||||||
* occur.
|
|
||||||
* <p>
|
|
||||||
* If null is specified for DatabaeErrorHandler param in the above calls, then this class is used
|
|
||||||
* as the default {@link DatabaseErrorHandler}.
|
|
||||||
*/
|
|
||||||
public final class DefaultDatabaseErrorHandler implements DatabaseErrorHandler {
|
|
||||||
|
|
||||||
private static final String TAG = "DefaultDatabaseErrorHandler";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* defines the default method to be invoked when database corruption is detected.
|
|
||||||
* @param dbObj the {@link SQLiteDatabase} object representing the database on which corruption
|
|
||||||
* is detected.
|
|
||||||
*/
|
|
||||||
public void onCorruption(SQLiteDatabase dbObj) {
|
|
||||||
Log.e(TAG, "Corruption reported by sqlite on database: " + dbObj.getPath());
|
|
||||||
|
|
||||||
// If this is a SEE build, do not delete any database files.
|
|
||||||
//
|
|
||||||
if( SQLiteDatabase.hasCodec() ) return;
|
|
||||||
|
|
||||||
// is the corruption detected even before database could be 'opened'?
|
|
||||||
if (!dbObj.isOpen()) {
|
|
||||||
// database files are not even openable. delete this database file.
|
|
||||||
// NOTE if the database has attached databases, then any of them could be corrupt.
|
|
||||||
// and not deleting all of them could cause corrupted database file to remain and
|
|
||||||
// make the application crash on database open operation. To avoid this problem,
|
|
||||||
// the application should provide its own {@link DatabaseErrorHandler} impl class
|
|
||||||
// to delete ALL files of the database (including the attached databases).
|
|
||||||
deleteDatabaseFile(dbObj.getPath());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<Pair<String, String>> attachedDbs = null;
|
|
||||||
try {
|
|
||||||
// Close the database, which will cause subsequent operations to fail.
|
|
||||||
// before that, get the attached database list first.
|
|
||||||
try {
|
|
||||||
attachedDbs = dbObj.getAttachedDbs();
|
|
||||||
} catch (SQLiteException e) {
|
|
||||||
/* ignore */
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
dbObj.close();
|
|
||||||
} catch (SQLiteException e) {
|
|
||||||
/* ignore */
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
// Delete all files of this corrupt database and/or attached databases
|
|
||||||
if (attachedDbs != null) {
|
|
||||||
for (Pair<String, String> p : attachedDbs) {
|
|
||||||
deleteDatabaseFile(p.second);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// attachedDbs = null is possible when the database is so corrupt that even
|
|
||||||
// "PRAGMA database_list;" also fails. delete the main database file
|
|
||||||
deleteDatabaseFile(dbObj.getPath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void deleteDatabaseFile(String fileName) {
|
|
||||||
if (fileName.equalsIgnoreCase(":memory:") || fileName.trim().length() == 0) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Log.e(TAG, "deleting the database file: " + fileName);
|
|
||||||
try {
|
|
||||||
SQLiteDatabase.deleteDatabase(new File(fileName));
|
|
||||||
} catch (Exception e) {
|
|
||||||
/* print warning and ignore exception */
|
|
||||||
Log.w(TAG, "delete failed: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,176 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2006 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.database.CursorWindow;
|
|
||||||
|
|
||||||
/* import org.apache.commons.codec.binary.Hex; */
|
|
||||||
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.OperationApplicationException;
|
|
||||||
import org.sqlite.database.sqlite.SQLiteAbortException;
|
|
||||||
import org.sqlite.database.sqlite.SQLiteConstraintException;
|
|
||||||
import org.sqlite.database.sqlite.SQLiteDatabase;
|
|
||||||
import org.sqlite.database.sqlite.SQLiteDatabaseCorruptException;
|
|
||||||
import org.sqlite.database.sqlite.SQLiteDiskIOException;
|
|
||||||
import org.sqlite.database.sqlite.SQLiteException;
|
|
||||||
import org.sqlite.database.sqlite.SQLiteFullException;
|
|
||||||
import org.sqlite.database.sqlite.SQLiteProgram;
|
|
||||||
import org.sqlite.database.sqlite.SQLiteStatement;
|
|
||||||
import android.os.OperationCanceledException;
|
|
||||||
import android.os.Parcel;
|
|
||||||
import android.os.ParcelFileDescriptor;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.PrintStream;
|
|
||||||
import java.text.Collator;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Static utility methods for dealing with databases and {@link Cursor}s.
|
|
||||||
*/
|
|
||||||
public class ExtraUtils {
|
|
||||||
private static final String TAG = "ExtraUtils";
|
|
||||||
|
|
||||||
private static final boolean DEBUG = false;
|
|
||||||
|
|
||||||
/** One of the values returned by {@link #getSqlStatementType(String)}. */
|
|
||||||
public static final int STATEMENT_SELECT = 1;
|
|
||||||
/** One of the values returned by {@link #getSqlStatementType(String)}. */
|
|
||||||
public static final int STATEMENT_UPDATE = 2;
|
|
||||||
/** One of the values returned by {@link #getSqlStatementType(String)}. */
|
|
||||||
public static final int STATEMENT_ATTACH = 3;
|
|
||||||
/** One of the values returned by {@link #getSqlStatementType(String)}. */
|
|
||||||
public static final int STATEMENT_BEGIN = 4;
|
|
||||||
/** One of the values returned by {@link #getSqlStatementType(String)}. */
|
|
||||||
public static final int STATEMENT_COMMIT = 5;
|
|
||||||
/** One of the values returned by {@link #getSqlStatementType(String)}. */
|
|
||||||
public static final int STATEMENT_ABORT = 6;
|
|
||||||
/** One of the values returned by {@link #getSqlStatementType(String)}. */
|
|
||||||
public static final int STATEMENT_PRAGMA = 7;
|
|
||||||
/** One of the values returned by {@link #getSqlStatementType(String)}. */
|
|
||||||
public static final int STATEMENT_DDL = 8;
|
|
||||||
/** One of the values returned by {@link #getSqlStatementType(String)}. */
|
|
||||||
public static final int STATEMENT_UNPREPARED = 9;
|
|
||||||
/** One of the values returned by {@link #getSqlStatementType(String)}. */
|
|
||||||
public static final int STATEMENT_OTHER = 99;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns column index of "_id" column, or -1 if not found.
|
|
||||||
*/
|
|
||||||
public static int findRowIdColumnIndex(String[] columnNames) {
|
|
||||||
int length = columnNames.length;
|
|
||||||
for (int i = 0; i < length; i++) {
|
|
||||||
if (columnNames[i].equals("_id")) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Picks a start position for {@link Cursor#fillWindow} such that the
|
|
||||||
* window will contain the requested row and a useful range of rows
|
|
||||||
* around it.
|
|
||||||
*
|
|
||||||
* When the data set is too large to fit in a cursor window, seeking the
|
|
||||||
* cursor can become a very expensive operation since we have to run the
|
|
||||||
* query again when we move outside the bounds of the current window.
|
|
||||||
*
|
|
||||||
* We try to choose a start position for the cursor window such that
|
|
||||||
* 1/3 of the window's capacity is used to hold rows before the requested
|
|
||||||
* position and 2/3 of the window's capacity is used to hold rows after the
|
|
||||||
* requested position.
|
|
||||||
*
|
|
||||||
* @param cursorPosition The row index of the row we want to get.
|
|
||||||
* @param cursorWindowCapacity The estimated number of rows that can fit in
|
|
||||||
* a cursor window, or 0 if unknown.
|
|
||||||
* @return The recommended start position, always less than or equal to
|
|
||||||
* the requested row.
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public static int cursorPickFillWindowStartPosition(
|
|
||||||
int cursorPosition, int cursorWindowCapacity) {
|
|
||||||
return Math.max(cursorPosition - cursorWindowCapacity / 3, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns data type of the given object's value.
|
|
||||||
*<p>
|
|
||||||
* Returned values are
|
|
||||||
* <ul>
|
|
||||||
* <li>{@link Cursor#FIELD_TYPE_NULL}</li>
|
|
||||||
* <li>{@link Cursor#FIELD_TYPE_INTEGER}</li>
|
|
||||||
* <li>{@link Cursor#FIELD_TYPE_FLOAT}</li>
|
|
||||||
* <li>{@link Cursor#FIELD_TYPE_STRING}</li>
|
|
||||||
* <li>{@link Cursor#FIELD_TYPE_BLOB}</li>
|
|
||||||
*</ul>
|
|
||||||
*</p>
|
|
||||||
*
|
|
||||||
* @param obj the object whose value type is to be returned
|
|
||||||
* @return object value type
|
|
||||||
*/
|
|
||||||
public static int getTypeOfObject(Object obj) {
|
|
||||||
if (obj == null) {
|
|
||||||
return Cursor.FIELD_TYPE_NULL;
|
|
||||||
} else if (obj instanceof byte[]) {
|
|
||||||
return Cursor.FIELD_TYPE_BLOB;
|
|
||||||
} else if (obj instanceof Float || obj instanceof Double) {
|
|
||||||
return Cursor.FIELD_TYPE_FLOAT;
|
|
||||||
} else if (obj instanceof Long || obj instanceof Integer
|
|
||||||
|| obj instanceof Short || obj instanceof Byte) {
|
|
||||||
return Cursor.FIELD_TYPE_INTEGER;
|
|
||||||
} else {
|
|
||||||
return Cursor.FIELD_TYPE_STRING;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility method to run the query on the db and return the value in the
|
|
||||||
* first column of the first row.
|
|
||||||
*/
|
|
||||||
public static long longForQuery(
|
|
||||||
SQLiteDatabase db, String query, String[] selectionArgs
|
|
||||||
) {
|
|
||||||
SQLiteStatement prog = db.compileStatement(query);
|
|
||||||
try {
|
|
||||||
return longForQuery(prog, selectionArgs);
|
|
||||||
} finally {
|
|
||||||
prog.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Utility method to run the pre-compiled query and return the value in the
|
|
||||||
* first column of the first row.
|
|
||||||
*/
|
|
||||||
public static long longForQuery(
|
|
||||||
SQLiteStatement prog, String[] selectionArgs
|
|
||||||
) {
|
|
||||||
prog.bindAllArgsAsStrings(selectionArgs);
|
|
||||||
return prog.simpleQueryForLong();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2006 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception that indicates there was an error with SQL parsing or execution.
|
|
||||||
*/
|
|
||||||
public class SQLException extends RuntimeException {
|
|
||||||
public SQLException() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public SQLException(String error) {
|
|
||||||
super(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SQLException(String error, Throwable cause) {
|
|
||||||
super(error, cause);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,235 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* CloseGuard is a mechanism for flagging implicit finalizer cleanup of
|
|
||||||
* resources that should have been cleaned up by explicit close
|
|
||||||
* methods (aka "explicit termination methods" in Effective Java).
|
|
||||||
* <p>
|
|
||||||
* A simple example: <pre> {@code
|
|
||||||
* class Foo {
|
|
||||||
*
|
|
||||||
* private final CloseGuard guard = CloseGuard.get();
|
|
||||||
*
|
|
||||||
* ...
|
|
||||||
*
|
|
||||||
* public Foo() {
|
|
||||||
* ...;
|
|
||||||
* guard.open("cleanup");
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* public void cleanup() {
|
|
||||||
* guard.close();
|
|
||||||
* ...;
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* protected void finalize() throws Throwable {
|
|
||||||
* try {
|
|
||||||
* if (guard != null) {
|
|
||||||
* guard.warnIfOpen();
|
|
||||||
* }
|
|
||||||
* cleanup();
|
|
||||||
* } finally {
|
|
||||||
* super.finalize();
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }</pre>
|
|
||||||
*
|
|
||||||
* In usage where the resource to be explicitly cleaned up are
|
|
||||||
* allocated after object construction, CloseGuard protection can
|
|
||||||
* be deferred. For example: <pre> {@code
|
|
||||||
* class Bar {
|
|
||||||
*
|
|
||||||
* private final CloseGuard guard = CloseGuard.get();
|
|
||||||
*
|
|
||||||
* ...
|
|
||||||
*
|
|
||||||
* public Bar() {
|
|
||||||
* ...;
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* public void connect() {
|
|
||||||
* ...;
|
|
||||||
* guard.open("cleanup");
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* public void cleanup() {
|
|
||||||
* guard.close();
|
|
||||||
* ...;
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* protected void finalize() throws Throwable {
|
|
||||||
* try {
|
|
||||||
* if (guard != null) {
|
|
||||||
* guard.warnIfOpen();
|
|
||||||
* }
|
|
||||||
* cleanup();
|
|
||||||
* } finally {
|
|
||||||
* super.finalize();
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* }</pre>
|
|
||||||
*
|
|
||||||
* When used in a constructor calls to {@code open} should occur at
|
|
||||||
* the end of the constructor since an exception that would cause
|
|
||||||
* abrupt termination of the constructor will mean that the user will
|
|
||||||
* not have a reference to the object to cleanup explicitly. When used
|
|
||||||
* in a method, the call to {@code open} should occur just after
|
|
||||||
* resource acquisition.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
*
|
|
||||||
* Note that the null check on {@code guard} in the finalizer is to
|
|
||||||
* cover cases where a constructor throws an exception causing the
|
|
||||||
* {@code guard} to be uninitialized.
|
|
||||||
*
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public final class CloseGuard {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instance used when CloseGuard is disabled to avoid allocation.
|
|
||||||
*/
|
|
||||||
private static final CloseGuard NOOP = new CloseGuard();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enabled by default so we can catch issues early in VM startup.
|
|
||||||
* Note, however, that Android disables this early in its startup,
|
|
||||||
* but enables it with DropBoxing for system apps on debug builds.
|
|
||||||
*/
|
|
||||||
private static volatile boolean ENABLED = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Hook for customizing how CloseGuard issues are reported.
|
|
||||||
*/
|
|
||||||
private static volatile Reporter REPORTER = new DefaultReporter();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a CloseGuard instance. If CloseGuard is enabled, {@code
|
|
||||||
* #open(String)} can be used to set up the instance to warn on
|
|
||||||
* failure to close. If CloseGuard is disabled, a non-null no-op
|
|
||||||
* instance is returned.
|
|
||||||
*/
|
|
||||||
public static CloseGuard get() {
|
|
||||||
if (!ENABLED) {
|
|
||||||
return NOOP;
|
|
||||||
}
|
|
||||||
return new CloseGuard();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to enable or disable CloseGuard. Note that CloseGuard only
|
|
||||||
* warns if it is enabled for both allocation and finalization.
|
|
||||||
*/
|
|
||||||
public static void setEnabled(boolean enabled) {
|
|
||||||
ENABLED = enabled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Used to replace default Reporter used to warn of CloseGuard
|
|
||||||
* violations. Must be non-null.
|
|
||||||
*/
|
|
||||||
public static void setReporter(Reporter reporter) {
|
|
||||||
if (reporter == null) {
|
|
||||||
throw new NullPointerException("reporter == null");
|
|
||||||
}
|
|
||||||
REPORTER = reporter;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns non-null CloseGuard.Reporter.
|
|
||||||
*/
|
|
||||||
public static Reporter getReporter() {
|
|
||||||
return REPORTER;
|
|
||||||
}
|
|
||||||
|
|
||||||
private CloseGuard() {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If CloseGuard is enabled, {@code open} initializes the instance
|
|
||||||
* with a warning that the caller should have explicitly called the
|
|
||||||
* {@code closer} method instead of relying on finalization.
|
|
||||||
*
|
|
||||||
* @param closer non-null name of explicit termination method
|
|
||||||
* @throws NullPointerException if closer is null, regardless of
|
|
||||||
* whether or not CloseGuard is enabled
|
|
||||||
*/
|
|
||||||
public void open(String closer) {
|
|
||||||
// always perform the check for valid API usage...
|
|
||||||
if (closer == null) {
|
|
||||||
throw new NullPointerException("closer == null");
|
|
||||||
}
|
|
||||||
// ...but avoid allocating an allocationSite if disabled
|
|
||||||
if (this == NOOP || !ENABLED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String message = "Explicit termination method '" + closer + "' not called";
|
|
||||||
allocationSite = new Throwable(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Throwable allocationSite;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Marks this CloseGuard instance as closed to avoid warnings on
|
|
||||||
* finalization.
|
|
||||||
*/
|
|
||||||
public void close() {
|
|
||||||
allocationSite = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* If CloseGuard is enabled, logs a warning if the caller did not
|
|
||||||
* properly cleanup by calling an explicit close method
|
|
||||||
* before finalization. If CloseGuard is disabled, no action is
|
|
||||||
* performed.
|
|
||||||
*/
|
|
||||||
public void warnIfOpen() {
|
|
||||||
if (allocationSite == null || !ENABLED) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
String message =
|
|
||||||
("A resource was acquired at attached stack trace but never released. "
|
|
||||||
+ "See java.io.Closeable for information on avoiding resource leaks.");
|
|
||||||
|
|
||||||
REPORTER.report(message, allocationSite);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface to allow customization of reporting behavior.
|
|
||||||
*/
|
|
||||||
public static interface Reporter {
|
|
||||||
public void report (String message, Throwable allocationSite);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default Reporter which reports CloseGuard violations to the log.
|
|
||||||
*/
|
|
||||||
private static final class DefaultReporter implements Reporter {
|
|
||||||
@Override public void report (String message, Throwable allocationSite) {
|
|
||||||
Log.w(message, allocationSite);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2006 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception that indicates that garbage-collector is finalizing a database object
|
|
||||||
* that is not explicitly closed
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public class DatabaseObjectNotClosedException extends RuntimeException {
|
|
||||||
private static final String s = "Application did not close the cursor or database object " +
|
|
||||||
"that was opened here";
|
|
||||||
|
|
||||||
public DatabaseObjectNotClosedException() {
|
|
||||||
super(s);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,34 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2008 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception that indicates that the SQLite program was aborted.
|
|
||||||
* This can happen either through a call to ABORT in a trigger,
|
|
||||||
* or as the result of using the ABORT conflict clause.
|
|
||||||
*/
|
|
||||||
public class SQLiteAbortException extends SQLiteException {
|
|
||||||
public SQLiteAbortException() {}
|
|
||||||
|
|
||||||
public SQLiteAbortException(String error) {
|
|
||||||
super(error);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This exception class is used when sqlite can't access the database file
|
|
||||||
* due to lack of permissions on the file.
|
|
||||||
*/
|
|
||||||
public class SQLiteAccessPermException extends SQLiteException {
|
|
||||||
public SQLiteAccessPermException() {}
|
|
||||||
|
|
||||||
public SQLiteAccessPermException(String error) {
|
|
||||||
super(error);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Thrown if the the bind or column parameter index is out of range
|
|
||||||
*/
|
|
||||||
public class SQLiteBindOrColumnIndexOutOfRangeException extends SQLiteException {
|
|
||||||
public SQLiteBindOrColumnIndexOutOfRangeException() {}
|
|
||||||
|
|
||||||
public SQLiteBindOrColumnIndexOutOfRangeException(String error) {
|
|
||||||
super(error);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
public class SQLiteBlobTooBigException extends SQLiteException {
|
|
||||||
public SQLiteBlobTooBigException() {}
|
|
||||||
|
|
||||||
public SQLiteBlobTooBigException(String error) {
|
|
||||||
super(error);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
public class SQLiteCantOpenDatabaseException extends SQLiteException {
|
|
||||||
public SQLiteCantOpenDatabaseException() {}
|
|
||||||
|
|
||||||
public SQLiteCantOpenDatabaseException(String error) {
|
|
||||||
super(error);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,112 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2007 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
import java.io.Closeable;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An object created from a SQLiteDatabase that can be closed.
|
|
||||||
*
|
|
||||||
* This class implements a primitive reference counting scheme for database objects.
|
|
||||||
*/
|
|
||||||
public abstract class SQLiteClosable implements Closeable {
|
|
||||||
private int mReferenceCount = 1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the last reference to the object was released by
|
|
||||||
* a call to {@link #releaseReference()} or {@link #close()}.
|
|
||||||
*/
|
|
||||||
protected abstract void onAllReferencesReleased();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the last reference to the object was released by
|
|
||||||
* a call to {@link #releaseReferenceFromContainer()}.
|
|
||||||
*
|
|
||||||
* @deprecated Do not use.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
protected void onAllReferencesReleasedFromContainer() {
|
|
||||||
onAllReferencesReleased();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Acquires a reference to the object.
|
|
||||||
*
|
|
||||||
* @throws IllegalStateException if the last reference to the object has already
|
|
||||||
* been released.
|
|
||||||
*/
|
|
||||||
public void acquireReference() {
|
|
||||||
synchronized(this) {
|
|
||||||
if (mReferenceCount <= 0) {
|
|
||||||
throw new IllegalStateException(
|
|
||||||
"attempt to re-open an already-closed object: " + this);
|
|
||||||
}
|
|
||||||
mReferenceCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Releases a reference to the object, closing the object if the last reference
|
|
||||||
* was released.
|
|
||||||
*
|
|
||||||
* @see #onAllReferencesReleased()
|
|
||||||
*/
|
|
||||||
public void releaseReference() {
|
|
||||||
boolean refCountIsZero = false;
|
|
||||||
synchronized(this) {
|
|
||||||
refCountIsZero = --mReferenceCount == 0;
|
|
||||||
}
|
|
||||||
if (refCountIsZero) {
|
|
||||||
onAllReferencesReleased();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Releases a reference to the object that was owned by the container of the object,
|
|
||||||
* closing the object if the last reference was released.
|
|
||||||
*
|
|
||||||
* @see #onAllReferencesReleasedFromContainer()
|
|
||||||
* @deprecated Do not use.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public void releaseReferenceFromContainer() {
|
|
||||||
boolean refCountIsZero = false;
|
|
||||||
synchronized(this) {
|
|
||||||
refCountIsZero = --mReferenceCount == 0;
|
|
||||||
}
|
|
||||||
if (refCountIsZero) {
|
|
||||||
onAllReferencesReleasedFromContainer();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Releases a reference to the object, closing the object if the last reference
|
|
||||||
* was released.
|
|
||||||
*
|
|
||||||
* Calling this method is equivalent to calling {@link #releaseReference}.
|
|
||||||
*
|
|
||||||
* @see #releaseReference()
|
|
||||||
* @see #onAllReferencesReleased()
|
|
||||||
*/
|
|
||||||
public void close() {
|
|
||||||
releaseReference();
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2008 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception that indicates that an integrity constraint was violated.
|
|
||||||
*/
|
|
||||||
public class SQLiteConstraintException extends SQLiteException {
|
|
||||||
public SQLiteConstraintException() {}
|
|
||||||
|
|
||||||
public SQLiteConstraintException(String error) {
|
|
||||||
super(error);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,318 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2006 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
import org.sqlite.database.ExtraUtils;
|
|
||||||
|
|
||||||
import android.database.AbstractWindowedCursor;
|
|
||||||
import android.database.CursorWindow;
|
|
||||||
|
|
||||||
import android.os.StrictMode;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A Cursor implementation that exposes results from a query on a
|
|
||||||
* {@link SQLiteDatabase}.
|
|
||||||
*
|
|
||||||
* SQLiteCursor is not internally synchronized so code using a SQLiteCursor from multiple
|
|
||||||
* threads should perform its own synchronization when using the SQLiteCursor.
|
|
||||||
*/
|
|
||||||
public class SQLiteCursor extends AbstractWindowedCursor {
|
|
||||||
static final String TAG = "SQLiteCursor";
|
|
||||||
static final int NO_COUNT = -1;
|
|
||||||
|
|
||||||
/** The name of the table to edit */
|
|
||||||
private final String mEditTable;
|
|
||||||
|
|
||||||
/** The names of the columns in the rows */
|
|
||||||
private final String[] mColumns;
|
|
||||||
|
|
||||||
/** The query object for the cursor */
|
|
||||||
private final SQLiteQuery mQuery;
|
|
||||||
|
|
||||||
/** The compiled query this cursor came from */
|
|
||||||
private final SQLiteCursorDriver mDriver;
|
|
||||||
|
|
||||||
/** The number of rows in the cursor */
|
|
||||||
private int mCount = NO_COUNT;
|
|
||||||
|
|
||||||
/** The number of rows that can fit in the cursor window, 0 if unknown */
|
|
||||||
private int mCursorWindowCapacity;
|
|
||||||
|
|
||||||
/** A mapping of column names to column indices, to speed up lookups */
|
|
||||||
private Map<String, Integer> mColumnNameMap;
|
|
||||||
|
|
||||||
/** Used to find out where a cursor was allocated in case it never got released. */
|
|
||||||
private final Throwable mStackTrace;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute a query and provide access to its result set through a Cursor
|
|
||||||
* interface. For a query such as: {@code SELECT name, birth, phone FROM
|
|
||||||
* myTable WHERE ... LIMIT 1,20 ORDER BY...} the column names (name, birth,
|
|
||||||
* phone) would be in the projection argument and everything from
|
|
||||||
* {@code FROM} onward would be in the params argument.
|
|
||||||
*
|
|
||||||
* @param db a reference to a Database object that is already constructed
|
|
||||||
* and opened. This param is not used any longer
|
|
||||||
* @param editTable the name of the table used for this query
|
|
||||||
* @param query the rest of the query terms
|
|
||||||
* cursor is finalized
|
|
||||||
* @deprecated use {@link #SQLiteCursor(SQLiteCursorDriver, String, SQLiteQuery)} instead
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public SQLiteCursor(SQLiteDatabase db, SQLiteCursorDriver driver,
|
|
||||||
String editTable, SQLiteQuery query) {
|
|
||||||
this(driver, editTable, query);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute a query and provide access to its result set through a Cursor
|
|
||||||
* interface. For a query such as: {@code SELECT name, birth, phone FROM
|
|
||||||
* myTable WHERE ... LIMIT 1,20 ORDER BY...} the column names (name, birth,
|
|
||||||
* phone) would be in the projection argument and everything from
|
|
||||||
* {@code FROM} onward would be in the params argument.
|
|
||||||
*
|
|
||||||
* @param editTable the name of the table used for this query
|
|
||||||
* @param query the {@link SQLiteQuery} object associated with this cursor object.
|
|
||||||
*/
|
|
||||||
public SQLiteCursor(SQLiteCursorDriver driver, String editTable, SQLiteQuery query) {
|
|
||||||
if (query == null) {
|
|
||||||
throw new IllegalArgumentException("query object cannot be null");
|
|
||||||
}
|
|
||||||
if (/* StrictMode.vmSqliteObjectLeaksEnabled() */ false ) {
|
|
||||||
mStackTrace = new DatabaseObjectNotClosedException().fillInStackTrace();
|
|
||||||
} else {
|
|
||||||
mStackTrace = null;
|
|
||||||
}
|
|
||||||
mDriver = driver;
|
|
||||||
mEditTable = editTable;
|
|
||||||
mColumnNameMap = null;
|
|
||||||
mQuery = query;
|
|
||||||
|
|
||||||
mColumns = query.getColumnNames();
|
|
||||||
//mRowIdColumnIndex = ExtraUtils.findRowIdColumnIndex(mColumns);
|
|
||||||
|
|
||||||
// try {
|
|
||||||
// Field field = AbstractCursor.class.getDeclaredField("mRowIdColumnIndex");
|
|
||||||
// field.setInt(this, ExtraUtils.findRowIdColumnIndex(mColumns));
|
|
||||||
// } catch (NoSuchFieldException nfe) {
|
|
||||||
// ;//loaded in system with api level 23 or later
|
|
||||||
// } catch (IllegalAccessException e) {
|
|
||||||
// ;//iae
|
|
||||||
// } catch (IllegalArgumentException e) {
|
|
||||||
// ;//iae
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the database that this cursor is associated with.
|
|
||||||
* @return the SQLiteDatabase that this cursor is associated with.
|
|
||||||
*/
|
|
||||||
public SQLiteDatabase getDatabase() {
|
|
||||||
return mQuery.getDatabase();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onMove(int oldPosition, int newPosition) {
|
|
||||||
// Make sure the row at newPosition is present in the window
|
|
||||||
if (mWindow == null || newPosition < mWindow.getStartPosition() ||
|
|
||||||
newPosition >= (mWindow.getStartPosition() + mWindow.getNumRows())) {
|
|
||||||
fillWindow(newPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getCount() {
|
|
||||||
if (mCount == NO_COUNT) {
|
|
||||||
fillWindow(0);
|
|
||||||
}
|
|
||||||
return mCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
** The AbstractWindowClass contains protected methods clearOrCreateWindow() and
|
|
||||||
** closeWindow(), which are used by the android.database.sqlite.* version of this
|
|
||||||
** class. But, since they are marked with "@hide", the following replacement
|
|
||||||
** versions are required.
|
|
||||||
*/
|
|
||||||
private void awc_clearOrCreateWindow(String name){
|
|
||||||
CursorWindow win = getWindow();
|
|
||||||
if( win==null ){
|
|
||||||
win = new CursorWindow(name);
|
|
||||||
setWindow(win);
|
|
||||||
}else{
|
|
||||||
win.clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private void awc_closeWindow(){
|
|
||||||
setWindow(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void fillWindow(int requiredPos) {
|
|
||||||
awc_clearOrCreateWindow(getDatabase().getPath());
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (mCount == NO_COUNT) {
|
|
||||||
int startPos = ExtraUtils.cursorPickFillWindowStartPosition(requiredPos, 0);
|
|
||||||
mCount = mQuery.fillWindow(mWindow, startPos, requiredPos, true);
|
|
||||||
mCursorWindowCapacity = mWindow.getNumRows();
|
|
||||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
|
||||||
Log.d(TAG, "received count(*) from native_fill_window: " + mCount);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
int startPos = ExtraUtils.cursorPickFillWindowStartPosition(requiredPos,
|
|
||||||
mCursorWindowCapacity);
|
|
||||||
mQuery.fillWindow(mWindow, startPos, requiredPos, false);
|
|
||||||
}
|
|
||||||
} catch (RuntimeException ex) {
|
|
||||||
// Close the cursor window if the query failed and therefore will
|
|
||||||
// not produce any results. This helps to avoid accidentally leaking
|
|
||||||
// the cursor window if the client does not correctly handle exceptions
|
|
||||||
// and fails to close the cursor.
|
|
||||||
awc_closeWindow();
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getColumnIndex(String columnName) {
|
|
||||||
// Create mColumnNameMap on demand
|
|
||||||
if (mColumnNameMap == null) {
|
|
||||||
String[] columns = mColumns;
|
|
||||||
int columnCount = columns.length;
|
|
||||||
HashMap<String, Integer> map = new HashMap<String, Integer>(columnCount, 1);
|
|
||||||
for (int i = 0; i < columnCount; i++) {
|
|
||||||
map.put(columns[i], i);
|
|
||||||
}
|
|
||||||
mColumnNameMap = map;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Hack according to bug 903852
|
|
||||||
final int periodIndex = columnName.lastIndexOf('.');
|
|
||||||
if (periodIndex != -1) {
|
|
||||||
Exception e = new Exception();
|
|
||||||
Log.e(TAG, "requesting column name with table name -- " + columnName, e);
|
|
||||||
columnName = columnName.substring(periodIndex + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
Integer i = mColumnNameMap.get(columnName);
|
|
||||||
if (i != null) {
|
|
||||||
return i.intValue();
|
|
||||||
} else {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String[] getColumnNames() {
|
|
||||||
return mColumns;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void deactivate() {
|
|
||||||
super.deactivate();
|
|
||||||
mDriver.cursorDeactivated();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void close() {
|
|
||||||
super.close();
|
|
||||||
synchronized (this) {
|
|
||||||
mQuery.close();
|
|
||||||
mDriver.cursorClosed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean requery() {
|
|
||||||
if (isClosed()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
synchronized (this) {
|
|
||||||
if (!mQuery.getDatabase().isOpen()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mWindow != null) {
|
|
||||||
mWindow.clear();
|
|
||||||
}
|
|
||||||
mPos = -1;
|
|
||||||
mCount = NO_COUNT;
|
|
||||||
|
|
||||||
mDriver.cursorRequeried(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
return super.requery();
|
|
||||||
} catch (IllegalStateException e) {
|
|
||||||
// for backwards compatibility, just return false
|
|
||||||
Log.w(TAG, "requery() failed " + e.getMessage(), e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setWindow(CursorWindow window) {
|
|
||||||
super.setWindow(window);
|
|
||||||
mCount = NO_COUNT;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Changes the selection arguments. The new values take effect after a call to requery().
|
|
||||||
*/
|
|
||||||
public void setSelectionArguments(String[] selectionArgs) {
|
|
||||||
mDriver.setBindArguments(selectionArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Release the native resources, if they haven't been released yet.
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
protected void finalize() {
|
|
||||||
try {
|
|
||||||
// if the cursor hasn't been closed yet, close it first
|
|
||||||
if (mWindow != null) {
|
|
||||||
/*
|
|
||||||
if (mStackTrace != null) {
|
|
||||||
String sql = mQuery.getSql();
|
|
||||||
int len = sql.length();
|
|
||||||
StrictMode.onSqliteObjectLeaked(
|
|
||||||
"Finalizing a Cursor that has not been deactivated or closed. " +
|
|
||||||
"database = " + mQuery.getDatabase().getLabel() +
|
|
||||||
", table = " + mEditTable +
|
|
||||||
", query = " + sql.substring(0, (len > 1000) ? 1000 : len),
|
|
||||||
mStackTrace);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
super.finalize();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,60 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2007 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
import android.database.Cursor;
|
|
||||||
import org.sqlite.database.sqlite.SQLiteDatabase.CursorFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A driver for SQLiteCursors that is used to create them and gets notified
|
|
||||||
* by the cursors it creates on significant events in their lifetimes.
|
|
||||||
*/
|
|
||||||
public interface SQLiteCursorDriver {
|
|
||||||
/**
|
|
||||||
* Executes the query returning a Cursor over the result set.
|
|
||||||
*
|
|
||||||
* @param factory The CursorFactory to use when creating the Cursors, or
|
|
||||||
* null if standard SQLiteCursors should be returned.
|
|
||||||
* @return a Cursor over the result set
|
|
||||||
*/
|
|
||||||
Cursor query(CursorFactory factory, String[] bindArgs);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by a SQLiteCursor when it is released.
|
|
||||||
*/
|
|
||||||
void cursorDeactivated();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by a SQLiteCursor when it is requeried.
|
|
||||||
*/
|
|
||||||
void cursorRequeried(Cursor cursor);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by a SQLiteCursor when it it closed to destroy this object as well.
|
|
||||||
*/
|
|
||||||
void cursorClosed();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set new bind arguments. These will take effect in cursorRequeried().
|
|
||||||
* @param bindArgs the new arguments
|
|
||||||
*/
|
|
||||||
public void setBindArguments(String[] bindArgs);
|
|
||||||
}
|
|
@ -1,57 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2011 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Describes a custom SQL function.
|
|
||||||
*
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public final class SQLiteCustomFunction {
|
|
||||||
public final String name;
|
|
||||||
public final int numArgs;
|
|
||||||
public final SQLiteDatabase.CustomFunction callback;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create custom function.
|
|
||||||
*
|
|
||||||
* @param name The name of the sqlite3 function.
|
|
||||||
* @param numArgs The number of arguments for the function, or -1 to
|
|
||||||
* support any number of arguments.
|
|
||||||
* @param callback The callback to invoke when the function is executed.
|
|
||||||
*/
|
|
||||||
public SQLiteCustomFunction(String name, int numArgs,
|
|
||||||
SQLiteDatabase.CustomFunction callback) {
|
|
||||||
if (name == null) {
|
|
||||||
throw new IllegalArgumentException("name must not be null.");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.name = name;
|
|
||||||
this.numArgs = numArgs;
|
|
||||||
this.callback = callback;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called from native.
|
|
||||||
@SuppressWarnings("unused")
|
|
||||||
private void dispatchCallback(String[] args) {
|
|
||||||
callback.callback(args);
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,169 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2011 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Describes how to configure a database.
|
|
||||||
* <p>
|
|
||||||
* The purpose of this object is to keep track of all of the little
|
|
||||||
* configuration settings that are applied to a database after it
|
|
||||||
* is opened so that they can be applied to all connections in the
|
|
||||||
* connection pool uniformly.
|
|
||||||
* </p><p>
|
|
||||||
* Each connection maintains its own copy of this object so it can
|
|
||||||
* keep track of which settings have already been applied.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public final class SQLiteDatabaseConfiguration {
|
|
||||||
// The pattern we use to strip email addresses from database paths
|
|
||||||
// when constructing a label to use in log messages.
|
|
||||||
private static final Pattern EMAIL_IN_DB_PATTERN =
|
|
||||||
Pattern.compile("[\\w\\.\\-]+@[\\w\\.\\-]+");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Special path used by in-memory databases.
|
|
||||||
*/
|
|
||||||
public static final String MEMORY_DB_PATH = ":memory:";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The database path.
|
|
||||||
*/
|
|
||||||
public final String path;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The label to use to describe the database when it appears in logs.
|
|
||||||
* This is derived from the path but is stripped to remove PII.
|
|
||||||
*/
|
|
||||||
public final String label;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The flags used to open the database.
|
|
||||||
*/
|
|
||||||
public int openFlags;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The maximum size of the prepared statement cache for each database connection.
|
|
||||||
* Must be non-negative.
|
|
||||||
*
|
|
||||||
* Default is 25.
|
|
||||||
*/
|
|
||||||
public int maxSqlCacheSize;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The database locale.
|
|
||||||
*
|
|
||||||
* Default is the value returned by {@link Locale#getDefault()}.
|
|
||||||
*/
|
|
||||||
public Locale locale;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* True if foreign key constraints are enabled.
|
|
||||||
*
|
|
||||||
* Default is false.
|
|
||||||
*/
|
|
||||||
public boolean foreignKeyConstraintsEnabled;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
public void updateParametersFrom(SQLiteDatabaseConfiguration other) {
|
|
||||||
if (other == null) {
|
|
||||||
throw new IllegalArgumentException("other must not be null.");
|
|
||||||
}
|
|
||||||
if (!path.equals(other.path)) {
|
|
||||||
throw new IllegalArgumentException("other configuration must refer to "
|
|
||||||
+ "the same database.");
|
|
||||||
}
|
|
||||||
|
|
||||||
openFlags = other.openFlags;
|
|
||||||
maxSqlCacheSize = other.maxSqlCacheSize;
|
|
||||||
locale = other.locale;
|
|
||||||
foreignKeyConstraintsEnabled = other.foreignKeyConstraintsEnabled;
|
|
||||||
customFunctions.clear();
|
|
||||||
customFunctions.addAll(other.customFunctions);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the database is in-memory.
|
|
||||||
* @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");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2006 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception that indicates that the SQLite database file is corrupt.
|
|
||||||
*/
|
|
||||||
public class SQLiteDatabaseCorruptException extends SQLiteException {
|
|
||||||
public SQLiteDatabaseCorruptException() {}
|
|
||||||
|
|
||||||
public SQLiteDatabaseCorruptException(String error) {
|
|
||||||
super(error);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Thrown if the database engine was unable to acquire the
|
|
||||||
* database locks it needs to do its job. If the statement is a [COMMIT]
|
|
||||||
* or occurs outside of an explicit transaction, then you can retry the
|
|
||||||
* statement. If the statement is not a [COMMIT] and occurs within a
|
|
||||||
* explicit transaction then you should rollback the transaction before
|
|
||||||
* continuing.
|
|
||||||
*/
|
|
||||||
public class SQLiteDatabaseLockedException extends SQLiteException {
|
|
||||||
public SQLiteDatabaseLockedException() {}
|
|
||||||
|
|
||||||
public SQLiteDatabaseLockedException(String error) {
|
|
||||||
super(error);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2008 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
public class SQLiteDatatypeMismatchException extends SQLiteException {
|
|
||||||
public SQLiteDatatypeMismatchException() {}
|
|
||||||
|
|
||||||
public SQLiteDatatypeMismatchException(String error) {
|
|
||||||
super(error);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,176 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2007 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import android.os.Build;
|
|
||||||
/* import android.os.SystemProperties; */
|
|
||||||
import android.util.Log;
|
|
||||||
import android.util.Printer;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides debugging info about all SQLite databases running in the current process.
|
|
||||||
*
|
|
||||||
* {@hide}
|
|
||||||
*/
|
|
||||||
public final class SQLiteDebug {
|
|
||||||
private static native void nativeGetPagerStats(PagerStats stats);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Controls the printing of informational SQL log messages.
|
|
||||||
*
|
|
||||||
* Enable using "adb shell setprop log.tag.SQLiteLog VERBOSE".
|
|
||||||
*/
|
|
||||||
public static final boolean DEBUG_SQL_LOG =
|
|
||||||
Log.isLoggable("SQLiteLog", Log.VERBOSE);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Controls the printing of SQL statements as they are executed.
|
|
||||||
*
|
|
||||||
* Enable using "adb shell setprop log.tag.SQLiteStatements VERBOSE".
|
|
||||||
*/
|
|
||||||
public static final boolean DEBUG_SQL_STATEMENTS =
|
|
||||||
Log.isLoggable("SQLiteStatements", Log.VERBOSE);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Controls the printing of wall-clock time taken to execute SQL statements
|
|
||||||
* as they are executed.
|
|
||||||
*
|
|
||||||
* Enable using "adb shell setprop log.tag.SQLiteTime VERBOSE".
|
|
||||||
*/
|
|
||||||
public static final boolean DEBUG_SQL_TIME =
|
|
||||||
Log.isLoggable("SQLiteTime", Log.VERBOSE);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* True to enable database performance testing instrumentation.
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public static final boolean DEBUG_LOG_SLOW_QUERIES = false;
|
|
||||||
|
|
||||||
private SQLiteDebug() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determines whether a query should be logged.
|
|
||||||
*
|
|
||||||
* Reads the "db.log.slow_query_threshold" system property, which can be changed
|
|
||||||
* by the user at any time. If the value is zero, then all queries will
|
|
||||||
* be considered slow. If the value does not exist or is negative, then no queries will
|
|
||||||
* be considered slow.
|
|
||||||
*
|
|
||||||
* This value can be changed dynamically while the system is running.
|
|
||||||
* For example, "adb shell setprop db.log.slow_query_threshold 200" will
|
|
||||||
* log all queries that take 200ms or longer to run.
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public static final boolean shouldLogSlowQuery(long elapsedTimeMillis) {
|
|
||||||
int slowQueryMillis = 10000;
|
|
||||||
return slowQueryMillis >= 0 && elapsedTimeMillis >= slowQueryMillis;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Contains statistics about the active pagers in the current process.
|
|
||||||
*
|
|
||||||
* @see #nativeGetPagerStats(PagerStats)
|
|
||||||
*/
|
|
||||||
public static class PagerStats {
|
|
||||||
/** the current amount of memory checked out by sqlite using sqlite3_malloc().
|
|
||||||
* documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html
|
|
||||||
*/
|
|
||||||
public int memoryUsed;
|
|
||||||
|
|
||||||
/** the number of bytes of page cache allocation which could not be sattisfied by the
|
|
||||||
* SQLITE_CONFIG_PAGECACHE buffer and where forced to overflow to sqlite3_malloc().
|
|
||||||
* The returned value includes allocations that overflowed because they where too large
|
|
||||||
* (they were larger than the "sz" parameter to SQLITE_CONFIG_PAGECACHE) and allocations
|
|
||||||
* that overflowed because no space was left in the page cache.
|
|
||||||
* documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html
|
|
||||||
*/
|
|
||||||
public int pageCacheOverflow;
|
|
||||||
|
|
||||||
/** records the largest memory allocation request handed to sqlite3.
|
|
||||||
* documented at http://www.sqlite.org/c3ref/c_status_malloc_size.html
|
|
||||||
*/
|
|
||||||
public int largestMemAlloc;
|
|
||||||
|
|
||||||
/** a list of {@link DbStats} - one for each main database opened by the applications
|
|
||||||
* running on the android device
|
|
||||||
*/
|
|
||||||
public ArrayList<DbStats> dbStats;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* contains statistics about a database
|
|
||||||
*/
|
|
||||||
public static class DbStats {
|
|
||||||
/** name of the database */
|
|
||||||
public String dbName;
|
|
||||||
|
|
||||||
/** the page size for the database */
|
|
||||||
public long pageSize;
|
|
||||||
|
|
||||||
/** the database size */
|
|
||||||
public long dbSize;
|
|
||||||
|
|
||||||
/** documented here http://www.sqlite.org/c3ref/c_dbstatus_lookaside_used.html */
|
|
||||||
public int lookaside;
|
|
||||||
|
|
||||||
/** statement cache stats: hits/misses/cachesize */
|
|
||||||
public String cache;
|
|
||||||
|
|
||||||
public DbStats(String dbName, long pageCount, long pageSize, int lookaside,
|
|
||||||
int hits, int misses, int cachesize) {
|
|
||||||
this.dbName = dbName;
|
|
||||||
this.pageSize = pageSize / 1024;
|
|
||||||
dbSize = (pageCount * pageSize) / 1024;
|
|
||||||
this.lookaside = lookaside;
|
|
||||||
this.cache = hits + "/" + misses + "/" + cachesize;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* return all pager and database stats for the current process.
|
|
||||||
* @return {@link PagerStats}
|
|
||||||
*/
|
|
||||||
public static PagerStats getDatabaseInfo() {
|
|
||||||
PagerStats stats = new PagerStats();
|
|
||||||
nativeGetPagerStats(stats);
|
|
||||||
stats.dbStats = SQLiteDatabase.getDbStats();
|
|
||||||
return stats;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Dumps detailed information about all databases used by the process.
|
|
||||||
* @param printer The printer for dumping database state.
|
|
||||||
* @param args Command-line arguments supplied to dumpsys dbinfo
|
|
||||||
*/
|
|
||||||
public static void dump(Printer printer, String[] args) {
|
|
||||||
boolean verbose = false;
|
|
||||||
for (String arg : args) {
|
|
||||||
if (arg.equals("-v")) {
|
|
||||||
verbose = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SQLiteDatabase.dumpAll(printer, verbose);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,87 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2007 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
import android.database.Cursor;
|
|
||||||
import org.sqlite.database.sqlite.SQLiteDatabase.CursorFactory;
|
|
||||||
import android.os.CancellationSignal;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A cursor driver that uses the given query directly.
|
|
||||||
*
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public final class SQLiteDirectCursorDriver implements SQLiteCursorDriver {
|
|
||||||
private final SQLiteDatabase mDatabase;
|
|
||||||
private final String mEditTable;
|
|
||||||
private final String mSql;
|
|
||||||
private final CancellationSignal mCancellationSignal;
|
|
||||||
private SQLiteQuery mQuery;
|
|
||||||
|
|
||||||
public SQLiteDirectCursorDriver(SQLiteDatabase db, String sql, String editTable,
|
|
||||||
CancellationSignal cancellationSignal) {
|
|
||||||
mDatabase = db;
|
|
||||||
mEditTable = editTable;
|
|
||||||
mSql = sql;
|
|
||||||
mCancellationSignal = cancellationSignal;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Cursor query(CursorFactory factory, String[] selectionArgs) {
|
|
||||||
final SQLiteQuery query = new SQLiteQuery(mDatabase, mSql, mCancellationSignal);
|
|
||||||
final Cursor cursor;
|
|
||||||
try {
|
|
||||||
query.bindAllArgsAsStrings(selectionArgs);
|
|
||||||
|
|
||||||
if (factory == null) {
|
|
||||||
cursor = new SQLiteCursor(this, mEditTable, query);
|
|
||||||
} else {
|
|
||||||
cursor = factory.newCursor(mDatabase, this, mEditTable, query);
|
|
||||||
}
|
|
||||||
} catch (RuntimeException ex) {
|
|
||||||
query.close();
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
mQuery = query;
|
|
||||||
return cursor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void cursorClosed() {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setBindArguments(String[] bindArgs) {
|
|
||||||
mQuery.bindAllArgsAsStrings(bindArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void cursorDeactivated() {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
public void cursorRequeried(Cursor cursor) {
|
|
||||||
// Do nothing
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "SQLiteDirectCursorDriver: " + mSql;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2006 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception that indicates that an IO error occured while accessing the
|
|
||||||
* SQLite database file.
|
|
||||||
*/
|
|
||||||
public class SQLiteDiskIOException extends SQLiteException {
|
|
||||||
public SQLiteDiskIOException() {}
|
|
||||||
|
|
||||||
public SQLiteDiskIOException(String error) {
|
|
||||||
super(error);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2008 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception that indicates that the SQLite program is done.
|
|
||||||
* Thrown when an operation that expects a row (such as {@link
|
|
||||||
* SQLiteStatement#simpleQueryForString} or {@link
|
|
||||||
* SQLiteStatement#simpleQueryForLong}) does not get one.
|
|
||||||
*/
|
|
||||||
public class SQLiteDoneException extends SQLiteException {
|
|
||||||
public SQLiteDoneException() {}
|
|
||||||
|
|
||||||
public SQLiteDoneException(String error) {
|
|
||||||
super(error);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2006 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
import org.sqlite.database.SQLException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A SQLite exception that indicates there was an error with SQL parsing or execution.
|
|
||||||
*/
|
|
||||||
public class SQLiteException extends SQLException {
|
|
||||||
public SQLiteException() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public SQLiteException(String error) {
|
|
||||||
super(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SQLiteException(String error, Throwable cause) {
|
|
||||||
super(error, cause);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2008 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An exception that indicates that the SQLite database is full.
|
|
||||||
*/
|
|
||||||
public class SQLiteFullException extends SQLiteException {
|
|
||||||
public SQLiteFullException() {}
|
|
||||||
|
|
||||||
public SQLiteFullException(String error) {
|
|
||||||
super(error);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,117 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2011 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
import android.content.res.Resources;
|
|
||||||
import android.os.StatFs;
|
|
||||||
/* import android.os.SystemProperties; */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides access to SQLite functions that affect all database connection,
|
|
||||||
* such as memory management.
|
|
||||||
*
|
|
||||||
* The native code associated with SQLiteGlobal is also sets global configuration options
|
|
||||||
* using sqlite3_config() then calls sqlite3_initialize() to ensure that the SQLite
|
|
||||||
* library is properly initialized exactly once before any other framework or application
|
|
||||||
* code has a chance to run.
|
|
||||||
*
|
|
||||||
* Verbose SQLite logging is enabled if the "log.tag.SQLiteLog" property is set to "V".
|
|
||||||
* (per {@link SQLiteDebug#DEBUG_SQL_LOG}).
|
|
||||||
*
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public final class SQLiteGlobal {
|
|
||||||
private static final String TAG = "SQLiteGlobal";
|
|
||||||
|
|
||||||
private static final Object sLock = new Object();
|
|
||||||
private static int sDefaultPageSize;
|
|
||||||
|
|
||||||
private static native int nativeReleaseMemory();
|
|
||||||
|
|
||||||
private SQLiteGlobal() {
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Attempts to release memory by pruning the SQLite page cache and other
|
|
||||||
* internal data structures.
|
|
||||||
*
|
|
||||||
* @return The number of bytes that were freed.
|
|
||||||
*/
|
|
||||||
public static int releaseMemory() {
|
|
||||||
return nativeReleaseMemory();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the default page size to use when creating a database.
|
|
||||||
*/
|
|
||||||
public static int getDefaultPageSize() {
|
|
||||||
synchronized (sLock) {
|
|
||||||
if (sDefaultPageSize == 0) {
|
|
||||||
sDefaultPageSize = new StatFs("/data").getBlockSize();
|
|
||||||
}
|
|
||||||
return 1024;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the default journal mode when WAL is not in use.
|
|
||||||
*/
|
|
||||||
public static String getDefaultJournalMode() {
|
|
||||||
return "delete";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the journal size limit in bytes.
|
|
||||||
*/
|
|
||||||
public static int getJournalSizeLimit() {
|
|
||||||
return 10000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the default database synchronization mode when WAL is not in use.
|
|
||||||
*/
|
|
||||||
public static String getDefaultSyncMode() {
|
|
||||||
return "normal";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the database synchronization mode when in WAL mode.
|
|
||||||
*/
|
|
||||||
public static String getWALSyncMode() {
|
|
||||||
return "normal";
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the WAL auto-checkpoint integer in database pages.
|
|
||||||
*/
|
|
||||||
public static int getWALAutoCheckpoint() {
|
|
||||||
int value = 1000;
|
|
||||||
return Math.max(1, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the connection pool size when in WAL mode.
|
|
||||||
*/
|
|
||||||
public static int getWALConnectionPoolSize() {
|
|
||||||
int value = 10;
|
|
||||||
return Math.max(2, value);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2008 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This error can occur if the application creates a SQLiteStatement object and allows multiple
|
|
||||||
* threads in the application use it at the same time.
|
|
||||||
* Sqlite returns this error if bind and execute methods on this object occur at the same time
|
|
||||||
* from multiple threads, like so:
|
|
||||||
* thread # 1: in execute() method of the SQLiteStatement object
|
|
||||||
* while thread # 2: is in bind..() on the same object.
|
|
||||||
*</p>
|
|
||||||
* FIX this by NEVER sharing the same SQLiteStatement object between threads.
|
|
||||||
* Create a local instance of the SQLiteStatement whenever it is needed, use it and close it ASAP.
|
|
||||||
* NEVER make it globally available.
|
|
||||||
*/
|
|
||||||
public class SQLiteMisuseException extends SQLiteException {
|
|
||||||
public SQLiteMisuseException() {}
|
|
||||||
|
|
||||||
public SQLiteMisuseException(String error) {
|
|
||||||
super(error);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,383 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2007 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import org.sqlite.database.DatabaseErrorHandler;
|
|
||||||
import org.sqlite.database.DefaultDatabaseErrorHandler;
|
|
||||||
import org.sqlite.database.sqlite.SQLiteDatabase.CursorFactory;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A helper class to manage database creation and version management.
|
|
||||||
*
|
|
||||||
* <p>You create a subclass implementing {@link #onCreate}, {@link #onUpgrade} and
|
|
||||||
* optionally {@link #onOpen}, and this class takes care of opening the database
|
|
||||||
* if it exists, creating it if it does not, and upgrading it as necessary.
|
|
||||||
* Transactions are used to make sure the database is always in a sensible state.
|
|
||||||
*
|
|
||||||
* <p>This class makes it easy for {@link android.content.ContentProvider}
|
|
||||||
* implementations to defer opening and upgrading the database until first use,
|
|
||||||
* to avoid blocking application startup with long-running database upgrades.
|
|
||||||
*
|
|
||||||
* <p>For an example, see the NotePadProvider class in the NotePad sample application,
|
|
||||||
* in the <em>samples/</em> directory of the SDK.</p>
|
|
||||||
*
|
|
||||||
* <p class="note"><strong>Note:</strong> this class assumes
|
|
||||||
* monotonically increasing version numbers for upgrades.</p>
|
|
||||||
*/
|
|
||||||
public abstract class SQLiteOpenHelper {
|
|
||||||
private static final String TAG = SQLiteOpenHelper.class.getSimpleName();
|
|
||||||
|
|
||||||
// When true, getReadableDatabase returns a read-only database if it is just being opened.
|
|
||||||
// The database handle is reopened in read/write mode when getWritableDatabase is called.
|
|
||||||
// We leave this behavior disabled in production because it is inefficient and breaks
|
|
||||||
// many applications. For debugging purposes it can be useful to turn on strict
|
|
||||||
// read-only semantics to catch applications that call getReadableDatabase when they really
|
|
||||||
// wanted getWritableDatabase.
|
|
||||||
private static final boolean DEBUG_STRICT_READONLY = false;
|
|
||||||
|
|
||||||
private final Context mContext;
|
|
||||||
private final String mName;
|
|
||||||
private final CursorFactory mFactory;
|
|
||||||
private final int mNewVersion;
|
|
||||||
|
|
||||||
private SQLiteDatabase mDatabase;
|
|
||||||
private boolean mIsInitializing;
|
|
||||||
private boolean mEnableWriteAheadLogging;
|
|
||||||
private final DatabaseErrorHandler mErrorHandler;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a helper object to create, open, and/or manage a database.
|
|
||||||
* This method always returns very quickly. The database is not actually
|
|
||||||
* created or opened until one of {@link #getWritableDatabase} or
|
|
||||||
* {@link #getReadableDatabase} is called.
|
|
||||||
*
|
|
||||||
* @param context to use to open or create the database
|
|
||||||
* @param name of the database file, or null for an in-memory database
|
|
||||||
* @param factory to use for creating cursor objects, or null for the default
|
|
||||||
* @param version number of the database (starting at 1); if the database is older,
|
|
||||||
* {@link #onUpgrade} will be used to upgrade the database; if the database is
|
|
||||||
* newer, {@link #onDowngrade} will be used to downgrade the database
|
|
||||||
*/
|
|
||||||
public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version) {
|
|
||||||
this(context, name, factory, version, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a helper object to create, open, and/or manage a database.
|
|
||||||
* The database is not actually created or opened until one of
|
|
||||||
* {@link #getWritableDatabase} or {@link #getReadableDatabase} is called.
|
|
||||||
*
|
|
||||||
* <p>Accepts input param: a concrete instance of {@link DatabaseErrorHandler} to be
|
|
||||||
* used to handle corruption when sqlite reports database corruption.</p>
|
|
||||||
*
|
|
||||||
* @param context to use to open or create the database
|
|
||||||
* @param name of the database file, or null for an in-memory database
|
|
||||||
* @param factory to use for creating cursor objects, or null for the default
|
|
||||||
* @param version number of the database (starting at 1); if the database is older,
|
|
||||||
* {@link #onUpgrade} will be used to upgrade the database; if the database is
|
|
||||||
* newer, {@link #onDowngrade} will be used to downgrade the database
|
|
||||||
* @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database
|
|
||||||
* corruption, or null to use the default error handler.
|
|
||||||
*/
|
|
||||||
public SQLiteOpenHelper(Context context, String name, CursorFactory factory, int version,
|
|
||||||
DatabaseErrorHandler errorHandler) {
|
|
||||||
if (version < 1) throw new IllegalArgumentException("Version must be >= 1, was " + version);
|
|
||||||
|
|
||||||
mContext = context;
|
|
||||||
mName = name;
|
|
||||||
mFactory = factory;
|
|
||||||
mNewVersion = version;
|
|
||||||
mErrorHandler = errorHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the name of the SQLite database being opened, as given to
|
|
||||||
* the constructor.
|
|
||||||
*/
|
|
||||||
public String getDatabaseName() {
|
|
||||||
return mName;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enables or disables the use of write-ahead logging for the database.
|
|
||||||
*
|
|
||||||
* Write-ahead logging cannot be used with read-only databases so the value of
|
|
||||||
* this flag is ignored if the database is opened read-only.
|
|
||||||
*
|
|
||||||
* @param enabled True if write-ahead logging should be enabled, false if it
|
|
||||||
* should be disabled.
|
|
||||||
*
|
|
||||||
* @see SQLiteDatabase#enableWriteAheadLogging()
|
|
||||||
*/
|
|
||||||
public void setWriteAheadLoggingEnabled(boolean enabled) {
|
|
||||||
synchronized (this) {
|
|
||||||
if (mEnableWriteAheadLogging != enabled) {
|
|
||||||
if (mDatabase != null && mDatabase.isOpen() && !mDatabase.isReadOnly()) {
|
|
||||||
if (enabled) {
|
|
||||||
mDatabase.enableWriteAheadLogging();
|
|
||||||
} else {
|
|
||||||
mDatabase.disableWriteAheadLogging();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
mEnableWriteAheadLogging = enabled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create and/or open a database that will be used for reading and writing.
|
|
||||||
* The first time this is called, the database will be opened and
|
|
||||||
* {@link #onCreate}, {@link #onUpgrade} and/or {@link #onOpen} will be
|
|
||||||
* called.
|
|
||||||
*
|
|
||||||
* <p>Once opened successfully, the database is cached, so you can
|
|
||||||
* call this method every time you need to write to the database.
|
|
||||||
* (Make sure to call {@link #close} when you no longer need the database.)
|
|
||||||
* Errors such as bad permissions or a full disk may cause this method
|
|
||||||
* to fail, but future attempts may succeed if the problem is fixed.</p>
|
|
||||||
*
|
|
||||||
* <p class="caution">Database upgrade may take a long time, you
|
|
||||||
* should not call this method from the application main thread, including
|
|
||||||
* from {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
|
|
||||||
*
|
|
||||||
* @throws SQLiteException if the database cannot be opened for writing
|
|
||||||
* @return a read/write database object valid until {@link #close} is called
|
|
||||||
*/
|
|
||||||
public SQLiteDatabase getWritableDatabase() {
|
|
||||||
synchronized (this) {
|
|
||||||
return getDatabaseLocked(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create and/or open a database. This will be the same object returned by
|
|
||||||
* {@link #getWritableDatabase} unless some problem, such as a full disk,
|
|
||||||
* requires the database to be opened read-only. In that case, a read-only
|
|
||||||
* database object will be returned. If the problem is fixed, a future call
|
|
||||||
* to {@link #getWritableDatabase} may succeed, in which case the read-only
|
|
||||||
* database object will be closed and the read/write object will be returned
|
|
||||||
* in the future.
|
|
||||||
*
|
|
||||||
* <p class="caution">Like {@link #getWritableDatabase}, this method may
|
|
||||||
* take a long time to return, so you should not call it from the
|
|
||||||
* application main thread, including from
|
|
||||||
* {@link android.content.ContentProvider#onCreate ContentProvider.onCreate()}.
|
|
||||||
*
|
|
||||||
* @throws SQLiteException if the database cannot be opened
|
|
||||||
* @return a database object valid until {@link #getWritableDatabase}
|
|
||||||
* or {@link #close} is called.
|
|
||||||
*/
|
|
||||||
public SQLiteDatabase getReadableDatabase() {
|
|
||||||
synchronized (this) {
|
|
||||||
return getDatabaseLocked(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private SQLiteDatabase getDatabaseLocked(boolean writable) {
|
|
||||||
if (mDatabase != null) {
|
|
||||||
if (!mDatabase.isOpen()) {
|
|
||||||
// Darn! The user closed the database by calling mDatabase.close().
|
|
||||||
mDatabase = null;
|
|
||||||
} else if (!writable || !mDatabase.isReadOnly()) {
|
|
||||||
// The database is already open for business.
|
|
||||||
return mDatabase;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mIsInitializing) {
|
|
||||||
throw new IllegalStateException("getDatabase called recursively");
|
|
||||||
}
|
|
||||||
|
|
||||||
SQLiteDatabase db = mDatabase;
|
|
||||||
try {
|
|
||||||
mIsInitializing = true;
|
|
||||||
|
|
||||||
if (db != null) {
|
|
||||||
if (writable && db.isReadOnly()) {
|
|
||||||
db.reopenReadWrite();
|
|
||||||
}
|
|
||||||
} else if (mName == null) {
|
|
||||||
db = SQLiteDatabase.create(null);
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
if (DEBUG_STRICT_READONLY && !writable) {
|
|
||||||
final String path = mContext.getDatabasePath(mName).getPath();
|
|
||||||
db = SQLiteDatabase.openDatabase(path, mFactory,
|
|
||||||
SQLiteDatabase.OPEN_READONLY, mErrorHandler);
|
|
||||||
} else {
|
|
||||||
db = SQLiteDatabase.openOrCreateDatabase(
|
|
||||||
mName, mFactory, mErrorHandler
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (SQLiteException ex) {
|
|
||||||
if (writable) {
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
Log.e(TAG, "Couldn't open " + mName
|
|
||||||
+ " for writing (will try read-only):", ex);
|
|
||||||
final String path = mContext.getDatabasePath(mName).getPath();
|
|
||||||
db = SQLiteDatabase.openDatabase(path, mFactory,
|
|
||||||
SQLiteDatabase.OPEN_READONLY, mErrorHandler);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onConfigure(db);
|
|
||||||
|
|
||||||
final int version = db.getVersion();
|
|
||||||
if (version != mNewVersion) {
|
|
||||||
if (db.isReadOnly()) {
|
|
||||||
throw new SQLiteException("Can't upgrade read-only database from version " +
|
|
||||||
db.getVersion() + " to " + mNewVersion + ": " + mName);
|
|
||||||
}
|
|
||||||
|
|
||||||
db.beginTransaction();
|
|
||||||
try {
|
|
||||||
if (version == 0) {
|
|
||||||
onCreate(db);
|
|
||||||
} else {
|
|
||||||
if (version > mNewVersion) {
|
|
||||||
onDowngrade(db, version, mNewVersion);
|
|
||||||
} else {
|
|
||||||
onUpgrade(db, version, mNewVersion);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
db.setVersion(mNewVersion);
|
|
||||||
db.setTransactionSuccessful();
|
|
||||||
} finally {
|
|
||||||
db.endTransaction();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onOpen(db);
|
|
||||||
|
|
||||||
if (db.isReadOnly()) {
|
|
||||||
Log.w(TAG, "Opened " + mName + " in read-only mode");
|
|
||||||
}
|
|
||||||
|
|
||||||
mDatabase = db;
|
|
||||||
return db;
|
|
||||||
} finally {
|
|
||||||
mIsInitializing = false;
|
|
||||||
if (db != null && db != mDatabase) {
|
|
||||||
db.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close any open database object.
|
|
||||||
*/
|
|
||||||
public synchronized void close() {
|
|
||||||
if (mIsInitializing) throw new IllegalStateException("Closed during initialization");
|
|
||||||
|
|
||||||
if (mDatabase != null && mDatabase.isOpen()) {
|
|
||||||
mDatabase.close();
|
|
||||||
mDatabase = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the database connection is being configured, to enable features
|
|
||||||
* such as write-ahead logging or foreign key support.
|
|
||||||
* <p>
|
|
||||||
* This method is called before {@link #onCreate}, {@link #onUpgrade},
|
|
||||||
* {@link #onDowngrade}, or {@link #onOpen} are called. It should not modify
|
|
||||||
* the database except to configure the database connection as required.
|
|
||||||
* </p><p>
|
|
||||||
* This method should only call methods that configure the parameters of the
|
|
||||||
* database connection, such as {@link SQLiteDatabase#enableWriteAheadLogging}
|
|
||||||
* {@link SQLiteDatabase#setForeignKeyConstraintsEnabled},
|
|
||||||
* {@link SQLiteDatabase#setLocale}, {@link SQLiteDatabase#setMaximumSize},
|
|
||||||
* or executing PRAGMA statements.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param db The database.
|
|
||||||
*/
|
|
||||||
public void onConfigure(SQLiteDatabase db) {}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the database is created for the first time. This is where the
|
|
||||||
* creation of tables and the initial population of the tables should happen.
|
|
||||||
*
|
|
||||||
* @param db The database.
|
|
||||||
*/
|
|
||||||
public abstract void onCreate(SQLiteDatabase db);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the database needs to be upgraded. The implementation
|
|
||||||
* should use this method to drop tables, add tables, or do anything else it
|
|
||||||
* needs to upgrade to the new schema version.
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* The SQLite ALTER TABLE documentation can be found
|
|
||||||
* <a href="http://sqlite.org/lang_altertable.html">here</a>. If you add new columns
|
|
||||||
* you can use ALTER TABLE to insert them into a live table. If you rename or remove columns
|
|
||||||
* you can use ALTER TABLE to rename the old table, then create the new table and then
|
|
||||||
* populate the new table with the contents of the old table.
|
|
||||||
* </p><p>
|
|
||||||
* This method executes within a transaction. If an exception is thrown, all changes
|
|
||||||
* will automatically be rolled back.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param db The database.
|
|
||||||
* @param oldVersion The old database version.
|
|
||||||
* @param newVersion The new database version.
|
|
||||||
*/
|
|
||||||
public abstract void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the database needs to be downgraded. This is strictly similar to
|
|
||||||
* {@link #onUpgrade} method, but is called whenever current version is newer than requested one.
|
|
||||||
* However, this method is not abstract, so it is not mandatory for a customer to
|
|
||||||
* implement it. If not overridden, default implementation will reject downgrade and
|
|
||||||
* throws SQLiteException
|
|
||||||
*
|
|
||||||
* <p>
|
|
||||||
* This method executes within a transaction. If an exception is thrown, all changes
|
|
||||||
* will automatically be rolled back.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param db The database.
|
|
||||||
* @param oldVersion The old database version.
|
|
||||||
* @param newVersion The new database version.
|
|
||||||
*/
|
|
||||||
public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
|
||||||
throw new SQLiteException("Can't downgrade database from version " +
|
|
||||||
oldVersion + " to " + newVersion);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called when the database has been opened. The implementation
|
|
||||||
* should check {@link SQLiteDatabase#isReadOnly} before updating the
|
|
||||||
* database.
|
|
||||||
* <p>
|
|
||||||
* This method is called after the database connection has been configured
|
|
||||||
* and after the database schema has been created, upgraded or downgraded as necessary.
|
|
||||||
* If the database connection must be configured in some way before the schema
|
|
||||||
* is created, upgraded, or downgraded, do it in {@link #onConfigure} instead.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param db The database.
|
|
||||||
*/
|
|
||||||
public void onOpen(SQLiteDatabase db) {}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
public class SQLiteOutOfMemoryException extends SQLiteException {
|
|
||||||
public SQLiteOutOfMemoryException() {}
|
|
||||||
|
|
||||||
public SQLiteOutOfMemoryException(String error) {
|
|
||||||
super(error);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,222 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2006 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
import android.database.DatabaseUtils;
|
|
||||||
import android.os.CancellationSignal;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A base class for compiled SQLite programs.
|
|
||||||
* <p>
|
|
||||||
* This class is not thread-safe.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public abstract class SQLiteProgram extends SQLiteClosable {
|
|
||||||
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
|
||||||
|
|
||||||
private final SQLiteDatabase mDatabase;
|
|
||||||
private final String mSql;
|
|
||||||
private final boolean mReadOnly;
|
|
||||||
private final String[] mColumnNames;
|
|
||||||
private final int mNumParameters;
|
|
||||||
private final Object[] mBindArgs;
|
|
||||||
|
|
||||||
SQLiteProgram(SQLiteDatabase db, String sql, Object[] bindArgs,
|
|
||||||
CancellationSignal cancellationSignalForPrepare) {
|
|
||||||
mDatabase = db;
|
|
||||||
mSql = sql.trim();
|
|
||||||
|
|
||||||
int n = DatabaseUtils.getSqlStatementType(mSql);
|
|
||||||
switch (n) {
|
|
||||||
case DatabaseUtils.STATEMENT_BEGIN:
|
|
||||||
case DatabaseUtils.STATEMENT_COMMIT:
|
|
||||||
case DatabaseUtils.STATEMENT_ABORT:
|
|
||||||
mReadOnly = false;
|
|
||||||
mColumnNames = EMPTY_STRING_ARRAY;
|
|
||||||
mNumParameters = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
boolean assumeReadOnly = (n == DatabaseUtils.STATEMENT_SELECT);
|
|
||||||
SQLiteStatementInfo info = new SQLiteStatementInfo();
|
|
||||||
db.getThreadSession().prepare(mSql,
|
|
||||||
db.getThreadDefaultConnectionFlags(assumeReadOnly),
|
|
||||||
cancellationSignalForPrepare, info);
|
|
||||||
mReadOnly = info.readOnly;
|
|
||||||
mColumnNames = info.columnNames;
|
|
||||||
mNumParameters = info.numParameters;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bindArgs != null && bindArgs.length > mNumParameters) {
|
|
||||||
throw new IllegalArgumentException("Too many bind arguments. "
|
|
||||||
+ bindArgs.length + " arguments were provided but the statement needs "
|
|
||||||
+ mNumParameters + " arguments.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mNumParameters != 0) {
|
|
||||||
mBindArgs = new Object[mNumParameters];
|
|
||||||
if (bindArgs != null) {
|
|
||||||
System.arraycopy(bindArgs, 0, mBindArgs, 0, bindArgs.length);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
mBindArgs = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final SQLiteDatabase getDatabase() {
|
|
||||||
return mDatabase;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String getSql() {
|
|
||||||
return mSql;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Object[] getBindArgs() {
|
|
||||||
return mBindArgs;
|
|
||||||
}
|
|
||||||
|
|
||||||
final String[] getColumnNames() {
|
|
||||||
return mColumnNames;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @hide */
|
|
||||||
protected final SQLiteSession getSession() {
|
|
||||||
return mDatabase.getThreadSession();
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @hide */
|
|
||||||
protected final int getConnectionFlags() {
|
|
||||||
return mDatabase.getThreadDefaultConnectionFlags(mReadOnly);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @hide */
|
|
||||||
protected final void onCorruption() {
|
|
||||||
mDatabase.onCorruption();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unimplemented.
|
|
||||||
* @deprecated This method is deprecated and must not be used.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public final int getUniqueId() {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bind a NULL value to this statement. The value remains bound until
|
|
||||||
* {@link #clearBindings} is called.
|
|
||||||
*
|
|
||||||
* @param index The 1-based index to the parameter to bind null to
|
|
||||||
*/
|
|
||||||
public void bindNull(int index) {
|
|
||||||
bind(index, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bind a long value to this statement. The value remains bound until
|
|
||||||
* {@link #clearBindings} is called.
|
|
||||||
*addToBindArgs
|
|
||||||
* @param index The 1-based index to the parameter to bind
|
|
||||||
* @param value The value to bind
|
|
||||||
*/
|
|
||||||
public void bindLong(int index, long value) {
|
|
||||||
bind(index, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bind a double value to this statement. The value remains bound until
|
|
||||||
* {@link #clearBindings} is called.
|
|
||||||
*
|
|
||||||
* @param index The 1-based index to the parameter to bind
|
|
||||||
* @param value The value to bind
|
|
||||||
*/
|
|
||||||
public void bindDouble(int index, double value) {
|
|
||||||
bind(index, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bind a String value to this statement. The value remains bound until
|
|
||||||
* {@link #clearBindings} is called.
|
|
||||||
*
|
|
||||||
* @param index The 1-based index to the parameter to bind
|
|
||||||
* @param value The value to bind, must not be null
|
|
||||||
*/
|
|
||||||
public void bindString(int index, String value) {
|
|
||||||
if (value == null) {
|
|
||||||
throw new IllegalArgumentException("the bind value at index " + index + " is null");
|
|
||||||
}
|
|
||||||
bind(index, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Bind a byte array value to this statement. The value remains bound until
|
|
||||||
* {@link #clearBindings} is called.
|
|
||||||
*
|
|
||||||
* @param index The 1-based index to the parameter to bind
|
|
||||||
* @param value The value to bind, must not be null
|
|
||||||
*/
|
|
||||||
public void bindBlob(int index, byte[] value) {
|
|
||||||
if (value == null) {
|
|
||||||
throw new IllegalArgumentException("the bind value at index " + index + " is null");
|
|
||||||
}
|
|
||||||
bind(index, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears all existing bindings. Unset bindings are treated as NULL.
|
|
||||||
*/
|
|
||||||
public void clearBindings() {
|
|
||||||
if (mBindArgs != null) {
|
|
||||||
Arrays.fill(mBindArgs, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given an array of String bindArgs, this method binds all of them in one single call.
|
|
||||||
*
|
|
||||||
* @param bindArgs the String array of bind args, none of which must be null.
|
|
||||||
*/
|
|
||||||
public void bindAllArgsAsStrings(String[] bindArgs) {
|
|
||||||
if (bindArgs != null) {
|
|
||||||
for (int i = bindArgs.length; i != 0; i--) {
|
|
||||||
bindString(i, bindArgs[i - 1]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onAllReferencesReleased() {
|
|
||||||
clearBindings();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bind(int index, Object value) {
|
|
||||||
if (index < 1 || index > mNumParameters) {
|
|
||||||
throw new IllegalArgumentException("Cannot bind argument at index "
|
|
||||||
+ index + " because the index is out of range. "
|
|
||||||
+ "The statement has " + mNumParameters + " parameters.");
|
|
||||||
}
|
|
||||||
mBindArgs[index - 1] = value;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2006 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
import android.database.CursorWindow;
|
|
||||||
import android.os.CancellationSignal;
|
|
||||||
import android.os.OperationCanceledException;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a query that reads the resulting rows into a {@link SQLiteQuery}.
|
|
||||||
* This class is used by {@link SQLiteCursor} and isn't useful itself.
|
|
||||||
* <p>
|
|
||||||
* This class is not thread-safe.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public final class SQLiteQuery extends SQLiteProgram {
|
|
||||||
private static final String TAG = "SQLiteQuery";
|
|
||||||
|
|
||||||
private final CancellationSignal mCancellationSignal;
|
|
||||||
|
|
||||||
SQLiteQuery(SQLiteDatabase db, String query, CancellationSignal cancellationSignal) {
|
|
||||||
super(db, query, null, cancellationSignal);
|
|
||||||
|
|
||||||
mCancellationSignal = cancellationSignal;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads rows into a buffer.
|
|
||||||
*
|
|
||||||
* @param window The window to fill into
|
|
||||||
* @param startPos The start position for filling the window.
|
|
||||||
* @param requiredPos The position of a row that MUST be in the window.
|
|
||||||
* If it won't fit, then the query should discard part of what it filled.
|
|
||||||
* @param countAllRows True to count all rows that the query would
|
|
||||||
* return regardless of whether they fit in the window.
|
|
||||||
* @return Number of rows that were enumerated. Might not be all rows
|
|
||||||
* unless countAllRows is true.
|
|
||||||
*
|
|
||||||
* @throws SQLiteException if an error occurs.
|
|
||||||
* @throws OperationCanceledException if the operation was canceled.
|
|
||||||
*/
|
|
||||||
int fillWindow(CursorWindow window, int startPos, int requiredPos, boolean countAllRows) {
|
|
||||||
acquireReference();
|
|
||||||
try {
|
|
||||||
window.acquireReference();
|
|
||||||
try {
|
|
||||||
int numRows = getSession().executeForCursorWindow(getSql(), getBindArgs(),
|
|
||||||
window, startPos, requiredPos, countAllRows, getConnectionFlags(),
|
|
||||||
mCancellationSignal);
|
|
||||||
return numRows;
|
|
||||||
} catch (SQLiteDatabaseCorruptException ex) {
|
|
||||||
onCorruption();
|
|
||||||
throw ex;
|
|
||||||
} catch (SQLiteException ex) {
|
|
||||||
Log.e(TAG, "exception: " + ex.getMessage() + "; query: " + getSql());
|
|
||||||
throw ex;
|
|
||||||
} finally {
|
|
||||||
window.releaseReference();
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
releaseReference();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "SQLiteQuery: " + getSql();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,663 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2006 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
import android.database.Cursor;
|
|
||||||
import android.database.DatabaseUtils;
|
|
||||||
import android.os.CancellationSignal;
|
|
||||||
import android.os.OperationCanceledException;
|
|
||||||
import android.provider.BaseColumns;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Map.Entry;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This is a convience class that helps build SQL queries to be sent to
|
|
||||||
* {@link SQLiteDatabase} objects.
|
|
||||||
*/
|
|
||||||
public class SQLiteQueryBuilder
|
|
||||||
{
|
|
||||||
private static final String TAG = "SQLiteQueryBuilder";
|
|
||||||
private static final Pattern sLimitPattern =
|
|
||||||
Pattern.compile("\\s*\\d+\\s*(,\\s*\\d+\\s*)?");
|
|
||||||
|
|
||||||
private Map<String, String> mProjectionMap = null;
|
|
||||||
private String mTables = "";
|
|
||||||
private StringBuilder mWhereClause = null; // lazily created
|
|
||||||
private boolean mDistinct;
|
|
||||||
private SQLiteDatabase.CursorFactory mFactory;
|
|
||||||
private boolean mStrict;
|
|
||||||
|
|
||||||
public SQLiteQueryBuilder() {
|
|
||||||
mDistinct = false;
|
|
||||||
mFactory = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Mark the query as DISTINCT.
|
|
||||||
*
|
|
||||||
* @param distinct if true the query is DISTINCT, otherwise it isn't
|
|
||||||
*/
|
|
||||||
public void setDistinct(boolean distinct) {
|
|
||||||
mDistinct = distinct;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the list of tables being queried
|
|
||||||
*
|
|
||||||
* @return the list of tables being queried
|
|
||||||
*/
|
|
||||||
public String getTables() {
|
|
||||||
return mTables;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the list of tables to query. Multiple tables can be specified to perform a join.
|
|
||||||
* For example:
|
|
||||||
* setTables("foo, bar")
|
|
||||||
* setTables("foo LEFT OUTER JOIN bar ON (foo.id = bar.foo_id)")
|
|
||||||
*
|
|
||||||
* @param inTables the list of tables to query on
|
|
||||||
*/
|
|
||||||
public void setTables(String inTables) {
|
|
||||||
mTables = inTables;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Append a chunk to the WHERE clause of the query. All chunks appended are surrounded
|
|
||||||
* by parenthesis and ANDed with the selection passed to {@link #query}. The final
|
|
||||||
* WHERE clause looks like:
|
|
||||||
*
|
|
||||||
* WHERE (<append chunk 1><append chunk2>) AND (<query() selection parameter>)
|
|
||||||
*
|
|
||||||
* @param inWhere the chunk of text to append to the WHERE clause.
|
|
||||||
*/
|
|
||||||
public void appendWhere(CharSequence inWhere) {
|
|
||||||
if (mWhereClause == null) {
|
|
||||||
mWhereClause = new StringBuilder(inWhere.length() + 16);
|
|
||||||
}
|
|
||||||
if (mWhereClause.length() == 0) {
|
|
||||||
mWhereClause.append('(');
|
|
||||||
}
|
|
||||||
mWhereClause.append(inWhere);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Append a chunk to the WHERE clause of the query. All chunks appended are surrounded
|
|
||||||
* by parenthesis and ANDed with the selection passed to {@link #query}. The final
|
|
||||||
* WHERE clause looks like:
|
|
||||||
*
|
|
||||||
* WHERE (<append chunk 1><append chunk2>) AND (<query() selection parameter>)
|
|
||||||
*
|
|
||||||
* @param inWhere the chunk of text to append to the WHERE clause. it will be escaped
|
|
||||||
* to avoid SQL injection attacks
|
|
||||||
*/
|
|
||||||
public void appendWhereEscapeString(String inWhere) {
|
|
||||||
if (mWhereClause == null) {
|
|
||||||
mWhereClause = new StringBuilder(inWhere.length() + 16);
|
|
||||||
}
|
|
||||||
if (mWhereClause.length() == 0) {
|
|
||||||
mWhereClause.append('(');
|
|
||||||
}
|
|
||||||
DatabaseUtils.appendEscapedSQLString(mWhereClause, inWhere);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the projection map for the query. The projection map maps
|
|
||||||
* from column names that the caller passes into query to database
|
|
||||||
* column names. This is useful for renaming columns as well as
|
|
||||||
* disambiguating column names when doing joins. For example you
|
|
||||||
* could map "name" to "people.name". If a projection map is set
|
|
||||||
* it must contain all column names the user may request, even if
|
|
||||||
* the key and value are the same.
|
|
||||||
*
|
|
||||||
* @param columnMap maps from the user column names to the database column names
|
|
||||||
*/
|
|
||||||
public void setProjectionMap(Map<String, String> columnMap) {
|
|
||||||
mProjectionMap = columnMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the cursor factory to be used for the query. You can use
|
|
||||||
* one factory for all queries on a database but it is normally
|
|
||||||
* easier to specify the factory when doing this query.
|
|
||||||
*
|
|
||||||
* @param factory the factory to use.
|
|
||||||
*/
|
|
||||||
public void setCursorFactory(SQLiteDatabase.CursorFactory factory) {
|
|
||||||
mFactory = factory;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* When set, the selection is verified against malicious arguments.
|
|
||||||
* When using this class to create a statement using
|
|
||||||
* {@link #buildQueryString(boolean, String, String[], String, String, String, String, String)},
|
|
||||||
* non-numeric limits will raise an exception. If a projection map is specified, fields
|
|
||||||
* not in that map will be ignored.
|
|
||||||
* If this class is used to execute the statement directly using
|
|
||||||
* {@link #query(SQLiteDatabase, String[], String, String[], String, String, String)}
|
|
||||||
* or
|
|
||||||
* {@link #query(SQLiteDatabase, String[], String, String[], String, String, String, String)},
|
|
||||||
* additionally also parenthesis escaping selection are caught.
|
|
||||||
*
|
|
||||||
* To summarize: To get maximum protection against malicious third party apps (for example
|
|
||||||
* content provider consumers), make sure to do the following:
|
|
||||||
* <ul>
|
|
||||||
* <li>Set this value to true</li>
|
|
||||||
* <li>Use a projection map</li>
|
|
||||||
* <li>Use one of the query overloads instead of getting the statement as a sql string</li>
|
|
||||||
* </ul>
|
|
||||||
* By default, this value is false.
|
|
||||||
*/
|
|
||||||
public void setStrict(boolean flag) {
|
|
||||||
mStrict = flag;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Build an SQL query string from the given clauses.
|
|
||||||
*
|
|
||||||
* @param distinct true if you want each row to be unique, false otherwise.
|
|
||||||
* @param tables The table names to compile the query against.
|
|
||||||
* @param columns A list of which columns to return. Passing null will
|
|
||||||
* return all columns, which is discouraged to prevent reading
|
|
||||||
* data from storage that isn't going to be used.
|
|
||||||
* @param where A filter declaring which rows to return, formatted as an SQL
|
|
||||||
* WHERE clause (excluding the WHERE itself). Passing null will
|
|
||||||
* return all rows for the given URL.
|
|
||||||
* @param groupBy A filter declaring how to group rows, formatted as an SQL
|
|
||||||
* GROUP BY clause (excluding the GROUP BY itself). Passing null
|
|
||||||
* will cause the rows to not be grouped.
|
|
||||||
* @param having A filter declare which row groups to include in the cursor,
|
|
||||||
* if row grouping is being used, formatted as an SQL HAVING
|
|
||||||
* clause (excluding the HAVING itself). Passing null will cause
|
|
||||||
* all row groups to be included, and is required when row
|
|
||||||
* grouping is not being used.
|
|
||||||
* @param orderBy How to order the rows, formatted as an SQL ORDER BY clause
|
|
||||||
* (excluding the ORDER BY itself). Passing null will use the
|
|
||||||
* default sort order, which may be unordered.
|
|
||||||
* @param limit Limits the number of rows returned by the query,
|
|
||||||
* formatted as LIMIT clause. Passing null denotes no LIMIT clause.
|
|
||||||
* @return the SQL query string
|
|
||||||
*/
|
|
||||||
public static String buildQueryString(
|
|
||||||
boolean distinct, String tables, String[] columns, String where,
|
|
||||||
String groupBy, String having, String orderBy, String limit) {
|
|
||||||
if (TextUtils.isEmpty(groupBy) && !TextUtils.isEmpty(having)) {
|
|
||||||
throw new IllegalArgumentException(
|
|
||||||
"HAVING clauses are only permitted when using a groupBy clause");
|
|
||||||
}
|
|
||||||
if (!TextUtils.isEmpty(limit) && !sLimitPattern.matcher(limit).matches()) {
|
|
||||||
throw new IllegalArgumentException("invalid LIMIT clauses:" + limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder query = new StringBuilder(120);
|
|
||||||
|
|
||||||
query.append("SELECT ");
|
|
||||||
if (distinct) {
|
|
||||||
query.append("DISTINCT ");
|
|
||||||
}
|
|
||||||
if (columns != null && columns.length != 0) {
|
|
||||||
appendColumns(query, columns);
|
|
||||||
} else {
|
|
||||||
query.append("* ");
|
|
||||||
}
|
|
||||||
query.append("FROM ");
|
|
||||||
query.append(tables);
|
|
||||||
appendClause(query, " WHERE ", where);
|
|
||||||
appendClause(query, " GROUP BY ", groupBy);
|
|
||||||
appendClause(query, " HAVING ", having);
|
|
||||||
appendClause(query, " ORDER BY ", orderBy);
|
|
||||||
appendClause(query, " LIMIT ", limit);
|
|
||||||
|
|
||||||
return query.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void appendClause(StringBuilder s, String name, String clause) {
|
|
||||||
if (!TextUtils.isEmpty(clause)) {
|
|
||||||
s.append(name);
|
|
||||||
s.append(clause);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add the names that are non-null in columns to s, separating
|
|
||||||
* them with commas.
|
|
||||||
*/
|
|
||||||
public static void appendColumns(StringBuilder s, String[] columns) {
|
|
||||||
int n = columns.length;
|
|
||||||
|
|
||||||
for (int i = 0; i < n; i++) {
|
|
||||||
String column = columns[i];
|
|
||||||
|
|
||||||
if (column != null) {
|
|
||||||
if (i > 0) {
|
|
||||||
s.append(", ");
|
|
||||||
}
|
|
||||||
s.append(column);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.append(' ');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform a query by combining all current settings and the
|
|
||||||
* information passed into this method.
|
|
||||||
*
|
|
||||||
* @param db the database to query on
|
|
||||||
* @param projectionIn A list of which columns to return. Passing
|
|
||||||
* null will return all columns, which is discouraged to prevent
|
|
||||||
* reading data from storage that isn't going to be used.
|
|
||||||
* @param selection A filter declaring which rows to return,
|
|
||||||
* formatted as an SQL WHERE clause (excluding the WHERE
|
|
||||||
* itself). Passing null will return all rows for the given URL.
|
|
||||||
* @param selectionArgs You may include ?s in selection, which
|
|
||||||
* will be replaced by the values from selectionArgs, in order
|
|
||||||
* that they appear in the selection. The values will be bound
|
|
||||||
* as Strings.
|
|
||||||
* @param groupBy A filter declaring how to group rows, formatted
|
|
||||||
* as an SQL GROUP BY clause (excluding the GROUP BY
|
|
||||||
* itself). Passing null will cause the rows to not be grouped.
|
|
||||||
* @param having A filter declare which row groups to include in
|
|
||||||
* the cursor, if row grouping is being used, formatted as an
|
|
||||||
* SQL HAVING clause (excluding the HAVING itself). Passing
|
|
||||||
* null will cause all row groups to be included, and is
|
|
||||||
* required when row grouping is not being used.
|
|
||||||
* @param sortOrder How to order the rows, formatted as an SQL
|
|
||||||
* ORDER BY clause (excluding the ORDER BY itself). Passing null
|
|
||||||
* will use the default sort order, which may be unordered.
|
|
||||||
* @return a cursor over the result set
|
|
||||||
* @see android.content.ContentResolver#query(android.net.Uri, String[],
|
|
||||||
* String, String[], String)
|
|
||||||
*/
|
|
||||||
public Cursor query(SQLiteDatabase db, String[] projectionIn,
|
|
||||||
String selection, String[] selectionArgs, String groupBy,
|
|
||||||
String having, String sortOrder) {
|
|
||||||
return query(db, projectionIn, selection, selectionArgs, groupBy, having, sortOrder,
|
|
||||||
null /* limit */, null /* cancellationSignal */);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform a query by combining all current settings and the
|
|
||||||
* information passed into this method.
|
|
||||||
*
|
|
||||||
* @param db the database to query on
|
|
||||||
* @param projectionIn A list of which columns to return. Passing
|
|
||||||
* null will return all columns, which is discouraged to prevent
|
|
||||||
* reading data from storage that isn't going to be used.
|
|
||||||
* @param selection A filter declaring which rows to return,
|
|
||||||
* formatted as an SQL WHERE clause (excluding the WHERE
|
|
||||||
* itself). Passing null will return all rows for the given URL.
|
|
||||||
* @param selectionArgs You may include ?s in selection, which
|
|
||||||
* will be replaced by the values from selectionArgs, in order
|
|
||||||
* that they appear in the selection. The values will be bound
|
|
||||||
* as Strings.
|
|
||||||
* @param groupBy A filter declaring how to group rows, formatted
|
|
||||||
* as an SQL GROUP BY clause (excluding the GROUP BY
|
|
||||||
* itself). Passing null will cause the rows to not be grouped.
|
|
||||||
* @param having A filter declare which row groups to include in
|
|
||||||
* the cursor, if row grouping is being used, formatted as an
|
|
||||||
* SQL HAVING clause (excluding the HAVING itself). Passing
|
|
||||||
* null will cause all row groups to be included, and is
|
|
||||||
* required when row grouping is not being used.
|
|
||||||
* @param sortOrder How to order the rows, formatted as an SQL
|
|
||||||
* ORDER BY clause (excluding the ORDER BY itself). Passing null
|
|
||||||
* will use the default sort order, which may be unordered.
|
|
||||||
* @param limit Limits the number of rows returned by the query,
|
|
||||||
* formatted as LIMIT clause. Passing null denotes no LIMIT clause.
|
|
||||||
* @return a cursor over the result set
|
|
||||||
* @see android.content.ContentResolver#query(android.net.Uri, String[],
|
|
||||||
* String, String[], String)
|
|
||||||
*/
|
|
||||||
public Cursor query(SQLiteDatabase db, String[] projectionIn,
|
|
||||||
String selection, String[] selectionArgs, String groupBy,
|
|
||||||
String having, String sortOrder, String limit) {
|
|
||||||
return query(db, projectionIn, selection, selectionArgs,
|
|
||||||
groupBy, having, sortOrder, limit, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform a query by combining all current settings and the
|
|
||||||
* information passed into this method.
|
|
||||||
*
|
|
||||||
* @param db the database to query on
|
|
||||||
* @param projectionIn A list of which columns to return. Passing
|
|
||||||
* null will return all columns, which is discouraged to prevent
|
|
||||||
* reading data from storage that isn't going to be used.
|
|
||||||
* @param selection A filter declaring which rows to return,
|
|
||||||
* formatted as an SQL WHERE clause (excluding the WHERE
|
|
||||||
* itself). Passing null will return all rows for the given URL.
|
|
||||||
* @param selectionArgs You may include ?s in selection, which
|
|
||||||
* will be replaced by the values from selectionArgs, in order
|
|
||||||
* that they appear in the selection. The values will be bound
|
|
||||||
* as Strings.
|
|
||||||
* @param groupBy A filter declaring how to group rows, formatted
|
|
||||||
* as an SQL GROUP BY clause (excluding the GROUP BY
|
|
||||||
* itself). Passing null will cause the rows to not be grouped.
|
|
||||||
* @param having A filter declare which row groups to include in
|
|
||||||
* the cursor, if row grouping is being used, formatted as an
|
|
||||||
* SQL HAVING clause (excluding the HAVING itself). Passing
|
|
||||||
* null will cause all row groups to be included, and is
|
|
||||||
* required when row grouping is not being used.
|
|
||||||
* @param sortOrder How to order the rows, formatted as an SQL
|
|
||||||
* ORDER BY clause (excluding the ORDER BY itself). Passing null
|
|
||||||
* will use the default sort order, which may be unordered.
|
|
||||||
* @param limit Limits the number of rows returned by the query,
|
|
||||||
* formatted as LIMIT clause. Passing null denotes no LIMIT clause.
|
|
||||||
* @param cancellationSignal A signal to cancel the operation in progress, or null if none.
|
|
||||||
* If the operation is canceled, then {@link OperationCanceledException} will be thrown
|
|
||||||
* when the query is executed.
|
|
||||||
* @return a cursor over the result set
|
|
||||||
* @see android.content.ContentResolver#query(android.net.Uri, String[],
|
|
||||||
* String, String[], String)
|
|
||||||
*/
|
|
||||||
public Cursor query(SQLiteDatabase db, String[] projectionIn,
|
|
||||||
String selection, String[] selectionArgs, String groupBy,
|
|
||||||
String having, String sortOrder, String limit, CancellationSignal cancellationSignal) {
|
|
||||||
if (mTables == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mStrict && selection != null && selection.length() > 0) {
|
|
||||||
// Validate the user-supplied selection to detect syntactic anomalies
|
|
||||||
// in the selection string that could indicate a SQL injection attempt.
|
|
||||||
// The idea is to ensure that the selection clause is a valid SQL expression
|
|
||||||
// by compiling it twice: once wrapped in parentheses and once as
|
|
||||||
// originally specified. An attacker cannot create an expression that
|
|
||||||
// would escape the SQL expression while maintaining balanced parentheses
|
|
||||||
// in both the wrapped and original forms.
|
|
||||||
String sqlForValidation = buildQuery(projectionIn, "(" + selection + ")", groupBy,
|
|
||||||
having, sortOrder, limit);
|
|
||||||
validateQuerySql(db, sqlForValidation,
|
|
||||||
cancellationSignal); // will throw if query is invalid
|
|
||||||
}
|
|
||||||
|
|
||||||
String sql = buildQuery(
|
|
||||||
projectionIn, selection, groupBy, having,
|
|
||||||
sortOrder, limit);
|
|
||||||
|
|
||||||
if (Log.isLoggable(TAG, Log.DEBUG)) {
|
|
||||||
Log.d(TAG, "Performing query: " + sql);
|
|
||||||
}
|
|
||||||
return db.rawQueryWithFactory(
|
|
||||||
mFactory, sql, selectionArgs,
|
|
||||||
SQLiteDatabase.findEditTable(mTables),
|
|
||||||
cancellationSignal); // will throw if query is invalid
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verifies that a SQL SELECT statement is valid by compiling it.
|
|
||||||
* If the SQL statement is not valid, this method will throw a {@link SQLiteException}.
|
|
||||||
*/
|
|
||||||
private void validateQuerySql(SQLiteDatabase db, String sql,
|
|
||||||
CancellationSignal cancellationSignal) {
|
|
||||||
db.getThreadSession().prepare(sql,
|
|
||||||
db.getThreadDefaultConnectionFlags(true /*readOnly*/), cancellationSignal, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a SELECT statement suitable for use in a group of
|
|
||||||
* SELECT statements that will be joined through UNION operators
|
|
||||||
* in buildUnionQuery.
|
|
||||||
*
|
|
||||||
* @param projectionIn A list of which columns to return. Passing
|
|
||||||
* null will return all columns, which is discouraged to
|
|
||||||
* prevent reading data from storage that isn't going to be
|
|
||||||
* used.
|
|
||||||
* @param selection A filter declaring which rows to return,
|
|
||||||
* formatted as an SQL WHERE clause (excluding the WHERE
|
|
||||||
* itself). Passing null will return all rows for the given
|
|
||||||
* URL.
|
|
||||||
* @param groupBy A filter declaring how to group rows, formatted
|
|
||||||
* as an SQL GROUP BY clause (excluding the GROUP BY itself).
|
|
||||||
* Passing null will cause the rows to not be grouped.
|
|
||||||
* @param having A filter declare which row groups to include in
|
|
||||||
* the cursor, if row grouping is being used, formatted as an
|
|
||||||
* SQL HAVING clause (excluding the HAVING itself). Passing
|
|
||||||
* null will cause all row groups to be included, and is
|
|
||||||
* required when row grouping is not being used.
|
|
||||||
* @param sortOrder How to order the rows, formatted as an SQL
|
|
||||||
* ORDER BY clause (excluding the ORDER BY itself). Passing null
|
|
||||||
* will use the default sort order, which may be unordered.
|
|
||||||
* @param limit Limits the number of rows returned by the query,
|
|
||||||
* formatted as LIMIT clause. Passing null denotes no LIMIT clause.
|
|
||||||
* @return the resulting SQL SELECT statement
|
|
||||||
*/
|
|
||||||
public String buildQuery(
|
|
||||||
String[] projectionIn, String selection, String groupBy,
|
|
||||||
String having, String sortOrder, String limit) {
|
|
||||||
String[] projection = computeProjection(projectionIn);
|
|
||||||
|
|
||||||
StringBuilder where = new StringBuilder();
|
|
||||||
boolean hasBaseWhereClause = mWhereClause != null && mWhereClause.length() > 0;
|
|
||||||
|
|
||||||
if (hasBaseWhereClause) {
|
|
||||||
where.append(mWhereClause.toString());
|
|
||||||
where.append(')');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tack on the user's selection, if present.
|
|
||||||
if (selection != null && selection.length() > 0) {
|
|
||||||
if (hasBaseWhereClause) {
|
|
||||||
where.append(" AND ");
|
|
||||||
}
|
|
||||||
|
|
||||||
where.append('(');
|
|
||||||
where.append(selection);
|
|
||||||
where.append(')');
|
|
||||||
}
|
|
||||||
|
|
||||||
return buildQueryString(
|
|
||||||
mDistinct, mTables, projection, where.toString(),
|
|
||||||
groupBy, having, sortOrder, limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated This method's signature is misleading since no SQL parameter
|
|
||||||
* substitution is carried out. The selection arguments parameter does not get
|
|
||||||
* used at all. To avoid confusion, call
|
|
||||||
* {@link #buildQuery(String[], String, String, String, String, String)} instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public String buildQuery(
|
|
||||||
String[] projectionIn, String selection, String[] selectionArgs,
|
|
||||||
String groupBy, String having, String sortOrder, String limit) {
|
|
||||||
return buildQuery(projectionIn, selection, groupBy, having, sortOrder, limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Construct a SELECT statement suitable for use in a group of
|
|
||||||
* SELECT statements that will be joined through UNION operators
|
|
||||||
* in buildUnionQuery.
|
|
||||||
*
|
|
||||||
* @param typeDiscriminatorColumn the name of the result column
|
|
||||||
* whose cells will contain the name of the table from which
|
|
||||||
* each row was drawn.
|
|
||||||
* @param unionColumns the names of the columns to appear in the
|
|
||||||
* result. This may include columns that do not appear in the
|
|
||||||
* table this SELECT is querying (i.e. mTables), but that do
|
|
||||||
* appear in one of the other tables in the UNION query that we
|
|
||||||
* are constructing.
|
|
||||||
* @param columnsPresentInTable a Set of the names of the columns
|
|
||||||
* that appear in this table (i.e. in the table whose name is
|
|
||||||
* mTables). Since columns in unionColumns include columns that
|
|
||||||
* appear only in other tables, we use this array to distinguish
|
|
||||||
* which ones actually are present. Other columns will have
|
|
||||||
* NULL values for results from this subquery.
|
|
||||||
* @param computedColumnsOffset all columns in unionColumns before
|
|
||||||
* this index are included under the assumption that they're
|
|
||||||
* computed and therefore won't appear in columnsPresentInTable,
|
|
||||||
* e.g. "date * 1000 as normalized_date"
|
|
||||||
* @param typeDiscriminatorValue the value used for the
|
|
||||||
* type-discriminator column in this subquery
|
|
||||||
* @param selection A filter declaring which rows to return,
|
|
||||||
* formatted as an SQL WHERE clause (excluding the WHERE
|
|
||||||
* itself). Passing null will return all rows for the given
|
|
||||||
* URL.
|
|
||||||
* @param groupBy A filter declaring how to group rows, formatted
|
|
||||||
* as an SQL GROUP BY clause (excluding the GROUP BY itself).
|
|
||||||
* Passing null will cause the rows to not be grouped.
|
|
||||||
* @param having A filter declare which row groups to include in
|
|
||||||
* the cursor, if row grouping is being used, formatted as an
|
|
||||||
* SQL HAVING clause (excluding the HAVING itself). Passing
|
|
||||||
* null will cause all row groups to be included, and is
|
|
||||||
* required when row grouping is not being used.
|
|
||||||
* @return the resulting SQL SELECT statement
|
|
||||||
*/
|
|
||||||
public String buildUnionSubQuery(
|
|
||||||
String typeDiscriminatorColumn,
|
|
||||||
String[] unionColumns,
|
|
||||||
Set<String> columnsPresentInTable,
|
|
||||||
int computedColumnsOffset,
|
|
||||||
String typeDiscriminatorValue,
|
|
||||||
String selection,
|
|
||||||
String groupBy,
|
|
||||||
String having) {
|
|
||||||
int unionColumnsCount = unionColumns.length;
|
|
||||||
String[] projectionIn = new String[unionColumnsCount];
|
|
||||||
|
|
||||||
for (int i = 0; i < unionColumnsCount; i++) {
|
|
||||||
String unionColumn = unionColumns[i];
|
|
||||||
|
|
||||||
if (unionColumn.equals(typeDiscriminatorColumn)) {
|
|
||||||
projectionIn[i] = "'" + typeDiscriminatorValue + "' AS "
|
|
||||||
+ typeDiscriminatorColumn;
|
|
||||||
} else if (i <= computedColumnsOffset
|
|
||||||
|| columnsPresentInTable.contains(unionColumn)) {
|
|
||||||
projectionIn[i] = unionColumn;
|
|
||||||
} else {
|
|
||||||
projectionIn[i] = "NULL AS " + unionColumn;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return buildQuery(
|
|
||||||
projectionIn, selection, groupBy, having,
|
|
||||||
null /* sortOrder */,
|
|
||||||
null /* limit */);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @deprecated This method's signature is misleading since no SQL parameter
|
|
||||||
* substitution is carried out. The selection arguments parameter does not get
|
|
||||||
* used at all. To avoid confusion, call
|
|
||||||
* {@link #buildUnionSubQuery}
|
|
||||||
* instead.
|
|
||||||
*/
|
|
||||||
@Deprecated
|
|
||||||
public String buildUnionSubQuery(
|
|
||||||
String typeDiscriminatorColumn,
|
|
||||||
String[] unionColumns,
|
|
||||||
Set<String> columnsPresentInTable,
|
|
||||||
int computedColumnsOffset,
|
|
||||||
String typeDiscriminatorValue,
|
|
||||||
String selection,
|
|
||||||
String[] selectionArgs,
|
|
||||||
String groupBy,
|
|
||||||
String having) {
|
|
||||||
return buildUnionSubQuery(
|
|
||||||
typeDiscriminatorColumn, unionColumns, columnsPresentInTable,
|
|
||||||
computedColumnsOffset, typeDiscriminatorValue, selection,
|
|
||||||
groupBy, having);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Given a set of subqueries, all of which are SELECT statements,
|
|
||||||
* construct a query that returns the union of what those
|
|
||||||
* subqueries return.
|
|
||||||
* @param subQueries an array of SQL SELECT statements, all of
|
|
||||||
* which must have the same columns as the same positions in
|
|
||||||
* their results
|
|
||||||
* @param sortOrder How to order the rows, formatted as an SQL
|
|
||||||
* ORDER BY clause (excluding the ORDER BY itself). Passing
|
|
||||||
* null will use the default sort order, which may be unordered.
|
|
||||||
* @param limit The limit clause, which applies to the entire union result set
|
|
||||||
*
|
|
||||||
* @return the resulting SQL SELECT statement
|
|
||||||
*/
|
|
||||||
public String buildUnionQuery(String[] subQueries, String sortOrder, String limit) {
|
|
||||||
StringBuilder query = new StringBuilder(128);
|
|
||||||
int subQueryCount = subQueries.length;
|
|
||||||
String unionOperator = mDistinct ? " UNION " : " UNION ALL ";
|
|
||||||
|
|
||||||
for (int i = 0; i < subQueryCount; i++) {
|
|
||||||
if (i > 0) {
|
|
||||||
query.append(unionOperator);
|
|
||||||
}
|
|
||||||
query.append(subQueries[i]);
|
|
||||||
}
|
|
||||||
appendClause(query, " ORDER BY ", sortOrder);
|
|
||||||
appendClause(query, " LIMIT ", limit);
|
|
||||||
return query.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String[] computeProjection(String[] projectionIn) {
|
|
||||||
if (projectionIn != null && projectionIn.length > 0) {
|
|
||||||
if (mProjectionMap != null) {
|
|
||||||
String[] projection = new String[projectionIn.length];
|
|
||||||
int length = projectionIn.length;
|
|
||||||
|
|
||||||
for (int i = 0; i < length; i++) {
|
|
||||||
String userColumn = projectionIn[i];
|
|
||||||
String column = mProjectionMap.get(userColumn);
|
|
||||||
|
|
||||||
if (column != null) {
|
|
||||||
projection[i] = column;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mStrict &&
|
|
||||||
( userColumn.contains(" AS ") || userColumn.contains(" as "))) {
|
|
||||||
/* A column alias already exist */
|
|
||||||
projection[i] = userColumn;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new IllegalArgumentException("Invalid column "
|
|
||||||
+ projectionIn[i]);
|
|
||||||
}
|
|
||||||
return projection;
|
|
||||||
} else {
|
|
||||||
return projectionIn;
|
|
||||||
}
|
|
||||||
} else if (mProjectionMap != null) {
|
|
||||||
// Return all columns in projection map.
|
|
||||||
Set<Entry<String, String>> entrySet = mProjectionMap.entrySet();
|
|
||||||
String[] projection = new String[entrySet.size()];
|
|
||||||
Iterator<Entry<String, String>> entryIter = entrySet.iterator();
|
|
||||||
int i = 0;
|
|
||||||
|
|
||||||
while (entryIter.hasNext()) {
|
|
||||||
Entry<String, String> entry = entryIter.next();
|
|
||||||
|
|
||||||
// Don't include the _count column when people ask for no projection.
|
|
||||||
if (entry.getKey().equals(BaseColumns._COUNT)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
projection[i++] = entry.getValue();
|
|
||||||
}
|
|
||||||
return projection;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
public class SQLiteReadOnlyDatabaseException extends SQLiteException {
|
|
||||||
public SQLiteReadOnlyDatabaseException() {}
|
|
||||||
|
|
||||||
public SQLiteReadOnlyDatabaseException(String error) {
|
|
||||||
super(error);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,967 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2011 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
import android.database.CursorWindow;
|
|
||||||
import android.database.DatabaseUtils;
|
|
||||||
import android.os.CancellationSignal;
|
|
||||||
import android.os.OperationCanceledException;
|
|
||||||
import android.os.ParcelFileDescriptor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provides a single client the ability to use a database.
|
|
||||||
*
|
|
||||||
* <h2>About database sessions</h2>
|
|
||||||
* <p>
|
|
||||||
* Database access is always performed using a session. The session
|
|
||||||
* manages the lifecycle of transactions and database connections.
|
|
||||||
* </p><p>
|
|
||||||
* Sessions can be used to perform both read-only and read-write operations.
|
|
||||||
* There is some advantage to knowing when a session is being used for
|
|
||||||
* read-only purposes because the connection pool can optimize the use
|
|
||||||
* of the available connections to permit multiple read-only operations
|
|
||||||
* to execute in parallel whereas read-write operations may need to be serialized.
|
|
||||||
* </p><p>
|
|
||||||
* When <em>Write Ahead Logging (WAL)</em> is enabled, the database can
|
|
||||||
* execute simultaneous read-only and read-write transactions, provided that
|
|
||||||
* at most one read-write transaction is performed at a time. When WAL is not
|
|
||||||
* enabled, read-only transactions can execute in parallel but read-write
|
|
||||||
* transactions are mutually exclusive.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* <h2>Ownership and concurrency guarantees</h2>
|
|
||||||
* <p>
|
|
||||||
* Session objects are not thread-safe. In fact, session objects are thread-bound.
|
|
||||||
* The {@link SQLiteDatabase} uses a thread-local variable to associate a session
|
|
||||||
* with each thread for the use of that thread alone. Consequently, each thread
|
|
||||||
* has its own session object and therefore its own transaction state independent
|
|
||||||
* of other threads.
|
|
||||||
* </p><p>
|
|
||||||
* A thread has at most one session per database. This constraint ensures that
|
|
||||||
* a thread can never use more than one database connection at a time for a
|
|
||||||
* given database. As the number of available database connections is limited,
|
|
||||||
* if a single thread tried to acquire multiple connections for the same database
|
|
||||||
* at the same time, it might deadlock. Therefore we allow there to be only
|
|
||||||
* one session (so, at most one connection) per thread per database.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* <h2>Transactions</h2>
|
|
||||||
* <p>
|
|
||||||
* There are two kinds of transaction: implicit transactions and explicit
|
|
||||||
* transactions.
|
|
||||||
* </p><p>
|
|
||||||
* An implicit transaction is created whenever a database operation is requested
|
|
||||||
* and there is no explicit transaction currently in progress. An implicit transaction
|
|
||||||
* only lasts for the duration of the database operation in question and then it
|
|
||||||
* is ended. If the database operation was successful, then its changes are committed.
|
|
||||||
* </p><p>
|
|
||||||
* An explicit transaction is started by calling {@link #beginTransaction} and
|
|
||||||
* specifying the desired transaction mode. Once an explicit transaction has begun,
|
|
||||||
* all subsequent database operations will be performed as part of that transaction.
|
|
||||||
* To end an explicit transaction, first call {@link #setTransactionSuccessful} if the
|
|
||||||
* transaction was successful, then call {@link #end}. If the transaction was
|
|
||||||
* marked successful, its changes will be committed, otherwise they will be rolled back.
|
|
||||||
* </p><p>
|
|
||||||
* Explicit transactions can also be nested. A nested explicit transaction is
|
|
||||||
* started with {@link #beginTransaction}, marked successful with
|
|
||||||
* {@link #setTransactionSuccessful}and ended with {@link #endTransaction}.
|
|
||||||
* If any nested transaction is not marked successful, then the entire transaction
|
|
||||||
* including all of its nested transactions will be rolled back
|
|
||||||
* when the outermost transaction is ended.
|
|
||||||
* </p><p>
|
|
||||||
* To improve concurrency, an explicit transaction can be yielded by calling
|
|
||||||
* {@link #yieldTransaction}. If there is contention for use of the database,
|
|
||||||
* then yielding ends the current transaction, commits its changes, releases the
|
|
||||||
* database connection for use by another session for a little while, and starts a
|
|
||||||
* new transaction with the same properties as the original one.
|
|
||||||
* Changes committed by {@link #yieldTransaction} cannot be rolled back.
|
|
||||||
* </p><p>
|
|
||||||
* When a transaction is started, the client can provide a {@link SQLiteTransactionListener}
|
|
||||||
* to listen for notifications of transaction-related events.
|
|
||||||
* </p><p>
|
|
||||||
* Recommended usage:
|
|
||||||
* <code><pre>
|
|
||||||
* // First, begin the transaction.
|
|
||||||
* session.beginTransaction(SQLiteSession.TRANSACTION_MODE_DEFERRED, 0);
|
|
||||||
* try {
|
|
||||||
* // Then do stuff...
|
|
||||||
* session.execute("INSERT INTO ...", null, 0);
|
|
||||||
*
|
|
||||||
* // As the very last step before ending the transaction, mark it successful.
|
|
||||||
* session.setTransactionSuccessful();
|
|
||||||
* } finally {
|
|
||||||
* // Finally, end the transaction.
|
|
||||||
* // This statement will commit the transaction if it was marked successful or
|
|
||||||
* // roll it back otherwise.
|
|
||||||
* session.endTransaction();
|
|
||||||
* }
|
|
||||||
* </pre></code>
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* <h2>Database connections</h2>
|
|
||||||
* <p>
|
|
||||||
* A {@link SQLiteDatabase} can have multiple active sessions at the same
|
|
||||||
* time. Each session acquires and releases connections to the database
|
|
||||||
* as needed to perform each requested database transaction. If all connections
|
|
||||||
* are in use, then database transactions on some sessions will block until a
|
|
||||||
* connection becomes available.
|
|
||||||
* </p><p>
|
|
||||||
* The session acquires a single database connection only for the duration
|
|
||||||
* of a single (implicit or explicit) database transaction, then releases it.
|
|
||||||
* This characteristic allows a small pool of database connections to be shared
|
|
||||||
* efficiently by multiple sessions as long as they are not all trying to perform
|
|
||||||
* database transactions at the same time.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* <h2>Responsiveness</h2>
|
|
||||||
* <p>
|
|
||||||
* Because there are a limited number of database connections and the session holds
|
|
||||||
* a database connection for the entire duration of a database transaction,
|
|
||||||
* it is important to keep transactions short. This is especially important
|
|
||||||
* for read-write transactions since they may block other transactions
|
|
||||||
* from executing. Consider calling {@link #yieldTransaction} periodically
|
|
||||||
* during long-running transactions.
|
|
||||||
* </p><p>
|
|
||||||
* Another important consideration is that transactions that take too long to
|
|
||||||
* run may cause the application UI to become unresponsive. Even if the transaction
|
|
||||||
* is executed in a background thread, the user will get bored and
|
|
||||||
* frustrated if the application shows no data for several seconds while
|
|
||||||
* a transaction runs.
|
|
||||||
* </p><p>
|
|
||||||
* Guidelines:
|
|
||||||
* <ul>
|
|
||||||
* <li>Do not perform database transactions on the UI thread.</li>
|
|
||||||
* <li>Keep database transactions as short as possible.</li>
|
|
||||||
* <li>Simple queries often run faster than complex queries.</li>
|
|
||||||
* <li>Measure the performance of your database transactions.</li>
|
|
||||||
* <li>Consider what will happen when the size of the data set grows.
|
|
||||||
* A query that works well on 100 rows may struggle with 10,000.</li>
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* <h2>Reentrance</h2>
|
|
||||||
* <p>
|
|
||||||
* This class must tolerate reentrant execution of SQLite operations because
|
|
||||||
* triggers may call custom SQLite functions that perform additional queries.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public final class SQLiteSession {
|
|
||||||
private final SQLiteConnectionPool mConnectionPool;
|
|
||||||
|
|
||||||
private SQLiteConnection mConnection;
|
|
||||||
private int mConnectionFlags;
|
|
||||||
private int mConnectionUseCount;
|
|
||||||
private Transaction mTransactionPool;
|
|
||||||
private Transaction mTransactionStack;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transaction mode: Deferred.
|
|
||||||
* <p>
|
|
||||||
* In a deferred transaction, no locks are acquired on the database
|
|
||||||
* until the first operation is performed. If the first operation is
|
|
||||||
* read-only, then a <code>SHARED</code> lock is acquired, otherwise
|
|
||||||
* a <code>RESERVED</code> lock is acquired.
|
|
||||||
* </p><p>
|
|
||||||
* While holding a <code>SHARED</code> lock, this session is only allowed to
|
|
||||||
* read but other sessions are allowed to read or write.
|
|
||||||
* While holding a <code>RESERVED</code> lock, this session is allowed to read
|
|
||||||
* or write but other sessions are only allowed to read.
|
|
||||||
* </p><p>
|
|
||||||
* Because the lock is only acquired when needed in a deferred transaction,
|
|
||||||
* it is possible for another session to write to the database first before
|
|
||||||
* this session has a chance to do anything.
|
|
||||||
* </p><p>
|
|
||||||
* Corresponds to the SQLite <code>BEGIN DEFERRED</code> transaction mode.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public static final int TRANSACTION_MODE_DEFERRED = 0;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transaction mode: Immediate.
|
|
||||||
* <p>
|
|
||||||
* When an immediate transaction begins, the session acquires a
|
|
||||||
* <code>RESERVED</code> lock.
|
|
||||||
* </p><p>
|
|
||||||
* While holding a <code>RESERVED</code> lock, this session is allowed to read
|
|
||||||
* or write but other sessions are only allowed to read.
|
|
||||||
* </p><p>
|
|
||||||
* Corresponds to the SQLite <code>BEGIN IMMEDIATE</code> transaction mode.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public static final int TRANSACTION_MODE_IMMEDIATE = 1;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Transaction mode: Exclusive.
|
|
||||||
* <p>
|
|
||||||
* When an exclusive transaction begins, the session acquires an
|
|
||||||
* <code>EXCLUSIVE</code> lock.
|
|
||||||
* </p><p>
|
|
||||||
* While holding an <code>EXCLUSIVE</code> lock, this session is allowed to read
|
|
||||||
* or write but no other sessions are allowed to access the database.
|
|
||||||
* </p><p>
|
|
||||||
* Corresponds to the SQLite <code>BEGIN EXCLUSIVE</code> transaction mode.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public static final int TRANSACTION_MODE_EXCLUSIVE = 2;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a session bound to the specified connection pool.
|
|
||||||
*
|
|
||||||
* @param connectionPool The connection pool.
|
|
||||||
*/
|
|
||||||
public SQLiteSession(SQLiteConnectionPool connectionPool) {
|
|
||||||
if (connectionPool == null) {
|
|
||||||
throw new IllegalArgumentException("connectionPool must not be null");
|
|
||||||
}
|
|
||||||
|
|
||||||
mConnectionPool = connectionPool;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the session has a transaction in progress.
|
|
||||||
*
|
|
||||||
* @return True if the session has a transaction in progress.
|
|
||||||
*/
|
|
||||||
public boolean hasTransaction() {
|
|
||||||
return mTransactionStack != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the session has a nested transaction in progress.
|
|
||||||
*
|
|
||||||
* @return True if the session has a nested transaction in progress.
|
|
||||||
*/
|
|
||||||
public boolean hasNestedTransaction() {
|
|
||||||
return mTransactionStack != null && mTransactionStack.mParent != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the session has an active database connection.
|
|
||||||
*
|
|
||||||
* @return True if the session has an active database connection.
|
|
||||||
*/
|
|
||||||
public boolean hasConnection() {
|
|
||||||
return mConnection != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Begins a transaction.
|
|
||||||
* <p>
|
|
||||||
* Transactions may nest. If the transaction is not in progress,
|
|
||||||
* then a database connection is obtained and a new transaction is started.
|
|
||||||
* Otherwise, a nested transaction is started.
|
|
||||||
* </p><p>
|
|
||||||
* Each call to {@link #beginTransaction} must be matched exactly by a call
|
|
||||||
* to {@link #endTransaction}. To mark a transaction as successful,
|
|
||||||
* call {@link #setTransactionSuccessful} before calling {@link #endTransaction}.
|
|
||||||
* If the transaction is not successful, or if any of its nested
|
|
||||||
* transactions were not successful, then the entire transaction will
|
|
||||||
* be rolled back when the outermost transaction is ended.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param transactionMode The transaction mode. One of: {@link #TRANSACTION_MODE_DEFERRED},
|
|
||||||
* {@link #TRANSACTION_MODE_IMMEDIATE}, or {@link #TRANSACTION_MODE_EXCLUSIVE}.
|
|
||||||
* Ignored when creating a nested transaction.
|
|
||||||
* @param transactionListener The transaction listener, or null if none.
|
|
||||||
* @param connectionFlags The connection flags to use if a connection must be
|
|
||||||
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
|
|
||||||
* @param cancellationSignal A signal to cancel the operation in progress, or null if none.
|
|
||||||
*
|
|
||||||
* @throws IllegalStateException if {@link #setTransactionSuccessful} has already been
|
|
||||||
* called for the current transaction.
|
|
||||||
* @throws SQLiteException if an error occurs.
|
|
||||||
* @throws OperationCanceledException if the operation was canceled.
|
|
||||||
*
|
|
||||||
* @see #setTransactionSuccessful
|
|
||||||
* @see #yieldTransaction
|
|
||||||
* @see #endTransaction
|
|
||||||
*/
|
|
||||||
public void beginTransaction(int transactionMode,
|
|
||||||
SQLiteTransactionListener transactionListener, int connectionFlags,
|
|
||||||
CancellationSignal cancellationSignal) {
|
|
||||||
throwIfTransactionMarkedSuccessful();
|
|
||||||
beginTransactionUnchecked(transactionMode, transactionListener, connectionFlags,
|
|
||||||
cancellationSignal);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void beginTransactionUnchecked(int transactionMode,
|
|
||||||
SQLiteTransactionListener transactionListener, int connectionFlags,
|
|
||||||
CancellationSignal cancellationSignal) {
|
|
||||||
if (cancellationSignal != null) {
|
|
||||||
cancellationSignal.throwIfCanceled();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mTransactionStack == null) {
|
|
||||||
acquireConnection(null, connectionFlags, cancellationSignal); // might throw
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
// Set up the transaction such that we can back out safely
|
|
||||||
// in case we fail part way.
|
|
||||||
if (mTransactionStack == null) {
|
|
||||||
// Execute SQL might throw a runtime exception.
|
|
||||||
switch (transactionMode) {
|
|
||||||
case TRANSACTION_MODE_IMMEDIATE:
|
|
||||||
mConnection.execute("BEGIN IMMEDIATE;", null,
|
|
||||||
cancellationSignal); // might throw
|
|
||||||
break;
|
|
||||||
case TRANSACTION_MODE_EXCLUSIVE:
|
|
||||||
mConnection.execute("BEGIN EXCLUSIVE;", null,
|
|
||||||
cancellationSignal); // might throw
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
mConnection.execute("BEGIN;", null, cancellationSignal); // might throw
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Listener might throw a runtime exception.
|
|
||||||
if (transactionListener != null) {
|
|
||||||
try {
|
|
||||||
transactionListener.onBegin(); // might throw
|
|
||||||
} catch (RuntimeException ex) {
|
|
||||||
if (mTransactionStack == null) {
|
|
||||||
mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw
|
|
||||||
}
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Bookkeeping can't throw, except an OOM, which is just too bad...
|
|
||||||
Transaction transaction = obtainTransaction(transactionMode, transactionListener);
|
|
||||||
transaction.mParent = mTransactionStack;
|
|
||||||
mTransactionStack = transaction;
|
|
||||||
} finally {
|
|
||||||
if (mTransactionStack == null) {
|
|
||||||
releaseConnection(); // might throw
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Marks the current transaction as having completed successfully.
|
|
||||||
* <p>
|
|
||||||
* This method can be called at most once between {@link #beginTransaction} and
|
|
||||||
* {@link #endTransaction} to indicate that the changes made by the transaction should be
|
|
||||||
* committed. If this method is not called, the changes will be rolled back
|
|
||||||
* when the transaction is ended.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @throws IllegalStateException if there is no current transaction, or if
|
|
||||||
* {@link #setTransactionSuccessful} has already been called for the current transaction.
|
|
||||||
*
|
|
||||||
* @see #beginTransaction
|
|
||||||
* @see #endTransaction
|
|
||||||
*/
|
|
||||||
public void setTransactionSuccessful() {
|
|
||||||
throwIfNoTransaction();
|
|
||||||
throwIfTransactionMarkedSuccessful();
|
|
||||||
|
|
||||||
mTransactionStack.mMarkedSuccessful = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ends the current transaction and commits or rolls back changes.
|
|
||||||
* <p>
|
|
||||||
* If this is the outermost transaction (not nested within any other
|
|
||||||
* transaction), then the changes are committed if {@link #setTransactionSuccessful}
|
|
||||||
* was called or rolled back otherwise.
|
|
||||||
* </p><p>
|
|
||||||
* This method must be called exactly once for each call to {@link #beginTransaction}.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param cancellationSignal A signal to cancel the operation in progress, or null if none.
|
|
||||||
*
|
|
||||||
* @throws IllegalStateException if there is no current transaction.
|
|
||||||
* @throws SQLiteException if an error occurs.
|
|
||||||
* @throws OperationCanceledException if the operation was canceled.
|
|
||||||
*
|
|
||||||
* @see #beginTransaction
|
|
||||||
* @see #setTransactionSuccessful
|
|
||||||
* @see #yieldTransaction
|
|
||||||
*/
|
|
||||||
public void endTransaction(CancellationSignal cancellationSignal) {
|
|
||||||
throwIfNoTransaction();
|
|
||||||
assert mConnection != null;
|
|
||||||
|
|
||||||
endTransactionUnchecked(cancellationSignal, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void endTransactionUnchecked(CancellationSignal cancellationSignal, boolean yielding) {
|
|
||||||
if (cancellationSignal != null) {
|
|
||||||
cancellationSignal.throwIfCanceled();
|
|
||||||
}
|
|
||||||
|
|
||||||
final Transaction top = mTransactionStack;
|
|
||||||
boolean successful = (top.mMarkedSuccessful || yielding) && !top.mChildFailed;
|
|
||||||
|
|
||||||
RuntimeException listenerException = null;
|
|
||||||
final SQLiteTransactionListener listener = top.mListener;
|
|
||||||
if (listener != null) {
|
|
||||||
try {
|
|
||||||
if (successful) {
|
|
||||||
listener.onCommit(); // might throw
|
|
||||||
} else {
|
|
||||||
listener.onRollback(); // might throw
|
|
||||||
}
|
|
||||||
} catch (RuntimeException ex) {
|
|
||||||
listenerException = ex;
|
|
||||||
successful = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
mTransactionStack = top.mParent;
|
|
||||||
recycleTransaction(top);
|
|
||||||
|
|
||||||
if (mTransactionStack != null) {
|
|
||||||
if (!successful) {
|
|
||||||
mTransactionStack.mChildFailed = true;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
try {
|
|
||||||
if (successful) {
|
|
||||||
mConnection.execute("COMMIT;", null, cancellationSignal); // might throw
|
|
||||||
} else {
|
|
||||||
mConnection.execute("ROLLBACK;", null, cancellationSignal); // might throw
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
releaseConnection(); // might throw
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listenerException != null) {
|
|
||||||
throw listenerException;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Temporarily ends a transaction to let other threads have use of
|
|
||||||
* the database. Begins a new transaction after a specified delay.
|
|
||||||
* <p>
|
|
||||||
* If there are other threads waiting to acquire connections,
|
|
||||||
* then the current transaction is committed and the database
|
|
||||||
* connection is released. After a short delay, a new transaction
|
|
||||||
* is started.
|
|
||||||
* </p><p>
|
|
||||||
* The transaction is assumed to be successful so far. Do not call
|
|
||||||
* {@link #setTransactionSuccessful()} before calling this method.
|
|
||||||
* This method will fail if the transaction has already been marked
|
|
||||||
* successful.
|
|
||||||
* </p><p>
|
|
||||||
* The changes that were committed by a yield cannot be rolled back later.
|
|
||||||
* </p><p>
|
|
||||||
* Before this method was called, there must already have been
|
|
||||||
* a transaction in progress. When this method returns, there will
|
|
||||||
* still be a transaction in progress, either the same one as before
|
|
||||||
* or a new one if the transaction was actually yielded.
|
|
||||||
* </p><p>
|
|
||||||
* This method should not be called when there is a nested transaction
|
|
||||||
* in progress because it is not possible to yield a nested transaction.
|
|
||||||
* If <code>throwIfNested</code> is true, then attempting to yield
|
|
||||||
* a nested transaction will throw {@link IllegalStateException}, otherwise
|
|
||||||
* the method will return <code>false</code> in that case.
|
|
||||||
* </p><p>
|
|
||||||
* If there is no nested transaction in progress but a previous nested
|
|
||||||
* transaction failed, then the transaction is not yielded (because it
|
|
||||||
* must be rolled back) and this method returns <code>false</code>.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param sleepAfterYieldDelayMillis A delay time to wait after yielding
|
|
||||||
* the database connection to allow other threads some time to run.
|
|
||||||
* If the value is less than or equal to zero, there will be no additional
|
|
||||||
* delay beyond the time it will take to begin a new transaction.
|
|
||||||
* @param throwIfUnsafe If true, then instead of returning false when no
|
|
||||||
* transaction is in progress, a nested transaction is in progress, or when
|
|
||||||
* the transaction has already been marked successful, throws {@link IllegalStateException}.
|
|
||||||
* @param cancellationSignal A signal to cancel the operation in progress, or null if none.
|
|
||||||
* @return True if the transaction was actually yielded.
|
|
||||||
*
|
|
||||||
* @throws IllegalStateException if <code>throwIfNested</code> is true and
|
|
||||||
* there is no current transaction, there is a nested transaction in progress or
|
|
||||||
* if {@link #setTransactionSuccessful} has already been called for the current transaction.
|
|
||||||
* @throws SQLiteException if an error occurs.
|
|
||||||
* @throws OperationCanceledException if the operation was canceled.
|
|
||||||
*
|
|
||||||
* @see #beginTransaction
|
|
||||||
* @see #endTransaction
|
|
||||||
*/
|
|
||||||
public boolean yieldTransaction(long sleepAfterYieldDelayMillis, boolean throwIfUnsafe,
|
|
||||||
CancellationSignal cancellationSignal) {
|
|
||||||
if (throwIfUnsafe) {
|
|
||||||
throwIfNoTransaction();
|
|
||||||
throwIfTransactionMarkedSuccessful();
|
|
||||||
throwIfNestedTransaction();
|
|
||||||
} else {
|
|
||||||
if (mTransactionStack == null || mTransactionStack.mMarkedSuccessful
|
|
||||||
|| mTransactionStack.mParent != null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
assert mConnection != null;
|
|
||||||
|
|
||||||
if (mTransactionStack.mChildFailed) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return yieldTransactionUnchecked(sleepAfterYieldDelayMillis,
|
|
||||||
cancellationSignal); // might throw
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean yieldTransactionUnchecked(long sleepAfterYieldDelayMillis,
|
|
||||||
CancellationSignal cancellationSignal) {
|
|
||||||
if (cancellationSignal != null) {
|
|
||||||
cancellationSignal.throwIfCanceled();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mConnectionPool.shouldYieldConnection(mConnection, mConnectionFlags)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
final int transactionMode = mTransactionStack.mMode;
|
|
||||||
final SQLiteTransactionListener listener = mTransactionStack.mListener;
|
|
||||||
final int connectionFlags = mConnectionFlags;
|
|
||||||
endTransactionUnchecked(cancellationSignal, true); // might throw
|
|
||||||
|
|
||||||
if (sleepAfterYieldDelayMillis > 0) {
|
|
||||||
try {
|
|
||||||
Thread.sleep(sleepAfterYieldDelayMillis);
|
|
||||||
} catch (InterruptedException ex) {
|
|
||||||
// we have been interrupted, that's all we need to do
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
beginTransactionUnchecked(transactionMode, listener, connectionFlags,
|
|
||||||
cancellationSignal); // might throw
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prepares a statement for execution but does not bind its parameters or execute it.
|
|
||||||
* <p>
|
|
||||||
* This method can be used to check for syntax errors during compilation
|
|
||||||
* prior to execution of the statement. If the {@code outStatementInfo} argument
|
|
||||||
* is not null, the provided {@link SQLiteStatementInfo} object is populated
|
|
||||||
* with information about the statement.
|
|
||||||
* </p><p>
|
|
||||||
* A prepared statement makes no reference to the arguments that may eventually
|
|
||||||
* be bound to it, consequently it it possible to cache certain prepared statements
|
|
||||||
* such as SELECT or INSERT/UPDATE statements. If the statement is cacheable,
|
|
||||||
* then it will be stored in the cache for later and reused if possible.
|
|
||||||
* </p>
|
|
||||||
*
|
|
||||||
* @param sql The SQL statement to prepare.
|
|
||||||
* @param connectionFlags The connection flags to use if a connection must be
|
|
||||||
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
|
|
||||||
* @param cancellationSignal A signal to cancel the operation in progress, or null if none.
|
|
||||||
* @param outStatementInfo The {@link SQLiteStatementInfo} object to populate
|
|
||||||
* with information about the statement, or null if none.
|
|
||||||
*
|
|
||||||
* @throws SQLiteException if an error occurs, such as a syntax error.
|
|
||||||
* @throws OperationCanceledException if the operation was canceled.
|
|
||||||
*/
|
|
||||||
public void prepare(String sql, int connectionFlags, CancellationSignal cancellationSignal,
|
|
||||||
SQLiteStatementInfo outStatementInfo) {
|
|
||||||
if (sql == null) {
|
|
||||||
throw new IllegalArgumentException("sql must not be null.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cancellationSignal != null) {
|
|
||||||
cancellationSignal.throwIfCanceled();
|
|
||||||
}
|
|
||||||
|
|
||||||
acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
|
|
||||||
try {
|
|
||||||
mConnection.prepare(sql, outStatementInfo); // might throw
|
|
||||||
} finally {
|
|
||||||
releaseConnection(); // might throw
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes a statement that does not return a result.
|
|
||||||
*
|
|
||||||
* @param sql The SQL statement to execute.
|
|
||||||
* @param bindArgs The arguments to bind, or null if none.
|
|
||||||
* @param connectionFlags The connection flags to use if a connection must be
|
|
||||||
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
|
|
||||||
* @param cancellationSignal A signal to cancel the operation in progress, or null if none.
|
|
||||||
*
|
|
||||||
* @throws SQLiteException if an error occurs, such as a syntax error
|
|
||||||
* or invalid number of bind arguments.
|
|
||||||
* @throws OperationCanceledException if the operation was canceled.
|
|
||||||
*/
|
|
||||||
public void execute(String sql, Object[] bindArgs, int connectionFlags,
|
|
||||||
CancellationSignal cancellationSignal) {
|
|
||||||
if (sql == null) {
|
|
||||||
throw new IllegalArgumentException("sql must not be null.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
|
|
||||||
try {
|
|
||||||
mConnection.execute(sql, bindArgs, cancellationSignal); // might throw
|
|
||||||
} finally {
|
|
||||||
releaseConnection(); // might throw
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes a statement that returns a single <code>long</code> result.
|
|
||||||
*
|
|
||||||
* @param sql The SQL statement to execute.
|
|
||||||
* @param bindArgs The arguments to bind, or null if none.
|
|
||||||
* @param connectionFlags The connection flags to use if a connection must be
|
|
||||||
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
|
|
||||||
* @param cancellationSignal A signal to cancel the operation in progress, or null if none.
|
|
||||||
* @return The value of the first column in the first row of the result set
|
|
||||||
* as a <code>long</code>, or zero if none.
|
|
||||||
*
|
|
||||||
* @throws SQLiteException if an error occurs, such as a syntax error
|
|
||||||
* or invalid number of bind arguments.
|
|
||||||
* @throws OperationCanceledException if the operation was canceled.
|
|
||||||
*/
|
|
||||||
public long executeForLong(String sql, Object[] bindArgs, int connectionFlags,
|
|
||||||
CancellationSignal cancellationSignal) {
|
|
||||||
if (sql == null) {
|
|
||||||
throw new IllegalArgumentException("sql must not be null.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
|
|
||||||
try {
|
|
||||||
return mConnection.executeForLong(sql, bindArgs, cancellationSignal); // might throw
|
|
||||||
} finally {
|
|
||||||
releaseConnection(); // might throw
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes a statement that returns a single {@link String} result.
|
|
||||||
*
|
|
||||||
* @param sql The SQL statement to execute.
|
|
||||||
* @param bindArgs The arguments to bind, or null if none.
|
|
||||||
* @param connectionFlags The connection flags to use if a connection must be
|
|
||||||
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
|
|
||||||
* @param cancellationSignal A signal to cancel the operation in progress, or null if none.
|
|
||||||
* @return The value of the first column in the first row of the result set
|
|
||||||
* as a <code>String</code>, or null if none.
|
|
||||||
*
|
|
||||||
* @throws SQLiteException if an error occurs, such as a syntax error
|
|
||||||
* or invalid number of bind arguments.
|
|
||||||
* @throws OperationCanceledException if the operation was canceled.
|
|
||||||
*/
|
|
||||||
public String executeForString(String sql, Object[] bindArgs, int connectionFlags,
|
|
||||||
CancellationSignal cancellationSignal) {
|
|
||||||
if (sql == null) {
|
|
||||||
throw new IllegalArgumentException("sql must not be null.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
|
|
||||||
try {
|
|
||||||
return mConnection.executeForString(sql, bindArgs, cancellationSignal); // might throw
|
|
||||||
} finally {
|
|
||||||
releaseConnection(); // might throw
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes a statement that returns a single BLOB result as a
|
|
||||||
* file descriptor to a shared memory region.
|
|
||||||
*
|
|
||||||
* @param sql The SQL statement to execute.
|
|
||||||
* @param bindArgs The arguments to bind, or null if none.
|
|
||||||
* @param connectionFlags The connection flags to use if a connection must be
|
|
||||||
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
|
|
||||||
* @param cancellationSignal A signal to cancel the operation in progress, or null if none.
|
|
||||||
* @return The file descriptor for a shared memory region that contains
|
|
||||||
* the value of the first column in the first row of the result set as a BLOB,
|
|
||||||
* or null if none.
|
|
||||||
*
|
|
||||||
* @throws SQLiteException if an error occurs, such as a syntax error
|
|
||||||
* or invalid number of bind arguments.
|
|
||||||
* @throws OperationCanceledException if the operation was canceled.
|
|
||||||
*/
|
|
||||||
public ParcelFileDescriptor executeForBlobFileDescriptor(String sql, Object[] bindArgs,
|
|
||||||
int connectionFlags, CancellationSignal cancellationSignal) {
|
|
||||||
if (sql == null) {
|
|
||||||
throw new IllegalArgumentException("sql must not be null.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
|
|
||||||
try {
|
|
||||||
return mConnection.executeForBlobFileDescriptor(sql, bindArgs,
|
|
||||||
cancellationSignal); // might throw
|
|
||||||
} finally {
|
|
||||||
releaseConnection(); // might throw
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes a statement that returns a count of the number of rows
|
|
||||||
* that were changed. Use for UPDATE or DELETE SQL statements.
|
|
||||||
*
|
|
||||||
* @param sql The SQL statement to execute.
|
|
||||||
* @param bindArgs The arguments to bind, or null if none.
|
|
||||||
* @param connectionFlags The connection flags to use if a connection must be
|
|
||||||
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
|
|
||||||
* @param cancellationSignal A signal to cancel the operation in progress, or null if none.
|
|
||||||
* @return The number of rows that were changed.
|
|
||||||
*
|
|
||||||
* @throws SQLiteException if an error occurs, such as a syntax error
|
|
||||||
* or invalid number of bind arguments.
|
|
||||||
* @throws OperationCanceledException if the operation was canceled.
|
|
||||||
*/
|
|
||||||
public int executeForChangedRowCount(String sql, Object[] bindArgs, int connectionFlags,
|
|
||||||
CancellationSignal cancellationSignal) {
|
|
||||||
if (sql == null) {
|
|
||||||
throw new IllegalArgumentException("sql must not be null.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
|
|
||||||
try {
|
|
||||||
return mConnection.executeForChangedRowCount(sql, bindArgs,
|
|
||||||
cancellationSignal); // might throw
|
|
||||||
} finally {
|
|
||||||
releaseConnection(); // might throw
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes a statement that returns the row id of the last row inserted
|
|
||||||
* by the statement. Use for INSERT SQL statements.
|
|
||||||
*
|
|
||||||
* @param sql The SQL statement to execute.
|
|
||||||
* @param bindArgs The arguments to bind, or null if none.
|
|
||||||
* @param connectionFlags The connection flags to use if a connection must be
|
|
||||||
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
|
|
||||||
* @param cancellationSignal A signal to cancel the operation in progress, or null if none.
|
|
||||||
* @return The row id of the last row that was inserted, or 0 if none.
|
|
||||||
*
|
|
||||||
* @throws SQLiteException if an error occurs, such as a syntax error
|
|
||||||
* or invalid number of bind arguments.
|
|
||||||
* @throws OperationCanceledException if the operation was canceled.
|
|
||||||
*/
|
|
||||||
public long executeForLastInsertedRowId(String sql, Object[] bindArgs, int connectionFlags,
|
|
||||||
CancellationSignal cancellationSignal) {
|
|
||||||
if (sql == null) {
|
|
||||||
throw new IllegalArgumentException("sql must not be null.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
|
|
||||||
try {
|
|
||||||
return mConnection.executeForLastInsertedRowId(sql, bindArgs,
|
|
||||||
cancellationSignal); // might throw
|
|
||||||
} finally {
|
|
||||||
releaseConnection(); // might throw
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes a statement and populates the specified {@link CursorWindow}
|
|
||||||
* with a range of results. Returns the number of rows that were counted
|
|
||||||
* during query execution.
|
|
||||||
*
|
|
||||||
* @param sql The SQL statement to execute.
|
|
||||||
* @param bindArgs The arguments to bind, or null if none.
|
|
||||||
* @param window The cursor window to clear and fill.
|
|
||||||
* @param startPos The start position for filling the window.
|
|
||||||
* @param requiredPos The position of a row that MUST be in the window.
|
|
||||||
* If it won't fit, then the query should discard part of what it filled
|
|
||||||
* so that it does. Must be greater than or equal to <code>startPos</code>.
|
|
||||||
* @param countAllRows True to count all rows that the query would return
|
|
||||||
* regagless of whether they fit in the window.
|
|
||||||
* @param connectionFlags The connection flags to use if a connection must be
|
|
||||||
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
|
|
||||||
* @param cancellationSignal A signal to cancel the operation in progress, or null if none.
|
|
||||||
* @return The number of rows that were counted during query execution. Might
|
|
||||||
* not be all rows in the result set unless <code>countAllRows</code> is true.
|
|
||||||
*
|
|
||||||
* @throws SQLiteException if an error occurs, such as a syntax error
|
|
||||||
* or invalid number of bind arguments.
|
|
||||||
* @throws OperationCanceledException if the operation was canceled.
|
|
||||||
*/
|
|
||||||
public int executeForCursorWindow(String sql, Object[] bindArgs,
|
|
||||||
CursorWindow window, int startPos, int requiredPos, boolean countAllRows,
|
|
||||||
int connectionFlags, CancellationSignal cancellationSignal) {
|
|
||||||
if (sql == null) {
|
|
||||||
throw new IllegalArgumentException("sql must not be null.");
|
|
||||||
}
|
|
||||||
if (window == null) {
|
|
||||||
throw new IllegalArgumentException("window must not be null.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (executeSpecial(sql, bindArgs, connectionFlags, cancellationSignal)) {
|
|
||||||
window.clear();
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
acquireConnection(sql, connectionFlags, cancellationSignal); // might throw
|
|
||||||
try {
|
|
||||||
return mConnection.executeForCursorWindow(sql, bindArgs,
|
|
||||||
window, startPos, requiredPos, countAllRows,
|
|
||||||
cancellationSignal); // might throw
|
|
||||||
} finally {
|
|
||||||
releaseConnection(); // might throw
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs special reinterpretation of certain SQL statements such as "BEGIN",
|
|
||||||
* "COMMIT" and "ROLLBACK" to ensure that transaction state invariants are
|
|
||||||
* maintained.
|
|
||||||
*
|
|
||||||
* This function is mainly used to support legacy apps that perform their
|
|
||||||
* own transactions by executing raw SQL rather than calling {@link #beginTransaction}
|
|
||||||
* and the like.
|
|
||||||
*
|
|
||||||
* @param sql The SQL statement to execute.
|
|
||||||
* @param bindArgs The arguments to bind, or null if none.
|
|
||||||
* @param connectionFlags The connection flags to use if a connection must be
|
|
||||||
* acquired by this operation. Refer to {@link SQLiteConnectionPool}.
|
|
||||||
* @param cancellationSignal A signal to cancel the operation in progress, or null if none.
|
|
||||||
* @return True if the statement was of a special form that was handled here,
|
|
||||||
* false otherwise.
|
|
||||||
*
|
|
||||||
* @throws SQLiteException if an error occurs, such as a syntax error
|
|
||||||
* or invalid number of bind arguments.
|
|
||||||
* @throws OperationCanceledException if the operation was canceled.
|
|
||||||
*/
|
|
||||||
private boolean executeSpecial(String sql, Object[] bindArgs, int connectionFlags,
|
|
||||||
CancellationSignal cancellationSignal) {
|
|
||||||
if (cancellationSignal != null) {
|
|
||||||
cancellationSignal.throwIfCanceled();
|
|
||||||
}
|
|
||||||
|
|
||||||
final int type = DatabaseUtils.getSqlStatementType(sql);
|
|
||||||
switch (type) {
|
|
||||||
case DatabaseUtils.STATEMENT_BEGIN:
|
|
||||||
beginTransaction(TRANSACTION_MODE_EXCLUSIVE, null, connectionFlags,
|
|
||||||
cancellationSignal);
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case DatabaseUtils.STATEMENT_COMMIT:
|
|
||||||
setTransactionSuccessful();
|
|
||||||
endTransaction(cancellationSignal);
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case DatabaseUtils.STATEMENT_ABORT:
|
|
||||||
endTransaction(cancellationSignal);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void acquireConnection(String sql, int connectionFlags,
|
|
||||||
CancellationSignal cancellationSignal) {
|
|
||||||
if (mConnection == null) {
|
|
||||||
assert mConnectionUseCount == 0;
|
|
||||||
mConnection = mConnectionPool.acquireConnection(sql, connectionFlags,
|
|
||||||
cancellationSignal); // might throw
|
|
||||||
mConnectionFlags = connectionFlags;
|
|
||||||
}
|
|
||||||
mConnectionUseCount += 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void releaseConnection() {
|
|
||||||
assert mConnection != null;
|
|
||||||
assert mConnectionUseCount > 0;
|
|
||||||
if (--mConnectionUseCount == 0) {
|
|
||||||
try {
|
|
||||||
mConnectionPool.releaseConnection(mConnection); // might throw
|
|
||||||
} finally {
|
|
||||||
mConnection = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void throwIfNoTransaction() {
|
|
||||||
if (mTransactionStack == null) {
|
|
||||||
throw new IllegalStateException("Cannot perform this operation because "
|
|
||||||
+ "there is no current transaction.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void throwIfTransactionMarkedSuccessful() {
|
|
||||||
if (mTransactionStack != null && mTransactionStack.mMarkedSuccessful) {
|
|
||||||
throw new IllegalStateException("Cannot perform this operation because "
|
|
||||||
+ "the transaction has already been marked successful. The only "
|
|
||||||
+ "thing you can do now is call endTransaction().");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void throwIfNestedTransaction() {
|
|
||||||
if (hasNestedTransaction()) {
|
|
||||||
throw new IllegalStateException("Cannot perform this operation because "
|
|
||||||
+ "a nested transaction is in progress.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Transaction obtainTransaction(int mode, SQLiteTransactionListener listener) {
|
|
||||||
Transaction transaction = mTransactionPool;
|
|
||||||
if (transaction != null) {
|
|
||||||
mTransactionPool = transaction.mParent;
|
|
||||||
transaction.mParent = null;
|
|
||||||
transaction.mMarkedSuccessful = false;
|
|
||||||
transaction.mChildFailed = false;
|
|
||||||
} else {
|
|
||||||
transaction = new Transaction();
|
|
||||||
}
|
|
||||||
transaction.mMode = mode;
|
|
||||||
transaction.mListener = listener;
|
|
||||||
return transaction;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void recycleTransaction(Transaction transaction) {
|
|
||||||
transaction.mParent = mTransactionPool;
|
|
||||||
transaction.mListener = null;
|
|
||||||
mTransactionPool = transaction;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final class Transaction {
|
|
||||||
public Transaction mParent;
|
|
||||||
public int mMode;
|
|
||||||
public SQLiteTransactionListener mListener;
|
|
||||||
public boolean mMarkedSuccessful;
|
|
||||||
public boolean mChildFailed;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,167 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2006 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
import android.os.ParcelFileDescriptor;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a statement that can be executed against a database. The statement
|
|
||||||
* cannot return multiple rows or columns, but single value (1 x 1) result sets
|
|
||||||
* are supported.
|
|
||||||
* <p>
|
|
||||||
* This class is not thread-safe.
|
|
||||||
* </p>
|
|
||||||
*/
|
|
||||||
public final class SQLiteStatement extends SQLiteProgram {
|
|
||||||
SQLiteStatement(SQLiteDatabase db, String sql, Object[] bindArgs) {
|
|
||||||
super(db, sql, bindArgs, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute this SQL statement, if it is not a SELECT / INSERT / DELETE / UPDATE, for example
|
|
||||||
* CREATE / DROP table, view, trigger, index etc.
|
|
||||||
*
|
|
||||||
* @throws org.sqlite.database.SQLException If the SQL string is invalid for
|
|
||||||
* some reason
|
|
||||||
*/
|
|
||||||
public void execute() {
|
|
||||||
acquireReference();
|
|
||||||
try {
|
|
||||||
getSession().execute(getSql(), getBindArgs(), getConnectionFlags(), null);
|
|
||||||
} catch (SQLiteDatabaseCorruptException ex) {
|
|
||||||
onCorruption();
|
|
||||||
throw ex;
|
|
||||||
} finally {
|
|
||||||
releaseReference();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute this SQL statement, if the the number of rows affected by execution of this SQL
|
|
||||||
* statement is of any importance to the caller - for example, UPDATE / DELETE SQL statements.
|
|
||||||
*
|
|
||||||
* @return the number of rows affected by this SQL statement execution.
|
|
||||||
* @throws org.sqlite.database.SQLException If the SQL string is invalid for
|
|
||||||
* some reason
|
|
||||||
*/
|
|
||||||
public int executeUpdateDelete() {
|
|
||||||
acquireReference();
|
|
||||||
try {
|
|
||||||
return getSession().executeForChangedRowCount(
|
|
||||||
getSql(), getBindArgs(), getConnectionFlags(), null);
|
|
||||||
} catch (SQLiteDatabaseCorruptException ex) {
|
|
||||||
onCorruption();
|
|
||||||
throw ex;
|
|
||||||
} finally {
|
|
||||||
releaseReference();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute this SQL statement and return the ID of the row inserted due to this call.
|
|
||||||
* The SQL statement should be an INSERT for this to be a useful call.
|
|
||||||
*
|
|
||||||
* @return the row ID of the last row inserted, if this insert is successful. -1 otherwise.
|
|
||||||
*
|
|
||||||
* @throws org.sqlite.database.SQLException If the SQL string is invalid for
|
|
||||||
* some reason
|
|
||||||
*/
|
|
||||||
public long executeInsert() {
|
|
||||||
acquireReference();
|
|
||||||
try {
|
|
||||||
return getSession().executeForLastInsertedRowId(
|
|
||||||
getSql(), getBindArgs(), getConnectionFlags(), null);
|
|
||||||
} catch (SQLiteDatabaseCorruptException ex) {
|
|
||||||
onCorruption();
|
|
||||||
throw ex;
|
|
||||||
} finally {
|
|
||||||
releaseReference();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute a statement that returns a 1 by 1 table with a numeric value.
|
|
||||||
* For example, SELECT COUNT(*) FROM table;
|
|
||||||
*
|
|
||||||
* @return The result of the query.
|
|
||||||
*
|
|
||||||
* @throws org.sqlite.database.sqlite.SQLiteDoneException if the query returns zero rows
|
|
||||||
*/
|
|
||||||
public long simpleQueryForLong() {
|
|
||||||
acquireReference();
|
|
||||||
try {
|
|
||||||
return getSession().executeForLong(
|
|
||||||
getSql(), getBindArgs(), getConnectionFlags(), null);
|
|
||||||
} catch (SQLiteDatabaseCorruptException ex) {
|
|
||||||
onCorruption();
|
|
||||||
throw ex;
|
|
||||||
} finally {
|
|
||||||
releaseReference();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Execute a statement that returns a 1 by 1 table with a text value.
|
|
||||||
* For example, SELECT COUNT(*) FROM table;
|
|
||||||
*
|
|
||||||
* @return The result of the query.
|
|
||||||
*
|
|
||||||
* @throws org.sqlite.database.sqlite.SQLiteDoneException if the query returns zero rows
|
|
||||||
*/
|
|
||||||
public String simpleQueryForString() {
|
|
||||||
acquireReference();
|
|
||||||
try {
|
|
||||||
return getSession().executeForString(
|
|
||||||
getSql(), getBindArgs(), getConnectionFlags(), null);
|
|
||||||
} catch (SQLiteDatabaseCorruptException ex) {
|
|
||||||
onCorruption();
|
|
||||||
throw ex;
|
|
||||||
} finally {
|
|
||||||
releaseReference();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes a statement that returns a 1 by 1 table with a blob value.
|
|
||||||
*
|
|
||||||
* @return A read-only file descriptor for a copy of the blob value, or {@code null}
|
|
||||||
* if the value is null or could not be read for some reason.
|
|
||||||
*
|
|
||||||
* @throws org.sqlite.database.sqlite.SQLiteDoneException if the query returns zero rows
|
|
||||||
*/
|
|
||||||
public ParcelFileDescriptor simpleQueryForBlobFileDescriptor() {
|
|
||||||
acquireReference();
|
|
||||||
try {
|
|
||||||
return getSession().executeForBlobFileDescriptor(
|
|
||||||
getSql(), getBindArgs(), getConnectionFlags(), null);
|
|
||||||
} catch (SQLiteDatabaseCorruptException ex) {
|
|
||||||
onCorruption();
|
|
||||||
throw ex;
|
|
||||||
} finally {
|
|
||||||
releaseReference();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "SQLiteProgram: " + getSql();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2011 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Describes a SQLite statement.
|
|
||||||
*
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
public final class SQLiteStatementInfo {
|
|
||||||
/**
|
|
||||||
* The number of parameters that the statement has.
|
|
||||||
*/
|
|
||||||
public int numParameters;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The names of all columns in the result set of the statement.
|
|
||||||
*/
|
|
||||||
public String[] columnNames;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* True if the statement is read-only.
|
|
||||||
*/
|
|
||||||
public boolean readOnly;
|
|
||||||
}
|
|
@ -1,29 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2010 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
public class SQLiteTableLockedException extends SQLiteException {
|
|
||||||
public SQLiteTableLockedException() {}
|
|
||||||
|
|
||||||
public SQLiteTableLockedException(String error) {
|
|
||||||
super(error);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2009 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A listener for transaction events.
|
|
||||||
*/
|
|
||||||
public interface SQLiteTransactionListener {
|
|
||||||
/**
|
|
||||||
* Called immediately after the transaction begins.
|
|
||||||
*/
|
|
||||||
void onBegin();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called immediately before commiting the transaction.
|
|
||||||
*/
|
|
||||||
void onCommit();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called if the transaction is about to be rolled back.
|
|
||||||
*/
|
|
||||||
void onRollback();
|
|
||||||
}
|
|
@ -1,111 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2008 Esmertec AG.
|
|
||||||
* Copyright (C) 2008 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
/*
|
|
||||||
** Modified to support SQLite extensions by the SQLite developers:
|
|
||||||
** sqlite-dev@sqlite.org.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.sqlite.database.sqlite;
|
|
||||||
|
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.ContentValues;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.database.Cursor;
|
|
||||||
import org.sqlite.database.sqlite.SQLiteException;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.util.Log;
|
|
||||||
import android.widget.Toast;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @hide
|
|
||||||
*/
|
|
||||||
|
|
||||||
public final class SqliteWrapper {
|
|
||||||
private static final String TAG = "SqliteWrapper";
|
|
||||||
private static final String SQLITE_EXCEPTION_DETAIL_MESSAGE
|
|
||||||
= "unable to open database file";
|
|
||||||
|
|
||||||
private SqliteWrapper() {
|
|
||||||
// Forbidden being instantiated.
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: need to optimize this method.
|
|
||||||
private static boolean isLowMemory(SQLiteException e) {
|
|
||||||
return e.getMessage().equals(SQLITE_EXCEPTION_DETAIL_MESSAGE);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void checkSQLiteException(Context context, SQLiteException e) {
|
|
||||||
if (isLowMemory(e)) {
|
|
||||||
Toast.makeText(context, "low memory", Toast.LENGTH_SHORT).show();
|
|
||||||
} else {
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Cursor query(Context context, ContentResolver resolver, Uri uri,
|
|
||||||
String[] projection, String selection, String[] selectionArgs, String sortOrder) {
|
|
||||||
try {
|
|
||||||
return resolver.query(uri, projection, selection, selectionArgs, sortOrder);
|
|
||||||
} catch (SQLiteException e) {
|
|
||||||
Log.e(TAG, "Catch a SQLiteException when query: ", e);
|
|
||||||
checkSQLiteException(context, e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static boolean requery(Context context, Cursor cursor) {
|
|
||||||
try {
|
|
||||||
return cursor.requery();
|
|
||||||
} catch (SQLiteException e) {
|
|
||||||
Log.e(TAG, "Catch a SQLiteException when requery: ", e);
|
|
||||||
checkSQLiteException(context, e);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public static int update(Context context, ContentResolver resolver, Uri uri,
|
|
||||||
ContentValues values, String where, String[] selectionArgs) {
|
|
||||||
try {
|
|
||||||
return resolver.update(uri, values, where, selectionArgs);
|
|
||||||
} catch (SQLiteException e) {
|
|
||||||
Log.e(TAG, "Catch a SQLiteException when update: ", e);
|
|
||||||
checkSQLiteException(context, e);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int delete(Context context, ContentResolver resolver, Uri uri,
|
|
||||||
String where, String[] selectionArgs) {
|
|
||||||
try {
|
|
||||||
return resolver.delete(uri, where, selectionArgs);
|
|
||||||
} catch (SQLiteException e) {
|
|
||||||
Log.e(TAG, "Catch a SQLiteException when delete: ", e);
|
|
||||||
checkSQLiteException(context, e);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Uri insert(Context context, ContentResolver resolver,
|
|
||||||
Uri uri, ContentValues values) {
|
|
||||||
try {
|
|
||||||
return resolver.insert(uri, values);
|
|
||||||
} catch (SQLiteException e) {
|
|
||||||
Log.e(TAG, "Catch a SQLiteException when insert: ", e);
|
|
||||||
checkSQLiteException(context, e);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,215 +0,0 @@
|
|||||||
<h1>
|
|
||||||
SQLite Android Bindings
|
|
||||||
</h1>
|
|
||||||
|
|
||||||
<p> The SQLite library is a core part of the Android environment. Java
|
|
||||||
applications and content providers access SQLite using the interface in
|
|
||||||
the
|
|
||||||
<a href="http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html">android.database.sqlite</a> namespace.
|
|
||||||
|
|
||||||
<p> One disadvantage of using Android's built-in SQLite support is that the
|
|
||||||
application is forced to use the version of SQLite that the current version of
|
|
||||||
Android happened to ship with. If your application happens to require a newer
|
|
||||||
version of SQLite, or a build with a custom extension or
|
|
||||||
<a href=http://www.sqlite.org/vfs.html>VFS</a> installed, you're out of luck.
|
|
||||||
|
|
||||||
<p>The code in this project allows an application to use the
|
|
||||||
<a href=http://developer.android.com/tools/sdk/ndk/index.html>Android NDK</a>
|
|
||||||
to build a custom version of SQLite to be shipped with the application while
|
|
||||||
still continuing to use the standard Java interface.
|
|
||||||
|
|
||||||
<h2>Normal Usage</h2>
|
|
||||||
|
|
||||||
<h3>Installation</h3>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Android API levels 15 (Android 4.0.3) and greater are supported. If
|
|
||||||
targetting API level 16 or greater, use the default "trunk" branch of this
|
|
||||||
project. Or, for API level 15, use the "api-level-15" branch. It is not possible
|
|
||||||
to target an API level lower than 15.
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Copy the following files from this project into the equivalent locations in
|
|
||||||
the application project.
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
jni/Android.mk
|
|
||||||
jni/Application.mk
|
|
||||||
jni/sqlite/* (copy contents of directory recursively)
|
|
||||||
src/org/sqlite/database/* (copy contents of directory recursively)
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Following this, the directory structures should contain
|
|
||||||
[/tree?ci=trunk&re=%5ejni%7csrc/org/sqlite/data&expand | these files].
|
|
||||||
|
|
||||||
<p>
|
|
||||||
For API level 15 only, also copy the following:
|
|
||||||
|
|
||||||
<pre>
|
|
||||||
src/org/sqlite/os/* (copy contents of directory recursively)
|
|
||||||
</pre>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Directory "jni/sqlite/" contains copies of the sqlite3.h and sqlite3.c
|
|
||||||
source files. Between them, they contain the
|
|
||||||
<a href=http://www.sqlite.org/amalgamation.html>source code for the SQLite
|
|
||||||
library</a>. If necessary, replace these with the source for the specific
|
|
||||||
version of SQLite required. If SQLite is to be compiled with any special
|
|
||||||
pre-processor macros defined, add them to the "jni/sqlite/Android.mk" file
|
|
||||||
(not jni/Android.mk).
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Once the files have been added to the project, run the command "ndk-build"
|
|
||||||
in the root directory of the project. This compiles the native code in
|
|
||||||
the jni/ directory (including the custom SQLite version) to shared libraries
|
|
||||||
that will be deployed to the device along with the application. Assuming
|
|
||||||
it is successful, unless you modify the sources or makefiles within the
|
|
||||||
jni/ directory structure, you should not need to run "ndk-build" again.
|
|
||||||
|
|
||||||
<h3>Application Programming</h3>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Before using any SQLite related methods or objects, the shared library
|
|
||||||
compiled using the ndk must be loaded into the application using the
|
|
||||||
following code:
|
|
||||||
|
|
||||||
<verbatim>
|
|
||||||
System.loadLibrary("sqliteX");
|
|
||||||
</verbatim>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
One way to ensure that the shared library is loaded early enough is
|
|
||||||
to add it to a "static" block within the declaration of the application's
|
|
||||||
main Activity class.
|
|
||||||
|
|
||||||
<p>
|
|
||||||
The classes that make up the built-in Android SQLite interface reside in
|
|
||||||
the "android.database.sqlite" namespace. This interface provides all of
|
|
||||||
the same classes, except within the "org.sqlite.database.sqlite" namespace.
|
|
||||||
This means that to modify an application to use the custom version of
|
|
||||||
SQLite, all that is usually required is to replace all occurrences
|
|
||||||
"android.database.sqlite" within the source code with
|
|
||||||
"org.sqlite.database.sqlite". For example, the following:
|
|
||||||
|
|
||||||
<verbatim>
|
|
||||||
import android.database.sqlite.SQLiteDatabase;
|
|
||||||
</verbatim>
|
|
||||||
|
|
||||||
<p>should be replaced with:
|
|
||||||
|
|
||||||
<verbatim>
|
|
||||||
import org.sqlite.database.sqlite.SQLiteDatabase;
|
|
||||||
</verbatim>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
As well as replacing all uses of the classes in the
|
|
||||||
android.database.sqlite.* namespace, the application must also be sure
|
|
||||||
to use the following two:
|
|
||||||
|
|
||||||
<verbatim>
|
|
||||||
org.sqlite.database.SQLException
|
|
||||||
org.sqlite.database.DatabaseErrorHandler
|
|
||||||
</verbatim>
|
|
||||||
|
|
||||||
<p>instead of:
|
|
||||||
|
|
||||||
<verbatim>
|
|
||||||
android.database.SQLException
|
|
||||||
android.database.DatabaseErrorHandler
|
|
||||||
</verbatim>
|
|
||||||
|
|
||||||
<p>Aside from namespace changes, there are other differences from the
|
|
||||||
stock Android interface that applications need to be aware of:
|
|
||||||
|
|
||||||
<ol>
|
|
||||||
<li> The SQLiteStatement.<a href="http://developer.android.com/reference/android/database/sqlite/SQLiteStatement.html#simpleQueryForBlobFileDescriptor()">simpleQueryForBlobFileDescriptor()</a>
|
|
||||||
API is not available.
|
|
||||||
|
|
||||||
<li> The collation sequence "UNICODE" is not available.
|
|
||||||
|
|
||||||
<li> The collation sequence "LOCALIZED", which normally changes with the
|
|
||||||
system's current locale, is always equivalent to SQLite's built
|
|
||||||
in collation BINARY.
|
|
||||||
</ol>
|
|
||||||
|
|
||||||
|
|
||||||
<h2>Using The SQLite Encryption Extension</h2>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
To use the <a href=http://www.sqlite.org/see/doc/trunk/www/readme.wiki>
|
|
||||||
SQLite Encryption Extension</a> (SEE) on Android, replace the sqlite3.c
|
|
||||||
file at "jni/sqlite/sqlite3.c" with a SEE-enabled version (i.e. the
|
|
||||||
concatenation of sqlite3.c and see.c - refer to the link above for
|
|
||||||
details). Next, open the file jni/sqlite/Android.mk and locate the
|
|
||||||
following two lines:
|
|
||||||
|
|
||||||
<verbatim>
|
|
||||||
# If using SEE, uncomment the following:
|
|
||||||
# LOCAL_CFLAGS += -DSQLITE_HAS_CODEC
|
|
||||||
</verbatim>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Uncomment the second of them, then run "ndk-build" as described above to
|
|
||||||
generate the shared libraries.
|
|
||||||
|
|
||||||
<p>
|
|
||||||
After opening or creating an encrypted database, the application must
|
|
||||||
immediately execute a PRAGMA to configure the encryption key. This must
|
|
||||||
be done before any other database methods are called. For example:
|
|
||||||
|
|
||||||
<verbatim>
|
|
||||||
import org.sqlite.database.sqlite.SQLiteDatabase;
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase("my.db", null);
|
|
||||||
db.execSQL("PRAGMA key = 'secretkey'");
|
|
||||||
</verbatim>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Or, if you are using the
|
|
||||||
<a href=http://developer.android.com/reference/android/database/sqlite/SQLiteOpenHelper.html>SQLiteOpenHelper</a>
|
|
||||||
helper class, the PRAGMA must be the first thing executed within the
|
|
||||||
onConfigure() callback. For example:
|
|
||||||
|
|
||||||
<verbatim>
|
|
||||||
import org.sqlite.database.sqlite.SQLiteDatabase;
|
|
||||||
import org.sqlite.database.sqlite.SQLiteHelper;
|
|
||||||
|
|
||||||
...
|
|
||||||
|
|
||||||
class MyHelper extends SQLiteOpenHelper {
|
|
||||||
...
|
|
||||||
void onConfigure(SQLiteDatabase db){
|
|
||||||
db.execSQL("PRAGMA key = 'secretkey'");
|
|
||||||
}
|
|
||||||
...
|
|
||||||
}
|
|
||||||
</verbatim>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Refer to the <a href=http://www.sqlite.org/see/doc/trunk/www/readme.wiki>
|
|
||||||
SEE documentation</a> for further details regarding encryption keys.
|
|
||||||
|
|
||||||
<p>Aside from supporting encrypted databases, SEE-enabled builds behave
|
|
||||||
differently in two more respects:
|
|
||||||
|
|
||||||
<ol>
|
|
||||||
<li> <p>The SQLiteDatabase.<a href="http://developer.android.com/reference/android/database/sqlite/SQLiteDatabase.html#enableWriteAheadLogging()">enableWriteAheadLogging()</a> method does not enable
|
|
||||||
connection pooling. It is not possible for connection pooling to be
|
|
||||||
used with a SEE-enabled build (even if the database is unencrypted).
|
|
||||||
|
|
||||||
<li> <p>In Android, if database corruption is encountered, or if an attempt is
|
|
||||||
made to open a file that is not an SQLite database, the default
|
|
||||||
behaviour is to delete the file and create an empty database file in
|
|
||||||
its place. In a SEE-enabled build, the default behaviour is to throw
|
|
||||||
an exception.<br><br>
|
|
||||||
The reason for this is that supplying an incorrect encryption key
|
|
||||||
is indistinguishable from opening a file that is not a database file.
|
|
||||||
And it seems too dangerous to simply delete the file in this case.
|
|
||||||
<br><br>
|
|
||||||
The default behaviour can be overriden using the
|
|
||||||
<a href="http://developer.android.com/reference/android/database/DatabaseErrorHandler.html">DatabaseErrorHandler</a> interface.
|
|
||||||
|
|
||||||
</ol>
|
|
@ -1,10 +0,0 @@
|
|||||||
Metadata-Version: 1.0
|
|
||||||
Name: yap
|
|
||||||
Version: 0.1
|
|
||||||
Summary: UNKNOWN
|
|
||||||
Home-page: UNKNOWN
|
|
||||||
Author: UNKNOWN
|
|
||||||
Author-email: UNKNOWN
|
|
||||||
License: UNKNOWN
|
|
||||||
Description: UNKNOWN
|
|
||||||
Platform: UNKNOWN
|
|
@ -1,14 +0,0 @@
|
|||||||
setup.py
|
|
||||||
yapPYTHON_wrap.cxx
|
|
||||||
/Users/vsc/github/yap-6.3/packages/python/pl2pl.c
|
|
||||||
/Users/vsc/github/yap-6.3/packages/python/pl2py.c
|
|
||||||
/Users/vsc/github/yap-6.3/packages/python/py2pl.c
|
|
||||||
/Users/vsc/github/yap-6.3/packages/python/pybips.c
|
|
||||||
/Users/vsc/github/yap-6.3/packages/python/pypreds.c
|
|
||||||
/Users/vsc/github/yap-6.3/packages/python/python.c
|
|
||||||
/Users/vsc/github/yap-6.3/packages/swig/yap.i
|
|
||||||
/Users/vsc/github/yap-6.3/packages/swig/python/yapex.py
|
|
||||||
/Users/vsc/github/yap-6.3/packages/swig/python/yap.egg-info/PKG-INFO
|
|
||||||
/Users/vsc/github/yap-6.3/packages/swig/python/yap.egg-info/SOURCES.txt
|
|
||||||
/Users/vsc/github/yap-6.3/packages/swig/python/yap.egg-info/dependency_links.txt
|
|
||||||
/Users/vsc/github/yap-6.3/packages/swig/python/yap.egg-info/top_level.txt
|
|
@ -1 +0,0 @@
|
|||||||
|
|
@ -1,2 +0,0 @@
|
|||||||
yap
|
|
||||||
yapex
|
|
@ -1,10 +0,0 @@
|
|||||||
Metadata-Version: 1.0
|
|
||||||
Name: yapex
|
|
||||||
Version: 0.1
|
|
||||||
Summary: UNKNOWN
|
|
||||||
Home-page: UNKNOWN
|
|
||||||
Author: UNKNOWN
|
|
||||||
Author-email: UNKNOWN
|
|
||||||
License: UNKNOWN
|
|
||||||
Description: UNKNOWN
|
|
||||||
Platform: UNKNOWN
|
|
@ -1,12 +0,0 @@
|
|||||||
setup.py
|
|
||||||
/Users/vsc/github/yap-6.3/packages/python/pl2pl.c
|
|
||||||
/Users/vsc/github/yap-6.3/packages/python/pl2py.c
|
|
||||||
/Users/vsc/github/yap-6.3/packages/python/py2pl.c
|
|
||||||
/Users/vsc/github/yap-6.3/packages/python/pybips.c
|
|
||||||
/Users/vsc/github/yap-6.3/packages/python/pypreds.c
|
|
||||||
/Users/vsc/github/yap-6.3/packages/python/python.c
|
|
||||||
/Users/vsc/github/yap-6.3/packages/swig/python/yapex.py
|
|
||||||
/Users/vsc/github/yap-6.3/packages/swig/python/yapex.egg-info/PKG-INFO
|
|
||||||
/Users/vsc/github/yap-6.3/packages/swig/python/yapex.egg-info/SOURCES.txt
|
|
||||||
/Users/vsc/github/yap-6.3/packages/swig/python/yapex.egg-info/dependency_links.txt
|
|
||||||
/Users/vsc/github/yap-6.3/packages/swig/python/yapex.egg-info/top_level.txt
|
|
@ -1 +0,0 @@
|
|||||||
yapex
|
|
Reference in New Issue
Block a user