Skip to content

Commit

Permalink
Client session tracking (#1092)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
insom and bibstha authored Mar 9, 2020
1 parent 48172b5 commit cb9e941
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 1 deletion.
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,23 @@ statement = @client.prepare("SELECT * FROM users WHERE last_login >= ? AND locat
result = statement.execute(1, "CA", :as => :array)
```

Session Tracking information can be accessed with

```ruby
c = Mysql2::Client.new(
host: "127.0.0.1",
username: "root",
flags: "SESSION_TRACK",
init_command: "SET @@SESSION.session_track_schema=ON"
)
c.query("INSERT INTO test VALUES (1)")
session_track_type = Mysql2::Client::SESSION_TRACK_SCHEMA
session_track_data = c.session_track(session_track_type)
```

The types of session track types can be found at
[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)

## Connection options

You may set the following connection options in Mysql2::Client.new(...):
Expand Down
53 changes: 53 additions & 0 deletions ext/mysql2/client.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,17 @@ static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args
#define MYSQL_LINK_VERSION MYSQL_SERVER_VERSION
#endif

/*
* mariadb-connector-c defines CLIENT_SESSION_TRACKING and SESSION_TRACK_TRANSACTION_TYPE
* while mysql-connector-c defines CLIENT_SESSION_TRACK and SESSION_TRACK_TRANSACTION_STATE
* This is a hack to take care of both clients.
*/
#if defined(CLIENT_SESSION_TRACK)
#elif defined(CLIENT_SESSION_TRACKING)
#define CLIENT_SESSION_TRACK CLIENT_SESSION_TRACKING
#define SESSION_TRACK_TRANSACTION_STATE SESSION_TRACK_TRANSACTION_TYPE
#endif

/*
* compatibility with mysql-connector-c 6.1.x, and with MySQL 5.7.3 - 5.7.10.
*/
Expand Down Expand Up @@ -1022,6 +1033,36 @@ static VALUE rb_mysql_client_last_id(VALUE self) {
return ULL2NUM(mysql_insert_id(wrapper->client));
}

/* call-seq:
* client.session_track
*
* Returns information about changes to the session state on the server.
*/
static VALUE rb_mysql_client_session_track(VALUE self, VALUE type) {
#ifdef CLIENT_SESSION_TRACK
const char *data;
size_t length;
my_ulonglong retVal;
GET_CLIENT(self);

REQUIRE_CONNECTED(wrapper);
retVal = mysql_session_track_get_first(wrapper->client, NUM2INT(type), &data, &length);
if (retVal != 0) {
return Qnil;
}
VALUE rbAry = rb_ary_new();
VALUE rbFirst = rb_str_new(data, length);
rb_ary_push(rbAry, rbFirst);
while(mysql_session_track_get_next(wrapper->client, NUM2INT(type), &data, &length) == 0) {
VALUE rbNext = rb_str_new(data, length);
rb_ary_push(rbAry, rbNext);
}
return rbAry;
#else
return Qnil;
#endif
}

/* call-seq:
* client.affected_rows
*
Expand Down Expand Up @@ -1442,6 +1483,7 @@ void init_mysql2_client() {
rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
rb_define_method(cMysql2Client, "session_track", rb_mysql_client_session_track, 1);

rb_define_private_method(cMysql2Client, "connect_timeout=", set_connect_timeout, 1);
rb_define_private_method(cMysql2Client, "read_timeout=", set_read_timeout, 1);
Expand Down Expand Up @@ -1614,6 +1656,17 @@ void init_mysql2_client() {
INT2NUM(0));
#endif

#ifdef CLIENT_SESSION_TRACK
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK"), INT2NUM(CLIENT_SESSION_TRACK));
/* From mysql_com.h -- but stable from at least 5.7.4 through 8.0.20 */
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_SYSTEM_VARIABLES"), INT2NUM(SESSION_TRACK_SYSTEM_VARIABLES));
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_SCHEMA"), INT2NUM(SESSION_TRACK_SCHEMA));
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_STATE_CHANGE"), INT2NUM(SESSION_TRACK_STATE_CHANGE));
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_GTIDS"), INT2NUM(SESSION_TRACK_GTIDS));
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_TRANSACTION_CHARACTERISTICS"), INT2NUM(SESSION_TRACK_TRANSACTION_CHARACTERISTICS));
rb_const_set(cMysql2Client, rb_intern("SESSION_TRACK_TRANSACTION_STATE"), INT2NUM(SESSION_TRACK_TRANSACTION_STATE));
#endif

#if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.7.11 and above
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
Expand Down
45 changes: 44 additions & 1 deletion spec/mysql2/client_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
require 'spec_helper'

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

Expand Down Expand Up @@ -1026,6 +1026,49 @@ def run_gc
expect(@client).to respond_to(:ping)
end

context "session_track" do
before(:each) do
unless Mysql2::Client.const_defined?(:SESSION_TRACK)
skip('Server versions must be MySQL 5.7 later.')
end
@client.query("SET @@SESSION.session_track_system_variables='*';")
end

it "returns changes system variables for SESSION_TRACK_SYSTEM_VARIABLES" do
@client.query("SET @@SESSION.session_track_state_change=ON;")
res = @client.session_track(Mysql2::Client::SESSION_TRACK_SYSTEM_VARIABLES)
expect(res).to eq(%w[session_track_state_change ON])
end

it "returns database name for SESSION_TRACK_SCHEMA" do
@client.query("USE information_schema")
res = @client.session_track(Mysql2::Client::SESSION_TRACK_SCHEMA)
expect(res).to eq(["information_schema"])
end

it "returns multiple session track type values when available" do
@client.query("SET @@SESSION.session_track_transaction_info='CHARACTERISTICS'")

res = @client.session_track(Mysql2::Client::SESSION_TRACK_TRANSACTION_STATE)
expect(res).to eq(["________"])

res = @client.session_track(Mysql2::Client::SESSION_TRACK_TRANSACTION_CHARACTERISTICS)
expect(res).to eq([""])

res = @client.session_track(Mysql2::Client::SESSION_TRACK_STATE_CHANGE)
expect(res).to be_nil

res = @client.session_track(Mysql2::Client::SESSION_TRACK_SYSTEM_VARIABLES)
expect(res).to eq(%w[session_track_transaction_info CHARACTERISTICS])
end

it "returns empty array if session track type not found" do
@client.query("SET @@SESSION.session_track_state_change=ON;")
res = @client.session_track(Mysql2::Client::SESSION_TRACK_TRANSACTION_CHARACTERISTICS)
expect(res).to be_nil
end
end

context "select_db" do
before(:each) do
2.times do |i|
Expand Down

0 comments on commit cb9e941

Please sign in to comment.