aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-db/src/main/java/org/sonar/db/DatabaseUtils.java
diff options
context:
space:
mode:
Diffstat (limited to 'sonar-db/src/main/java/org/sonar/db/DatabaseUtils.java')
-rw-r--r--sonar-db/src/main/java/org/sonar/db/DatabaseUtils.java327
1 files changed, 0 insertions, 327 deletions
diff --git a/sonar-db/src/main/java/org/sonar/db/DatabaseUtils.java b/sonar-db/src/main/java/org/sonar/db/DatabaseUtils.java
deleted file mode 100644
index 31f5abebb9f..00000000000
--- a/sonar-db/src/main/java/org/sonar/db/DatabaseUtils.java
+++ /dev/null
@@ -1,327 +0,0 @@
-/*
- * SonarQube
- * Copyright (C) 2009-2017 SonarSource SA
- * mailto:info AT sonarsource DOT com
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- */
-package org.sonar.db;
-
-import com.google.common.base.Function;
-import com.google.common.collect.Iterables;
-import com.google.common.collect.Lists;
-import com.google.common.collect.Ordering;
-import com.google.common.collect.Sets;
-import java.sql.Connection;
-import java.sql.DatabaseMetaData;
-import java.sql.ResultSet;
-import java.sql.SQLException;
-import java.sql.Statement;
-import java.sql.Timestamp;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.function.Consumer;
-import javax.annotation.CheckForNull;
-import javax.annotation.Nullable;
-import org.sonar.api.utils.log.Logger;
-import org.sonar.api.utils.log.Loggers;
-
-import static com.google.common.collect.Lists.newArrayList;
-import static java.lang.String.format;
-
-public class DatabaseUtils {
-
- public static final int PARTITION_SIZE_FOR_ORACLE = 1000;
-
- /**
- * @see DatabaseMetaData#getTableTypes()
- */
- private static final String[] TABLE_TYPE = {"TABLE"};
-
- public static void closeQuietly(@Nullable Connection connection) {
- if (connection != null) {
- try {
- connection.close();
- } catch (SQLException e) {
- Loggers.get(DatabaseUtils.class).warn("Fail to close connection", e);
- // ignore
- }
- }
- }
-
- public static void closeQuietly(@Nullable Statement stmt) {
- if (stmt != null) {
- try {
- stmt.close();
- } catch (SQLException e) {
- Loggers.get(DatabaseUtils.class).warn("Fail to close statement", e);
- // ignore
- }
- }
- }
-
- public static void closeQuietly(@Nullable ResultSet rs) {
- if (rs != null) {
- try {
- rs.close();
- } catch (SQLException e) {
- Loggers.get(DatabaseUtils.class).warn("Fail to close result set", e);
- // ignore
- }
- }
- }
-
- /**
- * Returns an escaped value in parameter, with the desired wildcards. Suitable to be used in a like sql query<br />
- * Escapes the "/", "%" and "_" characters.<br/>
- *
- * You <strong>must</strong> add "ESCAPE '/'" after your like query. It defines '/' as the escape character.
- */
- public static String buildLikeValue(String value, WildcardPosition wildcardPosition) {
- String escapedValue = escapePercentAndUnderscore(value);
- String wildcard = "%";
- switch (wildcardPosition) {
- case BEFORE:
- escapedValue = wildcard + escapedValue;
- break;
- case AFTER:
- escapedValue += wildcard;
- break;
- case BEFORE_AND_AFTER:
- escapedValue = wildcard + escapedValue + wildcard;
- break;
- default:
- throw new UnsupportedOperationException("Unhandled WildcardPosition: " + wildcardPosition);
- }
-
- return escapedValue;
- }
-
- /**
- * Replace escape percent and underscore by adding a slash just before
- */
- private static String escapePercentAndUnderscore(String value) {
- return value
- .replaceAll("/", "//")
- .replaceAll("%", "/%")
- .replaceAll("_", "/_");
- }
-
- /**
- * Partition by 1000 elements a list of input and execute a function on each part.
- *
- * The goal is to prevent issue with ORACLE when there's more than 1000 elements in a 'in ('X', 'Y', ...)'
- * and with MsSQL when there's more than 2000 parameters in a query
- */
- public static <OUTPUT, INPUT extends Comparable<INPUT>> List<OUTPUT> executeLargeInputs(Collection<INPUT> input, Function<List<INPUT>, List<OUTPUT>> function) {
- return executeLargeInputs(input, function, size -> size == 0 ? Collections.emptyList() : new ArrayList<>(size));
- }
-
- public static <OUTPUT, INPUT extends Comparable<INPUT>> Set<OUTPUT> executeLargeInputsIntoSet(Collection<INPUT> input, Function<List<INPUT>, Set<OUTPUT>> function) {
- return executeLargeInputs(input, function, size -> size == 0 ? Collections.emptySet() : new HashSet<>(size));
- }
-
- private static <OUTPUT, INPUT extends Comparable<INPUT>, RESULT extends Collection<OUTPUT>> RESULT executeLargeInputs(Collection<INPUT> input,
- Function<List<INPUT>, RESULT> function, java.util.function.Function<Integer, RESULT> outputInitializer) {
- if (input.isEmpty()) {
- return outputInitializer.apply(0);
- }
- RESULT results = outputInitializer.apply(input.size());
- for (List<INPUT> partition : toUniqueAndSortedPartitions(input)) {
- RESULT subResults = function.apply(partition);
- if (subResults != null) {
- results.addAll(subResults);
- }
- }
- return results;
- }
-
- /**
- * Partition by 1000 elements a list of input and execute a consumer on each part.
- *
- * The goal is to prevent issue with ORACLE when there's more than 1000 elements in a 'in ('X', 'Y', ...)'
- * and with MsSQL when there's more than 2000 parameters in a query
- */
- public static <INPUT extends Comparable<INPUT>> void executeLargeUpdates(Collection<INPUT> inputs, Consumer<List<INPUT>> consumer) {
- Iterable<List<INPUT>> partitions = toUniqueAndSortedPartitions(inputs);
- for (List<INPUT> partition : partitions) {
- consumer.accept(partition);
- }
- }
-
- /**
- * Partition by 1000 elements a list of input and execute a consumer on each part.
- *
- * The goal is to prevent issue with ORACLE when there's more than 1000 elements in a 'in ('X', 'Y', ...)'
- * and with MsSQL when there's more than 2000 parameters in a query
- *
- * @param sqlCaller a {@link Function} which calls the SQL update/delete and returns the number of updated/deleted rows.
- *
- * @return the total number of updated/deleted rows (computed as the sum of the values returned by {@code sqlCaller}).
- */
- public static <INPUT extends Comparable<INPUT>> int executeLargeUpdates(Collection<INPUT> inputs, Function<List<INPUT>, Integer> sqlCaller) {
- Iterable<List<INPUT>> partitions = toUniqueAndSortedPartitions(inputs);
- Integer res = 0;
- for (List<INPUT> partition : partitions) {
- res += sqlCaller.apply(partition);
- }
- return res;
- }
-
- /**
- * Ensure values {@code inputs} are unique (which avoids useless arguments) and sorted before creating the partition.
- */
- private static <INPUT extends Comparable<INPUT>> Iterable<List<INPUT>> toUniqueAndSortedPartitions(Collection<INPUT> inputs) {
- return Iterables.partition(toUniqueAndSortedList(inputs), PARTITION_SIZE_FOR_ORACLE);
- }
-
- /**
- * Ensure values {@code inputs} are unique (which avoids useless arguments) and sorted so that there is little
- * variations of SQL requests over time as possible with a IN clause and/or a group of OR clauses. Such requests can
- * then be more easily optimized by the SGDB engine.
- */
- public static <INPUT extends Comparable<INPUT>> List<INPUT> toUniqueAndSortedList(Iterable<INPUT> inputs) {
- if (inputs instanceof Set) {
- // inputs are unique but order is not enforced
- return Ordering.natural().immutableSortedCopy(inputs);
- }
- // inputs are not unique and order is not guaranteed
- return Ordering.natural().immutableSortedCopy(Sets.newHashSet(inputs));
- }
-
- /**
- * Partition by 1000 elements a list of input and execute a function on each part.
- * The function has not output (ex: delete operation)
- *
- * The goal is to prevent issue with ORACLE when there's more than 1000 elements in a 'in ('X', 'Y', ...)'
- * and with MsSQL when there's more than 2000 parameters in a query
- */
- public static <INPUT> void executeLargeInputsWithoutOutput(Collection<INPUT> input, Function<List<INPUT>, Void> function) {
- if (input.isEmpty()) {
- return;
- }
-
- List<List<INPUT>> partitions = Lists.partition(newArrayList(input), PARTITION_SIZE_FOR_ORACLE);
- for (List<INPUT> partition : partitions) {
- function.apply(partition);
- }
- }
-
- public static String repeatCondition(String sql, int count, String separator) {
- StringBuilder sb = new StringBuilder();
- for (int i = 0; i < count; i++) {
- sb.append(sql);
- if (i < count - 1) {
- sb.append(" ").append(separator).append(" ");
- }
- }
- return sb.toString();
- }
-
- /**
- * Logback does not log exceptions associated to {@link java.sql.SQLException#getNextException()}.
- * See http://jira.qos.ch/browse/LOGBACK-775
- */
- public static void log(Logger logger, SQLException e) {
- SQLException next = e.getNextException();
- while (next != null) {
- logger.error("SQL error: {}. Message: {}", next.getSQLState(), next.getMessage());
- next = next.getNextException();
- }
- }
-
- @CheckForNull
- public static Long getLong(ResultSet rs, String columnName) throws SQLException {
- long l = rs.getLong(columnName);
- return rs.wasNull() ? null : l;
- }
-
- @CheckForNull
- public static Double getDouble(ResultSet rs, String columnName) throws SQLException {
- double d = rs.getDouble(columnName);
- return rs.wasNull() ? null : d;
- }
-
- @CheckForNull
- public static Integer getInt(ResultSet rs, String columnName) throws SQLException {
- int i = rs.getInt(columnName);
- return rs.wasNull() ? null : i;
- }
-
- @CheckForNull
- public static String getString(ResultSet rs, String columnName) throws SQLException {
- String s = rs.getString(columnName);
- return rs.wasNull() ? null : s;
- }
-
- @CheckForNull
- public static Long getLong(ResultSet rs, int columnIndex) throws SQLException {
- long l = rs.getLong(columnIndex);
- return rs.wasNull() ? null : l;
- }
-
- @CheckForNull
- public static Double getDouble(ResultSet rs, int columnIndex) throws SQLException {
- double d = rs.getDouble(columnIndex);
- return rs.wasNull() ? null : d;
- }
-
- @CheckForNull
- public static Integer getInt(ResultSet rs, int columnIndex) throws SQLException {
- int i = rs.getInt(columnIndex);
- return rs.wasNull() ? null : i;
- }
-
- @CheckForNull
- public static String getString(ResultSet rs, int columnIndex) throws SQLException {
- String s = rs.getString(columnIndex);
- return rs.wasNull() ? null : s;
- }
-
- @CheckForNull
- public static Date getDate(ResultSet rs, int columnIndex) throws SQLException {
- Timestamp t = rs.getTimestamp(columnIndex);
- return rs.wasNull() ? null : new Date(t.getTime());
- }
-
- /**
- * @param table case-insensitive name of table
- * @return true if a table exists with this name, otherwise false
- * @throws SQLException
- */
- public static boolean tableExists(String table, Connection connection) {
- // table type is used to speed-up Oracle by removing introspection of system tables and aliases.
- try (ResultSet rs = connection.getMetaData().getTables(null, null, null, TABLE_TYPE)) {
- while (rs.next()) {
- String name = rs.getString("TABLE_NAME");
- if (table.equalsIgnoreCase(name)) {
- return true;
- }
- }
- return false;
- } catch (SQLException e) {
- throw wrapSqlException(e, "Can not check that table %s exists", table);
- }
- }
-
- public static IllegalStateException wrapSqlException(SQLException e, String message, Object... messageArgs) {
- return new IllegalStateException(format(message, messageArgs), e);
- }
-}