Remove DISTINCT and GROUP BY, double visitor

pull/618/head
Alex Baker 6 years ago
parent d6a3484004
commit 299a63d4c6

@ -7,7 +7,6 @@ package com.todoroo.astrid.dao;
import android.support.test.runner.AndroidJUnit4; import android.support.test.runner.AndroidJUnit4;
import com.todoroo.andlib.data.Property;
import com.todoroo.andlib.sql.Query; import com.todoroo.andlib.sql.Query;
import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.DateUtilities;
import com.todoroo.astrid.dao.TaskDao.TaskCriteria; import com.todoroo.astrid.dao.TaskDao.TaskCriteria;
@ -21,19 +20,12 @@ import org.tasks.injection.TestComponent;
import javax.inject.Inject; import javax.inject.Inject;
import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotSame; import static junit.framework.Assert.assertNotSame;
import static junit.framework.Assert.assertNull; import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public class TaskDaoTests extends InjectingTestCase { public class TaskDaoTests extends InjectingTestCase {
public static Property<?>[] IDS = new Property<?>[] { Task.ID };
public static Property<?>[] TITLES = new Property<?>[] { Task.ID,
Task.TITLE };
@Inject TaskDao taskDao; @Inject TaskDao taskDao;
/** /**
@ -41,13 +33,13 @@ public class TaskDaoTests extends InjectingTestCase {
*/ */
@Test @Test
public void testTaskCreation() { public void testTaskCreation() {
assertEquals(0, taskDao.toList(Query.select(IDS)).size()); assertEquals(0, taskDao.toList(Query.select()).size());
// create task "happy" // create task "happy"
Task task = new Task(); Task task = new Task();
task.setTitle("happy"); task.setTitle("happy");
taskDao.save(task); taskDao.save(task);
assertEquals(1, taskDao.toList(Query.select(IDS)).size()); assertEquals(1, taskDao.toList(Query.select()).size());
long happyId = task.getId(); long happyId = task.getId();
assertNotSame(Task.NO_ID, happyId); assertNotSame(Task.NO_ID, happyId);
task = taskDao.fetch(happyId); task = taskDao.fetch(happyId);
@ -57,14 +49,14 @@ public class TaskDaoTests extends InjectingTestCase {
task = new Task(); task = new Task();
task.setTitle("sad"); task.setTitle("sad");
taskDao.save(task); taskDao.save(task);
assertEquals(2, taskDao.toList(Query.select(IDS)).size()); assertEquals(2, taskDao.toList(Query.select()).size());
// rename sad to melancholy // rename sad to melancholy
long sadId = task.getId(); long sadId = task.getId();
assertNotSame(Task.NO_ID, sadId); assertNotSame(Task.NO_ID, sadId);
task.setTitle("melancholy"); task.setTitle("melancholy");
taskDao.save(task); taskDao.save(task);
assertEquals(2, taskDao.toList(Query.select(IDS)).size()); assertEquals(2, taskDao.toList(Query.select()).size());
// check state // check state
task = taskDao.fetch(happyId); task = taskDao.fetch(happyId);
@ -112,10 +104,10 @@ public class TaskDaoTests extends InjectingTestCase {
taskDao.save(task); taskDao.save(task);
// check is active // check is active
assertEquals(5, taskDao.toList(Query.select(TITLES).where(TaskCriteria.isActive())).size()); assertEquals(5, taskDao.toList(Query.select().where(TaskCriteria.isActive())).size());
// check is visible // check is visible
assertEquals(5, taskDao.toList(Query.select(TITLES).where(TaskCriteria.isVisible())).size()); assertEquals(5, taskDao.toList(Query.select().where(TaskCriteria.isVisible())).size());
} }
/** /**
@ -123,18 +115,18 @@ public class TaskDaoTests extends InjectingTestCase {
*/ */
@Test @Test
public void testTDeletion() { public void testTDeletion() {
assertEquals(0, taskDao.toList(Query.select(IDS)).size()); assertEquals(0, taskDao.toList(Query.select()).size());
// create task "happy" // create task "happy"
Task task = new Task(); Task task = new Task();
task.setTitle("happy"); task.setTitle("happy");
taskDao.save(task); taskDao.save(task);
assertEquals(1, taskDao.toList(Query.select(IDS)).size()); assertEquals(1, taskDao.toList(Query.select()).size());
// delete // delete
long happyId = task.getId(); long happyId = task.getId();
assertEquals(1, taskDao.deleteById(happyId)); assertEquals(1, taskDao.deleteById(happyId));
assertEquals(0, taskDao.toList(Query.select(IDS)).size()); assertEquals(0, taskDao.toList(Query.select()).size());
} }
/** /**
@ -149,7 +141,7 @@ public class TaskDaoTests extends InjectingTestCase {
taskDao.save(task); taskDao.save(task);
assertEquals(0, taskDao.toList(Query.select(IDS)).size()); assertEquals(0, taskDao.toList(Query.select()).size());
} }
/** /**
@ -157,14 +149,14 @@ public class TaskDaoTests extends InjectingTestCase {
*/ */
@Test @Test
public void testInvalidIndex() { public void testInvalidIndex() {
assertEquals(0, taskDao.toList(Query.select(IDS)).size()); assertEquals(0, taskDao.toList(Query.select()).size());
assertNull(taskDao.fetch(1)); assertNull(taskDao.fetch(1));
assertEquals(0, taskDao.deleteById(1)); assertEquals(0, taskDao.deleteById(1));
// make sure db still works // make sure db still works
assertEquals(0, taskDao.toList(Query.select(IDS)).size()); assertEquals(0, taskDao.toList(Query.select()).size());
} }
@Override @Override

@ -190,7 +190,7 @@ public class GoogleTaskSyncAdapter extends InjectingAbstractThreadedSyncAdapter
} }
private void pushLocalChanges() throws UserRecoverableAuthIOException { private void pushLocalChanges() throws UserRecoverableAuthIOException {
List<Task> tasks = taskDao.toList(Query.select(Task.PROPERTIES) List<Task> tasks = taskDao.toList(Query.select()
.join(Join.left(GoogleTask.TABLE.as("gt"), Task.ID.eq(Field.field("gt.task")))) .join(Join.left(GoogleTask.TABLE.as("gt"), Task.ID.eq(Field.field("gt.task"))))
.where(Criterion.or( .where(Criterion.or(
Task.MODIFICATION_DATE.gt(Field.field("gt.last_sync")), Task.MODIFICATION_DATE.gt(Field.field("gt.last_sync")),

@ -108,8 +108,6 @@ public abstract class Property<TYPE> extends Field implements Cloneable {
RETURN visitLong(Property<Long> property, PARAMETER data); RETURN visitLong(Property<Long> property, PARAMETER data);
RETURN visitDouble(Property<Double> property, PARAMETER data);
RETURN visitString(Property<String> property, PARAMETER data); RETURN visitString(Property<String> property, PARAMETER data);
} }
@ -231,18 +229,10 @@ public abstract class Property<TYPE> extends Field implements Cloneable {
// --- pseudo-properties // --- pseudo-properties
/** Runs a SQL function and returns the result as a string */ /** Runs a SQL function and returns the result as a string */
public static class IntegerFunctionProperty extends IntegerProperty { public static class CountProperty extends IntegerProperty {
public IntegerFunctionProperty(String function, String columnName) {
super(columnName, function);
alias = columnName;
}
}
/** Counting in aggregated tables. Returns the result of COUNT(1) */
public static final class CountProperty extends IntegerFunctionProperty {
public CountProperty() { public CountProperty() {
super("COUNT(1)", "count"); super("COUNT(1)", "count");
alias = "count";
} }
} }
} }

@ -114,12 +114,6 @@ public class TodorooCursor extends CursorWrapper {
return cursor.getLong(column); return cursor.getLong(column);
} }
@Override
public Object visitDouble(Property<Double> property, TodorooCursor cursor) {
int column = columnIndex(property, cursor);
return cursor.getDouble(column);
}
@Override @Override
public Object visitString(Property<String> property, TodorooCursor cursor) { public Object visitString(Property<String> property, TodorooCursor cursor) {
int column = columnIndex(property, cursor); int column = columnIndex(property, cursor);

@ -6,14 +6,13 @@
package com.todoroo.andlib.sql; package com.todoroo.andlib.sql;
import com.todoroo.andlib.data.Property; import com.todoroo.andlib.data.Property;
import com.todoroo.astrid.data.Task;
import java.util.ArrayList; import java.util.ArrayList;
import static com.todoroo.andlib.sql.SqlConstants.ALL; import static com.todoroo.andlib.sql.SqlConstants.ALL;
import static com.todoroo.andlib.sql.SqlConstants.COMMA; import static com.todoroo.andlib.sql.SqlConstants.COMMA;
import static com.todoroo.andlib.sql.SqlConstants.DISTINCT;
import static com.todoroo.andlib.sql.SqlConstants.FROM; import static com.todoroo.andlib.sql.SqlConstants.FROM;
import static com.todoroo.andlib.sql.SqlConstants.GROUP_BY;
import static com.todoroo.andlib.sql.SqlConstants.LIMIT; import static com.todoroo.andlib.sql.SqlConstants.LIMIT;
import static com.todoroo.andlib.sql.SqlConstants.ORDER_BY; import static com.todoroo.andlib.sql.SqlConstants.ORDER_BY;
import static com.todoroo.andlib.sql.SqlConstants.SELECT; import static com.todoroo.andlib.sql.SqlConstants.SELECT;
@ -28,23 +27,19 @@ public final class Query {
private final ArrayList<Criterion> criterions = new ArrayList<>(); private final ArrayList<Criterion> criterions = new ArrayList<>();
private final ArrayList<Field> fields = new ArrayList<>(); private final ArrayList<Field> fields = new ArrayList<>();
private final ArrayList<Join> joins = new ArrayList<>(); private final ArrayList<Join> joins = new ArrayList<>();
private final ArrayList<Field> groupBies = new ArrayList<>();
private final ArrayList<Order> orders = new ArrayList<>(); private final ArrayList<Order> orders = new ArrayList<>();
private int limits = -1; private int limits = -1;
private boolean distinct = false;
private Query(Field... fields) { private Query(Field... fields) {
this.fields.addAll(asList(fields)); this.fields.addAll(asList(fields));
} }
public static Query select(Field... fields) { public static Query select() {
return new Query(fields); return new Query(Task.PROPERTIES);
} }
public static Query selectDistinct(Field... fields) { public static Query select(Field... fields) {
Query query = new Query(fields); return new Query(fields);
query.distinct = true;
return query;
} }
public Query from(SqlTable fromTable) { public Query from(SqlTable fromTable) {
@ -62,11 +57,6 @@ public final class Query {
return this; return this;
} }
public Query groupBy(Field... groupBy) {
groupBies.addAll(asList(groupBy));
return this;
}
public Query orderBy(Order... order) { public Query orderBy(Order... order) {
orders.addAll(asList(order)); orders.addAll(asList(order));
return this; return this;
@ -96,11 +86,10 @@ public final class Query {
visitJoinClause(sql); visitJoinClause(sql);
if(queryTemplate == null) { if(queryTemplate == null) {
visitWhereClause(sql); visitWhereClause(sql);
visitGroupByClause(sql);
visitOrderByClause(sql); visitOrderByClause(sql);
visitLimitClause(sql); visitLimitClause(sql);
} else { } else {
if(groupBies.size() > 0 || orders.size() > 0) { if(orders.size() > 0) {
throw new IllegalStateException("Can't have extras AND query template"); //$NON-NLS-1$ throw new IllegalStateException("Can't have extras AND query template"); //$NON-NLS-1$
} }
sql.append(queryTemplate); sql.append(queryTemplate);
@ -120,17 +109,6 @@ public final class Query {
sql.deleteCharAt(sql.length() - 1).append(SPACE); sql.deleteCharAt(sql.length() - 1).append(SPACE);
} }
private void visitGroupByClause(StringBuilder sql) {
if (groupBies.isEmpty()) {
return;
}
sql.append(GROUP_BY);
for (Field groupBy : groupBies) {
sql.append(SPACE).append(groupBy).append(COMMA);
}
sql.deleteCharAt(sql.length() - 1).append(SPACE);
}
private void visitWhereClause(StringBuilder sql) { private void visitWhereClause(StringBuilder sql) {
if (criterions.isEmpty()) { if (criterions.isEmpty()) {
return; return;
@ -156,9 +134,6 @@ public final class Query {
private void visitSelectClause(StringBuilder sql) { private void visitSelectClause(StringBuilder sql) {
sql.append(SELECT).append(SPACE); sql.append(SELECT).append(SPACE);
if(distinct) {
sql.append(DISTINCT).append(SPACE);
}
if (fields.isEmpty()) { if (fields.isEmpty()) {
sql.append(ALL).append(SPACE); sql.append(ALL).append(SPACE);
return; return;

@ -8,7 +8,6 @@ package com.todoroo.andlib.sql;
import java.util.ArrayList; import java.util.ArrayList;
import static com.todoroo.andlib.sql.SqlConstants.COMMA; import static com.todoroo.andlib.sql.SqlConstants.COMMA;
import static com.todoroo.andlib.sql.SqlConstants.GROUP_BY;
import static com.todoroo.andlib.sql.SqlConstants.LIMIT; import static com.todoroo.andlib.sql.SqlConstants.LIMIT;
import static com.todoroo.andlib.sql.SqlConstants.ORDER_BY; import static com.todoroo.andlib.sql.SqlConstants.ORDER_BY;
import static com.todoroo.andlib.sql.SqlConstants.SPACE; import static com.todoroo.andlib.sql.SqlConstants.SPACE;
@ -26,7 +25,6 @@ public final class QueryTemplate {
private final ArrayList<Criterion> criterions = new ArrayList<>(); private final ArrayList<Criterion> criterions = new ArrayList<>();
private final ArrayList<Join> joins = new ArrayList<>(); private final ArrayList<Join> joins = new ArrayList<>();
private final ArrayList<Field> groupBies = new ArrayList<>();
private final ArrayList<Order> orders = new ArrayList<>(); private final ArrayList<Order> orders = new ArrayList<>();
private Integer limit = null; private Integer limit = null;
@ -50,7 +48,6 @@ public final class QueryTemplate {
StringBuilder sql = new StringBuilder(); StringBuilder sql = new StringBuilder();
visitJoinClause(sql); visitJoinClause(sql);
visitWhereClause(sql); visitWhereClause(sql);
visitGroupByClause(sql);
visitOrderByClause(sql); visitOrderByClause(sql);
if(limit != null) { if(limit != null) {
sql.append(LIMIT).append(SPACE).append(limit); sql.append(LIMIT).append(SPACE).append(limit);
@ -69,17 +66,6 @@ public final class QueryTemplate {
sql.deleteCharAt(sql.length() - 1).append(SPACE); sql.deleteCharAt(sql.length() - 1).append(SPACE);
} }
private void visitGroupByClause(StringBuilder sql) {
if (groupBies.isEmpty()) {
return;
}
sql.append(GROUP_BY);
for (Field groupBy : groupBies) {
sql.append(SPACE).append(groupBy).append(COMMA);
}
sql.deleteCharAt(sql.length() - 1).append(SPACE);
}
private void visitWhereClause(StringBuilder sql) { private void visitWhereClause(StringBuilder sql) {
if (criterions.isEmpty()) { if (criterions.isEmpty()) {
return; return;

@ -7,7 +7,6 @@ package com.todoroo.andlib.sql;
public final class SqlConstants { public final class SqlConstants {
public static final String SELECT = "SELECT"; public static final String SELECT = "SELECT";
public static final String DISTINCT = "DISTINCT";
public static final String SPACE = " "; public static final String SPACE = " ";
public static final String AS = "AS"; public static final String AS = "AS";
public static final String COMMA = ","; public static final String COMMA = ",";
@ -20,7 +19,6 @@ public final class SqlConstants {
public static final String AND = "AND"; public static final String AND = "AND";
public static final String OR = "OR"; public static final String OR = "OR";
public static final String ORDER_BY = "ORDER BY"; public static final String ORDER_BY = "ORDER BY";
public static final String GROUP_BY = "GROUP BY";
public static final String WHERE = "WHERE"; public static final String WHERE = "WHERE";
public static final String NOT = "NOT"; public static final String NOT = "NOT";
public static final String LIMIT = "LIMIT"; public static final String LIMIT = "LIMIT";

@ -124,7 +124,7 @@ public class TasksXmlExporter {
try { try {
String output = setupFile(backupDirectory, String output = setupFile(backupDirectory,
exportType); exportType);
int tasks = taskDao.count(Query.select(Task.ID)); int tasks = taskDao.count(Query.select());
if(tasks > 0) { if(tasks > 0) {
doTasksExport(output); doTasksExport(output);
@ -185,7 +185,7 @@ public class TasksXmlExporter {
} }
private void serializeTasks() throws IOException { private void serializeTasks() throws IOException {
List<Task> tasks = taskDao.toList(Query.select(Task.PROPERTIES).orderBy(Order.asc(Task.ID))); List<Task> tasks = taskDao.toList(Query.select().orderBy(Order.asc(Task.ID)));
int length = tasks.size(); int length = tasks.size();
for(int i = 0; i < length; i++) { for(int i = 0; i < length; i++) {
Task task = tasks.get(i); Task task = tasks.get(i);

@ -206,7 +206,7 @@ public class TasksXmlImporter {
} }
// if the task's name and creation date match an existing task, skip // if the task's name and creation date match an existing task, skip
Query query = Query.select(Task.ID, Task.COMPLETION_DATE, Task.DELETION_DATE) Query query = Query.select()
.where(Criterion.and(Task.TITLE.eq(title), Task.CREATION_DATE.eq(created))); .where(Criterion.and(Task.TITLE.eq(title), Task.CREATION_DATE.eq(created)));
if (taskDao.count(query) > 0) { if (taskDao.count(query) > 0) {
skipCount++; skipCount++;

@ -125,7 +125,7 @@ public class OldTaskPreferences extends InjectingPreferenceActivity {
private int deleteCalendarEvents(Criterion criterion) { private int deleteCalendarEvents(Criterion criterion) {
int deletedEventCount = 0; int deletedEventCount = 0;
List<Task> tasks = taskDao.toList(Query.select(Task.ID, Task.CALENDAR_URI).where(criterion)); List<Task> tasks = taskDao.toList(Query.select().where(criterion));
for (Task task : tasks) { for (Task task : tasks) {
if (calendarEventProvider.deleteEvent(task)) { if (calendarEventProvider.deleteEvent(task)) {
deletedEventCount++; deletedEventCount++;

@ -82,7 +82,7 @@ public abstract class TaskDao {
public List<Task> query(Filter filter) { public List<Task> query(Filter filter) {
String query = PermaSql.replacePlaceholders(filter.getSqlQuery()); String query = PermaSql.replacePlaceholders(filter.getSqlQuery());
return query(Query.select(Task.PROPERTIES).withQueryTemplate(query)).toList(); return query(Query.select().withQueryTemplate(query)).toList();
} }
public List<Task> toList(Query query) { public List<Task> toList(Query query) {
@ -207,9 +207,8 @@ public abstract class TaskDao {
} }
public TodorooCursor fetchFiltered(String queryTemplate, Property<?>... properties) { public TodorooCursor fetchFiltered(String queryTemplate, Property<?>... properties) {
return query(queryTemplate == null return query(Query.select(properties)
? Query.selectDistinct(properties) .withQueryTemplate(PermaSql.replacePlaceholders(queryTemplate)));
: Query.select(properties).withQueryTemplate(PermaSql.replacePlaceholders(queryTemplate)));
} }
/** /**

@ -167,7 +167,7 @@ public class Astrid2TaskProvider extends InjectingContentProvider {
private Cursor getTasks() { private Cursor getTasks() {
MatrixCursor ret = new MatrixCursor(TASK_FIELD_LIST); MatrixCursor ret = new MatrixCursor(TASK_FIELD_LIST);
List<Integer> importanceColors = checkBoxes.get().getPriorityColors(); List<Integer> importanceColors = checkBoxes.get().getPriorityColors();
Query query = Query.select(Task.ID, Task.TITLE, Task.IMPORTANCE, Task.DUE_DATE) Query query = Query.select()
.where(Criterion.and(TaskCriteria.isActive(), TaskCriteria.isVisible())) .where(Criterion.and(TaskCriteria.isActive(), TaskCriteria.isVisible()))
.orderBy(SortHelper.defaultTaskOrder()).limit(MAX_NUMBER_OF_TASKS); .orderBy(SortHelper.defaultTaskOrder()).limit(MAX_NUMBER_OF_TASKS);
for (Task task : taskDao.get().toList(query)) { for (Task task : taskDao.get().toList(query)) {

@ -53,7 +53,7 @@ public final class ReminderService {
} }
public void scheduleAllAlarms(TaskDao taskDao) { public void scheduleAllAlarms(TaskDao taskDao) {
Query query = Query.select(Task.PROPERTIES).where(Criterion.and( Query query = Query.select().where(Criterion.and(
TaskCriteria.isActive(), TaskCriteria.isActive(),
Criterion.or(Task.REMINDER_FLAGS.gt(0), Task.REMINDER_PERIOD.gt(0)))); Criterion.or(Task.REMINDER_FLAGS.gt(0), Task.REMINDER_PERIOD.gt(0))));
for (Task task : taskDao.toList(query)) { for (Task task : taskDao.toList(query)) {

@ -147,7 +147,7 @@ class AstridOrderedListFragmentHelper {
if(chained.size() > 0) { if(chained.size() > 0) {
// move recurring items to item parent // move recurring items to item parent
List<Task> tasks = taskDao.toList(Query.select(Task.UUID, Task.RECURRENCE).where( List<Task> tasks = taskDao.toList(Query.select().where(
Criterion.and(Task.UUID.in(chained.toArray(new String[chained.size()])), Criterion.and(Task.UUID.in(chained.toArray(new String[chained.size()])),
Task.RECURRENCE.isNotNull(), Functions.length(Task.RECURRENCE).gt(0)))); Task.RECURRENCE.isNotNull(), Functions.length(Task.RECURRENCE).gt(0))));

@ -231,7 +231,7 @@ public class NotificationManager {
ArrayList<Long> taskIds = newArrayList(transform(notifications, n -> n.taskId)); ArrayList<Long> taskIds = newArrayList(transform(notifications, n -> n.taskId));
QueryTemplate query = new QueryTemplate().where(Task.ID.in(taskIds)); QueryTemplate query = new QueryTemplate().where(Task.ID.in(taskIds));
Filter filter = new Filter(context.getString(R.string.notifications), query); Filter filter = new Filter(context.getString(R.string.notifications), query);
List<Task> tasks = taskDao.toList(Query.select(Task.PROPERTIES) List<Task> tasks = taskDao.toList(Query.select()
.withQueryTemplate(query.toString())); .withQueryTemplate(query.toString()));
long when = notificationDao.latestTimestamp(); long when = notificationDao.latestTimestamp();
int maxPriority = 3; int maxPriority = 3;

Loading…
Cancel
Save