<commit_before>package com.github.fjdbc.query;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;

import com.github.fjdbc.util.Consumers;
import com.github.fjdbc.util.FjdbcUtil;

/**
 * Extract objects from a ResultSet. The object may map to one or many rows from the result set.
 */
@FunctionalInterface
public interface ResultSetExtractor<T> {
	T extract(ResultSet rs) throws SQLException;

	default void extractAll(ResultSet rs, Consumer<? super T> callback) throws SQLException {
		while (rs.next()) {
			final T object = extract(rs);
			callback.accept(object);
		}
	}

	default List<T> extractAll(ResultSet rs) throws SQLException {
		final List<T> res = new ArrayList<>();
		extractAll(rs, Consumers.toList(res));
		return res;
	}

	default Iterator<T> iterator(ResultSet rs) {
		return new ResultSetIterator<>(rs, this);
	}

	/**
	 * 
	 * @author a507831
	 *
	 * @param <T>
	 */
	public static class ResultSetIterator<T> implements Iterator<T> {
		private final ResultSet rs;
		private final ResultSetExtractor<T> extractor;
		private boolean _hasNext = true;
		/**
		 * True if resultSet.next() has been called at least once, false otherwise.
		 */
		private boolean nextCalled = false;

		public ResultSetIterator(ResultSet rs, ResultSetExtractor<T> extractor) {
			this.rs = rs;
			this.extractor = extractor;
		}

		@Override
		public boolean hasNext() {
			try {
				// need to call next() at least once otherwise isLast doesn't work (MySql).
				if (!nextCalled) {
					final boolean next = rs.next();
					nextCalled = true;
					if (!next) {
						FjdbcUtil.close(null, null, rs);
						_hasNext = false;
						return false;
					}
				}
				final boolean hasNext = _hasNext && !rs.isAfterLast() && !rs.isClosed() && !rs.isLast();
				if (!hasNext) FjdbcUtil.close(null, null, rs);
				return hasNext;
			} catch (final SQLException e) {
				FjdbcUtil.close(null, null, rs);
				throw new RuntimeException(e);
			}
		}

		@Override
		public T next() {
			try {
				final boolean next = rs.next();
				nextCalled = true;
				if (!next) {
					FjdbcUtil.close(null, null, rs);
					_hasNext = false;
					return null;
				}
				return extractor.extract(rs);
			} catch (final SQLException e) {
				FjdbcUtil.close(null, null, rs);
				throw new RuntimeException(e);
			}
		}

	}

}<commit_msg>fix: do not close result set until hasNext call<commit_after>package com.github.fjdbc.query;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.function.Consumer;

import com.github.fjdbc.util.Consumers;
import com.github.fjdbc.util.FjdbcUtil;

/**
 * Extract objects from a ResultSet. The object may map to one or many rows from the result set.
 */
@FunctionalInterface
public interface ResultSetExtractor<T> {
	T extract(ResultSet rs) throws SQLException;

	default void extractAll(ResultSet rs, Consumer<? super T> callback) throws SQLException {
		while (rs.next()) {
			final T object = extract(rs);
			callback.accept(object);
		}
	}

	default List<T> extractAll(ResultSet rs) throws SQLException {
		final List<T> res = new ArrayList<>();
		extractAll(rs, Consumers.toList(res));
		return res;
	}

	default Iterator<T> iterator(ResultSet rs) {
		return new ResultSetIterator<>(rs, this);
	}

	/**
	 * An iterator backed by a ResultSet. The ResultSet is closed when the last element is read.
	 * 
	 * @param <T>
	 */
	public static class ResultSetIterator<T> implements Iterator<T> {
		private final ResultSet rs;
		private final ResultSetExtractor<T> extractor;
		private boolean _hasNext = true;
		/**
		 * True if resultSet.next() has been called at least once, false otherwise.
		 */
		private boolean nextCalled = false;

		public ResultSetIterator(ResultSet rs, ResultSetExtractor<T> extractor) {
			this.rs = rs;
			this.extractor = extractor;
		}

		@Override
		public boolean hasNext() {
			try {
				// need to call next() at least once otherwise isLast doesn't work (MySql).
				if (!nextCalled) {
					final boolean next = rs.next();
					nextCalled = true;
					if (!next) {
						FjdbcUtil.close(null, null, rs);
						_hasNext = false;
						return false;
					}
				}
				final boolean hasNext = _hasNext && !rs.isLast() && !rs.isAfterLast() && !rs.isClosed();
				if (!hasNext) FjdbcUtil.close(null, null, rs);
				return hasNext;
			} catch (final SQLException e) {
				FjdbcUtil.close(null, null, rs);
				throw new RuntimeException(e);
			}
		}

		@Override
		public T next() {
			try {
				final boolean next = rs.next();
				nextCalled = true;
				if (!next) {
					_hasNext = false;
					return null;
				}
				return extractor.extract(rs);
			} catch (final SQLException e) {
				FjdbcUtil.close(null, null, rs);
				throw new RuntimeException(e);
			}
		}

	}

}
{'fraction_non_alphanumeric': 0.11636880584890333, 'fraction_numerical': 0.0012185215272136475, 'mean_word_length': 3.3777777777777778, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, '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': '5627314', 'n_tokens_mistral': 1864, 'n_tokens_neox': 1557, 'n_words': 461}