diff --git a/.travis.yml b/.travis.yml index b56b510c..af0c9e6e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -36,6 +36,7 @@ jobs: fast_finish: true allow_failures: - env: srv=build + - env: srv=xpand include: - stage: Minimal env: srv=mariadb v=10.6 local=1 @@ -55,6 +56,8 @@ jobs: name: "SkySQL" - env: srv=skysql-ha name: "SkySQL with replication" + - env: srv=xpand + name: "Xpand" - stage: Community env: srv=mariadb v=10.6 diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b1cfba7..0062054c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Change Log +## [1.1.4](https://github.com/mariadb-corporation/mariadb-connector-r2dbc/tree/1.1.4) (16 Mar 2023) +[Full Changelog](https://github.com/mariadb-corporation/mariadb-connector-r2dbc/compare/1.1.3...1.1.4) + +Bugs Fixed: + +* [R2DBC-76] Wrong MEDIUM field binary decoding +* [R2DBC-77] Metadata is null when using returnGeneratedValues on server before 10.5 +* [R2DBC-79] Wrong client side parsing for named parameter when using user variable +* [R2DBC-80] add option to disable hostname verification for SslMode.TUNNEL. thanks to @shubha-rajan +* [R2DBC-81] missing parsing/builder variables for option restrictedAuth,rsaPublicKey,cachingRsaPublicKey and allowPublicKeyRetrievalString +* [R2DBC-82] wrong transactionIsolation level set/get with server without session tracking +* [R2DBC-85] adding hint in order to execute text command when useServerPrepStmts option is set +* [R2DBC-83] support xpand 0000-00-00 timestamp/date encoding + + + ## [1.1.3](https://github.com/mariadb-corporation/mariadb-connector-r2dbc/tree/1.1.3) (22 Dec 2022) [Full Changelog](https://github.com/mariadb-corporation/mariadb-connector-r2dbc/compare/1.1.2...1.1.3) diff --git a/README.md b/README.md index b75c1a0b..61071d1d 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ The MariaDB Connector is available through maven using : org.mariadb r2dbc-mariadb - 1.1.3 + 1.1.4 ``` @@ -78,39 +78,40 @@ Basic example: ### Connection options -|option| description |type|default| -|---:|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:---:|:---:| -| **`username`** | User to access database. |*string* | -| **`password`** | User password. |*string* | -| **`host`** | IP address or DNS of the database server. Multiple host can be set, separate by comma. If first host is not reachable (timeout is connectTimeout), driver use next hosts.*Not used when using option `socketPath`*. |*string*| "localhost"| -| **`port`** | Database server port number. *Not used when using option `socketPath`* |*integer*| 3306| -| **`database`** | Default database to use when establishing the connection. | *string* | -| **`connectTimeout`** | Sets the connection timeout | *Duration* | 10s| -| **`socketTimeout`** | Sets the socket timeout | *Duration* | | -| **`tcpKeepAlive`** | Sets socket keep alive | *Boolean* | false| -| **`tcpAbortiveClose`** | This option can be used in environments where connections are created and closed in rapid succession. Often, it is not possible to create a socket in such an environment after a while, since all local “ephemeral” ports are used up by TCP connections in TCP_WAIT state. Using tcpAbortiveClose works around this problem by resetting TCP connections (abortive or hard close) rather than doing an orderly close. | *boolean* | false| -| **`socket`** | Permits connections to the database through the Unix domain socket for faster connection whe server is local. | *string* | -| **`allowMultiQueries`** | Allows you to issue several SQL statements in a single call. (That is, `INSERT INTO a VALUES('b'); INSERT INTO c VALUES('d');`).

This may be a **security risk** as it allows for SQL Injection attacks. | *boolean* | false| -| **`connectionAttributes`** | When performance_schema is active, permit to send server some client information.
Those information can be retrieved on server within tables performance_schema.session_connect_attrs and performance_schema.session_account_connect_attrs. This can permit from server an identification of client/application per connection |*Map* | -| **`sessionVariables`** | Permits to set session variables upon successful connection | *Map* | -| **`tlsProtocol`** | Force TLS/SSL protocol to a specific set of TLS versions (example "TLSv1.2", "TLSv1.3"). |*List*| java default| -| **`serverSslCert`** | Permits providing server's certificate in DER form, or server's CA certificate.
This permits a self-signed certificate to be trusted. Can be used in one of 3 forms :
  • serverSslCert=/path/to/cert.pem (full path to certificate)
  • serverSslCert=classpath:relative/cert.pem (relative to current classpath)
  • as verbatim DER-encoded certificate string "------BEGIN CERTIFICATE-----"
|*String*| | -| **`clientSslCert`** | Permits providing client's certificate in DER form (use only for mutual authentication). Can be used in one of 3 forms :
  • clientSslCert=/path/to/cert.pem (full path to certificate)
  • clientSslCert=classpath:relative/cert.pem (relative to current classpath)
  • as verbatim DER-encoded certificate string "------BEGIN CERTIFICATE-----"
|*String*| | -| **`clientSslKey`** | client private key path(for mutual authentication) |*String* | | -| **`clientSslPassword`** | client private key password |*charsequence* | | -| **`sslMode`** | ssl requirement. Possible value are
  • DISABLE, // NO SSL
  • TRUST, // Encryption, but no certificate and hostname validation (DEVELOPMENT ONLY)
  • VERIFY_CA, // Encryption, certificates validation, BUT no hostname validation
  • VERIFY_FULL, // Standard SSL use: Encryption, certificate validation and hostname validation
  • TUNNEL, // Connect over a pre-created SSL tunnel. See sslContextBuilderCustomizer options
| SslMode |DISABLE| -| **`rsaPublicKey`** | only for MySQL server
Server RSA public key, for SHA256 authentication |*String* | | -| **`cachingRsaPublicKey`** | only for MySQL server
Server caching RSA public key, for cachingSHA256 authentication |*String* | | -| **`allowPublicKeyRetrieval`** | only for MySQL server
Permit retrieved Server RSA public key from server. This can create a security issue |*boolean* | true | -| **`allowPipelining`** | Permit to send queries to server without waiting for previous query to finish |*boolean* | true | -| **`useServerPrepStmts`** | Permit to indicate to use text or binary protocol for query with parameter |*boolean* | false | -| **`prepareCacheSize`** | if useServerPrepStmts = true, cache the prepared informations in a LRU cache to avoid re-preparation of command. Next use of that command, only prepared identifier and parameters (if any) will be sent to server. This mainly permit for server to avoid reparsing query. |*int* |256 | -| **`pamOtherPwd`** | Permit to provide additional password for PAM authentication with multiple authentication step. If multiple passwords, value must be URL encoded. |*string* | | -| **`autocommit`** | Set default autocommit value on connection initialization" |*boolean* | true | -| **`tinyInt1isBit`** | Convert Bit(1)/TINYINT(1) default to boolean type |*boolean* | true | -| **`restrictedAuth`** | if set, restrict authentication plugin to secure list. Default provided plugins are mysql_native_password, mysql_clear_password, client_ed25519, dialog, sha256_password and caching_sha2_password |*string* | | -| **`loopResources`** | permits to share netty EventLoopGroup among multiple async libraries/framework |*LoopResources* | | -| **`sslContextBuilderCustomizer`** | Permits to customized SSL context builder. |*UnaryOperator* | | +| option | description | type |default| +|-------------------------------------:|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|:----------------------------------:|:---:| +| **`username`** | User to access database. | *string* | +| **`password`** | User password. | *string* | +| **`host`** | IP address or DNS of the database server. Multiple host can be set, separate by comma. If first host is not reachable (timeout is connectTimeout), driver use next hosts.*Not used when using option `socketPath`*. | *string* | "localhost"| +| **`port`** | Database server port number. *Not used when using option `socketPath`* | *integer* | 3306| +| **`database`** | Default database to use when establishing the connection. | *string* | +| **`connectTimeout`** | Sets the connection timeout | *Duration* | 10s| +| **`socketTimeout`** | Sets the socket timeout | *Duration* | | +| **`tcpKeepAlive`** | Sets socket keep alive | *Boolean* | false| +| **`tcpAbortiveClose`** | This option can be used in environments where connections are created and closed in rapid succession. Often, it is not possible to create a socket in such an environment after a while, since all local “ephemeral” ports are used up by TCP connections in TCP_WAIT state. Using tcpAbortiveClose works around this problem by resetting TCP connections (abortive or hard close) rather than doing an orderly close. | *boolean* | false| +| **`socket`** | Permits connections to the database through the Unix domain socket for faster connection whe server is local. | *string* | +| **`allowMultiQueries`** | Allows you to issue several SQL statements in a single call. (That is, `INSERT INTO a VALUES('b'); INSERT INTO c VALUES('d');`).

This may be a **security risk** as it allows for SQL Injection attacks. | *boolean* | false| +| **`connectionAttributes`** | When performance_schema is active, permit to send server some client information.
Those information can be retrieved on server within tables performance_schema.session_connect_attrs and performance_schema.session_account_connect_attrs. This can permit from server an identification of client/application per connection | *Map* | +| **`sessionVariables`** | Permits to set session variables upon successful connection | *Map* | +| **`tlsProtocol`** | Force TLS/SSL protocol to a specific set of TLS versions (example "TLSv1.2", "TLSv1.3"). | *List* | java default| +| **`serverSslCert`** | Permits providing server's certificate in DER form, or server's CA certificate.
This permits a self-signed certificate to be trusted. Can be used in one of 3 forms :
  • serverSslCert=/path/to/cert.pem (full path to certificate)
  • serverSslCert=classpath:relative/cert.pem (relative to current classpath)
  • as verbatim DER-encoded certificate string "------BEGIN CERTIFICATE-----"
| *String* | | +| **`clientSslCert`** | Permits providing client's certificate in DER form (use only for mutual authentication). Can be used in one of 3 forms :
  • clientSslCert=/path/to/cert.pem (full path to certificate)
  • clientSslCert=classpath:relative/cert.pem (relative to current classpath)
  • as verbatim DER-encoded certificate string "------BEGIN CERTIFICATE-----"
| *String* | | +| **`clientSslKey`** | client private key path(for mutual authentication) | *String* | | +| **`clientSslPassword`** | client private key password | *charsequence* | | +| **`sslMode`** | ssl requirement. Possible value are
  • DISABLE, // NO SSL
  • TRUST, // Encryption, but no certificate and hostname validation (DEVELOPMENT ONLY)
  • VERIFY_CA, // Encryption, certificates validation, BUT no hostname validation
  • VERIFY_FULL, // Standard SSL use: Encryption, certificate validation and hostname validation
  • TUNNEL, // Connect over a pre-created SSL tunnel. See sslContextBuilderCustomizer options and sslTunnelDisableHostVerification
