@Override
  public TaskReport run(TaskSource taskSource, Schema schema, int taskIndex, PageOutput output) {
    PluginTask task = taskSource.loadTask(getTaskClass());

    JdbcSchema querySchema = task.getQuerySchema();
    BufferAllocator allocator = task.getBufferAllocator();
    PageBuilder pageBuilder = new PageBuilder(allocator, schema, output);

    try {
      List<ColumnGetter> getters = newColumnGetters(task, querySchema, pageBuilder);

      try (JdbcInputConnection con = newConnection(task)) {
        try (BatchSelect cursor =
            con.newSelectCursor(
                getQuery(task, con), task.getFetchRows(), task.getSocketTimeout())) {
          while (true) {
            // TODO run fetch() in another thread asynchronously
            // TODO retry fetch() if it failed (maybe order_by is required and unique_column(s)
            // option is also required)
            boolean cont = fetch(cursor, getters, pageBuilder);
            if (!cont) {
              break;
            }
          }
        }
      }

    } catch (SQLException ex) {
      throw Throwables.propagate(ex);
    }
    pageBuilder.finish();

    TaskReport report = Exec.newTaskReport();
    // TODO
    // if (orderByColumn != null) {
    //    report.set("last_value", lastValue);
    // }
    return report;
  }