001/*
002 *  Copyright (c) 2022-2023, Mybatis-Flex (fuhai999@gmail.com).
003 *  <p>
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *  <p>
008 *  http://www.apache.org/licenses/LICENSE-2.0
009 *  <p>
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package com.mybatisflex.core.dialect;
017
018import com.mybatisflex.core.query.CPI;
019import com.mybatisflex.core.query.QueryOrderBy;
020import com.mybatisflex.core.query.QueryTable;
021import com.mybatisflex.core.query.QueryWrapper;
022import com.mybatisflex.core.util.CollectionUtil;
023
024import java.util.List;
025
026import static com.mybatisflex.core.constant.SqlConsts.*;
027
028/**
029 * limit 和 offset 参数的处理器
030 */
031public interface LimitOffsetProcessor {
032
033    /**
034     * 处理构建 limit 和 offset
035     *
036     * @param dialect      数据方言
037     * @param sql          已经构建的 sql
038     * @param queryWrapper 参数内容
039     * @param limitRows    用户传入的 limit 参数 可能为 null
040     * @param limitOffset  用户传入的 offset 参数,可能为 null
041     */
042    StringBuilder process(IDialect dialect, StringBuilder sql, QueryWrapper queryWrapper, Long limitRows, Long limitOffset);
043
044
045    /**
046     * MySql 的处理器
047     * 适合 {@link DbType#MYSQL,DbType#MARIADB,DbType#H2,DbType#CLICK_HOUSE,DbType#XCloud}
048     */
049    LimitOffsetProcessor MYSQL = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
050        if (limitRows != null && limitOffset != null) {
051            sql.append(LIMIT).append(limitOffset).append(DELIMITER).append(limitRows);
052        } else if (limitRows != null) {
053            sql.append(LIMIT).append(limitRows);
054        }
055        return sql;
056    };
057
058    /**
059     * Postgresql 的处理器
060     * 适合  {@link DbType#POSTGRE_SQL,DbType#SQLITE,DbType#H2,DbType#HSQL,DbType#KINGBASE_ES,DbType#PHOENIX}
061     * 适合  {@link DbType#SAP_HANA,DbType#IMPALA,DbType#HIGH_GO,DbType#VERTICA,DbType#REDSHIFT}
062     * 适合  {@link DbType#OPENGAUSS,DbType#TDENGINE,DbType#UXDB}
063     */
064    LimitOffsetProcessor POSTGRESQL = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
065        if (limitRows != null && limitOffset != null) {
066            sql.append(LIMIT).append(limitRows).append(OFFSET).append(limitOffset);
067        } else if (limitRows != null) {
068            sql.append(LIMIT).append(limitRows);
069        }
070        return sql;
071    };
072
073    /**
074     * derby 的处理器
075     * 适合  {@link DbType#DERBY,DbType#ORACLE_12C,DbType#SQLSERVER ,DbType#POSTGRE_SQL}
076     */
077    LimitOffsetProcessor DERBY = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
078        if (limitRows != null && limitOffset != null) {
079            // OFFSET ** ROWS FETCH NEXT ** ROWS ONLY")
080            sql.append(OFFSET).append(limitOffset).append(ROWS_FETCH_NEXT).append(limitRows).append(ROWS_ONLY);
081        } else if (limitRows != null) {
082            sql.append(OFFSET).append(0).append(ROWS_FETCH_NEXT).append(limitRows).append(ROWS_ONLY);
083        }
084        return sql;
085    };
086
087    /**
088     * derby 的处理器
089     * 适合  {@link DbType#DERBY,DbType#ORACLE_12C,DbType#SQLSERVER ,DbType#POSTGRE_SQL}
090     */
091    LimitOffsetProcessor SQLSERVER = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
092        if (limitRows != null && limitOffset != null) {
093            // OFFSET ** ROWS FETCH NEXT ** ROWS ONLY")
094            sql.append(OFFSET).append(limitOffset).append(ROWS_FETCH_NEXT).append(limitRows).append(ROWS_ONLY);
095        } else if (limitRows != null) {
096            List<QueryOrderBy> orderBys = CPI.getOrderBys(queryWrapper);
097            if (CollectionUtil.isNotEmpty(orderBys)) {
098                sql.append(OFFSET).append(0).append(ROWS_FETCH_NEXT).append(limitRows).append(ROWS_ONLY);
099            } else {
100                sql.insert(6, TOP + limitRows);
101            }
102        }
103        return sql;
104    };
105
106
107    /**
108     * SqlServer 2005 limit 处理器
109     */
110    LimitOffsetProcessor SQLSERVER_2005 = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
111        if (limitRows != null) {
112            if (limitOffset == null) {
113                limitOffset = 0L;
114            }
115
116            // fix-bug:#I87AOA QueryWrapper 构建的SQL 与 执行的SQL不一致
117            List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
118            List<QueryTable> joinTables = CPI.getJoinTables(queryWrapper);
119            List<QueryTable> allTables = CollectionUtil.merge(queryTables, joinTables);
120            String originalSQL = sql.toString();
121            String orderByString;
122            List<QueryOrderBy> orderBys = CPI.getOrderBys(queryWrapper);
123            if (orderBys == null || orderBys.isEmpty()) {
124                orderByString = "ORDER BY CURRENT_TIMESTAMP";
125            } else {
126                StringBuilder orderBySql = new StringBuilder(ORDER_BY);
127                int index = 0;
128                for (QueryOrderBy orderBy : orderBys) {
129                    orderBySql.append(orderBy.toSql(allTables, dialect));
130                    if (index != orderBys.size() - 1) {
131                        orderBySql.append(DELIMITER);
132                    }
133                    index++;
134                }
135                originalSQL = originalSQL.substring(0, sql.lastIndexOf(ORDER_BY));
136                orderByString = orderBySql.toString();
137            }
138
139            StringBuilder newSql = new StringBuilder("WITH temp_datas AS(");
140            newSql.append("SELECT ROW_NUMBER() OVER (").append(orderByString).append(") as __rn,").append(originalSQL.substring(6));
141            newSql.append(")");
142            newSql.append(" SELECT * FROM temp_datas WHERE __rn BETWEEN ").append(limitOffset + 1).append(" AND ").append(limitOffset + limitRows);
143            newSql.append(" ORDER BY __rn");
144            return newSql;
145        }
146        return sql;
147    };
148
149
150    /**
151     * Informix 的处理器
152     * 适合  {@link DbType#INFORMIX}
153     * 文档 {@link <a href="https://www.ibm.com/docs/en/informix-servers/14.10?topic=clause-restricting-return-values-skip-limit-first-options">https://www.ibm.com/docs/en/informix-servers/14.10?topic=clause-restricting-return-values-skip-limit-first-options</a>}
154     */
155    LimitOffsetProcessor INFORMIX = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
156        if (limitRows != null && limitOffset != null) {
157            // SELECT SKIP 2 FIRST 1 * FROM
158            sql.insert(6, SKIP + limitOffset + FIRST + limitRows);
159        } else if (limitRows != null) {
160            sql.insert(6, FIRST + limitRows);
161        }
162        return sql;
163    };
164
165
166    /**
167     *
168     * SINODB 的处理器
169     * 适合  {@link DbType#SINODB}
170     */
171    LimitOffsetProcessor SINODB = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
172        if (limitRows != null && limitOffset != null) {
173            // SELECT SKIP 2 FIRST 1 * FROM
174            sql.insert(6, SKIP + limitOffset + FIRST + limitRows);
175        } else if (limitRows != null) {
176            sql.insert(6, FIRST + limitRows);
177        }
178        return sql;
179    };
180
181    /**
182     * Firebird 的处理器
183     * 适合  {@link DbType#FIREBIRD}
184     */
185    LimitOffsetProcessor FIREBIRD = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
186        if (limitRows != null && limitOffset != null) {
187            // ROWS 2 TO 3
188            sql.append(ROWS).append(limitOffset).append(TO).append(limitOffset + limitRows);
189        } else if (limitRows != null) {
190            sql.insert(6, FIRST + limitRows);
191        }
192        return sql;
193    };
194
195    /**
196     * Oracle11g及以下数据库的处理器
197     * 适合  {@link DbType#ORACLE,DbType#DM,DbType#GAUSS}
198     */
199    LimitOffsetProcessor ORACLE = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
200        if (limitRows != null) {
201            if (limitOffset == null) {
202                limitOffset = 0L;
203            }
204            StringBuilder newSql = new StringBuilder("SELECT * FROM (SELECT TEMP_DATAS.*, ROWNUM RN FROM (");
205            newSql.append(sql);
206            newSql.append(") TEMP_DATAS WHERE ROWNUM <= ").append(limitOffset + limitRows).append(") WHERE RN > ").append(limitOffset);
207            return newSql;
208        }
209        return sql;
210    };
211
212    /**
213     * Sybase 处理器
214     * 适合  {@link DbType#SYBASE}
215     */
216    LimitOffsetProcessor SYBASE = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
217        if (limitRows != null && limitOffset != null) {
218            //SELECT TOP 1 START AT 3 * FROM
219            sql.insert(6, TOP + limitRows + START_AT + (limitOffset + 1));
220        } else if (limitRows != null) {
221            sql.insert(6, TOP + limitRows);
222        }
223        return sql;
224    };
225
226
227}