mirror of
https://github.com/ebean-orm/ebean.git
synced 2024-04-21 10:51:47 +00:00
#1831 - Update query with maxRows does not include the limit in the generated SQL
This commit is contained in:
@@ -47,24 +47,19 @@ class CQueryBuilder {
|
||||
final String columnAliasPrefix;
|
||||
|
||||
private final SqlLimiter sqlLimiter;
|
||||
|
||||
private final CQueryBuilderRawSql rawSqlHandler;
|
||||
|
||||
private final Binder binder;
|
||||
|
||||
private final boolean selectCountWithAlias;
|
||||
|
||||
private final CQueryHistorySupport historySupport;
|
||||
|
||||
private final CQueryDraftSupport draftSupport;
|
||||
|
||||
private final DatabasePlatform dbPlatform;
|
||||
|
||||
/**
|
||||
* Create the SqlGenSelect.
|
||||
*/
|
||||
CQueryBuilder(DatabasePlatform dbPlatform, Binder binder, CQueryHistorySupport historySupport, CQueryDraftSupport draftSupport) {
|
||||
|
||||
this.dbPlatform = dbPlatform;
|
||||
this.binder = binder;
|
||||
this.draftSupport = draftSupport;
|
||||
@@ -102,7 +97,7 @@ class CQueryBuilder {
|
||||
|
||||
SpiQuery<T> query = request.getQuery();
|
||||
String rootTableAlias = query.getAlias();
|
||||
query.setDelete();
|
||||
query.setupForDeleteOrUpdate(deleteRequest);
|
||||
|
||||
CQueryPredicates predicates = new CQueryPredicates(binder, request);
|
||||
CQueryPlan queryPlan = request.getQueryPlan();
|
||||
@@ -135,16 +130,16 @@ class CQueryBuilder {
|
||||
if (sqlTree.noJoins()) {
|
||||
if (dbPlatform.isSupportsDeleteTableAlias()) {
|
||||
// delete from table <alias> ...
|
||||
return aliasReplace(buildSql("delete", request, predicates, sqlTree).getSql(), alias);
|
||||
return aliasReplace(buildSqlDelete("delete", request, predicates, sqlTree).getSql(), alias);
|
||||
} else if (isMySql(dbPlatform.getPlatform())) {
|
||||
return aliasReplace(buildSql("delete " + alias, request, predicates, sqlTree).getSql(), alias);
|
||||
return aliasReplace(buildSqlDelete("delete " + alias, request, predicates, sqlTree).getSql(), alias);
|
||||
} else {
|
||||
// simple - delete from table ...
|
||||
return aliasStrip(buildSql("delete", request, predicates, sqlTree).getSql());
|
||||
return aliasStrip(buildSqlDelete("delete", request, predicates, sqlTree).getSql());
|
||||
}
|
||||
}
|
||||
// wrap as - delete from table where id in (select id ...)
|
||||
String sql = buildSql(null, request, predicates, sqlTree).getSql();
|
||||
String sql = buildSqlDelete(null, request, predicates, sqlTree).getSql();
|
||||
sql = request.getBeanDescriptor().getDeleteByIdInSql() + "in (" + sql + ")";
|
||||
sql = aliasReplace(sql, alias);
|
||||
return sql;
|
||||
@@ -168,7 +163,7 @@ class CQueryBuilder {
|
||||
sb.append(" set ").append(predicates.getDbUpdateClause());
|
||||
String updateClause = sb.toString();
|
||||
|
||||
if (sqlTree.noJoins()) {
|
||||
if (sqlTree.noJoins() && request.isInlineSqlUpdateLimit()) {
|
||||
// simple - update table set ... where ...
|
||||
return aliasStrip(buildSqlUpdate(updateClause, request, predicates, sqlTree).getSql());
|
||||
}
|
||||
@@ -547,137 +542,156 @@ class CQueryBuilder {
|
||||
* Return the SQL response with row limiting (when not an update statement).
|
||||
*/
|
||||
private SqlLimitResponse buildSql(String selectClause, OrmQueryRequest<?> request, CQueryPredicates predicates, SqlTree select) {
|
||||
return buildSql(selectClause, request, predicates, select, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the SQL response for update statement (stripping table alias for find by id expression).
|
||||
*/
|
||||
private SqlLimitResponse buildSqlUpdate(String selectClause, OrmQueryRequest<?> request, CQueryPredicates predicates, SqlTree select) {
|
||||
return buildSql(selectClause, request, predicates, select, true);
|
||||
}
|
||||
|
||||
private SqlLimitResponse buildSql(String selectClause, OrmQueryRequest<?> request, CQueryPredicates predicates, SqlTree select, boolean stripAlias) {
|
||||
|
||||
SpiQuery<?> query = request.getQuery();
|
||||
|
||||
if (query.isNativeSql()) {
|
||||
return new SqlLimitResponse(query.getGeneratedSql(), false);
|
||||
}
|
||||
if (query.isRawSql()) {
|
||||
return rawSqlHandler.buildSql(request, predicates, query.getRawSql().getSql());
|
||||
}
|
||||
return new BuildReq(selectClause, request, predicates, select).buildSql();
|
||||
}
|
||||
|
||||
boolean distinct = query.isDistinct() || select.isSqlDistinct();
|
||||
boolean useSqlLimiter = false;
|
||||
StringBuilder sb = new StringBuilder(500);
|
||||
String dbOrderBy = predicates.getDbOrderBy();
|
||||
private SqlLimitResponse buildSqlDelete(String selectClause, OrmQueryRequest<?> request, CQueryPredicates predicates, SqlTree select) {
|
||||
return new BuildReq(selectClause, request, predicates, select).buildSql();
|
||||
}
|
||||
|
||||
if (selectClause != null) {
|
||||
sb.append(selectClause);
|
||||
private SqlLimitResponse buildSqlUpdate(String selectClause, OrmQueryRequest<?> request, CQueryPredicates predicates, SqlTree select) {
|
||||
return new BuildReq(selectClause, request, predicates, select, true).buildSql();
|
||||
}
|
||||
|
||||
} else {
|
||||
useSqlLimiter = (query.hasMaxRowsOrFirstRow() && select.getManyProperty() == null);
|
||||
private class BuildReq {
|
||||
private final StringBuilder sb = new StringBuilder(500);
|
||||
private final String selectClause;
|
||||
private final OrmQueryRequest<?> request;
|
||||
private final SpiQuery<?> query;
|
||||
private final CQueryPredicates predicates;
|
||||
private final SqlTree select;
|
||||
private final boolean updateStatement;
|
||||
|
||||
if (!useSqlLimiter) {
|
||||
sb.append("select ");
|
||||
if (distinct) {
|
||||
if (request.isInlineCountDistinct()) {
|
||||
sb.append("count(");
|
||||
}
|
||||
sb.append("distinct ");
|
||||
String distinctOn = select.getDistinctOn();
|
||||
if (distinctOn != null) {
|
||||
sb.append("on (");
|
||||
sb.append(distinctOn).append(") ");
|
||||
private final boolean distinct;
|
||||
private final String dbOrderBy;
|
||||
private boolean useSqlLimiter;
|
||||
private boolean hasWhere;
|
||||
|
||||
private BuildReq(String selectClause, OrmQueryRequest<?> request, CQueryPredicates predicates, SqlTree select) {
|
||||
this(selectClause, request, predicates, select, false);
|
||||
}
|
||||
|
||||
private BuildReq(String selectClause, OrmQueryRequest<?> request, CQueryPredicates predicates, SqlTree select, boolean updateStatement) {
|
||||
this.selectClause = selectClause;
|
||||
this.request = request;
|
||||
this.query = request.getQuery();
|
||||
this.predicates = predicates;
|
||||
this.select = select;
|
||||
this.updateStatement = updateStatement;
|
||||
this.distinct = query.isDistinct() || select.isSqlDistinct();
|
||||
this.dbOrderBy = predicates.getDbOrderBy();
|
||||
}
|
||||
|
||||
private void appendSelect() {
|
||||
if (selectClause != null) {
|
||||
sb.append(selectClause);
|
||||
|
||||
} else {
|
||||
useSqlLimiter = (query.hasMaxRowsOrFirstRow() && select.getManyProperty() == null);
|
||||
if (!useSqlLimiter) {
|
||||
appendSelectDistinct();
|
||||
}
|
||||
if (query.isCountDistinct() && query.isSingleAttribute()) {
|
||||
sb.append("r1.attribute_, count(*) from (select ").append(select.getSelectSql()).append(" as attribute_");
|
||||
} else {
|
||||
sb.append(select.getSelectSql());
|
||||
}
|
||||
if (request.isInlineCountDistinct()) {
|
||||
sb.append(")");
|
||||
}
|
||||
if (distinct && dbOrderBy != null && !query.isSingleAttribute()) {
|
||||
// add the orderBy columns to the select clause (due to distinct)
|
||||
final OrderBy<?> orderBy = query.getOrderBy();
|
||||
if (orderBy != null && orderBy.supportsSelect()) {
|
||||
sb.append(", ").append(DbOrderByTrim.trim(dbOrderBy));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (query.isCountDistinct() && query.isSingleAttribute()) {
|
||||
sb.append("r1.attribute_, count(*) from (select ");
|
||||
sb.append(select.getSelectSql());
|
||||
sb.append(" as attribute_");
|
||||
} else {
|
||||
sb.append(select.getSelectSql());
|
||||
}
|
||||
if (request.isInlineCountDistinct()) {
|
||||
sb.append(")");
|
||||
}
|
||||
if (distinct && dbOrderBy != null && !query.isSingleAttribute()) {
|
||||
// add the orderBy columns to the select clause (due to distinct)
|
||||
final OrderBy<?> orderBy = query.getOrderBy();
|
||||
if (orderBy != null && orderBy.supportsSelect()) {
|
||||
sb.append(", ").append(DbOrderByTrim.trim(dbOrderBy));
|
||||
}
|
||||
|
||||
private void appendSelectDistinct() {
|
||||
sb.append("select ");
|
||||
if (distinct) {
|
||||
if (request.isInlineCountDistinct()) {
|
||||
sb.append("count(");
|
||||
}
|
||||
sb.append("distinct ");
|
||||
String distinctOn = select.getDistinctOn();
|
||||
if (distinctOn != null) {
|
||||
sb.append("on (").append(distinctOn).append(") ");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selectClause == null || !selectClause.startsWith("update")) {
|
||||
sb.append(" from ");
|
||||
sb.append(select.getFromSql());
|
||||
}
|
||||
|
||||
String inheritanceWhere = select.getInheritanceWhereSql();
|
||||
|
||||
boolean hasWhere = false;
|
||||
if (!inheritanceWhere.isEmpty()) {
|
||||
sb.append(" where");
|
||||
sb.append(inheritanceWhere);
|
||||
hasWhere = true;
|
||||
}
|
||||
|
||||
if (query.isAsOfBaseTable() && !historySupport.isStandardsBased()) {
|
||||
hasWhere = appendWhere(hasWhere, sb);
|
||||
sb.append(historySupport.getAsOfPredicate(request.getBaseTableAlias()));
|
||||
}
|
||||
|
||||
if (request.isFindById() || query.getId() != null) {
|
||||
appendWhere(hasWhere, sb);
|
||||
|
||||
BeanDescriptor<?> desc = request.getBeanDescriptor();
|
||||
String idSql = desc.getIdBinderIdSql(query.getAlias());
|
||||
if (idSql.isEmpty()) {
|
||||
throw new IllegalStateException("Executing FindById query on entity bean " + desc.getName()
|
||||
+ " that doesn't have an @Id property??");
|
||||
private void appendFrom() {
|
||||
if (selectClause == null || !selectClause.startsWith("update")) {
|
||||
sb.append(" from ");
|
||||
sb.append(select.getFromSql());
|
||||
}
|
||||
if (stripAlias) {
|
||||
// strip the table alias for use in update statement
|
||||
idSql = StringHelper.replaceString(idSql, "t0.", "");
|
||||
}
|
||||
sb.append(idSql).append(" ");
|
||||
hasWhere = true;
|
||||
}
|
||||
|
||||
String dbWhere = predicates.getDbWhere();
|
||||
if (hasValue(dbWhere)) {
|
||||
if (!hasWhere) {
|
||||
hasWhere = true;
|
||||
sb.append(" where ");
|
||||
} else {
|
||||
private void appendAndOrWhere() {
|
||||
if (hasWhere) {
|
||||
sb.append(" and ");
|
||||
}
|
||||
sb.append(dbWhere);
|
||||
}
|
||||
|
||||
String dbFilterMany = predicates.getDbFilterMany();
|
||||
if (hasValue(dbFilterMany)) {
|
||||
if (!hasWhere) {
|
||||
hasWhere = true;
|
||||
sb.append(" where ");
|
||||
} else {
|
||||
sb.append("and ");
|
||||
sb.append(" where ");
|
||||
hasWhere = true;
|
||||
}
|
||||
sb.append(dbFilterMany);
|
||||
}
|
||||
|
||||
if (!query.isIncludeSoftDeletes()) {
|
||||
private void appendInheritanceWhere() {
|
||||
String inheritanceWhere = select.getInheritanceWhereSql();
|
||||
if (!inheritanceWhere.isEmpty()) {
|
||||
sb.append(" where");
|
||||
sb.append(inheritanceWhere);
|
||||
hasWhere = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void appendHistoryAsOfPredicate() {
|
||||
if (query.isAsOfBaseTable() && !historySupport.isStandardsBased()) {
|
||||
appendAndOrWhere();
|
||||
sb.append(historySupport.getAsOfPredicate(request.getBaseTableAlias()));
|
||||
}
|
||||
}
|
||||
|
||||
private void appendFindId() {
|
||||
if (request.isFindById() || query.getId() != null) {
|
||||
appendAndOrWhere();
|
||||
|
||||
BeanDescriptor<?> desc = request.getBeanDescriptor();
|
||||
String idSql = desc.getIdBinderIdSql(query.getAlias());
|
||||
if (idSql.isEmpty()) {
|
||||
throw new IllegalStateException("Executing FindById query on entity bean " + desc.getName()
|
||||
+ " that doesn't have an @Id property??");
|
||||
}
|
||||
if (updateStatement) {
|
||||
// strip the table alias for use in update statement
|
||||
idSql = StringHelper.replaceString(idSql, "t0.", "");
|
||||
}
|
||||
sb.append(idSql).append(" ");
|
||||
hasWhere = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void appendToWhere(String predicate) {
|
||||
if (hasValue(predicate)) {
|
||||
appendAndOrWhere();
|
||||
sb.append(predicate);
|
||||
}
|
||||
}
|
||||
|
||||
private void appendSoftDelete() {
|
||||
List<String> softDeletePredicates = query.getSoftDeletePredicates();
|
||||
if (softDeletePredicates != null) {
|
||||
if (!hasWhere) {
|
||||
sb.append(" where ");
|
||||
} else {
|
||||
sb.append(" and ");
|
||||
}
|
||||
appendAndOrWhere();
|
||||
for (int i = 0; i < softDeletePredicates.size(); i++) {
|
||||
if (i > 0) {
|
||||
sb.append(" and ");
|
||||
@@ -687,69 +701,75 @@ class CQueryBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
String groupBy = select.getGroupBy();
|
||||
if (groupBy != null) {
|
||||
sb.append(" group by ").append(groupBy);
|
||||
private SqlLimitResponse buildSql() {
|
||||
appendSelect();
|
||||
appendFrom();
|
||||
appendInheritanceWhere();
|
||||
appendHistoryAsOfPredicate();
|
||||
appendFindId();
|
||||
appendToWhere(predicates.getDbWhere());
|
||||
appendToWhere(predicates.getDbFilterMany());
|
||||
if (!query.isIncludeSoftDeletes()) {
|
||||
appendSoftDelete();
|
||||
}
|
||||
|
||||
String groupBy = select.getGroupBy();
|
||||
if (groupBy != null) {
|
||||
sb.append(" group by ").append(groupBy);
|
||||
}
|
||||
|
||||
String dbHaving = predicates.getDbHaving();
|
||||
if (hasValue(dbHaving)) {
|
||||
sb.append(" having ").append(dbHaving);
|
||||
}
|
||||
|
||||
if (dbOrderBy != null && !query.isCountDistinct()) {
|
||||
sb.append(" order by ").append(dbOrderBy);
|
||||
}
|
||||
|
||||
if (query.isCountDistinct() && query.isSingleAttribute()) {
|
||||
sb.append(") r1 group by r1.attribute_");
|
||||
sb.append(toSql(query.getCountDistinctOrder()));
|
||||
}
|
||||
|
||||
if (useSqlLimiter) {
|
||||
// use LIMIT/OFFSET, ROW_NUMBER() or rownum type SQL query limitation
|
||||
SqlLimitRequest r = new OrmQueryLimitRequest(sb.toString(), dbOrderBy, query, dbPlatform, distinct);
|
||||
return sqlLimiter.limit(r);
|
||||
} else {
|
||||
if (updateStatement) {
|
||||
final int maxRows = query.getMaxRows();
|
||||
if (maxRows > 0) {
|
||||
// limit on update statement only support on platforms with supportsMaxRowsOnUpdate
|
||||
sb.append(" limit ").append(maxRows);
|
||||
}
|
||||
}
|
||||
return new SqlLimitResponse(dbPlatform.completeSql(sb.toString(), query), false);
|
||||
}
|
||||
}
|
||||
|
||||
String dbHaving = predicates.getDbHaving();
|
||||
if (hasValue(dbHaving)) {
|
||||
sb.append(" having ").append(dbHaving);
|
||||
private String toSql(CountDistinctOrder orderBy) {
|
||||
switch (orderBy) {
|
||||
case ATTR_ASC:
|
||||
return " order by r1.attribute_";
|
||||
case ATTR_DESC:
|
||||
return " order by r1.attribute_ desc";
|
||||
case COUNT_ASC_ATTR_ASC:
|
||||
return " order by count(*), r1.attribute_";
|
||||
case COUNT_ASC_ATTR_DESC:
|
||||
return " order by count(*), r1.attribute_ desc";
|
||||
case COUNT_DESC_ATTR_ASC:
|
||||
return " order by count(*) desc, r1.attribute_";
|
||||
case COUNT_DESC_ATTR_DESC:
|
||||
return " order by count(*) desc, r1.attribute_ desc";
|
||||
default:
|
||||
throw new IllegalArgumentException("Illegal enum: " + orderBy);
|
||||
}
|
||||
}
|
||||
|
||||
if (dbOrderBy != null && !query.isCountDistinct()) {
|
||||
sb.append(" order by ").append(dbOrderBy);
|
||||
private boolean hasValue(String s) {
|
||||
return s != null && !s.isEmpty();
|
||||
}
|
||||
|
||||
if (query.isCountDistinct() && query.isSingleAttribute()) {
|
||||
sb.append(") r1 group by r1.attribute_");
|
||||
sb.append(toSql(query.getCountDistinctOrder()));
|
||||
}
|
||||
|
||||
if (useSqlLimiter) {
|
||||
// use LIMIT/OFFSET, ROW_NUMBER() or rownum type SQL query limitation
|
||||
SqlLimitRequest r = new OrmQueryLimitRequest(sb.toString(), dbOrderBy, query, dbPlatform, distinct);
|
||||
return sqlLimiter.limit(r);
|
||||
|
||||
} else {
|
||||
return new SqlLimitResponse(dbPlatform.completeSql(sb.toString(), query), false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private String toSql(CountDistinctOrder orderBy) {
|
||||
switch (orderBy) {
|
||||
case ATTR_ASC:
|
||||
return " order by r1.attribute_";
|
||||
case ATTR_DESC:
|
||||
return " order by r1.attribute_ desc";
|
||||
case COUNT_ASC_ATTR_ASC:
|
||||
return " order by count(*), r1.attribute_";
|
||||
case COUNT_ASC_ATTR_DESC:
|
||||
return " order by count(*), r1.attribute_ desc";
|
||||
case COUNT_DESC_ATTR_ASC:
|
||||
return " order by count(*) desc, r1.attribute_";
|
||||
case COUNT_DESC_ATTR_DESC:
|
||||
return " order by count(*) desc, r1.attribute_ desc";
|
||||
default:
|
||||
throw new IllegalArgumentException("Illegal enum: " + orderBy);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Append where or and based on the hasWhere flag.
|
||||
*/
|
||||
private boolean appendWhere(boolean hasWhere, StringBuilder sb) {
|
||||
if (hasWhere) {
|
||||
sb.append(" and ");
|
||||
} else {
|
||||
sb.append(" where ");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean hasValue(String s) {
|
||||
return s != null && !s.isEmpty();
|
||||
}
|
||||
|
||||
boolean isPlatformDistinctOn() {
|
||||
@@ -759,7 +779,7 @@ class CQueryBuilder {
|
||||
/**
|
||||
* Return the 'for update' FROM hint (sql server).
|
||||
*/
|
||||
public String fromForUpdate(SpiQuery<?> query) {
|
||||
String fromForUpdate(SpiQuery<?> query) {
|
||||
Query.ForUpdate mode = query.getForUpdateMode();
|
||||
if (mode == null) {
|
||||
return null;
|
||||
|
||||
Reference in New Issue
Block a user