001/*
002 *  Copyright (c) 2022-2025, 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 static com.mybatisflex.core.constant.SqlConsts.*;
019
020import com.mybatisflex.core.query.CPI;
021import com.mybatisflex.core.query.QueryOrderBy;
022import com.mybatisflex.core.query.QueryTable;
023import com.mybatisflex.core.query.QueryWrapper;
024import com.mybatisflex.core.util.CollectionUtil;
025import java.util.List;
026
027/**
028 * limit 和 offset 参数的处理器
029 */
030public interface LimitOffsetProcessor {
031
032    /**
033     * 处理构建 limit 和 offset
034     *
035     * @param dialect      数据方言
036     * @param sql          已经构建的 sql
037     * @param queryWrapper 参数内容
038     * @param limitRows    用户传入的 limit 参数 可能为 null
039     * @param limitOffset  用户传入的 offset 参数,可能为 null
040     */
041    StringBuilder process(IDialect dialect, StringBuilder sql, QueryWrapper queryWrapper, Long limitRows, Long limitOffset);
042
043
044    /**
045     * MySql 的处理器
046     * 适合 {@link DbType#MYSQL,DbType#MARIADB,DbType#H2,DbType#CLICK_HOUSE,DbType#XCloud}
047     */
048    LimitOffsetProcessor MYSQL = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
049        if (limitRows != null && limitOffset != null) {
050            sql.append(LIMIT).append(limitOffset).append(DELIMITER).append(limitRows);
051        } else if (limitRows != null) {
052            sql.append(LIMIT).append(limitRows);
053        }
054        return sql;
055    };
056    /**
057     * Postgresql 的处理器
058     * 适合  {@link DbType#POSTGRE_SQL,DbType#SQLITE,DbType#H2,DbType#HSQL,DbType#KINGBASE_ES,DbType#PHOENIX}
059     * 适合  {@link DbType#SAP_HANA,DbType#IMPALA,DbType#HIGH_GO,DbType#VERTICA,DbType#REDSHIFT}
060     * 适合  {@link DbType#OPENGAUSS,DbType#TDENGINE,DbType#UXDB}
061     */
062    LimitOffsetProcessor POSTGRESQL = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
063        if (limitRows != null && limitOffset != null) {
064            sql.append(LIMIT).append(limitRows).append(OFFSET).append(limitOffset);
065        } else if (limitRows != null) {
066            sql.append(LIMIT).append(limitRows);
067        }
068        return sql;
069    };
070    /**
071     * derby 的处理器
072     * 适合  {@link DbType#DERBY,DbType#ORACLE_12C,DbType#SQLSERVER ,DbType#POSTGRE_SQL}
073     */
074    LimitOffsetProcessor DERBY = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
075        if (limitRows != null && limitOffset != null) {
076            // OFFSET ** ROWS FETCH NEXT ** ROWS ONLY")
077            sql.append(OFFSET).append(limitOffset).append(ROWS_FETCH_NEXT).append(limitRows).append(ROWS_ONLY);
078        } else if (limitRows != null) {
079            sql.append(OFFSET).append(0).append(ROWS_FETCH_NEXT).append(limitRows).append(ROWS_ONLY);
080        }
081        return sql;
082    };
083    /**
084     * derby 的处理器
085     * 适合  {@link DbType#DERBY,DbType#ORACLE_12C,DbType#SQLSERVER ,DbType#POSTGRE_SQL}
086     */
087    LimitOffsetProcessor SQLSERVER = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
088        if (limitRows != null && limitOffset != null) {
089            // OFFSET ** ROWS FETCH NEXT ** ROWS ONLY")
090            sql.append(OFFSET).append(limitOffset).append(ROWS_FETCH_NEXT).append(limitRows).append(ROWS_ONLY);
091        } else if (limitRows != null) {
092            List<QueryOrderBy> orderBys = CPI.getOrderBys(queryWrapper);
093            if (CollectionUtil.isNotEmpty(orderBys)) {
094                sql.append(OFFSET).append(0).append(ROWS_FETCH_NEXT).append(limitRows).append(ROWS_ONLY);
095            } else {
096                sql.insert(6, TOP + limitRows);
097            }
098        }
099        return sql;
100    };
101    /**
102     * SqlServer 2005 limit 处理器
103     */
104    LimitOffsetProcessor SQLSERVER_2005 = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
105        if (limitRows != null) {
106            if (limitOffset == null) {
107                limitOffset = 0L;
108            }
109
110            // fix-bug:#I87AOA QueryWrapper 构建的SQL 与 执行的SQL不一致
111            List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
112            List<QueryTable> joinTables = CPI.getJoinTables(queryWrapper);
113            List<QueryTable> allTables = CollectionUtil.merge(queryTables, joinTables);
114            String originalSQL = sql.toString();
115            String orderByString;
116            List<QueryOrderBy> orderBys = CPI.getOrderBys(queryWrapper);
117            if (orderBys == null || orderBys.isEmpty()) {
118                orderByString = "ORDER BY CURRENT_TIMESTAMP";
119            } else {
120                StringBuilder orderBySql = new StringBuilder(ORDER_BY);
121                int index = 0;
122                for (QueryOrderBy orderBy : orderBys) {
123                    orderBySql.append(orderBy.toSql(allTables, dialect));
124                    if (index != orderBys.size() - 1) {
125                        orderBySql.append(DELIMITER);
126                    }
127                    index++;
128                }
129                originalSQL = originalSQL.substring(0, sql.lastIndexOf(ORDER_BY));
130                orderByString = orderBySql.toString();
131            }
132
133            StringBuilder newSql = new StringBuilder();
134            //fix SqlServer 多表关联查询,主表去重,执行SQL异常 https://gitee.com/mybatis-flex/mybatis-flex/issues/IABEJG
135            newSql.append("WITH temp_datas AS(SELECT __Tab.*, ROW_NUMBER() OVER ( ").append(orderByString)
136                .append(") as __rn ").append(" FROM ( ").append(originalSQL).append(" ) as __Tab ) ");
137            newSql.append(" SELECT * FROM temp_datas WHERE __rn BETWEEN ")
138                .append(limitOffset + 1).append(" AND ").append(limitOffset + limitRows);
139            newSql.append(" ORDER BY __rn");
140            return newSql;
141        }
142        return sql;
143    };
144    /**
145     * Informix 的处理器
146     * 适合  {@link DbType#INFORMIX}
147     * 文档 {@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>}
148     */
149    LimitOffsetProcessor INFORMIX = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
150        if (limitRows != null && limitOffset != null) {
151            // SELECT SKIP 2 FIRST 1 * FROM
152            sql.insert(6, SKIP + limitOffset + FIRST + limitRows);
153        } else if (limitRows != null) {
154            sql.insert(6, FIRST + limitRows);
155        }
156        return sql;
157    };
158    /**
159     *
160     * SINODB 的处理器
161     * 适合  {@link DbType#SINODB}
162     */
163    LimitOffsetProcessor SINODB = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
164        if (limitRows != null && limitOffset != null) {
165            // SELECT SKIP 2 FIRST 1 * FROM
166            sql.insert(6, SKIP + limitOffset + FIRST + limitRows);
167        } else if (limitRows != null) {
168            sql.insert(6, FIRST + limitRows);
169        }
170        return sql;
171    };
172    /**
173     * Firebird 的处理器
174     * 适合  {@link DbType#FIREBIRD}
175     */
176    LimitOffsetProcessor FIREBIRD = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
177        if (limitRows != null && limitOffset != null) {
178            // ROWS 2 TO 3
179            sql.append(ROWS).append(limitOffset).append(TO).append(limitOffset + limitRows);
180        } else if (limitRows != null) {
181            sql.insert(6, FIRST + limitRows);
182        }
183        return sql;
184    };
185    /**
186     * Oracle11g及以下数据库的处理器
187     * 适合  {@link DbType#ORACLE,DbType#DM,DbType#GAUSS}
188     */
189    LimitOffsetProcessor ORACLE = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
190        if (limitRows != null) {
191            if (limitOffset == null) {
192                limitOffset = 0L;
193            }
194            StringBuilder newSql = new StringBuilder("SELECT * FROM (SELECT TEMP_DATAS.*, ROWNUM RN FROM (");
195            newSql.append(sql);
196            newSql.append(") TEMP_DATAS WHERE ROWNUM <= ")
197                .append(limitOffset + limitRows)
198                .append(") WHERE RN > ")
199                .append(limitOffset);
200            return newSql;
201        }
202        return sql;
203    };
204    /**
205     * Sybase 处理器
206     * 适合  {@link DbType#SYBASE}
207     */
208    LimitOffsetProcessor SYBASE = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
209        if (limitRows != null && limitOffset != null) {
210            //SELECT TOP 1 START AT 3 * FROM
211            sql.insert(6, TOP + limitRows + START_AT + (limitOffset + 1));
212        } else if (limitRows != null) {
213            sql.insert(6, TOP + limitRows);
214        }
215        return sql;
216    };
217
218
219}