Skip to content

Commit

Permalink
Add insert and delete queries
Browse files Browse the repository at this point in the history
Query builder tests
Row builder insertion order preservation, test
  • Loading branch information
rchodava committed Nov 5, 2015
1 parent 40f0777 commit 12d5606
Show file tree
Hide file tree
Showing 6 changed files with 249 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package org.chodavarapu.datamill.db;

import rx.Observable;

import java.util.Map;
import java.util.function.Function;

/**
* @author Ravi Chodavarapu (rchodava@gmail.com)
*/
public interface InsertBuilder {
void values(Map<String, ?>... values);
void row(Function<RowBuilder, Map<String, ?>> constructor);
Observable<Row> values(Map<String, ?>... values);
Observable<Row> row(Function<RowBuilder, Map<String, ?>> constructor);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* @author Ravi Chodavarapu (rchodava@gmail.com)
*/
public interface QueryBuilder {
WhereBuilder deleteFrom(String table);
InsertBuilder insertInto(String table);
SelectBuilder selectAll();
SelectBuilder select(String column);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,18 +4,19 @@
import org.chodavarapu.datamill.db.*;
import rx.Observable;

import java.util.Arrays;
import java.util.Map;
import java.util.*;
import java.util.function.Function;

/**
* @author Ravi Chodavarapu (rchodava@gmail.com)
*/
public abstract class QueryBuilderImpl implements QueryBuilder {
private static final String SQL_DELETE_FROM = "DELETE FROM ";
private static final String SQL_INSERT_INTO = "INSERT INTO ";
private static final String SQL_SELECT = "SELECT ";
private static final String SQL_EQ = " = ";
private static final String SQL_FROM = " FROM ";
private static final String SQL_NULL = "NULL";
private static final String SQL_PARAMETER_PLACEHOLDER = "?";
private static final String SQL_WHERE = " WHERE ";

Expand All @@ -28,27 +29,64 @@ public InsertQuery(String table) {
}

@Override
public void row(Function<RowBuilder, Map<String, ?>> constructor) {
values(constructor.apply(new RowBuilderImpl()));
public Observable<Row> row(Function<RowBuilder, Map<String, ?>> constructor) {
return values(constructor.apply(new RowBuilderImpl()));
}

@Override
public void values(Map<String, ?>... values) {

public Observable<Row> values(Map<String, ?>... rows) {
Set<String> columns = new LinkedHashSet<>();
for (Map<String, ?> row : rows) {
columns.addAll(row.keySet());
}

int numColumns = columns.size();
if (numColumns < 1) {
throw new IllegalArgumentException("Cannot insert rows that have no columns!");
}

query.append(" (");
Joiner.on(", ").appendTo(query, columns);
query.append(") VALUES ");

List<Object> parameters = new ArrayList<>(rows.length * numColumns);
List<String> valueSets = new ArrayList<>(rows.length);

for (Map<String, ?> row : rows) {
StringBuilder values = new StringBuilder("(");

int columnIndex = 0;
for (String column : columns) {
Object value = row.get(column);
if (value == null) {
values.append(SQL_NULL);
} else {
values.append(SQL_PARAMETER_PLACEHOLDER);
parameters.add(value);
}

if (columnIndex < numColumns - 1) {
values.append(", ");
}

columnIndex++;
}

values.append(')');
valueSets.add(values.toString());
}

Joiner.on(", ").appendTo(query, valueSets);

return QueryBuilderImpl.this.query(query.toString(), parameters.toArray(new Object[parameters.size()]));
}
}

private class SelectQuery implements SelectBuilder, WhereBuilder, ConditionBuilder {
private final StringBuilder query = new StringBuilder();
private class WhereClause implements WhereBuilder, ConditionBuilder {
private final StringBuilder query;

public SelectQuery() {
query.append(SQL_SELECT);
query.append('*');
}

public SelectQuery(Iterable<String> columns) {
query.append(SQL_SELECT);
query.append(Joiner.on(',').join(columns));
public WhereClause(StringBuilder query) {
this.query = query;
}

@Override
Expand All @@ -65,19 +103,38 @@ public <T> Observable<Row> eq(String column, T value) {
}

@Override
public WhereBuilder from(String table) {
query.append(SQL_FROM);
query.append(table);
public ConditionBuilder where() {
query.append(SQL_WHERE);
return this;
}
}

private class SelectQuery implements SelectBuilder {
private final StringBuilder query = new StringBuilder();

public SelectQuery() {
query.append(SQL_SELECT);
query.append('*');
}

public SelectQuery(Iterable<String> columns) {
query.append(SQL_SELECT);
query.append(Joiner.on(", ").join(columns));
}

@Override
public ConditionBuilder where() {
query.append(SQL_WHERE);
return this;
public WhereBuilder from(String table) {
query.append(SQL_FROM);
query.append(table);
return new WhereClause(query);
}
}

@Override
public WhereBuilder deleteFrom(String table) {
return new WhereClause(new StringBuilder(SQL_DELETE_FROM).append(table));
}

@Override
public InsertBuilder insertInto(String table) {
return new InsertQuery(table);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

import org.chodavarapu.datamill.db.RowBuilder;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
* @author Ravi Chodavarapu (rchodava@gmail.com)
*/
public class RowBuilderImpl implements RowBuilder {
private final Map<String, Object> values = new HashMap<>();
private final Map<String, Object> values = new LinkedHashMap<>();

@Override
public Map<String, ?> build() {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package org.chodavarapu.datamill.db.impl;

import com.google.common.collect.ImmutableMap;
import org.chodavarapu.datamill.db.Row;
import org.junit.Test;
import rx.Observable;

import java.util.Arrays;
import java.util.HashMap;
import java.util.LinkedHashMap;

import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;

/**
* @author Ravi Chodavarapu (rchodava@gmail.com)
*/
public class QueryBuilderImplTest {
private static class TestQueryBuilderImpl extends QueryBuilderImpl {
private String lastQuery;
private Object[] lastParameters;

public String getLastQuery() {
return lastQuery;
}

public Object[] getLastParameters() {
return lastParameters;
}

@Override
protected Observable<Row> query(String query) {
lastQuery = query;
return null;
}

@Override
protected Observable<Row> query(String query, Object... parameters) {
lastQuery = query;
lastParameters = parameters;
return null;
}
}

@Test
public void selectQueries() {
TestQueryBuilderImpl queryBuilder = new TestQueryBuilderImpl();

queryBuilder.selectAll().from("table_name").all();
assertEquals("SELECT * FROM table_name", queryBuilder.getLastQuery());

queryBuilder.selectAll().from("table_name").where().eq("int_column", 2);
assertEquals("SELECT * FROM table_name WHERE int_column = ?", queryBuilder.getLastQuery());
assertArrayEquals(new Object[] { 2 }, queryBuilder.getLastParameters());

queryBuilder.selectAll().from("table_name").where().eq("boolean_column", true);
assertEquals("SELECT * FROM table_name WHERE boolean_column = ?", queryBuilder.getLastQuery());
assertArrayEquals(new Object[] { true }, queryBuilder.getLastParameters());

queryBuilder.selectAll().from("table_name").where().eq("string_column", "value");
assertEquals("SELECT * FROM table_name WHERE string_column = ?", queryBuilder.getLastQuery());
assertArrayEquals(new Object[] { "value" }, queryBuilder.getLastParameters());

queryBuilder.select("column_name").from("table_name").all();
assertEquals("SELECT column_name FROM table_name", queryBuilder.getLastQuery());

queryBuilder.select("column_name", "second_column").from("table_name").all();
assertEquals("SELECT column_name, second_column FROM table_name", queryBuilder.getLastQuery());

queryBuilder.select(Arrays.asList("column_name", "second_column")).from("table_name").all();
assertEquals("SELECT column_name, second_column FROM table_name", queryBuilder.getLastQuery());

queryBuilder.select("column_name", "second_column").from("table_name").where().eq("int_column", 2);
assertEquals("SELECT column_name, second_column FROM table_name WHERE int_column = ?", queryBuilder.getLastQuery());
assertArrayEquals(new Object[] { 2 }, queryBuilder.getLastParameters());
}

@Test
public void deleteQueries() {
TestQueryBuilderImpl queryBuilder = new TestQueryBuilderImpl();

queryBuilder.deleteFrom("table_name").all();
assertEquals("DELETE FROM table_name", queryBuilder.getLastQuery());

queryBuilder.deleteFrom("table_name").where().eq("int_column", 2);
assertEquals("DELETE FROM table_name WHERE int_column = ?", queryBuilder.getLastQuery());
assertArrayEquals(new Object[] { 2 }, queryBuilder.getLastParameters());

queryBuilder.deleteFrom("table_name").where().eq("boolean_column", true);
assertEquals("DELETE FROM table_name WHERE boolean_column = ?", queryBuilder.getLastQuery());
assertArrayEquals(new Object[] { true }, queryBuilder.getLastParameters());

queryBuilder.deleteFrom("table_name").where().eq("string_column", "value");
assertEquals("DELETE FROM table_name WHERE string_column = ?", queryBuilder.getLastQuery());
assertArrayEquals(new Object[] { "value" }, queryBuilder.getLastParameters());
}

@Test
public void insertQueries() {
TestQueryBuilderImpl queryBuilder = new TestQueryBuilderImpl();

queryBuilder.insertInto("table_name").row(r -> r.put("int_column", 2).put("boolean_column", true).build());
assertEquals("INSERT INTO table_name (int_column, boolean_column) VALUES (?, ?)", queryBuilder.getLastQuery());
assertArrayEquals(new Object[] { 2, true }, queryBuilder.getLastParameters());

queryBuilder.insertInto("table_name").row(r -> r.put("boolean_column", true).put("int_column", 2).build());
assertEquals("INSERT INTO table_name (boolean_column, int_column) VALUES (?, ?)", queryBuilder.getLastQuery());
assertArrayEquals(new Object[] { true, 2 }, queryBuilder.getLastParameters());

queryBuilder.insertInto("table_name").row(r -> r.put("boolean_value", true).put("null_column", null).build());
assertEquals("INSERT INTO table_name (boolean_value, null_column) VALUES (?, NULL)", queryBuilder.getLastQuery());
assertArrayEquals(new Object[] { true }, queryBuilder.getLastParameters());

queryBuilder.insertInto("table_name").row(r -> r.put("string_column", "value").build());
assertEquals("INSERT INTO table_name (string_column) VALUES (?)", queryBuilder.getLastQuery());
assertArrayEquals(new Object[] { "value" }, queryBuilder.getLastParameters());

queryBuilder.insertInto("table_name").values(ImmutableMap.of("int_column", 2, "boolean_column", true));
assertEquals("INSERT INTO table_name (int_column, boolean_column) VALUES (?, ?)", queryBuilder.getLastQuery());
assertArrayEquals(new Object[] { 2, true }, queryBuilder.getLastParameters());

HashMap<String, Object> values = new LinkedHashMap<>();
values.put("int_column", 2);
values.put("boolean_column", null);
queryBuilder.insertInto("table_name").values(values);
assertEquals("INSERT INTO table_name (int_column, boolean_column) VALUES (?, NULL)", queryBuilder.getLastQuery());
assertArrayEquals(new Object[] { 2 }, queryBuilder.getLastParameters());

HashMap<String, Object> row1 = new LinkedHashMap<>();
row1.put("int_column", 2);
row1.put("boolean_column", null);
HashMap<String, Object> row2 = new LinkedHashMap<>();
row2.put("int_column", null);
row2.put("string_column", "value");
queryBuilder.insertInto("table_name").values(row1, row2);
assertEquals("INSERT INTO table_name (int_column, boolean_column, string_column) VALUES (?, NULL, NULL), (NULL, NULL, ?)",
queryBuilder.getLastQuery());
assertArrayEquals(new Object[] { 2, "value" }, queryBuilder.getLastParameters());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,27 @@ public void mapWithValues() {
assertEquals((byte) 1, values.get("byteVal"));
assertEquals(1L, values.get("longVal"));
}

@Test
public void mapPreservesOrder() {
RowBuilder builder = new RowBuilderImpl();
builder.put("booleanVal", true)
.put("stringVal", "string")
.put("integerVal", 1)
.put("byteVal", (byte) 1)
.put("longVal", 1L);

int index = 0;
for (String column : builder.build().keySet()) {
switch (index) {
case 0: assertEquals("booleanVal", column); break;
case 1: assertEquals("stringVal", column); break;
case 2: assertEquals("integerVal", column); break;
case 3: assertEquals("byteVal", column); break;
case 4: assertEquals("longVal", column); break;
}

index++;
}
}
}

0 comments on commit 12d5606

Please sign in to comment.