From 43ecfe7c19708db2b67fe7deb84a88da65245b18 Mon Sep 17 00:00:00 2001 From: rbygrave Date: Wed, 17 Dec 2014 21:03:53 +1300 Subject: [PATCH] Fix for #185 - RawSql parse not working with "order siblings by NAME" --- .../java/com/avaje/ebean/DRawSqlParser.java | 22 ++++++++++-------- src/main/java/com/avaje/ebean/RawSql.java | 20 ++++++++++++---- .../server/query/CQueryBuilderRawSql.java | 2 +- .../com/avaje/ebean/TestRawSqlBuilder.java | 23 +++++++++++++++++++ .../tests/rawsql/TestRawSqlOrmWrapper2.java | 2 +- 5 files changed, 53 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/avaje/ebean/DRawSqlParser.java b/src/main/java/com/avaje/ebean/DRawSqlParser.java index eaa196146..9aa3586ba 100644 --- a/src/main/java/com/avaje/ebean/DRawSqlParser.java +++ b/src/main/java/com/avaje/ebean/DRawSqlParser.java @@ -16,8 +16,6 @@ class DRawSqlParser { public static final String $_WHERE = "${where}"; - private static final String ORDER_BY = "order by"; - private final SimpleTextParser textParser; private String sql; @@ -35,6 +33,7 @@ class DRawSqlParser { private int groupByPos = -1; private int havingPos = -1; private int orderByPos = -1; + private int orderByStmtPos = -1; private boolean whereExprAnd; private int whereExprPos = -1; @@ -66,12 +65,12 @@ class DRawSqlParser { String preFrom = removeWhitespace(findPreFromSql()); String preWhere = removeWhitespace(findPreWhereSql()); String preHaving = removeWhitespace(findPreHavingSql()); + String orderByPrefix = findOrderByPrefixSql(); String orderBySql = findOrderBySql(); preFrom = trimSelectKeyword(preFrom); - return new Sql(sql.hashCode(), preFrom, preWhere, whereExprAnd, preHaving, havingExprAnd, - orderBySql, (distinctPos > -1)); + return new Sql(sql.hashCode(), preFrom, preWhere, whereExprAnd, preHaving, havingExprAnd, orderByPrefix, orderBySql, (distinctPos > -1)); } /** @@ -142,12 +141,12 @@ class DRawSqlParser { return preWhereExprSql; } + private String findOrderByPrefixSql() { + return (orderByPos < 1) ? null : sql.substring(orderByPos, orderByStmtPos); + } + private String findOrderBySql() { - if (orderByPos > -1) { - int pos = orderByPos + ORDER_BY.length(); - return sql.substring(pos).trim(); - } - return null; + return (orderByStmtPos < 1) ? null : sql.substring(orderByStmtPos).trim(); } private String findPreHavingSql() { @@ -226,6 +225,11 @@ class DRawSqlParser { } orderByPos = textParser.findWordLower("order", startOrderBy); + if (orderByPos > 1) { + // there might be keywords like siblings in between the order + // and by so search for the by keyword explicitly + orderByStmtPos = 2 + textParser.findWordLower("by", orderByPos); + } } private int findWhereExprPosition() { diff --git a/src/main/java/com/avaje/ebean/RawSql.java b/src/main/java/com/avaje/ebean/RawSql.java index 4dc5211fb..0397a2d1c 100644 --- a/src/main/java/com/avaje/ebean/RawSql.java +++ b/src/main/java/com/avaje/ebean/RawSql.java @@ -222,6 +222,8 @@ public final class RawSql implements Serializable { private final boolean andHavingExpr; + private final String orderByPrefix; + private final String orderBy; private final boolean distinct; @@ -240,6 +242,7 @@ public final class RawSql implements Serializable { this.preWhere = null; this.andHavingExpr = false; this.andWhereExpr = false; + this.orderByPrefix = null; this.orderBy = null; this.distinct = false; } @@ -248,8 +251,7 @@ public final class RawSql implements Serializable { * Construct for parsed SQL. */ protected Sql(int queryHashCode, String preFrom, String preWhere, boolean andWhereExpr, - String preHaving, boolean andHavingExpr, - String orderBy, boolean distinct) { + String preHaving, boolean andHavingExpr, String orderByPrefix, String orderBy, boolean distinct) { this.queryHashCode = queryHashCode; this.parsed = true; @@ -259,6 +261,7 @@ public final class RawSql implements Serializable { this.preWhere = preWhere; this.andHavingExpr = andHavingExpr; this.andWhereExpr = andWhereExpr; + this.orderByPrefix = orderByPrefix; this.orderBy = orderBy; this.distinct = distinct; } @@ -337,6 +340,14 @@ public final class RawSql implements Serializable { return andHavingExpr; } + /** + * Return the 'order by' keywords. + * This can contain additional keywords, for example 'order siblings by' as Oracle syntax. + */ + public String getOrderByPrefix() { + return (orderByPrefix == null) ? "order by" : orderByPrefix; + } + /** * Return the SQL ORDER BY clause. */ @@ -429,9 +440,8 @@ public final class RawSql implements Serializable { for (Column c : dbColumnMap.values()) { pMap.put(c.getPropertyName(), c.getDbColumn()); pcMap.put(c.getPropertyName(), c); - - hc = 31 * hc + c.getPropertyName() == null ? 0 : c.getPropertyName().hashCode(); - hc = 31 * hc + c.getDbColumn() == null ? 0 : c.getDbColumn().hashCode(); + hc = 31 * hc + ((c.getPropertyName() == null) ? 0 : c.getPropertyName().hashCode()); + hc = 31 * hc + ((c.getDbColumn() == null) ? 0 : c.getDbColumn().hashCode()); } this.propertyMap = Collections.unmodifiableMap(pMap); this.propertyColumnMap = Collections.unmodifiableMap(pcMap); diff --git a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilderRawSql.java b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilderRawSql.java index 9f5f31173..ee1826d93 100644 --- a/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilderRawSql.java +++ b/src/main/java/com/avaje/ebeaninternal/server/query/CQueryBuilderRawSql.java @@ -127,7 +127,7 @@ public class CQueryBuilderRawSql implements Constants { } if (!isEmpty(orderBy)) { - sb.append(" order by ").append(orderBy); + sb.append(" ").append(sql.getOrderByPrefix()).append(" ").append(orderBy); } return sb.toString().trim(); diff --git a/src/test/java/com/avaje/ebean/TestRawSqlBuilder.java b/src/test/java/com/avaje/ebean/TestRawSqlBuilder.java index 41576cd1d..3b397f43e 100644 --- a/src/test/java/com/avaje/ebean/TestRawSqlBuilder.java +++ b/src/test/java/com/avaje/ebean/TestRawSqlBuilder.java @@ -38,6 +38,7 @@ public class TestRawSqlBuilder extends BaseTestCase { Assert.assertEquals("id", sql.getPreFrom()); Assert.assertEquals("from t_cust where id > ?", sql.getPreWhere()); Assert.assertEquals("", sql.getPreHaving()); + Assert.assertEquals("order by", sql.getOrderByPrefix()); Assert.assertEquals("id desc", sql.getOrderBy()); r = RawSqlBuilder.parse("select id from t_cust order by id desc"); @@ -65,6 +66,7 @@ public class TestRawSqlBuilder extends BaseTestCase { Assert.assertEquals("id, sum(x)", sql.getPreFrom()); Assert.assertEquals("from t_cust where id > ?", sql.getPreWhere()); Assert.assertEquals("group by id having sum(x) > ?", sql.getPreHaving()); + Assert.assertEquals("order by", sql.getOrderByPrefix()); Assert.assertEquals("id desc", sql.getOrderBy()); // no where @@ -74,6 +76,7 @@ public class TestRawSqlBuilder extends BaseTestCase { Assert.assertEquals("id, sum(x)", sql.getPreFrom()); Assert.assertEquals("from t_cust", sql.getPreWhere()); Assert.assertEquals("group by id having sum(x) > ?", sql.getPreHaving()); + Assert.assertEquals("order by", sql.getOrderByPrefix()); Assert.assertEquals("id desc", sql.getOrderBy()); // no where, no order by @@ -83,6 +86,7 @@ public class TestRawSqlBuilder extends BaseTestCase { Assert.assertEquals("from t_cust", sql.getPreWhere()); Assert.assertEquals("group by id having sum(x) > ?", sql.getPreHaving()); Assert.assertNull(sql.getOrderBy()); + Assert.assertEquals("order by", sql.getOrderByPrefix()); // no order by r = RawSqlBuilder @@ -92,5 +96,24 @@ public class TestRawSqlBuilder extends BaseTestCase { Assert.assertEquals("from t_cust where id > ?", sql.getPreWhere()); Assert.assertEquals("group by id having sum(x) > ?", sql.getPreHaving()); Assert.assertNull(sql.getOrderBy()); + Assert.assertEquals("order by", sql.getOrderByPrefix()); + } + + /** + * test support for order siblings by ... Oracle syntax. + */ + @Test + public void testWithOrderSiblingsByName() { + + String s = "SELECT ID, DESCRIPTION, NAME, PARENT_ID FROM SOME_TABLE WHERE lower(NAME) like :name START WITH ID = :parentId CONNECT BY PRIOR ID = PARENT_ID order siblings by NAME"; + + RawSql rawSql = RawSqlBuilder.parse(s).create(); + + Sql sql = rawSql.getSql(); + Assert.assertEquals("ID, DESCRIPTION, NAME, PARENT_ID", sql.getPreFrom()); + Assert.assertEquals("order siblings by", sql.getOrderByPrefix()); + Assert.assertEquals("NAME", sql.getOrderBy()); + Assert.assertEquals("FROM SOME_TABLE WHERE lower(NAME) like :name START WITH ID = :parentId CONNECT BY PRIOR ID = PARENT_ID", sql.getPreWhere()); + } } diff --git a/src/test/java/com/avaje/tests/rawsql/TestRawSqlOrmWrapper2.java b/src/test/java/com/avaje/tests/rawsql/TestRawSqlOrmWrapper2.java index 0c97a1e17..c9addbf03 100644 --- a/src/test/java/com/avaje/tests/rawsql/TestRawSqlOrmWrapper2.java +++ b/src/test/java/com/avaje/tests/rawsql/TestRawSqlOrmWrapper2.java @@ -23,7 +23,7 @@ public class TestRawSqlOrmWrapper2 extends BaseTestCase { ResetBasicData.reset(); String sql = " select order_id, 'ignoreMe', sum(d.order_qty*d.unit_price) as totalAmount " - + " from o_order_detail d" + " group by order_id "; + + " from o_order_detail d group by order_id "; RawSql rawSql = RawSqlBuilder.parse(sql).columnMapping("order_id", "order.id") .columnMappingIgnore("'ignoreMe'")