package org.thoughtcrime.securesms.util; import android.content.ContentValues; import android.database.Cursor; import androidx.annotation.NonNull; import net.sqlcipher.database.SQLiteDatabase; import org.thoughtcrime.securesms.recipients.RecipientId; import org.whispersystems.libsignal.util.guava.Preconditions; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; public final class SqlUtil { private SqlUtil() {} public static boolean tableExists(@NonNull SQLiteDatabase db, @NonNull String table) { try (Cursor cursor = db.rawQuery("SELECT name FROM sqlite_master WHERE type=? AND name=?", new String[] { "table", table })) { return cursor != null && cursor.moveToNext(); } } public static boolean columnExists(@NonNull SQLiteDatabase db, @NonNull String table, @NonNull String column) { try (Cursor cursor = db.rawQuery("PRAGMA table_info(" + table + ")", null)) { int nameColumnIndex = cursor.getColumnIndexOrThrow("name"); while (cursor.moveToNext()) { String name = cursor.getString(nameColumnIndex); if (name.equals(column)) { return true; } } } return false; } public static String[] buildArgs(Object... objects) { String[] args = new String[objects.length]; for (int i = 0; i < objects.length; i++) { if (objects[i] == null) { throw new NullPointerException("Cannot have null arg!"); } else if (objects[i] instanceof RecipientId) { args[i] = ((RecipientId) objects[i]).serialize(); } else { args[i] = objects[i].toString(); } } return args; } /** * Returns an updated query and args pairing that will only update rows that would *actually* * change. In other words, if {@link SQLiteDatabase#update(String, ContentValues, String, String[])} * returns > 0, then you know something *actually* changed. */ public static @NonNull Query buildTrueUpdateQuery(@NonNull String selection, @NonNull String[] args, @NonNull ContentValues contentValues) { StringBuilder qualifier = new StringBuilder(); Set> valueSet = contentValues.valueSet(); List fullArgs = new ArrayList<>(args.length + valueSet.size()); fullArgs.addAll(Arrays.asList(args)); int i = 0; for (Map.Entry entry : valueSet) { if (entry.getValue() != null) { qualifier.append(entry.getKey()).append(" != ? OR ").append(entry.getKey()).append(" IS NULL"); fullArgs.add(String.valueOf(entry.getValue())); } else { qualifier.append(entry.getKey()).append(" NOT NULL"); } if (i != valueSet.size() - 1) { qualifier.append(" OR "); } i++; } return new Query("(" + selection + ") AND (" + qualifier + ")", fullArgs.toArray(new String[0])); } public static @NonNull Query buildCollectionQuery(@NonNull String column, @NonNull Collection values) { Preconditions.checkArgument(values.size() > 0); StringBuilder query = new StringBuilder(); Object[] args = new Object[values.size()]; int i = 0; for (Object value : values) { query.append("?"); args[i] = value; if (i != values.size() - 1) { query.append(", "); } i++; } return new Query(column + " IN (" + query.toString() + ")", buildArgs(args)); } public static String[] appendArg(@NonNull String[] args, String addition) { String[] output = new String[args.length + 1]; System.arraycopy(args, 0, output, 0, args.length); output[output.length - 1] = addition; return output; } public static class Query { private final String where; private final String[] whereArgs; private Query(@NonNull String where, @NonNull String[] whereArgs) { this.where = where; this.whereArgs = whereArgs; } public String getWhere() { return where; } public String[] getWhereArgs() { return whereArgs; } } }