| SslMode |DISABLE| +| **`rsaPublicKey`** | only for MySQL server
Server RSA public key, for SHA256 authentication | *String* | | +| **`cachingRsaPublicKey`** | only for MySQL server
Server caching RSA public key, for cachingSHA256 authentication | *String* | | +| **`allowPublicKeyRetrieval`** | only for MySQL server
Permit retrieved Server RSA public key from server. This can create a security issue | *boolean* | true | +| **`allowPipelining`** | Permit to send queries to server without waiting for previous query to finish | *boolean* | true | +| **`useServerPrepStmts`** | Permit to indicate to use text or binary protocol for query with parameter | *boolean* | false | +| **`prepareCacheSize`** | if useServerPrepStmts = true, cache the prepared informations in a LRU cache to avoid re-preparation of command. Next use of that command, only prepared identifier and parameters (if any) will be sent to server. This mainly permit for server to avoid reparsing query. | *int* |256 | +| **`pamOtherPwd`** | Permit to provide additional password for PAM authentication with multiple authentication step. If multiple passwords, value must be URL encoded. | *string* | | +| **`autocommit`** | Set default autocommit value on connection initialization" | *boolean* | true | +| **`tinyInt1isBit`** | Convert Bit(1)/TINYINT(1) default to boolean type | *boolean* | true | +| **`restrictedAuth`** | if set, restrict authentication plugin to secure list. Default provided plugins are mysql_native_password, mysql_clear_password, client_ed25519, dialog, sha256_password and caching_sha2_password | *string* | | +| **`loopResources`** | permits to share netty EventLoopGroup among multiple async libraries/framework | *LoopResources* | | +| **`sslContextBuilderCustomizer`** | Permits to customized SSL context builder. | *UnaryOperator* | | +| **`sslTunnelDisableHostVerification`** | Disable hostname verification during SSLHandshake when SslMode.TUNNEL is set | *boolean* | | ## Failover diff --git a/pom.xml b/pom.xml index 0609bcab..88687382 100644 --- a/pom.xml +++ b/pom.xml @@ -11,7 +11,7 @@ 4.0.0 org.mariadb r2dbc-mariadb - 1.1.3 + 1.1.4 jar https://github.com/mariadb-corporation/mariadb-connector-r2dbc diff --git a/src/main/java/org/mariadb/r2dbc/MariadbConnection.java b/src/main/java/org/mariadb/r2dbc/MariadbConnection.java index a8c08a3e..d01585e3 100644 --- a/src/main/java/org/mariadb/r2dbc/MariadbConnection.java +++ b/src/main/java/org/mariadb/r2dbc/MariadbConnection.java @@ -98,7 +98,7 @@ public MariadbStatement createStatement(String sql) { throw new IllegalArgumentException("Statement cannot be empty."); } - if (this.configuration.useServerPrepStmts() || sql.contains("call")) { + if ((this.configuration.useServerPrepStmts() || sql.contains("call")) && !sql.startsWith("/*text*/")) { return new MariadbServerParameterizedQueryStatement(this.client, sql, this.configuration); } return new MariadbClientParameterizedQueryStatement(this.client, sql, this.configuration); @@ -112,7 +112,7 @@ public MariadbConnectionMetadata getMetadata() { @Override public IsolationLevel getTransactionIsolationLevel() { if (isolationLevel != null) return isolationLevel; - if ((client.getContext().getClientCapabilities() | Capabilities.CLIENT_SESSION_TRACK) > 0 + if ((client.getContext().getClientCapabilities() & Capabilities.CLIENT_SESSION_TRACK) > 0 && client.getContext().getIsolationLevel() != null) return client.getContext().getIsolationLevel(); return this.sessionIsolationLevel; @@ -221,7 +221,7 @@ public Mono setStatementTimeout(Duration timeout) { public Mono setTransactionIsolationLevel(IsolationLevel isolationLevel) { Assert.requireNonNull(isolationLevel, "isolationLevel must not be null"); - if ((client.getContext().getClientCapabilities() | Capabilities.CLIENT_SESSION_TRACK) > 0 + if ((client.getContext().getClientCapabilities() & Capabilities.CLIENT_SESSION_TRACK) > 0 && client.getContext().getIsolationLevel() != null && client.getContext().getIsolationLevel().equals(isolationLevel)) return Mono.empty(); diff --git a/src/main/java/org/mariadb/r2dbc/MariadbConnectionConfiguration.java b/src/main/java/org/mariadb/r2dbc/MariadbConnectionConfiguration.java index b561e634..c6855f5e 100644 --- a/src/main/java/org/mariadb/r2dbc/MariadbConnectionConfiguration.java +++ b/src/main/java/org/mariadb/r2dbc/MariadbConnectionConfiguration.java @@ -95,7 +95,8 @@ private MariadbConnectionConfiguration( boolean tinyInt1isBit, String restrictedAuth, @Nullable LoopResources loopResources, - @Nullable UnaryOperator sslContextBuilderCustomizer) { + @Nullable UnaryOperator sslContextBuilderCustomizer, + boolean sslTunnelDisableHostVerification) { this.haMode = haMode == null ? HaMode.NONE : HaMode.from(haMode); this.connectTimeout = connectTimeout == null ? Duration.ofSeconds(10) : connectTimeout; this.tcpKeepAlive = tcpKeepAlive == null ? Boolean.FALSE : tcpKeepAlive; @@ -128,6 +129,7 @@ private MariadbConnectionConfiguration( clientSslKey, clientSslPassword, tlsProtocol, + sslTunnelDisableHostVerification, sslContextBuilderCustomizer); } this.rsaPublicKey = rsaPublicKey; @@ -195,6 +197,28 @@ public static Builder fromOptions(ConnectionFactoryOptions connectionFactoryOpti connectionFactoryOptions.getValue(MariadbConnectionFactoryProvider.TCP_KEEP_ALIVE))); } + if (connectionFactoryOptions.hasOption( + MariadbConnectionFactoryProvider.ALLOW_PUBLIC_KEY_RETRIEVAL)) { + builder.allowPublicKeyRetrieval( + boolValue( + connectionFactoryOptions.getValue( + MariadbConnectionFactoryProvider.ALLOW_PUBLIC_KEY_RETRIEVAL))); + } + + if (connectionFactoryOptions.hasOption( + MariadbConnectionFactoryProvider.CACHING_RSA_PUBLIC_KEY)) { + builder.cachingRsaPublicKey( + (String) + connectionFactoryOptions.getValue( + MariadbConnectionFactoryProvider.CACHING_RSA_PUBLIC_KEY)); + } + + if (connectionFactoryOptions.hasOption(MariadbConnectionFactoryProvider.RSA_PUBLIC_KEY)) { + builder.rsaPublicKey( + (String) + connectionFactoryOptions.getValue(MariadbConnectionFactoryProvider.RSA_PUBLIC_KEY)); + } + if (connectionFactoryOptions.hasOption(MariadbConnectionFactoryProvider.TCP_ABORTIVE_CLOSE)) { builder.tcpAbortiveClose( boolValue( @@ -316,6 +340,13 @@ public static Builder fromOptions(ConnectionFactoryOptions connectionFactoryOpti } builder.pamOtherPwd(pairs); } + + if (connectionFactoryOptions.hasOption(MariadbConnectionFactoryProvider.RESTRICTED_AUTH)) { + builder.restrictedAuth( + (String) + connectionFactoryOptions.getValue(MariadbConnectionFactoryProvider.RESTRICTED_AUTH)); + } + if (connectionFactoryOptions.hasOption(MariadbConnectionFactoryProvider.LOOP_RESOURCES)) { LoopResources loopResources = (LoopResources) @@ -323,6 +354,14 @@ public static Builder fromOptions(ConnectionFactoryOptions connectionFactoryOpti builder.loopResources(loopResources); } + if (connectionFactoryOptions.hasOption( + MariadbConnectionFactoryProvider.SSL_TUNNEL_DISABLE_HOST_VERIFICATION)) { + builder.sslTunnelDisableHostVerification( + boolValue( + connectionFactoryOptions.getValue( + MariadbConnectionFactoryProvider.SSL_TUNNEL_DISABLE_HOST_VERIFICATION))); + } + if (connectionFactoryOptions.hasOption( MariadbConnectionFactoryProvider.SSL_CONTEXT_BUILDER_CUSTOMIZER)) { builder.sslContextBuilderCustomizer( @@ -539,7 +578,7 @@ public String toString() { + ", pamOtherPwd=" + hiddenPamPwd + ", restrictedAuth=" - + restrictedAuth + + (restrictedAuth == null ? "" : Arrays.toString(restrictedAuth)) + '}'; } @@ -587,6 +626,8 @@ public static final class Builder implements Cloneable { private Builder() {} + private boolean sslTunnelDisableHostVerification; + /** * Returns a configured {@link MariadbConnectionConfiguration}. * @@ -641,7 +682,8 @@ public MariadbConnectionConfiguration build() { this.tinyInt1isBit, this.restrictedAuth, this.loopResources, - this.sslContextBuilderCustomizer); + this.sslContextBuilderCustomizer, + this.sslTunnelDisableHostVerification); } /** @@ -976,6 +1018,11 @@ public Builder sslContextBuilderCustomizer( return this; } + public Builder sslTunnelDisableHostVerification(boolean sslTunnelDisableHostVerification) { + this.sslTunnelDisableHostVerification = sslTunnelDisableHostVerification; + return this; + } + @Override public Builder clone() throws CloneNotSupportedException { return (Builder) super.clone(); @@ -1054,6 +1101,8 @@ public String toString() { + clientSslPassword + ", sslMode=" + sslMode + + ", sslTunnelDisableHostVerification=" + + sslTunnelDisableHostVerification + ", pamOtherPwd=" + hiddenPamPwd + ", tinyInt1isBit=" diff --git a/src/main/java/org/mariadb/r2dbc/MariadbConnectionFactoryProvider.java b/src/main/java/org/mariadb/r2dbc/MariadbConnectionFactoryProvider.java index 571e69d9..c603a229 100644 --- a/src/main/java/org/mariadb/r2dbc/MariadbConnectionFactoryProvider.java +++ b/src/main/java/org/mariadb/r2dbc/MariadbConnectionFactoryProvider.java @@ -25,7 +25,7 @@ public final class MariadbConnectionFactoryProvider implements ConnectionFactory public static final Option ALLOW_PIPELINING = Option.valueOf("allowPipelining"); public static final Option USE_SERVER_PREPARE = Option.valueOf("useServerPrepStmts"); public static final Option ISOLATION_LEVEL = Option.valueOf("isolationLevel"); - public static final Option AUTO_COMMIT = Option.valueOf("autoCommit"); + public static final Option AUTO_COMMIT = Option.valueOf("autocommit"); public static final Option TINY_IS_BIT = Option.valueOf("tinyInt1isBit"); public static final Option PREPARE_CACHE_SIZE = Option.valueOf("prepareCacheSize"); public static final Option SSL_MODE = Option.valueOf("sslMode"); @@ -38,8 +38,18 @@ public final class MariadbConnectionFactoryProvider implements ConnectionFactory public static final Option TCP_ABORTIVE_CLOSE = Option.valueOf("tcpAbortiveClose"); public static final Option SESSION_VARIABLES = Option.valueOf("sessionVariables"); public static final Option LOOP_RESOURCES = Option.valueOf("loopResources"); + public static final Option ALLOW_PUBLIC_KEY_RETRIEVAL = + Option.valueOf("allowPublicKeyRetrieval"); + + public static final Option CACHING_RSA_PUBLIC_KEY = Option.valueOf("cachingRsaPublicKey"); + public static final Option RSA_PUBLIC_KEY = Option.valueOf("rsaPublicKey"); + + public static final Option RESTRICTED_AUTH = Option.valueOf("restrictedAuth"); + public static final Option> SSL_CONTEXT_BUILDER_CUSTOMIZER = Option.valueOf("sslContextBuilderCustomizer"); + public static final Option SSL_TUNNEL_DISABLE_HOST_VERIFICATION = + Option.valueOf("sslTunnelDisableHostVerification"); static MariadbConnectionConfiguration createConfiguration( ConnectionFactoryOptions connectionFactoryOptions) { diff --git a/src/main/java/org/mariadb/r2dbc/authentication/standard/Sha256PasswordPluginFlow.java b/src/main/java/org/mariadb/r2dbc/authentication/standard/Sha256PasswordPluginFlow.java index 4daa5c1f..6cbaa9b6 100644 --- a/src/main/java/org/mariadb/r2dbc/authentication/standard/Sha256PasswordPluginFlow.java +++ b/src/main/java/org/mariadb/r2dbc/authentication/standard/Sha256PasswordPluginFlow.java @@ -113,7 +113,7 @@ public ClientMessage next( } else { if (!configuration.allowPublicKeyRetrieval()) { throw new R2dbcNonTransientResourceException( - "RSA public key is not available client side (option " + "serverRsaPublicKeyFile)", + "RSA public key is not available client side (option serverRsaPublicKeyFile)", "S1009"); } state = State.REQUEST_SERVER_KEY; diff --git a/src/main/java/org/mariadb/r2dbc/client/SimpleClient.java b/src/main/java/org/mariadb/r2dbc/client/SimpleClient.java index 0784b281..bd297aaf 100644 --- a/src/main/java/org/mariadb/r2dbc/client/SimpleClient.java +++ b/src/main/java/org/mariadb/r2dbc/client/SimpleClient.java @@ -106,7 +106,9 @@ protected SimpleClient( sslContext.newEngine( connection.channel().alloc(), hostAddress.getHost(), hostAddress.getPort()); SSLParameters sslParameters = engine.getSSLParameters(); - sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); + if (!configuration.getSslConfig().tunnelHostVerificationDisabled()) { + sslParameters.setEndpointIdentificationAlgorithm("HTTPS"); + } engine.setSSLParameters(sslParameters); } else { engine = sslContext.newEngine(connection.channel().alloc()); diff --git a/src/main/java/org/mariadb/r2dbc/codec/list/BigDecimalCodec.java b/src/main/java/org/mariadb/r2dbc/codec/list/BigDecimalCodec.java index a031395d..910c07a6 100644 --- a/src/main/java/org/mariadb/r2dbc/codec/list/BigDecimalCodec.java +++ b/src/main/java/org/mariadb/r2dbc/codec/list/BigDecimalCodec.java @@ -109,10 +109,10 @@ public BigDecimal decodeBinary( return BigDecimal.valueOf((int) buf.readShortLE()); case MEDIUMINT: - if (!column.isSigned()) { - return BigDecimal.valueOf((buf.readUnsignedMediumLE())); - } - return BigDecimal.valueOf(buf.readMediumLE()); + BigDecimal v = + BigDecimal.valueOf(column.isSigned() ? buf.readMediumLE() : buf.readUnsignedMediumLE()); + buf.readByte(); // needed since binary protocol exchange for medium are on 4 bytes + return v; case INTEGER: if (!column.isSigned()) { diff --git a/src/main/java/org/mariadb/r2dbc/codec/list/BigIntegerCodec.java b/src/main/java/org/mariadb/r2dbc/codec/list/BigIntegerCodec.java index a505f3b8..c2b66662 100644 --- a/src/main/java/org/mariadb/r2dbc/codec/list/BigIntegerCodec.java +++ b/src/main/java/org/mariadb/r2dbc/codec/list/BigIntegerCodec.java @@ -120,10 +120,10 @@ public BigInteger decodeBinary( return BigInteger.valueOf((int) buf.readShortLE()); case MEDIUMINT: - if (!column.isSigned()) { - return BigInteger.valueOf((buf.readUnsignedMediumLE())); - } - return BigInteger.valueOf(buf.readMediumLE()); + BigInteger v = + BigInteger.valueOf(column.isSigned() ? buf.readMediumLE() : buf.readUnsignedMediumLE()); + buf.readByte(); // needed since binary protocol exchange for medium are on 4 bytes + return v; case INTEGER: if (!column.isSigned()) { diff --git a/src/main/java/org/mariadb/r2dbc/codec/list/BooleanCodec.java b/src/main/java/org/mariadb/r2dbc/codec/list/BooleanCodec.java index c1fc5e5e..87933d88 100644 --- a/src/main/java/org/mariadb/r2dbc/codec/list/BooleanCodec.java +++ b/src/main/java/org/mariadb/r2dbc/codec/list/BooleanCodec.java @@ -109,7 +109,10 @@ public Boolean decodeBinary( return buf.readShortLE() != 0; case MEDIUMINT: - return buf.readMediumLE() != 0; + boolean b = buf.readMediumLE() != 0; + buf.readByte(); // needed since binary protocol exchange for medium are on 4 bytes + return b; + case INTEGER: return buf.readIntLE() != 0; case BIGINT: diff --git a/src/main/java/org/mariadb/r2dbc/codec/list/ByteCodec.java b/src/main/java/org/mariadb/r2dbc/codec/list/ByteCodec.java index 1958c86a..949213ef 100644 --- a/src/main/java/org/mariadb/r2dbc/codec/list/ByteCodec.java +++ b/src/main/java/org/mariadb/r2dbc/codec/list/ByteCodec.java @@ -139,6 +139,7 @@ public Byte decodeBinary( case MEDIUMINT: result = column.isSigned() ? buf.readMediumLE() : buf.readUnsignedMediumLE(); + buf.readByte(); // needed since binary protocol exchange for medium are on 4 bytes break; case INTEGER: diff --git a/src/main/java/org/mariadb/r2dbc/codec/list/DoubleCodec.java b/src/main/java/org/mariadb/r2dbc/codec/list/DoubleCodec.java index 0b67e257..4b65168b 100644 --- a/src/main/java/org/mariadb/r2dbc/codec/list/DoubleCodec.java +++ b/src/main/java/org/mariadb/r2dbc/codec/list/DoubleCodec.java @@ -101,10 +101,9 @@ public Double decodeBinary( return (double) buf.readShortLE(); case MEDIUMINT: - if (!column.isSigned()) { - return (double) buf.readUnsignedMediumLE(); - } - return (double) buf.readMediumLE(); + double v = column.isSigned() ? buf.readMediumLE() : buf.readUnsignedMediumLE(); + buf.readByte(); // needed since binary protocol exchange for medium are on 4 bytes + return v; case INTEGER: if (!column.isSigned()) { diff --git a/src/main/java/org/mariadb/r2dbc/codec/list/FloatCodec.java b/src/main/java/org/mariadb/r2dbc/codec/list/FloatCodec.java index fec8d9ff..7304dc59 100644 --- a/src/main/java/org/mariadb/r2dbc/codec/list/FloatCodec.java +++ b/src/main/java/org/mariadb/r2dbc/codec/list/FloatCodec.java @@ -102,10 +102,9 @@ public Float decodeBinary( return (float) buf.readShortLE(); case MEDIUMINT: - if (!column.isSigned()) { - return (float) buf.readUnsignedMediumLE(); - } - return (float) buf.readMediumLE(); + float v = column.isSigned() ? buf.readMediumLE() : buf.readUnsignedMediumLE(); + buf.readByte(); // needed since binary protocol exchange for medium are on 4 bytes + return v; case INTEGER: if (!column.isSigned()) { diff --git a/src/main/java/org/mariadb/r2dbc/codec/list/IntCodec.java b/src/main/java/org/mariadb/r2dbc/codec/list/IntCodec.java index 392f63ca..8f2b344b 100644 --- a/src/main/java/org/mariadb/r2dbc/codec/list/IntCodec.java +++ b/src/main/java/org/mariadb/r2dbc/codec/list/IntCodec.java @@ -123,6 +123,7 @@ public Integer decodeBinary( case MEDIUMINT: result = column.isSigned() ? buf.readMediumLE() : buf.readUnsignedMediumLE(); + buf.readByte(); // needed since binary protocol exchange for medium are on 4 bytes break; case BIGINT: diff --git a/src/main/java/org/mariadb/r2dbc/codec/list/LocalDateCodec.java b/src/main/java/org/mariadb/r2dbc/codec/list/LocalDateCodec.java index 25a7f1a8..9d7b575b 100644 --- a/src/main/java/org/mariadb/r2dbc/codec/list/LocalDateCodec.java +++ b/src/main/java/org/mariadb/r2dbc/codec/list/LocalDateCodec.java @@ -143,6 +143,7 @@ public LocalDate decodeBinary( if (length > 4) { buf.skipBytes(length - 4); } + if (year == 0 && month == 0 && dayOfMonth == 0) return null; return LocalDate.of(year, month, dayOfMonth); } return null; diff --git a/src/main/java/org/mariadb/r2dbc/codec/list/LocalDateTimeCodec.java b/src/main/java/org/mariadb/r2dbc/codec/list/LocalDateTimeCodec.java index 37d8682b..517e9f85 100644 --- a/src/main/java/org/mariadb/r2dbc/codec/list/LocalDateTimeCodec.java +++ b/src/main/java/org/mariadb/r2dbc/codec/list/LocalDateTimeCodec.java @@ -182,6 +182,12 @@ public LocalDateTime decodeBinary( microseconds = buf.readUnsignedIntLE(); } } + if (year == 0 + && month == 0 + && dayOfMonth == 0 + && hour == 0 + && minutes == 0 + && seconds == 0) return null; } else return null; break; diff --git a/src/main/java/org/mariadb/r2dbc/codec/list/LongCodec.java b/src/main/java/org/mariadb/r2dbc/codec/list/LongCodec.java index 7ce52cc4..41c5ab65 100644 --- a/src/main/java/org/mariadb/r2dbc/codec/list/LongCodec.java +++ b/src/main/java/org/mariadb/r2dbc/codec/list/LongCodec.java @@ -179,10 +179,9 @@ public Long decodeBinary( return (long) buf.readShortLE(); case MEDIUMINT: - if (!column.isSigned()) { - return (long) buf.readUnsignedMediumLE(); - } - return (long) buf.readMediumLE(); + long v = column.isSigned() ? buf.readMediumLE() : buf.readUnsignedMediumLE(); + buf.readByte(); // needed since binary protocol exchange for medium are on 4 bytes + return v; case INTEGER: if (!column.isSigned()) { diff --git a/src/main/java/org/mariadb/r2dbc/codec/list/ShortCodec.java b/src/main/java/org/mariadb/r2dbc/codec/list/ShortCodec.java index 0d01387b..f7bcf7ed 100644 --- a/src/main/java/org/mariadb/r2dbc/codec/list/ShortCodec.java +++ b/src/main/java/org/mariadb/r2dbc/codec/list/ShortCodec.java @@ -112,6 +112,7 @@ public Short decodeBinary( case MEDIUMINT: result = column.isSigned() ? buf.readMediumLE() : buf.readUnsignedMediumLE(); + buf.readByte(); // needed since binary protocol exchange for medium are on 4 bytes break; case INTEGER: diff --git a/src/main/java/org/mariadb/r2dbc/util/ClientParser.java b/src/main/java/org/mariadb/r2dbc/util/ClientParser.java index 053c9d88..31bf9d09 100644 --- a/src/main/java/org/mariadb/r2dbc/util/ClientParser.java +++ b/src/main/java/org/mariadb/r2dbc/util/ClientParser.java @@ -124,7 +124,6 @@ public static ClientParser parameterPartsCheckReturning( case ':': if (state == LexState.Normal) { int beginPos = i; - paramPositions.add(i); while (++i < queryLength && (car = query[i]) != ' ' && ((car >= '0' && car <= '9') @@ -132,7 +131,13 @@ public static ClientParser parameterPartsCheckReturning( || (car >= 'a' && car <= 'z') || car == '-' || car == '_')) {} + if (beginPos + 1 == i) { + // this is a ":" not followed by name or index + // so not a parameter + break; + } paramNameList.add(new String(query, beginPos + 1, i - (beginPos + 1))); + paramPositions.add(beginPos); paramPositions.add(i); } break; @@ -358,7 +363,6 @@ public static ClientParser parameterParts(String queryString, boolean noBackslas case ':': if (state == LexState.Normal) { int beginPos = i; - paramPositions.add(i); while (++i < queryLength && (car = query[i]) != ' ' && ((car >= '0' && car <= '9') @@ -366,7 +370,13 @@ public static ClientParser parameterParts(String queryString, boolean noBackslas || (car >= 'a' && car <= 'z') || car == '-' || car == '_')) {} + if (beginPos + 1 == i) { + // this is a ":" not followed by name or index + // so not a parameter + break; + } paramNameList.add(new String(query, beginPos + 1, i - (beginPos + 1))); + paramPositions.add(beginPos); paramPositions.add(i); } break; @@ -489,6 +499,7 @@ public static boolean hasParameter(String queryString, boolean noBackslashEscape case ':': if (state == LexState.Normal) { + int beginPos = i; while (++i < queryLength && (car = query[i]) != ' ' && ((car >= '0' && car <= '9') @@ -496,6 +507,11 @@ public static boolean hasParameter(String queryString, boolean noBackslashEscape || (car >= 'a' && car <= 'z') || car == '-' || car == '_')) {} + if (beginPos + 1 == i) { + // this is a ":" not followed by name or index + // so not a parameter + break; + } return true; } break; diff --git a/src/main/java/org/mariadb/r2dbc/util/SslConfig.java b/src/main/java/org/mariadb/r2dbc/util/SslConfig.java index 3b545a50..79500feb 100644 --- a/src/main/java/org/mariadb/r2dbc/util/SslConfig.java +++ b/src/main/java/org/mariadb/r2dbc/util/SslConfig.java @@ -7,7 +7,11 @@ import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.ssl.util.InsecureTrustManagerFactory; import io.r2dbc.spi.R2dbcTransientResourceException; -import java.io.*; +import java.io.ByteArrayInputStream; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; import java.util.List; import java.util.function.UnaryOperator; import javax.net.ssl.SSLException; @@ -26,6 +30,8 @@ public class SslConfig { private SslContextBuilder sslContextBuilder; private UnaryOperator sslContextBuilderCustomizer; + private boolean disableHostVerification; + public SslConfig( SslMode sslMode, String serverSslCert, @@ -33,6 +39,7 @@ public SslConfig( String clientSslKey, CharSequence clientSslPassword, List tlsProtocol, + boolean disableHostVerification, UnaryOperator sslContextBuilderCustomizer) throws R2dbcTransientResourceException { this.sslMode = sslMode; @@ -41,6 +48,7 @@ public SslConfig( this.tlsProtocol = tlsProtocol; this.clientSslKey = clientSslKey; this.clientSslPassword = clientSslPassword; + this.disableHostVerification = disableHostVerification; this.sslContextBuilderCustomizer = sslContextBuilderCustomizer; if (sslMode != SslMode.DISABLE) { this.sslContextBuilder = getSslContextBuilder(); @@ -55,6 +63,10 @@ public SslMode getSslMode() { return sslMode; } + public boolean tunnelHostVerificationDisabled() { + return this.disableHostVerification; + } + private SslContextBuilder getSslContextBuilder() throws R2dbcTransientResourceException { final SslContextBuilder sslCtxBuilder = SslContextBuilder.forClient(); diff --git a/src/test/java/org/mariadb/r2dbc/BaseConnectionTest.java b/src/test/java/org/mariadb/r2dbc/BaseConnectionTest.java index dbe24a14..36d5cdef 100644 --- a/src/test/java/org/mariadb/r2dbc/BaseConnectionTest.java +++ b/src/test/java/org/mariadb/r2dbc/BaseConnectionTest.java @@ -27,7 +27,7 @@ public class BaseConnectionTest { public static final boolean isWindows = System.getProperty("os.name").toLowerCase().contains("win"); - public static int initialConnectionNumber = -1; + public static Integer initialConnectionNumber = -1; public static TcpProxy proxy; private static final Random rand = new Random(); public static final Boolean backslashEscape = @@ -48,12 +48,14 @@ public void assertThrows( private class Follow implements BeforeEachCallback, AfterEachCallback { @Override public void beforeEach(ExtensionContext extensionContext) throws Exception { - initialConnectionNumber = - sharedConn - .createStatement("SHOW STATUS WHERE `variable_name` = 'Threads_connected'") - .execute() - .flatMap(r -> r.map((row, metadata) -> row.get(1, Integer.class))) - .blockLast(); + if (!isXpand()) { + initialConnectionNumber = + sharedConn + .createStatement("SHOW STATUS WHERE `variable_name` = 'Threads_connected'") + .execute() + .flatMap(r -> r.map((row, metadata) -> row.get(1, Integer.class))) + .blockLast(); + } initialTest = Instant.now(); System.out.println( " test : " + extensionContext.getTestMethod().get().getName() + " begin"); @@ -61,44 +63,45 @@ public void beforeEach(ExtensionContext extensionContext) throws Exception { @AfterEach public void afterEach(ExtensionContext extensionContext) throws Exception { - int finalConnectionNumber = - sharedConn - .createStatement("SHOW STATUS WHERE `variable_name` = 'Threads_connected'") - .execute() - .flatMap(r -> r.map((row, metadata) -> row.get(1, Integer.class))) - .onErrorResume( - t -> { - t.printStackTrace(); - return Mono.just(-999999); - }) - .blockLast(); - if (finalConnectionNumber - initialConnectionNumber != 0) { - int retry = 5; - do { - Thread.sleep(50); - finalConnectionNumber = - sharedConn - .createStatement("SHOW STATUS WHERE `variable_name` = 'Threads_connected'") - .execute() - .flatMap(r -> r.map((row, metadata) -> row.get(1, Integer.class))) - .onErrorResume( - t -> { - t.printStackTrace(); - return Mono.just(-999999); - }) - .blockLast(); - } while (retry-- > 0 && finalConnectionNumber != initialConnectionNumber); - - if (finalConnectionNumber - initialConnectionNumber != 0) { - System.err.printf( - "%s: Error connection not ended : changed=%s (initial:%s ended:%s)%n", - extensionContext.getTestMethod().get(), - (finalConnectionNumber - initialConnectionNumber), - initialConnectionNumber, - finalConnectionNumber); + if (!isXpand()) { + Integer finalConnectionNumber = + sharedConn + .createStatement("SHOW STATUS WHERE `variable_name` = 'Threads_connected'") + .execute() + .flatMap(r -> r.map((row, metadata) -> row.get(1, Integer.class))) + .onErrorResume( + t -> { + t.printStackTrace(); + return Mono.just(-999999); + }) + .blockLast(); + if (finalConnectionNumber != null && finalConnectionNumber - initialConnectionNumber != 0) { + int retry = 5; + do { + Thread.sleep(50); + finalConnectionNumber = + sharedConn + .createStatement("SHOW STATUS WHERE `variable_name` = 'Threads_connected'") + .execute() + .flatMap(r -> r.map((row, metadata) -> row.get(1, Integer.class))) + .onErrorResume( + t -> { + t.printStackTrace(); + return Mono.just(-999999); + }) + .blockLast(); + } while (retry-- > 0 && finalConnectionNumber != initialConnectionNumber); + + if (finalConnectionNumber - initialConnectionNumber != 0) { + System.err.printf( + "%s: Error connection not ended : changed=%s (initial:%s ended:%s)%n", + extensionContext.getTestMethod().get(), + (finalConnectionNumber - initialConnectionNumber), + initialConnectionNumber, + finalConnectionNumber); + } } } - System.out.println( " test : " + extensionContext.getTestMethod().get().getName() @@ -120,6 +123,7 @@ public void afterEach(ExtensionContext extensionContext) throws Exception { public static void beforeAll() throws Exception { sharedConn = factory.create().block(); + MariadbConnectionConfiguration confPipeline = TestConfiguration.defaultBuilder.clone().useServerPrepStmts(true).build(); sharedConnPrepare = new MariadbConnectionFactory(confPipeline).create().block(); @@ -187,6 +191,11 @@ public static boolean isMariaDBServer() { return meta.isMariaDBServer(); } + public static boolean isXpand() { + MariadbConnectionMetadata meta = sharedConn.getMetadata(); + return meta.getDatabaseVersion().toLowerCase().contains("xpand"); + } + public static boolean minVersion(int major, int minor, int patch) { MariadbConnectionMetadata meta = sharedConn.getMetadata(); return meta.minVersion(major, minor, patch); diff --git a/src/test/java/org/mariadb/r2dbc/integration/ConfigurationTest.java b/src/test/java/org/mariadb/r2dbc/integration/ConfigurationTest.java index a6b4f86d..a0c3838b 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/ConfigurationTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/ConfigurationTest.java @@ -336,11 +336,11 @@ void confStringValue() { builder.pamOtherPwd(new String[] {"fff", "ddd"}); builder.tlsProtocol((String[]) null); Assertions.assertEquals( - "Builder{rsaPublicKey=null, cachingRsaPublicKey=null, allowPublicKeyRetrieval=false, username=admin, connectTimeout=null, tcpKeepAlive=null, tcpAbortiveClose=null, transactionReplay=null, database=dbname, host=localhost, sessionVariables=null, connectionAttributes=null, password=*, restrictedAuth=null, port=3306, hosts={}, socket=null, allowMultiQueries=false, allowPipelining=true, useServerPrepStmts=false, prepareCacheSize=null, isolationLevel=null, tlsProtocol=null, serverSslCert=null, clientSslCert=null, clientSslKey=null, clientSslPassword=null, sslMode=TRUST, pamOtherPwd=*,*, tinyInt1isBit=false, autoCommit=true}", + "Builder{rsaPublicKey=null, cachingRsaPublicKey=null, allowPublicKeyRetrieval=false, username=admin, connectTimeout=null, tcpKeepAlive=null, tcpAbortiveClose=null, transactionReplay=null, database=dbname, host=localhost, sessionVariables=null, connectionAttributes=null, password=*, restrictedAuth=null, port=3306, hosts={}, socket=null, allowMultiQueries=false, allowPipelining=true, useServerPrepStmts=false, prepareCacheSize=null, isolationLevel=null, tlsProtocol=null, serverSslCert=null, clientSslCert=null, clientSslKey=null, clientSslPassword=null, sslMode=TRUST, sslTunnelDisableHostVerification=false, pamOtherPwd=*,*, tinyInt1isBit=false, autoCommit=true}", builder.toString()); builder.tlsProtocol((String) null); Assertions.assertEquals( - "Builder{rsaPublicKey=null, cachingRsaPublicKey=null, allowPublicKeyRetrieval=false, username=admin, connectTimeout=null, tcpKeepAlive=null, tcpAbortiveClose=null, transactionReplay=null, database=dbname, host=localhost, sessionVariables=null, connectionAttributes=null, password=*, restrictedAuth=null, port=3306, hosts={}, socket=null, allowMultiQueries=false, allowPipelining=true, useServerPrepStmts=false, prepareCacheSize=null, isolationLevel=null, tlsProtocol=null, serverSslCert=null, clientSslCert=null, clientSslKey=null, clientSslPassword=null, sslMode=TRUST, pamOtherPwd=*,*, tinyInt1isBit=false, autoCommit=true}", + "Builder{rsaPublicKey=null, cachingRsaPublicKey=null, allowPublicKeyRetrieval=false, username=admin, connectTimeout=null, tcpKeepAlive=null, tcpAbortiveClose=null, transactionReplay=null, database=dbname, host=localhost, sessionVariables=null, connectionAttributes=null, password=*, restrictedAuth=null, port=3306, hosts={}, socket=null, allowMultiQueries=false, allowPipelining=true, useServerPrepStmts=false, prepareCacheSize=null, isolationLevel=null, tlsProtocol=null, serverSslCert=null, clientSslCert=null, clientSslKey=null, clientSslPassword=null, sslMode=TRUST, sslTunnelDisableHostVerification=false, pamOtherPwd=*,*, tinyInt1isBit=false, autoCommit=true}", builder.toString()); MariadbConnectionConfiguration conf = builder.build(); Assertions.assertEquals( diff --git a/src/test/java/org/mariadb/r2dbc/integration/ConnectionMetadataTest.java b/src/test/java/org/mariadb/r2dbc/integration/ConnectionMetadataTest.java index 70caa211..1b0f7465 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/ConnectionMetadataTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/ConnectionMetadataTest.java @@ -18,7 +18,7 @@ public class ConnectionMetadataTest extends BaseConnectionTest { void connectionMeta() { ConnectionMetadata meta = sharedConn.getMetadata(); assertEquals(meta.getDatabaseProductName(), isMariaDBServer() ? "MariaDB" : "MySQL"); - if (isMariaDBServer()) { + if (isMariaDBServer() && !isXpand()) { assertTrue(meta.getDatabaseVersion().contains("10.")); } else { assertTrue( diff --git a/src/test/java/org/mariadb/r2dbc/integration/ConnectionTest.java b/src/test/java/org/mariadb/r2dbc/integration/ConnectionTest.java index bfc8ae0f..051d7a8a 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/ConnectionTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/ConnectionTest.java @@ -444,6 +444,7 @@ void multipleBeginWithIsolation( @Test void beginTransactionWithIsolation() throws Exception { + Assumptions.assumeFalse(isXpand()); TransactionDefinition transactionDefinition = MariadbTransactionDefinition.READ_ONLY.isolationLevel(IsolationLevel.READ_COMMITTED); TransactionDefinition transactionDefinition2 = @@ -509,20 +510,28 @@ void queryAfterClose() throws Exception { } private void consume(Connection connection) { - int numberOfUserCol = 41; - Statement statement = connection.createStatement("select * FROM mysql.user LIMIT 1"); - Flux.from(statement.execute()) - .flatMap( - it -> - it.map( - (row, rowMetadata) -> { - Object[] objs = new Object[numberOfUserCol]; - for (int i = 0; i < numberOfUserCol; i++) { - objs[i] = row.get(i); - } - return objs; - })) - .blockLast(); + if (isXpand()) { + Statement statement = connection.createStatement("select 1"); + Flux.from(statement.execute()) + .flatMap(it -> it.map((row, rowMetadata) -> row.get(0))) + .blockLast(); + + } else { + int numberOfUserCol = 41; + Statement statement = connection.createStatement("select * FROM mysql.user LIMIT 1"); + Flux.from(statement.execute()) + .flatMap( + it -> + it.map( + (row, rowMetadata) -> { + Object[] objs = new Object[numberOfUserCol]; + for (int i = 0; i < numberOfUserCol; i++) { + objs[i] = row.get(i); + } + return objs; + })) + .blockLast(); + } } @Test @@ -703,6 +712,7 @@ void getTransactionIsolationLevel() { @Test void getDatabase() { + Assumptions.assumeFalse(isXpand()); MariadbConnection connection = new MariadbConnectionFactory(TestConfiguration.defaultBuilder.build()).create().block(); assertEquals(TestConfiguration.database, connection.getDatabase()); @@ -821,10 +831,14 @@ void useTransaction(MariadbConnection conn) { @Test void wrongSavePoint() { sharedConn.createStatement("START TRANSACTION").execute().blockLast(); - assertThrows( - Exception.class, - () -> sharedConn.rollbackTransactionToSavepoint("wrong").block(), - "SAVEPOINT wrong does not exist"); + try { + sharedConn.rollbackTransactionToSavepoint("wrong").block(); + Assertions.fail("Must have thrown error"); + } catch (Exception e) { + Assertions.assertTrue( + e.getMessage().contains("Savepoint does not exist") + || e.getMessage().contains("SAVEPOINT wrong does not exist")); + } sharedConn.rollbackTransaction().block(); } @@ -891,6 +905,7 @@ public void isolationLevel() { Assertions.assertThrows( Exception.class, () -> connection.setTransactionIsolationLevel(null).block()); for (IsolationLevel level : levels) { + System.out.println(level); connection.setTransactionIsolationLevel(level).block(); assertEquals(level, connection.getTransactionIsolationLevel()); } @@ -971,7 +986,8 @@ public void errorOnConnection() throws Throwable { Assumptions.assumeTrue( !"maxscale".equals(System.getenv("srv")) && !"skysql-ha".equals(System.getenv("srv")) - && !"skysql".equals(System.getenv("srv"))); + && !"skysql".equals(System.getenv("srv")) + && !isXpand()); BigInteger maxConn = sharedConn @@ -1065,7 +1081,8 @@ public void queryTimeout() throws Throwable { Assumptions.assumeTrue( !"maxscale".equals(System.getenv("srv")) && !"skysql".equals(System.getenv("srv")) - && !"skysql-ha".equals(System.getenv("srv"))); + && !"skysql-ha".equals(System.getenv("srv")) + && !isXpand()); MariadbConnection connection = new MariadbConnectionFactory(TestConfiguration.defaultBuilder.clone().build()) .create() diff --git a/src/test/java/org/mariadb/r2dbc/integration/ErrorTest.java b/src/test/java/org/mariadb/r2dbc/integration/ErrorTest.java index 646f542f..22dca512 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/ErrorTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/ErrorTest.java @@ -56,9 +56,8 @@ void permissionDenied() throws Exception { .expectErrorMatches( throwable -> throwable instanceof R2dbcNonTransientResourceException - && (throwable - .getMessage() - .contains("Access denied for user 'userWithoutRight'"))) + && (throwable.getMessage().contains("Access denied for user 'userWithoutRight'") + || throwable.getMessage().contains("Insufficient user permissions"))) .verify(); conf = @@ -75,10 +74,11 @@ void permissionDenied() throws Exception { throwable -> throwable instanceof R2dbcNonTransientResourceException && throwable.getMessage().contains("Fail to establish connection to") - && throwable - .getCause() - .getMessage() - .contains("Access denied for user 'userWithoutRight'")) + && (throwable + .getCause() + .getMessage() + .contains("Access denied for user 'userWithoutRight'") + || throwable.getMessage().contains("Access denied"))) .verify(); } @@ -105,7 +105,8 @@ void rollbackException() { Assumptions.assumeTrue( !"maxscale".equals(System.getenv("srv")) && !"skysql".equals(System.getenv("srv")) - && !"skysql-ha".equals(System.getenv("srv"))); + && !"skysql-ha".equals(System.getenv("srv")) + && !isXpand()); MariadbConnection connection = null; MariadbConnection connection2 = null; try { diff --git a/src/test/java/org/mariadb/r2dbc/integration/FailoverConnectionTest.java b/src/test/java/org/mariadb/r2dbc/integration/FailoverConnectionTest.java index 9d700b86..87facf85 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/FailoverConnectionTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/FailoverConnectionTest.java @@ -199,7 +199,8 @@ void transactionReplayFailingDuringCmd() throws Exception { Assumptions.assumeTrue( !"maxscale".equals(System.getenv("srv")) && !"skysql".equals(System.getenv("srv")) - && !"skysql-ha".equals(System.getenv("srv"))); + && !"skysql-ha".equals(System.getenv("srv")) + && !isXpand()); transactionReplayFailingDuringCmd( createFailoverProxyConnection(HaMode.SEQUENTIAL, true, false)); diff --git a/src/test/java/org/mariadb/r2dbc/integration/LoggingTest.java b/src/test/java/org/mariadb/r2dbc/integration/LoggingTest.java index c8a13278..7116ebe2 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/LoggingTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/LoggingTest.java @@ -14,6 +14,7 @@ import java.nio.file.Files; import java.nio.file.Paths; import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.Test; import org.mariadb.r2dbc.BaseConnectionTest; import org.mariadb.r2dbc.MariadbConnectionConfiguration; @@ -28,6 +29,7 @@ public class LoggingTest extends BaseConnectionTest { @Test void basicLogging() throws IOException { + Assumptions.assumeFalse(isXpand()); File tempFile = File.createTempFile("log", ".tmp"); Logger logger = (Logger) LoggerFactory.getLogger("org.mariadb.r2dbc"); diff --git a/src/test/java/org/mariadb/r2dbc/integration/MultiQueriesTest.java b/src/test/java/org/mariadb/r2dbc/integration/MultiQueriesTest.java index 2a75bb2d..354a26e8 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/MultiQueriesTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/MultiQueriesTest.java @@ -19,7 +19,9 @@ public class MultiQueriesTest extends BaseConnectionTest { @Test void multiQueryDefault() { Assumptions.assumeTrue( - !"maxscale".equals(System.getenv("srv")) && !"skysql-ha".equals(System.getenv("srv"))); + !"maxscale".equals(System.getenv("srv")) + && !"skysql-ha".equals(System.getenv("srv")) + && !isXpand()); sharedConn .createStatement("SELECT 1; SELECT 'a'") .execute() @@ -50,7 +52,9 @@ void multiQueryEnable() throws Exception { @Test void multiQueryDisable() throws Exception { Assumptions.assumeTrue( - !"maxscale".equals(System.getenv("srv")) && !"skysql-ha".equals(System.getenv("srv"))); + !"maxscale".equals(System.getenv("srv")) + && !"skysql-ha".equals(System.getenv("srv")) + && !isXpand()); MariadbConnectionConfiguration conf = TestConfiguration.defaultBuilder.clone().allowMultiQueries(false).build(); @@ -71,7 +75,9 @@ void multiQueryDisable() throws Exception { @Test void multiQueryWithParameterDefault() { Assumptions.assumeTrue( - !"maxscale".equals(System.getenv("srv")) && !"skysql-ha".equals(System.getenv("srv"))); + !"maxscale".equals(System.getenv("srv")) + && !"skysql-ha".equals(System.getenv("srv")) + && !isXpand()); sharedConn .createStatement("SELECT CAST(? as CHAR); SELECT ?") @@ -107,7 +113,9 @@ void multiQueryWithParameterEnable() throws Exception { @Test void multiQueryWithParameterDisable() throws Exception { Assumptions.assumeTrue( - !"maxscale".equals(System.getenv("srv")) && !"skysql-ha".equals(System.getenv("srv"))); + !"maxscale".equals(System.getenv("srv")) + && !"skysql-ha".equals(System.getenv("srv")) + && !isXpand()); MariadbConnectionConfiguration conf = TestConfiguration.defaultBuilder.clone().allowMultiQueries(false).build(); diff --git a/src/test/java/org/mariadb/r2dbc/integration/PrepareResultSetTest.java b/src/test/java/org/mariadb/r2dbc/integration/PrepareResultSetTest.java index d1395a40..dc17fd08 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/PrepareResultSetTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/PrepareResultSetTest.java @@ -394,7 +394,13 @@ public void returningBefore105() { .bind(0, "test1") .returnGeneratedValues("id") .execute() - .flatMap(r -> r.map((row, metadata) -> row.get(0, String.class))) + .flatMap( + r -> + r.map( + (row, metadata) -> { + Assertions.assertEquals("id", metadata.getColumnMetadata(0).getName()); + return row.get(0, String.class); + })) .as(StepVerifier::create) .expectNext("3") .verifyComplete(); @@ -404,7 +410,14 @@ public void returningBefore105() { .bind(0, "test3") .returnGeneratedValues("TEST_COL_NAME") .execute() - .flatMap(r -> r.map((row, metadata) -> row.get("TEST_COL_NAME", String.class))) + .flatMap( + r -> + r.map( + (row, metadata) -> { + Assertions.assertEquals( + "TEST_COL_NAME", metadata.getColumnMetadata(0).getName()); + return row.get("TEST_COL_NAME", String.class); + })) .as(StepVerifier::create) .expectNext("4") .verifyComplete(); @@ -481,6 +494,7 @@ void cannotPrepare() throws Throwable { // unexpected error "unexpected message received when no command was send: 0x48000002" Assumptions.assumeTrue( !"maxscale".equals(System.getenv("srv")) && !"skysql-ha".equals(System.getenv("srv"))); + Assumptions.assumeFalse(isXpand()); MariadbConnectionConfiguration confPipeline = TestConfiguration.defaultBuilder.clone().useServerPrepStmts(true).build(); MariadbConnection conn = new MariadbConnectionFactory(confPipeline).create().block(); @@ -548,6 +562,8 @@ void parameterNull() { @Test void prepareReuse() { + // https://jira.mariadb.org/browse/XPT-599 XPand doesn't support DO + Assumptions.assumeFalse(isXpand()); MariadbStatement stmt = sharedConnPrepare.createStatement("DO 1 = ?"); assertThrows( IndexOutOfBoundsException.class, @@ -715,7 +731,8 @@ void cacheReuse() throws Throwable { // Com_stmt_prepare if (!"maxscale".equals(System.getenv("srv")) && !"skysql-ha".equals(System.getenv("srv")) - && (isMariaDBServer() || !minVersion(8, 0, 0))) { + && (isMariaDBServer() || !minVersion(8, 0, 0)) + && !isXpand()) { Assertions.assertEquals("5", endingStatus.get(1), endingStatus.get(1)); } diff --git a/src/test/java/org/mariadb/r2dbc/integration/ProcedureResultsetTest.java b/src/test/java/org/mariadb/r2dbc/integration/ProcedureResultsetTest.java index ecc622be..3edd2108 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/ProcedureResultsetTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/ProcedureResultsetTest.java @@ -10,12 +10,10 @@ import java.time.LocalDateTime; import java.util.List; import java.util.NoSuchElementException; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import org.mariadb.r2dbc.BaseConnectionTest; import reactor.core.publisher.Flux; +import reactor.test.StepVerifier; public class ProcedureResultsetTest extends BaseConnectionTest { @@ -34,15 +32,25 @@ public static void before2() { + "END") .execute() .blockLast(); + sharedConn + .createStatement( + "CREATE PROCEDURE no_out_proc (IN t1 INT, IN t2 INT) BEGIN \n" + + "DO t1 + t2;\n" + + "END") + .execute() + .blockLast(); } @AfterAll public static void dropAll() { sharedConn.createStatement("DROP PROCEDURE IF EXISTS basic_proc").execute().blockLast(); + sharedConn.createStatement("DROP PROCEDURE IF EXISTS no_out_proc").execute().blockLast(); } @Test void outputParameter() { + // https://jira.mariadb.org/browse/XPT-268 + Assumptions.assumeFalse(isXpand()); List> l = sharedConn .createStatement("call basic_proc(?,?,?,?,?,?,?)") @@ -102,5 +110,58 @@ void outputParameter() { Assertions.assertEquals("test", l.get(1).get(4)); } Assertions.assertEquals(LocalDateTime.parse("2003-12-31T12:00:00"), l.get(1).get(3)); + + List> l2 = + sharedConn + .createStatement("/*text*/ call basic_proc(?,?,?,?,?,?,?)") + .bind(0, 2) + .bind(1, Parameters.inOut(2)) + .bind(2, Parameters.out(R2dbcType.INTEGER)) + .bind(3, 10) + .bind(4, Parameters.out(R2dbcType.VARCHAR)) + .bind(5, Parameters.out(R2dbcType.TIMESTAMP)) + .bind(6, Parameters.out(R2dbcType.VARCHAR)) + .execute() + .flatMap( + r -> + Flux.from( + r.filter(Result.OutSegment.class::isInstance) + .flatMap( + seg -> { + return Flux.just( + ((Result.OutSegment) seg).outParameters().get(0), + ((Result.OutSegment) seg).outParameters().get(1), + ((Result.OutSegment) seg).outParameters().get(2), + ((Result.OutSegment) seg).outParameters().get(3), + ((Result.OutSegment) seg).outParameters().get(4)); + })) + .collectList()) + .collectList() + .block(); + + Assertions.assertEquals(1, l2.size()); } + @Test + void inParameter() { + Assumptions.assumeFalse(isXpand()); + sharedConn + .createStatement("call no_out_proc(?,?)") + .bind(0, 2) + .bind(1, 10) + .execute() + .flatMap(it -> it.getRowsUpdated()) + .as(StepVerifier::create) + .expectNext(0L) + .verifyComplete(); + sharedConn + .createStatement("/*text*/ call no_out_proc(?,?)") + .bind(0, 2) + .bind(1, 10) + .execute() + .flatMap(it -> it.getRowsUpdated()) + .as(StepVerifier::create) + .expectNext(0L) + .verifyComplete(); + } + } diff --git a/src/test/java/org/mariadb/r2dbc/integration/RowMetadataTest.java b/src/test/java/org/mariadb/r2dbc/integration/RowMetadataTest.java index 069dbd89..6a860faf 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/RowMetadataTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/RowMetadataTest.java @@ -96,8 +96,10 @@ void rowMeta() { assertEquals("t1", t1Meta.getColumn()); assertEquals("rowmeta", t1Meta.getTable()); assertEquals("rowMetaAlias", t1Meta.getTableAlias()); - assertTrue(t1Meta.getCharset() == 224 || t1Meta.getCharset() == 45); - assertEquals(256, t1Meta.getDisplaySize()); + if (!isXpand()) { + assertTrue(t1Meta.getCharset() == 224 || t1Meta.getCharset() == 45); + assertEquals(256, t1Meta.getDisplaySize()); + } assertFalse(t1Meta.isBinary()); assertFalse(t1Meta.isBlob()); assertFalse(t1Meta.isMultipleKey()); @@ -107,7 +109,9 @@ void rowMeta() { assertFalse(t1Meta.isUniqueKey()); colMeta = metadata.getColumnMetadata("t2"); - assertEquals(Long.class, colMeta.getJavaType()); + if (!isXpand()) { + assertEquals(Long.class, colMeta.getJavaType()); + } assertEquals("t2", colMeta.getName()); this.assertThrows( @@ -116,10 +120,13 @@ void rowMeta() { "Column name 'wrongName' does not exist in column names "); colMeta = metadata.getColumnMetadata(1); - assertEquals(Long.class, colMeta.getJavaType()); - assertEquals("t2", colMeta.getName()); - assertEquals(Nullability.NULLABLE, colMeta.getNullability()); - assertEquals(10, colMeta.getPrecision()); + if (!isXpand()) { + + assertEquals(Long.class, colMeta.getJavaType()); + assertEquals("t2", colMeta.getName()); + assertEquals(Nullability.NULLABLE, colMeta.getNullability()); + assertEquals(10, colMeta.getPrecision()); + } assertEquals(0, colMeta.getScale()); assertEquals( ColumnDefinitionPacket.class, colMeta.getNativeTypeMetadata().getClass()); @@ -133,14 +140,18 @@ void rowMeta() { assertEquals("rowmeta", t2Meta.getTable()); assertEquals("rowMetaAlias", t2Meta.getTableAlias()); - assertEquals(10, t2Meta.getDisplaySize()); - assertEquals(63, t2Meta.getCharset()); + if (!isXpand()) { + assertEquals(10, t2Meta.getDisplaySize()); + assertEquals(63, t2Meta.getCharset()); + } assertTrue(t2Meta.isBinary()); assertFalse(t2Meta.isBlob()); assertFalse(t2Meta.isMultipleKey()); assertFalse(t2Meta.isPrimaryKey()); - assertFalse(t2Meta.isSigned()); - assertTrue(t2Meta.isZeroFill()); + if (!isXpand()) { + assertFalse(t2Meta.isSigned()); + assertTrue(t2Meta.isZeroFill()); + } assertFalse(t2Meta.isUniqueKey()); colMeta = metadata.getColumnMetadata(2); diff --git a/src/test/java/org/mariadb/r2dbc/integration/StatementTest.java b/src/test/java/org/mariadb/r2dbc/integration/StatementTest.java index 23e7bb8c..2db8fe96 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/StatementTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/StatementTest.java @@ -90,6 +90,17 @@ public static void dropAll() { sharedConn.createStatement("DROP TABLE IF EXISTS returningBefore105").execute().blockLast(); } + @Test + void basicSetter() { + sharedConn + .createStatement("SELECT @amount := 10") + .execute() + .flatMap(r -> r.map((row, metadata) -> row.get(0, String.class))) + .as(StepVerifier::create) + .expectNext("10") + .verifyComplete(); + } + @Test void bindOnStatementWithoutParameter() { Statement stmt = sharedConn.createStatement("INSERT INTO someTable values (1,2)"); @@ -329,7 +340,7 @@ public void dupplicate() { @Test public void sinkEndCheck() throws Throwable { - Assumptions.assumeTrue(isMariaDBServer()); + Assumptions.assumeTrue(isMariaDBServer() && !isXpand()); AtomicReference d = new AtomicReference<>(); AtomicReference d2 = new AtomicReference<>(); MariadbConnection connection = factory.create().block(); @@ -370,7 +381,7 @@ public void sinkEndCheck() throws Throwable { @Test public void sinkFirstOnly() throws Throwable { - Assumptions.assumeTrue(isMariaDBServer()); + Assumptions.assumeTrue(isMariaDBServer() && !isXpand()); AtomicReference d2 = new AtomicReference<>(); MariadbConnection connection = factory.create().block(); connection.beginTransaction().block(); diff --git a/src/test/java/org/mariadb/r2dbc/integration/TlsTest.java b/src/test/java/org/mariadb/r2dbc/integration/TlsTest.java index dd99d77f..0c193842 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/TlsTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/TlsTest.java @@ -31,6 +31,7 @@ public class TlsTest extends BaseConnectionTest { @BeforeAll public static void before2() { + Assumptions.assumeTrue(!isXpand()); serverSslCert = System.getenv("TEST_DB_SERVER_CERT"); clientSslCert = System.getenv("TEST_DB_CLIENT_CERT"); clientSslKey = System.getenv("TEST_DB_CLIENT_KEY"); diff --git a/src/test/java/org/mariadb/r2dbc/integration/authentication/Sha256PluginTest.java b/src/test/java/org/mariadb/r2dbc/integration/authentication/Sha256PluginTest.java index e329002d..847e3661 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/authentication/Sha256PluginTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/authentication/Sha256PluginTest.java @@ -249,7 +249,8 @@ public void sha256PluginTestWrongServerRsaKey() throws Exception { public void sha256PluginTestWithoutServerRsaKey() throws Exception { Assumptions.assumeTrue(!isWindows && !isMariaDBServer() && (minVersion(8, 0, 0))); // mysql 8.0.31 broken public key retrieval, so avoid FLUSHING for now - Assumptions.assumeTrue(!isMariaDBServer() && !exactVersion(8, 0, 31)); + Assumptions.assumeTrue( + !isMariaDBServer() && !exactVersion(8, 0, 31) && !exactVersion(8, 0, 32)); MariadbConnectionConfiguration conf = TestConfiguration.defaultBuilder diff --git a/src/test/java/org/mariadb/r2dbc/integration/codec/BigIntegerParseTest.java b/src/test/java/org/mariadb/r2dbc/integration/codec/BigIntegerParseTest.java index 3c3d0cd1..1167da10 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/codec/BigIntegerParseTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/codec/BigIntegerParseTest.java @@ -499,7 +499,10 @@ private void stringValue(MariadbConnection connection) { .flatMap(r -> r.map((row, metadata) -> Optional.ofNullable(row.get(0, String.class)))) .as(StepVerifier::create) .expectNext( - Optional.of("0002"), Optional.of("0020"), Optional.of("0120"), Optional.of("1250")) + Optional.of(isXpand() ? "2" : "0002"), + Optional.of(isXpand() ? "20" : "0020"), + Optional.of(isXpand() ? "120" : "0120"), + Optional.of("1250")) .verifyComplete(); connection diff --git a/src/test/java/org/mariadb/r2dbc/integration/codec/DateParseTest.java b/src/test/java/org/mariadb/r2dbc/integration/codec/DateParseTest.java index f4b06a1b..a8ae3d49 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/codec/DateParseTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/codec/DateParseTest.java @@ -82,10 +82,17 @@ void localDateValuePrepare() { private void localDateValue(MariadbConnection connection) { connection - .createStatement("SELECT t1 FROM DateTable WHERE 1 = ?") + .createStatement("SELECT t1,t2 FROM DateTable WHERE 1 = ?") .bind(0, 1) .execute() - .flatMap(r -> r.map((row, metadata) -> Optional.ofNullable(row.get(0, LocalDate.class)))) + .flatMap( + r -> + r.map( + (row, metadata) -> { + row.get(0, LocalDate.class); + row.get(1); + return Optional.ofNullable(row.get(0, LocalDate.class)); + })) .as(StepVerifier::create) .expectNext( Optional.of(LocalDate.parse("2010-01-12")), diff --git a/src/test/java/org/mariadb/r2dbc/integration/codec/DateTimeParseTest.java b/src/test/java/org/mariadb/r2dbc/integration/codec/DateTimeParseTest.java index 2a3968a7..6f0b9c3e 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/codec/DateTimeParseTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/codec/DateTimeParseTest.java @@ -113,6 +113,7 @@ void localTimeValue() { @Test void localTimeValuePrepare() { + Assumptions.assumeFalse(isXpand()); localTimeValue(sharedConnPrepare); } @@ -121,7 +122,13 @@ private void localTimeValue(MariadbConnection connection) { .createStatement("SELECT t1 FROM DateTimeTable WHERE 1 = ?") .bind(0, 1) .execute() - .flatMap(r -> r.map((row, metadata) -> Optional.ofNullable(row.get(0, LocalTime.class)))) + .flatMap( + r -> + r.map( + (row, metadata) -> { + System.out.println(row.get(0, LocalTime.class)); + return Optional.ofNullable(row.get(0, LocalTime.class)); + })) .as(StepVerifier::create) .expectNext( Optional.of(LocalTime.parse("12:50:05.012300")), @@ -379,6 +386,7 @@ void stringValue() { @Test void stringValuePrepare() { + Assumptions.assumeFalse(isXpand()); stringValue( sharedConnPrepare, Optional.of("2013-07-22 12:50:05.012300"), diff --git a/src/test/java/org/mariadb/r2dbc/integration/codec/IntParseTest.java b/src/test/java/org/mariadb/r2dbc/integration/codec/IntParseTest.java index cf545772..2c289562 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/codec/IntParseTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/codec/IntParseTest.java @@ -491,8 +491,8 @@ private void stringValue(MariadbConnection connection) { .flatMap(r -> r.map((row, metadata) -> Optional.ofNullable(row.get(0, String.class)))) .as(StepVerifier::create) .expectNext( - Optional.of("0000000000"), - Optional.of("0000000010"), + Optional.of(isXpand() ? "0" : "0000000000"), + Optional.of(isXpand() ? "10" : "0000000010"), Optional.of("1294967295"), Optional.empty()) .verifyComplete(); diff --git a/src/test/java/org/mariadb/r2dbc/integration/codec/MediumIntParseTest.java b/src/test/java/org/mariadb/r2dbc/integration/codec/MediumIntParseTest.java index 719f6fa9..d17690ce 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/codec/MediumIntParseTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/codec/MediumIntParseTest.java @@ -9,6 +9,7 @@ import java.math.BigInteger; import java.util.Optional; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mariadb.r2dbc.BaseConnectionTest; @@ -401,9 +402,9 @@ private void stringValue(MariadbConnection connection) { .flatMap(r -> r.map((row, metadata) -> Optional.ofNullable(row.get(0, String.class)))) .as(StepVerifier::create) .expectNext( - Optional.of("00000000"), - Optional.of("00000010"), - Optional.of("00000100"), + Optional.of(isXpand() ? "0" : "00000000"), + Optional.of(isXpand() ? "10" : "00000010"), + Optional.of(isXpand() ? "100" : "00000100"), Optional.empty()) .verifyComplete(); @@ -440,6 +441,42 @@ private void decimalValue(MariadbConnection connection) { Optional.of(BigDecimal.valueOf(-1)), Optional.empty()) .verifyComplete(); + + connection + .createStatement("SELECT t1, t2 FROM MediumIntTable WHERE 1 = ?") + .bind(0, 1) + .execute() + .flatMap( + r -> + r.map( + (row, metadata) -> { + return new BigDecimal[] { + row.get(0, BigDecimal.class), row.get(1, BigDecimal.class) + }; + })) + .as(StepVerifier::create) + .assertNext( + r -> { + Assertions.assertEquals(0, r[0].intValue()); + Assertions.assertEquals(0, r[1].intValue()); + }) + .assertNext( + r -> { + Assertions.assertEquals(1, r[0].intValue()); + Assertions.assertEquals(10, r[1].intValue()); + }) + .assertNext( + r -> { + Assertions.assertEquals(-1, r[0].intValue()); + Assertions.assertEquals(100, r[1].intValue()); + }) + .assertNext( + r -> { + Assertions.assertNull(r[0]); + Assertions.assertNull(r[1]); + }) + .verifyComplete(); + connection .createStatement("SELECT t1 FROM MediumIntUnsignedTable WHERE 1 = ?") .bind(0, 1) diff --git a/src/test/java/org/mariadb/r2dbc/integration/codec/ShortParseTest.java b/src/test/java/org/mariadb/r2dbc/integration/codec/ShortParseTest.java index ae945522..e6cd5ad8 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/codec/ShortParseTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/codec/ShortParseTest.java @@ -448,7 +448,10 @@ private void stringValue(MariadbConnection connection) { .flatMap(r -> r.map((row, metadata) -> Optional.ofNullable(row.get(0, String.class)))) .as(StepVerifier::create) .expectNext( - Optional.of("00000"), Optional.of("00010"), Optional.of("00100"), Optional.empty()) + Optional.of(isXpand() ? "0" : "00000"), + Optional.of(isXpand() ? "10" : "00010"), + Optional.of(isXpand() ? "100" : "00100"), + Optional.empty()) .verifyComplete(); connection diff --git a/src/test/java/org/mariadb/r2dbc/integration/codec/StringParseTest.java b/src/test/java/org/mariadb/r2dbc/integration/codec/StringParseTest.java index e20f8107..39ff4ce1 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/codec/StringParseTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/codec/StringParseTest.java @@ -123,25 +123,26 @@ private void defaultValue(MariadbConnection connection) { .verifyComplete(); } - @Test void tt() { - String b = "0xFFBF0F23485930303054686520636C69656E742077617320646973636F6E6E656374656420627920746865207365727665722062656361757365206F6620696E61637469766974792E2053656520776169745F74696D656F757420616E6420696E7465726163746976655F74696D656F757420666F7220636F6E6669677572696E672074686973206265686176696F722E"; + String b = + "0xFFBF0F23485930303054686520636C69656E742077617320646973636F6E6E656374656420627920746865207365727665722062656361757365206F6620696E61637469766974792E2053656520776169745F74696D656F757420616E6420696E7465726163746976655F74696D656F757420666F7220636F6E6669677572696E672074686973206265686176696F722E"; byte[] bytes = hexStringToByteArray(b); String st = new String(bytes, StandardCharsets.UTF_8); System.out.println(st); - } public static byte[] hexStringToByteArray(String hex) { int l = hex.length(); byte[] data = new byte[l / 2]; for (int i = 0; i < l; i += 2) { - data[i / 2] = (byte) ((Character.digit(hex.charAt(i), 16) << 4) - + Character.digit(hex.charAt(i + 1), 16)); + data[i / 2] = + (byte) + ((Character.digit(hex.charAt(i), 16) << 4) + Character.digit(hex.charAt(i + 1), 16)); } return data; } + @Test void defaultValueBinary() { defaultValueBinary(sharedConn); diff --git a/src/test/java/org/mariadb/r2dbc/integration/codec/TimeParseTest.java b/src/test/java/org/mariadb/r2dbc/integration/codec/TimeParseTest.java index b6f862c8..6e707524 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/codec/TimeParseTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/codec/TimeParseTest.java @@ -12,6 +12,7 @@ import java.time.LocalTime; import java.util.Optional; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mariadb.r2dbc.BaseConnectionTest; @@ -415,6 +416,7 @@ void stringValue() { @Test void stringValuePrepare() { + Assumptions.assumeFalse(isXpand()); stringValue( sharedConnPrepare, "90:00:00.012340", diff --git a/src/test/java/org/mariadb/r2dbc/integration/codec/TimestampParseTest.java b/src/test/java/org/mariadb/r2dbc/integration/codec/TimestampParseTest.java index 99d285f3..3d7d2b04 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/codec/TimestampParseTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/codec/TimestampParseTest.java @@ -179,9 +179,14 @@ private void booleanValue(MariadbConnection connection) { .expectErrorMatches( throwable -> throwable instanceof R2dbcTransientResourceException - && throwable - .getMessage() - .equals("No decoder for type java.lang.Boolean and column type TIMESTAMP")) + && (throwable + .getMessage() + .equals( + "No decoder for type java.lang.Boolean and column type TIMESTAMP") + || throwable + .getMessage() + .equals( + "No decoder for type java.lang.Boolean and column type DATETIME"))) .verify(); } @@ -205,9 +210,12 @@ private void byteArrayValue(MariadbConnection connection) { .expectErrorMatches( throwable -> throwable instanceof R2dbcTransientResourceException - && throwable + && (throwable + .getMessage() + .equals("No decoder for type byte[] and column type TIMESTAMP")) + || throwable .getMessage() - .equals("No decoder for type byte[] and column type TIMESTAMP")) + .equals("No decoder for type byte[] and column type DATETIME")) .verify(); } @@ -231,9 +239,12 @@ private void ByteValue(MariadbConnection connection) { .expectErrorMatches( throwable -> throwable instanceof R2dbcTransientResourceException - && throwable - .getMessage() - .equals("No decoder for type java.lang.Byte and column type TIMESTAMP")) + && (throwable + .getMessage() + .equals("No decoder for type java.lang.Byte and column type TIMESTAMP") + || throwable + .getMessage() + .equals("No decoder for type java.lang.Byte and column type DATETIME"))) .verify(); } @@ -257,9 +268,12 @@ private void byteValue(MariadbConnection connection) { .expectErrorMatches( throwable -> throwable instanceof R2dbcTransientResourceException - && throwable - .getMessage() - .equals("No decoder for type byte and column type TIMESTAMP")) + && (throwable + .getMessage() + .equals("No decoder for type byte and column type TIMESTAMP") + || throwable + .getMessage() + .equals("No decoder for type byte and column type DATETIME"))) .verify(); } @@ -283,9 +297,13 @@ private void shortValue(MariadbConnection connection) { .expectErrorMatches( throwable -> throwable instanceof R2dbcTransientResourceException - && throwable - .getMessage() - .equals("No decoder for type java.lang.Short and column type TIMESTAMP")) + && (throwable + .getMessage() + .equals("No decoder for type java.lang.Short and column type TIMESTAMP") + || throwable + .getMessage() + .equals( + "No decoder for type java.lang.Short and column type DATETIME"))) .verify(); } @@ -309,9 +327,14 @@ private void intValue(MariadbConnection connection) { .expectErrorMatches( throwable -> throwable instanceof R2dbcTransientResourceException - && throwable - .getMessage() - .equals("No decoder for type java.lang.Integer and column type TIMESTAMP")) + && (throwable + .getMessage() + .equals( + "No decoder for type java.lang.Integer and column type TIMESTAMP") + || throwable + .getMessage() + .equals( + "No decoder for type java.lang.Integer and column type DATETIME"))) .verify(); } @@ -335,9 +358,12 @@ private void longValue(MariadbConnection connection) { .expectErrorMatches( throwable -> throwable instanceof R2dbcTransientResourceException - && throwable - .getMessage() - .equals("No decoder for type java.lang.Long and column type TIMESTAMP")) + && (throwable + .getMessage() + .equals("No decoder for type java.lang.Long and column type TIMESTAMP") + || throwable + .getMessage() + .equals("No decoder for type java.lang.Long and column type DATETIME"))) .verify(); } @@ -361,9 +387,13 @@ private void floatValue(MariadbConnection connection) { .expectErrorMatches( throwable -> throwable instanceof R2dbcTransientResourceException - && throwable - .getMessage() - .equals("No decoder for type java.lang.Float and column type TIMESTAMP")) + && (throwable + .getMessage() + .equals("No decoder for type java.lang.Float and column type TIMESTAMP") + || throwable + .getMessage() + .equals( + "No decoder for type java.lang.Float and column type DATETIME"))) .verify(); } @@ -387,9 +417,14 @@ private void doubleValue(MariadbConnection connection) { .expectErrorMatches( throwable -> throwable instanceof R2dbcTransientResourceException - && throwable - .getMessage() - .equals("No decoder for type java.lang.Double and column type TIMESTAMP")) + && (throwable + .getMessage() + .equals( + "No decoder for type java.lang.Double and column type TIMESTAMP") + || throwable + .getMessage() + .equals( + "No decoder for type java.lang.Double and column type DATETIME"))) .verify(); } @@ -434,10 +469,14 @@ private void decimalValue(MariadbConnection connection) { .expectErrorMatches( throwable -> throwable instanceof R2dbcTransientResourceException - && throwable - .getMessage() - .equals( - "No decoder for type java.math.BigDecimal and column type TIMESTAMP")) + && (throwable + .getMessage() + .equals( + "No decoder for type java.math.BigDecimal and column type TIMESTAMP") + || throwable + .getMessage() + .equals( + "No decoder for type java.math.BigDecimal and column type DATETIME"))) .verify(); } @@ -461,10 +500,14 @@ private void bigintValue(MariadbConnection connection) { .expectErrorMatches( throwable -> throwable instanceof R2dbcTransientResourceException - && throwable - .getMessage() - .equals( - "No decoder for type java.math.BigInteger and column type TIMESTAMP")) + && (throwable + .getMessage() + .equals( + "No decoder for type java.math.BigInteger and column type TIMESTAMP") + || throwable + .getMessage() + .equals( + "No decoder for type java.math.BigInteger and column type DATETIME"))) .verify(); } diff --git a/src/test/java/org/mariadb/r2dbc/integration/codec/TinyIntParseTest.java b/src/test/java/org/mariadb/r2dbc/integration/codec/TinyIntParseTest.java index 9d39d174..4086dc9e 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/codec/TinyIntParseTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/codec/TinyIntParseTest.java @@ -449,7 +449,11 @@ private void stringValue(MariadbConnection connection) { .execute() .flatMap(r -> r.map((row, metadata) -> Optional.ofNullable(row.get(0, String.class)))) .as(StepVerifier::create) - .expectNext(Optional.of("000"), Optional.of("010"), Optional.of("100"), Optional.empty()) + .expectNext( + Optional.of(isXpand() ? "0" : "000"), + Optional.of(isXpand() ? "10" : "010"), + Optional.of("100"), + Optional.empty()) .verifyComplete(); connection diff --git a/src/test/java/org/mariadb/r2dbc/integration/codec/YearParseTest.java b/src/test/java/org/mariadb/r2dbc/integration/codec/YearParseTest.java index 31045df2..c13758f1 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/codec/YearParseTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/codec/YearParseTest.java @@ -10,6 +10,7 @@ import java.time.LocalDate; import java.util.Optional; import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assumptions; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; import org.mariadb.r2dbc.BaseConnectionTest; @@ -23,6 +24,8 @@ public class YearParseTest extends BaseConnectionTest { @BeforeAll public static void before2() { + // xpand doesn't support YEAR 2 + Assumptions.assumeFalse(isXpand()); afterAll2(); sharedConn.beginTransaction().block(); String sqlCreate = "CREATE TABLE YearTable (t1 YEAR(4), t2 YEAR(2))"; diff --git a/src/test/java/org/mariadb/r2dbc/integration/parameter/BigIntegerParameterTest.java b/src/test/java/org/mariadb/r2dbc/integration/parameter/BigIntegerParameterTest.java index 0650f22e..48f88b7d 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/parameter/BigIntegerParameterTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/parameter/BigIntegerParameterTest.java @@ -362,6 +362,7 @@ private void LongValue(MariadbConnection connection) { @Test void localDateTimeValue() { + Assumptions.assumeFalse(isXpand()); localDateTimeValue(sharedConn); } @@ -406,6 +407,7 @@ void localDateTimeValuePrepare() { @Test void localDateValue() { + Assumptions.assumeFalse(isXpand()); localDateValue(sharedConn); } @@ -451,6 +453,7 @@ private void localDateValue(MariadbConnection connection) { @Test void localTimeValue() { + Assumptions.assumeFalse(isXpand()); localTimeValue(sharedConn); } diff --git a/src/test/java/org/mariadb/r2dbc/integration/parameter/BlobParameterTest.java b/src/test/java/org/mariadb/r2dbc/integration/parameter/BlobParameterTest.java index 6118f537..080213e7 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/parameter/BlobParameterTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/parameter/BlobParameterTest.java @@ -407,7 +407,7 @@ void localDateTimeValue() { @Test void localDateTimeValuePrepare() { - + Assumptions.assumeFalse(isXpand()); localDateTimeValue( sharedConnPrepare, meta.isMariaDBServer() ? "2013-07-22 12:50:05.012300" : "2013-07-22 12:50:05", diff --git a/src/test/java/org/mariadb/r2dbc/integration/parameter/DateParameterTest.java b/src/test/java/org/mariadb/r2dbc/integration/parameter/DateParameterTest.java index a8459be3..e75fbdcb 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/parameter/DateParameterTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/parameter/DateParameterTest.java @@ -399,6 +399,7 @@ private void localDateValue(MariadbConnection connection) { @Test void localTimeValue() { + Assumptions.assumeFalse(isXpand()); localTimeValue(sharedConn); } diff --git a/src/test/java/org/mariadb/r2dbc/integration/parameter/DateTimeParameterTest.java b/src/test/java/org/mariadb/r2dbc/integration/parameter/DateTimeParameterTest.java index 3066fa82..cac16e7f 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/parameter/DateTimeParameterTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/parameter/DateTimeParameterTest.java @@ -401,11 +401,13 @@ private void localDateValue(MariadbConnection connection) { @Test void localTimeValue() { + Assumptions.assumeFalse(isXpand()); localTimeValue(sharedConn); } @Test void localTimeValuePrepare() { + Assumptions.assumeFalse(isXpand()); LocalTime localTime = LocalTime.parse("05:08:10.123456"); LocalDateTime localDateTime = LocalDateTime.now().withHour(5).withMinute(8).withSecond(10).withNano(123456 * 1000); diff --git a/src/test/java/org/mariadb/r2dbc/integration/parameter/FloatParameterTest.java b/src/test/java/org/mariadb/r2dbc/integration/parameter/FloatParameterTest.java index e71847b2..dea6255f 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/parameter/FloatParameterTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/parameter/FloatParameterTest.java @@ -266,6 +266,7 @@ private void longValue(MariadbConnection connection) { @Test void localDateTimeValue() { + Assumptions.assumeFalse(isXpand()); localDateTimeValue(sharedConn); } @@ -287,6 +288,7 @@ private void localDateTimeValue(MariadbConnection connection) { @Test void localDateValue() { + Assumptions.assumeFalse(isXpand()); localDateValue(sharedConn); } @@ -308,6 +310,7 @@ private void localDateValue(MariadbConnection connection) { @Test void localTimeValue() { + Assumptions.assumeFalse(isXpand()); localTimeValue(sharedConn); } diff --git a/src/test/java/org/mariadb/r2dbc/integration/parameter/IntParameterTest.java b/src/test/java/org/mariadb/r2dbc/integration/parameter/IntParameterTest.java index bd61deae..a1f9438e 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/parameter/IntParameterTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/parameter/IntParameterTest.java @@ -261,7 +261,10 @@ void localDateTimeValue() { .expectErrorMatches( throwable -> throwable instanceof R2dbcTransientResourceException - && ((R2dbcTransientResourceException) throwable).getSqlState().equals("01000")) + && (((R2dbcTransientResourceException) throwable).getSqlState().equals("01000") + || ((R2dbcTransientResourceException) throwable) + .getMessage() + .contains("truncated for conversion to INT"))) .verify(); } @@ -278,7 +281,10 @@ void localDateValue() { .expectErrorMatches( throwable -> throwable instanceof R2dbcTransientResourceException - && ((R2dbcTransientResourceException) throwable).getSqlState().equals("01000")) + && (((R2dbcTransientResourceException) throwable).getSqlState().equals("01000") + || ((R2dbcTransientResourceException) throwable) + .getMessage() + .contains("truncated for conversion to INT"))) .verify(); } @@ -295,7 +301,10 @@ void localTimeValue() { .expectErrorMatches( throwable -> throwable instanceof R2dbcTransientResourceException - && ((R2dbcTransientResourceException) throwable).getSqlState().equals("01000")) + && (((R2dbcTransientResourceException) throwable).getSqlState().equals("01000") + || ((R2dbcTransientResourceException) throwable) + .getMessage() + .contains("truncated for conversion to INT"))) .verify(); } diff --git a/src/test/java/org/mariadb/r2dbc/integration/parameter/MediumIntParameterTest.java b/src/test/java/org/mariadb/r2dbc/integration/parameter/MediumIntParameterTest.java index 906f5b00..c4cc8e94 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/parameter/MediumIntParameterTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/parameter/MediumIntParameterTest.java @@ -10,10 +10,7 @@ import java.time.LocalDateTime; import java.time.LocalTime; import java.util.Optional; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import org.mariadb.r2dbc.BaseConnectionTest; import org.mariadb.r2dbc.api.MariadbConnection; import reactor.core.publisher.Flux; @@ -250,6 +247,7 @@ private void longValue(MariadbConnection connection) { @Test void localDateTimeValue() { + Assumptions.assumeFalse(isXpand()); sharedConn .createStatement("INSERT INTO MediumIntParam VALUES (?,?,?)") .bind(0, LocalDateTime.now()) @@ -267,6 +265,7 @@ void localDateTimeValue() { @Test void localDateValue() { + Assumptions.assumeFalse(isXpand()); sharedConn .createStatement("INSERT INTO MediumIntParam VALUES (?,?,?)") .bind(0, LocalDate.now()) @@ -284,6 +283,7 @@ void localDateValue() { @Test void localTimeValue() { + Assumptions.assumeFalse(isXpand()); sharedConn .createStatement("INSERT INTO MediumIntParam VALUES (?,?,?)") .bind(0, LocalTime.now()) diff --git a/src/test/java/org/mariadb/r2dbc/integration/parameter/ShortParameterTest.java b/src/test/java/org/mariadb/r2dbc/integration/parameter/ShortParameterTest.java index b4437d86..6e672d60 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/parameter/ShortParameterTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/parameter/ShortParameterTest.java @@ -10,10 +10,7 @@ import java.time.LocalDateTime; import java.time.LocalTime; import java.util.Optional; -import org.junit.jupiter.api.AfterAll; -import org.junit.jupiter.api.BeforeAll; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.*; import org.mariadb.r2dbc.BaseConnectionTest; import org.mariadb.r2dbc.api.MariadbConnection; import reactor.core.publisher.Flux; @@ -277,6 +274,7 @@ private void longValue(MariadbConnection connection) { @Test void localDateTimeValue() { + Assumptions.assumeFalse(isXpand()); sharedConn .createStatement("INSERT INTO SmallIntParam VALUES (?,?,?)") .bind(0, LocalDateTime.now()) @@ -294,6 +292,7 @@ void localDateTimeValue() { @Test void localDateValue() { + Assumptions.assumeFalse(isXpand()); sharedConn .createStatement("INSERT INTO SmallIntParam VALUES (?,?,?)") .bind(0, LocalDate.now()) @@ -311,6 +310,7 @@ void localDateValue() { @Test void localTimeValue() { + Assumptions.assumeFalse(isXpand()); sharedConn .createStatement("INSERT INTO SmallIntParam VALUES (?,?,?)") .bind(0, LocalTime.now()) diff --git a/src/test/java/org/mariadb/r2dbc/integration/parameter/StringParameterTest.java b/src/test/java/org/mariadb/r2dbc/integration/parameter/StringParameterTest.java index ab27747a..0f6f6875 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/parameter/StringParameterTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/parameter/StringParameterTest.java @@ -459,7 +459,10 @@ void durationValue() { void durationValuePrepare() { durationValue(sharedConnPrepare); if (meta.isMariaDBServer()) { - validate(Optional.of("90:00:00.012340"), Optional.of("00:08:00"), Optional.of("00:00:22")); + validate( + Optional.of("90:00:00.012340"), + Optional.of(isXpand() ? "00:08:00.000000" : "00:08:00"), + Optional.of(isXpand() ? "00:00:22.000000" : "00:00:22")); } else { validate(Optional.of("90:00:00"), Optional.of("00:08:00"), Optional.of("00:00:22")); } diff --git a/src/test/java/org/mariadb/r2dbc/integration/parameter/TimeParameterTest.java b/src/test/java/org/mariadb/r2dbc/integration/parameter/TimeParameterTest.java index 711fb1c0..7fa074a5 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/parameter/TimeParameterTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/parameter/TimeParameterTest.java @@ -326,6 +326,7 @@ private void localDateTimeValue(MariadbConnection connection) { @Test void localDateValue() { + Assumptions.assumeFalse(isXpand()); localDateValue(sharedConn); } diff --git a/src/test/java/org/mariadb/r2dbc/integration/parameter/TimeStampParameterTest.java b/src/test/java/org/mariadb/r2dbc/integration/parameter/TimeStampParameterTest.java index e455f7c6..7dfd131f 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/parameter/TimeStampParameterTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/parameter/TimeStampParameterTest.java @@ -376,6 +376,7 @@ private void localDateValue(MariadbConnection connection) { @Test void localTimeValue() { + Assumptions.assumeFalse(isXpand()); localTimeValue(sharedConn); } diff --git a/src/test/java/org/mariadb/r2dbc/integration/parameter/TinyIntParameterTest.java b/src/test/java/org/mariadb/r2dbc/integration/parameter/TinyIntParameterTest.java index 0db37b25..97769e07 100644 --- a/src/test/java/org/mariadb/r2dbc/integration/parameter/TinyIntParameterTest.java +++ b/src/test/java/org/mariadb/r2dbc/integration/parameter/TinyIntParameterTest.java @@ -343,6 +343,7 @@ private void localDateValue(MariadbConnection connection) { @Test void localTimeValue() { + Assumptions.assumeFalse(isXpand()); localTimeValue(sharedConn); } diff --git a/src/test/java/org/mariadb/r2dbc/unit/MariadbConnectionConfigurationTest.java b/src/test/java/org/mariadb/r2dbc/unit/MariadbConnectionConfigurationTest.java new file mode 100644 index 00000000..80ea8454 --- /dev/null +++ b/src/test/java/org/mariadb/r2dbc/unit/MariadbConnectionConfigurationTest.java @@ -0,0 +1,96 @@ +package org.mariadb.r2dbc.unit; + +import io.r2dbc.spi.ConnectionFactoryOptions; +import io.r2dbc.spi.IsolationLevel; +import java.time.Duration; +import java.util.Map; +import java.util.TreeMap; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.mariadb.r2dbc.MariadbConnectionConfiguration; +import org.mariadb.r2dbc.SslMode; +import reactor.netty.resources.LoopResources; + +public class MariadbConnectionConfigurationTest { + @Test + public void builder() { + TreeMap connectionAttributes = new TreeMap<>(); + connectionAttributes.put("entry1", "val1"); + connectionAttributes.put("entry2", "val2"); + + MariadbConnectionConfiguration conf = + MariadbConnectionConfiguration.builder() + .connectTimeout(Duration.ofMillis(150)) + .haMode("LOADBALANCE") + .restrictedAuth("mysql_native_password,client_ed25519") + .tcpKeepAlive(true) + .tcpAbortiveClose(true) + .transactionReplay(true) + .connectionAttributes(connectionAttributes) + .sessionVariables(Map.of("timezone", "Europe/Paris")) + .pamOtherPwd(new String[] {"otherPwd"}) + .database("MyDB") + .password("MyPassword") + .tlsProtocol("TLSv1.2", "TLSv1.3") + .serverSslCert("/path/to/serverCert") + .prepareCacheSize(125) + .clientSslKey("clientSecretKey") + .clientSslPassword("ClientSecretPwd") + .sslMode(SslMode.TRUST) + .rsaPublicKey("/path/to/publicRSAKey") + .cachingRsaPublicKey("cachingRSAPublicKey") + .allowPublicKeyRetrieval(true) + .useServerPrepStmts(true) + .isolationLevel(IsolationLevel.SERIALIZABLE) + .autocommit(false) + .tinyInt1isBit(false) + .allowPipelining(false) + .allowMultiQueries(true) + .socket("/path/to/mysocket") + .username("MyUSer") + .loopResources(LoopResources.create("mariadb")) + .sslContextBuilderCustomizer((b) -> b) + .sslTunnelDisableHostVerification(true) + .build(); + Assertions.assertEquals( + "MariadbConnectionConfiguration{database='MyDB', hosts={[localhost:3306]}, connectTimeout=PT0.15S, tcpKeepAlive=true, tcpAbortiveClose=true, transactionReplay=true, password=*, prepareCacheSize=125, socket='/path/to/mysocket', username='MyUSer', allowMultiQueries=true, allowPipelining=false, connectionAttributes={entry1=val1, entry2=val2}, sessionVariables={timezone=Europe/Paris}, sslConfig=SslConfig{sslMode=TRUST, serverSslCert=/path/to/serverCert, clientSslCert=null, tlsProtocol=[TLSv1.2, TLSv1.3], clientSslKey=clientSecretKey}, rsaPublicKey='/path/to/publicRSAKey', cachingRsaPublicKey='cachingRSAPublicKey', allowPublicKeyRetrieval=true, isolationLevel=IsolationLevel{sql='SERIALIZABLE'}, useServerPrepStmts=false, autocommit=false, tinyInt1isBit=false, pamOtherPwd=*, restrictedAuth=[mysql_native_password, client_ed25519]}", + conf.toString()); + } + + @Test + public void connectionString() { + ConnectionFactoryOptions options = + ConnectionFactoryOptions.parse( + "r2dbc:mariadb://ro%3Aot:pw%3Ad@localhost:3306/db?connectTimeout=PT0.15S" + + "&haMode=LOADBALANCE" + + "&restrictedAuth=mysql_native_password,client_ed25519" + + "&tcpKeepAlive=true" + + "&tcpAbortiveClose=true" + + "&transactionReplay=true" + + "&connectionAttributes=entry1=val1,entry2=val2" + + "&sessionVariables=timezone=Europe/Paris" + + "&pamOtherPwd=otherPwd" + + "&tlsProtocol=TLSv1.2,TLSv1.3" + + "&serverSslCert=/path/to/serverCert" + + "&prepareCacheSize=125" + + "&clientSslKey=clientSecretKey" + + "&clientSslPassword=ClientSecretPwd" + + "&sslMode=TRUST" + + "&rsaPublicKey=/path/to/publicRSAKey" + + "&cachingRsaPublicKey=cachingRSAPublicKey" + + "&allowPublicKeyRetrieval=true" + + "&useServerPrepStmts=true" + + "&isolationLevel=SERIALIZABLE" + + "&autocommit=false" + + "&tinyInt1isBit=false" + + "&allowPipelining=false" + + "&allowMultiQueries=true" + + "&socket=/path/to/mysocket" + + "&sslTunnelDisableHostVerification=true"); + MariadbConnectionConfiguration conf = + MariadbConnectionConfiguration.fromOptions(options).build(); + Assertions.assertEquals( + "MariadbConnectionConfiguration{database='db', hosts={[localhost:3306]}, connectTimeout=PT0.15S, tcpKeepAlive=true, tcpAbortiveClose=true, transactionReplay=true, password=*, prepareCacheSize=125, socket='/path/to/mysocket', username='ro:ot', allowMultiQueries=true, allowPipelining=false, connectionAttributes={entry1=val1, entry2=val2}, sessionVariables={timezone=Europe/Paris}, sslConfig=SslConfig{sslMode=TRUST, serverSslCert=/path/to/serverCert, clientSslCert=null, tlsProtocol=[TLSv1.2, TLSv1.3], clientSslKey=clientSecretKey}, rsaPublicKey='/path/to/publicRSAKey', cachingRsaPublicKey='cachingRSAPublicKey', allowPublicKeyRetrieval=true, isolationLevel=IsolationLevel{sql='SERIALIZABLE'}, useServerPrepStmts=false, autocommit=false, tinyInt1isBit=false, pamOtherPwd=*, restrictedAuth=[mysql_native_password, client_ed25519]}", + conf.toString()); + } +} diff --git a/src/test/java/org/mariadb/r2dbc/unit/util/ClientPrepareResultTest.java b/src/test/java/org/mariadb/r2dbc/unit/util/ClientPrepareResultTest.java index 7cfa9d61..fdfbaed8 100644 --- a/src/test/java/org/mariadb/r2dbc/unit/util/ClientPrepareResultTest.java +++ b/src/test/java/org/mariadb/r2dbc/unit/util/ClientPrepareResultTest.java @@ -151,6 +151,19 @@ public void stringEscapeParsing() throws Exception { }); } + @Test + public void clientNoParameterParsing() { + checkParsing( + "SELECT @amount := 10", + 0, + 0, + true, + false, + false, + new String[] {"SELECT @amount := 10"}, + new String[] {"SELECT @amount := 10"}); + } + @Test public void stringReturningParsing() throws Exception { checkParsing(