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.impl;
017
018import com.mybatisflex.core.dialect.KeywordWrap;
019import com.mybatisflex.core.dialect.LimitOffsetProcessor;
020import com.mybatisflex.core.query.CPI;
021import com.mybatisflex.core.query.QueryOrderBy;
022import com.mybatisflex.core.query.QueryTable;
023import com.mybatisflex.core.query.QueryWrapper;
024
025import java.util.List;
026import java.util.regex.Matcher;
027import java.util.regex.Pattern;
028
029import static com.mybatisflex.core.constant.FuncName.SUBSTRING;
030import static com.mybatisflex.core.constant.SqlConsts.*;
031
032public class DB2105Dialect extends CommonsDialectImpl {
033      //TODO: 根据DatabaseMetaData获取数据库厂商名和版本号
034    public static final String DB2_1005_PRODUCT_VERSION = "1005";
035    public static final String DB2_PRODUCT_NAME = "DB2";
036    private static final Pattern ORDERBY_PATTERN = Pattern.compile("(\\S+)\\s+(\\S*)\\s*("+NULLS_FIRST.trim()+"|"+NULLS_LAST.trim()+")");
037    private static final Pattern SUBSTRING_PATTERN = Pattern.compile("((?i)"+SUBSTRING.trim()+")(\\s*)(\\(.*?\\))");
038
039    public DB2105Dialect(KeywordWrap keywordWrap, LimitOffsetProcessor limitOffsetProcessor) {
040        super(keywordWrap, limitOffsetProcessor);
041    }
042
043    @Override
044    public String buildSelectSql(QueryWrapper queryWrapper){
045        String sql = super.buildSelectSql(queryWrapper);
046        if(sql!=null){
047            Matcher matcher = SUBSTRING_PATTERN.matcher(sql);
048            sql = matcher.replaceAll("SUBSTR$2$3");
049        }
050        return sql;
051    }
052
053    @Override
054    protected void buildOrderBySql(StringBuilder sqlBuilder, QueryWrapper queryWrapper, List<QueryTable> queryTables) {
055        List<QueryOrderBy> orderBys = CPI.getOrderBys(queryWrapper);
056        if (orderBys != null && !orderBys.isEmpty()) {
057            sqlBuilder.append(ORDER_BY);
058            int index = 0;
059            for (QueryOrderBy orderBy : orderBys) {
060                String orderBySql = orderBy.toSql(queryTables, this);
061                orderBySql = convertOrderBySqlForDB2105(orderBySql);  // 转换SQL语句
062                sqlBuilder.append(orderBySql);
063                if (index != orderBys.size() - 1) {
064                    sqlBuilder.append(DELIMITER);
065                }
066                index++;
067            }
068        }
069    }
070
071    private  String convertOrderBySqlForDB2105(String sql) {
072        Matcher matcher = ORDERBY_PATTERN.matcher(sql);
073        if (matcher.find()) {
074            String column = matcher.group(1);
075            String orderType = matcher.group(2);
076            String nullOrder = matcher.group(3);
077            if (NULLS_FIRST.trim().equals(nullOrder)) {
078                sql = "CASE WHEN " + column + " IS NULL THEN 0 ELSE 1 END, " + column+" "+orderType;
079            } else if (NULLS_LAST.trim().equals(nullOrder)) {
080                sql = "CASE WHEN " + column + " IS NULL THEN 1 ELSE 0 END, " + column+" "+orderType;
081            }
082        }
083        return sql;
084    }
085
086
087    public interface DB2105LimitOffsetProcessor {
088        LimitOffsetProcessor DB2105 = (dialect, sql, queryWrapper, limitRows, limitOffset) -> {
089            StringBuilder limitSqlFragment = new StringBuilder(
090                    "select * from ( select u_.*,rownumber() over()  as rn from ( ");
091            limitSqlFragment.append(sql);
092            limitSqlFragment.append(" )u_  ) temp_ where temp_.rn between ");
093
094            if (limitRows != null && limitOffset != null) {
095                limitSqlFragment.append(limitOffset + 1);
096                limitSqlFragment.append(" and ");
097                limitSqlFragment.append(limitRows + limitOffset);
098            } else if (limitRows != null) {
099                limitSqlFragment.append("1 and ");
100                limitSqlFragment.append(limitRows);
101            } else {
102                return sql;
103            }
104            return limitSqlFragment;
105        };
106    }
107}