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