java.sql2 DynamicMultiOperation with interlaced exceptions

Lukas Eder lukas.eder at gmail.com
Mon Oct 9 16:25:19 UTC 2017


Yes, that would be your original suggestion again, plus the fact that the
two onError methods mutually exclude each other. I think that's the best
approach given the current idea of the Operation type hierarchy.

An alternative might be to remove onError() from Operation entirely,
because the direct Operation subtypes might not have a comparable error
handler in common. For instance, we could have a similar discussion about
BatchCountOperation, in case of which there might be errors on a
per-batch-element basis.

I think this discussion might be a good point in favour of re-thinking the
entire type hierarchy again. I think this was criticised in a different
discussion, recently:
http://mail.openjdk.java.net/pipermail/jdbc-spec-discuss/2017-October/000102.html

I currently don't have a solution for this, but I do agree with Jens that
the feeling is: Operation currently does too much.

2017-10-09 18:11 GMT+02:00 Douglas Surber <douglas.surber at oracle.com>:

> How about this:
>
>   - if onError(BiConsumer) is called all errors are passed to that handler
>   - if onError(Consumer) is called all errors are passed to that handler
>   - if both are called whichever is called second throws
> IllegalStateException
>
> Douglas
>
> On Oct 9, 2017, at 8:33 AM, Lukas Eder <lukas.eder at gmail.com> wrote:
>
> Yes, I had seen that inherited onError() method, which is why I raised the
> concern:
>
> > However, now I wonder how the new
> > DynamicMultiOperation.onError(BiConsumer<Integer, Throwable>)
> > method would communicate with the pre-existing
> > Operation.onError(Consumer<Throable>) method...
>
> Specifically, because the new one has a useful Integer argument indicating
> what the result number is. If that is dropped, then the inherited onError()
> method no longer has such an Integer argument, which is a bit inconsistent.
> Also, this leads to another question: Will those Integer indexes be skipped
> onError, or not?
>
> 2017-10-09 17:04 GMT+02:00 Douglas Surber <douglas.surber at oracle.com>:
>
>> A DynamicMultiOperation is an Operation and so inherits onError. We can
>> specify that the onError handler is called for any error and after it
>> returns processing continues instead of completing the Operation
>> exceptionally.
>>
>> Douglas
>>
>> On Oct 9, 2017, at 5:43 AM, Lukas Eder <lukas.eder at gmail.com> wrote:
>>
>> Hi Douglas,
>>
>> I'm not sure if this is an improvement over the previous version. Again,
>> my T-SQL example:
>>
>> DECLARE @t TABLE(i INT);
>> INSERT INTO @t VALUES (1),(2),(3);
>> RAISERROR('message 1', 16, 2, 3);
>> RAISERROR('message 2', 16, 2, 3);
>> SELECT * FROM @t
>> RAISERROR('message 3', 16, 2, 3);
>>
>> As you can see, in such a batch, there is no strict notion of an error
>> belonging to a count operation, or to a result operation. It's just an
>> error. There might as well be no result at all, yet there could still be
>> errors. E.g. when you run this:
>>
>> RAISERROR('message 1', 16, 2, 3);
>> RAISERROR('message 2', 16, 2, 3);
>> RAISERROR('message 3', 16, 2, 3);
>>
>> In the presence of both onCount and onRows handlers, would you pass the
>> errors along with a synthetic count or a synthetic result, or both? What if
>> there are several errors? Neither seems very compelling to me. There are
>> three distinct types of result, which are not strictly connected:
>>
>> - Results / rows
>> - Update counts
>> - Errors
>>
>> Oh, and there are warnings too (SQLWarning), which are a special kind of
>> error, which I currently don't see in the API.
>>
>> Lukas
>>
>> 2017-10-05 17:40 GMT+02:00 Douglas Surber <douglas.surber at oracle.com>:
>>
>>> Lukas,
>>>
>>> On further thought, the onError handler is not needed. Each Operation
>>> has an error handler so the onCount and onRows handlers have an opportunity
>>> to configure an error handler specific to a result.
>>>
>>>   conn.dynamicMultiOperation(sql)
>>>     .onCount( (n, op) -> {
>>>       op.onError( t -> handle(t) )
>>>       ….
>>>         .submit();
>>>     })
>>>   .submit();
>>>
>>> Given the design guideline that one way to do things is enough, I have
>>> eliminated the onError handler. I do like having separate onCount and
>>> onRows handlers so that is retained.
>>>
>>> Douglas
>>>
>>> On Oct 5, 2017, at 8:12 AM, Douglas Surber <DOUGLAS.SURBER at oracle.com>
>>> wrote:
>>>
>>> Lukas,
>>>
>>> If there is no handler for a particular result, e.g. no onRows handler
>>> and a rows result is returned, that result is ignored. If an error is
>>> limited to a specific result it would be passed to the onError handler if
>>> one exists. If there is no onError handler, such an error is ignored. If an
>>> error is not limited to a specific result then the Operation is completed
>>> exceptionally.
>>>
>>> Does that work?
>>>
>>> Does there need to be a way to abort processing subsequent results? What
>>> happens when a handler throws an exception?
>>>
>>> Douglas
>>>
>>>
>>> On Oct 5, 2017, at 2:14 AM, Lukas Eder <lukas.eder at gmail.com> wrote:
>>>
>>> I like your suggestion of having three separate handlers (which may
>>> point to the same handler if the API user so desires). It is definitely
>>> cleaner than having a three-arg handler.
>>>
>>> In classic JDBC, the relevant call is statement.getMoreResults() and the
>>> user is free to completely ignore the result, e.g. by not calling
>>> statement.getResultSet() or statement.getUpdateCount(). However, now I
>>> wonder how the new DynamicMultiOperation.onError(BiConsumer<Integer,
>>> Throwable>) method would communicate with the pre-existing
>>> Operation.onError(Consumer<Throable>) method...
>>>
>>> 2017-10-04 18:21 GMT+02:00 Douglas Surber <douglas.surber at oracle.com>:
>>>
>>>> Interlaced errors was not considered. It would be simple to add a
>>>> Throwable as a third arg to ResultHandler.handle. Having two args of which
>>>> one was always null is a bit of a hack. Three is definitely a hack. So I
>>>> would eliminate ResultHandler and add the following methods to
>>>> DynamicMultiOperation:
>>>>
>>>>   public DynamicMultiOperation<T> onCount(BiConsumer<Integer,
>>>> CountOperation<T>> handler);
>>>>   public DynamicMultiOperation<T> onRows(BiConsumer<Integer,
>>>> RowOperation<T>> handler);
>>>>   public DynamicMultiOperation<T> onError(BiConsumer<Integer,
>>>> Throwable> handler);
>>>>
>>>> If there is no handler for a result the result is ignored or the
>>>> Operation completes exceptionally. Not sure which would be better.
>>>>
>>>> Douglas
>>>>
>>>>
>>>> > On Oct 4, 2017, at 2:22 AM, Lukas Eder <lukas.eder at gmail.com> wrote:
>>>> >
>>>> > Hello,
>>>> >
>>>> > The java.sql2 API contains a DynamicMultiOperation type which seems
>>>> > intended for use with statement batches or procedure calls that
>>>> produce an
>>>> > unknown set of:
>>>> >
>>>> > - Update counts
>>>> > - Result sets
>>>> > - Exceptions
>>>> >
>>>> > Unfortunately, I'm missing the part where interlaced exceptions can be
>>>> > received. This may be SQL Server (and perhaps MySQL) specific, as I
>>>> haven't
>>>> > really encountered this in any other databases. But in SQL Server,
>>>> when
>>>> > running the following statement batch:
>>>> >
>>>> > DECLARE @t TABLE(i INT);
>>>> > INSERT INTO @t VALUES (1),(2),(3);
>>>> > RAISERROR('message 1', 16, 2, 3);
>>>> > RAISERROR('message 2', 16, 2, 3);
>>>> > SELECT * FROM @t
>>>> > RAISERROR('message 3', 16, 2, 3);
>>>> >
>>>> > We can run the following, complete program (taken from a blog post
>>>> I've
>>>> > written recently [1]):
>>>> >
>>>> > String sql = "DECLARE @t TABLE(i INT);\r\n" +
>>>> >    "INSERT INTO @t VALUES (1),(2),(3);\r\n" +
>>>> >    "RAISERROR('message 1', 16, 2, 3);\r\n" +
>>>> >    "RAISERROR('message 2', 16, 2, 3);\r\n" +
>>>> >    "SELECT * FROM @t\r\n" +
>>>> >    "RAISERROR('message 3', 16, 2, 3);";
>>>> > try (Connection c = new
>>>> > com.microsoft.sqlserver.jdbc.SQLServerDriver().connect(url,
>>>> properties);
>>>> >     Statement s = c.createStatement()) {
>>>> >
>>>> >    fetchLoop:
>>>> >        for (int i = 0, updateCount = 0; i < 256; i++) {
>>>> >            try {
>>>> >                boolean result = (i == 0)
>>>> >                    ? s.execute(sql)
>>>> >                    : s.getMoreResults();
>>>> >
>>>> >                SQLWarning w = s.getWarnings();
>>>> >                for (int j = 0; j < 255 && w != null; j++) {
>>>> >                    System.out.println("Warning     : " +
>>>> w.getMessage());
>>>> >                    w = w.getNextWarning();
>>>> >                }
>>>> >
>>>> >                s.clearWarnings();
>>>> >
>>>> >                if (result)
>>>> >                    try (ResultSet rs = s.getResultSet()) {
>>>> >                        System.out.println("Result      :");
>>>> >                        ResultSetMetaData m = rs.getMetaData();
>>>> >
>>>> >                        while (rs.next())
>>>> >                            for (int col = 1; col <=
>>>> m.getColumnCount();
>>>> > col++)
>>>> >                                System.out.println(
>>>> >                                    "  " + m.getColumnName(col) +
>>>> >                                    ": " + rs.getInt(col));
>>>> >                    }
>>>> >                else if ((updateCount = s.getUpdateCount()) != -1)
>>>> >                    System.out.println("Update Count: " + updateCount);
>>>> >                else
>>>> >                    break fetchLoop;
>>>> >                }
>>>> >            catch (SQLException e) {
>>>> >                System.out.println("Exception   : " + e.getMessage());
>>>> >            }
>>>> >        }
>>>> > }
>>>> >
>>>> >
>>>> > And get this result:
>>>> >
>>>> > Update Count: 3
>>>> > Exception   : message 1
>>>> > Exception   : message 2
>>>> > Result      :
>>>> >  i: 1
>>>> >  i: 2
>>>> >  i: 3
>>>> > Exception   : message 3
>>>> >
>>>> > I currently don't see a way how the exception messages "message 1" /
>>>> > "message 2" / "message 3" can be fetched through the
>>>> DynamicMultiOperation.
>>>> > Could you perhaps show an example?
>>>> >
>>>> > Thanks,
>>>> > Lukas
>>>> >
>>>> > [1]:
>>>> > https://blog.jooq.org/2017/07/13/how-i-incorrectly-
>>>> fetched-jdbc-resultsets-again/
>>>> <https://urldefense.proofpoint.com/v2/url?u=https-3A__blog.jooq.org_2017_07_13_how-2Di-2Dincorrectly-2Dfetched-2Djdbc-2Dresultsets-2Dagain_&d=DwMFaQ&c=RoP1YumCXCgaWHvlZYR8PQcxBKCX5YTpkKY057SbK10&r=ChRVNBZ3Ru5F7CzL9kG_sNBRUO0uuqD6z6ltcMO-LbA&m=56RBDwLoPx1hjE7ozVe90yWivV6VyNiMfXWMyf3pnLw&s=jCcOZl10pZh4vO0d15g0aat2s7O9RrEPwMWcP8IVfl0&e=>
>>>>
>>>
>


More information about the jdbc-spec-discuss mailing list