コード例 #1
0
ファイル: Insert.java プロジェクト: 416235710/openddal
 /**
  * Keep a collection of the columns to pass to update if a duplicate key happens, for MySQL-style
  * INSERT ... ON DUPLICATE KEY UPDATE ....
  *
  * @param column the column
  * @param expression the expression
  */
 public void addAssignmentForDuplicate(Column column, Expression expression) {
   if (duplicateKeyAssignmentMap == null) {
     duplicateKeyAssignmentMap = New.hashMap();
   }
   if (duplicateKeyAssignmentMap.containsKey(column)) {
     throw DbException.get(ErrorCode.DUPLICATE_COLUMN_NAME_1, column.getName());
   }
   duplicateKeyAssignmentMap.put(column, expression);
 }
コード例 #2
0
ファイル: TableMate.java プロジェクト: openddal/openddal
  private void tryReadMetaData(Connection conn, String oCatalog, String oSchema, String tableName)
      throws SQLException {

    DatabaseMetaData meta = conn.getMetaData();
    storesLowerCase = meta.storesLowerCaseIdentifiers();
    storesMixedCase = meta.storesMixedCaseIdentifiers();
    storesMixedCaseQuoted = meta.storesMixedCaseQuotedIdentifiers();
    supportsMixedCaseIdentifiers = meta.supportsMixedCaseIdentifiers();

    ResultSet rs = meta.getTables(oCatalog, oSchema, tableName, null);
    if (rs.next() && rs.next()) {
      throw DbException.get(ErrorCode.SCHEMA_NAME_MUST_MATCH, tableName);
    }
    rs.close();
    rs = meta.getColumns(null, null, tableName, null);
    int i = 0;
    ArrayList<Column> columnList = New.arrayList();
    HashMap<String, Column> columnMap = New.hashMap();
    String catalog = null, schema = null;
    while (rs.next()) {
      String thisCatalog = rs.getString("TABLE_CAT");
      if (catalog == null) {
        catalog = thisCatalog;
      }
      String thisSchema = rs.getString("TABLE_SCHEM");
      if (schema == null) {
        schema = thisSchema;
      }
      if (!StringUtils.equals(catalog, thisCatalog) || !StringUtils.equals(schema, thisSchema)) {
        // if the table exists in multiple schemas or tables,
        // use the alternative solution
        columnMap.clear();
        columnList.clear();
        break;
      }
      String n = rs.getString("COLUMN_NAME");
      n = convertColumnName(n);
      int sqlType = rs.getInt("DATA_TYPE");
      long precision = rs.getInt("COLUMN_SIZE");
      precision = convertPrecision(sqlType, precision);
      int scale = rs.getInt("DECIMAL_DIGITS");
      scale = convertScale(sqlType, scale);
      int displaySize = MathUtils.convertLongToInt(precision);
      int type = DataType.convertSQLTypeToValueType(sqlType);
      Column col = new Column(n, type, precision, scale, displaySize);
      col.setTable(this, i++);
      columnList.add(col);
      columnMap.put(n, col);
    }
    rs.close();
    // check if the table is accessible
    Statement stat = null;
    try {
      stat = conn.createStatement();
      rs = stat.executeQuery("SELECT * FROM " + tableName + " T WHERE 1=0");
      if (columnList.size() == 0) {
        // alternative solution
        ResultSetMetaData rsMeta = rs.getMetaData();
        for (i = 0; i < rsMeta.getColumnCount(); ) {
          String n = rsMeta.getColumnName(i + 1);
          n = convertColumnName(n);
          int sqlType = rsMeta.getColumnType(i + 1);
          long precision = rsMeta.getPrecision(i + 1);
          precision = convertPrecision(sqlType, precision);
          int scale = rsMeta.getScale(i + 1);
          scale = convertScale(sqlType, scale);
          int displaySize = rsMeta.getColumnDisplaySize(i + 1);
          int type = DataType.getValueTypeFromResultSet(rsMeta, i + 1);
          Column col = new Column(n, type, precision, scale, displaySize);
          col.setTable(this, i++);
          columnList.add(col);
          columnMap.put(n, col);
        }
      }
      rs.close();
    } catch (Exception e) {
      throw DbException.get(
          ErrorCode.TABLE_OR_VIEW_NOT_FOUND_1, e, tableName + "(" + e.toString() + ")");
    } finally {
      JdbcUtils.closeSilently(stat);
    }
    Column[] cols = new Column[columnList.size()];
    columnList.toArray(cols);
    setColumns(cols);
    // create scan index

    // load primary keys
    try {
      rs = meta.getPrimaryKeys(null, null, tableName);
    } catch (Exception e) {
      // Some ODBC bridge drivers don't support it:
      // some combinations of "DataDirect SequeLink(R) for JDBC"
      // http://www.datadirect.com/index.ssp
      rs = null;
    }
    String pkName = "";
    ArrayList<Column> list;
    if (rs != null && rs.next()) {
      // the problem is, the rows are not sorted by KEY_SEQ
      list = New.arrayList();
      do {
        int idx = rs.getInt("KEY_SEQ");
        if (pkName == null) {
          pkName = rs.getString("PK_NAME");
        }
        while (list.size() < idx) {
          list.add(null);
        }
        String col = rs.getString("COLUMN_NAME");
        col = convertColumnName(col);
        Column column = columnMap.get(col);
        if (idx == 0) {
          // workaround for a bug in the SQLite JDBC driver
          list.add(column);
        } else {
          list.set(idx - 1, column);
        }
      } while (rs.next());
      addIndex(pkName, list, IndexType.createPrimaryKey(false));
      rs.close();
    }

    try {
      rs = meta.getIndexInfo(null, null, tableName, false, true);
    } catch (Exception e) {
      // Oracle throws an exception if the table is not found or is a
      // SYNONYM
      rs = null;
    }
    String indexName = null;
    list = New.arrayList();
    IndexType indexType = null;
    if (rs != null) {
      while (rs.next()) {
        if (rs.getShort("TYPE") == DatabaseMetaData.tableIndexStatistic) {
          // ignore index statistics
          continue;
        }
        String newIndex = rs.getString("INDEX_NAME");
        if (pkName.equals(newIndex)) {
          continue;
        }
        if (indexName != null && !indexName.equals(newIndex)) {
          addIndex(indexName, list, indexType);
          indexName = null;
        }
        if (indexName == null) {
          indexName = newIndex;
          list.clear();
        }
        boolean unique = !rs.getBoolean("NON_UNIQUE");
        indexType = unique ? IndexType.createUnique(false) : IndexType.createNonUnique();
        String col = rs.getString("COLUMN_NAME");
        col = convertColumnName(col);
        Column column = columnMap.get(col);
        list.add(column);
      }
      rs.close();
    }
    if (indexName != null) {
      addIndex(indexName, list, indexType);
    }
    shardingKeyIndex();
  }
