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}