content: /*
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   *
   *     http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package org.apache.cassandra.distributed.api;
  
  import java.util.Arrays;
  import java.util.Collections;
  import java.util.List;
  import java.util.NoSuchElementException;
  import java.util.Objects;
  import java.util.function.Predicate;
  import java.util.stream.Collectors;
  import java.util.stream.Stream;
  
  /**
   * A table of data representing a complete query result.
   * <p>
   * A <code>QueryResult</code> is different from {@link java.sql.ResultSet} in several key ways:
   *
   * <ul>
   *     <li>represents a complete result rather than a cursor</li>
   *     <li>returns a {@link Row} to access the current row of data</li>
   *     <li>relies on object pooling; {@link #hasNext()} may return the same object just with different data, accessing a
   *     {@link Row} from a previous {@link #hasNext()} call has undefined behavior.</li>
   *     <li>includes {@link #filter(Predicate)}, this will do client side filtering since Apache Cassandra is more
   *     restrictive on server side filtering</li>
   * </ul>
   *
   * <h2>Unsafe patterns</h2>
   * <p>
   * Below are a few unsafe patterns which may lead to unexpected results
   *
   * <code>{@code
   * while (rs.hasNext()) {
   *   list.add(rs.next());
   * }
   * }</code>
   *
   * <code>{@code
   * rs.forEach(list::add)
   * }</code>
   * <p>
   * Both cases have the same issue; reference to a row from a previous call to {@link #hasNext()}.  Since the same {@link Row}
   * object can be used accross different calls to {@link #hasNext()} this would mean any attempt to access after the fact
   * points to newer data.  If this behavior is not desirable and access is needed between calls, then {@link Row#copy()}
   * should be used; this will clone the {@link Row} and return a new object pointing to the same data.
   */
  public class SimpleQueryResult implements QueryResult
  {
      private static final int NO_MARK = -1;
  
      private final String[] names;
      private final Object[][] results;
      private final List<String> warnings;
      private final Predicate<Row> filter;
      private final Row row;
      private int offset = NO_MARK;
      private int mark = NO_MARK;
  
      public SimpleQueryResult(String[] names, Object[][] results)
      {
          this(names, results, Collections.emptyList());
      }
  
      public SimpleQueryResult(String[] names, Object[][] results, List<String> warnings)
      {
          this.names = Objects.requireNonNull(names, "names");
          this.results = results;
          this.warnings = Objects.requireNonNull(warnings, "warnings");
          this.row = new Row(names);
          this.filter = ignore -> true;
      }
  
      private SimpleQueryResult(String[] names, Object[][] results, Predicate<Row> filter, int offset)
      {
          this.names = names;
          this.results = results;
          this.warnings = Collections.emptyList();
          this.filter = filter;
          this.offset = offset;
          this.row = new Row(names);
      }
  
      public List<String> names()
      {
          return Collections.unmodifiableList(Arrays.asList(names));
      }
  
      @Override
      public List<String> warnings()
      {
          return Collections.unmodifiableList(warnings);
      }
  
      public SimpleQueryResult filter(Predicate<Row> fn)
      {
          return new SimpleQueryResult(names, results, filter.and(fn), offset);
      }
  
      public void mark()
      {
          mark = offset;
      }
  
      /**
       * Reset the cursor to the start of the query result; if the query result has not been iterated, this has no effect.
       */
      public void reset()
      {
          offset = mark;
          mark = NO_MARK;
          row.setResults(null);
      }
  
      /**
       * Get all rows as a 2d array.  Any calls to {@link #filter(Predicate)} will be ignored and the array returned will
       * be the full set from the query.
       */
      public Object[][] toObjectArrays()
      {
          return results;
      }
  
      @Override
      public boolean hasNext()
      {
          if (results == null)
              return false;
          while ((offset += 1) < results.length)
          {
              row.setResults(results[offset]);
              if (filter.test(row))
              {
                  return true;
              }
          }
          row.setResults(null);
          return false;
      }
  
      @Override
      public Row next()
      {
          // no null check needed for results since offset only increments IFF results is not null
          if (offset < 0 || offset >= results.length)
              throw new NoSuchElementException();
          return row;
      }
  
      @Override
      public String toString() {
          if (results == null)
              return "[]";
          return Stream.of(results)
                       .map(Arrays::toString)
                       .collect(Collectors.joining(",", "[", "]"));
      }
  }
  