jdbc-protolib/src/main/java/com/streamsets/pipeline/lib/jdbc/JdbcGenericRecordWriter.java
private void handleBatchUpdateException(
      Collection<Record> failedRecords, SQLException e, List<OnRecordErrorException> errorRecords
  ) throws StageException {
    if (jdbcUtil.isDataError(getCustomDataSqlStateCodes(), getConnectionString(), e)) {
      String formattedError = JdbcErrors.JDBC_79.getMessage();
      LOG.error(formattedError);
      LOG.debug(formattedError, e);

      if (!getRollbackOnError() && e instanceof BatchUpdateException &&
          ((BatchUpdateException) e).getUpdateCounts().length > 0) {
        BatchUpdateException bue = (BatchUpdateException) e;

        int i = 0;
        for (Record record : failedRecords) {
          if (i >= bue.getUpdateCounts().length || bue.getUpdateCounts()[i] == PreparedStatement.EXECUTE_FAILED) {
            errorRecords.add(new OnRecordErrorException(
                record,
                JDBC_14,
                e.getSQLState(),
                e.getErrorCode(),
                e.getMessage(),
                jdbcUtil.formatSqlException(e),
                e
            ));
          }
          i++;
        }
      } else {
        for (Record record : failedRecords) {
          errorRecords.add(new OnRecordErrorException(
              record,
              JDBC_14,
              e.getSQLState(),
              e.getErrorCode(),
              e.getMessage(),
              jdbcUtil.formatSqlException(e),
              e
          ));
        }
      }
    } else {
      handleSqlException(e);
    }
  }
private void handleBatchUpdateException(
      Collection<Record> failedRecords, SQLException e, List<OnRecordErrorException> errorRecords
  ) throws StageException {
    if (jdbcUtil.isDataError(getCustomDataSqlStateCodes(), getConnectionString(), e)) {
      String formattedError = JdbcErrors.JDBC_79.getMessage();
      LOG.error(formattedError);
      LOG.debug(formattedError, e);

      if (!getRollbackOnError() && e instanceof BatchUpdateException &&
          ((BatchUpdateException) e).getUpdateCounts().length > 0) {
        BatchUpdateException bue = (BatchUpdateException) e;

        int i = 0;
        for (Record record : failedRecords) {
          if (i >= bue.getUpdateCounts().length || bue.getUpdateCounts()[i] == PreparedStatement.EXECUTE_FAILED) {
            errorRecords.add(new OnRecordErrorException(
                record,
                JDBC_14,
                e.getSQLState(),
                e.getErrorCode(),
                e.getMessage(),
                jdbcUtil.formatSqlException(e),
                e
            ));
          }
          i++;
        }
      } else {
        for (Record record : failedRecords) {
          errorRecords.add(new OnRecordErrorException(
              record,
              JDBC_14,
              e.getSQLState(),
              e.getErrorCode(),
              e.getMessage(),
              jdbcUtil.formatSqlException(e),
              e
          ));
        }
      }
    } else {
      handleSqlException(e);
    }
  }
<p>
Some databases drivers allow us to figure out which record in a particular batch failed.
</p>
<p>
In the case that we have a list of update counts, we can mark just the record as erroneous.
Otherwise we must send the entire batch to error.
</p>
@param failedRecords List of Failed Records
@param e BatchUpdateException
@param errorRecords List of error records for this batch