mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#1633 - ENH: Add bind parameter expansion for raw() expressions
This commit is contained in:
@@ -529,7 +529,7 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
|
||||
*/
|
||||
@Override
|
||||
public Expression raw(String raw, Object value) {
|
||||
return new RawExpression(raw, new Object[]{value});
|
||||
return RawExpressionBuilder.buildSingle(raw, value);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -541,7 +541,7 @@ public class DefaultExpressionFactory implements SpiExpressionFactory {
|
||||
*/
|
||||
@Override
|
||||
public Expression raw(String raw, Object[] values) {
|
||||
return new RawExpression(raw, values);
|
||||
return RawExpressionBuilder.build(raw, values);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,9 +12,9 @@ import java.util.Collection;
|
||||
|
||||
class RawExpression extends NonPrepareExpression {
|
||||
|
||||
private final String sql;
|
||||
final String sql;
|
||||
|
||||
private final Object[] values;
|
||||
final Object[] values;
|
||||
|
||||
RawExpression(String sql, Object[] values) {
|
||||
this.sql = sql;
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package io.ebeaninternal.server.expression;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
class RawExpressionBuilder {
|
||||
|
||||
private static final String BP_1 = "?1";
|
||||
|
||||
private static final String[] BP = {BP_1, "?2", "?3", "?4", "?5", "?6", "?7", "?8",
|
||||
"?9", "?10", "?11", "?12", "?13", "?14", "?15", "?16", "?17", "?18", "?19", "?20"};
|
||||
|
||||
/**
|
||||
* Build RawExpression taking into account parameter expansion.
|
||||
*/
|
||||
static RawExpression buildSingle(String raw, Object value) {
|
||||
if (isExpand(value, raw, BP_1)) {
|
||||
Collection val = (Collection) value;
|
||||
raw = raw.replace(BP_1, expand(val));
|
||||
return new RawExpression(raw, val.toArray());
|
||||
}
|
||||
return new RawExpression(raw, new Object[]{value});
|
||||
}
|
||||
|
||||
/**
|
||||
* Build RawExpression for multiple bind values taking into account parameter expansion.
|
||||
*/
|
||||
static RawExpression build(String raw, Object[] values) {
|
||||
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
if (isExpand(values[i], raw, match(i))) {
|
||||
return new RawExpressionBuilder(raw, values).build();
|
||||
}
|
||||
}
|
||||
return new RawExpression(raw, values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if this value is a collection that should be expanded.
|
||||
*/
|
||||
private static boolean isExpand(Object value, String raw, String bindMatch) {
|
||||
return value instanceof Collection && raw.contains(bindMatch);
|
||||
}
|
||||
|
||||
private static String match(int i) {
|
||||
if (i < 20) {
|
||||
return BP[i];
|
||||
}
|
||||
return "?" + (i + 1);
|
||||
}
|
||||
|
||||
private static String expand(Collection values) {
|
||||
|
||||
StringBuilder sqlExpand = new StringBuilder(values.size() * 2);
|
||||
for (int i = 0; i < values.size(); i++) {
|
||||
if (i > 0) {
|
||||
sqlExpand.append(",");
|
||||
}
|
||||
sqlExpand.append("?");
|
||||
}
|
||||
return sqlExpand.toString();
|
||||
}
|
||||
|
||||
private final String expanded;
|
||||
|
||||
private final List<Object> params = new ArrayList<>();
|
||||
|
||||
private RawExpressionBuilder(String raw, Object[] values) {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
String match = match(i);
|
||||
if (!isExpand(values[i], raw, match)) {
|
||||
params.add(values[i]);
|
||||
} else {
|
||||
Collection val = (Collection) values[i];
|
||||
params.addAll(val);
|
||||
raw = raw.replace(match, expand(val));
|
||||
}
|
||||
}
|
||||
this.expanded = raw;
|
||||
}
|
||||
|
||||
private RawExpression build() {
|
||||
return new RawExpression(expanded, params.toArray());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package io.ebeaninternal.server.expression;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class RawExpressionBuilderTest {
|
||||
|
||||
@Test
|
||||
public void buildSingle_noop() {
|
||||
|
||||
RawExpression exp = RawExpressionBuilder.buildSingle("foo = ?", 42);
|
||||
assertThat(exp.sql).isEqualTo("foo = ?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildSingle_noExpand() {
|
||||
|
||||
RawExpression exp = RawExpressionBuilder.buildSingle("foo = ?", asList(42, 43));
|
||||
assertThat(exp.sql).isEqualTo("foo = ?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildSingle_expand() {
|
||||
|
||||
RawExpression exp = RawExpressionBuilder.buildSingle("foo in (?1)", asList(42, 43));
|
||||
assertThat(exp.sql).isEqualTo("foo in (?,?)");
|
||||
assertThat(exp.values).contains(42, 43);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void buildSingle_expand_more() {
|
||||
|
||||
RawExpression exp = RawExpressionBuilder.buildSingle("foo in (?1)", asList(42, 43, 44, 45));
|
||||
assertThat(exp.sql).isEqualTo("foo in (?,?,?,?)");
|
||||
assertThat(exp.values).contains(42, 43, 44, 45);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void buildSingle_expand_single() {
|
||||
|
||||
RawExpression exp = RawExpressionBuilder.buildSingle("foo in (?1)", asList(42));
|
||||
assertThat(exp.sql).isEqualTo("foo in (?)");
|
||||
assertThat(exp.values).contains(42);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void build_noop() {
|
||||
RawExpression exp = RawExpressionBuilder.build("foo = ?", asArray(42));
|
||||
assertThat(exp.sql).isEqualTo("foo = ?");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void build_noExpand() {
|
||||
|
||||
RawExpression exp = RawExpressionBuilder.buildSingle("foo = ? and bar = any(?)", asArray(44, asList(42, 43)));
|
||||
assertThat(exp.sql).isEqualTo("foo = ? and bar = any(?)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void build_expand() {
|
||||
|
||||
RawExpression exp = RawExpressionBuilder.build("foo in (?) and bar in (?2)", asArray(44, asList(42, 43)));
|
||||
assertThat(exp.sql).isEqualTo("foo in (?) and bar in (?,?)");
|
||||
assertThat(exp.values).contains(44, 42, 43);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void build_expand2() {
|
||||
|
||||
RawExpression exp = RawExpressionBuilder.build("foo in (?) and bar in (?2) (?3)", asArray(44, asList(42, 43), asList(91, 92, 93)));
|
||||
assertThat(exp.sql).isEqualTo("foo in (?) and bar in (?,?) (?,?,?)");
|
||||
assertThat(exp.values).containsExactly(44, 42, 43, 91, 92, 93);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void build_expand3() {
|
||||
|
||||
RawExpression exp = RawExpressionBuilder.build("foo in (?) and bar in (?2) (?3) and ?", asArray(44, asList(42, 43), asList(91, 92, 93), 87));
|
||||
assertThat(exp.sql).isEqualTo("foo in (?) and bar in (?,?) (?,?,?) and ?");
|
||||
assertThat(exp.values).containsExactly(44, 42, 43, 91, 92, 93, 87);
|
||||
}
|
||||
|
||||
private Object[] asArray(Object... values) {
|
||||
return values;
|
||||
}
|
||||
}
|
||||
@@ -6,16 +6,16 @@ import io.ebean.Expr;
|
||||
import io.ebean.Query;
|
||||
import io.ebean.annotation.ForPlatform;
|
||||
import io.ebean.annotation.Platform;
|
||||
import org.junit.Test;
|
||||
import org.tests.model.basic.Customer;
|
||||
import org.tests.model.basic.Order;
|
||||
import org.tests.model.basic.OrderDetail;
|
||||
import org.tests.model.basic.ResetBasicData;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.sql.Timestamp;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class TestWhereRawClause extends BaseTestCase {
|
||||
@@ -50,6 +50,35 @@ public class TestWhereRawClause extends BaseTestCase {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRaw_bindExpansion_subquery() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Customer> query = Ebean.find(Customer.class)
|
||||
.where()
|
||||
.raw("id in (select c.id from o_customer c where c.name in (?1))", asList("Rob", "Fiona", "Jack"))
|
||||
.query();
|
||||
|
||||
List<Customer> list = query.findList();
|
||||
assertThat(list).isNotEmpty();
|
||||
assertThat(sqlOf(query)).contains(" t0.id in (select c.id from o_customer c where c.name in (?,?,?))");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRaw_bindExpansion() {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
Query<Customer> query = Ebean.find(Customer.class)
|
||||
.where()
|
||||
.raw("name in (?1)", asList("Rob", "Fiona", "Jack"))
|
||||
.query();
|
||||
|
||||
List<Customer> list = query.findList();
|
||||
assertThat(list).isNotEmpty();
|
||||
assertThat(sqlOf(query)).contains(" t0.name in (?,?,?)");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ForPlatform(Platform.POSTGRES)
|
||||
@@ -57,13 +86,9 @@ public class TestWhereRawClause extends BaseTestCase {
|
||||
|
||||
ResetBasicData.reset();
|
||||
|
||||
List<String> names = new ArrayList<>();
|
||||
names.add("Rob");
|
||||
names.add("Fiona");
|
||||
|
||||
List<Customer> list = Ebean.find(Customer.class)
|
||||
.where()
|
||||
.raw("name = any(?)", names)
|
||||
.raw("name = any(?)", asList("Rob", "Fiona", "Jack"))
|
||||
.findList();
|
||||
|
||||
assertThat(list).isNotEmpty();
|
||||
|
||||
Reference in New Issue
Block a user