Skip to content

Commit 721ae48

Browse files
committed
Support CLIENT_SESSION_TRACK. Fixes #323
1 parent 30c7c79 commit 721ae48

File tree

10 files changed

+119
-13
lines changed

10 files changed

+119
-13
lines changed

.ci/config/config.compression.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"ConnectionString": "server=127.0.0.1;user id=mysqltest;password='test;key=\"val';port=3306;database=mysqltest;ssl mode=none;UseCompression=true;DefaultCommandTimeout=3600",
44
"PasswordlessUser": "no_password",
55
"SecondaryDatabase": "testdb2",
6-
"SupportedFeatures": "Json,StoredProcedures,Sha256Password,LargePackets",
6+
"SupportedFeatures": "Json,StoredProcedures,Sha256Password,LargePackets,SessionTrack",
77
"MySqlBulkLoaderLocalCsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.CSV",
88
"MySqlBulkLoaderLocalTsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.TSV"
99
}

.ci/config/config.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"ConnectionString": "server=127.0.0.1;user id=mysqltest;password='test;key=\"val';port=3306;database=mysqltest;ssl mode=none;Use Affected Rows=true;DefaultCommandTimeout=3600",
44
"PasswordlessUser": "no_password",
55
"SecondaryDatabase": "testdb2",
6-
"SupportedFeatures": "Json,StoredProcedures,Sha256Password,LargePackets",
6+
"SupportedFeatures": "Json,StoredProcedures,Sha256Password,LargePackets,SessionTrack",
77
"MySqlBulkLoaderLocalCsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.CSV",
88
"MySqlBulkLoaderLocalTsvFile": "../../../../TestData/LoadData_UTF8_BOM_Unix.TSV"
99
}

.travis.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,16 @@ env:
88
FEATURES=StoredProcedures
99
- IMAGE=mysql:5.7
1010
NAME=mysql
11-
FEATURES=Json,StoredProcedures,Sha256Password,LargePackets
11+
FEATURES=Json,StoredProcedures,Sha256Password,LargePackets,SessionTrack
1212
- IMAGE=mysql:8.0
1313
NAME=mysql
14-
FEATURES=Json,StoredProcedures,Sha256Password,LargePackets,CachingSha2Password
14+
FEATURES=Json,StoredProcedures,Sha256Password,LargePackets,CachingSha2Password,SessionTrack
1515
- IMAGE=percona:5.7
1616
NAME=percona
17-
FEATURES=Json,StoredProcedures,Sha256Password,OpenSsl,LargePackets
17+
FEATURES=Json,StoredProcedures,Sha256Password,OpenSsl,LargePackets,SessionTrack
1818
- IMAGE=mariadb:10.2
1919
NAME=mariadb
20-
FEATURES=StoredProcedures,OpenSsl,LargePackets
20+
FEATURES=StoredProcedures,OpenSsl,LargePackets,SessionTrack
2121

2222
before_install:
2323
- .ci/docker-run.sh $IMAGE $NAME 3307 $FEATURES

src/MySqlConnector/Core/ResultSet.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ public async Task<ResultSet> ReadResultSetHeaderAsync(IOBehavior ioBehavior)
4848
var ok = OkPayload.Create(payload);
4949
RecordsAffected = (RecordsAffected ?? 0) + ok.AffectedRowCount;
5050
LastInsertId = ok.LastInsertId;
51+
if (ok.NewSchema != null)
52+
Connection.Session.DatabaseOverride = ok.NewSchema;
5153
ColumnDefinitions = null;
5254
ColumnTypes = null;
5355
State = (ok.ServerStatus & ServerStatus.MoreResultsExist) == 0

src/MySqlConnector/Protocol/Payloads/HandshakeResponse41Payload.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ private static PayloadWriter CreateCapabilitiesPayload(ProtocolCapabilities serv
2222
(cs.UseAffectedRows ? 0 : ProtocolCapabilities.FoundRows) |
2323
(useCompression ? ProtocolCapabilities.Compress : ProtocolCapabilities.None) |
2424
(serverCapabilities & ProtocolCapabilities.ConnectionAttributes) |
25+
(serverCapabilities & ProtocolCapabilities.SessionTrack) |
2526
(serverCapabilities & ProtocolCapabilities.DeprecateEof) |
2627
additionalCapabilities));
2728
writer.WriteInt32(0x4000_0000);

src/MySqlConnector/Protocol/Payloads/OkPayload.cs

Lines changed: 39 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
11
using System;
2+
using System.Text;
23
using MySqlConnector.Protocol.Serialization;
34
using MySqlConnector.Utilities;
45

