mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
291 lines
7.8 KiB
Java
291 lines
7.8 KiB
Java
package com.avaje.ebean;
|
|
|
|
import com.avaje.ebean.RawSql.Sql;
|
|
|
|
/**
|
|
* Parses sql-select queries to try and determine the location where WHERE and
|
|
* HAVING clauses can be added dynamically to the sql.
|
|
*/
|
|
class DRawSqlParser {
|
|
|
|
public static final String $_AND_HAVING = "${andHaving}";
|
|
|
|
public static final String $_HAVING = "${having}";
|
|
|
|
public static final String $_AND_WHERE = "${andWhere}";
|
|
|
|
public static final String $_WHERE = "${where}";
|
|
|
|
private final SimpleTextParser textParser;
|
|
|
|
private String sql;
|
|
|
|
private int placeHolderWhere;
|
|
private int placeHolderAndWhere;
|
|
private int placeHolderHaving;
|
|
private int placeHolderAndHaving;
|
|
private final boolean hasPlaceHolders;
|
|
|
|
private int selectPos = -1;
|
|
private int distinctPos = -1;
|
|
private int fromPos = -1;
|
|
private int wherePos = -1;
|
|
private int groupByPos = -1;
|
|
private int havingPos = -1;
|
|
private int orderByPos = -1;
|
|
private int orderByStmtPos = -1;
|
|
|
|
private boolean whereExprAnd;
|
|
private int whereExprPos = -1;
|
|
private boolean havingExprAnd;
|
|
private int havingExprPos = -1;
|
|
|
|
public static Sql parse(String sql) {
|
|
return new DRawSqlParser(sql).parse();
|
|
}
|
|
|
|
private DRawSqlParser(String sqlString) {
|
|
sqlString = sqlString.trim();
|
|
sqlString = sqlString.replace('\n',' ');
|
|
this.sql = sqlString;
|
|
this.hasPlaceHolders = findAndRemovePlaceHolders();
|
|
this.textParser = new SimpleTextParser(sqlString);
|
|
}
|
|
|
|
private Sql parse() {
|
|
|
|
if (!hasPlaceHolders()) {
|
|
// parse the sql for the keywords...
|
|
// select, from, where, having, group by, order by
|
|
parseSqlFindKeywords(true);
|
|
}
|
|
|
|
whereExprPos = findWhereExprPosition();
|
|
havingExprPos = findHavingExprPosition();
|
|
|
|
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, preFrom, preWhere, whereExprAnd, preHaving, havingExprAnd, orderByPrefix, orderBySql, (distinctPos > -1));
|
|
}
|
|
|
|
/**
|
|
* Find and remove the known place holders such as ${where}.
|
|
*/
|
|
private boolean findAndRemovePlaceHolders() {
|
|
placeHolderWhere = removePlaceHolder($_WHERE);
|
|
placeHolderAndWhere = removePlaceHolder($_AND_WHERE);
|
|
placeHolderHaving = removePlaceHolder($_HAVING);
|
|
placeHolderAndHaving = removePlaceHolder($_AND_HAVING);
|
|
return hasPlaceHolders();
|
|
}
|
|
|
|
private int removePlaceHolder(String placeHolder) {
|
|
int pos = sql.indexOf(placeHolder);
|
|
if (pos > -1) {
|
|
int after = pos + placeHolder.length() + 1;
|
|
if (after > sql.length()) {
|
|
sql = sql.substring(0, pos);
|
|
} else {
|
|
sql = sql.substring(0, pos) + sql.substring(after);
|
|
}
|
|
}
|
|
return pos;
|
|
}
|
|
|
|
private boolean hasPlaceHolders() {
|
|
return placeHolderWhere > -1 || placeHolderAndWhere > -1 || placeHolderHaving > -1 || placeHolderAndHaving > -1;
|
|
}
|
|
|
|
/**
|
|
* Trim off the select keyword (to support row_number() limit function).
|
|
*/
|
|
private String trimSelectKeyword(String preWhereExprSql) {
|
|
|
|
if (selectPos < 0) {
|
|
throw new IllegalStateException("select keyword not found?");
|
|
}
|
|
// trim of select keyword
|
|
preWhereExprSql = preWhereExprSql.trim();
|
|
String select = preWhereExprSql.substring(0, 7);
|
|
if (!select.equalsIgnoreCase("select ")) {
|
|
throw new RuntimeException("Expecting [" + preWhereExprSql + "] to start with \"select\"");
|
|
}
|
|
preWhereExprSql = preWhereExprSql.substring(7).trim();
|
|
if (distinctPos > -1) {
|
|
// trim of distinct keyword
|
|
String distinct = preWhereExprSql.substring(0, 9);
|
|
if (!distinct.equalsIgnoreCase("distinct ")) {
|
|
throw new RuntimeException("Expecting [" + preWhereExprSql + "] to start with \"select distinct\"");
|
|
}
|
|
preWhereExprSql = preWhereExprSql.substring(9);
|
|
}
|
|
|
|
return preWhereExprSql;
|
|
}
|
|
|
|
private String findOrderByPrefixSql() {
|
|
return (orderByPos < 1) ? null : sql.substring(orderByPos, orderByStmtPos);
|
|
}
|
|
|
|
private String findOrderBySql() {
|
|
return (orderByStmtPos < 1) ? null : sql.substring(orderByStmtPos).trim();
|
|
}
|
|
|
|
private String findPreHavingSql() {
|
|
if (havingExprPos > whereExprPos) {
|
|
// an order by clause follows...
|
|
return sql.substring(whereExprPos, havingExprPos - 1);
|
|
}
|
|
if (whereExprPos > -1) {
|
|
if (orderByPos == -1) {
|
|
return sql.substring(whereExprPos);
|
|
|
|
} else if (whereExprPos == orderByPos) {
|
|
return "";
|
|
|
|
} else {
|
|
return sql.substring(whereExprPos, orderByPos - 1);
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private String findPreFromSql() {
|
|
return sql.substring(0, fromPos - 1);
|
|
}
|
|
|
|
private String findPreWhereSql() {
|
|
if (whereExprPos > -1) {
|
|
return sql.substring(fromPos, whereExprPos - 1);
|
|
} else {
|
|
return sql.substring(fromPos);
|
|
}
|
|
}
|
|
|
|
private void parseSqlFindKeywords(boolean allKeywords) {
|
|
|
|
selectPos = textParser.findWordLower("select");
|
|
if (selectPos == -1) {
|
|
String msg = "Error parsing sql, can not find SELECT keyword in:";
|
|
throw new RuntimeException(msg + sql);
|
|
}
|
|
|
|
String possibleDistinct = textParser.nextWord();
|
|
if ("distinct".equals(possibleDistinct)) {
|
|
distinctPos = textParser.getPos() - 8;
|
|
}
|
|
|
|
fromPos = textParser.findWordLower("from");
|
|
if (fromPos == -1) {
|
|
String msg = "Error parsing sql, can not find FROM keyword in:";
|
|
throw new RuntimeException(msg + sql);
|
|
}
|
|
|
|
if (!allKeywords) {
|
|
return;
|
|
}
|
|
|
|
wherePos = textParser.findWordLower("where");
|
|
if (wherePos == -1) {
|
|
groupByPos = textParser.findWordLower("group", fromPos + 5);
|
|
} else {
|
|
groupByPos = textParser.findWordLower("group");
|
|
}
|
|
if (groupByPos > -1) {
|
|
havingPos = textParser.findWordLower("having");
|
|
}
|
|
|
|
int startOrderBy = havingPos;
|
|
if (startOrderBy == -1) {
|
|
startOrderBy = groupByPos;
|
|
}
|
|
if (startOrderBy == -1) {
|
|
startOrderBy = wherePos;
|
|
}
|
|
if (startOrderBy == -1) {
|
|
startOrderBy = fromPos;
|
|
}
|
|
|
|
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() {
|
|
if (hasPlaceHolders) {
|
|
if (placeHolderWhere > -1) {
|
|
return placeHolderWhere;
|
|
} else {
|
|
whereExprAnd = true;
|
|
return placeHolderAndWhere;
|
|
}
|
|
}
|
|
whereExprAnd = wherePos > 0;
|
|
if (groupByPos > 0) {
|
|
return groupByPos;
|
|
}
|
|
if (havingPos > 0) {
|
|
return havingPos;
|
|
}
|
|
if (orderByPos > 0) {
|
|
return orderByPos;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
private int findHavingExprPosition() {
|
|
if (hasPlaceHolders) {
|
|
if (placeHolderHaving > -1) {
|
|
return placeHolderHaving;
|
|
} else {
|
|
havingExprAnd = true;
|
|
return placeHolderAndHaving;
|
|
}
|
|
}
|
|
havingExprAnd = havingPos > 0;
|
|
if (orderByPos > 0) {
|
|
return orderByPos;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
private String removeWhitespace(String sql) {
|
|
if (sql == null) {
|
|
return "";
|
|
}
|
|
|
|
boolean removeWhitespace = false;
|
|
|
|
int length = sql.length();
|
|
StringBuilder sb = new StringBuilder();
|
|
for (int i = 0; i < length; i++) {
|
|
char c = sql.charAt(i);
|
|
if (removeWhitespace) {
|
|
if (!Character.isWhitespace(c)) {
|
|
sb.append(c);
|
|
removeWhitespace = false;
|
|
}
|
|
} else {
|
|
if (c == '\r' || c == '\n') {
|
|
sb.append('\n');
|
|
removeWhitespace = true;
|
|
} else {
|
|
sb.append(c);
|
|
}
|
|
}
|
|
}
|
|
|
|
String s = sb.toString();
|
|
return s.trim();
|
|
}
|
|
}
|