Giter VIP home page Giter VIP logo

r2dbc-mssql's Introduction

Reactive Relational Database Connectivity Microsoft SQL Server Implementation Java CI with Maven Maven Central

This project contains the Microsoft SQL Server implementation of the R2DBC SPI. This implementation is not intended to be used directly, but rather to be used as the backing implementation for a humane client library to delegate to

This driver provides the following features:

  • Complies with R2DBC 1.0
  • Login with username/password with temporary SSL encryption
  • Full SSL encryption support (for e.g. Azure usage).
  • Transaction Control
  • Simple execution of SQL batches (direct and cursored execution)
  • Execution of parametrized statements (direct and cursored execution)
  • Extensive type support (including TEXT, VARCHAR(MAX), IMAGE, VARBINARY(MAX) and national variants, see below for exceptions)
  • Execution of stored procedures

Next steps:

  • Add support for TVP and UDTs

Code of Conduct

This project is governed by the R2DBC Code of Conduct. By participating, you are expected to uphold this code of conduct. Please report unacceptable behavior to [email protected].

Getting Started

Here is a quick teaser of how to use R2DBC MSSQL in Java:

URL Connection Factory Discovery

ConnectionFactory connectionFactory = ConnectionFactories.get("r2dbc:mssql://<host>:1433/<database>");

Publisher<? extends Connection> connectionPublisher = connectionFactory.create();

Programmatic Connection Factory Discovery

ConnectionFactoryOptions options = builder()
    .option(DRIVER, "sqlserver")
    .option(HOST, "…")
    .option(PORT, …)  // optional, defaults to 1433
    .option(USER, "…")
    .option(PASSWORD, "…")
    .option(DATABASE, "…") // optional
    .option(SSL, true) // optional, defaults to false
    .option(Option.valueOf("applicationName"), "…") // optional
    .option(Option.valueOf("preferCursoredExecution"), true/false) // optional
    .option(Option.valueOf("connectionId"), new UUID(…)) // optional
    .build();

ConnectionFactory connectionFactory = ConnectionFactories.get(options);

Publisher<? extends Connection> connectionPublisher = connectionFactory.create();

// Alternative: Creating a Mono using Project Reactor
Mono<Connection> connectionMono = Mono.from(connectionFactory.create());

Supported ConnectionFactory Discovery Options

Option Description
ssl Whether to use transport-level encryption for the entire SQL server traffic.
driver Must be sqlserver.
host Server hostname to connect to.
port Server port to connect to. Defaults to 1433. (Optional)
username Login username.
password Login password.
database Initial database to select. Defaults to SQL Server user profile settings. (Optional)
applicationName Name of the application. Defaults to driver name and version. (Optional)
connectionId Connection Id for tracing purposes. Defaults to a random Id. (Optional)
connectTimeout Connection Id for tracing purposes. Defaults to 30 seconds. (Optional)
hostNameInCertificate Expected hostname in SSL certificate. Supports wildcards (e.g. *.database.windows.net). (Optional)
lockWaitTimeout Lock wait timeout using SET LOCK_TIMEOUT …. (Optional)
preferCursoredExecution Whether to prefer cursors or direct execution for queries. Uses by default direct. Cursors require more round-trips but are more backpressure-friendly. Defaults to direct execution. Can be boolean or a Predicate<String> accepting the SQL query. (Optional)
sendStringParametersAsUnicode Configure whether to send character data as unicode (NVARCHAR, NCHAR, NTEXT) or whether to use the database encoding, defaults to true. If disabled, CharSequence data is sent using the database-specific collation such as ASCII/MBCS instead of Unicode.
sslTunnel Enables SSL tunnel usage when using a SSL tunnel or SSL terminator in front of SQL Server. Accepts Function<SslContextBuilder, SslContextBuilder> to customize the SSL tunnel settings. SSL tunneling is not related to SQL Server's built-in SSL support. (Optional)
sslContextBuilderCustomizer SSL Context customizer to configure SQL Server's built-in SSL support (Function<SslContextBuilder, SslContextBuilder>) (Optional)
tcpKeepAlive Enable/disable TCP KeepAlive. Disabled by default. (Optional)
tcpNoDelay Enable/disable TCP NoDelay. Enabled by default. (Optional)
trustServerCertificate Fully trust the server certificate bypassing X.509 certificate validation. Disabled by default. (Optional)
trustStoreType Type of the TrustStore. Defaults to KeyStore.getDefaultType(). (Optional)
trustStore Path to the certificate TrustStore file. (Optional)
trustStorePassword Password used to check the integrity of the TrustStore data. (Optional)

Programmatic Configuration

MssqlConnectionConfiguration configuration = MssqlConnectionConfiguration.builder()
    .host("…")
    .username("…")
    .password("…")
    .database("…")
    .preferCursoredExecution(…)
    .build();

MssqlConnectionFactory factory = new MssqlConnectionFactory(configuration);

Mono<MssqlConnection> connectionMono = factory.create();

Microsoft SQL Server uses named parameters that are prefixed with @. The following SQL statement makes use of parameters:

INSERT INTO person (id, first_name, last_name) VALUES(@id, @firstname, @lastname)

Parameters are referenced without the @ prefix when binding these:

connection.createStatement("INSERT INTO person (id, first_name, last_name) VALUES(@id, @firstname, @lastname)")
            .bind("id", 1)
            .bind("firstname", "Walter")
            .bind("lastname", "White")
            .execute()

Binding also allows positional index (zero-based) references. The parameter index is derived from the parameter discovery order when parsing the query.

Maven configuration

Artifacts can be found on Maven Central.

<dependency>
  <groupId>io.r2dbc</groupId>
  <artifactId>r2dbc-mssql</artifactId>
  <version>${version}</version>
</dependency>

If you'd rather like the latest snapshots of the upcoming major version, use our Maven snapshot repository and declare the appropriate dependency version.

<dependency>
  <groupId>io.r2dbc</groupId>
  <artifactId>r2dbc-mssql</artifactId>
  <version>${version}.BUILD-SNAPSHOT</version>
</dependency>

<repository>
<id>sonatype-nexus-snapshots</id>
<name>Sonatype OSS Snapshot Repository</name>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>

Transaction Definitions

SQL Server supports additional options when starting a transaction. In particular, the following options can be specified:

  • Isolation Level (isolationLevel) (reset after the transaction to previous value)
  • Transaction Name (name)
  • Transaction Log Mark (mark)
  • Lock Wait Timeout (lockWaitTimeout) (reset after the transaction to -1)

These options can be specified upon transaction begin to start the transaction and apply options in a single command roundtrip:

