// Path: sane-dbc-core/src/main/java/com/novarto/sanedbc/core/ops/EffectOp.java
// public class EffectOp extends DB<Unit>
// {
// 
//     private final UpdateOp op;
// 
//     public EffectOp(String sql)
//     {
//         this.op = new UpdateOp(sql, NO_BINDER);
//     }
// 
//     @Override public Unit run(Connection c) throws SQLException
//     {
//         op.run(c);
//         return Unit.unit();
//     }
// }
// 
// Path: sane-dbc-core/src/main/java/com/novarto/sanedbc/core/ops/SelectOp.java
// public class SelectOp<A, C1 extends Iterable<A>, C2 extends Iterable<A>> extends AbstractSelectOp<C2>
// {
// 
//     private final CanBuildFrom<A, C1, C2> cbf;
//     private final Try1<ResultSet, A, SQLException> mapper;
// 
//     /**
//      *
//      * @param sql the query to execute
//      * @param binder a function to bind the PreparedStatement parameters
//      * @param mapper a function mapping a single ResultSet row to a single element of the result iterable. The function
//      *               must not advance or modify the ResultSet state, i.e. by calling next()
//      * @param cbf the CanBuildFrom to construct the result iterable
//      */
//     public SelectOp(String sql, TryEffect1<PreparedStatement, SQLException> binder,
//             Try1<ResultSet, A, SQLException> mapper, CanBuildFrom<A, C1, C2> cbf)
//     {
// 
//         super(sql, binder);
//         this.mapper = mapper;
//         this.cbf = cbf;
//     }
// 
//     @Override protected C2 doRun(ResultSet rs) throws SQLException
//     {
//         C1 buf = cbf.createBuffer();
// 
//         while (rs.next())
//         {
//             buf = cbf.add(mapper.f(rs), buf);
//         }
// 
// 
//         return cbf.build(buf);
//     }
// 
//     /**
//      * A convenience SelectOp specialized for java.util.List
//      */
//     public static final class List<A> extends SelectOp<A, java.util.List<A>, java.util.List<A>>
//     {
// 
//         public List(String sql, TryEffect1<PreparedStatement, SQLException> binder,
//                 Try1<ResultSet, A, SQLException> mapper)
//         {
//             super(sql, binder, mapper, CanBuildFrom.listCanBuildFrom());
//         }
// 
//     }
// 
//     /**
//      * A convenience SelectOp specialized for fj.data.List, utilizing a fj.data.List.Buffer as an intermediate result
//      */
//     public static final class FjList<A> extends SelectOp<A, fj.data.List.Buffer<A>, fj.data.List<A>>
//     {
// 
//         public FjList(String sql, TryEffect1<PreparedStatement, SQLException> binder,
//                 Try1<ResultSet, A, SQLException> mapper)
//         {
//             super(sql, binder, mapper, CanBuildFrom.fjListCanBuildFrom());
//         }
// 
//     }
// 
// }
// 
// Path: sane-dbc-core/src/main/java/com/novarto/sanedbc/core/ops/UpdateOp.java
// public class UpdateOp extends DB<Integer>
// {
//     private final String sql;
//     private final TryEffect1<PreparedStatement, SQLException> binder;
// 
//     public UpdateOp(String sql, TryEffect1<PreparedStatement, SQLException> binder)
//     {
//         this.sql = sql;
//         this.binder = binder;
//     }
// 
// 
//     @Override
//     public Integer run(Connection c) throws SQLException
//     {
//         try (PreparedStatement s = c.prepareStatement(sql))
//         {
//             binder.f(s);
//             return s.executeUpdate();
//         }
//     }
// }
// 
// Path: sane-dbc-core/src/main/java/com/novarto/sanedbc/core/ops/Binders.java
// public static final TryEffect1<PreparedStatement, SQLException> NO_BINDER = (s) -> {
// };


