001/*
002 * Copyright 2012-2018 the original author or authors.
003 *
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 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
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 */
016
017package org.springframework.boot.jdbc;
018
019import java.util.Arrays;
020import java.util.Collection;
021import java.util.Collections;
022import java.util.Locale;
023
024import org.springframework.util.Assert;
025import org.springframework.util.StringUtils;
026
027/**
028 * Enumeration of common database drivers.
029 *
030 * @author Phillip Webb
031 * @author Maciej Walkowiak
032 * @author Marten Deinum
033 * @author Stephane Nicoll
034 * @since 1.4.0
035 */
036public enum DatabaseDriver {
037
038        /**
039         * Unknown type.
040         */
041        UNKNOWN(null, null),
042
043        /**
044         * Apache Derby.
045         */
046        DERBY("Apache Derby", "org.apache.derby.jdbc.EmbeddedDriver",
047                        "org.apache.derby.jdbc.EmbeddedXADataSource",
048                        "SELECT 1 FROM SYSIBM.SYSDUMMY1"),
049
050        /**
051         * H2.
052         */
053        H2("H2", "org.h2.Driver", "org.h2.jdbcx.JdbcDataSource", "SELECT 1"),
054
055        /**
056         * HyperSQL DataBase.
057         */
058        HSQLDB("HSQL Database Engine", "org.hsqldb.jdbc.JDBCDriver",
059                        "org.hsqldb.jdbc.pool.JDBCXADataSource",
060                        "SELECT COUNT(*) FROM INFORMATION_SCHEMA.SYSTEM_USERS"),
061
062        /**
063         * SQL Lite.
064         */
065        SQLITE("SQLite", "org.sqlite.JDBC"),
066
067        /**
068         * MySQL.
069         */
070        MYSQL("MySQL", "com.mysql.cj.jdbc.Driver", "com.mysql.cj.jdbc.MysqlXADataSource",
071                        "/* ping */ SELECT 1"),
072
073        /**
074         * Maria DB.
075         */
076        MARIADB("MySQL", "org.mariadb.jdbc.Driver", "org.mariadb.jdbc.MariaDbDataSource",
077                        "SELECT 1") {
078
079                @Override
080                public String getId() {
081                        return "mysql";
082                }
083        },
084
085        /**
086         * Google App Engine.
087         */
088        GAE(null, "com.google.appengine.api.rdbms.AppEngineDriver"),
089
090        /**
091         * Oracle.
092         */
093        ORACLE("Oracle", "oracle.jdbc.OracleDriver",
094                        "oracle.jdbc.xa.client.OracleXADataSource", "SELECT 'Hello' from DUAL"),
095
096        /**
097         * Postgres.
098         */
099        POSTGRESQL("PostgreSQL", "org.postgresql.Driver", "org.postgresql.xa.PGXADataSource",
100                        "SELECT 1"),
101
102        /**
103         * HANA - SAP HANA Database - HDB.
104         * @since 2.1.0
105         */
106        HANA("HDB", "com.sap.db.jdbc.Driver", "com.sap.db.jdbcext.XADataSourceSAP",
107                        "SELECT 1 FROM SYS.DUMMY") {
108                @Override
109                protected Collection<String> getUrlPrefixes() {
110                        return Collections.singleton("sap");
111                }
112        },
113
114        /**
115         * jTDS. As it can be used for several databases, there isn't a single product name we
116         * could rely on.
117         */
118        JTDS(null, "net.sourceforge.jtds.jdbc.Driver"),
119
120        /**
121         * SQL Server.
122         */
123        SQLSERVER("Microsoft SQL Server", "com.microsoft.sqlserver.jdbc.SQLServerDriver",
124                        "com.microsoft.sqlserver.jdbc.SQLServerXADataSource", "SELECT 1") {
125
126                @Override
127                protected boolean matchProductName(String productName) {
128                        return super.matchProductName(productName)
129                                        || "SQL SERVER".equalsIgnoreCase(productName);
130
131                }
132
133        },
134
135        /**
136         * Firebird.
137         */
138        FIREBIRD("Firebird", "org.firebirdsql.jdbc.FBDriver",
139                        "org.firebirdsql.ds.FBXADataSource", "SELECT 1 FROM RDB$DATABASE") {
140
141                @Override
142                protected Collection<String> getUrlPrefixes() {
143                        return Collections.singleton("firebirdsql");
144                }
145
146                @Override
147                protected boolean matchProductName(String productName) {
148                        return super.matchProductName(productName)
149                                        || productName.toLowerCase(Locale.ENGLISH).startsWith("firebird");
150                }
151        },
152
153        /**
154         * DB2 Server.
155         */
156        DB2("DB2", "com.ibm.db2.jcc.DB2Driver", "com.ibm.db2.jcc.DB2XADataSource",
157                        "SELECT 1 FROM SYSIBM.SYSDUMMY1") {
158
159                @Override
160                protected boolean matchProductName(String productName) {
161                        return super.matchProductName(productName)
162                                        || productName.toLowerCase(Locale.ENGLISH).startsWith("db2/");
163                }
164        },
165
166        /**
167         * DB2 AS400 Server.
168         */
169        DB2_AS400("DB2 UDB for AS/400", "com.ibm.as400.access.AS400JDBCDriver",
170                        "com.ibm.as400.access.AS400JDBCXADataSource",
171                        "SELECT 1 FROM SYSIBM.SYSDUMMY1") {
172
173                @Override
174                public String getId() {
175                        return "db2";
176                }
177
178                @Override
179                protected Collection<String> getUrlPrefixes() {
180                        return Collections.singleton("as400");
181                }
182
183                @Override
184                protected boolean matchProductName(String productName) {
185                        return super.matchProductName(productName)
186                                        || productName.toLowerCase(Locale.ENGLISH).contains("as/400");
187                }
188        },
189
190        /**
191         * Teradata.
192         */
193        TERADATA("Teradata", "com.teradata.jdbc.TeraDriver"),
194
195        /**
196         * Informix.
197         */
198        INFORMIX("Informix Dynamic Server", "com.informix.jdbc.IfxDriver", null,
199                        "select count(*) from systables") {
200
201                @Override
202                protected Collection<String> getUrlPrefixes() {
203                        return Arrays.asList("informix-sqli", "informix-direct");
204                }
205
206        };
207
208        private final String productName;
209
210        private final String driverClassName;
211
212        private final String xaDataSourceClassName;
213
214        private final String validationQuery;
215
216        DatabaseDriver(String productName, String driverClassName) {
217                this(productName, driverClassName, null);
218        }
219
220        DatabaseDriver(String productName, String driverClassName,
221                        String xaDataSourceClassName) {
222                this(productName, driverClassName, xaDataSourceClassName, null);
223        }
224
225        DatabaseDriver(String productName, String driverClassName,
226                        String xaDataSourceClassName, String validationQuery) {
227                this.productName = productName;
228                this.driverClassName = driverClassName;
229                this.xaDataSourceClassName = xaDataSourceClassName;
230                this.validationQuery = validationQuery;
231        }
232
233        /**
234         * Return the identifier of this driver.
235         * @return the identifier
236         */
237        public String getId() {
238                return name().toLowerCase(Locale.ENGLISH);
239        }
240
241        protected boolean matchProductName(String productName) {
242                return this.productName != null && this.productName.equalsIgnoreCase(productName);
243        }
244
245        protected Collection<String> getUrlPrefixes() {
246                return Collections.singleton(this.name().toLowerCase(Locale.ENGLISH));
247        }
248
249        /**
250         * Return the driver class name.
251         * @return the class name or {@code null}
252         */
253        public String getDriverClassName() {
254                return this.driverClassName;
255        }
256
257        /**
258         * Return the XA driver source class name.
259         * @return the class name or {@code null}
260         */
261        public String getXaDataSourceClassName() {
262                return this.xaDataSourceClassName;
263        }
264
265        /**
266         * Return the validation query.
267         * @return the validation query or {@code null}
268         */
269        public String getValidationQuery() {
270                return this.validationQuery;
271        }
272
273        /**
274         * Find a {@link DatabaseDriver} for the given URL.
275         * @param url the JDBC URL
276         * @return the database driver or {@link #UNKNOWN} if not found
277         */
278        public static DatabaseDriver fromJdbcUrl(String url) {
279                if (StringUtils.hasLength(url)) {
280                        Assert.isTrue(url.startsWith("jdbc"), "URL must start with 'jdbc'");
281                        String urlWithoutPrefix = url.substring("jdbc".length())
282                                        .toLowerCase(Locale.ENGLISH);
283                        for (DatabaseDriver driver : values()) {
284                                for (String urlPrefix : driver.getUrlPrefixes()) {
285                                        String prefix = ":" + urlPrefix + ":";
286                                        if (driver != UNKNOWN && urlWithoutPrefix.startsWith(prefix)) {
287                                                return driver;
288                                        }
289                                }
290                        }
291                }
292                return UNKNOWN;
293        }
294
295        /**
296         * Find a {@link DatabaseDriver} for the given product name.
297         * @param productName product name
298         * @return the database driver or {@link #UNKNOWN} if not found
299         */
300        public static DatabaseDriver fromProductName(String productName) {
301                if (StringUtils.hasLength(productName)) {
302                        for (DatabaseDriver candidate : values()) {
303                                if (candidate.matchProductName(productName)) {
304                                        return candidate;
305                                }
306                        }
307                }
308                return UNKNOWN;
309        }
310
311}