From 6e196a3376561ca0df7a12e328c78fa5e62371d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20Westk=C3=A4mper?= Date: Tue, 5 Nov 2013 22:11:51 +0200 Subject: [PATCH] Fix serialization of single item column lists #541 --- .../mysema/query/types/ExpressionUtils.java | 155 +++++++++--------- .../main/java/com/mysema/query/types/Ops.java | 2 + .../com/mysema/query/types/Templates.java | 1 + .../java/com/mysema/query/CoverageTest.java | 9 +- .../mysema/query/sql/SerializationTest.java | 83 ++++++---- 5 files changed, 142 insertions(+), 108 deletions(-) diff --git a/querydsl-core/src/main/java/com/mysema/query/types/ExpressionUtils.java b/querydsl-core/src/main/java/com/mysema/query/types/ExpressionUtils.java index e8760d2a5..eca2bbbbe 100644 --- a/querydsl-core/src/main/java/com/mysema/query/types/ExpressionUtils.java +++ b/querydsl-core/src/main/java/com/mysema/query/types/ExpressionUtils.java @@ -1,6 +1,6 @@ /* * Copyright 2011, Mysema Ltd - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -25,15 +25,15 @@ import com.mysema.query.QueryException; /** - * ExpressionUtils provides utilities for constructing common operation instances. This class is + * ExpressionUtils provides utilities for constructing common operation instances. This class is * used internally in Querydsl and is not suitable to be used in cases where DSL methods are needed, * since the Expression implementations used in this class are minimal internal implementations. - * + * * @author tiwe * */ public final class ExpressionUtils { - + /** * @param col * @return @@ -51,10 +51,10 @@ public final class ExpressionUtils { public static Expression any(CollectionExpression col) { return OperationImpl.create((Class)col.getParameter(0), Ops.QuantOps.ANY, col); } - + /** * Create the intersection of the given arguments - * + * * @param exprs * @return */ @@ -63,15 +63,15 @@ public final class ExpressionUtils { Predicate rv = null; for (Predicate b : exprs) { if (b != null) { - rv = rv == null ? b : ExpressionUtils.and(rv,b); - } + rv = rv == null ? b : ExpressionUtils.and(rv,b); + } } return rv; } - + /** * Create the intersection of the given arguments - * + * * @param exprs * @return */ @@ -80,15 +80,15 @@ public final class ExpressionUtils { Predicate rv = null; for (Predicate b : exprs) { if (b != null) { - rv = rv == null ? b : ExpressionUtils.and(rv,b); - } + rv = rv == null ? b : ExpressionUtils.and(rv,b); + } } return rv; } /** * Create the intersection of the given arguments - * + * * @param left * @param right * @return @@ -96,11 +96,11 @@ public final class ExpressionUtils { public static Predicate and(Predicate left, Predicate right) { return PredicateOperation.create(Ops.AND, left, right); } - + /** * Create the union of the given arguments - * + * * @param exprs * @return */ @@ -109,15 +109,15 @@ public final class ExpressionUtils { Predicate rv = null; for (Predicate b : exprs) { if (b != null) { - rv = rv == null ? b : ExpressionUtils.or(rv,b); - } + rv = rv == null ? b : ExpressionUtils.or(rv,b); + } } return rv; } - + /** * Create the union of the given arguments - * + * * @param exprs * @return */ @@ -126,15 +126,15 @@ public final class ExpressionUtils { Predicate rv = null; for (Predicate b : exprs) { if (b != null) { - rv = rv == null ? b : ExpressionUtils.or(rv,b); - } + rv = rv == null ? b : ExpressionUtils.or(rv,b); + } } return rv; } - + /** * Create an alias expression (source as alias) with the given source and alias - * + * * @param * @param source * @param alias @@ -143,10 +143,10 @@ public final class ExpressionUtils { public static Expression as(Expression source, Path alias) { return OperationImpl.create(alias.getType(), Ops.ALIAS, source, alias); } - + /** * Create an alias expression (source as alias) with the given source and alias - * + * * @param * @param source * @param alias @@ -163,10 +163,10 @@ public final class ExpressionUtils { public static Expression count(Expression source) { return OperationImpl.create(Long.class, Ops.AggOps.COUNT_AGG, source); } - + /** * Create an left equals constant expression - * + * * @param * @param left * @param constant @@ -175,10 +175,10 @@ public final class ExpressionUtils { public static Predicate eqConst(Expression left, D constant) { return eq(left, new ConstantImpl(constant)); } - + /** * Create an left equals right expression - * + * * @param * @param left * @param right @@ -187,10 +187,10 @@ public final class ExpressionUtils { public static Predicate eq(Expression left, Expression right) { return PredicateOperation.create(Ops.EQ, left, right); } - + /** * Create an left in right expression - * + * * @param * @param left * @param right @@ -199,10 +199,10 @@ public final class ExpressionUtils { public static Predicate in(Expression left, CollectionExpression right) { return PredicateOperation.create(Ops.IN, left, right); } - + /** * Create an left in right expression - * + * * @param * @param left * @param right @@ -215,37 +215,37 @@ public final class ExpressionUtils { return PredicateOperation.create(Ops.IN, left, new ConstantImpl>(right)); } } - + /** * Create a left is null expression - * + * * @param left * @return */ public static Predicate isNull(Expression left) { return PredicateOperation.create(Ops.IS_NULL, left); } - + /** * Create a left is not null expression - * + * * @param left * @return */ public static Predicate isNotNull(Expression left) { return PredicateOperation.create(Ops.IS_NOT_NULL, left); - } - + } + /** * Convert the given like pattern to a regex pattern - * + * * @param expr * @return - */ + */ public static Expression likeToRegex(Expression expr) { return likeToRegex(expr, true); } - + @SuppressWarnings("unchecked") public static Expression likeToRegex(Expression expr, boolean matchStartAndEnd) { @@ -257,14 +257,14 @@ public final class ExpressionUtils { rv.append('^'); } for (int i = 0; i < like.length(); i++) { - char ch = like.charAt(i); + char ch = like.charAt(i); if (ch == '.' || ch == '*' || ch == '?') { rv.append('\\'); } else if (ch == '%') { - rv.append(".*"); + rv.append(".*"); continue; } else if (ch == '_') { - rv.append('.'); + rv.append('.'); continue; } rv.append(ch); @@ -273,21 +273,21 @@ public final class ExpressionUtils { rv.append('$'); } if (!like.equals(rv.toString())) { - return ConstantImpl.create(rv.toString()); - } + return ConstantImpl.create(rv.toString()); + } } else if (expr instanceof Operation) { Operation o = (Operation)expr; if (o.getOperator() == Ops.CONCAT) { Expression lhs = likeToRegex((Expression) o.getArg(0), false); Expression rhs = likeToRegex((Expression) o.getArg(1), false); if (lhs != o.getArg(0) || rhs != o.getArg(1)) { - return OperationImpl.create(String.class, Ops.CONCAT, lhs, rhs); - } + return OperationImpl.create(String.class, Ops.CONCAT, lhs, rhs); + } } } return expr; } - + /** * @param exprs * @return @@ -295,23 +295,28 @@ public final class ExpressionUtils { public static Expression list(Class clazz, Expression... exprs) { return list(clazz, ImmutableList.copyOf(exprs)); } - + /** * @param exprs * @return */ - public static Expression list(Class clazz, List> exprs) { + public static Expression list(Class clazz, List> exprs) { Expression rv = (Expression)exprs.get(0); - for (int i = 1; i < exprs.size(); i++) { - rv = OperationImpl.create(clazz, Ops.LIST, rv, exprs.get(i)); + if (exprs.size() == 1) { + rv = OperationImpl.create(clazz, Ops.SINGLETON, rv, exprs.get(0)); + } else { + for (int i = 1; i < exprs.size(); i++) { + rv = OperationImpl.create(clazz, Ops.LIST, rv, exprs.get(i)); + } } + return rv; - } - + } + @SuppressWarnings("unchecked") public static Expression regexToLike(Expression expr) { - if (expr instanceof Constant) { + if (expr instanceof Constant) { final String str = expr.toString(); final StringBuilder rv = new StringBuilder(str.length() + 2); boolean escape = false; @@ -331,30 +336,30 @@ public final class ExpressionUtils { } else if (!escape && (ch == '[' || ch == ']' || ch == '^' || ch == '.' || ch == '*')) { throw new QueryException("'" + str + "' can't be converted to like form"); } else if (escape && (ch == 'd' || ch == 'D' || ch == 's' || ch == 'S' || ch == 'w' || ch == 'W')) { - throw new QueryException("'" + str + "' can't be converted to like form"); + throw new QueryException("'" + str + "' can't be converted to like form"); } rv.append(ch); escape = false; } if (!rv.toString().equals(str)) { return ConstantImpl.create(rv.toString()); - } + } } else if (expr instanceof Operation) { Operation o = (Operation)expr; if (o.getOperator() == Ops.CONCAT) { Expression lhs = regexToLike((Expression) o.getArg(0)); Expression rhs = regexToLike((Expression) o.getArg(1)); if (lhs != o.getArg(0) || rhs != o.getArg(1)) { - return OperationImpl.create(String.class, Ops.CONCAT, lhs, rhs); - } - } + return OperationImpl.create(String.class, Ops.CONCAT, lhs, rhs); + } + } } return expr; } - + /** * Create a left not equals constant expression - * + * * @param * @param left * @param constant @@ -363,10 +368,10 @@ public final class ExpressionUtils { public static Predicate neConst(Expression left, D constant) { return ne(left, new ConstantImpl(constant)); } - + /** * Create a left not equals right expression - * + * * @param * @param left * @param right @@ -375,10 +380,10 @@ public final class ExpressionUtils { public static Predicate ne(Expression left, Expression right) { return PredicateOperation.create(Ops.NE, left, right); } - + /** * Create a left or right expression - * + * * @param left * @param right * @return @@ -386,7 +391,7 @@ public final class ExpressionUtils { public static Predicate or(Predicate left, Predicate right) { return PredicateOperation.create(Ops.OR, left, right); } - + /** * @param args * @return @@ -401,7 +406,7 @@ public final class ExpressionUtils { } return builder.build(); } - + /** * @param args * @return @@ -418,7 +423,7 @@ public final class ExpressionUtils { } return builder.build(); } - + /** * @param expr * @return @@ -428,10 +433,10 @@ public final class ExpressionUtils { if (clazz == PathImpl.class || clazz == PredicateOperation.class || clazz == ConstantImpl.class) { return expr; } else { - return (Expression) expr.accept(ExtractorVisitor.DEFAULT, null); - } + return (Expression) expr.accept(ExtractorVisitor.DEFAULT, null); + } } - + private ExpressionUtils() {} - + } diff --git a/querydsl-core/src/main/java/com/mysema/query/types/Ops.java b/querydsl-core/src/main/java/com/mysema/query/types/Ops.java index fabbc6838..c2f4911be 100644 --- a/querydsl-core/src/main/java/com/mysema/query/types/Ops.java +++ b/querydsl-core/src/main/java/com/mysema/query/types/Ops.java @@ -45,6 +45,8 @@ public final class Ops { public static final Operator LIST = new OperatorImpl(NS, "LIST"); + public static final Operator SINGLETON = new OperatorImpl(NS, "SINGLETON"); + public static final Operator ORDINAL = new OperatorImpl(NS, "ORDINAL"); public static final Operator WRAPPED = new OperatorImpl(NS, "WRAPPED"); diff --git a/querydsl-core/src/main/java/com/mysema/query/types/Templates.java b/querydsl-core/src/main/java/com/mysema/query/types/Templates.java index b06a5c81d..d39f4fcdc 100644 --- a/querydsl-core/src/main/java/com/mysema/query/types/Templates.java +++ b/querydsl-core/src/main/java/com/mysema/query/types/Templates.java @@ -45,6 +45,7 @@ public class Templates { //CHECKSTYLE:OFF add(Ops.LIST, "{0}, {1}", 40); + add(Ops.SINGLETON, "{0}", 40); add(Ops.WRAPPED, "({0})"); // boolean diff --git a/querydsl-core/src/test/java/com/mysema/query/CoverageTest.java b/querydsl-core/src/test/java/com/mysema/query/CoverageTest.java index b56dffd15..79941d3fe 100644 --- a/querydsl-core/src/test/java/com/mysema/query/CoverageTest.java +++ b/querydsl-core/src/test/java/com/mysema/query/CoverageTest.java @@ -1,6 +1,6 @@ /* * Copyright 2011, Mysema Ltd - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -105,9 +105,9 @@ public class CoverageTest { Ops.ARRAY_SIZE, Ops.MOD, Ops.STRING_CAST, -// Ops.DELEGATE, +// Ops.DELEGATE, Ops.WRAPPED, - + Ops.XOR, Ops.XNOR, @@ -118,10 +118,11 @@ public class CoverageTest { Ops.CASE_EQ_ELSE, Ops.LIST, + Ops.SINGLETON, Ops.COALESCE, Ops.ORDINAL, // TODO: add support Ops.MATCHES_IC, - + // aggregation Ops.AggOps.AVG_AGG, Ops.AggOps.MAX_AGG, diff --git a/querydsl-sql/src/test/java/com/mysema/query/sql/SerializationTest.java b/querydsl-sql/src/test/java/com/mysema/query/sql/SerializationTest.java index effc6cb78..38aa2c3a0 100644 --- a/querydsl-sql/src/test/java/com/mysema/query/sql/SerializationTest.java +++ b/querydsl-sql/src/test/java/com/mysema/query/sql/SerializationTest.java @@ -1,6 +1,6 @@ /* * Copyright 2011, Mysema Ltd - * + * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at @@ -25,43 +25,44 @@ import com.mysema.query.sql.dml.SQLInsertClause; import com.mysema.query.sql.dml.SQLUpdateClause; import com.mysema.query.sql.domain.QEmployee; import com.mysema.query.sql.domain.QSurvey; +import com.mysema.query.types.Path; import com.mysema.query.types.SubQueryExpression; import com.mysema.query.types.path.PathBuilder; public class SerializationTest { - + private static final QSurvey survey = QSurvey.survey; - + private final Connection connection = EasyMock.createMock(Connection.class); @Test - public void InnerJoin() { + public void InnerJoin() { SQLQuery query = new SQLQuery(connection,SQLTemplates.DEFAULT); query.from(new QSurvey("s1")).innerJoin(new QSurvey("s2")); assertEquals("from SURVEY s1\ninner join SURVEY s2", query.toString()); } - + @Test - public void LeftJoin() { + public void LeftJoin() { SQLQuery query = new SQLQuery(connection,SQLTemplates.DEFAULT); query.from(new QSurvey("s1")).leftJoin(new QSurvey("s2")); assertEquals("from SURVEY s1\nleft join SURVEY s2", query.toString()); } - + @Test - public void RightJoin() { + public void RightJoin() { SQLQuery query = new SQLQuery(connection,SQLTemplates.DEFAULT); query.from(new QSurvey("s1")).rightJoin(new QSurvey("s2")); assertEquals("from SURVEY s1\nright join SURVEY s2", query.toString()); } - + @Test - public void FullJoin() { + public void FullJoin() { SQLQuery query = new SQLQuery(connection,SQLTemplates.DEFAULT); query.from(new QSurvey("s1")).fullJoin(new QSurvey("s2")); assertEquals("from SURVEY s1\nfull join SURVEY s2", query.toString()); } - + @Test public void Update() { SQLUpdateClause updateClause = new SQLUpdateClause(connection,SQLTemplates.DEFAULT,survey); @@ -69,7 +70,7 @@ public class SerializationTest { updateClause.set(survey.name, (String)null); assertEquals("update SURVEY\nset ID = ?, NAME = ?", updateClause.toString()); } - + @Test public void Update_Where() { SQLUpdateClause updateClause = new SQLUpdateClause(connection,SQLTemplates.DEFAULT,survey); @@ -78,7 +79,7 @@ public class SerializationTest { updateClause.where(survey.name.eq("XXX")); assertEquals("update SURVEY\nset ID = ?, NAME = ?\nwhere SURVEY.NAME = ?", updateClause.toString()); } - + @Test public void Insert() { SQLInsertClause insertClause = new SQLInsertClause(connection,SQLTemplates.DEFAULT,survey); @@ -86,7 +87,7 @@ public class SerializationTest { insertClause.set(survey.name, (String)null); assertEquals("insert into SURVEY (ID, NAME)\nvalues (?, ?)", insertClause.toString()); } - + @Test public void Delete_with_SubQuery_exists() { QSurvey survey1 = new QSurvey("s1"); @@ -106,7 +107,7 @@ public class SerializationTest { serializer.serialize(sq.getMetadata(), false); assertEquals("select nextval('myseq')\nfrom SURVEY SURVEY", serializer.toString()); } - + @Test public void FunctionCall() { RelationalFunctionCall func = RelationalFunctionCall.create(String.class, "TableValuedFunction", "parameter"); @@ -115,7 +116,7 @@ public class SerializationTest { SubQueryExpression expr = sq.from(survey) .join(func, funcAlias).on(survey.name.like(funcAlias.getString("prop")).not()) .list(survey.name); - + SQLSerializer serializer = new SQLSerializer(new Configuration(new SQLServerTemplates())); serializer.serialize(expr.getMetadata(), false); assertEquals("select SURVEY.NAME\n" + @@ -124,7 +125,7 @@ public class SerializationTest { "on not SURVEY.NAME like tokFunc.prop escape '\\'", serializer.toString()); } - + @Test public void FunctionCall2() { RelationalFunctionCall func = RelationalFunctionCall.create(String.class, "TableValuedFunction", "parameter"); @@ -132,55 +133,79 @@ public class SerializationTest { SQLQuery q = new SQLQuery(new SQLServerTemplates()); q.from(survey) .join(func, funcAlias).on(survey.name.like(funcAlias.getString("prop")).not()); - + assertEquals("from SURVEY SURVEY\n" + "join TableValuedFunction(?) as tokFunc\n" + "on not SURVEY.NAME like tokFunc.prop escape '\\'", q.toString()); } - + @Test public void Union() { SQLQuery q = new SQLQuery(SQLTemplates.DEFAULT); q.union(new SQLSubQuery().from(survey).list(survey.all()), new SQLSubQuery().from(survey).list(survey.all())); - + assertEquals("(select SURVEY.NAME, SURVEY.NAME2, SURVEY.ID\n" + "from SURVEY SURVEY)\n" + "union\n" + "(select SURVEY.NAME, SURVEY.NAME2, SURVEY.ID\n" + "from SURVEY SURVEY)", q.toString()); - + } - + @Test public void Union_GroupBy() { SQLQuery q = new SQLQuery(SQLTemplates.DEFAULT); q.union(new SQLSubQuery().from(survey).list(survey.all()), new SQLSubQuery().from(survey).list(survey.all())) .groupBy(survey.id); - + assertEquals("(select SURVEY.NAME, SURVEY.NAME2, SURVEY.ID\n" + "from SURVEY SURVEY)\n" + "union\n" + "(select SURVEY.NAME, SURVEY.NAME2, SURVEY.ID\n" + "from SURVEY SURVEY)\n"+ "group by SURVEY.ID", q.toString()); - + } - + @Test public void Union2() { SQLQuery q = new SQLQuery(SQLTemplates.DEFAULT); - q.union(survey, + q.union(survey, new SQLSubQuery().from(survey).list(survey.all()), new SQLSubQuery().from(survey).list(survey.all())); - + assertEquals("from ((select SURVEY.NAME, SURVEY.NAME2, SURVEY.ID\n"+ "from SURVEY SURVEY)\n" + "union\n" + "(select SURVEY.NAME, SURVEY.NAME2, SURVEY.ID\n" + "from SURVEY SURVEY)) as SURVEY", q.toString()); - + } - + + @Test + public void With() { + QSurvey survey2 = new QSurvey("survey2"); + SQLQuery q = new SQLQuery(SQLTemplates.DEFAULT); + q.with(survey, survey.id, survey.name).as( + new SQLSubQuery().from(survey2).list(survey2.id, survey2.name)); + + assertEquals("with SURVEY (ID, NAME) as (select survey2.ID, survey2.NAME\n" + + "from SURVEY survey2)\n\n" + + "from dual", q.toString()); + } + + @Test + public void With_SingleColumn() { + QSurvey survey2 = new QSurvey("survey2"); + SQLQuery q = new SQLQuery(SQLTemplates.DEFAULT); + q.with(survey, new Path[]{ survey.id }).as( + new SQLSubQuery().from(survey2).list(survey2.id)); + + assertEquals("with SURVEY (ID) as (select survey2.ID\n" + + "from SURVEY survey2)\n\n" + + "from dual", q.toString()); + } + }