Skip to content

Commit cb9e941

Browse files
insombibstha
andauthored
Client session tracking (#1092)
* Client session tracking * Add session tracking enumeration * Conditionally add the session_track method * Add tests for client.session_track * Add session track information to Readme * Disable rubocop block length * Allow compilation with mariadb-connector-c Co-authored-by: Bibek Shrestha <[email protected]>
1 parent 48172b5 commit cb9e941

File tree

3 files changed

+114
-1
lines changed

3 files changed

+114
-1
lines changed

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,23 @@ statement = @client.prepare("SELECT * FROM users WHERE last_login >= ? AND locat
197197
result = statement.execute(1, "CA", :as => :array)
198198
```
199199

200+
Session Tracking information can be accessed with
201+
202+
```ruby
203+
c = Mysql2::Client.new(
204+
host: "127.0.0.1",
205+
username: "root",
206+
flags: "SESSION_TRACK",
207+
init_command: "SET @@SESSION.session_track_schema=ON"
208+
)
209+
c.query("INSERT INTO test VALUES (1)")
210+
session_track_type = Mysql2::Client::SESSION_TRACK_SCHEMA
211+
session_track_data = c.session_track(session_track_type)
212+
```
213+
214+
The types of session track types can be found at
215+
[https://dev.mysql.com/doc/refman/5.7/en/mysql-session-track-get-first.html](https://dev.mysql.com/doc/refman/5.7/en/mysql-session-track-get-first.html)
216+
200217
## Connection options
201218

202219
You may set the following connection options in Mysql2::Client.new(...):

ext/mysql2/client.c

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,17 @@ static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args
5757
#define MYSQL_LINK_VERSION MYSQL_SERVER_VERSION
5858
#endif
5959

60+
/*
61+
* mariadb-connector-c defines CLIENT_SESSION_TRACKING and SESSION_TRACK_TRANSACTION_TYPE
62+
* while mysql-connector-c defines CLIENT_SESSION_TRACK and SESSION_TRACK_TRANSACTION_STATE
63+
* This is a hack to take care of both clients.
64+
*/
65+
#if defined(CLIENT_SESSION_TRACK)
66+
#elif defined(CLIENT_SESSION_TRACKING)
67+
#define CLIENT_SESSION_TRACK CLIENT_SESSION_TRACKING
68+
#define SESSION_TRACK_TRANSACTION_STATE SESSION_TRACK_TRANSACTION_TYPE
69+
#endif
70+
6071
/*
6172
* compatibility with mysql-connector-c 6.1.x, and with MySQL 5.7.3 - 5.7.10.
6273
*/
@@ -1022,6 +1033,36 @@ static VALUE rb_mysql_client_last_id(VALUE self) {
10221033
return ULL2NUM(mysql_insert_id(wrapper->client));
10231034
}
10241035

1036+
/* call-seq:
1037+
* client.session_track
1038+
*
1039+
* Returns information about changes to the session state on the server.
1040+
*/
1041+
static VALUE rb_mysql_client_session_track(VALUE self, VALUE type) {
1042+
#ifdef CLIENT_SESSION_TRACK
1043+
const char *data;
1044+
size_t length;
1045+
my_ulonglong retVal;
1046+
GET_CLIENT(self);
1047+
1048+
REQUIRE_CONNECTED(wrapper);
1049+
retVal = mysql_session_track_get_first(wrapper->client, NUM2INT(type), &data, &length);
1050+
if (retVal != 0) {
1051+
return Qnil;
1052+
}
1053+
VALUE rbAry = rb_ary_new();
1054+
VALUE rbFirst = rb_str_new(data, length);
1055+
rb_ary_push(rbAry, rbFirst);
1056+
while(mysql_session_track_get_next(wrapper->client, NUM2INT(type), &data, &length) == 0) {
1057+
VALUE rbNext = rb_str_new(data, length);
1058+
rb_ary_push(rbAry, rbNext);
1059+
}
1060+
return rbAry;
1061+
#else
1062+
return Qnil;
1063+
#endif
1064+
}
1065+
10251066
/* call-seq:
10261067
* client.affected_rows
10271068
*
@@ -1442,6 +1483,7 @@ void init_mysql2_client() {
14421483
rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
14431484
rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
14441485
rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
1486+
rb_define_method(cMysql2Client, "session_track", rb_mysql_client_session_track, 1);
14451487

14461488
rb_define_private_method(cMysql2Client, "connect_timeout=", set_connect_timeout, 1);
14471489
rb_define_private_method(cMysql2Client, "read_timeout=", set_read_timeout, 1);
@@ -1614,6 +1656,17 @@ void init_mysql2_client() {
16141656
INT2NUM(0));
16151657
#endif
16161658

1659+
#ifdef CLIENT_SESSION_TRACK
1660+
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK"), INT2NUM(CLIENT_SESSION_TRACK));
1661+
/* From mysql_com.h -- but stable from at least 5.7.4 through 8.0.20 */
1662+
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_SYSTEM_VARIABLES"), INT2NUM(SESSION_TRACK_SYSTEM_VARIABLES));
1663+
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_SCHEMA"), INT2NUM(SESSION_TRACK_SCHEMA));
1664+
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_STATE_CHANGE"), INT2NUM(SESSION_TRACK_STATE_CHANGE));
1665+
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_GTIDS"), INT2NUM(SESSION_TRACK_GTIDS));
1666+
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_TRANSACTION_CHARACTERISTICS"), INT2NUM(SESSION_TRACK_TRANSACTION_CHARACTERISTICS));
1667+
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_TRANSACTION_STATE"), INT2NUM(SESSION_TRACK_TRANSACTION_STATE));
1668+
#endif
1669+
16171670
#if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.7.11 and above
16181671
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
16191672
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));

spec/mysql2/client_spec.rb

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
require 'spec_helper'
22

3-
RSpec.describe Mysql2::Client do
3+
RSpec.describe Mysql2::Client do # rubocop:disable Metrics/BlockLength
44
context "using defaults file" do
55
let(:cnf_file) { File.expand_path('../../my.cnf', __FILE__) }
66

@@ -1026,6 +1026,49 @@ def run_gc
10261026
expect(@client).to respond_to(:ping)
10271027
end
10281028

1029+
context "session_track" do
1030+
before(:each) do
1031+
unless Mysql2::Client.const_defined?(:SESSION_TRACK)
1032+
skip('Server versions must be MySQL 5.7 later.')
1033+
end
1034+
@client.query("SET @@SESSION.session_track_system_variables='*';")
1035+
end
1036+
1037+
it "returns changes system variables for SESSION_TRACK_SYSTEM_VARIABLES" do
1038+
@client.query("SET @@SESSION.session_track_state_change=ON;")
1039+
res = @client.session_track(Mysql2::Client::SESSION_TRACK_SYSTEM_VARIABLES)
1040+
expect(res).to eq(%w[session_track_state_change ON])
1041+
end
1042+
1043+
it "returns database name for SESSION_TRACK_SCHEMA" do
1044+
@client.query("USE information_schema")
1045+
res = @client.session_track(Mysql2::Client::SESSION_TRACK_SCHEMA)
1046+
expect(res).to eq(["information_schema"])
1047+
end
1048+
1049+
it "returns multiple session track type values when available" do
1050+
@client.query("SET @@SESSION.session_track_transaction_info='CHARACTERISTICS'")
1051+
1052+
res = @client.session_track(Mysql2::Client::SESSION_TRACK_TRANSACTION_STATE)
1053+
expect(res).to eq(["________"])
1054+
1055+
res = @client.session_track(Mysql2::Client::SESSION_TRACK_TRANSACTION_CHARACTERISTICS)
1056+
expect(res).to eq([""])
1057+
1058+
res = @client.session_track(Mysql2::Client::SESSION_TRACK_STATE_CHANGE)
1059+
expect(res).to be_nil
1060+
1061+
res = @client.session_track(Mysql2::Client::SESSION_TRACK_SYSTEM_VARIABLES)
1062+
expect(res).to eq(%w[session_track_transaction_info CHARACTERISTICS])
1063+
end
1064+
1065+
it "returns empty array if session track type not found" do
1066+
@client.query("SET @@SESSION.session_track_state_change=ON;")
1067+
res = @client.session_track(Mysql2::Client::SESSION_TRACK_TRANSACTION_CHARACTERISTICS)
1068+
expect(res).to be_nil
1069+
end
1070+
end
1071+
10291072
context "select_db" do
10301073
before(:each) do
10311074
2.times do |i|

0 commit comments

Comments
 (0)