Improve order by/selection logic.

The order by stuff previously only allowed specifying a particular field.
We should also be able to sort based on arbitrary expressions, and such
expressions will require the ability to bind arguments using the "?" syntax.
This changes provides a Java abstraction for the order by, and improves
the handling of selection arguments that need to bind to "?" so that both
the selection (i.e. WHERE clause) and the ORDER BY clause can provide
arguments as required.
This commit is contained in:
Peter Serwylo 2016-06-02 21:51:18 +10:00
parent d47e87e53b
commit f023cd7757
5 changed files with 83 additions and 19 deletions

View File

@ -488,10 +488,10 @@ public class ApkProvider extends FDroidProvider {
for (final String field : projection) { for (final String field : projection) {
queryBuilder.addField(field); queryBuilder.addField(field);
} }
queryBuilder.addSelection(query.getSelection()); queryBuilder.addSelection(query);
queryBuilder.addOrderBy(sortOrder); queryBuilder.addOrderBy(sortOrder);
Cursor cursor = db().rawQuery(queryBuilder.toString(), query.getArgs()); Cursor cursor = db().rawQuery(queryBuilder.toString(), queryBuilder.getArgs());
cursor.setNotificationUri(getContext().getContentResolver(), uri); cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor; return cursor;
} }

View File

@ -317,7 +317,7 @@ public class AppProvider extends FDroidProvider {
} }
public void addSelection(AppQuerySelection selection) { public void addSelection(AppQuerySelection selection) {
addSelection(selection.getSelection()); super.addSelection(selection);
if (selection.naturalJoinToInstalled()) { if (selection.naturalJoinToInstalled()) {
naturalJoinToInstalledTable(); naturalJoinToInstalledTable();
} }
@ -828,12 +828,11 @@ public class AppProvider extends FDroidProvider {
} }
Query query = new Query(); Query query = new Query();
query.addSelection(selection); query.addSelection(selection);
query.addFields(projection); // TODO: Make the order of addFields/addSelection not dependent on each other... query.addFields(projection); // TODO: Make the order of addFields/addSelection not dependent on each other...
query.addOrderBy(sortOrder); query.addOrderBy(sortOrder);
Cursor cursor = db().rawQuery(query.toString(), selection.getArgs()); Cursor cursor = db().rawQuery(query.toString(), query.getArgs());
cursor.setNotificationUri(getContext().getContentResolver(), uri); cursor.setNotificationUri(getContext().getContentResolver(), uri);
return cursor; return cursor;
} }

View File

@ -0,0 +1,25 @@
package org.fdroid.fdroid.data;
public class OrderClause {
public final String expression;
private String[] args;
public OrderClause(String expression) {
this.expression = expression;
}
public OrderClause(String field, String[] args, boolean isAscending) {
this.expression = field + " " + (isAscending ? "ASC" : "DESC");
this.args = args;
}
@Override
public String toString() {
return expression;
}
public String[] getArgs() {
return args;
}
}

View File

@ -1,5 +1,8 @@
package org.fdroid.fdroid.data; package org.fdroid.fdroid.data;
import android.support.annotation.Nullable;
import android.text.TextUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -8,7 +11,8 @@ abstract class QueryBuilder {
private final List<String> fields = new ArrayList<>(); private final List<String> fields = new ArrayList<>();
private final StringBuilder tables = new StringBuilder(getRequiredTables()); private final StringBuilder tables = new StringBuilder(getRequiredTables());
private String selection; private String selection;
private String orderBy; private String[] selectionArgs;
private final List<OrderClause> orderBys = new ArrayList<>();
protected abstract String getRequiredTables(); protected abstract String getRequiredTables();
@ -58,12 +62,51 @@ abstract class QueryBuilder {
fields.add(fieldBuilder.toString()); fields.add(fieldBuilder.toString());
} }
public void addSelection(String selection) { public void addSelection(@Nullable QuerySelection selection) {
this.selection = selection; if (selection == null) {
this.selection = null;
this.selectionArgs = null;
} else {
this.selection = selection.getSelection();
this.selectionArgs = selection.getArgs();
}
} }
/**
* Add an order by, which includes an expression and optionally ASC or DESC afterward.
*/
public void addOrderBy(String orderBy) { public void addOrderBy(String orderBy) {
this.orderBy = orderBy; if (orderBy != null) {
orderBys.add(new OrderClause(orderBy));
}
}
public void addOrderBy(@Nullable OrderClause orderClause) {
if (orderClause != null) {
orderBys.add(orderClause);
}
}
public String[] getArgs() {
List<String> args = new ArrayList<>();
if (selectionArgs != null) {
for (String arg : selectionArgs) {
args.add(arg);
}
}
for (OrderClause orderBy : orderBys) {
if (orderBy.getArgs() != null) {
for (String arg : orderBy.getArgs()) {
args.add(arg);
}
}
}
String[] strings = new String[args.size()];
args.toArray(strings);
return strings;
} }
protected final void leftJoin(String table, String alias, String condition) { protected final void leftJoin(String table, String alias, String condition) {
@ -94,14 +137,7 @@ abstract class QueryBuilder {
} }
private String fieldsSql() { private String fieldsSql() {
StringBuilder sb = new StringBuilder(); return TextUtils.join(", ", fields);
for (int i = 0; i < fields.size(); i++) {
if (i > 0) {
sb.append(',');
}
sb.append(fields.get(i));
}
return sb.toString();
} }
private String whereSql() { private String whereSql() {
@ -109,7 +145,11 @@ abstract class QueryBuilder {
} }
private String orderBySql() { private String orderBySql() {
return orderBy != null ? " ORDER BY " + orderBy : ""; if (orderBys.size() == 0) {
return "";
} else {
return " ORDER BY " + TextUtils.join(", ", orderBys);
}
} }
private String groupBySql() { private String groupBySql() {

View File

@ -19,7 +19,7 @@ public class QuerySelection {
public QuerySelection(String selection) { public QuerySelection(String selection) {
this.selection = selection; this.selection = selection;
this.args = null; this.args = new String[] {};
} }
public QuerySelection(String selection, String[] args) { public QuerySelection(String selection, String[] args) {