indenting, + don't use men streams in Android

This commit is contained in:
vscosta
2016-04-05 02:18:47 +01:00
parent 636992781c
commit 396bd2f8c3
44 changed files with 241 additions and 252 deletions

View File

@@ -0,0 +1,37 @@
/*
* 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);
}

View File

@@ -0,0 +1,117 @@
/*
* 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());
}
}
}

View File

@@ -0,0 +1,176 @@
/*
* 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();
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,235 @@
/*
* 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);
}
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,34 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,33 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,29 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,29 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,112 @@
/*
* 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();
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,318 @@
/*
* 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();
}
}
}

View File

@@ -0,0 +1,60 @@
/*
* 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);
}

View File

@@ -0,0 +1,57 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,169 @@
/*
* 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");
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,29 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,176 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,87 @@
/*
* 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;
}
}

View File

@@ -0,0 +1,33 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,39 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,32 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,117 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,41 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,383 @@
/*
* 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) {}
}

View File

@@ -0,0 +1,29 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,222 @@
/*
* 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;
}
}

View File

@@ -0,0 +1,88 @@
/*
* 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();
}
}

View File

@@ -0,0 +1,663 @@
/*
* 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 (&lt;append chunk 1>&lt;append chunk2>) AND (&lt;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 (&lt;append chunk 1>&lt;append chunk2>) AND (&lt;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;
}
}

View File

@@ -0,0 +1,29 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,967 @@
/*
* 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;
}
}

View File

@@ -0,0 +1,167 @@
/*
* 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();
}
}

View File

@@ -0,0 +1,43 @@
/*
* 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;
}

View File

@@ -0,0 +1,29 @@
/*
* 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);
}
}

View File

@@ -0,0 +1,41 @@
/*
* 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();
}

View File

@@ -0,0 +1,111 @@
/*
* 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;
}
}
}