mirror of
https://github.com/alibaba/druid.git
synced 2023-03-04 19:17:16 +00:00
370 lines
16 KiB
Java
370 lines
16 KiB
Java
/*
|
|
* Copyright 1999-2011 Alibaba Group Holding 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
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package com.alibaba.druid.sql;
|
|
|
|
import java.util.List;
|
|
|
|
import com.alibaba.druid.sql.ast.SQLExpr;
|
|
import com.alibaba.druid.sql.ast.SQLOrderBy;
|
|
import com.alibaba.druid.sql.ast.SQLOver;
|
|
import com.alibaba.druid.sql.ast.SQLStatement;
|
|
import com.alibaba.druid.sql.ast.expr.SQLAggregateExpr;
|
|
import com.alibaba.druid.sql.ast.expr.SQLAllColumnExpr;
|
|
import com.alibaba.druid.sql.ast.expr.SQLBinaryOpExpr;
|
|
import com.alibaba.druid.sql.ast.expr.SQLBinaryOperator;
|
|
import com.alibaba.druid.sql.ast.expr.SQLIdentifierExpr;
|
|
import com.alibaba.druid.sql.ast.expr.SQLNumberExpr;
|
|
import com.alibaba.druid.sql.ast.expr.SQLPropertyExpr;
|
|
import com.alibaba.druid.sql.ast.statement.SQLSelect;
|
|
import com.alibaba.druid.sql.ast.statement.SQLSelectItem;
|
|
import com.alibaba.druid.sql.ast.statement.SQLSelectQuery;
|
|
import com.alibaba.druid.sql.ast.statement.SQLSelectQueryBlock;
|
|
import com.alibaba.druid.sql.ast.statement.SQLSelectStatement;
|
|
import com.alibaba.druid.sql.ast.statement.SQLSubqueryTableSource;
|
|
import com.alibaba.druid.sql.ast.statement.SQLUnionQuery;
|
|
import com.alibaba.druid.sql.dialect.db2.ast.stmt.DB2SelectQueryBlock;
|
|
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock;
|
|
import com.alibaba.druid.sql.dialect.mysql.ast.statement.MySqlSelectQueryBlock.Limit;
|
|
import com.alibaba.druid.sql.dialect.oracle.ast.expr.OracleAggregateExpr;
|
|
import com.alibaba.druid.sql.dialect.oracle.ast.stmt.OracleSelectQueryBlock;
|
|
import com.alibaba.druid.sql.dialect.postgresql.ast.stmt.PGSelectQueryBlock;
|
|
import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerSelectQueryBlock;
|
|
import com.alibaba.druid.sql.dialect.sqlserver.ast.SQLServerTop;
|
|
import com.alibaba.druid.util.JdbcConstants;
|
|
|
|
public class PagerUtils {
|
|
|
|
public static String count(String sql, String dbType) {
|
|
List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, dbType);
|
|
|
|
if (stmtList.size() != 1) {
|
|
throw new IllegalArgumentException("sql not support count : " + sql);
|
|
}
|
|
|
|
SQLStatement stmt = stmtList.get(0);
|
|
|
|
if (!(stmt instanceof SQLSelectStatement)) {
|
|
throw new IllegalArgumentException("sql not support count : " + sql);
|
|
}
|
|
|
|
SQLSelectStatement selectStmt = (SQLSelectStatement) stmt;
|
|
return count(selectStmt.getSelect(), dbType);
|
|
}
|
|
|
|
public static String limit(String sql, String dbType, int offset, int count) {
|
|
List<SQLStatement> stmtList = SQLUtils.parseStatements(sql, dbType);
|
|
|
|
if (stmtList.size() != 1) {
|
|
throw new IllegalArgumentException("sql not support count : " + sql);
|
|
}
|
|
|
|
SQLStatement stmt = stmtList.get(0);
|
|
|
|
if (!(stmt instanceof SQLSelectStatement)) {
|
|
throw new IllegalArgumentException("sql not support count : " + sql);
|
|
}
|
|
|
|
SQLSelectStatement selectStmt = (SQLSelectStatement) stmt;
|
|
|
|
return limit(selectStmt.getSelect(), dbType, offset, count);
|
|
}
|
|
|
|
public static String limit(SQLSelect select, String dbType, int offset, int count) {
|
|
SQLSelectQuery query = select.getQuery();
|
|
|
|
if (JdbcConstants.ORACLE.equals(dbType)) {
|
|
return limitOracle(select, dbType, offset, count);
|
|
}
|
|
|
|
if (JdbcConstants.DB2.equals(dbType)) {
|
|
return limitDB2(select, dbType, offset, count);
|
|
}
|
|
|
|
if (JdbcConstants.SQL_SERVER.equals(dbType)) {
|
|
return limitSQLServer(select, dbType, offset, count);
|
|
}
|
|
|
|
if (query instanceof SQLSelectQueryBlock) {
|
|
return limitQueryBlock(select, dbType, offset, count);
|
|
}
|
|
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
|
|
private static String limitQueryBlock(SQLSelect select, String dbType, int offset, int count) {
|
|
SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock) select.getQuery();
|
|
if (JdbcConstants.MYSQL.equals(dbType) || //
|
|
JdbcConstants.MARIADB.equals(dbType) || //
|
|
JdbcConstants.H2.equals(dbType)) {
|
|
return limitMySqlQueryBlock((MySqlSelectQueryBlock) queryBlock, dbType, offset, count);
|
|
}
|
|
|
|
throw new UnsupportedOperationException();
|
|
}
|
|
|
|
private static String limitDB2(SQLSelect select, String dbType, int offset, int count) {
|
|
SQLSelectQuery query = select.getQuery();
|
|
|
|
if (query instanceof SQLSelectQueryBlock) {
|
|
DB2SelectQueryBlock queryBlock = (DB2SelectQueryBlock) query;
|
|
if (offset <= 0) {
|
|
queryBlock.setFirst(new SQLNumberExpr(count));
|
|
return SQLUtils.toSQLString(select, dbType);
|
|
}
|
|
|
|
SQLAggregateExpr aggregateExpr = new SQLAggregateExpr("ROW_NUMBER");
|
|
SQLOrderBy orderBy = select.getOrderBy();
|
|
if (orderBy != null) {
|
|
aggregateExpr.setOver(new SQLOver(orderBy));
|
|
select.setOrderBy(null);
|
|
}
|
|
|
|
queryBlock.getSelectList().add(new SQLSelectItem(aggregateExpr, "ROWNUM"));
|
|
|
|
DB2SelectQueryBlock countQueryBlock = new DB2SelectQueryBlock();
|
|
countQueryBlock.getSelectList().add(new SQLSelectItem(new SQLAllColumnExpr()));
|
|
|
|
countQueryBlock.setFrom(new SQLSubqueryTableSource(select, "XX"));
|
|
|
|
SQLBinaryOpExpr gt = new SQLBinaryOpExpr(new SQLIdentifierExpr("ROWNUM"), //
|
|
SQLBinaryOperator.GreaterThan, //
|
|
new SQLNumberExpr(offset));
|
|
SQLBinaryOpExpr lteq = new SQLBinaryOpExpr(new SQLIdentifierExpr("ROWNUM"), //
|
|
SQLBinaryOperator.LessThanOrEqual, //
|
|
new SQLNumberExpr(count + offset));
|
|
SQLBinaryOpExpr pageCondition = new SQLBinaryOpExpr(gt, SQLBinaryOperator.BooleanAnd, lteq);
|
|
|
|
countQueryBlock.setWhere(pageCondition);
|
|
|
|
return SQLUtils.toSQLString(countQueryBlock, dbType);
|
|
}
|
|
|
|
return SQLUtils.toSQLString(select, dbType);
|
|
}
|
|
|
|
private static String limitSQLServer(SQLSelect select, String dbType, int offset, int count) {
|
|
SQLSelectQuery query = select.getQuery();
|
|
|
|
if (query instanceof SQLSelectQueryBlock) {
|
|
SQLServerSelectQueryBlock queryBlock = (SQLServerSelectQueryBlock) query;
|
|
if (offset <= 0) {
|
|
queryBlock.setTop(new SQLServerTop(new SQLNumberExpr(count)));
|
|
return SQLUtils.toSQLString(select, dbType);
|
|
}
|
|
|
|
SQLAggregateExpr aggregateExpr = new SQLAggregateExpr("ROW_NUMBER");
|
|
SQLOrderBy orderBy = select.getOrderBy();
|
|
if (orderBy != null) {
|
|
aggregateExpr.setOver(new SQLOver(orderBy));
|
|
select.setOrderBy(null);
|
|
}
|
|
|
|
queryBlock.getSelectList().add(new SQLSelectItem(aggregateExpr, "ROWNUM"));
|
|
|
|
DB2SelectQueryBlock countQueryBlock = new DB2SelectQueryBlock();
|
|
countQueryBlock.getSelectList().add(new SQLSelectItem(new SQLAllColumnExpr()));
|
|
|
|
countQueryBlock.setFrom(new SQLSubqueryTableSource(select, "XX"));
|
|
|
|
SQLBinaryOpExpr gt = new SQLBinaryOpExpr(new SQLIdentifierExpr("ROWNUM"), //
|
|
SQLBinaryOperator.GreaterThan, //
|
|
new SQLNumberExpr(offset));
|
|
SQLBinaryOpExpr lteq = new SQLBinaryOpExpr(new SQLIdentifierExpr("ROWNUM"), //
|
|
SQLBinaryOperator.LessThanOrEqual, //
|
|
new SQLNumberExpr(count + offset));
|
|
SQLBinaryOpExpr pageCondition = new SQLBinaryOpExpr(gt, SQLBinaryOperator.BooleanAnd, lteq);
|
|
|
|
countQueryBlock.setWhere(pageCondition);
|
|
|
|
return SQLUtils.toSQLString(countQueryBlock, dbType);
|
|
}
|
|
|
|
return SQLUtils.toSQLString(select, dbType);
|
|
}
|
|
|
|
private static String limitOracle(SQLSelect select, String dbType, int offset, int count) {
|
|
SQLSelectQuery query = select.getQuery();
|
|
|
|
if (query instanceof SQLSelectQueryBlock) {
|
|
OracleSelectQueryBlock queryBlock = (OracleSelectQueryBlock) query;
|
|
if (queryBlock.getGroupBy() == null && select.getOrderBy() == null && offset <= 0) {
|
|
SQLExpr condition = new SQLBinaryOpExpr(new SQLIdentifierExpr("ROWNUM"), //
|
|
SQLBinaryOperator.LessThanOrEqual, //
|
|
new SQLNumberExpr(count));
|
|
if (queryBlock.getWhere() == null) {
|
|
queryBlock.setWhere(condition);
|
|
} else {
|
|
queryBlock.setWhere(new SQLBinaryOpExpr(queryBlock.getWhere(), //
|
|
SQLBinaryOperator.BooleanAnd, //
|
|
condition));
|
|
}
|
|
|
|
return SQLUtils.toSQLString(select, dbType);
|
|
}
|
|
}
|
|
|
|
OracleSelectQueryBlock countQueryBlock = new OracleSelectQueryBlock();
|
|
countQueryBlock.getSelectList().add(new SQLSelectItem(new SQLPropertyExpr(new SQLIdentifierExpr("XX"), "*")));
|
|
countQueryBlock.getSelectList().add(new SQLSelectItem(new SQLIdentifierExpr("ROWNUM"), "RN"));
|
|
|
|
countQueryBlock.setFrom(new SQLSubqueryTableSource(select, "XX"));
|
|
countQueryBlock.setWhere(new SQLBinaryOpExpr(new SQLIdentifierExpr("ROWNUM"), //
|
|
SQLBinaryOperator.LessThanOrEqual, //
|
|
new SQLNumberExpr(count + offset)));
|
|
if (offset <= 0) {
|
|
return SQLUtils.toSQLString(countQueryBlock, dbType);
|
|
}
|
|
|
|
OracleSelectQueryBlock offsetQueryBlock = new OracleSelectQueryBlock();
|
|
offsetQueryBlock.getSelectList().add(new SQLSelectItem(new SQLAllColumnExpr()));
|
|
offsetQueryBlock.setFrom(new SQLSubqueryTableSource(new SQLSelect(countQueryBlock), "XXX"));
|
|
offsetQueryBlock.setWhere(new SQLBinaryOpExpr(new SQLIdentifierExpr("RN"), //
|
|
SQLBinaryOperator.GreaterThan, //
|
|
new SQLNumberExpr(offset)));
|
|
|
|
return SQLUtils.toSQLString(offsetQueryBlock, dbType);
|
|
}
|
|
|
|
private static String limitMySqlQueryBlock(MySqlSelectQueryBlock queryBlock, String dbType, int offset, int count) {
|
|
if (queryBlock.getLimit() != null) {
|
|
throw new IllegalArgumentException("limit already exists.");
|
|
}
|
|
|
|
Limit limit = new Limit();
|
|
if (offset > 0) {
|
|
limit.setOffset(new SQLNumberExpr(offset));
|
|
}
|
|
limit.setRowCount(new SQLNumberExpr(count));
|
|
queryBlock.setLimit(limit);
|
|
|
|
return SQLUtils.toSQLString(queryBlock, dbType);
|
|
}
|
|
|
|
private static String count(SQLSelect select, String dbType) {
|
|
if (select.getOrderBy() != null) {
|
|
select.setOrderBy(null);
|
|
}
|
|
|
|
SQLSelectQuery query = select.getQuery();
|
|
clearOrderBy(query);
|
|
|
|
if (query instanceof SQLSelectQueryBlock) {
|
|
SQLSelectItem countItem = createCountItem(dbType);
|
|
|
|
SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock) query;
|
|
|
|
if (queryBlock.getGroupBy() != null && queryBlock.getGroupBy().getItems().size() > 0) {
|
|
return createCountUseSubQuery(select, dbType);
|
|
}
|
|
|
|
queryBlock.getSelectList().clear();
|
|
queryBlock.getSelectList().add(countItem);
|
|
return SQLUtils.toSQLString(select, dbType);
|
|
} else if (query instanceof SQLUnionQuery) {
|
|
return createCountUseSubQuery(select, dbType);
|
|
}
|
|
|
|
throw new IllegalStateException();
|
|
}
|
|
|
|
private static String createCountUseSubQuery(SQLSelect select, String dbType) {
|
|
SQLSelectQueryBlock countSelectQuery = createQueryBlock(dbType);
|
|
|
|
SQLSelectItem countItem = createCountItem(dbType);
|
|
countSelectQuery.getSelectList().add(countItem);
|
|
|
|
countSelectQuery.setFrom(new SQLSubqueryTableSource(select));
|
|
|
|
SQLSelect countSelect = new SQLSelect(countSelectQuery);
|
|
SQLSelectStatement countStmt = new SQLSelectStatement(countSelect);
|
|
|
|
return SQLUtils.toSQLString(countStmt, dbType);
|
|
}
|
|
|
|
private static SQLSelectQueryBlock createQueryBlock(String dbType) {
|
|
if (JdbcConstants.MYSQL.equals(dbType)) {
|
|
return new MySqlSelectQueryBlock();
|
|
}
|
|
|
|
if (JdbcConstants.MARIADB.equals(dbType)) {
|
|
return new MySqlSelectQueryBlock();
|
|
}
|
|
|
|
if (JdbcConstants.H2.equals(dbType)) {
|
|
return new MySqlSelectQueryBlock();
|
|
}
|
|
|
|
if (JdbcConstants.ORACLE.equals(dbType)) {
|
|
return new OracleSelectQueryBlock();
|
|
}
|
|
|
|
if (JdbcConstants.POSTGRESQL.equals(dbType)) {
|
|
return new PGSelectQueryBlock();
|
|
}
|
|
|
|
if (JdbcConstants.SQL_SERVER.equals(dbType)) {
|
|
return new SQLServerSelectQueryBlock();
|
|
}
|
|
|
|
if (JdbcConstants.DB2.equals(dbType)) {
|
|
return new DB2SelectQueryBlock();
|
|
}
|
|
|
|
return new SQLSelectQueryBlock();
|
|
}
|
|
|
|
private static SQLSelectItem createCountItem(String dbType) {
|
|
SQLAggregateExpr countExpr;
|
|
|
|
if (JdbcConstants.ORACLE.equals(dbType)) {
|
|
countExpr = new OracleAggregateExpr("COUNT");
|
|
} else {
|
|
countExpr = new SQLAggregateExpr("COUNT");
|
|
}
|
|
countExpr.getArguments().add(new SQLAllColumnExpr());
|
|
|
|
SQLSelectItem countItem = new SQLSelectItem(countExpr);
|
|
return countItem;
|
|
}
|
|
|
|
private static void clearOrderBy(SQLSelectQuery query) {
|
|
if (query instanceof SQLSelectQueryBlock) {
|
|
SQLSelectQueryBlock queryBlock = (SQLSelectQueryBlock) query;
|
|
if (queryBlock instanceof MySqlSelectQueryBlock) {
|
|
MySqlSelectQueryBlock mysqlQueryBlock = (MySqlSelectQueryBlock) queryBlock;
|
|
if (mysqlQueryBlock.getOrderBy() != null) {
|
|
mysqlQueryBlock.setOrderBy(null);
|
|
}
|
|
} else if (queryBlock instanceof PGSelectQueryBlock) {
|
|
PGSelectQueryBlock pgQueryBlock = (PGSelectQueryBlock) queryBlock;
|
|
if (pgQueryBlock.getOrderBy() != null) {
|
|
pgQueryBlock.setOrderBy(null);
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (query instanceof SQLUnionQuery) {
|
|
SQLUnionQuery union = (SQLUnionQuery) query;
|
|
if (union.getOrderBy() != null) {
|
|
union.setOrderBy(null);
|
|
}
|
|
clearOrderBy(union.getLeft());
|
|
clearOrderBy(union.getRight());
|
|
}
|
|
}
|
|
}
|