// Path: src/main/java/com/github/pgasync/PgColumn.java
// public class PgColumn {
// 
//     private final int index;
//     private final String name;
//     private final Oid type;
// 
//     public PgColumn(int index, String name, Oid type) {
//         this.index = index;
//         this.name = name;
//         this.type = type;
//     }
// 
//     public Oid getType() {
//         return type;
//     }
// 
//     public String getName() {
//         return name;
//     }
// 
//     public int at() {
//         return index;
//     }
// }
// 
// Path: src/main/java/com/github/pgasync/PgResultSet.java
// public class PgResultSet implements ResultSet {
// 
//     private final List<Row> rows;
//     private final Map<String, PgColumn> columnsByName;
//     private final List<PgColumn> orderedColumns;
//     private final int affectedRows;
// 
//     public PgResultSet(Map<String, PgColumn> columnsByName, List<PgColumn> orderedColumns, List<Row> rows, int affectedRows) {
//         this.columnsByName = columnsByName;
//         this.orderedColumns = orderedColumns;
//         this.rows = rows;
//         this.affectedRows = affectedRows;
//     }
// 
//     @Override
//     public Map<String, PgColumn> getColumnsByName() {
//         return columnsByName != null ? columnsByName : Map.of();
//     }
// 
//     @Override
//     public List<PgColumn> getOrderedColumns() {
//         return orderedColumns;
//     }
// 
//     @Override
//     public Iterator<Row> iterator() {
//         return rows != null ? rows.iterator() : Collections.emptyIterator();
//     }
// 
//     @Override
//     public Row at(int index) {
//         if (rows == null) {
//             throw new IndexOutOfBoundsException();
//         }
//         return rows.get(index);
//     }
// 
//     @Override
//     public int size() {
//         return rows != null ? rows.size() : 0;
//     }
// 
//     @Override
//     public int affectedRows() {
//         return affectedRows;
//     }
// 
// }


import com.github.pgasync.PgColumn;
import com.github.pgasync.PgResultSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
     */
    CompletableFuture<Void> script(BiConsumer<Map<String, PgColumn>, PgColumn[]> onColumns, Consumer<Row> onRow, Consumer<Integer> onAffected, String sql);

    /**
     * Sends single query with parameters. Uses extended query protocol of Postgres.
     * Accumulates fetched columns, rows and affected rows count into memory and transforms them into a {@link ResultSet} when it is fetched.
     * Completes returned {@link CompletableFuture} when the whole process of {@link ResultSet} fetching ends.
     *
     * @param sql    Sql query text with parameters substituted with ?.
     * @param params Parameters of the query.
     * @return CompletableFuture of {@link ResultSet}.
     */
    default CompletableFuture<ResultSet> completeQuery(String sql, Object... params) {
        class ResultSetAssembly {
            private Map<String, PgColumn> columnsByName;
            private PgColumn[] orderedColumns;
            private List<Row> rows;
        }
        ResultSetAssembly assembly = new ResultSetAssembly();
        return query(
                (columnsByName, orderedColumns) -> {
                    assembly.columnsByName = columnsByName;
                    assembly.orderedColumns = orderedColumns;
                    assembly.rows = new ArrayList<>();
                },
                row -> assembly.rows.add(row),
                sql,
                params
        )
                .thenApply(affected -> new PgResultSet(
// Path: src/main/java/com/github/pgasync/PgColumn.java
// public class PgColumn {
// 
//     private final int index;
//     private final String name;
//     private final Oid type;
// 
//     public PgColumn(int index, String name, Oid type) {
//         this.index = index;
//         this.name = name;
//         this.type = type;
//     }
// 
//     public Oid getType() {
//         return type;
//     }
// 
//     public String getName() {
//         return name;
//     }
// 
//     public int at() {
//         return index;
//     }
// }
// 
// Path: src/main/java/com/github/pgasync/PgResultSet.java
// public class PgResultSet implements ResultSet {
// 
//     private final List<Row> rows;
//     private final Map<String, PgColumn> columnsByName;
//     private final List<PgColumn> orderedColumns;
//     private final int affectedRows;
// 
//     public PgResultSet(Map<String, PgColumn> columnsByName, List<PgColumn> orderedColumns, List<Row> rows, int affectedRows) {
//         this.columnsByName = columnsByName;
//         this.orderedColumns = orderedColumns;
//         this.rows = rows;
//         this.affectedRows = affectedRows;
//     }
// 
//     @Override
//     public Map<String, PgColumn> getColumnsByName() {
//         return columnsByName != null ? columnsByName : Map.of();
//     }
// 
//     @Override
//     public List<PgColumn> getOrderedColumns() {
//         return orderedColumns;
//     }
// 
//     @Override
//     public Iterator<Row> iterator() {
//         return rows != null ? rows.iterator() : Collections.emptyIterator();
//     }
// 
//     @Override
//     public Row at(int index) {
//         if (rows == null) {
//             throw new IndexOutOfBoundsException();
//         }
//         return rows.get(index);
//     }
// 
//     @Override
//     public int size() {
//         return rows != null ? rows.size() : 0;
//     }
// 
//     @Override
//     public int affectedRows() {
//         return affectedRows;
//     }
// 
// }


// Path: src/main/java/com/pgasync/QueryExecutor.java
import com.github.pgasync.PgColumn;
import com.github.pgasync.PgResultSet;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.function.Consumer;

     */
    CompletableFuture<Void> script(BiConsumer<Map<String, PgColumn>, PgColumn[]> onColumns, Consumer<Row> onRow, Consumer<Integer> onAffected, String sql);

    /**
     * Sends single query with parameters. Uses extended query protocol of Postgres.
     * Accumulates fetched columns, rows and affected rows count into memory and transforms them into a {@link ResultSet} when it is fetched.
     * Completes returned {@link CompletableFuture} when the whole process of {@link ResultSet} fetching ends.
     *
     * @param sql    Sql query text with parameters substituted with ?.
     * @param params Parameters of the query.
     * @return CompletableFuture of {@link ResultSet}.
     */
    default CompletableFuture<ResultSet> completeQuery(String sql, Object... params) {
        class ResultSetAssembly {
            private Map<String, PgColumn> columnsByName;
            private PgColumn[] orderedColumns;
            private List<Row> rows;
        }
        ResultSetAssembly assembly = new ResultSetAssembly();
        return query(
                (columnsByName, orderedColumns) -> {
                    assembly.columnsByName = columnsByName;
                    assembly.orderedColumns = orderedColumns;
                    assembly.rows = new ArrayList<>();
                },
                row -> assembly.rows.add(row),
                sql,
                params
        )
                .thenApply(affected -> new PgResultSet(
                        assembly.columnsByName != null ? assembly.columnsByName : Map.of(),