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, Integer limitRows, Integer 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 = 0;
114            }
115
116            List<QueryTable> queryTables = CPI.getQueryTables(queryWrapper);
117            String originalSQL = sql.toString();
118            String orderByString;
119            List<QueryOrderBy> orderBys = CPI.getOrderBys(queryWrapper);
120            if (orderBys == null || orderBys.isEmpty()) {
121                orderByString = "ORDER BY CURRENT_TIMESTAMP";
122            } else {
123                StringBuilder orderBySql = new StringBuilder(ORDER_BY);
124                int index = 0;
125                for (QueryOrderBy orderBy : orderBys) {
126                    orderBySql.append(orderBy.toSql(queryTables, dialect));
127                    if (index != orderBys.size() - 1) {
128                        orderBySql.append(DELIMITER);
129                    }
130                    index++;
131                }
132                originalSQL = originalSQL.substring(0, sql.lastIndexOf(ORDER_BY));
133                orderByString = orderBySql.toString();
134            }
135
136            StringBuilder newSql = new StringBuilder("WITH temp_datas AS(");
137            newSql.append("SELECT ROW_NUMBER() OVER (").append(orderByString).append(") as __rn,").append(originalSQL.substring(6));
138            newSql.append(")");
139            newSql.append(" SELECT * FROM temp_datas WHERE __rn BETWEEN ").append(limitOffset + 1).append(" AND ").append(limitOffset + limitRows);
140            newSql.append(" ORDER BY __rn");
141            return newSql;
142        }
143        return sql;
144    };
145
146
147    /**
148     * Informix 的处理器
149     * 适合  {@link DbType#INFORMIX}
150     * 文档 {@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>}
151     */
152    LimitOffsetProcessor INFORMIX = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
153        if (limitRows != null && limitOffset != null) {
154            // SELECT SKIP 2 FIRST 1 * FROM
155            sql.insert(6, SKIP + limitOffset + FIRST + limitRows);
156        } else if (limitRows != null) {
157            sql.insert(6, FIRST + limitRows);
158        }
159        return sql;
160    };
161
162
163    /**
164     *
165     * SINODB 的处理器
166     * 适合  {@link DbType#SINODB}
167     */
168    LimitOffsetProcessor SINODB = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
169        if (limitRows != null && limitOffset != null) {
170            // SELECT SKIP 2 FIRST 1 * FROM
171            sql.insert(6, SKIP + limitOffset + FIRST + limitRows);
172        } else if (limitRows != null) {
173            sql.insert(6, FIRST + limitRows);
174        }
175        return sql;
176    };
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    /**
193     * Oracle11g及以下数据库的处理器
194     * 适合  {@link DbType#ORACLE,DbType#DM,DbType#GAUSS}
195     */
196    LimitOffsetProcessor ORACLE = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
197        if (limitRows != null) {
198            if (limitOffset == null) {
199                limitOffset = 0;
200            }
201            StringBuilder newSql = new StringBuilder("SELECT * FROM (SELECT TEMP_DATAS.*, ROWNUM RN FROM (");
202            newSql.append(sql);
203            newSql.append(") TEMP_DATAS WHERE ROWNUM <= ").append(limitOffset + limitRows).append(") WHERE RN > ").append(limitOffset);
204            return newSql;
205        }
206        return sql;
207    };
208
209    /**
210     * Sybase 处理器
211     * 适合  {@link DbType#SYBASE}
212     */
213    LimitOffsetProcessor SYBASE = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
214        if (limitRows != null && limitOffset != null) {
215            //SELECT TOP 1 START AT 3 * FROM
216            sql.insert(6, TOP + limitRows + START_AT + (limitOffset + 1));
217        } else if (limitRows != null) {
218            sql.insert(6, TOP + limitRows);
219        }
220        return sql;
221    };
222
223
224}