<commit_before>/**
 * Copyright (C) 2000 - 2013 Silverpeas
 *
 * This program is free software: you can redistribute it and/or modify it under the terms of the
 * GNU Affero General Public License as published by the Free Software Foundation, either version 3
 * of the License, or (at your option) any later version.
 *
 * As a special exception to the terms and conditions of version 3.0 of the GPL, you may
 * redistribute this Program in connection with Free/Libre Open Source Software ("FLOSS")
 * applications as described in Silverpeas's FLOSS exception. You should have received a copy of the
 * text describing the FLOSS exception, and it is also available here:
 * "http://www.silverpeas.org/docs/core/legal/floss_exception.html"
 *
 * 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
 * Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License along with this program.
 * If not, see <http://www.gnu.org/licenses/>.
 */
package org.silverpeas.util;

import org.apache.commons.lang3.tuple.Pair;
import org.silverpeas.persistence.Transaction;
import org.silverpeas.util.pool.ConnectionPool;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;

public class DBUtil {

  /**
   * @return the DateFieldLength
   */
  public static int getDateFieldLength() {
    return dateFieldLength;
  }

  /**
   * @return the TextMaxiLength
   */
  public static int getTextMaxiLength() {
    return textMaxiLength;
  }

  /**
   * @return the TextAreaLength
   */
  public static int getTextAreaLength() {
    return textAreaLength;
  }

  /**
   * @return the TextFieldLength
   */
  public static int getTextFieldLength() {
    return textFieldLength;
  }

  /**
   * TextFieldLength is the maximum length to store an html textfield input in db.
   */
  private static final int textFieldLength = 1000;
  /**
   * TextAreaLength is the maximum length to store an html textarea input in db.
   */
  private static final int textAreaLength = 2000;
  /**
   * TextMaxiLength is the maximum length to store in db. This length is to use with fields that
   * can
   * contain a lot of information. This is the case of publication's model for exemple. TODO : In
   * the near future, these fields will have to be put in BLOB (Binary Large OBject).
   */
  private static final int textMaxiLength = 4000;
  /**
   * DateFieldLength is the length to use for date storage.
   */
  private static final int dateFieldLength = 10;

  @Override
  public String toString() {
    return super.toString();
  }

  private static final Logger logger = Logger.getLogger(DBUtil.class.getSimpleName());

  /**
   * fabrique une nouvelle connection
   * @return a new connection to the database.
   */
  public static Connection openConnection() throws SQLException {
    return ServiceProvider.getService(ConnectionPool.class).getConnection();
  }

  /**
   * Returns a new unique identifier of type string.
   * @return a new unique string identifier.
   */
  public static String getUniqueId() {
    return UUID.randomUUID().toString();
  }

  /**
   * Return a new unique Id for a table.
   * @param tableName the name of the table.
   * @param idName the name of the column.
   * @return a unique id.
   * @throws java.sql.SQLException
   */
  public static synchronized int getNextId(final String tableName, final String idName)
      throws SQLException {
    //noinspection RedundantCast
    final Pair<Integer, SQLException> result =
        Transaction.performInNew((Transaction.Process<Pair<Integer, SQLException>>) () -> {
          Connection connection = null;
          try {
            connection = openConnection();
            return Pair.of(getMaxId(connection, tableName, idName), null);
          } catch (SQLException ex) {
            return Pair.of(null, ex);
          } finally {
            close(connection);
          }
        });
    //noinspection ThrowableResultOfMethodCallIgnored
    if (result.getRight() != null) {
      throw result.getRight();
    }
    return result.getLeft();
  }

  private static int getMaxId(Connection connection, String tableName, String idName)
  throws SQLException {
    // tentative d'update
    try {
      return updateMaxFromTable(connection, tableName);
    } catch (Exception ignored) {
    }
    int max = getMaxFromTable(connection, tableName, idName);
    PreparedStatement createStmt = null;
    try {
      // on enregistre le max
      String createStatement = "INSERT INTO UniqueId (maxId, tableName) VALUES (?, ?)";
      createStmt = connection.prepareStatement(createStatement);
      createStmt.setInt(1, max);
      createStmt.setString(2, tableName.toLowerCase());
      createStmt.executeUpdate();
      return max;
    } catch (Exception e) {
      // access concurrency
    } finally {
      close(createStmt);
    }
    max = updateMaxFromTable(connection, tableName);
    return max;
  }

