package com.revolsys.jdbc; import java.math.BigDecimal; import java.sql.Array; import java.sql.Connection; import java.sql.DatabaseMetaData; import java.sql.Date; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import java.sql.Struct; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.sql.DataSource; import org.apache.log4j.Logger; import org.springframework.dao.DataAccessException; import org.springframework.jdbc.datasource.DataSourceUtils; import org.springframework.jdbc.support.SQLExceptionTranslator; import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator; import com.revolsys.collection.map.LinkedHashMapEx; import com.revolsys.collection.map.MapEx; import com.revolsys.io.PathName; import com.revolsys.io.PathUtil; import com.revolsys.jdbc.exception.JdbcExceptionTranslator; import com.revolsys.jdbc.field.JdbcFieldDefinition; import com.revolsys.jdbc.io.JdbcRecordStore; import com.revolsys.record.query.Condition; import com.revolsys.record.query.Query; import com.revolsys.record.schema.FieldDefinition; import com.revolsys.record.schema.RecordDefinition; import com.revolsys.record.schema.RecordDefinitionImpl; import com.revolsys.record.schema.RecordStore; import com.revolsys.transaction.Propagation; import com.revolsys.transaction.Transaction; import com.revolsys.util.Property; public final class JdbcUtils { private static final Logger LOG = Logger.getLogger(JdbcUtils.class); public static void addColumnNames(final StringBuilder sql, final RecordDefinition recordDefinition, final String tablePrefix) { for (int i = 0; i < recordDefinition.getFieldCount(); i++) { if (i > 0) { sql.append(", "); } final FieldDefinition fieldDefinition = recordDefinition.getField(i); addFieldName(sql, tablePrefix, fieldDefinition); } } public static void addColumnNames(final StringBuilder sql, final RecordDefinition recordDefinition, final String tablePrefix, final List<String> fieldNames, boolean hasColumns) { for (final String fieldName : fieldNames) { if (hasColumns) { sql.append(", "); } final FieldDefinition attribute = recordDefinition.getField(fieldName); if (attribute == null) { sql.append(fieldName); } else { addFieldName(sql, tablePrefix, attribute); } hasColumns = true; } } public static void addFieldName(final StringBuilder sql, final String tablePrefix, final FieldDefinition fieldDefinition) { if (fieldDefinition instanceof JdbcFieldDefinition) { final JdbcFieldDefinition jdbcFieldDefinition = (JdbcFieldDefinition)fieldDefinition; jdbcFieldDefinition.addColumnName(sql, tablePrefix); } else { sql.append(fieldDefinition.getName()); } } public static void addOrderBy(final StringBuilder sql, final Map<String, Boolean> orderBy) { if (!orderBy.isEmpty()) { sql.append(" ORDER BY "); appendOrderByFields(sql, orderBy); } } public static StringBuilder appendOrderByFields(final StringBuilder sql, final Map<String, Boolean> orderBy) { boolean first = true; for (final Entry<String, Boolean> entry : orderBy.entrySet()) { if (first) { first = false; } else { sql.append(", "); } final String column = entry.getKey(); sql.append(column); final Boolean ascending = entry.getValue(); if (!ascending) { sql.append(" DESC"); } } return sql; } public static void appendWhere(final StringBuilder sql, final Query query) { final Condition where = query.getWhereCondition(); if (!where.isEmpty()) { sql.append(" WHERE "); final RecordDefinition recordDefinition = query.getRecordDefinition(); if (recordDefinition == null) { where.appendSql(query, null, sql); } else { final RecordStore recordStore = recordDefinition.getRecordStore(); where.appendSql(query, recordStore, sql); } } } public static String cleanObjectName(final String objectName) { return objectName.replaceAll("[^a-zA-Z\\._]", ""); } public static void close(final ResultSet resultSet) { if (resultSet != null) { try { resultSet.close(); } catch (final SQLException e) { LOG.debug("SQL error closing result set", e); } catch (final Throwable e) { LOG.debug("Unknown error closing result set", e); } } } public static void close(final Statement statement) { if (statement != null) { try { statement.close(); } catch (final SQLException e) { LOG.debug("SQL error closing statement", e); } catch (final Throwable e) { LOG.debug("Unknown error closing statement", e); } } } public static void close(final Statement statement, final ResultSet resultSet) { close(resultSet); close(statement); } public static void commit(final Connection connection) { try { connection.commit(); } catch (final SQLException e) { } } public static void delete(final Connection connection, final String tableName, final String idColumn, final Object id) { final String sql = "DELETE FROM " + cleanObjectName(tableName) + " WHERE " + cleanObjectName(idColumn) + " = ?"; try { final PreparedStatement statement = connection.prepareStatement(sql); try { setValue(statement, 1, id); statement.executeQuery(); } catch (final SQLException e) { LOG.error("Unable to delete:" + sql, e); throw new RuntimeException("Unable to delete:" + sql, e); } finally { close(statement); connection.commit(); } } catch (final SQLException e) { LOG.error("Invalid table name or id column: " + sql, e); throw new IllegalArgumentException("Invalid table name or id column: " + sql); } } public static int executeUpdate(final Connection connection, final String sql, final Object... parameters) throws SQLException { final PreparedStatement statement = connection.prepareStatement(sql); try { setParameters(statement, parameters); return statement.executeUpdate(); } finally { close(statement); } } public static int executeUpdate(final DataSource dataSource, final String sql, final Object... parameters) { final Connection connection = getConnection(dataSource); try { return executeUpdate(connection, sql, parameters); } catch (final SQLException e) { throw getException(dataSource, connection, "Update", sql, e); } finally { release(connection, dataSource); } } public static int executeUpdate(final JdbcRecordStore recordStore, final String sql, final Object... parameters) { try ( Transaction transaction = recordStore.newTransaction(Propagation.REQUIRED); final JdbcConnection connection = recordStore.getJdbcConnection()) { try { return executeUpdate(connection, sql, parameters); } catch (final SQLException e) { throw connection.getException("Update", sql, e); } } } public static BigDecimal[] getBigDecimalArray(final ResultSet resultSet, final int index) throws SQLException { final Array array = resultSet.getArray(index); return (BigDecimal[])array.getArray(); } public static Connection getConnection(final DataSource dataSource) { try { return DataSourceUtils.doGetConnection(dataSource); } catch (final SQLException e) { throw getException(dataSource, null, "Get Connection", null, e); } } public static String getDeleteSql(final Query query) { final String tableName = query.getTypeName(); final String dbTableName = getQualifiedTableName(tableName); final StringBuilder sql = new StringBuilder(); sql.append("DELETE FROM "); sql.append(dbTableName); sql.append(" T "); appendWhere(sql, query); return sql.toString(); } public static DataAccessException getException(final DataSource dataSource, final Connection connection, final String task, final String sql, final SQLException e) { SQLExceptionTranslator translator; if (dataSource == null) { translator = new SQLStateSQLExceptionTranslator(); } else { translator = new JdbcExceptionTranslator(dataSource); } return translator.translate(task, sql, e); } public static String getProductName(final DataSource dataSource) { if (dataSource == null) { return null; } else { final Connection connection = getConnection(dataSource); try { if (connection == null) { if (dataSource.getClass().getName().toLowerCase().contains("oracle")) { return "Oracle"; } else if (dataSource.getClass().getName().toLowerCase().contains("postgres")) { return "PostgreSQL"; } else { return null; } } else { final DatabaseMetaData metaData = connection.getMetaData(); return metaData.getDatabaseProductName(); } } catch (final SQLException e) { throw new IllegalArgumentException("Unable to get database product name", e); } finally { release(connection, dataSource); } } } public static String getQualifiedTableName(final String typePath) { if (Property.hasValue(typePath)) { final String tableName = typePath.replaceAll("^/+", ""); return tableName.replaceAll("/", "."); } else { return null; } } public static String getSchemaName(final String typePath) { if (Property.hasValue(typePath)) { final String path = PathUtil.getPath(typePath); return path.replaceAll("(^/|/$)", ""); } else { return ""; } } public static String getSelectSql(final Query query) { final String tableName = query.getTypeName(); final String dbTableName = getQualifiedTableName(tableName); String sql = query.getSql(); final Map<String, Boolean> orderBy = query.getOrderBy(); RecordDefinition recordDefinition = query.getRecordDefinition(); if (sql == null) { if (recordDefinition == null) { recordDefinition = new RecordDefinitionImpl(PathName.newPathName(tableName)); // throw new IllegalArgumentException("Unknown table name " + // tableName); } final List<String> fieldNames = new ArrayList<>(query.getFieldNames()); if (fieldNames.isEmpty()) { final List<String> recordDefinitionFieldNames = recordDefinition.getFieldNames(); if (recordDefinitionFieldNames.isEmpty()) { fieldNames.add("T.*"); } else { fieldNames.addAll(recordDefinitionFieldNames); } } final String fromClause = query.getFromClause(); final boolean lockResults = query.isLockResults(); sql = newSelectSql(recordDefinition, "T", fromClause, lockResults, fieldNames, query, orderBy); } else { if (sql.toUpperCase().startsWith("SELECT * FROM ")) { final StringBuilder newSql = new StringBuilder("SELECT "); addColumnNames(newSql, recordDefinition, dbTableName); newSql.append(" FROM "); newSql.append(sql.substring(14)); sql = newSql.toString(); } if (!orderBy.isEmpty()) { final StringBuilder buffer = new StringBuilder(sql); addOrderBy(buffer, orderBy); sql = buffer.toString(); } } return sql; } public static String getTableName(final String typePath) { final String tableName = PathUtil.getName(typePath); return tableName; } public static void lockTable(final Connection connection, final String tableName) throws SQLException { final String sql = "LOCK TABLE " + tableName + " IN SHARE MODE"; final PreparedStatement statement = connection.prepareStatement(sql); try { statement.execute(); } finally { close(statement); } } public static void lockTable(final RecordStore recordStore, final String typePath) { if (recordStore instanceof JdbcRecordStore) { final JdbcRecordStore jdbcRecordStore = (JdbcRecordStore)recordStore; try ( final JdbcConnection connection = jdbcRecordStore.getJdbcConnection()) { final String tableName = getQualifiedTableName(typePath); final String sql = "LOCK TABLE " + tableName + " IN SHARE MODE"; executeUpdate(connection, sql); } catch (final SQLException e) { throw new RuntimeException("Unable to lock table " + typePath, e); } } } public static String newSelectSql(final RecordDefinition recordDefinition, final String tablePrefix, final String fromClause, final boolean lockResults, final List<String> fieldNames, final Query query, final Map<String, Boolean> orderBy) { final String typePath = recordDefinition.getPath(); final StringBuilder sql = new StringBuilder(); sql.append("SELECT "); boolean hasColumns = false; if (fieldNames.isEmpty() || fieldNames.remove("*")) { addColumnNames(sql, recordDefinition, tablePrefix); hasColumns = true; } addColumnNames(sql, recordDefinition, tablePrefix, fieldNames, hasColumns); sql.append(" FROM "); if (Property.hasValue(fromClause)) { sql.append(fromClause); } else { final String tableName = getQualifiedTableName(typePath); sql.append(tableName); sql.append(" "); sql.append(tablePrefix); } appendWhere(sql, query); addOrderBy(sql, orderBy); if (lockResults) { sql.append(" FOR UPDATE"); } return sql.toString(); } public static MapEx readMap(final ResultSet rs) throws SQLException { final MapEx values = new LinkedHashMapEx(); final ResultSetMetaData metaData = rs.getMetaData(); for (int i = 1; i <= metaData.getColumnCount(); i++) { final String name = metaData.getColumnName(i); final Object value = rs.getObject(i); values.put(name, value); } return values; } public static void release(final Connection connection, final DataSource dataSource) { if (dataSource != null && connection != null) { DataSourceUtils.releaseConnection(connection, dataSource); } } public static Date selectDate(final Connection connection, final String sql, final Object... parameters) throws SQLException { final PreparedStatement statement = connection.prepareStatement(sql); try { setParameters(statement, parameters); final ResultSet resultSet = statement.executeQuery(); try { if (resultSet.next()) { return resultSet.getDate(1); } else { throw new IllegalArgumentException("Value not found"); } } finally { close(resultSet); } } finally { close(statement); } } public static Date selectDate(final DataSource dataSource, final Connection connection, final String sql, final Object... parameters) throws SQLException { if (dataSource == null) { return selectDate(connection, sql, parameters); } else { return selectDate(dataSource, sql, parameters); } } public static Date selectDate(final DataSource dataSource, final String sql, final Object... parameters) throws SQLException { final Connection connection = getConnection(dataSource); try { return selectDate(connection, sql, parameters); } finally { release(connection, dataSource); } } public static int selectInt(final Connection connection, final String sql, final Object... parameters) { return selectInt(null, connection, sql, parameters); } public static int selectInt(final DataSource dataSource, Connection connection, final String sql, final Object... parameters) { if (dataSource != null) { connection = getConnection(dataSource); } try { final PreparedStatement statement = connection.prepareStatement(sql); try { setParameters(statement, parameters); final ResultSet resultSet = statement.executeQuery(); try { if (resultSet.next()) { return resultSet.getInt(1); } else { throw new IllegalArgumentException("Value not found"); } } finally { close(resultSet); } } finally { close(statement); } } catch (final SQLException e) { throw getException(dataSource, connection, "selectInt", sql, e); } finally { if (dataSource != null) { release(connection, dataSource); } } } public static int selectInt(final DataSource dataSource, final String sql, final Object... parameters) { return selectInt(dataSource, null, sql, parameters); } public static int selectInt(final JdbcRecordStore recordStore, final String sql, final Object... parameters) { try ( JdbcConnection connection = recordStore.getJdbcConnection()) { try ( final PreparedStatement statement = connection.prepareStatement(sql)) { setParameters(statement, parameters); try ( final ResultSet resultSet = statement.executeQuery()) { if (resultSet.next()) { return resultSet.getInt(1); } else { throw new IllegalArgumentException("Value not found"); } } } catch (final SQLException e) { throw connection.getException("selectInt", sql, e); } } } public static <T> List<T> selectList(final Connection connection, final String sql, final int columnIndex, final Object... parameters) throws SQLException { final List<T> results = new ArrayList<>(); final PreparedStatement statement = connection.prepareStatement(sql); try { setParameters(statement, parameters); final ResultSet resultSet = statement.executeQuery(); try { while (resultSet.next()) { @SuppressWarnings("unchecked") final T value = (T)resultSet.getObject(columnIndex); results.add(value); } return results; } finally { close(resultSet); } } finally { close(statement); } } public static long selectLong(final Connection connection, final String sql, final Object... parameters) throws SQLException { final PreparedStatement statement = connection.prepareStatement(sql); try { setParameters(statement, parameters); final ResultSet resultSet = statement.executeQuery(); try { if (resultSet.next()) { return resultSet.getLong(1); } else { throw new IllegalArgumentException("Value not found"); } } finally { close(resultSet); } } finally { close(statement); } } public static long selectLong(final DataSource dataSource, final Connection connection, final String sql, final Object... parameters) throws SQLException { if (dataSource == null) { return selectLong(connection, sql, parameters); } else { return selectLong(dataSource, sql, parameters); } } public static long selectLong(final DataSource dataSource, final String sql, final Object... parameters) throws SQLException { final Connection connection = getConnection(dataSource); try { return selectLong(connection, sql, parameters); } finally { release(connection, dataSource); } } public static long selectLong(final JdbcRecordStore recordStore, final String sql, final Object... parameters) { try ( JdbcConnection connection = recordStore.getJdbcConnection()) { try ( final PreparedStatement statement = connection.prepareStatement(sql)) { setParameters(statement, parameters); try ( final ResultSet resultSet = statement.executeQuery()) { if (resultSet.next()) { return resultSet.getLong(1); } else { throw new IllegalArgumentException("Value not found"); } } } catch (final SQLException e) { throw connection.getException("selectInt", sql, e); } } } public static Map<String, Object> selectMap(final Connection connection, final String sql, final Object... parameters) throws SQLException { final PreparedStatement statement = connection.prepareStatement(sql); try { setParameters(statement, parameters); final ResultSet resultSet = statement.executeQuery(); try { if (resultSet.next()) { return readMap(resultSet); } else { throw new IllegalArgumentException( "Value not found for " + sql + " " + Arrays.asList(parameters)); } } finally { close(resultSet); } } finally { close(statement); } } public static Map<String, Object> selectMap(final DataSource dataSource, final String sql, final Object... parameters) throws SQLException { final Connection connection = getConnection(dataSource); try { return selectMap(connection, sql, parameters); } finally { release(connection, dataSource); } } public static MapEx selectMap(final JdbcRecordStore recordStore, final String sql, final Object... parameters) { try ( JdbcConnection connection = recordStore.getJdbcConnection()) { try ( final PreparedStatement statement = connection.prepareStatement(sql)) { setParameters(statement, parameters); try ( final ResultSet resultSet = statement.executeQuery()) { if (resultSet.next()) { return readMap(resultSet); } else { throw new IllegalArgumentException( "Value not found for " + sql + " " + Arrays.asList(parameters)); } } } catch (final SQLException e) { throw connection.getException(null, sql, e); } } } public static String selectString(final Connection connection, final String sql, final Object... parameters) throws SQLException { try ( final PreparedStatement statement = connection.prepareStatement(sql)) { setParameters(statement, parameters); try ( final ResultSet resultSet = statement.executeQuery()) { if (resultSet.next()) { return resultSet.getString(1); } else { throw new IllegalArgumentException("Value not found"); } } } } public static String selectString(final DataSource dataSource, final Connection connection, final String sql, final Object... parameters) throws SQLException { if (dataSource == null) { return selectString(connection, sql, parameters); } else { return selectString(dataSource, sql, parameters); } } public static String selectString(final DataSource dataSource, final String sql, final Object... parameters) throws SQLException { final Connection connection = getConnection(dataSource); try { return selectString(connection, sql, parameters); } finally { release(connection, dataSource); } } public static String selectString(final JdbcRecordStore recordStore, final String sql, final Object... parameters) throws SQLException { try ( JdbcConnection connection = recordStore.getJdbcConnection()) { return selectString(connection, sql, parameters); } } public static void setParameters(final PreparedStatement statement, final Object... parameters) throws SQLException { int index = 1; for (final Object parameter : parameters) { index = setValue(statement, index, parameter); } } public static void setPreparedStatementParameters(final PreparedStatement statement, final Query query) { int index = 1; for (final Object parameter : query.getParameters()) { final JdbcFieldDefinition attribute = JdbcFieldDefinition.newFieldDefinition(parameter); try { index = attribute.setPreparedStatementValue(statement, index, parameter); } catch (final SQLException e) { throw new RuntimeException("Error setting value:" + parameter, e); } } final Condition where = query.getWhereCondition(); if (!where.isEmpty()) { where.appendParameters(index, statement); } } public static int setValue(final PreparedStatement statement, final int index, final Object value) throws SQLException { final JdbcFieldDefinition fieldDefinition = JdbcFieldDefinition.newFieldDefinition(value); return fieldDefinition.setPreparedStatementValue(statement, index, value); } public static Struct struct(final Connection connection, final String type, final Object... args) throws SQLException { return connection.createStruct(type, args); } private JdbcUtils() { } }