org.h2.util.JdbcUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of h2-mvstore Show documentation
Show all versions of h2-mvstore Show documentation
Fork of h2database to maintain Java 8 compatibility
The newest version!
/*
* Copyright 2004-2023 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (https://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Properties;
import javax.naming.Context;
import javax.sql.DataSource;
import org.h2.api.ErrorCode;
import org.h2.api.JavaObjectSerializer;
import org.h2.engine.Constants;
import org.h2.engine.SysProperties;
import org.h2.jdbc.JdbcConnection;
import org.h2.jdbc.JdbcPreparedStatement;
import org.h2.message.DbException;
import org.h2.tools.SimpleResultSet;
import org.h2.util.Utils.ClassFactory;
import org.h2.value.Value;
import org.h2.value.ValueLob;
import org.h2.value.ValueToObjectConverter;
import org.h2.value.ValueUuid;
/**
* This is a utility class with JDBC helper functions.
*/
public class JdbcUtils {
/**
* The serializer to use.
*/
public static JavaObjectSerializer serializer;
private static final String[] DRIVERS = {
"h2:", "org.h2.Driver",
"Cache:", "com.intersys.jdbc.CacheDriver",
"daffodilDB://", "in.co.daffodil.db.rmi.RmiDaffodilDBDriver",
"daffodil", "in.co.daffodil.db.jdbc.DaffodilDBDriver",
"db2:", "com.ibm.db2.jcc.DB2Driver",
"derby:net:", "org.apache.derby.client.ClientAutoloadedDriver",
"derby://", "org.apache.derby.client.ClientAutoloadedDriver",
"derby:", "org.apache.derby.iapi.jdbc.AutoloadedDriver",
"FrontBase:", "com.frontbase.jdbc.FBJDriver",
"firebirdsql:", "org.firebirdsql.jdbc.FBDriver",
"hsqldb:", "org.hsqldb.jdbcDriver",
"informix-sqli:", "com.informix.jdbc.IfxDriver",
"jtds:", "net.sourceforge.jtds.jdbc.Driver",
"microsoft:", "com.microsoft.jdbc.sqlserver.SQLServerDriver",
"mimer:", "com.mimer.jdbc.Driver",
"mysql:", "com.mysql.cj.jdbc.Driver",
"mariadb:", "org.mariadb.jdbc.Driver",
"odbc:", "sun.jdbc.odbc.JdbcOdbcDriver",
"oracle:", "oracle.jdbc.driver.OracleDriver",
"pervasive:", "com.pervasive.jdbc.v2.Driver",
"pointbase:micro:", "com.pointbase.me.jdbc.jdbcDriver",
"pointbase:", "com.pointbase.jdbc.jdbcUniversalDriver",
"postgresql:", "org.postgresql.Driver",
"sybase:", "com.sybase.jdbc3.jdbc.SybDriver",
"sqlserver:", "com.microsoft.sqlserver.jdbc.SQLServerDriver",
"teradata:", "com.ncr.teradata.TeraDriver",
};
private static final byte[] UUID_PREFIX =
"\254\355\0\5sr\0\16java.util.UUID\274\231\3\367\230m\205/\2\0\2J\0\14leastSigBitsJ\0\13mostSigBitsxp"
.getBytes(StandardCharsets.ISO_8859_1);
private static boolean allowAllClasses;
private static HashSet allowedClassNames;
/**
* In order to manage more than one class loader
*/
private static final ArrayList userClassFactories = new ArrayList<>();
private static String[] allowedClassNamePrefixes;
private JdbcUtils() {
// utility class
}
/**
* Add a class factory in order to manage more than one class loader.
*
* @param classFactory An object that implements ClassFactory
*/
public static void addClassFactory(ClassFactory classFactory) {
userClassFactories.add(classFactory);
}
/**
* Remove a class factory
*
* @param classFactory Already inserted class factory instance
*/
public static void removeClassFactory(ClassFactory classFactory) {
userClassFactories.remove(classFactory);
}
static {
String clazz = SysProperties.JAVA_OBJECT_SERIALIZER;
if (clazz != null) {
try {
serializer = (JavaObjectSerializer) loadUserClass(clazz).getDeclaredConstructor().newInstance();
} catch (Exception e) {
throw DbException.convert(e);
}
}
}
/**
* Load a class, but check if it is allowed to load this class first. To
* perform access rights checking, the system property h2.allowedClasses
* needs to be set to a list of class file name prefixes.
*
* @param generic return type
* @param className the name of the class
* @return the class object
*/
@SuppressWarnings("unchecked")
public static Class loadUserClass(String className) {
if (allowedClassNames == null) {
// initialize the static fields
String s = SysProperties.ALLOWED_CLASSES;
ArrayList prefixes = new ArrayList<>();
boolean allowAll = false;
HashSet classNames = new HashSet<>();
for (String p : StringUtils.arraySplit(s, ',', true)) {
if (p.equals("*")) {
allowAll = true;
} else if (p.endsWith("*")) {
prefixes.add(p.substring(0, p.length() - 1));
} else {
classNames.add(p);
}
}
allowedClassNamePrefixes = prefixes.toArray(new String[0]);
allowAllClasses = allowAll;
allowedClassNames = classNames;
}
if (!allowAllClasses && !allowedClassNames.contains(className)) {
boolean allowed = false;
for (String s : allowedClassNamePrefixes) {
if (className.startsWith(s)) {
allowed = true;
break;
}
}
if (!allowed) {
throw DbException.get(
ErrorCode.ACCESS_DENIED_TO_CLASS_1, className);
}
}
// Use provided class factory first.
for (ClassFactory classFactory : userClassFactories) {
if (classFactory.match(className)) {
try {
Class> userClass = classFactory.loadClass(className);
if (userClass != null) {
return (Class) userClass;
}
} catch (Exception e) {
throw DbException.get(
ErrorCode.CLASS_NOT_FOUND_1, e, className);
}
}
}
// Use local ClassLoader
try {
return (Class) Class.forName(className);
} catch (ClassNotFoundException e) {
try {
return (Class) Class.forName(
className, true,
Thread.currentThread().getContextClassLoader());
} catch (Exception e2) {
throw DbException.get(
ErrorCode.CLASS_NOT_FOUND_1, e, className);
}
} catch (NoClassDefFoundError e) {
throw DbException.get(
ErrorCode.CLASS_NOT_FOUND_1, e, className);
} catch (Error e) {
// UnsupportedClassVersionError
throw DbException.get(
ErrorCode.GENERAL_ERROR_1, e, className);
}
}
/**
* Close a statement without throwing an exception.
*
* @param stat the statement or null
*/
public static void closeSilently(Statement stat) {
if (stat != null) {
try {
stat.close();
} catch (SQLException e) {
// ignore
}
}
}
/**
* Close a connection without throwing an exception.
*
* @param conn the connection or null
*/
public static void closeSilently(Connection conn) {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// ignore
}
}
}
/**
* Close a result set without throwing an exception.
*
* @param rs the result set or null
*/
public static void closeSilently(ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
// ignore
}
}
}
/**
* Open a new database connection with the given settings.
*
* @param driver the driver class name
* @param url the database URL
* @param user the user name
* @param password the password
* @return the database connection
* @throws SQLException on failure
*/
public static Connection getConnection(String driver, String url,
String user, String password) throws SQLException {
return getConnection(driver, url, user, password, null, false);
}
/**
* Open a new database connection with the given settings.
*
* @param driver the driver class name
* @param url the database URL
* @param user the user name or {@code null}
* @param password the password or {@code null}
* @param networkConnectionInfo the network connection information, or {@code null}
* @param forbidCreation whether database creation is forbidden
* @return the database connection
* @throws SQLException on failure
*/
public static Connection getConnection(String driver, String url, String user, String password,
NetworkConnectionInfo networkConnectionInfo, boolean forbidCreation) throws SQLException {
if (url.startsWith(Constants.START_URL)) {
JdbcConnection connection = new JdbcConnection(url, null, user, password, forbidCreation);
if (networkConnectionInfo != null) {
connection.getSession().setNetworkConnectionInfo(networkConnectionInfo);
}
return connection;
}
if (StringUtils.isNullOrEmpty(driver)) {
JdbcUtils.load(url);
} else {
Class> d = loadUserClass(driver);
try {
if (java.sql.Driver.class.isAssignableFrom(d)) {
Driver driverInstance = (Driver) d.getDeclaredConstructor().newInstance();
Properties prop = new Properties();
if (user != null) {
prop.setProperty("user", user);
}
if (password != null) {
prop.setProperty("password", password);
}
/*
* fix issue #695 with drivers with the same jdbc
* subprotocol in classpath of jdbc drivers (as example
* redshift and postgresql drivers)
*/
Connection connection = driverInstance.connect(url, prop);
if (connection != null) {
return connection;
}
throw new SQLException("Driver " + driver + " is not suitable for " + url, "08001");
} else if (javax.naming.Context.class.isAssignableFrom(d)) {
if (!url.startsWith("java:")) {
throw new SQLException("Only java scheme is supported for JNDI lookups", "08001");
}
// JNDI context
Context context = (Context) d.getDeclaredConstructor().newInstance();
DataSource ds = (DataSource) context.lookup(url);
if (StringUtils.isNullOrEmpty(user) && StringUtils.isNullOrEmpty(password)) {
return ds.getConnection();
}
return ds.getConnection(user, password);
}
} catch (Exception e) {
throw DbException.toSQLException(e);
}
// don't know, but maybe it loaded a JDBC Driver
}
return DriverManager.getConnection(url, user, password);
}
/**
* Get the driver class name for the given URL, or null if the URL is
* unknown.
*
* @param url the database URL
* @return the driver class name
*/
public static String getDriver(String url) {
if (url.startsWith("jdbc:")) {
url = url.substring("jdbc:".length());
for (int i = 0; i < DRIVERS.length; i += 2) {
String prefix = DRIVERS[i];
if (url.startsWith(prefix)) {
return DRIVERS[i + 1];
}
}
}
return null;
}
/**
* Load the driver class for the given URL, if the database URL is known.
*
* @param url the database URL
*/
public static void load(String url) {
String driver = getDriver(url);
if (driver != null) {
loadUserClass(driver);
}
}
/**
* Serialize the object to a byte array, using the serializer specified by
* the connection info if set, or the default serializer.
*
* @param obj the object to serialize
* @param javaObjectSerializer the object serializer (may be null)
* @return the byte array
*/
public static byte[] serialize(Object obj, JavaObjectSerializer javaObjectSerializer) {
try {
if (javaObjectSerializer != null) {
return javaObjectSerializer.serialize(obj);
}
if (serializer != null) {
return serializer.serialize(obj);
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
ObjectOutputStream os = new ObjectOutputStream(out);
os.writeObject(obj);
return out.toByteArray();
} catch (Throwable e) {
throw DbException.get(ErrorCode.SERIALIZATION_FAILED_1, e, e.toString());
}
}
/**
* De-serialize the byte array to an object, eventually using the serializer
* specified by the connection info.
*
* @param data the byte array
* @param javaObjectSerializer the object serializer (may be null)
* @return the object
* @throws DbException if serialization fails
*/
public static Object deserialize(byte[] data, JavaObjectSerializer javaObjectSerializer) {
try {
if (javaObjectSerializer != null) {
return javaObjectSerializer.deserialize(data);
}
if (serializer != null) {
return serializer.deserialize(data);
}
ByteArrayInputStream in = new ByteArrayInputStream(data);
ObjectInputStream is;
if (SysProperties.USE_THREAD_CONTEXT_CLASS_LOADER) {
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
is = new ObjectInputStream(in) {
@Override
protected Class> resolveClass(ObjectStreamClass desc)
throws IOException, ClassNotFoundException {
try {
return Class.forName(desc.getName(), true, loader);
} catch (ClassNotFoundException e) {
return super.resolveClass(desc);
}
}
};
} else {
is = new ObjectInputStream(in);
}
return is.readObject();
} catch (Throwable e) {
throw DbException.get(ErrorCode.DESERIALIZATION_FAILED_1, e, e.toString());
}
}
/**
* De-serialize the byte array to a UUID object. This method is called on
* the server side where regular de-serialization of user-supplied Java
* objects may create a security hole if object was maliciously crafted.
* Unlike {@link #deserialize(byte[], JavaObjectSerializer)}, this method
* does not try to de-serialize instances of other classes.
*
* @param data the byte array
* @return the UUID object
* @throws DbException if serialization fails
*/
public static ValueUuid deserializeUuid(byte[] data) {
uuid: if (data.length == 80) {
for (int i = 0; i < 64; i++) {
if (data[i] != UUID_PREFIX[i]) {
break uuid;
}
}
return ValueUuid.get(Bits.readLong(data, 72), Bits.readLong(data, 64));
}
throw DbException.get(ErrorCode.DESERIALIZATION_FAILED_1, "Is not a UUID");
}
/**
* Set a value as a parameter in a prepared statement.
*
* @param prep the prepared statement
* @param parameterIndex the parameter index
* @param value the value
* @param conn the own connection
* @throws SQLException on failure
*/
public static void set(PreparedStatement prep, int parameterIndex, Value value, JdbcConnection conn)
throws SQLException {
if (prep instanceof JdbcPreparedStatement) {
if (value instanceof ValueLob) {
setLob(prep, parameterIndex, (ValueLob) value);
} else {
prep.setObject(parameterIndex, value);
}
} else {
setOther(prep, parameterIndex, value, conn);
}
}
private static void setOther(PreparedStatement prep, int parameterIndex, Value value, JdbcConnection conn)
throws SQLException {
int valueType = value.getValueType();
switch (valueType) {
case Value.NULL:
prep.setNull(parameterIndex, Types.NULL);
break;
case Value.BOOLEAN:
prep.setBoolean(parameterIndex, value.getBoolean());
break;
case Value.TINYINT:
prep.setByte(parameterIndex, value.getByte());
break;
case Value.SMALLINT:
prep.setShort(parameterIndex, value.getShort());
break;
case Value.INTEGER:
prep.setInt(parameterIndex, value.getInt());
break;
case Value.BIGINT:
prep.setLong(parameterIndex, value.getLong());
break;
case Value.NUMERIC:
case Value.DECFLOAT:
prep.setBigDecimal(parameterIndex, value.getBigDecimal());
break;
case Value.DOUBLE:
prep.setDouble(parameterIndex, value.getDouble());
break;
case Value.REAL:
prep.setFloat(parameterIndex, value.getFloat());
break;
case Value.TIME:
try {
prep.setObject(parameterIndex, JSR310Utils.valueToLocalTime(value, null), Types.TIME);
} catch (SQLException ignore) {
prep.setTime(parameterIndex, LegacyDateTimeUtils.toTime(null, null, value));
}
break;
case Value.DATE:
try {
prep.setObject(parameterIndex, JSR310Utils.valueToLocalDate(value, null), Types.DATE);
} catch (SQLException ignore) {
prep.setDate(parameterIndex, LegacyDateTimeUtils.toDate(null, null, value));
}
break;
case Value.TIMESTAMP:
try {
prep.setObject(parameterIndex, JSR310Utils.valueToLocalDateTime(value, null), Types.TIMESTAMP);
} catch (SQLException ignore) {
prep.setTimestamp(parameterIndex, LegacyDateTimeUtils.toTimestamp(null, null, value));
}
break;
case Value.VARBINARY:
case Value.BINARY:
case Value.GEOMETRY:
case Value.JSON:
prep.setBytes(parameterIndex, value.getBytesNoCopy());
break;
case Value.VARCHAR:
case Value.VARCHAR_IGNORECASE:
case Value.ENUM:
case Value.INTERVAL_YEAR:
case Value.INTERVAL_MONTH:
case Value.INTERVAL_DAY:
case Value.INTERVAL_HOUR:
case Value.INTERVAL_MINUTE:
case Value.INTERVAL_SECOND:
case Value.INTERVAL_YEAR_TO_MONTH:
case Value.INTERVAL_DAY_TO_HOUR:
case Value.INTERVAL_DAY_TO_MINUTE:
case Value.INTERVAL_DAY_TO_SECOND:
case Value.INTERVAL_HOUR_TO_MINUTE:
case Value.INTERVAL_HOUR_TO_SECOND:
case Value.INTERVAL_MINUTE_TO_SECOND:
prep.setString(parameterIndex, value.getString());
break;
case Value.BLOB:
case Value.CLOB:
setLob(prep, parameterIndex, (ValueLob) value);
break;
case Value.ARRAY:
prep.setArray(parameterIndex, prep.getConnection().createArrayOf("NULL",
(Object[]) ValueToObjectConverter.valueToDefaultObject(value, conn, true)));
break;
case Value.JAVA_OBJECT:
prep.setObject(parameterIndex,
JdbcUtils.deserialize(value.getBytesNoCopy(), conn.getJavaObjectSerializer()),
Types.JAVA_OBJECT);
break;
case Value.UUID:
prep.setBytes(parameterIndex, value.getBytes());
break;
case Value.CHAR:
try {
prep.setObject(parameterIndex, value.getString(), Types.CHAR);
} catch (SQLException ignore) {
prep.setString(parameterIndex, value.getString());
}
break;
case Value.TIMESTAMP_TZ:
try {
prep.setObject(parameterIndex, JSR310Utils.valueToOffsetDateTime(value, null),
Types.TIMESTAMP_WITH_TIMEZONE);
return;
} catch (SQLException ignore) {
prep.setString(parameterIndex, value.getString());
}
break;
case Value.TIME_TZ:
try {
prep.setObject(parameterIndex, JSR310Utils.valueToOffsetTime(value, null), Types.TIME_WITH_TIMEZONE);
return;
} catch (SQLException ignore) {
prep.setString(parameterIndex, value.getString());
}
break;
default:
throw DbException.getUnsupportedException(Value.getTypeName(valueType));
}
}
private static void setLob(PreparedStatement prep, int parameterIndex, ValueLob value) throws SQLException {
if (value.getValueType() == Value.BLOB) {
long p = value.octetLength();
prep.setBinaryStream(parameterIndex, value.getInputStream(), p > Integer.MAX_VALUE ? -1 : (int) p);
} else {
long p = value.charLength();
prep.setCharacterStream(parameterIndex, value.getReader(), p > Integer.MAX_VALUE ? -1 : (int) p);
}
}
/**
* Get metadata from the database.
*
* @param conn the connection
* @param sql the SQL statement
* @return the metadata
* @throws SQLException on failure
*/
public static ResultSet getMetaResultSet(Connection conn, String sql)
throws SQLException {
DatabaseMetaData meta = conn.getMetaData();
if (isBuiltIn(sql, "@best_row_identifier")) {
String[] p = split(sql);
int scale = p[4] == null ? 0 : Integer.parseInt(p[4]);
boolean nullable = Boolean.parseBoolean(p[5]);
return meta.getBestRowIdentifier(p[1], p[2], p[3], scale, nullable);
} else if (isBuiltIn(sql, "@catalogs")) {
return meta.getCatalogs();
} else if (isBuiltIn(sql, "@columns")) {
String[] p = split(sql);
return meta.getColumns(p[1], p[2], p[3], p[4]);
} else if (isBuiltIn(sql, "@column_privileges")) {
String[] p = split(sql);
return meta.getColumnPrivileges(p[1], p[2], p[3], p[4]);
} else if (isBuiltIn(sql, "@cross_references")) {
String[] p = split(sql);
return meta.getCrossReference(p[1], p[2], p[3], p[4], p[5], p[6]);
} else if (isBuiltIn(sql, "@exported_keys")) {
String[] p = split(sql);
return meta.getExportedKeys(p[1], p[2], p[3]);
} else if (isBuiltIn(sql, "@imported_keys")) {
String[] p = split(sql);
return meta.getImportedKeys(p[1], p[2], p[3]);
} else if (isBuiltIn(sql, "@index_info")) {
String[] p = split(sql);
boolean unique = Boolean.parseBoolean(p[4]);
boolean approx = Boolean.parseBoolean(p[5]);
return meta.getIndexInfo(p[1], p[2], p[3], unique, approx);
} else if (isBuiltIn(sql, "@primary_keys")) {
String[] p = split(sql);
return meta.getPrimaryKeys(p[1], p[2], p[3]);
} else if (isBuiltIn(sql, "@procedures")) {
String[] p = split(sql);
return meta.getProcedures(p[1], p[2], p[3]);
} else if (isBuiltIn(sql, "@procedure_columns")) {
String[] p = split(sql);
return meta.getProcedureColumns(p[1], p[2], p[3], p[4]);
} else if (isBuiltIn(sql, "@schemas")) {
return meta.getSchemas();
} else if (isBuiltIn(sql, "@tables")) {
String[] p = split(sql);
String[] types = p[4] == null ? null : StringUtils.arraySplit(p[4], ',', false);
return meta.getTables(p[1], p[2], p[3], types);
} else if (isBuiltIn(sql, "@table_privileges")) {
String[] p = split(sql);
return meta.getTablePrivileges(p[1], p[2], p[3]);
} else if (isBuiltIn(sql, "@table_types")) {
return meta.getTableTypes();
} else if (isBuiltIn(sql, "@type_info")) {
return meta.getTypeInfo();
} else if (isBuiltIn(sql, "@udts")) {
String[] p = split(sql);
int[] types;
if (p[4] == null) {
types = null;
} else {
String[] t = StringUtils.arraySplit(p[4], ',', false);
types = new int[t.length];
for (int i = 0; i < t.length; i++) {
types[i] = Integer.parseInt(t[i]);
}
}
return meta.getUDTs(p[1], p[2], p[3], types);
} else if (isBuiltIn(sql, "@version_columns")) {
String[] p = split(sql);
return meta.getVersionColumns(p[1], p[2], p[3]);
} else if (isBuiltIn(sql, "@memory")) {
SimpleResultSet rs = new SimpleResultSet();
rs.addColumn("Type", Types.VARCHAR, 0, 0);
rs.addColumn("KB", Types.VARCHAR, 0, 0);
rs.addRow("Used Memory", Long.toString(Utils.getMemoryUsed()));
rs.addRow("Free Memory", Long.toString(Utils.getMemoryFree()));
return rs;
} else if (isBuiltIn(sql, "@info")) {
SimpleResultSet rs = new SimpleResultSet();
rs.addColumn("KEY", Types.VARCHAR, 0, 0);
rs.addColumn("VALUE", Types.VARCHAR, 0, 0);
rs.addRow("conn.getCatalog", conn.getCatalog());
rs.addRow("conn.getAutoCommit", Boolean.toString(conn.getAutoCommit()));
rs.addRow("conn.getTransactionIsolation", Integer.toString(conn.getTransactionIsolation()));
rs.addRow("conn.getWarnings", String.valueOf(conn.getWarnings()));
String map;
try {
map = String.valueOf(conn.getTypeMap());
} catch (SQLException e) {
map = e.toString();
}
rs.addRow("conn.getTypeMap", map);
rs.addRow("conn.isReadOnly", Boolean.toString(conn.isReadOnly()));
rs.addRow("conn.getHoldability", Integer.toString(conn.getHoldability()));
addDatabaseMetaData(rs, meta);
return rs;
} else if (isBuiltIn(sql, "@attributes")) {
String[] p = split(sql);
return meta.getAttributes(p[1], p[2], p[3], p[4]);
} else if (isBuiltIn(sql, "@super_tables")) {
String[] p = split(sql);
return meta.getSuperTables(p[1], p[2], p[3]);
} else if (isBuiltIn(sql, "@super_types")) {
String[] p = split(sql);
return meta.getSuperTypes(p[1], p[2], p[3]);
} else if (isBuiltIn(sql, "@pseudo_columns")) {
String[] p = split(sql);
return meta.getPseudoColumns(p[1], p[2], p[3], p[4]);
}
return null;
}
private static void addDatabaseMetaData(SimpleResultSet rs,
DatabaseMetaData meta) {
Method[] methods = DatabaseMetaData.class.getDeclaredMethods();
Arrays.sort(methods, Comparator.comparing(Method::toString));
for (Method m : methods) {
if (m.getParameterTypes().length == 0) {
try {
Object o = m.invoke(meta);
rs.addRow("meta." + m.getName(), String.valueOf(o));
} catch (InvocationTargetException e) {
rs.addRow("meta." + m.getName(), e.getTargetException().toString());
} catch (Exception e) {
rs.addRow("meta." + m.getName(), e.toString());
}
}
}
}
/**
* Check is the SQL string starts with a prefix (case insensitive).
*
* @param sql the SQL statement
* @param builtIn the prefix
* @return true if yes
*/
public static boolean isBuiltIn(String sql, String builtIn) {
return sql.regionMatches(true, 0, builtIn, 0, builtIn.length());
}
/**
* Split the string using the space separator into at least 10 entries.
*
* @param s the string
* @return the array
*/
public static String[] split(String s) {
String[] t = StringUtils.arraySplit(s, ' ', true);
String[] list = new String[Math.max(10, t.length)];
System.arraycopy(t, 0, list, 0, t.length);
for (int i = 0; i < list.length; i++) {
if ("null".equals(list[i])) {
list[i] = null;
}
}
return list;
}
}