import com.novarto.sanedbc.core.ops.EffectOp;
import com.novarto.sanedbc.core.ops.SelectOp;
import com.novarto.sanedbc.core.ops.UpdateOp;
import fj.Unit;
import fj.control.db.DB;
import fj.data.List;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import static com.novarto.sanedbc.core.ops.Binders.NO_BINDER;
import static fj.data.List.arrayList;
import static fj.data.List.single;
import static fj.data.Validation.fail;
import static fj.data.Validation.success;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
                is(fail(ex))
        );

        assertThat(DB.submit(selectAll()).f(), is(success(single("x"))));

        SQLException noConn = new SQLException("no connection");
        ValidationDbInterpreter noConnection = new ValidationDbInterpreter(() -> {
            throw noConn;
        });

        assertThat(noConnection.transact(insert("alabala")).f(), is(fail(noConn)));

        assertThat(DB.submit(selectAll()).f(), is(success(single("x"))));

        assertThat(
                DB.transact(insert("y").bind(ignore -> insert("z")).map(ignore2 -> Unit.unit())).f(),
                is(success(Unit.unit()))
        );

        assertThat(DB.submit(selectAll()).f(), is(success(arrayList("x", "y", "z"))));

    }

    private DB<Integer> insert(String x)
    {
        return new UpdateOp("INSERT INTO BAR VALUES(?)", ps -> ps.setString(1, x));
    }

    private DB<List<String>> selectAll()
    {
// Path: sane-dbc-core/src/main/java/com/novarto/sanedbc/core/ops/EffectOp.java
// public class EffectOp extends DB<Unit>
// {
// 
//     private final UpdateOp op;
// 
//     public EffectOp(String sql)
//     {
//         this.op = new UpdateOp(sql, NO_BINDER);
//     }
// 
//     @Override public Unit run(Connection c) throws SQLException
//     {
//         op.run(c);
//         return Unit.unit();
//     }
// }
// 
// Path: sane-dbc-core/src/main/java/com/novarto/sanedbc/core/ops/SelectOp.java
// public class SelectOp<A, C1 extends Iterable<A>, C2 extends Iterable<A>> extends AbstractSelectOp<C2>
// {
// 
//     private final CanBuildFrom<A, C1, C2> cbf;
//     private final Try1<ResultSet, A, SQLException> mapper;
// 
//     /**
//      *
//      * @param sql the query to execute
//      * @param binder a function to bind the PreparedStatement parameters
//      * @param mapper a function mapping a single ResultSet row to a single element of the result iterable. The function
//      *               must not advance or modify the ResultSet state, i.e. by calling next()
//      * @param cbf the CanBuildFrom to construct the result iterable
//      */
//     public SelectOp(String sql, TryEffect1<PreparedStatement, SQLException> binder,
//             Try1<ResultSet, A, SQLException> mapper, CanBuildFrom<A, C1, C2> cbf)
//     {
// 
//         super(sql, binder);
//         this.mapper = mapper;
//         this.cbf = cbf;
//     }
// 
//     @Override protected C2 doRun(ResultSet rs) throws SQLException
//     {
//         C1 buf = cbf.createBuffer();
// 
//         while (rs.next())
//         {
//             buf = cbf.add(mapper.f(rs), buf);
//         }
// 
// 
//         return cbf.build(buf);
//     }
// 
//     /**
//      * A convenience SelectOp specialized for java.util.List
//      */
//     public static final class List<A> extends SelectOp<A, java.util.List<A>, java.util.List<A>>
//     {
// 
//         public List(String sql, TryEffect1<PreparedStatement, SQLException> binder,
//                 Try1<ResultSet, A, SQLException> mapper)
//         {
//             super(sql, binder, mapper, CanBuildFrom.listCanBuildFrom());
//         }
// 
//     }
// 
//     /**
//      * A convenience SelectOp specialized for fj.data.List, utilizing a fj.data.List.Buffer as an intermediate result
//      */
//     public static final class FjList<A> extends SelectOp<A, fj.data.List.Buffer<A>, fj.data.List<A>>
//     {
// 
//         public FjList(String sql, TryEffect1<PreparedStatement, SQLException> binder,
//                 Try1<ResultSet, A, SQLException> mapper)
//         {
//             super(sql, binder, mapper, CanBuildFrom.fjListCanBuildFrom());
//         }
// 
//     }
// 
// }
// 
// Path: sane-dbc-core/src/main/java/com/novarto/sanedbc/core/ops/UpdateOp.java
// public class UpdateOp extends DB<Integer>
// {
//     private final String sql;
//     private final TryEffect1<PreparedStatement, SQLException> binder;
// 
//     public UpdateOp(String sql, TryEffect1<PreparedStatement, SQLException> binder)
//     {
//         this.sql = sql;
//         this.binder = binder;
//     }
// 
// 
//     @Override
//     public Integer run(Connection c) throws SQLException
//     {
//         try (PreparedStatement s = c.prepareStatement(sql))
//         {
//             binder.f(s);
//             return s.executeUpdate();
//         }
//     }
// }
// 
// Path: sane-dbc-core/src/main/java/com/novarto/sanedbc/core/ops/Binders.java
// public static final TryEffect1<PreparedStatement, SQLException> NO_BINDER = (s) -> {
// };


// Path: sane-dbc-core/src/test/java/com/novarto/sanedbc/core/interpreter/ValidationDbInterpreterTest.java
import com.novarto.sanedbc.core.ops.EffectOp;
import com.novarto.sanedbc.core.ops.SelectOp;
import com.novarto.sanedbc.core.ops.UpdateOp;
import fj.Unit;
import fj.control.db.DB;
import fj.data.List;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import static com.novarto.sanedbc.core.ops.Binders.NO_BINDER;
import static fj.data.List.arrayList;
import static fj.data.List.single;
import static fj.data.Validation.fail;
import static fj.data.Validation.success;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;

                is(fail(ex))
        );

        assertThat(DB.submit(selectAll()).f(), is(success(single("x"))));

        SQLException noConn = new SQLException("no connection");
        ValidationDbInterpreter noConnection = new ValidationDbInterpreter(() -> {
            throw noConn;
        });

        assertThat(noConnection.transact(insert("alabala")).f(), is(fail(noConn)));

        assertThat(DB.submit(selectAll()).f(), is(success(single("x"))));

        assertThat(
                DB.transact(insert("y").bind(ignore -> insert("z")).map(ignore2 -> Unit.unit())).f(),
                is(success(Unit.unit()))
        );

        assertThat(DB.submit(selectAll()).f(), is(success(arrayList("x", "y", "z"))));

    }

    private DB<Integer> insert(String x)
    {
        return new UpdateOp("INSERT INTO BAR VALUES(?)", ps -> ps.setString(1, x));
    }

    private DB<List<String>> selectAll()
    {
        return new SelectOp.FjList<>("SELECT * FROM BAR", NO_BINDER, rs -> rs.getString(1));