コード例 #3
0
public class ConnectionHolder implements ConnectionProvider {

  private final Session session;
  private final HolderStrategy holderStrategy;
  private final ConnectionProvider target;
  private final Trace trace;
  private Map<String, Connection> connectionMap = New.hashMap();
  private final Closer closer = new Closer();

  public ConnectionHolder(Session session) {
    Database database = session.getDatabase();
    JdbcRepository repository = (JdbcRepository) database.getRepository();
    this.session = session;
    this.trace = database.getTrace(Trace.TRANSACTION);
    this.target = repository.getConnectionProvider();
    String mode = database.getSettings().transactionMode;
    this.holderStrategy = transactionMode(mode);
  }

  public synchronized <T> List<T> foreach(Callback<T> callback) throws DbException {
    List<T> results = New.arrayList();
    for (String name : connectionMap.keySet()) {
      try {
        Connection conn = connectionMap.get(name);
        results.add(callback.handle(name, conn));
      } catch (SQLException e) {
        trace.error(e, "foreach {0} connection error", name);
        throw DbException.convert(e);
      }
    }
    return results;
  }

  public synchronized <T> List<T> foreach(Set<String> shards, Callback<T> callback)
      throws DbException {
    List<T> results = New.arrayList();
    for (String name : connectionMap.keySet()) {
      if (shards.contains(name)) {
        try {
          Connection conn = connectionMap.get(name);
          results.add(callback.handle(name, conn));
        } catch (SQLException e) {
          trace.error(e, "foreach {0} connection error", name);
          throw DbException.convert(e);
        }
      }
    }
    return results;
  }

  @Override
  public synchronized Connection getConnection(Options options) {
    Connection conn;
    if (session.getAutoCommit()) {
      conn = getRawConnection(options);
    } else {
      conn = getConnectionWithStrategy(options);
    }
    return conn;
  }

  @Override
  public synchronized void closeConnection(Connection connection, Options options) {
    if (!connectionMap.containsKey(options.shardName)) {
      target.closeConnection(connection, options);
    }
  }

  public synchronized boolean hasConnection() {
    return !connectionMap.isEmpty();
  }

