mysql2 0.4.10 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/README.md +66 -32
- data/ext/mysql2/client.c +125 -59
- data/ext/mysql2/client.h +1 -39
- data/ext/mysql2/extconf.rb +24 -21
- data/ext/mysql2/mysql2_ext.c +2 -1
- data/ext/mysql2/mysql2_ext.h +8 -4
- data/ext/mysql2/mysql_enc_name_to_ruby.h +60 -56
- data/ext/mysql2/mysql_enc_to_ruby.h +64 -3
- data/ext/mysql2/result.c +32 -85
- data/ext/mysql2/result.h +2 -3
- data/ext/mysql2/statement.c +81 -72
- data/ext/mysql2/statement.h +0 -2
- data/ext/mysql2/wait_for_single_fd.h +2 -1
- data/lib/mysql2.rb +17 -15
- data/lib/mysql2/client.rb +33 -27
- data/lib/mysql2/em.rb +2 -4
- data/lib/mysql2/error.rb +51 -22
- data/lib/mysql2/result.rb +2 -0
- data/lib/mysql2/statement.rb +3 -9
- data/lib/mysql2/version.rb +1 -1
- data/support/5072E1F5.asc +5 -5
- data/support/mysql_enc_to_ruby.rb +8 -3
- data/support/ruby_enc_to_mysql.rb +7 -5
- metadata +8 -58
- data/examples/eventmachine.rb +0 -21
- data/examples/threaded.rb +0 -18
- data/spec/configuration.yml.example +0 -11
- data/spec/em/em_spec.rb +0 -136
- data/spec/my.cnf.example +0 -9
- data/spec/mysql2/client_spec.rb +0 -1039
- data/spec/mysql2/error_spec.rb +0 -82
- data/spec/mysql2/result_spec.rb +0 -545
- data/spec/mysql2/statement_spec.rb +0 -776
- data/spec/rcov.opts +0 -3
- data/spec/spec_helper.rb +0 -108
- data/spec/ssl/ca-cert.pem +0 -17
- data/spec/ssl/ca-key.pem +0 -27
- data/spec/ssl/ca.cnf +0 -22
- data/spec/ssl/cert.cnf +0 -22
- data/spec/ssl/client-cert.pem +0 -17
- data/spec/ssl/client-key.pem +0 -27
- data/spec/ssl/client-req.pem +0 -15
- data/spec/ssl/gen_certs.sh +0 -48
- data/spec/ssl/pkcs8-client-key.pem +0 -28
- data/spec/ssl/pkcs8-server-key.pem +0 -28
- data/spec/ssl/server-cert.pem +0 -17
- data/spec/ssl/server-key.pem +0 -27
- data/spec/ssl/server-req.pem +0 -15
- data/spec/test_data +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 8be4e0f4afbea38c5460544e39c9e0d987dd040c9de5ebdbc4c1e200102ca8e4
|
4
|
+
data.tar.gz: 996e05358297b03bf8f06abffd37d83a590912246ec607d2c3ee53b325c43022
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: daf37441bcb29366453963529b5a144df0d599b79b7a0b9d90b2ae5ed3dee97c1945a58111d6fe6bc2f1c20f75c9960c3cc1cd988a0e5d1be5ce44b7dcdf3633
|
7
|
+
data.tar.gz: 421df08c45f4257c2f04f3dc0a0e1c1557c695598c4e1a80236d618d1a79510459b8919b753ad9af2d46d21fa19ba060d5b53da2f54a00ff90ca45a4534c9108
|
data/README.md
CHANGED
@@ -7,7 +7,7 @@ The Mysql2 gem is meant to serve the extremely common use-case of connecting, qu
|
|
7
7
|
Some database libraries out there serve as direct 1:1 mappings of the already complex C APIs available.
|
8
8
|
This one is not.
|
9
9
|
|
10
|
-
It also forces the use of UTF-8 [or binary] for the connection
|
10
|
+
It also forces the use of UTF-8 [or binary] for the connection and uses encoding-aware MySQL API calls where it can.
|
11
11
|
|
12
12
|
The API consists of three classes:
|
13
13
|
|
@@ -18,16 +18,18 @@ The API consists of three classes:
|
|
18
18
|
`Mysql2::Statement` - returned from issuing a #prepare on the connection. Execute the statement to get a Result.
|
19
19
|
|
20
20
|
## Installing
|
21
|
+
|
21
22
|
### General Instructions
|
23
|
+
|
22
24
|
``` sh
|
23
25
|
gem install mysql2
|
24
26
|
```
|
25
27
|
|
26
28
|
This gem links against MySQL's `libmysqlclient` library or `Connector/C`
|
27
29
|
library, and compatible alternatives such as MariaDB.
|
28
|
-
You may need to install a package such as `
|
29
|
-
or other appropriate package for your system. See below for
|
30
|
-
instructions.
|
30
|
+
You may need to install a package such as `libmariadb-dev`, `libmysqlclient-dev`,
|
31
|
+
`mysql-devel`, or other appropriate package for your system. See below for
|
32
|
+
system-specific instructions.
|
31
33
|
|
32
34
|
By default, the mysql2 gem will try to find a copy of MySQL in this order:
|
33
35
|
|
@@ -74,10 +76,11 @@ To see line numbers in backtraces, declare these environment variables
|
|
74
76
|
|
75
77
|
### Linux and other Unixes
|
76
78
|
|
77
|
-
You may need to install a package such as `
|
78
|
-
refer to your distribution's package guide to
|
79
|
-
The most common issue we see is a user who has
|
80
|
-
|
79
|
+
You may need to install a package such as `libmariadb-dev`, `libmysqlclient-dev`,
|
80
|
+
`mysql-devel`, or `default-libmysqlclient-dev`; refer to your distribution's package guide to
|
81
|
+
find the particular package. The most common issue we see is a user who has
|
82
|
+
the library file `libmysqlclient.so` but is missing the header file `mysql.h`
|
83
|
+
-- double check that you have the _-dev_ packages installed.
|
81
84
|
|
82
85
|
### Mac OS X
|
83
86
|
|
@@ -89,6 +92,7 @@ If you have not done so already, you will need to install the XCode select tools
|
|
89
92
|
`xcode-select --install`.
|
90
93
|
|
91
94
|
### Windows
|
95
|
+
|
92
96
|
Make sure that you have Ruby and the DevKit compilers installed. We recommend
|
93
97
|
the [Ruby Installer](http://rubyinstaller.org) distribution.
|
94
98
|
|
@@ -138,7 +142,7 @@ results.each do |row|
|
|
138
142
|
# conveniently, row is a hash
|
139
143
|
# the keys are the fields, as you'd expect
|
140
144
|
# the values are pre-built ruby primitives mapped from their corresponding field types in MySQL
|
141
|
-
puts row["id"] # row["id"].
|
145
|
+
puts row["id"] # row["id"].is_a? Integer
|
142
146
|
if row["dne"] # non-existant hash entry is nil
|
143
147
|
puts row["dne"]
|
144
148
|
end
|
@@ -156,7 +160,7 @@ end
|
|
156
160
|
How about with symbolized keys?
|
157
161
|
|
158
162
|
``` ruby
|
159
|
-
client.query("SELECT * FROM users WHERE group='githubbers'", :symbolize_keys => true) do |row|
|
163
|
+
client.query("SELECT * FROM users WHERE group='githubbers'", :symbolize_keys => true).each do |row|
|
160
164
|
# do something with row, it's ready to rock
|
161
165
|
end
|
162
166
|
```
|
@@ -175,7 +179,11 @@ end
|
|
175
179
|
Prepared statements are supported, as well. In a prepared statement, use a `?`
|
176
180
|
in place of each value and then execute the statement to retrieve a result set.
|
177
181
|
Pass your arguments to the execute method in the same number and order as the
|
178
|
-
question marks in the statement.
|
182
|
+
question marks in the statement. Query options can be passed as keyword arguments
|
183
|
+
to the execute method.
|
184
|
+
|
185
|
+
Be sure to read about the known limitations of prepared statements at
|
186
|
+
[https://dev.mysql.com/doc/refman/5.6/en/c-api-prepared-statement-problems.html](https://dev.mysql.com/doc/refman/5.6/en/c-api-prepared-statement-problems.html)
|
179
187
|
|
180
188
|
``` ruby
|
181
189
|
statement = @client.prepare("SELECT * FROM users WHERE login_count = ?")
|
@@ -184,6 +192,9 @@ result2 = statement.execute(2)
|
|
184
192
|
|
185
193
|
statement = @client.prepare("SELECT * FROM users WHERE last_login >= ? AND location LIKE ?")
|
186
194
|
result = statement.execute(1, "CA")
|
195
|
+
|
196
|
+
statement = @client.prepare("SELECT * FROM users WHERE last_login >= ? AND location LIKE ?")
|
197
|
+
result = statement.execute(1, "CA", :as => :array)
|
187
198
|
```
|
188
199
|
|
189
200
|
## Connection options
|
@@ -203,12 +214,14 @@ Mysql2::Client.new(
|
|
203
214
|
:read_timeout = seconds,
|
204
215
|
:write_timeout = seconds,
|
205
216
|
:connect_timeout = seconds,
|
217
|
+
:connect_attrs = {:program_name => $PROGRAM_NAME, ...},
|
206
218
|
:reconnect = true/false,
|
207
219
|
:local_infile = true/false,
|
208
220
|
:secure_auth = true/false,
|
209
221
|
:ssl_mode = :disabled / :preferred / :required / :verify_ca / :verify_identity,
|
210
222
|
:default_file = '/path/to/my.cfg',
|
211
223
|
:default_group = 'my.cfg section',
|
224
|
+
:default_auth = 'authentication_windows_client'
|
212
225
|
:init_command => sql
|
213
226
|
)
|
214
227
|
```
|
@@ -265,8 +278,10 @@ The string form will be split on whitespace and parsed as with the array form:
|
|
265
278
|
Plain flags are added to the default flags, while flags prefixed with `-`
|
266
279
|
(minus) are removed from the default flags.
|
267
280
|
|
268
|
-
|
269
|
-
|
281
|
+
### Using Active Record's database.yml
|
282
|
+
|
283
|
+
Active Record typically reads its configuration from a file named `database.yml` or an environment variable `DATABASE_URL`.
|
284
|
+
Use the value `mysql2` as the adapter name. For example:
|
270
285
|
|
271
286
|
``` yaml
|
272
287
|
development:
|
@@ -284,6 +299,15 @@ development:
|
|
284
299
|
secure_auth: false
|
285
300
|
```
|
286
301
|
|
302
|
+
### Using Active Record's DATABASE_URL
|
303
|
+
|
304
|
+
Active Record typically reads its configuration from a file named `database.yml` or an environment variable `DATABASE_URL`.
|
305
|
+
Use the value `mysql2` as the protocol name. For example:
|
306
|
+
|
307
|
+
``` shell
|
308
|
+
DATABASE_URL=mysql2://sql_user:sql_pass@sql_host_name:port/sql_db_name?option1=value1&option2=value2
|
309
|
+
```
|
310
|
+
|
287
311
|
### Reading a MySQL config file
|
288
312
|
|
289
313
|
You may read configuration options from a MySQL configuration file by passing
|
@@ -338,7 +362,8 @@ end
|
|
338
362
|
```
|
339
363
|
|
340
364
|
Yields:
|
341
|
-
|
365
|
+
|
366
|
+
```ruby
|
342
367
|
{"1"=>1}
|
343
368
|
{"2"=>2}
|
344
369
|
next_result: Unknown column 'A' in 'field list' (Mysql2::Error)
|
@@ -381,6 +406,15 @@ c = Mysql2::Client.new
|
|
381
406
|
c.query(sql, :symbolize_keys => true)
|
382
407
|
```
|
383
408
|
|
409
|
+
or
|
410
|
+
|
411
|
+
``` ruby
|
412
|
+
# this will set the options for the Mysql2::Result instance returned from the #execute method
|
413
|
+
c = Mysql2::Client.new
|
414
|
+
s = c.prepare(sql)
|
415
|
+
s.execute(arg1, args2, :symbolize_keys => true)
|
416
|
+
```
|
417
|
+
|
384
418
|
## Result types
|
385
419
|
|
386
420
|
### Array of Arrays
|
@@ -488,7 +522,7 @@ There are a few things that need to be kept in mind while using streaming:
|
|
488
522
|
* `:cache_rows` is ignored currently. (if you want to use `:cache_rows` you probably don't want to be using `:stream`)
|
489
523
|
* You must fetch all rows in the result set of your query before you can make new queries. (i.e. with `Mysql2::Result#each`)
|
490
524
|
|
491
|
-
Read more about the consequences of using `mysql_use_result` (what streaming is implemented with) here: http://dev.mysql.com/doc/refman/5.0/en/mysql-use-result.html.
|
525
|
+
Read more about the consequences of using `mysql_use_result` (what streaming is implemented with) here: [http://dev.mysql.com/doc/refman/5.0/en/mysql-use-result.html](http://dev.mysql.com/doc/refman/5.0/en/mysql-use-result.html).
|
492
526
|
|
493
527
|
### Lazy Everything
|
494
528
|
|
@@ -509,21 +543,21 @@ As for field values themselves, I'm workin on it - but expect that soon.
|
|
509
543
|
|
510
544
|
This gem is tested with the following Ruby versions on Linux and Mac OS X:
|
511
545
|
|
512
|
-
|
513
|
-
|
514
|
-
* Rubinius 2.x and 3.x do work but may fail under some workloads
|
546
|
+
* Ruby MRI 2.0.0, 2.1.x, 2.2.x, 2.3.x, 2.4.x, 2.5.x, 2.6.x
|
547
|
+
* Rubinius 2.x and 3.x do work but may fail under some workloads
|
515
548
|
|
516
549
|
This gem is tested with the following MySQL and MariaDB versions:
|
517
550
|
|
518
|
-
|
519
|
-
|
520
|
-
|
551
|
+
* MySQL 5.5, 5.6, 5.7, 8.0
|
552
|
+
* MySQL Connector/C 6.0 and 6.1 (primarily on Windows)
|
553
|
+
* MariaDB 5.5, 10.0, 10.1, 10.2, 10.3
|
521
554
|
|
522
555
|
### Ruby on Rails / Active Record
|
523
556
|
|
524
|
-
|
525
|
-
|
526
|
-
|
557
|
+
* mysql2 0.5.x works with Rails / Active Record 5.0.7, 5.1.6, and higher.
|
558
|
+
* mysql2 0.4.x works with Rails / Active Record 4.2.5 - 5.0 and higher.
|
559
|
+
* mysql2 0.3.x works with Rails / Active Record 3.1, 3.2, 4.x, 5.0.
|
560
|
+
* mysql2 0.2.x works with Rails / Active Record 2.3 - 3.0.
|
527
561
|
|
528
562
|
### Asynchronous Active Record
|
529
563
|
|
@@ -606,11 +640,11 @@ though.
|
|
606
640
|
## Special Thanks
|
607
641
|
|
608
642
|
* Eric Wong - for the contribution (and the informative explanations) of some thread-safety, non-blocking I/O and cleanup patches. You rock dude
|
609
|
-
* Yury Korolev
|
610
|
-
* Aaron Patterson
|
611
|
-
* Mike Perham
|
612
|
-
* Aaron Stone
|
613
|
-
* Kouhei Ueno
|
614
|
-
* John Cant
|
615
|
-
* Justin Case
|
616
|
-
* Tamir Duberstein
|
643
|
+
* [Yury Korolev](http://github.com/yury) - for TONS of help testing the Active Record adapter
|
644
|
+
* [Aaron Patterson](http://github.com/tenderlove) - tons of contributions, suggestions and general badassness
|
645
|
+
* [Mike Perham](http://github.com/mperham) - Async Active Record adapter (uses Fibers and EventMachine)
|
646
|
+
* [Aaron Stone](http://github.com/sodabrew) - additional client settings, local files, microsecond time, maintenance support
|
647
|
+
* [Kouhei Ueno](https://github.com/nyaxt) - for the original work on Prepared Statements way back in 2012
|
648
|
+
* [John Cant](http://github.com/johncant) - polishing and updating Prepared Statements support
|
649
|
+
* [Justin Case](http://github.com/justincase) - polishing and updating Prepared Statements support and getting it merged
|
650
|
+
* [Tamir Duberstein](http://github.com/tamird) - for help with timeouts and all around updates and cleanups
|
data/ext/mysql2/client.c
CHANGED
@@ -15,15 +15,11 @@
|
|
15
15
|
#include "mysql_enc_name_to_ruby.h"
|
16
16
|
|
17
17
|
VALUE cMysql2Client;
|
18
|
-
extern VALUE mMysql2, cMysql2Error;
|
18
|
+
extern VALUE mMysql2, cMysql2Error, cMysql2TimeoutError;
|
19
19
|
static VALUE sym_id, sym_version, sym_header_version, sym_async, sym_symbolize_keys, sym_as, sym_array, sym_stream;
|
20
|
-
static
|
21
|
-
|
22
|
-
|
23
|
-
VALUE rb_hash_dup(VALUE other) {
|
24
|
-
return rb_funcall(rb_cHash, intern_brackets, 1, other);
|
25
|
-
}
|
26
|
-
#endif
|
20
|
+
static VALUE sym_no_good_index_used, sym_no_index_used, sym_query_was_slow;
|
21
|
+
static ID intern_brackets, intern_merge, intern_merge_bang, intern_new_with_args,
|
22
|
+
intern_current_query_options, intern_read_timeout;
|
27
23
|
|
28
24
|
#define REQUIRE_INITIALIZED(wrapper) \
|
29
25
|
if (!wrapper->initialized) { \
|
@@ -117,15 +113,19 @@ static VALUE rb_set_ssl_mode_option(VALUE self, VALUE setting) {
|
|
117
113
|
#ifdef HAVE_CONST_MYSQL_OPT_SSL_ENFORCE
|
118
114
|
GET_CLIENT(self);
|
119
115
|
int val = NUM2INT( setting );
|
120
|
-
|
116
|
+
// Either MySQL 5.7.3 - 5.7.10, or Connector/C 6.1.3 - 6.1.x
|
117
|
+
if ((version >= 50703 && version < 50711) || (version >= 60103 && version < 60200)) {
|
121
118
|
if (val == SSL_MODE_DISABLED || val == SSL_MODE_REQUIRED) {
|
122
|
-
|
119
|
+
my_bool b = ( val == SSL_MODE_REQUIRED );
|
123
120
|
int result = mysql_options( wrapper->client, MYSQL_OPT_SSL_ENFORCE, &b );
|
124
121
|
return INT2NUM(result);
|
125
122
|
} else {
|
126
123
|
rb_warn( "MySQL client libraries between 5.7.3 and 5.7.10 only support SSL_MODE_DISABLED and SSL_MODE_REQUIRED" );
|
127
124
|
return Qnil;
|
128
125
|
}
|
126
|
+
} else {
|
127
|
+
rb_warn( "Your mysql client library does not support ssl_mode as expected." );
|
128
|
+
return Qnil;
|
129
129
|
}
|
130
130
|
#endif
|
131
131
|
#ifdef FULL_SSL_MODE_SUPPORT
|
@@ -178,10 +178,8 @@ static VALUE rb_raise_mysql2_error(mysql_client_wrapper *wrapper) {
|
|
178
178
|
VALUE rb_sql_state = rb_tainted_str_new2(mysql_sqlstate(wrapper->client));
|
179
179
|
VALUE e;
|
180
180
|
|
181
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
182
181
|
rb_enc_associate(rb_error_msg, rb_utf8_encoding());
|
183
182
|
rb_enc_associate(rb_sql_state, rb_usascii_encoding());
|
184
|
-
#endif
|
185
183
|
|
186
184
|
e = rb_funcall(cMysql2Error, intern_new_with_args, 4,
|
187
185
|
rb_error_msg,
|
@@ -357,9 +355,7 @@ static VALUE rb_mysql_client_escape(RB_MYSQL_UNUSED VALUE klass, VALUE str) {
|
|
357
355
|
return str;
|
358
356
|
} else {
|
359
357
|
rb_str = rb_str_new((const char*)newStr, newLen);
|
360
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
361
358
|
rb_enc_copy(rb_str, str);
|
362
|
-
#endif
|
363
359
|
xfree(newStr);
|
364
360
|
return rb_str;
|
365
361
|
}
|
@@ -386,9 +382,7 @@ static VALUE rb_mysql_info(VALUE self) {
|
|
386
382
|
}
|
387
383
|
|
388
384
|
rb_str = rb_str_new2(info);
|
389
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
390
385
|
rb_enc_associate(rb_str, rb_utf8_encoding());
|
391
|
-
#endif
|
392
386
|
|
393
387
|
return rb_str;
|
394
388
|
}
|
@@ -406,14 +400,25 @@ static VALUE rb_mysql_get_ssl_cipher(VALUE self)
|
|
406
400
|
}
|
407
401
|
|
408
402
|
rb_str = rb_str_new2(cipher);
|
409
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
410
403
|
rb_enc_associate(rb_str, rb_utf8_encoding());
|
411
|
-
#endif
|
412
404
|
|
413
405
|
return rb_str;
|
414
406
|
}
|
415
407
|
|
416
|
-
|
408
|
+
#ifdef CLIENT_CONNECT_ATTRS
|
409
|
+
static int opt_connect_attr_add_i(VALUE key, VALUE value, VALUE arg)
|
410
|
+
{
|
411
|
+
mysql_client_wrapper *wrapper = (mysql_client_wrapper *)arg;
|
412
|
+
rb_encoding *enc = rb_to_encoding(wrapper->encoding);
|
413
|
+
key = rb_str_export_to_enc(key, enc);
|
414
|
+
value = rb_str_export_to_enc(value, enc);
|
415
|
+
|
416
|
+
mysql_options4(wrapper->client, MYSQL_OPT_CONNECT_ATTR_ADD, StringValueCStr(key), StringValueCStr(value));
|
417
|
+
return ST_CONTINUE;
|
418
|
+
}
|
419
|
+
#endif
|
420
|
+
|
421
|
+
static VALUE rb_mysql_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE port, VALUE database, VALUE socket, VALUE flags, VALUE conn_attrs) {
|
417
422
|
struct nogvl_connect_args args;
|
418
423
|
time_t start_time, end_time, elapsed_time, connect_timeout;
|
419
424
|
VALUE rv;
|
@@ -428,6 +433,11 @@ static VALUE rb_connect(VALUE self, VALUE user, VALUE pass, VALUE host, VALUE po
|
|
428
433
|
args.mysql = wrapper->client;
|
429
434
|
args.client_flag = NUM2ULONG(flags);
|
430
435
|
|
436
|
+
#ifdef CLIENT_CONNECT_ATTRS
|
437
|
+
mysql_options(wrapper->client, MYSQL_OPT_CONNECT_ATTR_RESET, 0);
|
438
|
+
rb_hash_foreach(conn_attrs, opt_connect_attr_add_i, (VALUE)wrapper);
|
439
|
+
#endif
|
440
|
+
|
431
441
|
if (wrapper->connect_timeout)
|
432
442
|
time(&start_time);
|
433
443
|
rv = (VALUE) rb_thread_call_without_gvl(nogvl_connect, &args, RUBY_UBF_IO, 0);
|
@@ -521,7 +531,7 @@ static VALUE do_send_query(void *args) {
|
|
521
531
|
*/
|
522
532
|
static void *nogvl_read_query_result(void *ptr) {
|
523
533
|
MYSQL * client = ptr;
|
524
|
-
|
534
|
+
my_bool res = mysql_read_query_result(client);
|
525
535
|
|
526
536
|
return (void *)(res == 0 ? Qtrue : Qfalse);
|
527
537
|
}
|
@@ -574,7 +584,7 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
574
584
|
rb_raise_mysql2_error(wrapper);
|
575
585
|
}
|
576
586
|
|
577
|
-
is_streaming = rb_hash_aref(
|
587
|
+
is_streaming = rb_hash_aref(rb_ivar_get(self, intern_current_query_options), sym_stream);
|
578
588
|
if (is_streaming == Qtrue) {
|
579
589
|
result = (MYSQL_RES *)rb_thread_call_without_gvl(nogvl_use_result, wrapper, RUBY_UBF_IO, 0);
|
580
590
|
} else {
|
@@ -590,11 +600,14 @@ static VALUE rb_mysql_client_async_result(VALUE self) {
|
|
590
600
|
return Qnil;
|
591
601
|
}
|
592
602
|
|
593
|
-
|
603
|
+
// Duplicate the options hash and put the copy in the Result object
|
604
|
+
current = rb_hash_dup(rb_ivar_get(self, intern_current_query_options));
|
594
605
|
(void)RB_GC_GUARD(current);
|
595
606
|
Check_Type(current, T_HASH);
|
596
607
|
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
|
597
608
|
|
609
|
+
rb_mysql_set_server_query_flags(wrapper->client, resultObj);
|
610
|
+
|
598
611
|
return resultObj;
|
599
612
|
}
|
600
613
|
|
@@ -631,7 +644,7 @@ static VALUE do_query(void *args) {
|
|
631
644
|
int retval;
|
632
645
|
VALUE read_timeout;
|
633
646
|
|
634
|
-
read_timeout =
|
647
|
+
read_timeout = rb_ivar_get(async_args->self, intern_read_timeout);
|
635
648
|
|
636
649
|
tvp = NULL;
|
637
650
|
if (!NIL_P(read_timeout)) {
|
@@ -652,7 +665,7 @@ static VALUE do_query(void *args) {
|
|
652
665
|
retval = rb_wait_for_single_fd(async_args->fd, RB_WAITFD_IN, tvp);
|
653
666
|
|
654
667
|
if (retval == 0) {
|
655
|
-
rb_raise(
|
668
|
+
rb_raise(cMysql2TimeoutError, "Timeout waiting for a response from the last query. (waited %d seconds)", FIX2INT(read_timeout));
|
656
669
|
}
|
657
670
|
|
658
671
|
if (retval < 0) {
|
@@ -747,7 +760,7 @@ static VALUE rb_mysql_client_abandon_results(VALUE self) {
|
|
747
760
|
* Query the database with +sql+, with optional +options+. For the possible
|
748
761
|
* options, see default_query_options on the Mysql2::Client class.
|
749
762
|
*/
|
750
|
-
static VALUE
|
763
|
+
static VALUE rb_mysql_query(VALUE self, VALUE sql, VALUE current) {
|
751
764
|
#ifndef _WIN32
|
752
765
|
struct async_query_args async_args;
|
753
766
|
#endif
|
@@ -759,15 +772,11 @@ static VALUE rb_query(VALUE self, VALUE sql, VALUE current) {
|
|
759
772
|
|
760
773
|
(void)RB_GC_GUARD(current);
|
761
774
|
Check_Type(current, T_HASH);
|
762
|
-
|
775
|
+
rb_ivar_set(self, intern_current_query_options, current);
|
763
776
|
|
764
777
|
Check_Type(sql, T_STRING);
|
765
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
766
778
|
/* ensure the string is in the encoding the connection is expecting */
|
767
779
|
args.sql = rb_str_export_to_enc(sql, rb_to_encoding(wrapper->encoding));
|
768
|
-
#else
|
769
|
-
args.sql = sql;
|
770
|
-
#endif
|
771
780
|
args.sql_ptr = RSTRING_PTR(args.sql);
|
772
781
|
args.sql_len = RSTRING_LEN(args.sql);
|
773
782
|
args.wrapper = wrapper;
|
@@ -804,20 +813,16 @@ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
|
|
804
813
|
unsigned char *newStr;
|
805
814
|
VALUE rb_str;
|
806
815
|
unsigned long newLen, oldLen;
|
807
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
808
816
|
rb_encoding *default_internal_enc;
|
809
817
|
rb_encoding *conn_enc;
|
810
|
-
#endif
|
811
818
|
GET_CLIENT(self);
|
812
819
|
|
813
820
|
REQUIRE_CONNECTED(wrapper);
|
814
821
|
Check_Type(str, T_STRING);
|
815
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
816
822
|
default_internal_enc = rb_default_internal_encoding();
|
817
823
|
conn_enc = rb_to_encoding(wrapper->encoding);
|
818
824
|
/* ensure the string is in the encoding the connection is expecting */
|
819
825
|
str = rb_str_export_to_enc(str, conn_enc);
|
820
|
-
#endif
|
821
826
|
|
822
827
|
oldLen = RSTRING_LEN(str);
|
823
828
|
newStr = xmalloc(oldLen*2+1);
|
@@ -825,21 +830,17 @@ static VALUE rb_mysql_client_real_escape(VALUE self, VALUE str) {
|
|
825
830
|
newLen = mysql_real_escape_string(wrapper->client, (char *)newStr, RSTRING_PTR(str), oldLen);
|
826
831
|
if (newLen == oldLen) {
|
827
832
|
/* no need to return a new ruby string if nothing changed */
|
828
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
829
833
|
if (default_internal_enc) {
|
830
834
|
str = rb_str_export_to_enc(str, default_internal_enc);
|
831
835
|
}
|
832
|
-
#endif
|
833
836
|
xfree(newStr);
|
834
837
|
return str;
|
835
838
|
} else {
|
836
839
|
rb_str = rb_str_new((const char*)newStr, newLen);
|
837
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
838
840
|
rb_enc_associate(rb_str, conn_enc);
|
839
841
|
if (default_internal_enc) {
|
840
842
|
rb_str = rb_str_export_to_enc(rb_str, default_internal_enc);
|
841
843
|
}
|
842
|
-
#endif
|
843
844
|
xfree(newStr);
|
844
845
|
return rb_str;
|
845
846
|
}
|
@@ -850,7 +851,7 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
850
851
|
const void *retval = NULL;
|
851
852
|
unsigned int intval = 0;
|
852
853
|
const char * charval = NULL;
|
853
|
-
|
854
|
+
my_bool boolval;
|
854
855
|
|
855
856
|
GET_CLIENT(self);
|
856
857
|
|
@@ -907,6 +908,11 @@ static VALUE _mysql_client_options(VALUE self, int opt, VALUE value) {
|
|
907
908
|
retval = charval;
|
908
909
|
break;
|
909
910
|
|
911
|
+
case MYSQL_DEFAULT_AUTH:
|
912
|
+
charval = (const char *)StringValueCStr(value);
|
913
|
+
retval = charval;
|
914
|
+
break;
|
915
|
+
|
910
916
|
#ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
|
911
917
|
case MYSQL_ENABLE_CLEARTEXT_PLUGIN:
|
912
918
|
boolval = (value == Qfalse ? 0 : 1);
|
@@ -950,10 +956,8 @@ static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
|
|
950
956
|
version = rb_str_new2(mysql_get_client_info());
|
951
957
|
header_version = rb_str_new2(MYSQL_LINK_VERSION);
|
952
958
|
|
953
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
954
959
|
rb_enc_associate(version, rb_usascii_encoding());
|
955
960
|
rb_enc_associate(header_version, rb_usascii_encoding());
|
956
|
-
#endif
|
957
961
|
|
958
962
|
rb_hash_aset(version_info, sym_id, LONG2NUM(mysql_get_client_version()));
|
959
963
|
rb_hash_aset(version_info, sym_version, version);
|
@@ -969,27 +973,21 @@ static VALUE rb_mysql_client_info(RB_MYSQL_UNUSED VALUE klass) {
|
|
969
973
|
*/
|
970
974
|
static VALUE rb_mysql_client_server_info(VALUE self) {
|
971
975
|
VALUE version, server_info;
|
972
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
973
976
|
rb_encoding *default_internal_enc;
|
974
977
|
rb_encoding *conn_enc;
|
975
|
-
#endif
|
976
978
|
GET_CLIENT(self);
|
977
979
|
|
978
980
|
REQUIRE_CONNECTED(wrapper);
|
979
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
980
981
|
default_internal_enc = rb_default_internal_encoding();
|
981
982
|
conn_enc = rb_to_encoding(wrapper->encoding);
|
982
|
-
#endif
|
983
983
|
|
984
984
|
version = rb_hash_new();
|
985
985
|
rb_hash_aset(version, sym_id, LONG2FIX(mysql_get_server_version(wrapper->client)));
|
986
986
|
server_info = rb_str_new2(mysql_get_server_info(wrapper->client));
|
987
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
988
987
|
rb_enc_associate(server_info, conn_enc);
|
989
988
|
if (default_internal_enc) {
|
990
989
|
server_info = rb_str_export_to_enc(server_info, default_internal_enc);
|
991
990
|
}
|
992
|
-
#endif
|
993
991
|
rb_hash_aset(version, sym_version, server_info);
|
994
992
|
return version;
|
995
993
|
}
|
@@ -1110,6 +1108,23 @@ static VALUE rb_mysql_client_ping(VALUE self) {
|
|
1110
1108
|
}
|
1111
1109
|
}
|
1112
1110
|
|
1111
|
+
/* call-seq:
|
1112
|
+
* client.set_server_option(value)
|
1113
|
+
*
|
1114
|
+
* Enables or disables an option for the connection.
|
1115
|
+
* Read https://dev.mysql.com/doc/refman/5.7/en/mysql-set-server-option.html
|
1116
|
+
* for more information.
|
1117
|
+
*/
|
1118
|
+
static VALUE rb_mysql_client_set_server_option(VALUE self, VALUE value) {
|
1119
|
+
GET_CLIENT(self);
|
1120
|
+
|
1121
|
+
if (mysql_set_server_option(wrapper->client, NUM2INT(value)) == 0) {
|
1122
|
+
return Qtrue;
|
1123
|
+
} else {
|
1124
|
+
return Qfalse;
|
1125
|
+
}
|
1126
|
+
}
|
1127
|
+
|
1113
1128
|
/* call-seq:
|
1114
1129
|
* client.more_results?
|
1115
1130
|
*
|
@@ -1168,7 +1183,8 @@ static VALUE rb_mysql_client_store_result(VALUE self)
|
|
1168
1183
|
return Qnil;
|
1169
1184
|
}
|
1170
1185
|
|
1171
|
-
|
1186
|
+
// Duplicate the options hash and put the copy in the Result object
|
1187
|
+
current = rb_hash_dup(rb_ivar_get(self, intern_current_query_options));
|
1172
1188
|
(void)RB_GC_GUARD(current);
|
1173
1189
|
Check_Type(current, T_HASH);
|
1174
1190
|
resultObj = rb_mysql_result_to_obj(self, wrapper->encoding, current, result, Qnil);
|
@@ -1176,7 +1192,6 @@ static VALUE rb_mysql_client_store_result(VALUE self)
|
|
1176
1192
|
return resultObj;
|
1177
1193
|
}
|
1178
1194
|
|
1179
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
1180
1195
|
/* call-seq:
|
1181
1196
|
* client.encoding
|
1182
1197
|
*
|
@@ -1186,7 +1201,6 @@ static VALUE rb_mysql_client_encoding(VALUE self) {
|
|
1186
1201
|
GET_CLIENT(self);
|
1187
1202
|
return wrapper->encoding;
|
1188
1203
|
}
|
1189
|
-
#endif
|
1190
1204
|
|
1191
1205
|
/* call-seq:
|
1192
1206
|
* client.automatic_close?
|
@@ -1256,7 +1270,7 @@ static VALUE set_read_timeout(VALUE self, VALUE value) {
|
|
1256
1270
|
/* Set the instance variable here even though _mysql_client_options
|
1257
1271
|
might not succeed, because the timeout is used in other ways
|
1258
1272
|
elsewhere */
|
1259
|
-
|
1273
|
+
rb_ivar_set(self, intern_read_timeout, value);
|
1260
1274
|
return _mysql_client_options(self, MYSQL_OPT_READ_TIMEOUT, value);
|
1261
1275
|
}
|
1262
1276
|
|
@@ -1272,17 +1286,14 @@ static VALUE set_write_timeout(VALUE self, VALUE value) {
|
|
1272
1286
|
|
1273
1287
|
static VALUE set_charset_name(VALUE self, VALUE value) {
|
1274
1288
|
char *charset_name;
|
1275
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
1276
1289
|
const struct mysql2_mysql_enc_name_to_rb_map *mysql2rb;
|
1277
1290
|
rb_encoding *enc;
|
1278
1291
|
VALUE rb_enc;
|
1279
|
-
#endif
|
1280
1292
|
GET_CLIENT(self);
|
1281
1293
|
|
1282
1294
|
Check_Type(value, T_STRING);
|
1283
1295
|
charset_name = RSTRING_PTR(value);
|
1284
1296
|
|
1285
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
1286
1297
|
mysql2rb = mysql2_mysql_enc_name_to_rb(charset_name, (unsigned int)RSTRING_LEN(value));
|
1287
1298
|
if (mysql2rb == NULL || mysql2rb->rb_name == NULL) {
|
1288
1299
|
VALUE inspect = rb_inspect(value);
|
@@ -1292,7 +1303,6 @@ static VALUE set_charset_name(VALUE self, VALUE value) {
|
|
1292
1303
|
rb_enc = rb_enc_from_encoding(enc);
|
1293
1304
|
wrapper->encoding = rb_enc;
|
1294
1305
|
}
|
1295
|
-
#endif
|
1296
1306
|
|
1297
1307
|
if (mysql_options(wrapper->client, MYSQL_SET_CHARSET_NAME, charset_name)) {
|
1298
1308
|
/* TODO: warning - unable to set charset */
|
@@ -1336,6 +1346,10 @@ static VALUE set_init_command(VALUE self, VALUE value) {
|
|
1336
1346
|
return _mysql_client_options(self, MYSQL_INIT_COMMAND, value);
|
1337
1347
|
}
|
1338
1348
|
|
1349
|
+
static VALUE set_default_auth(VALUE self, VALUE value) {
|
1350
|
+
return _mysql_client_options(self, MYSQL_DEFAULT_AUTH, value);
|
1351
|
+
}
|
1352
|
+
|
1339
1353
|
static VALUE set_enable_cleartext_plugin(VALUE self, VALUE value) {
|
1340
1354
|
#ifdef HAVE_CONST_MYSQL_ENABLE_CLEARTEXT_PLUGIN
|
1341
1355
|
return _mysql_client_options(self, MYSQL_ENABLE_CLEARTEXT_PLUGIN, value);
|
@@ -1416,6 +1430,7 @@ void init_mysql2_client() {
|
|
1416
1430
|
rb_define_method(cMysql2Client, "thread_id", rb_mysql_client_thread_id, 0);
|
1417
1431
|
rb_define_method(cMysql2Client, "ping", rb_mysql_client_ping, 0);
|
1418
1432
|
rb_define_method(cMysql2Client, "select_db", rb_mysql_client_select_db, 1);
|
1433
|
+
rb_define_method(cMysql2Client, "set_server_option", rb_mysql_client_set_server_option, 1);
|
1419
1434
|
rb_define_method(cMysql2Client, "more_results?", rb_mysql_client_more_results, 0);
|
1420
1435
|
rb_define_method(cMysql2Client, "next_result", rb_mysql_client_next_result, 0);
|
1421
1436
|
rb_define_method(cMysql2Client, "store_result", rb_mysql_client_store_result, 0);
|
@@ -1425,9 +1440,7 @@ void init_mysql2_client() {
|
|
1425
1440
|
rb_define_method(cMysql2Client, "warning_count", rb_mysql_client_warning_count, 0);
|
1426
1441
|
rb_define_method(cMysql2Client, "query_info_string", rb_mysql_info, 0);
|
1427
1442
|
rb_define_method(cMysql2Client, "ssl_cipher", rb_mysql_get_ssl_cipher, 0);
|
1428
|
-
#ifdef HAVE_RUBY_ENCODING_H
|
1429
1443
|
rb_define_method(cMysql2Client, "encoding", rb_mysql_client_encoding, 0);
|
1430
|
-
#endif
|
1431
1444
|
|
1432
1445
|
rb_define_private_method(cMysql2Client, "connect_timeout=", set_connect_timeout, 1);
|
1433
1446
|
rb_define_private_method(cMysql2Client, "read_timeout=", set_read_timeout, 1);
|
@@ -1438,12 +1451,13 @@ void init_mysql2_client() {
|
|
1438
1451
|
rb_define_private_method(cMysql2Client, "default_file=", set_read_default_file, 1);
|
1439
1452
|
rb_define_private_method(cMysql2Client, "default_group=", set_read_default_group, 1);
|
1440
1453
|
rb_define_private_method(cMysql2Client, "init_command=", set_init_command, 1);
|
1454
|
+
rb_define_private_method(cMysql2Client, "default_auth=", set_default_auth, 1);
|
1441
1455
|
rb_define_private_method(cMysql2Client, "ssl_set", set_ssl_options, 5);
|
1442
1456
|
rb_define_private_method(cMysql2Client, "ssl_mode=", rb_set_ssl_mode_option, 1);
|
1443
1457
|
rb_define_private_method(cMysql2Client, "enable_cleartext_plugin=", set_enable_cleartext_plugin, 1);
|
1444
1458
|
rb_define_private_method(cMysql2Client, "initialize_ext", initialize_ext, 0);
|
1445
|
-
rb_define_private_method(cMysql2Client, "connect",
|
1446
|
-
rb_define_private_method(cMysql2Client, "_query",
|
1459
|
+
rb_define_private_method(cMysql2Client, "connect", rb_mysql_connect, 8);
|
1460
|
+
rb_define_private_method(cMysql2Client, "_query", rb_mysql_query, 2);
|
1447
1461
|
|
1448
1462
|
sym_id = ID2SYM(rb_intern("id"));
|
1449
1463
|
sym_version = ID2SYM(rb_intern("version"));
|
@@ -1454,10 +1468,16 @@ void init_mysql2_client() {
|
|
1454
1468
|
sym_array = ID2SYM(rb_intern("array"));
|
1455
1469
|
sym_stream = ID2SYM(rb_intern("stream"));
|
1456
1470
|
|
1471
|
+
sym_no_good_index_used = ID2SYM(rb_intern("no_good_index_used"));
|
1472
|
+
sym_no_index_used = ID2SYM(rb_intern("no_index_used"));
|
1473
|
+
sym_query_was_slow = ID2SYM(rb_intern("query_was_slow"));
|
1474
|
+
|
1457
1475
|
intern_brackets = rb_intern("[]");
|
1458
1476
|
intern_merge = rb_intern("merge");
|
1459
1477
|
intern_merge_bang = rb_intern("merge!");
|
1460
1478
|
intern_new_with_args = rb_intern("new_with_args");
|
1479
|
+
intern_current_query_options = rb_intern("@current_query_options");
|
1480
|
+
intern_read_timeout = rb_intern("@read_timeout");
|
1461
1481
|
|
1462
1482
|
#ifdef CLIENT_LONG_PASSWORD
|
1463
1483
|
rb_const_set(cMysql2Client, rb_intern("LONG_PASSWORD"),
|
@@ -1543,6 +1563,16 @@ void init_mysql2_client() {
|
|
1543
1563
|
rb_const_set(cMysql2Client, rb_intern("SECURE_CONNECTION"), LONG2NUM(0));
|
1544
1564
|
#endif
|
1545
1565
|
|
1566
|
+
#ifdef HAVE_CONST_MYSQL_OPTION_MULTI_STATEMENTS_ON
|
1567
|
+
rb_const_set(cMysql2Client, rb_intern("OPTION_MULTI_STATEMENTS_ON"),
|
1568
|
+
LONG2NUM(MYSQL_OPTION_MULTI_STATEMENTS_ON));
|
1569
|
+
#endif
|
1570
|
+
|
1571
|
+
#ifdef HAVE_CONST_MYSQL_OPTION_MULTI_STATEMENTS_OFF
|
1572
|
+
rb_const_set(cMysql2Client, rb_intern("OPTION_MULTI_STATEMENTS_OFF"),
|
1573
|
+
LONG2NUM(MYSQL_OPTION_MULTI_STATEMENTS_OFF));
|
1574
|
+
#endif
|
1575
|
+
|
1546
1576
|
#ifdef CLIENT_MULTI_STATEMENTS
|
1547
1577
|
rb_const_set(cMysql2Client, rb_intern("MULTI_STATEMENTS"),
|
1548
1578
|
LONG2NUM(CLIENT_MULTI_STATEMENTS));
|
@@ -1573,6 +1603,16 @@ void init_mysql2_client() {
|
|
1573
1603
|
LONG2NUM(CLIENT_BASIC_FLAGS));
|
1574
1604
|
#endif
|
1575
1605
|
|
1606
|
+
#ifdef CLIENT_CONNECT_ATTRS
|
1607
|
+
rb_const_set(cMysql2Client, rb_intern("CONNECT_ATTRS"),
|
1608
|
+
LONG2NUM(CLIENT_CONNECT_ATTRS));
|
1609
|
+
#else
|
1610
|
+
/* HACK because MySQL 5.5 and earlier don't define this constant,
|
1611
|
+
* but we're using it in our default connection flags. */
|
1612
|
+
rb_const_set(cMysql2Client, rb_intern("CONNECT_ATTRS"),
|
1613
|
+
INT2NUM(0));
|
1614
|
+
#endif
|
1615
|
+
|
1576
1616
|
#if defined(FULL_SSL_MODE_SUPPORT) // MySQL 5.7.11 and above
|
1577
1617
|
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_DISABLED"), INT2NUM(SSL_MODE_DISABLED));
|
1578
1618
|
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_PREFERRED"), INT2NUM(SSL_MODE_PREFERRED));
|
@@ -1600,3 +1640,29 @@ void init_mysql2_client() {
|
|
1600
1640
|
rb_const_set(cMysql2Client, rb_intern("SSL_MODE_VERIFY_IDENTITY"), INT2NUM(0));
|
1601
1641
|
#endif
|
1602
1642
|
}
|
1643
|
+
|
1644
|
+
#define flag_to_bool(f) ((client->server_status & f) ? Qtrue : Qfalse)
|
1645
|
+
|
1646
|
+
void rb_mysql_set_server_query_flags(MYSQL *client, VALUE result) {
|
1647
|
+
VALUE server_flags = rb_hash_new();
|
1648
|
+
|
1649
|
+
#ifdef HAVE_CONST_SERVER_QUERY_NO_GOOD_INDEX_USED
|
1650
|
+
rb_hash_aset(server_flags, sym_no_good_index_used, flag_to_bool(SERVER_QUERY_NO_GOOD_INDEX_USED));
|
1651
|
+
#else
|
1652
|
+
rb_hash_aset(server_flags, sym_no_good_index_used, Qnil);
|
1653
|
+
#endif
|
1654
|
+
|
1655
|
+
#ifdef HAVE_CONST_SERVER_QUERY_NO_INDEX_USED
|
1656
|
+
rb_hash_aset(server_flags, sym_no_index_used, flag_to_bool(SERVER_QUERY_NO_INDEX_USED));
|
1657
|
+
#else
|
1658
|
+
rb_hash_aset(server_flags, sym_no_index_used, Qnil);
|
1659
|
+
#endif
|
1660
|
+
|
1661
|
+
#ifdef HAVE_CONST_SERVER_QUERY_WAS_SLOW
|
1662
|
+
rb_hash_aset(server_flags, sym_query_was_slow, flag_to_bool(SERVER_QUERY_WAS_SLOW));
|
1663
|
+
#else
|
1664
|
+
rb_hash_aset(server_flags, sym_query_was_slow, Qnil);
|
1665
|
+
#endif
|
1666
|
+
|
1667
|
+
rb_iv_set(result, "@server_flags", server_flags);
|
1668
|
+
}
|