  private static int updateMaxFromTable(Connection connection, String tableName)
      throws SQLException {
    String table = tableName.toLowerCase(Locale.ROOT);
    int max = 0;
    PreparedStatement prepStmt = null;
    int count = 0;
    try {
      prepStmt =
          connection.prepareStatement("UPDATE UniqueId SET maxId = maxId + 1 WHERE tableName = ?");
      prepStmt.setString(1, table);
      count = prepStmt.executeUpdate();
    } finally {
      close(prepStmt);
    }<commit_msg>fixing bug #6327, first part removing Transaction in DBUtil.getNextId waiting for next step : change process order select/insert statement in order to avoid SQLException<commit_after>/**
 * Copyright (C) 2000 - 2013 Silverpeas
 *
 * This program is free software: you can redistribute it and/or modify it under the terms of the
 * GNU Affero General Public License as published by the Free Software Foundation, either version 3
 * of the License, or (at your option) any later version.
 *
 * As a special exception to the terms and conditions of version 3.0 of the GPL, you may
 * redistribute this Program in connection with Free/Libre Open Source Software ("FLOSS")
 * applications as described in Silverpeas's FLOSS exception. You should have received a copy of the
 * text describing the FLOSS exception, and it is also available here:
 * "http://www.silverpeas.org/docs/core/legal/floss_exception.html"
 *
 * 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
 * Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License along with this program.
 * If not, see <http://www.gnu.org/licenses/>.
 */
package org.silverpeas.util;

import org.silverpeas.util.pool.ConnectionPool;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.LinkedHashSet;
import java.util.Locale;
import java.util.Set;
import java.util.UUID;
import java.util.logging.Level;
import java.util.logging.Logger;

public class DBUtil {

  /**
   * @return the DateFieldLength
   */
  public static int getDateFieldLength() {
    return dateFieldLength;
  }

  /**
   * @return the TextMaxiLength
   */
  public static int getTextMaxiLength() {
    return textMaxiLength;
  }

  /**
   * @return the TextAreaLength
   */
  public static int getTextAreaLength() {
    return textAreaLength;
  }

  /**
   * @return the TextFieldLength
   */
  public static int getTextFieldLength() {
    return textFieldLength;
  }

  /**
   * TextFieldLength is the maximum length to store an html textfield input in db.
   */
  private static final int textFieldLength = 1000;
  /**
   * TextAreaLength is the maximum length to store an html textarea input in db.
   */
  private static final int textAreaLength = 2000;
  /**
   * TextMaxiLength is the maximum length to store in db. This length is to use with fields that
   * can
   * contain a lot of information. This is the case of publication's model for exemple. TODO : In
   * the near future, these fields will have to be put in BLOB (Binary Large OBject).
   */
  private static final int textMaxiLength = 4000;
  /**
   * DateFieldLength is the length to use for date storage.
   */
  private static final int dateFieldLength = 10;

  @Override
  public String toString() {
    return super.toString();
  }

  private static final Logger logger = Logger.getLogger(DBUtil.class.getSimpleName());

  /**
   * fabrique une nouvelle connection
   * @return a new connection to the database.
   */
  public static Connection openConnection() throws SQLException {
    return ServiceProvider.getService(ConnectionPool.class).getConnection();
  }

  /**
   * Returns a new unique identifier of type string.
   * @return a new unique string identifier.
   */
  public static String getUniqueId() {
    return UUID.randomUUID().toString();
  }

  /**
   * Return a new unique Id for a table.
   * @param tableName the name of the table.
   * @param idName the name of the column.
   * @return a unique id.
   * @throws java.sql.SQLException
   */
  public static synchronized int getNextId(final String tableName, final String idName)
      throws SQLException {
    //noinspection RedundantCast
    try (Connection connection = openConnection()) {
      return getMaxId(connection, tableName, idName);
    }
    /*
    TODO replace with another process selectOrInsertMaxId keeping the performInNew transaction
    final Pair<Integer, SQLException> result =
        Transaction.performInNew((Transaction.Process<Pair<Integer, SQLException>>) () -> {
          Connection connection = null;
          try {
            connection = openConnection();
            return Pair.of(getMaxId(connection, tableName, idName), null);
          } catch (SQLException ex) {
            return Pair.of(null, ex);
          } finally {
            close(connection);
          }
        });
    //noinspection ThrowableResultOfMethodCallIgnored
    if (result.getRight() != null) {
      throw result.getRight();
    }
    return result.getLeft();
     */
  }

  private static int getMaxId(Connection connection, String tableName, String idName)
      throws SQLException {
    // tentative d'update
    try {
      return updateMaxFromTable(connection, tableName);
    } catch (Exception ignored) {
    }
    int max = getMaxFromTable(connection, tableName, idName);
    PreparedStatement createStmt = null;
    try {
      // on enregistre le max
      String createStatement = "INSERT INTO UniqueId (maxId, tableName) VALUES (?, ?)";
      createStmt = connection.prepareStatement(createStatement);
      createStmt.setInt(1, max);
      createStmt.setString(2, tableName.toLowerCase());
      createStmt.executeUpdate();
      return max;
    } catch (Exception e) {
      // access concurrency
      logger.log(Level.WARNING, e.getMessage(), e);
    } finally {
      close(createStmt);
    }
    max = updateMaxFromTable(connection, tableName);
    return max;
  }

  private static int updateMaxFromTable(Connection connection, String tableName)
      throws SQLException {
    String table = tableName.toLowerCase(Locale.ROOT);
    int max = 0;
    PreparedStatement prepStmt = null;
    int count = 0;
    try {
      prepStmt =
          connection.prepareStatement("UPDATE UniqueId SET maxId = maxId + 1 WHERE tableName = ?");
      prepStmt.setString(1, table);
      count = prepStmt.executeUpdate();
    } finally {
      close(prepStmt);
    }
{'fraction_non_alphanumeric': 0.0867008943358728, 'fraction_numerical': 0.0055481947664789664, 'mean_word_length': 3.184684684684685, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '823027', 'n_tokens_mistral': 3352, 'n_tokens_neox': 3287, 'n_words': 1175}