3434
3535import java .sql .BatchUpdateException ;
3636import java .sql .ResultSet ;
37+ import java .sql .SQLWarning ;
38+ import java .util .Objects ;
3739import java .util .concurrent .CompletableFuture ;
3840import java .util .concurrent .atomic .AtomicBoolean ;
3941import java .util .function .BiFunction ;
4042import java .util .function .Function ;
4143import java .util .function .Predicate ;
4244import java .util .stream .LongStream ;
45+ import java .util .stream .Stream ;
4346
4447import static oracle .r2dbc .impl .OracleR2dbcExceptions .fromJdbc ;
4548import static oracle .r2dbc .impl .OracleR2dbcExceptions .requireNonNull ;
@@ -376,6 +379,20 @@ static OracleResultImpl createErrorResult(R2dbcException r2dbcException) {
376379 return new ErrorResult (r2dbcException );
377380 }
378381
382+ /**
383+ * Creates a {@code Result} that publishes a {@code warning} as a
384+ * {@link Message} segment, followed by any {@code Segment}s of a
385+ * {@code result}.
386+ * @param warning Warning to publish
387+ * @param result Result to publisher
388+ * @return A {@code Result} for a {@code Statement} execution that
389+ * completed with a warning.
390+ */
391+ static OracleResultImpl createWarningResult (
392+ SQLWarning warning , OracleResultImpl result ) {
393+ return new WarningResult (warning , result );
394+ }
395+
379396 /**
380397 * {@link OracleResultImpl} subclass that publishes a single update count. An
381398 * instance of this class constructed with negative valued update count
@@ -392,16 +409,37 @@ private UpdateCountResult(long updateCount) {
392409 @ Override
393410 <T > Publisher <T > publishSegments (Function <Segment , T > mappingFunction ) {
394411 return updateCount >= 0
395- ? Mono .just (mappingFunction .apply (new UpdateCountImpl (updateCount )))
412+ ? Mono .just (new UpdateCountImpl (updateCount ))
413+ .map (mappingFunction )
396414 : Mono .empty ();
397415 }
398416 }
399417
400418 /**
419+ * <p>
401420 * {@link OracleResultImpl} subclass that publishes JDBC {@link ResultSet} as
402421 * {@link RowSegment}s. {@link RowMetadata} of published {@code Rows} is
403422 * derived from the {@link java.sql.ResultSetMetaData} of the
404423 * {@link ResultSet}.
424+ * </p><p>
425+ * This {@code Result} is <i>not</i> implemented to publish
426+ * {@link SQLWarning} chains returned by {@link ResultSet#getWarnings()} as
427+ * {@link Message} segments. This implementation is correct for the 21.1
428+ * Oracle JDBC Driver which is known to implement {@code getWarnings()} by
429+ * returning {@code null} for forward-only insensitive {@code ResultSets}
430+ * when no invocation of {@link java.sql.Statement#setMaxRows(int)}
431+ * or {@link java.sql.Statement#setLargeMaxRows(long)} has occurred.
432+ * </p><p>
433+ * It is a known limitation of the 21.1 Oracle JDBC Driver that
434+ * {@link ResultSet#getWarnings()} can not be invoked after row publishing
435+ * has been initiated; The {@code ResultSet} is logically closed once row
436+ * publishing has been initiated, and so {@code getWarnings} would throw a
437+ * {@link java.sql.SQLException} to indicate a closed {@code ResultSet}. If
438+ * a later release of Oracle JDBC removes this limitation, then this
439+ * {@code Result} should be implemented to invoke {@code getWarnings} to
440+ * ensure correctness if a later release of Oracle JDBC also returns non-null
441+ * values from that method.
442+ * </p>
405443 */
406444 private static final class ResultSetResult extends OracleResultImpl {
407445
@@ -482,8 +520,8 @@ private BatchUpdateResult(long[] updateCounts) {
482520 @ Override
483521 <T > Publisher <T > publishSegments (Function <Segment , T > mappingFunction ) {
484522 return Flux .fromStream (LongStream .of (updateCounts )
485- .mapToObj (UpdateCountImpl ::new )
486- .map (mappingFunction )) ;
523+ .mapToObj (UpdateCountImpl ::new ))
524+ .map (mappingFunction );
487525 }
488526 }
489527
@@ -532,6 +570,35 @@ <T> Publisher<T> publishSegments(Function<Segment, T> mappingFunction) {
532570 }
533571 }
534572
573+ /**
574+ * {@link OracleResultImpl} subclass that publishes a {@link SQLWarning}
575+ * chain as {@link Message} segments, followed by the segments of another
576+ * {@link OracleResultImpl}.
577+ */
578+ private static final class WarningResult extends OracleResultImpl {
579+
580+ private final SQLWarning warning ;
581+ private final OracleResultImpl result ;
582+
583+ private WarningResult (SQLWarning warning , OracleResultImpl result ) {
584+ this .warning = warning ;
585+ this .result = result ;
586+ }
587+
588+ @ Override
589+ <T > Publisher <T > publishSegments (Function <Segment , T > mappingFunction ) {
590+ return Flux .fromStream (Stream .iterate (
591+ warning , Objects ::nonNull , SQLWarning ::getNextWarning )
592+ .map (OracleR2dbcExceptions ::toR2dbcException )
593+ .map (MessageImpl ::new ))
594+ .map (mappingFunction )
595+ // Invoke publishSegments(Class, Function) rather than
596+ // publishSegments(Function) to update the state of the result; Namely,
597+ // the state that has the onConsumed Publisher emit a terminal signal.
598+ .concatWith (result .publishSegments (Segment .class , mappingFunction ));
599+ }
600+ }
601+
535602
536603 /**
537604 * Common interface for instances of {@link Segment} with a {@link Readable}
0 commit comments