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
018
019import com.mybatisflex.core.exception.FlexExceptions;
020import com.mybatisflex.core.exception.locale.LocalizedFormats;
021import com.mybatisflex.core.util.StringUtil;
022import org.apache.ibatis.datasource.unpooled.UnpooledDataSource;
023
024import javax.sql.DataSource;
025import java.lang.reflect.Method;
026import java.sql.Connection;
027import java.util.regex.Pattern;
028
029/**
030 * DbType 解析 工具类
031 */
032public class DbTypeUtil {
033
034    private DbTypeUtil() {
035    }
036
037    /**
038     * 获取当前配置的 DbType
039     */
040    public static DbType getDbType(DataSource dataSource) {
041        String jdbcUrl = getJdbcUrl(dataSource);
042
043        if (StringUtil.isNotBlank(jdbcUrl)) {
044            return parseDbType(jdbcUrl);
045        }
046
047        throw new IllegalStateException("Can not get dataSource jdbcUrl: " + dataSource.getClass().getName());
048    }
049
050    /**
051     * 通过数据源中获取 jdbc 的 url 配置
052     * 符合 HikariCP, druid, c3p0, DBCP, beecp 数据源框架 以及 MyBatis UnpooledDataSource 的获取规则
053     * UnpooledDataSource 参考 @{@link UnpooledDataSource#getUrl()}
054     *
055     * @return jdbc url 配置
056     */
057    public static String getJdbcUrl(DataSource dataSource) {
058        String[] methodNames = new String[]{"getUrl", "getJdbcUrl"};
059        for (String methodName : methodNames) {
060            try {
061                Method method = dataSource.getClass().getMethod(methodName);
062                return (String) method.invoke(dataSource);
063            } catch (Exception e) {
064                //ignore
065            }
066        }
067
068        try (Connection connection = dataSource.getConnection()) {
069            return connection.getMetaData().getURL();
070        } catch (Exception e) {
071            throw FlexExceptions.wrap(e, LocalizedFormats.DATASOURCE_JDBC_URL);
072        }
073    }
074
075
076    /**
077     * 参考 druid  和 MyBatis-plus 的 JdbcUtils
078     * {@link com.alibaba.druid.util.JdbcUtils#getDbType(String, String)}
079     * {@link com.baomidou.mybatisplus.extension.toolkit.JdbcUtils#getDbType(String)}
080     *
081     * @param jdbcUrl jdbcURL
082     * @return 返回数据库类型
083     */
084    public static DbType parseDbType(String jdbcUrl) {
085        jdbcUrl = jdbcUrl.toLowerCase();
086        if (jdbcUrl.contains(":mysql:") || jdbcUrl.contains(":cobar:")) {
087            return DbType.MYSQL;
088        } else if (jdbcUrl.contains(":mariadb:")) {
089            return DbType.MARIADB;
090        } else if (jdbcUrl.contains(":oracle:")) {
091            return DbType.ORACLE;
092        } else if (jdbcUrl.contains(":sqlserver2012:")) {
093            return DbType.SQLSERVER;
094        } else if (jdbcUrl.contains(":sqlserver:") || jdbcUrl.contains(":microsoft:")) {
095            return DbType.SQLSERVER_2005;
096        } else if (jdbcUrl.contains(":postgresql:")) {
097            return DbType.POSTGRE_SQL;
098        } else if (jdbcUrl.contains(":hsqldb:")) {
099            return DbType.HSQL;
100        } else if (jdbcUrl.contains(":db2:")) {
101            return DbType.DB2;
102        } else if (jdbcUrl.contains(":sqlite:")) {
103            return DbType.SQLITE;
104        } else if (jdbcUrl.contains(":h2:")) {
105            return DbType.H2;
106        } else if (isMatchedRegex(":dm\\d*:", jdbcUrl)) {
107            return DbType.DM;
108        } else if (jdbcUrl.contains(":xugu:")) {
109            return DbType.XUGU;
110        } else if (isMatchedRegex(":kingbase\\d*:", jdbcUrl)) {
111            return DbType.KINGBASE_ES;
112        } else if (jdbcUrl.contains(":phoenix:")) {
113            return DbType.PHOENIX;
114        } else if (jdbcUrl.contains(":zenith:")) {
115            return DbType.GAUSS;
116        } else if (jdbcUrl.contains(":gbase:")) {
117            return DbType.GBASE;
118        } else if (jdbcUrl.contains(":gbasedbt-sqli:") || jdbcUrl.contains(":informix-sqli:")) {
119            return DbType.GBASE_8S;
120        } else if (jdbcUrl.contains(":ch:") || jdbcUrl.contains(":clickhouse:")) {
121            return DbType.CLICK_HOUSE;
122        } else if (jdbcUrl.contains(":oscar:")) {
123            return DbType.OSCAR;
124        } else if (jdbcUrl.contains(":sybase:")) {
125            return DbType.SYBASE;
126        } else if (jdbcUrl.contains(":oceanbase:")) {
127            return DbType.OCEAN_BASE;
128        } else if (jdbcUrl.contains(":highgo:")) {
129            return DbType.HIGH_GO;
130        } else if (jdbcUrl.contains(":cubrid:")) {
131            return DbType.CUBRID;
132        } else if (jdbcUrl.contains(":goldilocks:")) {
133            return DbType.GOLDILOCKS;
134        } else if (jdbcUrl.contains(":csiidb:")) {
135            return DbType.CSIIDB;
136        } else if (jdbcUrl.contains(":sap:")) {
137            return DbType.SAP_HANA;
138        } else if (jdbcUrl.contains(":impala:")) {
139            return DbType.IMPALA;
140        } else if (jdbcUrl.contains(":vertica:")) {
141            return DbType.VERTICA;
142        } else if (jdbcUrl.contains(":xcloud:")) {
143            return DbType.XCloud;
144        } else if (jdbcUrl.contains(":firebirdsql:")) {
145            return DbType.FIREBIRD;
146        } else if (jdbcUrl.contains(":redshift:")) {
147            return DbType.REDSHIFT;
148        } else if (jdbcUrl.contains(":opengauss:")) {
149            return DbType.OPENGAUSS;
150        } else if (jdbcUrl.contains(":taos:") || jdbcUrl.contains(":taos-rs:")) {
151            return DbType.TDENGINE;
152        } else if (jdbcUrl.contains(":informix")) {
153            return DbType.INFORMIX;
154        } else if (jdbcUrl.contains(":sinodb")) {
155            return DbType.SINODB;
156        } else if (jdbcUrl.contains(":uxdb:")) {
157            return DbType.UXDB;
158        } else if (jdbcUrl.contains(":greenplum:")) {
159            return DbType.GREENPLUM;
160        } else if (jdbcUrl.contains(":lealone:")) {
161            return DbType.LEALONE;
162        }  else if (jdbcUrl.contains(":hive2:")) {
163            return DbType.HIVE;
164        } else {
165            return DbType.OTHER;
166        }
167    }
168
169    /**
170     * 正则匹配,验证成功返回 true,验证失败返回 false
171     */
172    public static boolean isMatchedRegex(String regex, String jdbcUrl) {
173        if (null == jdbcUrl) {
174            return false;
175        }
176        return Pattern.compile(regex).matcher(jdbcUrl).find();
177    }
178
179}