blob: 38b4b74a921ad6e78be60a5f6ad7681b6fa6c574 [file] [log] [blame]
package org.wordpress.android.util;
import android.database.Cursor;
import android.database.DatabaseUtils;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDoneException;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteStatement;
import org.wordpress.android.util.AppLog.T;
import java.util.ArrayList;
import java.util.List;
public class SqlUtils {
private SqlUtils() {
throw new AssertionError();
}
/*
* SQLite doesn't have a boolean datatype, so booleans are stored as 0=false, 1=true
*/
public static long boolToSql(boolean value) {
return (value ? 1 : 0);
}
public static boolean sqlToBool(int value) {
return (value != 0);
}
public static void closeStatement(SQLiteStatement stmt) {
if (stmt != null) {
stmt.close();
}
}
public static void closeCursor(Cursor c) {
if (c != null && !c.isClosed()) {
c.close();
}
}
/*
* wrapper for DatabaseUtils.longForQuery() which returns 0 if query returns no rows
*/
public static long longForQuery(SQLiteDatabase db, String query, String[] selectionArgs) {
try {
return DatabaseUtils.longForQuery(db, query, selectionArgs);
} catch (SQLiteDoneException e) {
return 0;
}
}
public static int intForQuery(SQLiteDatabase db, String query, String[] selectionArgs) {
long value = longForQuery(db, query, selectionArgs);
return (int)value;
}
public static boolean boolForQuery(SQLiteDatabase db, String query, String[] selectionArgs) {
long value = longForQuery(db, query, selectionArgs);
return sqlToBool((int) value);
}
/*
* wrapper for DatabaseUtils.stringForQuery(), returns "" if query returns no rows
*/
public static String stringForQuery(SQLiteDatabase db, String query, String[] selectionArgs) {
try {
return DatabaseUtils.stringForQuery(db, query, selectionArgs);
} catch (SQLiteDoneException e) {
return "";
}
}
/*
* returns the number of rows in the passed table
*/
public static long getRowCount(SQLiteDatabase db, String tableName) {
return DatabaseUtils.queryNumEntries(db, tableName);
}
/*
* removes all rows from the passed table
*/
public static void deleteAllRowsInTable(SQLiteDatabase db, String tableName) {
db.delete(tableName, null, null);
}
/*
* drop all tables from the passed SQLiteDatabase - make sure to pass a
* writable database
*/
public static boolean dropAllTables(SQLiteDatabase db) throws SQLiteException {
if (db == null) {
return false;
}
if (db.isReadOnly()) {
throw new SQLiteException("can't drop tables from a read-only database");
}
List<String> tableNames = new ArrayList<String>();
Cursor cursor = db.rawQuery("SELECT name FROM sqlite_master WHERE type='table'", null);
if (cursor.moveToFirst()) {
do {
String tableName = cursor.getString(0);
if (!tableName.equals("android_metadata") && !tableName.equals("sqlite_sequence")) {
tableNames.add(tableName);
}
} while (cursor.moveToNext());
}
db.beginTransaction();
try {
for (String tableName: tableNames) {
db.execSQL("DROP TABLE IF EXISTS " + tableName);
}
db.setTransactionSuccessful();
return true;
} finally {
db.endTransaction();
closeCursor(cursor);
}
}
/*
* Android's CursorWindow has a max size of 2MB per row which can be exceeded
* with a very large text column, causing an IllegalStateException when the
* row is read - prevent this by limiting the amount of text that's stored in
* the text column.
* https://github.com/android/platform_frameworks_base/blob/b77bc869241644a662f7e615b0b00ecb5aee373d/core/res/res/values/config.xml#L1268
* https://github.com/android/platform_frameworks_base/blob/3bdbf644d61f46b531838558fabbd5b990fc4913/core/java/android/database/CursorWindow.java#L103
*/
// Max 512K characters (a UTF-8 char is 4 bytes max, so a 512K characters string is always < 2Mb)
private static final int MAX_TEXT_LEN = 1024 * 1024 / 2;
public static String maxSQLiteText(final String text) {
if (text.length() <= MAX_TEXT_LEN) {
return text;
}
AppLog.w(T.UTILS, "sqlite > max text exceeded, storing truncated text");
return text.substring(0, MAX_TEXT_LEN);
}
}