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,DbType#DUCKDB}
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            //Fix https://gitee.com/mybatis-flex/mybatis-flex/issues/IBIJT3 [Bug]: SqlServer2005方言List查询SQL语句BUG
133            String columeSQL = originalSQL.substring(0, sql.indexOf(FROM));
134            String contitionSQL = originalSQL.substring(sql.indexOf(FROM));
135
136            StringBuilder newSql = new StringBuilder();
137            //fix SqlServer 多表关联查询,主表去重,执行SQL异常 https://gitee.com/mybatis-flex/mybatis-flex/issues/IABEJG
138            newSql.append("WITH temp_datas AS(").append(columeSQL).append(", ROW_NUMBER() OVER (")
139                .append(orderByString)
140                .append(") as __rn ");
141            newSql.append(contitionSQL);
142            newSql.append(")");
143            newSql.append(" SELECT * FROM temp_datas WHERE __rn BETWEEN ").append(limitOffset + 1).append(" AND ")
144                .append(limitOffset + limitRows);
145            newSql.append(" ORDER BY __rn");
146            return newSql;
147        }
148        return sql;
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     * SINODB 的处理器
167     * 适合  {@link DbType#SINODB}
168     */
169    LimitOffsetProcessor SINODB = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
170        if (limitRows != null && limitOffset != null) {
171            // SELECT SKIP 2 FIRST 1 * FROM
172            sql.insert(6, SKIP + limitOffset + FIRST + limitRows);
173        } else if (limitRows != null) {
174            sql.insert(6, FIRST + limitRows);
175        }
176        return sql;
177    };
178    /**
179     * Firebird 的处理器
180     * 适合  {@link DbType#FIREBIRD}
181     */
182    LimitOffsetProcessor FIREBIRD = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
183        if (limitRows != null && limitOffset != null) {
184            // ROWS 2 TO 3
185            sql.append(ROWS).append(limitOffset).append(TO).append(limitOffset + limitRows);
186        } else if (limitRows != null) {
187            sql.insert(6, FIRST + limitRows);
188        }
189        return sql;
190    };
191    /**
192     * Oracle11g及以下数据库的处理器
193     * 适合  {@link DbType#ORACLE,DbType#DM,DbType#GAUSS}
194     */
195    LimitOffsetProcessor ORACLE = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
196        if (limitRows != null) {
197            if (limitOffset == null) {
198                limitOffset = 0L;
199            }
200            StringBuilder newSql = new StringBuilder("SELECT * FROM (SELECT TEMP_DATAS.*, ROWNUM RN FROM (");
201            newSql.append(sql);
202            newSql.append(") TEMP_DATAS WHERE ROWNUM <= ").append(limitOffset + limitRows).append(") WHERE RN > ")
203                .append(limitOffset);
204            return newSql;
205        }
206        return sql;
207    };
208    /**
209     * Sybase 处理器
210     * 适合  {@link DbType#SYBASE}
211     */
212    LimitOffsetProcessor SYBASE = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
213        if (limitRows != null && limitOffset != null) {
214            //SELECT TOP 1 START AT 3 * FROM
215            sql.insert(6, TOP + limitRows + START_AT + (limitOffset + 1));
216        } else if (limitRows != null) {
217            sql.insert(6, TOP + limitRows);
218        }
219        return sql;
220    };
221
222
223}