  public synchronized List<String> closeAndClear() {
    List<String> foreach = foreach(closer);
    connectionMap.clear();
    return foreach;
  }

  /**
   * Each shard have a database connection, the worker thread may concurrently use a shard
   * connection executing SQL (such as executing ddl statement), If the JDBC driver is
   * spec-compliant, then technically yes, the object is thread-safe, MySQL Connector/J, all methods
   * to execute statements lock the connection with a synchronized block.
   *
   * @param options
   * @return
   */
  protected Connection getConnectionWithStrategy(Options options) {
    String shardName = options.shardName;
    Connection conn;
    switch (holderStrategy) {
      case STRICTLY:
        if (connectionMap.isEmpty()) {
          conn = getRawConnection(options);
          connectionMap.put(shardName, conn);
        } else {
          conn = connectionMap.get(shardName);
          if (conn == null) {
            String lastTransactionNode = connectionMap.keySet().iterator().next();
            throw DbException.get(
                ErrorCode.GENERAL_ERROR_1,
                "STRICTLY transaction mode not supported operation on multi-node, opend on "
                    + lastTransactionNode
                    + " and try on "
                    + shardName);
          }
        }
        break;
      case ALLOW_CROSS_SHARD_READ:
        if (options.readOnly) {
          conn = connectionMap.get(shardName);
          if (conn == null) {
            conn = getRawConnectionForReadOnly(options);
          }
        } else {
          if (connectionMap.isEmpty()) {
            conn = getRawConnection(options);
            connectionMap.put(shardName, conn);
          } else {
            conn = connectionMap.get(shardName);
            if (conn == null) {
              String lastTransactionNode = connectionMap.keySet().iterator().next();
              throw DbException.get(
                  ErrorCode.GENERAL_ERROR_1,
                  "ALLOW_READ_CROSS_DB transaction mode not supported writing operation on multi-node, opend on "
                      + lastTransactionNode
                      + " and try on "
                      + shardName);
            }
          }
        }

        break;

      case BESTEFFORTS_1PC:
        conn = connectionMap.get(shardName);
        if (conn == null) {
          conn = getRawConnection(options);
          connectionMap.put(shardName, conn);
        }
        break;

      default:
        throw DbException.getInvalidValueException("transactionMode", holderStrategy);
    }
    return conn;
  }

  /**
   * @param options
   * @return
   * @throws SQLException
   */
  private Connection getRawConnection(Options options) throws DbException {
    Connection conn = target.getConnection(options);
    try {
      if (conn.getAutoCommit() != session.getAutoCommit()) {
        conn.setAutoCommit(session.getAutoCommit());
      }
      if (session.getTransactionIsolation() != 0) {
        if (conn.getTransactionIsolation() != session.getTransactionIsolation()) {
          conn.setTransactionIsolation(session.getTransactionIsolation());
        }
      }
      if (conn.isReadOnly() != session.isReadOnly()) {
        conn.setReadOnly(session.isReadOnly());
      }
    } catch (Exception e) {
      throw DbException.convert(e);
    }
    return conn;
  }

  private Connection getRawConnectionForReadOnly(Options options) {
    try {
      Connection conn = target.getConnection(options);
      conn.setAutoCommit(true);
      conn.setReadOnly(true);
      if (session.getTransactionIsolation() != 0) {
        if (conn.getTransactionIsolation() != session.getTransactionIsolation()) {
          conn.setTransactionIsolation(session.getTransactionIsolation());
        }
      }
      return conn;
    } catch (SQLException e) {
      throw DbException.convert(e);
    }
  }

  private HolderStrategy transactionMode(String mode) {
    try {
      HolderStrategy holderStrategy =
          StringUtils.isNullOrEmpty(mode)
              ? HolderStrategy.BESTEFFORTS_1PC
              : HolderStrategy.valueOf(mode);
      return holderStrategy;
    } catch (Exception e) {
      throw DbException.getInvalidValueException("transactionMode", mode);
    }
  }

  public static interface Callback<T> {
    T handle(String shardName, Connection connection) throws SQLException;
  }

  private class Closer implements Callback<String> {
    @Override
    public String handle(String name, Connection connection) throws SQLException {
      try {
        target.closeConnection(connection, Options.build().shardName(name));
      } catch (Exception e) {
        trace.error(e, "Close {0} connection error", name);
        // throw DbException.convert(e);
      }
      return name;
    }
  };

  enum HolderStrategy {
    STRICTLY,
    ALLOW_CROSS_SHARD_READ,
    BESTEFFORTS_1PC
  }
}