MssqlConnection connection= …;

        connection.beginTransaction(MssqlTransactionDefinition.from(IsolationLevel.READ_UNCOMMITTED)
        .name("my-transaction").mark("tx-log-mark")
        .lockTimeout(Duration.ofMinutes(1)));

See also: https://docs.microsoft.com/en-us/sql/t-sql/language-elements/begin-transaction-transact-sql

Data Type Mapping

This reference table shows the type mapping between Microsoft SQL Server and Java data types:

Microsoft SQL Server Type Java Data Type
bit Boolean, Byte, Short, Integer, Long, BigDecimal, BigInteger
tinyint Byte, Boolean, Short, Integer, Long, BigDecimal, BigInteger
smallint Short, Boolean, Byte, Integer, Long, BigDecimal, BigInteger
int Integer, Boolean, Byte, Short, Long, BigDecimal, BigInteger
bigint Long, Boolean, Byte, Short, Integer, BigDecimal, BigInteger
real Float, Double
float Double, Float
decimal BigDecimal, BigInteger
numeric BigDecimal, BigInteger
uniqueidentifier UUID, String
smalldatetime LocalDateTime
datetime LocalDateTime
datetime2 LocalDateTime
date LocalDate
time LocalTime
datetimeoffset OffsetDateTime, ZonedDateTime
timestamp byte[]
smallmoney BigDecimal
money BigDecimal
char String, Clob
varchar String, Clob
varcharmax String, Clob
nchar String, Clob
nvarchar String, Clob
nvarcharmax String, Clob
text String, Clob
ntext String, Clob
image ByteBuffer, byte[], Blob
binary ByteBuffer, byte[], Blob
varbinary ByteBuffer, byte[], Blob
varbinarymax ByteBuffer, byte[], Blob
sql_variant Not yet supported.
xml Not yet supported.
udt Not yet supported.
geometry Not yet supported.
geography Not yet supported.

Types in bold indicate the native (default) Java type.

Note: BLOB (image, binary, varbinary and varbinary(max)) and CLOB (text, ntext, varchar(max) and nvarchar(max)) values are fully materialized in the client before decoding. Make sure to account for proper memory sizing.

Logging

If SL4J is on the classpath, it will be used. Otherwise, there are two possible fallbacks: Console or java.util.logging.Logger). By default, the Console fallback is used. To use the JDK loggers, set the reactor.logging.fallback System property to JDK.

Logging facilities:

  • Driver Logging (io.r2dbc.mssql)
  • Query Logging (io.r2dbc.mssql.QUERY on DEBUG level)
  • Transport Logging (io.r2dbc.mssql.client)
    • DEBUG enables Message exchange logging
    • TRACE enables traffic logging

Getting Help

Having trouble with R2DBC? We'd love to help!

Reporting Issues