56
namespace MySqlConnector.Protocol.Payloads
67
{
78
internal sealed class OkPayload
89
{
9-
public int AffectedRowCount { get; set; }
10-
public long LastInsertId { get; set; }
11-
public ServerStatus ServerStatus { get; set; }
12-
public int WarningCount { get; set; }
10+
public int AffectedRowCount { get; }
11+
public long LastInsertId { get; }
12+
public ServerStatus ServerStatus { get; }
13+
public int WarningCount { get; }
14+
public string NewSchema { get; }
1315

1416
public const byte Signature = 0x00;
1517

@@ -35,16 +37,47 @@ public static OkPayload Create(PayloadData payload, bool deprecateEof)
3537
var lastInsertId = checked((long) reader.ReadLengthEncodedInteger());
3638
var serverStatus = (ServerStatus) reader.ReadUInt16();
3739
var warningCount = (int) reader.ReadUInt16();
40+
string newSchema = null;
3841

39-
return new OkPayload(affectedRowCount, lastInsertId, serverStatus, warningCount);
42+
if ((serverStatus & ServerStatus.SessionStateChanged) == ServerStatus.SessionStateChanged)
43+
{
44+
reader.ReadLengthEncodedByteString(); // human-readable info
45+
46+
// implies ProtocolCapabilities.SessionTrack
47+
var sessionStateChangeDataLength = checked((int) reader.ReadLengthEncodedInteger());
48+
var endOffset = reader.Offset + sessionStateChangeDataLength;
49+
while (reader.Offset < endOffset)
50+
{
51+
var kind = (SessionTrackKind) reader.ReadByte();
52+
var dataLength = (int) reader.ReadLengthEncodedInteger();
53+
switch (kind)
54+
{
55+
case SessionTrackKind.Schema:
56+
newSchema = Encoding.UTF8.GetString(reader.ReadLengthEncodedByteString());
57+
break;
58+
59+
default:
60+
reader.Offset += dataLength;
61+
break;
62+
}
63+
}
64+
}
65+
else
66+
{
67+
// either "string<EOF> info" or "string<lenenc> info" (followed by no session change info)
68+
// ignore human-readable string in both cases
69+
}
70+
71+
return new OkPayload(affectedRowCount, lastInsertId, serverStatus, warningCount, newSchema);
4072
}
4173

42-
private OkPayload(int affectedRowCount, long lastInsertId, ServerStatus serverStatus, int warningCount)
74+
private OkPayload(int affectedRowCount, long lastInsertId, ServerStatus serverStatus, int warningCount, string newSchema)
4375
{
4476
AffectedRowCount = affectedRowCount;
4577
LastInsertId = lastInsertId;
4678
ServerStatus = serverStatus;
4779
WarningCount = warningCount;
80+
NewSchema = newSchema;
4881
}
4982
}
5083
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
namespace MySqlConnector.Protocol
2+
{
3+
internal enum SessionTrackKind : byte
4+
{
5+
/// <summary>
6+
/// SESSION_TRACK_SYSTEM_VARIABLES: one or more system variables changed
7+
/// </summary>
8+
SystemVariables = 0,
9+
10+
/// <summary>
11+
/// SESSION_TRACK_SCHEMA: schema changed
12+
/// </summary>
13+
Schema = 1,
14+
15+
/// <summary>
16+
/// SESSION_TRACK_STATE_CHANGE: "track state change" changed
17+
/// </summary>
18+
StateChange = 2,
19+
20+
/// <summary>
21+
/// SESSION_TRACK_GTIDS: "track GTIDs" changed
22+
/// </summary>
23+
Gtids = 3,
24+
}
25+
}

tests/SideBySide/ConnectAsync.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,28 @@ public async Task ChangeDatabaseConnectionPooling()
226226
}
227227
}
228228

229+
[SkippableFact(ServerFeatures.SessionTrack, ConfigSettings.SecondaryDatabase, Baseline = "https://bugs.mysql.com/bug.php?id=89085")]
230+
public async Task UseDatabase()
231+
{
232+
var csb = AppConfig.CreateConnectionStringBuilder();
233+
using (var connection = new MySqlConnection(csb.ConnectionString))
234+
{
235+
connection.Open();
236+
237+
Assert.Equal(csb.Database, connection.Database);
238+
Assert.Equal(csb.Database, await QueryCurrentDatabaseAsync(connection));
239+
240+
using (var cmd = connection.CreateCommand())
241+
{
242+
cmd.CommandText = $"USE {AppConfig.SecondaryDatabase};";
243+
await cmd.ExecuteNonQueryAsync();
244+
}
245+
246+
Assert.Equal(AppConfig.SecondaryDatabase, connection.Database);
247+
Assert.Equal(AppConfig.SecondaryDatabase, await QueryCurrentDatabaseAsync(connection));
248+
}
249+
}
250+
229251
private static async Task<string> QueryCurrentDatabaseAsync(MySqlConnection connection)
230252
{
231253
using (var cmd = connection.CreateCommand())

tests/SideBySide/ConnectSync.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,28 @@ public void ChangeDatabaseConnectionPooling()
307307
}
308308
}
309309

310+
[SkippableFact(ServerFeatures.SessionTrack, ConfigSettings.SecondaryDatabase, Baseline = "https://bugs.mysql.com/bug.php?id=89085")]
311+
public void UseDatabase()
312+
{
313+
var csb = AppConfig.CreateConnectionStringBuilder();
314+
using (var connection = new MySqlConnection(csb.ConnectionString))
315+
{
316+
connection.Open();
317+
318+
Assert.Equal(csb.Database, connection.Database);
319+
Assert.Equal(csb.Database, QueryCurrentDatabase(connection));
320+
321+
using (var cmd = connection.CreateCommand())
322+
{
323+
cmd.CommandText = $"USE {AppConfig.SecondaryDatabase};";
324+
cmd.ExecuteNonQuery();
325+
}
326+
327+
Assert.Equal(AppConfig.SecondaryDatabase, connection.Database);
328+
Assert.Equal(AppConfig.SecondaryDatabase, QueryCurrentDatabase(connection));
329+
}
330+
}
331+
310332
private static string QueryCurrentDatabase(MySqlConnection connection)
311333
{
312334
using (var cmd = connection.CreateCommand())

tests/SideBySide/ServerFeatures.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ public enum ServerFeatures
1111
Sha256Password = 4,
1212
OpenSsl = 8,
1313
LargePackets = 16,
14-
CachingSha2Password = 32
14+
CachingSha2Password = 32,
15+
SessionTrack = 64,
1516
}
1617
}

0 commit comments

Comments
 (0)