Skip to content

Commit 9ce085e

Browse files
authored
Add MariaDB capabilities support (#187)
Motivation: See also #176 . Modification: Add recognition for MariaDB, and extend Capability to 64-bits. Result: Extend Capability to 64-bits, and extended bits will be read and sent at handshake. Currently, the driver will unset all MariaDB features, we can enable them after we support these features.
1 parent 871339d commit 9ce085e

File tree

4 files changed

+103
-61
lines changed

4 files changed

+103
-61
lines changed

src/main/java/io/asyncer/r2dbc/mysql/Capability.java

Lines changed: 75 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -25,81 +25,80 @@
2525
public final class Capability {
2626

2727
/**
28-
* Can use long password.
29-
* <p>
30-
* TODO: Reinterpret it as {@code CLIENT_MYSQL} to support MariaDB 10.2 and above.
28+
* If UNSET, the server supports the MariaDB protocol and statements.
3129
*/
32-
private static final int LONG_PASSWORD = 1;
30+
private static final long CLIENT_MYSQL = 1L;
3331

3432
/**
3533
* Use found/touched rows instead of changed rows for affected rows. Should enable it by default.
3634
*/
37-
private static final int FOUND_ROWS = 2;
35+
private static final long FOUND_ROWS = 2L;
3836

3937
/**
4038
* Use 2-bytes definition flags of {@code DefinitionMetadataMessage}.
4139
* <p>
4240
* Very old servers (before 3.23) will not set this capability flag.
4341
*/
44-
private static final int LONG_FLAG = 4;
42+
private static final long LONG_FLAG = 4L;
4543

4644
/**
4745
* Connect to server with a database.
4846
*/
49-
private static final int CONNECT_WITH_DB = 8;
47+
private static final long CONNECT_WITH_DB = 8L;
5048

5149
/**
5250
* Enable it to disallow a statement which use {@code database.table.column} for access other schema
5351
* data.
5452
*/
55-
private static final int NO_SCHEMA = 16;
53+
private static final long NO_SCHEMA = 16L;
5654

5755
/**
5856
* The deflate compression, old compression flag.
5957
*/
60-
private static final int COMPRESS = 32;
58+
private static final long COMPRESS = 32L;
6159

62-
// private static final int ODBC = 64; // R2DBC driver is not ODBC driver.
60+
// private static final long ODBC = 64L; // R2DBC driver is not ODBC driver.
6361

6462
/**
6563
* Allow to use LOAD DATA [LOCAL] INFILE statement.
6664
*/
67-
private static final int LOCAL_FILES = 128;
65+
private static final long LOCAL_FILES = 128L;
6866

6967
/**
7068
* Ignore space between a built-in function name and the subsequent parenthesis.
7169
* <p>
7270
* Note: Ignoring spaces may cause ambiguity.
7371
* <p>
74-
* See also https://dev.mysql.com/doc/refman/8.0/en/function-resolution.html .
72+
* See also <a href="https://dev.mysql.com/doc/refman/8.0/en/function-resolution.html">
73+
* Function Resolution</a>.
7574
*/
76-
private static final int IGNORE_SPACE = 256;
75+
private static final long IGNORE_SPACE = 256L;
7776

7877
/**
7978
* The protocol version is 4.1 (instead of 3.20).
8079
*/
81-
private static final int PROTOCOL_41 = 512;
80+
private static final long PROTOCOL_41 = 512L;
8281

8382
/**
8483
* Use {@code wait_interactive_timeout} instead of {@code wait_timeout} for the server waits for activity
8584
* on a connection before closing it.
8685
*/
87-
private static final int INTERACTIVE = 1024;
86+
private static final long INTERACTIVE = 1024L;
8887

8988
/**
9089
* Enable SSL.
9190
*/
92-
private static final int SSL = 2048;
91+
private static final long SSL = 2048L;
9392

94-
// private static final int IGNORE_SIGPIPE = 4096; // Connector/C only flag.
93+
// private static final long IGNORE_SIGPIPE = 4096L; // Connector/C only flag.
9594

9695
/**
9796
* Allow transactions. All available versions of MySQL server support it.
9897
*/
99-
private static final int TRANSACTIONS = 8192;
98+
private static final long TRANSACTIONS = 8192L;
10099

101100
// Old flag and alias of PROTOCOL_41. It will not be used by any available server version/edition.
102-
// private static final int RESERVED = 16384;
101+
// private static final int RESERVED = 16384L;
103102

104103
/**
105104
* Allow second part of authentication hashing salt.
@@ -108,67 +107,77 @@ public final class Capability {
108107
* <p>
109108
* Origin name: SECURE_CONNECTION.
110109
*/
111-
private static final int SECURE_SALT = 32768;
110+
private static final long SECURE_SALT = 32768L;
112111

113112
/**
114113
* Allow to send multiple statements in text query and prepare query.
115114
* <p>
116115
* Old name: MULTI_QUERIES.
117116
*/
118-
private static final int MULTI_STATEMENTS = 65536;
117+
private static final long MULTI_STATEMENTS = 65536L;
119118

120119
/**
121120
* Allow to receive multiple results in the response of executing a text query.
122121
*/
123-
private static final int MULTI_RESULTS = 1 << 17;
122+
private static final long MULTI_RESULTS = 1L << 17;
124123

125124
/**
126125
* Allow to receive multiple results in the response of executing a prepare query.
127126
*/
128-
private static final int PS_MULTI_RESULTS = 1 << 18;
127+
private static final long PS_MULTI_RESULTS = 1L << 18;
129128

130129
/**
131130
* Supports authentication plugins. Server will send more details (i.e. name) for authentication plugin.
132131
*/
133-
private static final int PLUGIN_AUTH = 1 << 19;
132+
private static final long PLUGIN_AUTH = 1L << 19;
134133

135134
/**
136135
* Connection attributes should be sent.
137136
*/
138-
private static final int CONNECT_ATTRS = 1 << 20;
137+
private static final long CONNECT_ATTRS = 1L << 20;
139138

140139
/**
141140
* Can use var-integer sized bytes to encode client authentication.
142141
* <p>
143142
* Origin name: PLUGIN_AUTH_LENENC_CLIENT_DATA.
144143
*/
145-
private static final int VAR_INT_SIZED_AUTH = 1 << 21;
144+
private static final long VAR_INT_SIZED_AUTH = 1L << 21;
146145

147-
// private static final int HANDLE_EXPIRED_PASSWORD = 1 << 22; // Client can handle expired passwords.
148-
// private static final int SESSION_TRACK = 1 << 23;
146+
// private static final long HANDLE_EXPIRED_PASSWORD = 1L << 22; // Client can handle expired passwords.
147+
// private static final long SESSION_TRACK = 1L << 23;
149148

150149
/**
151150
* The MySQL server marks the EOF message as deprecated and use OK message instead.
152151
*/
153-
private static final int DEPRECATE_EOF = 1 << 24;
152+
private static final long DEPRECATE_EOF = 1L << 24;
154153

155154
// Allow the server not to send column metadata in result set,
156155
// should NEVER enable this option.
157-
// private static final int OPTIONAL_RESULT_SET_METADATA = 1 << 25;
158-
// private static final int Z_STD_COMPRESSION = 1 << 26;
156+
// private static final long OPTIONAL_RESULT_SET_METADATA = 1L << 25;
157+
// private static final long Z_STD_COMPRESSION = 1L << 26;
159158

160159
// A reserved flag, used to extend the 32-bits capability bitmap to 64-bits.
161160
// There is no available MySql server version/edition to support it.
162-
// private static final int CAPABILITY_EXTENSION = 1 << 29;
163-
// private static final int SSL_VERIFY_SERVER_CERT = 1 << 30; // Client only flag, use SslMode instead.
164-
// private static final int REMEMBER_OPTIONS = 1 << 31; // Connector/C only flag.
161+
// private static final long CAPABILITY_EXTENSION = 1L << 29;
162+
// private static final long SSL_VERIFY_SERVER_CERT = 1L << 30; // Client only flag, use SslMode instead.
163+
// private static final long REMEMBER_OPTIONS = 1L << 31; // Connector/C only flag.
164+
165+
// private static final long MARIADB_CLIENT_PROGRESS = 1L << 32;
166+
// private static final long MARIADB_CLIENT_COM_MULTI = 1L << 33;
167+
// private static final long MARIADB_CLIENT_STMT_BULK_OPERATIONS = 1L << 34;
168+
// private static final long MARIADB_CLIENT_EXTENDED_TYPE_INFO = 1L << 35;
169+
// private static final long MARIADB_CLIENT_CACHE_METADATA = 1L << 36;
165170

166-
private static final int ALL_SUPPORTED = LONG_PASSWORD | FOUND_ROWS | LONG_FLAG | CONNECT_WITH_DB |
171+
private static final long ALL_SUPPORTED = CLIENT_MYSQL | FOUND_ROWS | LONG_FLAG | CONNECT_WITH_DB |
167172
NO_SCHEMA | COMPRESS | LOCAL_FILES | IGNORE_SPACE | PROTOCOL_41 | INTERACTIVE | SSL |
168173
TRANSACTIONS | SECURE_SALT | MULTI_STATEMENTS | MULTI_RESULTS | PS_MULTI_RESULTS |
169174
PLUGIN_AUTH | CONNECT_ATTRS | VAR_INT_SIZED_AUTH | DEPRECATE_EOF;
170175

171-
private final int bitmap;
176+
private final long bitmap;
177+
178+
public boolean isMariaDb() {
179+
return (bitmap & CLIENT_MYSQL) == 0;
180+
}
172181

173182
/**
174183
* Checks if the connection will be connected and logon with a database.
@@ -261,12 +270,31 @@ public boolean isTransactionAllowed() {
261270
}
262271

263272
/**
264-
* Get the original bitmap of {@link Capability this}.
273+
* Extends MariaDB capabilities.
274+
*
275+
* @param hiCapabilities the bitmap of extend capabilities.
276+
* @return a new {@link Capability} takes base and extend capabilities.
277+
*/
278+
public Capability extendMariaDb(long hiCapabilities) {
279+
return of((this.bitmap & 0xFFFFFFFFL) | (hiCapabilities << 32));
280+
}
281+
282+
/**
283+
* Get the lower 32-bits bitmap of {@link Capability this}.
284+
*
285+
* @return the lower 32-bits bitmap.
286+
*/
287+
public int getBaseBitmap() {
288+
return (int) bitmap;
289+
}
290+
291+
/**
292+
* Get the higher 32-bits bitmap of {@link Capability this}.
265293
*
266-
* @return the bitmap.
294+
* @return the higher 32-bits bitmap.
267295
*/
268-
public int getBitmap() {
269-
return bitmap;
296+
public int getExtendBitmap() {
297+
return (int) (bitmap >>> 32);
270298
}
271299

272300
@Override
@@ -285,36 +313,36 @@ public boolean equals(Object o) {
285313

286314
@Override
287315
public int hashCode() {
288-
return bitmap;
316+
return Long.hashCode(bitmap);
289317
}
290318

291319
@Override
292320
public String toString() {
293321
// Do not consider complex output, just use hex.
294-
return "Capability<0x" + Integer.toHexString(bitmap) + '>';
322+
return "Capability<0x" + Long.toHexString(bitmap) + '>';
295323
}
296324

297325
Builder mutate() {
298326
return new Builder(bitmap);
299327
}
300328

301-
private Capability(int bitmap) {
329+
private Capability(long bitmap) {
302330
this.bitmap = bitmap;
303331
}
304332

305333
/**
306334
* Creates a {@link Capability} with capabilities bitmap. It will unset all unknown flags.
307335
*
308-
* @param capabilities the capabilities bitmap.
336+
* @param capabilities the bitmap of capabilities.
309337
* @return the {@link Capability} without unknown flags.
310338
*/
311-
public static Capability of(int capabilities) {
339+
public static Capability of(long capabilities) {
312340
return new Capability(capabilities & ALL_SUPPORTED);
313341
}
314342

315343
static final class Builder {
316344

317-
private int bitmap;
345+
private long bitmap;
318346

319347
void disableConnectWithDatabase() {
320348
this.bitmap &= ~CONNECT_WITH_DB;
@@ -352,7 +380,7 @@ Capability build() {
352380
return of(this.bitmap);
353381
}
354382

355-
private Builder(int bitmap) {
383+
private Builder(long bitmap) {
356384
this.bitmap = bitmap;
357385
}
358386
}

src/main/java/io/asyncer/r2dbc/mysql/message/client/SslRequest320.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ protected int size() {
8383
@Override
8484
protected void writeTo(ByteBuf buf) {
8585
// Protocol 3.20 only allows low 16-bits capabilities.
86-
buf.writeShortLE(capability.getBitmap() & 0xFFFF)
86+
buf.writeShortLE(capability.getBaseBitmap() & 0xFFFF)
8787
.writeMediumLE(Envelopes.MAX_ENVELOPE_SIZE);
8888
}
8989
}

src/main/java/io/asyncer/r2dbc/mysql/message/client/SslRequest41.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,12 @@
2727
*/
2828
final class SslRequest41 extends SizedClientMessage implements SslRequest {
2929

30-
private static final int FILTER_SIZE = 23;
30+
private static final int RESERVED_SIZE = 19;
3131

32-
private static final int BUF_SIZE = Integer.BYTES + Integer.BYTES + Byte.BYTES + FILTER_SIZE;
32+
private static final int MARIA_DB_CAPABILITY_SIZE = Integer.BYTES;
33+
34+
private static final int BUF_SIZE = Integer.BYTES + Integer.BYTES + Byte.BYTES +
35+
RESERVED_SIZE + MARIA_DB_CAPABILITY_SIZE;
3336

3437
private final int envelopeId;
3538

@@ -91,10 +94,16 @@ protected int size() {
9194

9295
@Override
9396
protected void writeTo(ByteBuf buf) {
94-
buf.writeIntLE(capability.getBitmap())
97+
buf.writeIntLE(capability.getBaseBitmap())
9598
.writeIntLE(Envelopes.MAX_ENVELOPE_SIZE)
96-
.writeByte(collationId & 0xFF) // only low 8-bits
97-
.writeZero(FILTER_SIZE);
99+
.writeByte(collationId & 0xFF); // only low 8-bits
100+
101+
if (capability.isMariaDb()) {
102+
buf.writeZero(RESERVED_SIZE)
103+
.writeIntLE(capability.getExtendBitmap());
104+
} else {
105+
buf.writeZero(RESERVED_SIZE + MARIA_DB_CAPABILITY_SIZE);
106+
}
98107
}
99108

100109
int getCollationId() {

src/main/java/io/asyncer/r2dbc/mysql/message/server/HandshakeV10Request.java

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,9 @@
3232
*/
3333
final class HandshakeV10Request implements HandshakeRequest, ServerStatusMessage {
3434

35-
private static final int RESERVED_SIZE = 10;
35+
private static final int RESERVED_SIZE = 6;
36+
37+
private static final int MARIA_DB_CAPABILITY_SIZE = Integer.BYTES;
3638

3739
private static final int SALT_FIRST_PART_SIZE = 8;
3840

@@ -133,23 +135,26 @@ static HandshakeV10Request decode(int envelopeId, ByteBuf buf, HandshakeHeader h
133135
buf.skipBytes(SALT_FIRST_PART_SIZE + 1);
134136

135137
// The Server Capabilities first part following the salt first part. (always lower 2-bytes)
136-
int loCapabilities = buf.readUnsignedShortLE();
138+
long loCapabilities = buf.readUnsignedShortLE();
137139

138140
// MySQL is using 16 bytes to identify server character. There has lower 8-bits only, skip it.
139141
buf.skipBytes(1);
140142
builder.serverStatuses(buf.readShortLE());
141143

142144
// The Server Capabilities second part following the server statuses. (always upper 2-bytes)
143-
int hiCapabilities = buf.readUnsignedShortLE() << Short.SIZE;
144-
Capability capability = Capability.of(loCapabilities | hiCapabilities);
145-
146-
builder.serverCapability(capability);
145+
long miCapabilities = ((long) buf.readUnsignedShortLE()) << Short.SIZE;
146+
Capability capability = Capability.of(loCapabilities | miCapabilities);
147147

148148
// If PLUGIN_AUTH flag not exists, MySQL server will return 0x00 always.
149149
short saltSize = buf.readUnsignedByte();
150150

151-
// Reserved field, all bytes are 0x00.
152-
buf.skipBytes(RESERVED_SIZE);
151+
if (capability.isMariaDb()) {
152+
buf.skipBytes(RESERVED_SIZE);
153+
builder.serverCapability(capability.extendMariaDb(buf.readUnsignedIntLE()));
154+
} else {
155+
buf.skipBytes(RESERVED_SIZE + MARIA_DB_CAPABILITY_SIZE);
156+
builder.serverCapability(capability);
157+
}
153158

154159
if (capability.isSaltSecured()) {
155160
// If it has not this part, means it is using mysql_old_password,

0 commit comments

Comments
 (0)