R2DBC uses GitHub as issue tracking system to record bugs and feature requests. If you want to raise an issue, please follow the recommendations below:

  • Before you log a bug, please search the issue tracker to see if someone has already reported the problem.
  • If the issue doesn't already exist, create a new issue.
  • Please provide as much information as possible with the issue report, we like to know the version of R2DBC MSSQL that you are using and JVM version.
  • If you need to paste code, or include a stack trace use Markdown ``` escapes before and after your text.
  • If possible try to create a test-case or project that replicates the issue. Attach a link to your code or a compressed file containing your code.

Building from Source

You don't need to build from source to use R2DBC MSSQL (binaries in Maven Central), but if you want to try out the latest and greatest, R2DBC MSSQL can be easily built with the maven wrapper. You also need JDK 1.8 and Docker to run integration tests.

 $ ./mvnw clean install

If you want to build with the regular mvn command, you will need Maven v3.6.0 or above.

Also see CONTRIBUTING.adoc if you wish to submit pull requests. Commits require Signed-off-by (git commit -s) to ensure Developer Certificate of Origin.

Running JMH Benchmarks

Running the JMH benchmarks builds and runs the benchmarks without running tests.

 $ ./mvnw clean install -Pjmh

Staging to Maven Central

To stage a release to Maven Central, you need to create a release tag (release version) that contains the desired state and version numbers (mvn versions:set versions:commit -q -o -DgenerateBackupPoms=false -DnewVersion=x.y.z.(RELEASE|Mnnn|RCnnn) and force-push it to the release-0.x branch. This push will trigger a Maven staging build (see build-and-deploy-to-maven-central.sh).

License

R2DBC MSSQL is Open Source software released under the Apache 2.0 license.

r2dbc-mssql's People

Contributors

dajudge avatar gregturn avatar lhaatveit avatar mirromutth avatar mp911de avatar mrotteveel avatar nebhale avatar nhajratw avatar spring-operator avatar squiry avatar thankuswr avatar uaihebert avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

r2dbc-mssql's Issues

Reduce dependencies

Currently, this project depends on reactor-extra, Spring Boot JDBC starter, and transitively on netty-codec-http (1+2), and netty-handler-proxy.

We should eliminate dead dependencies.

Support for XML data types

Ideally, we interchange SQLXML as Clob type. We should also provide a utility to retrieve SQLXML for document parsing and to provide Clob data from a Document.

Add configurable fetch size to MssqlStatement

We should allow the configuration of fetchSize on Statement level to supply a fetch size for cursored execution and to override the cursored execution preference. Setting fetch size to zero enforces direct execution, setting it to a non-negative non-zero value enforces cursored execution.

Support SQL Server-specific transaction isolation levels by adding setTransactionIsolationLevel(MssqlIsolationLevel) to MssqlConnection

Hi, this is a first-timers-only issue. This means we've worked to make it more legible to folks who either haven't contributed to our codebase before or even folks who haven't contributed to open source before.

If that's you, we're interested in helping you take the first step and can answer questions and help you out as you do. Note that we're especially interested in contributions from people from groups underrepresented in free and open source software!

If you have contributed before, consider leaving this one for someone new, and looking through our general ideal-for-contribution issues. Thanks!

Background

SQL server supports more isolation levels than specified in R2DBC SPI.

Problem

We would like to support SNAPSHOT isolation in this driver. R2DBC defines isolation levels in an enum which makes extension impossible.

R2DBC SPI declares the following levels:

  • READ_COMMITTED
  • READ_UNCOMMITTED
  • REPEATABLE_READ
  • SERIALIZABLE

SQL Server supports additionally the SNAPSHOT level.

Solution

We need a driver-specific enum that contains all SQL server isolation levels (modelled along the lines of R2DBC's IsolationLevel enum).
We also need another setTransactionIsolationLevel method that allows to set the isolation level through the new enumeration.

Steps to fix

  • Claim this issue with a comment below and ask any clarifying questions you need.
  • Set up a repository locally following the Contributing Guidelines.
  • Try to fix the issue following the steps above.
  • Commit your changes and start a pull request.

Deliverables

  • Create a MssqlIsolationLevel enum.
  • Extend io.r2dbc.mssql.MssqlConnection by adding a setTransactionIsolationLevel(MssqlIsolationLevel) method.
  • Add a unit test for the new method to MssqlConnectionUnitTests.

Query-Subscribers of Client.exchange(…) remain subscribed

When issuing a query, downstream subscribers of query flows remain subscribed. This causes duplication of signals and a lot of dropped notifications. We should unsubscribe from the message exchange after completing a query execution.

Add EnumCodec

When trying to save an object that contains an enum value, I got the following error:

java.lang.IllegalArgumentException: Cannot encode [MY_VALUE] parameter of type [com.mycompany.MyEnum]
	at io.r2dbc.mssql.codec.DefaultCodecs.encode(DefaultCodecs.java:92) ~[r2dbc-mssql-1.0.0.M7.jar:1.0.0.M7]
	at io.r2dbc.mssql.PreparedMssqlStatement.bind(PreparedMssqlStatement.java:155) ~[r2dbc-mssql-1.0.0.M7.jar:1.0.0.M7]
	at io.r2dbc.mssql.PreparedMssqlStatement.bind(PreparedMssqlStatement.java:57) ~[r2dbc-mssql-1.0.0.M7.jar:1.0.0.M7]
	at org.springframework.data.r2dbc.dialect.NamedBindMarkers$NamedBindMarker.bind(NamedBindMarkers.java:105) ~[spring-data-r2dbc-1.0.0.M1.jar:1.0.0.M1]
	at org.springframework.data.r2dbc.function.DefaultReactiveDataAccessStrategy$DefaultBindableInsert.bind(DefaultReactiveDataAccessStrategy.java:465) ~[spring-data-r2dbc-1.0.0.M1.jar:1.0.0.M1]
	at org.springframework.data.r2dbc.function.BindableOperation.bind(BindableOperation.java:53) ~[spring-data-r2dbc-1.0.0.M1.jar:1.0.0.M1]
	at org.springframework.data.r2dbc.function.DefaultDatabaseClient$DefaultTypedInsertSpec.lambda$exchange$2(DefaultDatabaseClient.java:996) ~[spring-data-r2dbc-1.0.0.M1.jar:1.0.0.M1]
	at org.springframework.data.r2dbc.function.DefaultDatabaseClient$DefaultTypedInsertSpec.lambda$exchange$3(DefaultDatabaseClient.java:1002) ~[spring-data-r2dbc-1.0.0.M1.jar:1.0.0.M1]
	at org.springframework.data.r2dbc.function.DefaultSqlResult$2.apply(DefaultSqlResult.java:81) ~[spring-data-r2dbc-1.0.0.M1.jar:1.0.0.M1]
	at org.springframework.data.r2dbc.function.DefaultSqlResult$2.apply(DefaultSqlResult.java:78) ~[spring-data-r2dbc-1.0.0.M1.jar:1.0.0.M1]
	at org.springframework.data.r2dbc.function.DefaultDatabaseClient.doInConnectionMany(DefaultDatabaseClient.java:1018) ~[spring-data-r2dbc-1.0.0.M1.jar:1.0.0.M1]
	at org.springframework.data.r2dbc.function.DefaultDatabaseClient.lambda$inConnectionMany$2(DefaultDatabaseClient.java:159) ~[spring-data-r2dbc-1.0.0.M1.jar:1.0.0.M1]

There is no EnumCodec yet, but once implemented it should look like the one from postgresql: https://github.com/r2dbc/r2dbc-postgresql/blob/master/src/main/java/io/r2dbc/postgresql/codec/EnumCodec.java

Interestingly decoding of varchar to an enum value works. It is decoded to a String and Springs ConversionService turns it into an enum value.

I would have added a codec by myself, but there is no easy way to add a custom codec to DefaultCodecs.

Propagate decoding exception to the receiver Publisher

Decoding exceptions are not propagated to the resulting Publisher but bubbled up to the netty handler pipeline. Decode exceptions should be propagated down to the receiver and should also close the connection as decoding issues typically destroy the connection state.

Example:

16:14:19.862 reactor-tcp-nio-1 reactor.core.publisher.Operators Operator called default onErrorDropped
reactor.core.Exceptions$BubblingException: java.lang.IllegalArgumentException: minimumReadableBytes: -1 (expected: >= 0)
	at reactor.core.Exceptions.bubble(Exceptions.java:154)
	at reactor.core.publisher.FluxGenerate$GenerateSubscription.poll(FluxGenerate.java:364)
	at reactor.core.publisher.FluxOnAssembly$OnAssemblySubscriber.poll(FluxOnAssembly.java:470)
	at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.drainSync(FluxFlattenIterable.java:498)
	at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.drain(FluxFlattenIterable.java:635)
	at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.request(FluxFlattenIterable.java:266)
	at reactor.core.publisher.FluxOnAssembly$OnAssemblySubscriber.request(FluxOnAssembly.java:458)
	at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:1849)
	at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:1723)
	at reactor.core.publisher.FluxOnAssembly$OnAssemblySubscriber.onSubscribe(FluxOnAssembly.java:442)
	at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.onSubscribe(FluxFlattenIterable.java:208)
	at reactor.core.publisher.FluxOnAssembly$OnAssemblySubscriber.onSubscribe(FluxOnAssembly.java:442)
	at reactor.core.publisher.FluxGenerate.subscribe(FluxGenerate.java:83)
	at reactor.core.publisher.FluxOnAssembly.subscribe(FluxOnAssembly.java:164)
	at reactor.core.publisher.FluxFlattenIterable.subscribe(FluxFlattenIterable.java:109)
	at reactor.core.publisher.FluxOnAssembly.subscribe(FluxOnAssembly.java:164)
	at reactor.core.publisher.Flux.subscribe(Flux.java:7734)
	at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:442)
	at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onNext(FluxConcatMap.java:244)
	at reactor.netty.channel.FluxReceive.drainReceiver(FluxReceive.java:211)
	at reactor.netty.channel.FluxReceive.onInboundNext(FluxReceive.java:327)
	at reactor.netty.channel.ChannelOperations.onInboundNext(ChannelOperations.java:310)
	at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:141)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)
	at io.r2dbc.mssql.client.ssl.TdsSslHandler.channelRead(TdsSslHandler.java:374)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1434)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:965)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:648)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:583)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:500)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:462)
	at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:897)
	at java.lang.Thread.run(Thread.java:748)
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Assembly trace from producer [reactor.core.publisher.FluxFlattenIterable] :
	reactor.core.publisher.Flux.flatMapIterable(Flux.java:4853)
	reactor.core.publisher.Flux.flatMapIterable(Flux.java:4828)
	io.r2dbc.mssql.client.StreamDecoder.decode(StreamDecoder.java:123)
	io.r2dbc.mssql.client.ReactorNettyClient.lambda$new$4(ReactorNettyClient.java:177)
	reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:368)
	reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onNext(FluxConcatMap.java:244)
	reactor.netty.channel.FluxReceive.drainReceiver(FluxReceive.java:211)
	reactor.netty.channel.FluxReceive.onInboundNext(FluxReceive.java:327)
	reactor.netty.channel.ChannelOperations.onInboundNext(ChannelOperations.java:310)
	reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:141)
	io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)
	io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)
	io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)
	io.r2dbc.mssql.client.ssl.TdsSslHandler.channelRead(TdsSslHandler.java:374)
	io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1434)
	io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:965)
	io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
	io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:648)
	io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:583)
	io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:500)
	io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:462)
	io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:897)
Error has been observed by the following operator(s):
	|_	Flux.flatMapIterable ⇢ io.r2dbc.mssql.client.StreamDecoder.decode(StreamDecoder.java:123)

Caused by: java.lang.IllegalArgumentException: minimumReadableBytes: -1 (expected: >= 0)
	at io.netty.buffer.AbstractByteBuf.checkReadableBytes(AbstractByteBuf.java:1418)
	at io.netty.buffer.AbstractByteBuf.skipBytes(AbstractByteBuf.java:971)
	at io.r2dbc.mssql.message.token.RowToken.canDecodeColumn(RowToken.java:125)
	at io.r2dbc.mssql.message.token.RowToken.canDecode(RowToken.java:95)
	at io.r2dbc.mssql.message.token.Tabular.lambda$decodeFunction$0(Tabular.java:184)
	at io.r2dbc.mssql.message.token.Tabular$TabularDecoder.decode(Tabular.java:372)
	at io.r2dbc.mssql.client.ConnectionState$4.lambda$decoder$1(ConnectionState.java:194)
	at io.r2dbc.mssql.client.StreamDecoder.lambda$decode$1(StreamDecoder.java:94)
	at reactor.core.publisher.FluxGenerate$GenerateSubscription.poll(FluxGenerate.java:347)
	... 48 common frames omitted
16:14:19.863 reactor-tcp-nio-1 io.netty.channel.AbstractChannelHandlerContext An exception '{}' [enable DEBUG level for full stacktrace] was thrown by a user handler's exceptionCaught() method while handling the following exception:
reactor.core.Exceptions$BubblingException: java.lang.IllegalArgumentException: minimumReadableBytes: -1 (expected: >= 0)
	at reactor.core.Exceptions.bubble(Exceptions.java:154)
	at reactor.core.publisher.FluxGenerate$GenerateSubscription.poll(FluxGenerate.java:364)
	at reactor.core.publisher.FluxOnAssembly$OnAssemblySubscriber.poll(FluxOnAssembly.java:470)
	at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.drainSync(FluxFlattenIterable.java:498)
	at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.drain(FluxFlattenIterable.java:635)
	at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.request(FluxFlattenIterable.java:266)
	at reactor.core.publisher.FluxOnAssembly$OnAssemblySubscriber.request(FluxOnAssembly.java:458)
	at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:1849)
	at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:1723)
	at reactor.core.publisher.FluxOnAssembly$OnAssemblySubscriber.onSubscribe(FluxOnAssembly.java:442)
	at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.onSubscribe(FluxFlattenIterable.java:208)
	at reactor.core.publisher.FluxOnAssembly$OnAssemblySubscriber.onSubscribe(FluxOnAssembly.java:442)
	at reactor.core.publisher.FluxGenerate.subscribe(FluxGenerate.java:83)
	at reactor.core.publisher.FluxOnAssembly.subscribe(FluxOnAssembly.java:164)
	at reactor.core.publisher.FluxFlattenIterable.subscribe(FluxFlattenIterable.java:109)
	at reactor.core.publisher.FluxOnAssembly.subscribe(FluxOnAssembly.java:164)
	at reactor.core.publisher.Flux.subscribe(Flux.java:7734)
	at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:442)
	at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onNext(FluxConcatMap.java:244)
	at reactor.netty.channel.FluxReceive.drainReceiver(FluxReceive.java:211)
	at reactor.netty.channel.FluxReceive.onInboundNext(FluxReceive.java:327)
	at reactor.netty.channel.ChannelOperations.onInboundNext(ChannelOperations.java:310)
	at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:141)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)
	at io.r2dbc.mssql.client.ssl.TdsSslHandler.channelRead(TdsSslHandler.java:374)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1434)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:965)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:648)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:583)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:500)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:462)
	at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:897)
	at java.lang.Thread.run(Thread.java:748)
	Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: 
Assembly trace from producer [reactor.core.publisher.FluxFlattenIterable] :
	reactor.core.publisher.Flux.flatMapIterable(Flux.java:4853)
	reactor.core.publisher.Flux.flatMapIterable(Flux.java:4828)
	io.r2dbc.mssql.client.StreamDecoder.decode(StreamDecoder.java:123)
	io.r2dbc.mssql.client.ReactorNettyClient.lambda$new$4(ReactorNettyClient.java:177)
	reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:368)
	reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onNext(FluxConcatMap.java:244)
	reactor.netty.channel.FluxReceive.drainReceiver(FluxReceive.java:211)
	reactor.netty.channel.FluxReceive.onInboundNext(FluxReceive.java:327)
	reactor.netty.channel.ChannelOperations.onInboundNext(ChannelOperations.java:310)
	reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:141)
	io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)
	io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)
	io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)
	io.r2dbc.mssql.client.ssl.TdsSslHandler.channelRead(TdsSslHandler.java:374)
	io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1434)
	io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:965)
	io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
	io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:648)
	io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:583)
	io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:500)
	io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:462)
	io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:897)
Error has been observed by the following operator(s):
	|_	Flux.flatMapIterable ⇢ io.r2dbc.mssql.client.StreamDecoder.decode(StreamDecoder.java:123)

Caused by: java.lang.IllegalArgumentException: minimumReadableBytes: -1 (expected: >= 0)
	at io.netty.buffer.AbstractByteBuf.checkReadableBytes(AbstractByteBuf.java:1418)
	at io.netty.buffer.AbstractByteBuf.skipBytes(AbstractByteBuf.java:971)
	at io.r2dbc.mssql.message.token.RowToken.canDecodeColumn(RowToken.java:125)
	at io.r2dbc.mssql.message.token.RowToken.canDecode(RowToken.java:95)
	at io.r2dbc.mssql.message.token.Tabular.lambda$decodeFunction$0(Tabular.java:184)
	at io.r2dbc.mssql.message.token.Tabular$TabularDecoder.decode(Tabular.java:372)
	at io.r2dbc.mssql.client.ConnectionState$4.lambda$decoder$1(ConnectionState.java:194)
	at io.r2dbc.mssql.client.StreamDecoder.lambda$decode$1(StreamDecoder.java:94)
	at reactor.core.publisher.FluxGenerate$GenerateSubscription.poll(FluxGenerate.java:347)
	... 48 common frames omitted

Reduce object allocations

We should consider caching for Length and lazily create row consumers instead of pre-preparing row count and row data sequences.

Add support for varchar(max) and nvarchar(max)

varchar(max) and nvarchar(max) are PLP (Partially Length-Prefixed) Data Types and they are not supported by the driver yet.

Using a varchar(max) column typically results in a decoding exception:

java.lang.IllegalArgumentException: minimumReadableBytes: -1 (expected: >= 0)
	at io.netty.buffer.AbstractByteBuf.checkReadableBytes(AbstractByteBuf.java:1418)
	at io.netty.buffer.AbstractByteBuf.skipBytes(AbstractByteBuf.java:971)
	at io.r2dbc.mssql.message.token.RowToken.canDecodeColumn(RowToken.java:125)
	at io.r2dbc.mssql.message.token.RowToken.canDecode(RowToken.java:95)
	at io.r2dbc.mssql.message.token.Tabular.lambda$decodeFunction$0(Tabular.java:184)
	at io.r2dbc.mssql.message.token.Tabular$TabularDecoder.decode(Tabular.java:372)

Getting java.lang.IllegalStateException: Collation not available when querying the database.

Hi all. I'm trying to write a basic example of the Spring R2DBC using a SQL Server database.
Whenever I try to query it, I get the following exception: java.lang.IllegalStateException: Collation not available.
Here are my classes:

My ConnectionFactoryConfiguration:

@Configuration
class ConnectionFactoryConfiguration {

	@Bean
	DatabaseClient databaseClient() {
		return DatabaseClient.create(connectionFactory());
	}

	@Bean
	public ConnectionFactory connectionFactory() {
		MssqlConnectionConfiguration config = MssqlConnectionConfiguration.builder() //
			.host("localhost")
			.port(1434)
			.database("pizza")
			.username("test")
			.password("test")
			.build();
		return new MssqlConnectionFactory(config);
	}
}

My repository (I'm not using Spring Data, though):

@Repository
public class ManualPizzaRepository {

    private final ConnectionFactory connectionFactory;

    public ManualPizzaRepository(ConnectionFactory connectionFactory) {
        this.connectionFactory = connectionFactory;
    }

    Mono<Void> deleteById(Integer id) {
        return this.connection()
            .flatMapMany(c -> c.createStatement("delete from pizza where id = $1")
                .bind("$1", id)
                .execute())
            .then();
    }

    Flux<Pizza> findAll() {
        return this.connection()
            .flatMapMany(connection ->
                Flux.from(connection.createStatement("select * from pizza").execute())
                    .flatMap(r ->
                        r.map((row, rowMetadata) ->
                            new Pizza(row.get("id", Integer.class), row.get("flavor", String.class)))));
    }

    Flux<Pizza> save(Pizza pizza) {
        Flux<? extends Result> flux = this.connection().flatMapMany(conn -> conn.createStatement("insert into pizza (flavor) values ($1)")
            .bind("$1", pizza.getFlavor())
            .add()
            .execute());

        return flux.switchMap(x -> Flux.just(new Pizza(pizza.getId(), pizza.getFlavor())));
    }

    private Mono<Connection> connection() {
        return Mono.from(this.connectionFactory.create());
    }

}

My model class:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Pizza {

    @Id
    private Integer id;
    private String flavor;

}

My test class (where I perform the call that's throwing the exception):

@RunWith(SpringRunner.class)
@SpringBootTest(classes = {ConnectionFactoryConfiguration.class, ManualPizzaRepository.class})
public class ReactiveSqlSqlserverApplicationTests {

	@Autowired
	ManualPizzaRepository pizzas;

	@Autowired
	DatabaseClient databaseClient;

	@Test
	public void testAllManual() {
		Flux<Void> deleteAll = this.pizzas.findAll().flatMap(pizza -> this.pizzas.deleteById(pizza.getId()));
		StepVerifier.create(deleteAll).expectNextCount(0).verifyComplete();

		Flux<Pizza> savePizzas = Flux.just("Mozzarella", "Pepperoni", "Margherita")
			.map(flavor -> new Pizza(null, flavor))
			.flatMap(pizza -> this.pizzas.save(pizza));
		StepVerifier.create(savePizzas).expectNextCount(3).verifyComplete();

		Flux<Pizza> findAll = this.pizzas.findAll();
		StepVerifier.create(findAll).expectNextCount(3).verifyComplete();

	}
}

My database configuration:
database options
database options 2

Stack trace of the exception:

2019-03-15 13:31:34.058  WARN 8292 --- [actor-tcp-nio-1] i.r2dbc.mssql.client.ReactorNettyClient  : Failed onEnvironmentChange() in io.r2dbc.mssql.client.ReactorNettyClient$CollationListener@1680fe48

io.r2dbc.mssql.message.tds.ProtocolException: null
	at io.r2dbc.mssql.message.type.Collation.decode(Collation.java:127) ~[r2dbc-mssql-1.0.0.M7.jar:1.0.0.M7]
	at io.r2dbc.mssql.client.ReactorNettyClient$CollationListener.onEnvironmentChange(ReactorNettyClient.java:429) ~[r2dbc-mssql-1.0.0.M7.jar:1.0.0.M7]
	at io.r2dbc.mssql.client.ReactorNettyClient.lambda$new$1(ReactorNettyClient.java:106) ~[r2dbc-mssql-1.0.0.M7.jar:1.0.0.M7]
	at io.r2dbc.mssql.client.ReactorNettyClient.lambda$handleMessage$19(ReactorNettyClient.java:363) ~[r2dbc-mssql-1.0.0.M7.jar:1.0.0.M7]
	at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:177) ~[reactor-core-3.2.6.RELEASE.jar:3.2.6.RELEASE]
	at reactor.core.publisher.FluxHandle$HandleSubscriber.onNext(FluxHandle.java:113) ~[reactor-core-3.2.6.RELEASE.jar:3.2.6.RELEASE]
	at reactor.core.publisher.FluxPeekFuseable$PeekConditionalSubscriber.onNext(FluxPeekFuseable.java:826) ~[reactor-core-3.2.6.RELEASE.jar:3.2.6.RELEASE]
	at reactor.core.publisher.FluxPeekFuseable$PeekConditionalSubscriber.onNext(FluxPeekFuseable.java:826) ~[reactor-core-3.2.6.RELEASE.jar:3.2.6.RELEASE]
	at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.innerNext(FluxConcatMap.java:275) ~[reactor-core-3.2.6.RELEASE.jar:3.2.6.RELEASE]
	at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onNext(FluxConcatMap.java:849) ~[reactor-core-3.2.6.RELEASE.jar:3.2.6.RELEASE]
	at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.drainSync(FluxFlattenIterable.java:564) ~[reactor-core-3.2.6.RELEASE.jar:3.2.6.RELEASE]
	at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.drain(FluxFlattenIterable.java:635) ~[reactor-core-3.2.6.RELEASE.jar:3.2.6.RELEASE]
	at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.request(FluxFlattenIterable.java:266) ~[reactor-core-3.2.6.RELEASE.jar:3.2.6.RELEASE]
	at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:1878) ~[reactor-core-3.2.6.RELEASE.jar:3.2.6.RELEASE]
	at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:1752) ~[reactor-core-3.2.6.RELEASE.jar:3.2.6.RELEASE]
	at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.onSubscribe(FluxFlattenIterable.java:208) ~[reactor-core-3.2.6.RELEASE.jar:3.2.6.RELEASE]
	at reactor.core.publisher.FluxGenerate.subscribe(FluxGenerate.java:83) ~[reactor-core-3.2.6.RELEASE.jar:3.2.6.RELEASE]
	at reactor.core.publisher.FluxFlattenIterable.subscribe(FluxFlattenIterable.java:109) ~[reactor-core-3.2.6.RELEASE.jar:3.2.6.RELEASE]
	at reactor.core.publisher.Flux.subscribe(Flux.java:7777) ~[reactor-core-3.2.6.RELEASE.jar:3.2.6.RELEASE]
	at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:442) ~[reactor-core-3.2.6.RELEASE.jar:3.2.6.RELEASE]
	at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onNext(FluxConcatMap.java:244) ~[reactor-core-3.2.6.RELEASE.jar:3.2.6.RELEASE]
	at reactor.netty.channel.FluxReceive.drainReceiver(FluxReceive.java:205) ~[reactor-netty-0.8.5.RELEASE.jar:0.8.5.RELEASE]
	at reactor.netty.channel.FluxReceive.onInboundNext(FluxReceive.java:321) ~[reactor-netty-0.8.5.RELEASE.jar:0.8.5.RELEASE]
	at reactor.netty.channel.ChannelOperations.onInboundNext(ChannelOperations.java:319) ~[reactor-netty-0.8.5.RELEASE.jar:0.8.5.RELEASE]
	at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:141) ~[reactor-netty-0.8.5.RELEASE.jar:0.8.5.RELEASE]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
	at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
	at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
	at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
	at io.r2dbc.mssql.client.ssl.TdsSslHandler.channelRead(TdsSslHandler.java:374) ~[r2dbc-mssql-1.0.0.M7.jar:1.0.0.M7]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1408) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:930) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:677) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:612) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:529) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:491) ~[netty-transport-4.1.33.Final.jar:4.1.33.Final]
	at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:905) ~[netty-common-4.1.33.Final.jar:4.1.33.Final]
	at java.lang.Thread.run(Thread.java:748) ~[na:1.8.0_201]
Caused by: java.io.UnsupportedEncodingException: Windows collation not supported: D000
	at io.r2dbc.mssql.message.type.Collation.getEncodingFromLCID(Collation.java:226) ~[r2dbc-mssql-1.0.0.M7.jar:1.0.0.M7]
	at io.r2dbc.mssql.message.type.Collation.<init>(Collation.java:89) ~[r2dbc-mssql-1.0.0.M7.jar:1.0.0.M7]
	at io.r2dbc.mssql.message.type.Collation.decode(Collation.java:125) ~[r2dbc-mssql-1.0.0.M7.jar:1.0.0.M7]
	... 51 common frames omitted


java.lang.AssertionError: expectation "expectComplete" failed (expected: onComplete(); actual: onError(java.lang.IllegalStateException: Collation not available))

	at reactor.test.ErrorFormatter.assertionError(ErrorFormatter.java:105)
	at reactor.test.ErrorFormatter.failPrefix(ErrorFormatter.java:94)
	at reactor.test.ErrorFormatter.fail(ErrorFormatter.java:64)
	at reactor.test.ErrorFormatter.failOptional(ErrorFormatter.java:79)
	at reactor.test.DefaultStepVerifierBuilder.lambda$expectComplete$4(DefaultStepVerifierBuilder.java:323)
	at reactor.test.DefaultStepVerifierBuilder$SignalEvent.test(DefaultStepVerifierBuilder.java:2112)
	at reactor.test.DefaultStepVerifierBuilder$DefaultVerifySubscriber.onSignal(DefaultStepVerifierBuilder.java:1408)
	at reactor.test.DefaultStepVerifierBuilder$DefaultVerifySubscriber.onExpectation(DefaultStepVerifierBuilder.java:1356)
	at reactor.test.DefaultStepVerifierBuilder$DefaultVerifySubscriber.onError(DefaultStepVerifierBuilder.java:1030)
	at reactor.core.publisher.FluxFlatMap$FlatMapMain.checkTerminated(FluxFlatMap.java:790)
	at reactor.core.publisher.FluxFlatMap$FlatMapMain.drainLoop(FluxFlatMap.java:560)
	at reactor.core.publisher.FluxFlatMap$FlatMapMain.drain(FluxFlatMap.java:540)
	at reactor.core.publisher.FluxFlatMap$FlatMapMain.onError(FluxFlatMap.java:412)
	at reactor.core.publisher.MonoFlatMapMany$FlatMapManyInner.onError(MonoFlatMapMany.java:243)
	at reactor.core.publisher.FluxFlatMap$FlatMapMain.checkTerminated(FluxFlatMap.java:790)
	at reactor.core.publisher.FluxFlatMap$FlatMapMain.drainLoop(FluxFlatMap.java:560)
	at reactor.core.publisher.FluxFlatMap$FlatMapMain.drain(FluxFlatMap.java:540)
	at reactor.core.publisher.FluxFlatMap$FlatMapMain.onError(FluxFlatMap.java:412)
	at reactor.core.publisher.Operators.error(Operators.java:181)
	at reactor.core.publisher.FluxDefer.subscribe(FluxDefer.java:50)
	at reactor.core.publisher.FluxFlatMap.subscribe(FluxFlatMap.java:97)
	at reactor.core.publisher.Flux.subscribe(Flux.java:7777)
	at reactor.core.publisher.MonoFlatMapMany$FlatMapManyMain.onNext(MonoFlatMapMany.java:184)
	at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:114)
	at reactor.core.publisher.Operators$MonoSubscriber.complete(Operators.java:1505)
	at reactor.core.publisher.MonoDelayUntil$DelayUntilCoordinator.signal(MonoDelayUntil.java:211)
	at reactor.core.publisher.MonoDelayUntil$DelayUntilTrigger.onComplete(MonoDelayUntil.java:290)
	at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:252)
	at reactor.core.publisher.FluxHandle$HandleSubscriber.onComplete(FluxHandle.java:207)
	at reactor.core.publisher.FluxHandle$HandleSubscriber.onNext(FluxHandle.java:128)
	at reactor.core.publisher.FluxFilter$FilterConditionalSubscriber.onNext(FluxFilter.java:240)
	at reactor.core.publisher.FluxPeekFuseable$PeekConditionalSubscriber.onNext(FluxPeekFuseable.java:826)
	at reactor.core.publisher.FluxFlatMap$FlatMapMain.drainLoop(FluxFlatMap.java:664)
	at reactor.core.publisher.FluxFlatMap$FlatMapMain.drain(FluxFlatMap.java:540)
	at reactor.core.publisher.FluxFlatMap$FlatMapInner.onNext(FluxFlatMap.java:940)
	at reactor.core.publisher.FluxReplay$UnboundedReplayBuffer.replayFused(FluxReplay.java:626)
	at reactor.core.publisher.FluxReplay$UnboundedReplayBuffer.replay(FluxReplay.java:656)
	at reactor.core.publisher.FluxReplay$ReplaySubscriber.onNext(FluxReplay.java:1161)
	at reactor.core.publisher.FluxWindowPredicate$WindowFlux.drainRegular(FluxWindowPredicate.java:636)
	at reactor.core.publisher.FluxWindowPredicate$WindowFlux.drain(FluxWindowPredicate.java:714)
	at reactor.core.publisher.FluxWindowPredicate$WindowFlux.onNext(FluxWindowPredicate.java:756)
	at reactor.core.publisher.FluxWindowPredicate$WindowPredicateMain.onNext(FluxWindowPredicate.java:227)
	at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:192)
	at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:192)
	at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:192)
	at reactor.core.publisher.FluxPeek$PeekSubscriber.onNext(FluxPeek.java:192)
	at reactor.core.publisher.FluxHandle$HandleSubscriber.onNext(FluxHandle.java:113)
	at reactor.core.publisher.FluxPeekFuseable$PeekConditionalSubscriber.onNext(FluxPeekFuseable.java:826)
	at reactor.core.publisher.FluxPeekFuseable$PeekConditionalSubscriber.onNext(FluxPeekFuseable.java:826)
	at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.innerNext(FluxConcatMap.java:275)
	at reactor.core.publisher.FluxConcatMap$ConcatMapInner.onNext(FluxConcatMap.java:849)
	at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.drainSync(FluxFlattenIterable.java:564)
	at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.drain(FluxFlattenIterable.java:635)
	at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.request(FluxFlattenIterable.java:266)
	at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.set(Operators.java:1878)
	at reactor.core.publisher.Operators$MultiSubscriptionSubscriber.onSubscribe(Operators.java:1752)
	at reactor.core.publisher.FluxFlattenIterable$FlattenIterableSubscriber.onSubscribe(FluxFlattenIterable.java:208)
	at reactor.core.publisher.FluxGenerate.subscribe(FluxGenerate.java:83)
	at reactor.core.publisher.FluxFlattenIterable.subscribe(FluxFlattenIterable.java:109)
	at reactor.core.publisher.Flux.subscribe(Flux.java:7777)
	at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.drain(FluxConcatMap.java:442)
	at reactor.core.publisher.FluxConcatMap$ConcatMapImmediate.onNext(FluxConcatMap.java:244)
	at reactor.netty.channel.FluxReceive.drainReceiver(FluxReceive.java:205)
	at reactor.netty.channel.FluxReceive.onInboundNext(FluxReceive.java:321)
	at reactor.netty.channel.ChannelOperations.onInboundNext(ChannelOperations.java:319)
	at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:141)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	at io.netty.channel.ChannelInboundHandlerAdapter.channelRead(ChannelInboundHandlerAdapter.java:86)
	at io.r2dbc.mssql.client.ssl.TdsSslHandler.channelRead(TdsSslHandler.java:374)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:340)
	at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1408)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:362)
	at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:348)
	at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:930)
	at io.netty.channel.nio.AbstractNioByteChannel$NioByteUnsafe.read(AbstractNioByteChannel.java:163)
	at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:677)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:612)
	at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:529)
	at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:491)
	at io.netty.util.concurrent.SingleThreadEventExecutor$5.run(SingleThreadEventExecutor.java:905)
	at java.lang.Thread.run(Thread.java:748)
	Suppressed: java.lang.IllegalStateException: Collation not available
		at io.r2dbc.mssql.client.Client.lambda$getRequiredCollation$0(Client.java:85)
		at java.util.Optional.orElseThrow(Optional.java:290)
		at io.r2dbc.mssql.client.Client.getRequiredCollation(Client.java:85)
		at io.r2dbc.mssql.CursoredQueryMessageFlow.exchange(CursoredQueryMessageFlow.java:112)
		at io.r2dbc.mssql.SimpleCursoredMssqlStatement.lambda$execute$1(SimpleCursoredMssqlStatement.java:58)
		at reactor.core.publisher.FluxDefer.subscribe(FluxDefer.java:46)
		... 73 more

UPDATE: Forgot to add my POM

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.3.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>reactive-sql-sqlserver</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>reactive-sql-sqlserver</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
    </repositories>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>io.projectreactor</groupId>
            <artifactId>reactor-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-r2dbc</artifactId>
            <version>1.0.0.M1</version>
        </dependency>
        <dependency>
            <groupId>io.r2dbc</groupId>
            <artifactId>r2dbc-mssql</artifactId>
            <version>1.0.0.M7</version>
        </dependency>
        <dependency>
            <groupId>io.r2dbc</groupId>
            <artifactId>r2dbc-spi</artifactId>
            <version>1.0.0.M7</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

Thanks in advance for your help.

Executing a parametrized Statement twice fails

Reusing a parametrized statement (multiple subscriptions) fails because bound values (ref-counted) are attempted to be released.

We should either reject the execution early or allow execution multiple times.

Defer error signal emission in MssqlResult until done token is processed

Error signals in MssqlResult are emitted directly and this causes cancellation of the upstream subscription. Frames may remain on the upstream source and this breaks downstream/next consumers.

We should defer error emission until receiving a done token and emit the error signal if an error was recorded.

Tabular decode function retains previous column metadata

Executing a Tabular decode retains previous column metadata (ColumnMetadataToken) token and does not replace it when receiving a subsequent token. This leads to decoding errors of row metadata.

We need to check after metadata decoding whether the token is qualified to replate a potential previous metadata token.

Document supported data types

Hi, this is a first-timers-only issue. This means we've worked to make it more legible to folks who either haven't contributed to our codebase before or even folks who haven't contributed to open source before.

If that's you, we're interested in helping you take the first step and can answer questions and help you out as you do. Note that we're especially interested in contributions from people from groups underrepresented in free and open source software!

If you have contributed before, consider leaving this one for someone new, and looking through our general ideal-for-contribution issues. Thanks!

Background

We should document which SQL Server data types are currently supported by this driver to document features/limitations. Any non-documented data type is not supported.

Problem

Right now, it's not obvious which data types can be used and to which Java types SQL Server types map to.

Solution

Supported data types can be found by checking for Codec implementations in the io.r2dbc.mssql.codec package. Supported data types can be a table (matrix) between the SQL Server data type (e.g. VARCHAR) and to which Java type it maps to (e.g. java.lang.String, based on StringCodec.

The table with the type mapping should be added to our README.md.

Steps to fix

  • Claim this issue with a comment below and ask any clarifying questions you need.
  • Set up a repository locally following the Contributing Guidelines.
  • Try to fix the issue following the steps above.
  • Commit your changes and start a pull request.

Deliverables

  • Updated README.md containing a section which SQL Server data types are currently supported by this driver.

Fix ConnectionFactories usage example in readme

The example

ConnectionFactory connectionFactory = ConnectionFactories.get(ConnectionFactoryOptions.builder()
...
    .build());

Mono<Connection> connection = connectionFactory.create();

does not work because ConnectionFactory returns Publisher<…> and not Mono<…>.

SELECT TOP @size fails with R2dbcException

Parameter binding is not working at all on the latest snapshot or 1.0.0.M7.

Depending on what you do it says

Binding parameters is not supported for the statement [SELECT TOP $1 * FROM metadata
Binding parameters is not supported for the statement [SELECT TOP :size * FROM metadata
executeMany; uncategorized R2dbcException for SQL [SELECT TOP @size * FROM metadata]; Incorrect syntax near '@size'.; nested exception is R2dbcException{errorCode=102, sqlState='S0001'} io.r2dbc.mssql.MssqlException: Incorrect syntax near '@size'.

A prepared statement is recognized when it contains an @ but parameters which such a character cannot be bound. The documentation says named parameters start with : and indexed with $.

Add benchmark suites

We should provide a suite of benchmarks to measure the performance and throughput of the driver. Metrics that we should gather:

Offline metrics

  • Codec throughput
  • Statement creation
  • Query parsing (ParsedQuery)

Integration metrics

  • Query execution using SQLBATCH and RPC (Non-parametrized Cursored, Parametrized Direct, Parametrized Cursored)

Output of generated keys not working for UUID / uniqueidentifier

I am using UUID / uniqueidentifier primary keys.

CREATE TABLE test (
  id UNIQUEIDENTIFIER DEFAULT NEWID(),
  text VARCHAR(255)
)

When inserting a new object, I dont get the generated id. GENERATED_KEYS is empty.

INSERT INTO test (text) VALUES('test') select SCOPE_IDENTITY() AS GENERATED_KEYS;

Will return NULL.

That is an issue of SQL Server but the workaround would be to use the OUTPUT syntax:

INSERT INTO test (text) OUTPUT inserted.id VALUES('test');

That will actually return something like | id | B5BA707B-872E-4BFC-8A96-97A1D789E74C |

Introduce direct/cursored preference Predicate to prefer direct/cursored execution

SQL Server statements can be executed using various execution methods:

  • SQLBATCH: Direct execution without parameters
  • SP_CURSOROPEN: Cursored execution with parameters
  • SP_EXECUTESQL: Direct execution with parameters
  • SP_CURSORPREPEXEC: Prepared execution with parameters

We should introduce a Predicate<String> (or a similar type) and a preference flag whether to prefer direct (cursor-less) or cursored execution. The predicate would allow controlling the preference regarding cursored/non-cursored execution.

Cursored execution has a factor 4 overhead in contrast to direct execution:

  • Direct execution: ~ 2000 queries/sec (simple queries, just SELECT foo FROM bar)
  • Cursored execution ~ 500 queries/sec (simple queries, just SELECT foo FROM bar)

Consider large chunks in StreamDecoder

StreamDecoder attempts to de-chunk a single segment and does not consider multiple chunks/header sequences in its aggregated body. This leaves not decoded segments in the aggregator buffer and protocol sequence does not advance.

This is a blocker for chunks greater 16k.

Related to #58.

Add support for parametrized batches

Statement.add() allows the creation of multiple bindings for a prepared statement and so execution of the prepared statements with various bindings ("Prepared Statement Batch"). We should add support for this mode of execution.

The difficulty is to consume/retain protocol state to make sure the next batch is sent when the previous one's response is fully consumed.

Add FluxDiscardOnCancel operator

We need a Flux operator that allows discarding data signals (except terminal signals) when a downstream Subscriber cancels the subscription. A typical example is:

result.map((row, metadata)-> …).next();

Canceling the subscription can leave data signals non-consumed so the protocol state gets mixed up:

  1. No demand, so no further reads on the channel
  2. Consumers of different windows might receive the remaining frames

FluxDiscardOnCancel would behave ideally:

  • Passthrough signals by default
  • On cancellation:
    1. request(Long.MAX_VALUE)
    2. discard data signal
    3. propagate terminal signals

/cc @nebhale @simonbasle @smaldini @gregturn

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.