ruby-mysql-ext 2.9.7

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,68 @@
1
+ = ruby-mysql
2
+
3
+ == Description
4
+
5
+ MySQL connector for Ruby.
6
+
7
+ == Installation
8
+
9
+ ruby setup.rb
10
+
11
+ === Gem Installation
12
+
13
+ gem install ruby-mysql
14
+
15
+ == Features/Problems
16
+
17
+ * Ruby だけで書かれているのでコンパイル不要です。
18
+
19
+ * Ruby 1.9.x の M17N に対応しています。
20
+
21
+ * MySQL/Ruby 2.8.x とほぼ互換があります。
22
+
23
+ == Synopsis
24
+
25
+ 使用例:
26
+
27
+ my = Mysql.connect('hostname', 'username', 'password', 'dbname')
28
+ my.query("select col1, col2 from tblname").each do |col1, col2|
29
+ p col1, col2
30
+ end
31
+ stmt = my.prepare('insert into tblname (col1,col2) values (?,?)')
32
+ stmt.execute 123, 'abc'
33
+
34
+ == Incompatible with MySQL/Ruby 2.8.x
35
+
36
+ * Ruby 1.8.x ではシフトJISのような安全でないマルチバイト文字セットに対して Mysql#escape_string を使用すると例外が発生します。
37
+
38
+ * いくつかのメソッドがありません: Mysql#debug, Mysql#change_user,
39
+ Mysql#create_db, Mysql#drop_db, Mysql#dump_debug_info,
40
+ Mysql#ssl_set, Mysql#reconnect
41
+
42
+ * Mysql#options でサポートしているオプションは次のものだけです:
43
+ Mysql::INIT_COMMAND, Mysql::OPT_CONNECT_TIMEOUT,
44
+ Mysql::OPT_LOCAL_INFILE, Mysql::OPT_READ_TIMEOUT,
45
+ Mysql::OPT_WRITE_TIMEOUT, Mysql::SET_CHARSET_NAME.
46
+ これら以外を指定すると "option not implementted" という warning が標準エラー出力に出力されます。
47
+
48
+ * Mysql#use_result は Mysql#store_result と同じです。つまりサーバーから一気に結果セットを読み込みます。
49
+
50
+ == Improvement from MySQL/Ruby 2.8.x
51
+
52
+ * Ruby 1.9.x の M17N に対応しています。
53
+ mysqld へのクエリ文字列やプリペアドステートメントで与える値は mysqld との接続の文字コードに自動的に変換されます。
54
+ mysqld からの結果文字列は接続文字コードとして取り出されます。
55
+
56
+ * Mysql::Result, Mysql::Stmt が Enumerable を include しています。
57
+
58
+ * ブロックなしの Mysql::Result#each, each_hash Mysql::Stmt#each, each_hash が Enumerator を返します。
59
+
60
+ * Mysql#charset= で接続 charset を指定できます。
61
+
62
+ * Mysql::Error だけでなく、エラー種別ごとにエラークラスが用意されてます。たとえば、構文エラーの場合は Mysql::ServerError::ParseError など。これらのエラーは Mysql::Error の継承クラスです。
63
+
64
+ == Copyright
65
+
66
+ Author :: TOMITA Masahiro <tommy@tmtm.org>
67
+ Copyright :: Copyright (c) 2009-2012 TOMITA Masahiro
68
+ License :: Ruby's
@@ -0,0 +1,3 @@
1
+ require 'mkmf'
2
+
3
+ create_makefile 'mysql/packet'
@@ -0,0 +1,246 @@
1
+ #include "ruby.h"
2
+
3
+ typedef struct {
4
+ unsigned char *ptr;
5
+ unsigned char *endp;
6
+ } data_t;
7
+
8
+ static VALUE s_lcb(VALUE klass, VALUE val)
9
+ {
10
+ unsigned long long n;
11
+ unsigned char buf[9];
12
+
13
+ if (val == Qnil)
14
+ return rb_str_new("\xfb", 1);
15
+ n = NUM2ULL(val);
16
+ if (n < 251) {
17
+ buf[0] = n;
18
+ return rb_str_new(buf, 1);
19
+ }
20
+ if (n < 65536) {
21
+ buf[0] = '\xfc';
22
+ buf[1] = n % 256;
23
+ buf[2] = n / 256;
24
+ return rb_str_new(buf, 3);
25
+ }
26
+ if (n < 16777216) {
27
+ buf[0] = '\xfd';
28
+ buf[1] = n % 256;
29
+ n /= 256;
30
+ buf[2] = n % 256;
31
+ buf[3] = n / 256;
32
+ return rb_str_new(buf, 4);
33
+ }
34
+ buf[0] = '\xfe';
35
+ buf[1] = n % 256;
36
+ n /= 256;
37
+ buf[2] = n % 256;
38
+ n /= 256;
39
+ buf[3] = n % 256;
40
+ n /= 256;
41
+ buf[4] = n % 256;
42
+ n /= 256;
43
+ buf[5] = n % 256;
44
+ n /= 256;
45
+ buf[6] = n % 256;
46
+ n /= 256;
47
+ buf[7] = n % 256;
48
+ buf[8] = n / 256;
49
+ return rb_str_new(buf, 9);
50
+ }
51
+
52
+ static VALUE s_lcs(VALUE klass, VALUE val)
53
+ {
54
+ VALUE ret = s_lcb(klass, ULONG2NUM(RSTRING_LEN(val)));
55
+ return rb_str_cat(ret, RSTRING_PTR(val), RSTRING_LEN(val));
56
+ }
57
+
58
+ static VALUE allocate(VALUE klass)
59
+ {
60
+ data_t *data;
61
+
62
+ data = xmalloc(sizeof *data);
63
+ data->ptr = NULL;
64
+ data->endp = NULL;
65
+ return Data_Wrap_Struct(klass, 0, xfree, data);
66
+ }
67
+
68
+ static VALUE initialize(VALUE obj, VALUE buf)
69
+ {
70
+ data_t *data;
71
+
72
+ Data_Get_Struct(obj, data_t, data);
73
+ rb_ivar_set(obj, rb_intern("buf"), buf);
74
+ data->ptr = RSTRING_PTR(buf);
75
+ data->endp = data->ptr + RSTRING_LEN(buf);
76
+ }
77
+
78
+ #define NIL_VALUE 0xFFFFFFFFFFFFFFFF
79
+
80
+ static unsigned long long _lcb(data_t *data)
81
+ {
82
+ unsigned char v;
83
+ unsigned long long n;
84
+
85
+ if (data->ptr >= data->endp)
86
+ return NIL_VALUE;
87
+
88
+ v = *data->ptr++;
89
+ switch (v) {
90
+ case 0xfb:
91
+ return NIL_VALUE;
92
+ case 0xfc:
93
+ n = *data->ptr++;
94
+ n |= ((unsigned int)*data->ptr++) << 8;
95
+ return n;
96
+ case 0xfd:
97
+ n = *data->ptr++;
98
+ n |= ((unsigned int)*data->ptr++) << 8;
99
+ n |= ((unsigned int)*data->ptr++) << 16;
100
+ return n;
101
+ case 0xfe:
102
+ n = *data->ptr++;
103
+ n |= ((unsigned long long)*data->ptr++) << 8;
104
+ n |= ((unsigned long long)*data->ptr++) << 16;
105
+ n |= ((unsigned long long)*data->ptr++) << 24;
106
+ n |= ((unsigned long long)*data->ptr++) << 32;
107
+ n |= ((unsigned long long)*data->ptr++) << 40;
108
+ n |= ((unsigned long long)*data->ptr++) << 48;
109
+ n |= ((unsigned long long)*data->ptr++) << 56;
110
+ return n;
111
+ default:
112
+ return v;
113
+ }
114
+ }
115
+
116
+ static VALUE lcb(VALUE obj)
117
+ {
118
+ data_t *data;
119
+ unsigned char v;
120
+ unsigned long long n;
121
+
122
+ Data_Get_Struct(obj, data_t, data);
123
+ n = _lcb(data);
124
+ if (n == NIL_VALUE)
125
+ return Qnil;
126
+ return ULL2NUM(n);
127
+ }
128
+
129
+ static VALUE lcs(VALUE obj)
130
+ {
131
+ data_t *data;
132
+ unsigned long long l;
133
+ VALUE ret;
134
+
135
+ Data_Get_Struct(obj, data_t, data);
136
+ l = _lcb(data);
137
+ if (l == NIL_VALUE)
138
+ return Qnil;
139
+ if (data->ptr+l > data->endp)
140
+ l = data->endp - data->ptr;
141
+ ret = rb_str_new(data->ptr, l);
142
+ data->ptr += l;
143
+ return ret;
144
+ }
145
+
146
+ static VALUE read(VALUE obj, VALUE len)
147
+ {
148
+ data_t *data;
149
+ unsigned long long l = NUM2ULL(len);
150
+ VALUE ret;
151
+
152
+ Data_Get_Struct(obj, data_t, data);
153
+ if (data->ptr+l > data->endp)
154
+ l = data->endp - data->ptr;
155
+ ret = rb_str_new(data->ptr, l);
156
+ data->ptr += l;
157
+ return ret;
158
+ }
159
+
160
+ static VALUE string(VALUE obj)
161
+ {
162
+ data_t *data;
163
+ unsigned char *p;
164
+ VALUE ret;
165
+
166
+ Data_Get_Struct(obj, data_t, data);
167
+ p = data->ptr;
168
+ while (p < data->endp && *p++ != '\0')
169
+ ;
170
+ ret = rb_str_new(data->ptr, (p - data->ptr)-1);
171
+ data->ptr = p;
172
+ return ret;
173
+ }
174
+
175
+ static VALUE utiny(VALUE obj)
176
+ {
177
+ data_t *data;
178
+
179
+ Data_Get_Struct(obj, data_t, data);
180
+ return UINT2NUM(*data->ptr++);
181
+ }
182
+
183
+ static VALUE _ushort(VALUE obj)
184
+ {
185
+ data_t *data;
186
+ unsigned short n;
187
+
188
+ Data_Get_Struct(obj, data_t, data);
189
+ n = *data->ptr++;
190
+ n |= *data->ptr++ * 0x100;
191
+ return UINT2NUM(n);
192
+ }
193
+
194
+ static VALUE _ulong(VALUE obj)
195
+ {
196
+ data_t *data;
197
+ unsigned long n;
198
+
199
+ Data_Get_Struct(obj, data_t, data);
200
+ n = *data->ptr++;
201
+ n |= *data->ptr++ * 0x100;
202
+ n |= *data->ptr++ * 0x10000;
203
+ n |= *data->ptr++ * 0x1000000;
204
+ return UINT2NUM(n);
205
+ }
206
+
207
+ static VALUE eofQ(VALUE obj)
208
+ {
209
+ data_t *data;
210
+
211
+ Data_Get_Struct(obj, data_t, data);
212
+ if (*data->ptr == 0xfe && data->endp - data->ptr == 5)
213
+ return Qtrue;
214
+ else
215
+ return Qfalse;
216
+ }
217
+
218
+ static VALUE to_s(VALUE obj)
219
+ {
220
+ data_t *data;
221
+
222
+ Data_Get_Struct(obj, data_t, data);
223
+ return rb_str_new(data->ptr, data->endp-data->ptr);
224
+ }
225
+
226
+ void Init_packet(void)
227
+ {
228
+ VALUE cMysql;
229
+ VALUE cPacket;
230
+
231
+ cMysql = rb_define_class("Mysql", rb_cObject);
232
+ cPacket = rb_define_class_under(cMysql, "Packet", rb_cObject);
233
+ rb_define_alloc_func(cPacket, allocate);
234
+ rb_define_singleton_method(cPacket, "lcb", s_lcb, 1);
235
+ rb_define_singleton_method(cPacket, "lcs", s_lcs, 1);
236
+ rb_define_method(cPacket, "initialize", initialize, 1);
237
+ rb_define_method(cPacket, "lcb", lcb, 0);
238
+ rb_define_method(cPacket, "lcs", lcs, 0);
239
+ rb_define_method(cPacket, "read", read, 1);
240
+ rb_define_method(cPacket, "string", string, 0);
241
+ rb_define_method(cPacket, "utiny", utiny, 0);
242
+ rb_define_method(cPacket, "ushort", _ushort, 0);
243
+ rb_define_method(cPacket, "ulong", _ulong, 0);
244
+ rb_define_method(cPacket, "eof?", eofQ, 0);
245
+ rb_define_method(cPacket, "to_s", to_s, 0);
246
+ }
data/lib/mysql.rb ADDED
@@ -0,0 +1,1134 @@
1
+ # Copyright (C) 2008-2012 TOMITA Masahiro
2
+ # mailto:tommy@tmtm.org
3
+
4
+ # MySQL connection class.
5
+ # === Example
6
+ # my = Mysql.connect('hostname', 'user', 'password', 'dbname')
7
+ # res = my.query 'select col1,col2 from tbl where id=123'
8
+ # res.each do |c1, c2|
9
+ # p c1, c2
10
+ # end
11
+ class Mysql
12
+
13
+ require "mysql/constants"
14
+ require "mysql/error"
15
+ require "mysql/charset"
16
+ require "mysql/protocol"
17
+ begin
18
+ require "mysql/packet.so"
19
+ rescue LoadError
20
+ require "mysql/packet.rb"
21
+ end
22
+
23
+ VERSION = 20907 # Version number of this library
24
+ MYSQL_UNIX_PORT = "/tmp/mysql.sock" # UNIX domain socket filename
25
+ MYSQL_TCP_PORT = 3306 # TCP socket port number
26
+
27
+ attr_reader :charset # character set of MySQL connection
28
+ attr_reader :protocol # :nodoc:
29
+
30
+ attr_accessor :query_with_result
31
+
32
+ class << self
33
+ # Make Mysql object without connecting.
34
+ def init
35
+ my = self.allocate
36
+ my.instance_eval{initialize}
37
+ my
38
+ end
39
+
40
+ # Make Mysql object and connect to mysqld.
41
+ # Arguments are same as Mysql#connect.
42
+ def new(*args)
43
+ my = self.init
44
+ my.connect(*args)
45
+ end
46
+
47
+ alias real_connect new
48
+ alias connect new
49
+
50
+ # Escape special character in string.
51
+ # === Argument
52
+ # str :: [String]
53
+ def escape_string(str)
54
+ str.gsub(/[\0\n\r\\\'\"\x1a]/) do |s|
55
+ case s
56
+ when "\0" then "\\0"
57
+ when "\n" then "\\n"
58
+ when "\r" then "\\r"
59
+ when "\x1a" then "\\Z"
60
+ else "\\#{s}"
61
+ end
62
+ end
63
+ end
64
+ alias quote escape_string
65
+
66
+ # Return client version as String.
67
+ # This value is dummy.
68
+ def client_info
69
+ "5.0.0"
70
+ end
71
+ alias get_client_info client_info
72
+
73
+ # Return client version as Integer.
74
+ # This value is dummy. If you want to get version of this library, use Mysql::VERSION.
75
+ def client_version
76
+ 50000
77
+ end
78
+ alias get_client_version client_version
79
+ end
80
+
81
+ def initialize # :nodoc:
82
+ @fields = nil
83
+ @protocol = nil
84
+ @charset = nil
85
+ @connect_timeout = nil
86
+ @read_timeout = nil
87
+ @write_timeout = nil
88
+ @init_command = nil
89
+ @sqlstate = "00000"
90
+ @query_with_result = true
91
+ @host_info = nil
92
+ @last_error = nil
93
+ @result_exist = false
94
+ @local_infile = nil
95
+ end
96
+
97
+ # Connect to mysqld.
98
+ # === Argument
99
+ # host :: [String / nil] hostname mysqld running
100
+ # user :: [String / nil] username to connect to mysqld
101
+ # passwd :: [String / nil] password to connect to mysqld
102
+ # db :: [String / nil] initial database name
103
+ # port :: [Integer / nil] port number (used if host is not 'localhost' or nil)
104
+ # socket :: [String / nil] socket file name (used if host is 'localhost' or nil)
105
+ # flag :: [Integer / nil] connection flag. Mysql::CLIENT_* ORed
106
+ # === Return
107
+ # self
108
+ def connect(host=nil, user=nil, passwd=nil, db=nil, port=nil, socket=nil, flag=0)
109
+ if flag & CLIENT_COMPRESS != 0
110
+ warn 'unsupported flag: CLIENT_COMPRESS'
111
+ flag &= ~CLIENT_COMPRESS
112
+ end
113
+ @protocol = Protocol.new host, port, socket, @connect_timeout, @read_timeout, @write_timeout
114
+ @protocol.authenticate user, passwd, db, (@local_infile ? CLIENT_LOCAL_FILES : 0) | flag, @charset
115
+ @charset ||= @protocol.charset
116
+ @host_info = (host.nil? || host == "localhost") ? 'Localhost via UNIX socket' : "#{host} via TCP/IP"
117
+ query @init_command if @init_command
118
+ return self
119
+ end
120
+ alias real_connect connect
121
+
122
+ # Disconnect from mysql.
123
+ def close
124
+ if @protocol
125
+ @protocol.quit_command
126
+ @protocol = nil
127
+ end
128
+ return self
129
+ end
130
+
131
+ # Disconnect from mysql without QUIT packet.
132
+ def close!
133
+ if @protocol
134
+ @protocol.close
135
+ @protocol = nil
136
+ end
137
+ return self
138
+ end
139
+
140
+ # Set option for connection.
141
+ #
142
+ # Available options:
143
+ # Mysql::INIT_COMMAND, Mysql::OPT_CONNECT_TIMEOUT, Mysql::OPT_READ_TIMEOUT,
144
+ # Mysql::OPT_WRITE_TIMEOUT, Mysql::SET_CHARSET_NAME
145
+ # === Argument
146
+ # opt :: [Integer] option
147
+ # value :: option value that is depend on opt
148
+ # === Return
149
+ # self
150
+ def options(opt, value=nil)
151
+ case opt
152
+ when Mysql::INIT_COMMAND
153
+ @init_command = value.to_s
154
+ # when Mysql::OPT_COMPRESS
155
+ when Mysql::OPT_CONNECT_TIMEOUT
156
+ @connect_timeout = value
157
+ # when Mysql::GUESS_CONNECTION
158
+ when Mysql::OPT_LOCAL_INFILE
159
+ @local_infile = value
160
+ # when Mysql::OPT_NAMED_PIPE
161
+ # when Mysql::OPT_PROTOCOL
162
+ when Mysql::OPT_READ_TIMEOUT
163
+ @read_timeout = value.to_i
164
+ # when Mysql::OPT_RECONNECT
165
+ # when Mysql::SET_CLIENT_IP
166
+ # when Mysql::OPT_SSL_VERIFY_SERVER_CERT
167
+ # when Mysql::OPT_USE_EMBEDDED_CONNECTION
168
+ # when Mysql::OPT_USE_REMOTE_CONNECTION
169
+ when Mysql::OPT_WRITE_TIMEOUT
170
+ @write_timeout = value.to_i
171
+ # when Mysql::READ_DEFAULT_FILE
172
+ # when Mysql::READ_DEFAULT_GROUP
173
+ # when Mysql::REPORT_DATA_TRUNCATION
174
+ # when Mysql::SECURE_AUTH
175
+ # when Mysql::SET_CHARSET_DIR
176
+ when Mysql::SET_CHARSET_NAME
177
+ @charset = Charset.by_name value.to_s
178
+ # when Mysql::SHARED_MEMORY_BASE_NAME
179
+ else
180
+ warn "option not implemented: #{opt}"
181
+ end
182
+ self
183
+ end
184
+
185
+ # Escape special character in MySQL.
186
+ # === Note
187
+ # In Ruby 1.8, this is not safe for multibyte charset such as 'SJIS'.
188
+ # You should use place-holder in prepared-statement.
189
+ def escape_string(str)
190
+ if not defined? Encoding and @charset.unsafe
191
+ raise ClientError, 'Mysql#escape_string is called for unsafe multibyte charset'
192
+ end
193
+ self.class.escape_string str
194
+ end
195
+ alias quote escape_string
196
+
197
+ # === Return
198
+ # [String] client version
199
+ def client_info
200
+ self.class.client_info
201
+ end
202
+ alias get_client_info client_info
203
+
204
+ # === Return
205
+ # [Integer] client version
206
+ def client_version
207
+ self.class.client_version
208
+ end
209
+ alias get_client_version client_version
210
+
211
+ # Set charset of MySQL connection.
212
+ # === Argument
213
+ # cs :: [String / Mysql::Charset]
214
+ # === Return
215
+ # cs
216
+ def charset=(cs)
217
+ charset = cs.is_a?(Charset) ? cs : Charset.by_name(cs)
218
+ if @protocol
219
+ @protocol.charset = charset
220
+ query "SET NAMES #{charset.name}"
221
+ end
222
+ @charset = charset
223
+ cs
224
+ end
225
+
226
+ # === Return
227
+ # [String] charset name
228
+ def character_set_name
229
+ @charset.name
230
+ end
231
+
232
+ # === Return
233
+ # [Integer] last error number
234
+ def errno
235
+ @last_error ? @last_error.errno : 0
236
+ end
237
+
238
+ # === Return
239
+ # [String] last error message
240
+ def error
241
+ @last_error && @last_error.error
242
+ end
243
+
244
+ # === Return
245
+ # [String] sqlstate for last error
246
+ def sqlstate
247
+ @last_error ? @last_error.sqlstate : "00000"
248
+ end
249
+
250
+ # === Return
251
+ # [Integer] number of columns for last query
252
+ def field_count
253
+ @fields.size
254
+ end
255
+
256
+ # === Return
257
+ # [String] connection type
258
+ def host_info
259
+ @host_info
260
+ end
261
+ alias get_host_info host_info
262
+
263
+ # === Return
264
+ # [Integer] protocol version
265
+ def proto_info
266
+ Mysql::Protocol::VERSION
267
+ end
268
+ alias get_proto_info proto_info
269
+
270
+ # === Return
271
+ # [String] server version
272
+ def server_info
273
+ check_connection
274
+ @protocol.server_info
275
+ end
276
+ alias get_server_info server_info
277
+
278
+ # === Return
279
+ # [Integer] server version
280
+ def server_version
281
+ check_connection
282
+ @protocol.server_version
283
+ end
284
+ alias get_server_version server_version
285
+
286
+ # === Return
287
+ # [String] information for last query
288
+ def info
289
+ @protocol && @protocol.message
290
+ end
291
+
292
+ # === Return
293
+ # [Integer] number of affected records by insert/update/delete.
294
+ def affected_rows
295
+ @protocol ? @protocol.affected_rows : 0
296
+ end
297
+
298
+ # === Return
299
+ # [Integer] latest auto_increment value
300
+ def insert_id
301
+ @protocol ? @protocol.insert_id : 0
302
+ end
303
+
304
+ # === Return
305
+ # [Integer] number of warnings for previous query
306
+ def warning_count
307
+ @protocol ? @protocol.warning_count : 0
308
+ end
309
+
310
+ # Kill query.
311
+ # === Argument
312
+ # pid :: [Integer] thread id
313
+ # === Return
314
+ # self
315
+ def kill(pid)
316
+ check_connection
317
+ @protocol.kill_command pid
318
+ self
319
+ end
320
+
321
+ # Return database list.
322
+ # === Argument
323
+ # db :: [String] database name that may contain wild card.
324
+ # === Return
325
+ # [Array of String] database list
326
+ def list_dbs(db=nil)
327
+ db &&= db.gsub(/[\\\']/){"\\#{$&}"}
328
+ query(db ? "show databases like '#{db}'" : "show databases").map(&:first)
329
+ end
330
+
331
+ # Execute query string.
332
+ # === Argument
333
+ # str :: [String] Query.
334
+ # block :: If it is given then it is evaluated with Result object as argument.
335
+ # === Return
336
+ # Mysql::Result :: If result set exist.
337
+ # nil :: If the query does not return result set.
338
+ # self :: If block is specified.
339
+ # === Block parameter
340
+ # [Mysql::Result]
341
+ # === Example
342
+ # my.query("select 1,NULL,'abc'").fetch # => [1, nil, "abc"]
343
+ def query(str, &block)
344
+ check_connection
345
+ @fields = nil
346
+ begin
347
+ nfields = @protocol.query_command str
348
+ if nfields
349
+ @fields = @protocol.retr_fields nfields
350
+ @result_exist = true
351
+ end
352
+ if block
353
+ while true
354
+ block.call store_result if @fields
355
+ break unless next_result
356
+ end
357
+ return self
358
+ end
359
+ if @query_with_result
360
+ return @fields ? store_result : nil
361
+ else
362
+ return self
363
+ end
364
+ rescue ServerError => e
365
+ @last_error = e
366
+ @sqlstate = e.sqlstate
367
+ raise
368
+ end
369
+ end
370
+ alias real_query query
371
+
372
+ # Get all data for last query if query_with_result is false.
373
+ # === Return
374
+ # [Mysql::Result]
375
+ def store_result
376
+ check_connection
377
+ raise ClientError, 'invalid usage' unless @result_exist
378
+ res = Result.new @fields, @protocol
379
+ @result_exist = false
380
+ res
381
+ end
382
+
383
+ # Returns thread ID.
384
+ # === Return
385
+ # [Integer] Thread ID
386
+ def thread_id
387
+ check_connection
388
+ @protocol.thread_id
389
+ end
390
+
391
+ # Use result of query. The result data is retrieved when you use Mysql::Result#fetch_row.
392
+ def use_result
393
+ store_result
394
+ end
395
+
396
+ # Set server option.
397
+ # === Argument
398
+ # opt :: [Integer] Mysql::OPTION_MULTI_STATEMENTS_ON or Mysql::OPTION_MULTI_STATEMENTS_OFF
399
+ # === Return
400
+ # self
401
+ def set_server_option(opt)
402
+ check_connection
403
+ @protocol.set_option_command opt
404
+ self
405
+ end
406
+
407
+ # true if multiple queries are specified and unexecuted queries exists.
408
+ def more_results
409
+ @protocol.server_status & SERVER_MORE_RESULTS_EXISTS != 0
410
+ end
411
+ alias more_results? more_results
412
+
413
+ # execute next query if multiple queries are specified.
414
+ # === Return
415
+ # true if next query exists.
416
+ def next_result
417
+ return false unless more_results
418
+ check_connection
419
+ @fields = nil
420
+ nfields = @protocol.get_result
421
+ if nfields
422
+ @fields = @protocol.retr_fields nfields
423
+ @result_exist = true
424
+ end
425
+ return true
426
+ end
427
+
428
+ # Parse prepared-statement.
429
+ # === Argument
430
+ # str :: [String] query string
431
+ # === Return
432
+ # Mysql::Statement :: Prepared-statement object
433
+ def prepare(str)
434
+ st = Stmt.new @protocol, @charset
435
+ st.prepare str
436
+ st
437
+ end
438
+
439
+ # Make empty prepared-statement object.
440
+ # === Return
441
+ # Mysql::Stmt :: If block is not specified.
442
+ def stmt_init
443
+ Stmt.new @protocol, @charset
444
+ end
445
+
446
+ # Returns Mysql::Result object that is empty.
447
+ # Use fetch_fields to get list of fields.
448
+ # === Argument
449
+ # table :: [String] table name.
450
+ # field :: [String] field name that may contain wild card.
451
+ # === Return
452
+ # [Mysql::Result]
453
+ def list_fields(table, field=nil)
454
+ check_connection
455
+ begin
456
+ fields = @protocol.field_list_command table, field
457
+ return Result.new fields
458
+ rescue ServerError => e
459
+ @last_error = e
460
+ @sqlstate = e.sqlstate
461
+ raise
462
+ end
463
+ end
464
+
465
+ # Returns Mysql::Result object containing process list.
466
+ # === Return
467
+ # [Mysql::Result]
468
+ def list_processes
469
+ check_connection
470
+ @fields = @protocol.process_info_command
471
+ @result_exist = true
472
+ store_result
473
+ end
474
+
475
+ # Returns list of table name.
476
+ #
477
+ # NOTE for Ruby 1.8: This is not multi-byte safe. Don't use for
478
+ # multi-byte charset such as cp932.
479
+ # === Argument
480
+ # table :: [String] database name that may contain wild card.
481
+ # === Return
482
+ # [Array of String]
483
+ def list_tables(table=nil)
484
+ q = table ? "show tables like '#{quote table}'" : "show tables"
485
+ query(q).map(&:first)
486
+ end
487
+
488
+ # Check whether the connection is available.
489
+ # === Return
490
+ # self
491
+ def ping
492
+ check_connection
493
+ @protocol.ping_command
494
+ self
495
+ end
496
+
497
+ # Flush tables or caches.
498
+ # === Argument
499
+ # op :: [Integer] operation. Use Mysql::REFRESH_* value.
500
+ # === Return
501
+ # self
502
+ def refresh(op)
503
+ check_connection
504
+ @protocol.refresh_command op
505
+ self
506
+ end
507
+
508
+ # Reload grant tables.
509
+ # === Return
510
+ # self
511
+ def reload
512
+ refresh Mysql::REFRESH_GRANT
513
+ end
514
+
515
+ # Select default database
516
+ # === Return
517
+ # self
518
+ def select_db(db)
519
+ query "use #{db}"
520
+ self
521
+ end
522
+
523
+ # shutdown server.
524
+ # === Return
525
+ # self
526
+ def shutdown(level=0)
527
+ check_connection
528
+ @protocol.shutdown_command level
529
+ self
530
+ end
531
+
532
+ # === Return
533
+ # [String] statistics message
534
+ def stat
535
+ @protocol ? @protocol.statistics_command : 'MySQL server has gone away'
536
+ end
537
+
538
+ # Commit transaction
539
+ # === Return
540
+ # self
541
+ def commit
542
+ query 'commit'
543
+ self
544
+ end
545
+
546
+ # Rollback transaction
547
+ # === Return
548
+ # self
549
+ def rollback
550
+ query 'rollback'
551
+ self
552
+ end
553
+
554
+ # Set autocommit mode
555
+ # === Argument
556
+ # flag :: [true / false]
557
+ # === Return
558
+ # self
559
+ def autocommit(flag)
560
+ query "set autocommit=#{flag ? 1 : 0}"
561
+ self
562
+ end
563
+
564
+ private
565
+
566
+ def check_connection
567
+ raise ClientError::ServerGoneError, 'The MySQL server has gone away' unless @protocol
568
+ end
569
+
570
+ # Field class
571
+ class Field
572
+ attr_reader :db # database name
573
+ attr_reader :table # table name
574
+ attr_reader :org_table # original table name
575
+ attr_reader :name # field name
576
+ attr_reader :org_name # original field name
577
+ attr_reader :charsetnr # charset id number
578
+ attr_reader :length # field length
579
+ attr_reader :type # field type
580
+ attr_reader :flags # flag
581
+ attr_reader :decimals # number of decimals
582
+ attr_reader :default # defualt value
583
+ alias :def :default
584
+ attr_accessor :result # :nodoc:
585
+
586
+ # === Argument
587
+ # [Protocol::FieldPacket]
588
+ def initialize(packet)
589
+ @db, @table, @org_table, @name, @org_name, @charsetnr, @length, @type, @flags, @decimals, @default =
590
+ packet.db, packet.table, packet.org_table, packet.name, packet.org_name, packet.charsetnr, packet.length, packet.type, packet.flags, packet.decimals, packet.default
591
+ @flags |= NUM_FLAG if is_num_type?
592
+ @max_length = nil
593
+ end
594
+
595
+ def hash
596
+ {
597
+ "name" => @name,
598
+ "table" => @table,
599
+ "def" => @default,
600
+ "type" => @type,
601
+ "length" => @length,
602
+ "max_length" => max_length,
603
+ "flags" => @flags,
604
+ "decimals" => @decimals
605
+ }
606
+ end
607
+
608
+ def inspect
609
+ "#<Mysql::Field:#{@name}>"
610
+ end
611
+
612
+ # Return true if numeric field.
613
+ def is_num?
614
+ @flags & NUM_FLAG != 0
615
+ end
616
+
617
+ # Return true if not null field.
618
+ def is_not_null?
619
+ @flags & NOT_NULL_FLAG != 0
620
+ end
621
+
622
+ # Return true if primary key field.
623
+ def is_pri_key?
624
+ @flags & PRI_KEY_FLAG != 0
625
+ end
626
+
627
+ # maximum width of the field for the result set
628
+ def max_length
629
+ return @max_length if @max_length
630
+ @max_length = 0
631
+ @result.calculate_field_max_length if @result
632
+ @max_length
633
+ end
634
+
635
+ attr_writer :max_length
636
+
637
+ private
638
+
639
+ def is_num_type?
640
+ [TYPE_DECIMAL, TYPE_TINY, TYPE_SHORT, TYPE_LONG, TYPE_FLOAT, TYPE_DOUBLE, TYPE_LONGLONG, TYPE_INT24].include?(@type) || (@type == TYPE_TIMESTAMP && (@length == 14 || @length == 8))
641
+ end
642
+
643
+ end
644
+
645
+ # Result set
646
+ class ResultBase
647
+ include Enumerable
648
+
649
+ attr_reader :fields
650
+
651
+ # === Argument
652
+ # fields :: [Array of Mysql::Field]
653
+ def initialize(fields)
654
+ @fields = fields
655
+ @field_index = 0 # index of field
656
+ @records = [] # all records
657
+ @index = 0 # index of record
658
+ @fieldname_with_table = nil
659
+ @fetched_record = nil
660
+ end
661
+
662
+ # ignore
663
+ def free
664
+ end
665
+
666
+ # === Return
667
+ # [Integer] number of record
668
+ def size
669
+ @records.size
670
+ end
671
+ alias num_rows size
672
+
673
+ # Return current record.
674
+ # === Return
675
+ # [Array] record data
676
+ def fetch
677
+ @fetched_record = nil
678
+ return nil if @index >= @records.size
679
+ @records[@index] = @records[@index].to_a unless @records[@index].is_a? Array
680
+ @fetched_record = @records[@index]
681
+ @index += 1
682
+ return @fetched_record
683
+ end
684
+ alias fetch_row fetch
685
+
686
+ # Return data of current record as Hash.
687
+ # The hash key is field name.
688
+ # === Argument
689
+ # with_table :: if true, hash key is "table_name.field_name".
690
+ # === Return
691
+ # [Array of Hash] record data
692
+ def fetch_hash(with_table=nil)
693
+ row = fetch
694
+ return nil unless row
695
+ if with_table and @fieldname_with_table.nil?
696
+ @fieldname_with_table = @fields.map{|f| [f.table, f.name].join(".")}
697
+ end
698
+ ret = {}
699
+ @fields.each_index do |i|
700
+ fname = with_table ? @fieldname_with_table[i] : @fields[i].name
701
+ ret[fname] = row[i]
702
+ end
703
+ ret
704
+ end
705
+
706
+ # Iterate block with record.
707
+ # === Block parameter
708
+ # [Array] record data
709
+ # === Return
710
+ # self. If block is not specified, this returns Enumerator.
711
+ def each(&block)
712
+ return enum_for(:each) unless block
713
+ while rec = fetch
714
+ block.call rec
715
+ end
716
+ self
717
+ end
718
+
719
+ # Iterate block with record as Hash.
720
+ # === Argument
721
+ # with_table :: if true, hash key is "table_name.field_name".
722
+ # === Block parameter
723
+ # [Array of Hash] record data
724
+ # === Return
725
+ # self. If block is not specified, this returns Enumerator.
726
+ def each_hash(with_table=nil, &block)
727
+ return enum_for(:each_hash, with_table) unless block
728
+ while rec = fetch_hash(with_table)
729
+ block.call rec
730
+ end
731
+ self
732
+ end
733
+
734
+ # Set record position
735
+ # === Argument
736
+ # n :: [Integer] record index
737
+ # === Return
738
+ # self
739
+ def data_seek(n)
740
+ @index = n
741
+ self
742
+ end
743
+
744
+ # Return current record position
745
+ # === Return
746
+ # [Integer] record position
747
+ def row_tell
748
+ @index
749
+ end
750
+
751
+ # Set current position of record
752
+ # === Argument
753
+ # n :: [Integer] record index
754
+ # === Return
755
+ # [Integer] previous position
756
+ def row_seek(n)
757
+ ret = @index
758
+ @index = n
759
+ ret
760
+ end
761
+ end
762
+
763
+ # Result set for simple query
764
+ class Result < ResultBase
765
+ def initialize(fields, protocol=nil)
766
+ super fields
767
+ return unless protocol
768
+ @records = protocol.retr_all_records fields.size
769
+ fields.each{|f| f.result = self} # for calculating max_field
770
+ end
771
+
772
+ # calculate max_length of all fields
773
+ def calculate_field_max_length
774
+ max_length = Array.new(@fields.size, 0)
775
+ @records.each_with_index do |rec, i|
776
+ rec = @records[i] = rec.to_a if rec.is_a? RawRecord
777
+ max_length.each_index do |i|
778
+ max_length[i] = rec[i].length if rec[i] && rec[i].length > max_length[i]
779
+ end
780
+ end
781
+ max_length.each_with_index do |len, i|
782
+ @fields[i].max_length = len
783
+ end
784
+ end
785
+
786
+ # Return current field
787
+ # === Return
788
+ # [Mysql::Field] field object
789
+ def fetch_field
790
+ return nil if @field_index >= @fields.length
791
+ ret = @fields[@field_index]
792
+ @field_index += 1
793
+ ret
794
+ end
795
+
796
+ # Return current position of field
797
+ # === Return
798
+ # [Integer] field position
799
+ def field_tell
800
+ @field_index
801
+ end
802
+
803
+ # Set field position
804
+ # === Argument
805
+ # n :: [Integer] field index
806
+ # === Return
807
+ # [Integer] previous position
808
+ def field_seek(n)
809
+ ret = @field_index
810
+ @field_index = n
811
+ ret
812
+ end
813
+
814
+ # Return field
815
+ # === Argument
816
+ # n :: [Integer] field index
817
+ # === Return
818
+ # [Mysql::Field] field
819
+ def fetch_field_direct(n)
820
+ raise ClientError, "invalid argument: #{n}" if n < 0 or n >= @fields.length
821
+ @fields[n]
822
+ end
823
+
824
+ # Return all fields
825
+ # === Return
826
+ # [Array of Mysql::Field] all fields
827
+ def fetch_fields
828
+ @fields
829
+ end
830
+
831
+ # Return length of each fields
832
+ # === Return
833
+ # [Array of Integer] length of each fields
834
+ def fetch_lengths
835
+ return nil unless @fetched_record
836
+ @fetched_record.map{|c|c.nil? ? 0 : c.length}
837
+ end
838
+
839
+ # === Return
840
+ # [Integer] number of fields
841
+ def num_fields
842
+ @fields.size
843
+ end
844
+ end
845
+
846
+ # Result set for prepared statement
847
+ class StatementResult < ResultBase
848
+ def initialize(fields, protocol, charset)
849
+ super fields
850
+ @records = protocol.stmt_retr_all_records @fields, charset
851
+ end
852
+ end
853
+
854
+ # Prepared statement
855
+ class Stmt
856
+ include Enumerable
857
+
858
+ attr_reader :affected_rows, :insert_id, :server_status, :warning_count
859
+ attr_reader :param_count, :fields, :sqlstate
860
+
861
+ def self.finalizer(protocol, statement_id)
862
+ proc do
863
+ protocol.gc_stmt statement_id
864
+ end
865
+ end
866
+
867
+ def initialize(protocol, charset)
868
+ @protocol = protocol
869
+ @charset = charset
870
+ @statement_id = nil
871
+ @affected_rows = @insert_id = @server_status = @warning_count = 0
872
+ @sqlstate = "00000"
873
+ @param_count = nil
874
+ @bind_result = nil
875
+ end
876
+
877
+ # parse prepared-statement and return Mysql::Statement object
878
+ # === Argument
879
+ # str :: [String] query string
880
+ # === Return
881
+ # self
882
+ def prepare(str)
883
+ close
884
+ begin
885
+ @sqlstate = "00000"
886
+ @statement_id, @param_count, @fields = @protocol.stmt_prepare_command(str)
887
+ rescue ServerError => e
888
+ @last_error = e
889
+ @sqlstate = e.sqlstate
890
+ raise
891
+ end
892
+ ObjectSpace.define_finalizer(self, self.class.finalizer(@protocol, @statement_id))
893
+ self
894
+ end
895
+
896
+ # Execute prepared statement.
897
+ # === Argument
898
+ # values passed to query
899
+ # === Return
900
+ # self
901
+ def execute(*values)
902
+ raise ClientError, "not prepared" unless @param_count
903
+ raise ClientError, "parameter count mismatch" if values.length != @param_count
904
+ values = values.map{|v| @charset.convert v}
905
+ begin
906
+ @sqlstate = "00000"
907
+ nfields = @protocol.stmt_execute_command @statement_id, values
908
+ if nfields
909
+ @fields = @protocol.retr_fields nfields
910
+ @result = StatementResult.new @fields, @protocol, @charset
911
+ else
912
+ @affected_rows, @insert_id, @server_status, @warning_count, @info =
913
+ @protocol.affected_rows, @protocol.insert_id, @protocol.server_status, @protocol.warning_count, @protocol.message
914
+ end
915
+ return self
916
+ rescue ServerError => e
917
+ @last_error = e
918
+ @sqlstate = e.sqlstate
919
+ raise
920
+ end
921
+ end
922
+
923
+ # Close prepared statement
924
+ def close
925
+ ObjectSpace.undefine_finalizer(self)
926
+ @protocol.stmt_close_command @statement_id if @statement_id
927
+ @statement_id = nil
928
+ end
929
+
930
+ # Return current record
931
+ # === Return
932
+ # [Array] record data
933
+ def fetch
934
+ row = @result.fetch
935
+ return row unless @bind_result
936
+ row.zip(@bind_result).map do |col, type|
937
+ if col.nil?
938
+ nil
939
+ elsif [Numeric, Integer, Fixnum].include? type
940
+ col.to_i
941
+ elsif type == String
942
+ col.to_s
943
+ elsif type == Float && !col.is_a?(Float)
944
+ col.to_i.to_f
945
+ elsif type == Mysql::Time && !col.is_a?(Mysql::Time)
946
+ if col.to_s =~ /\A\d+\z/
947
+ i = col.to_s.to_i
948
+ if i < 100000000
949
+ y = i/10000
950
+ m = i/100%100
951
+ d = i%100
952
+ h, mm, s = 0
953
+ else
954
+ y = i/10000000000
955
+ m = i/100000000%100
956
+ d = i/1000000%100
957
+ h = i/10000%100
958
+ mm= i/100%100
959
+ s = i%100
960
+ end
961
+ if y < 70
962
+ y += 2000
963
+ elsif y < 100
964
+ y += 1900
965
+ end
966
+ Mysql::Time.new(y, m, d, h, mm, s)
967
+ else
968
+ Mysql::Time.new
969
+ end
970
+ else
971
+ col
972
+ end
973
+ end
974
+ end
975
+
976
+ # Return data of current record as Hash.
977
+ # The hash key is field name.
978
+ # === Argument
979
+ # with_table :: if true, hash key is "table_name.field_name".
980
+ # === Return
981
+ # [Array of Hash] record data
982
+ def fetch_hash(with_table=nil)
983
+ @result.fetch_hash with_table
984
+ end
985
+
986
+ # Set retrieve type of value
987
+ # === Argument
988
+ # [Numeric / Fixnum / Integer / Float / String / Mysql::Time / nil] value type
989
+ # === Return
990
+ # self
991
+ def bind_result(*args)
992
+ if @fields.length != args.length
993
+ raise ClientError, "bind_result: result value count(#{@fields.length}) != number of argument(#{args.length})"
994
+ end
995
+ args.each do |a|
996
+ raise TypeError unless [Numeric, Fixnum, Integer, Float, String, Mysql::Time, nil].include? a
997
+ end
998
+ @bind_result = args
999
+ self
1000
+ end
1001
+
1002
+ # Iterate block with record.
1003
+ # === Block parameter
1004
+ # [Array] record data
1005
+ # === Return
1006
+ # self. If block is not specified, this returns Enumerator.
1007
+ def each(&block)
1008
+ return enum_for(:each) unless block
1009
+ while rec = fetch
1010
+ block.call rec
1011
+ end
1012
+ self
1013
+ end
1014
+
1015
+ # Iterate block with record as Hash.
1016
+ # === Argument
1017
+ # with_table :: if true, hash key is "table_name.field_name".
1018
+ # === Block parameter
1019
+ # [Array of Hash] record data
1020
+ # === Return
1021
+ # self. If block is not specified, this returns Enumerator.
1022
+ def each_hash(with_table=nil, &block)
1023
+ return enum_for(:each_hash, with_table) unless block
1024
+ while rec = fetch_hash(with_table)
1025
+ block.call rec
1026
+ end
1027
+ self
1028
+ end
1029
+
1030
+ # === Return
1031
+ # [Integer] number of record
1032
+ def size
1033
+ @result.size
1034
+ end
1035
+ alias num_rows size
1036
+
1037
+ # Set record position
1038
+ # === Argument
1039
+ # n :: [Integer] record index
1040
+ # === Return
1041
+ # self
1042
+ def data_seek(n)
1043
+ @result.data_seek(n)
1044
+ end
1045
+
1046
+ # Return current record position
1047
+ # === Return
1048
+ # [Integer] record position
1049
+ def row_tell
1050
+ @result.row_tell
1051
+ end
1052
+
1053
+ # Set current position of record
1054
+ # === Argument
1055
+ # n :: [Integer] record index
1056
+ # === Return
1057
+ # [Integer] previous position
1058
+ def row_seek(n)
1059
+ @result.row_seek(n)
1060
+ end
1061
+
1062
+ # === Return
1063
+ # [Integer] number of columns for last query
1064
+ def field_count
1065
+ @fields.length
1066
+ end
1067
+
1068
+ # ignore
1069
+ def free_result
1070
+ end
1071
+
1072
+ # Returns Mysql::Result object that is empty.
1073
+ # Use fetch_fields to get list of fields.
1074
+ # === Return
1075
+ # [Mysql::Result]
1076
+ def result_metadata
1077
+ return nil if @fields.empty?
1078
+ Result.new @fields
1079
+ end
1080
+ end
1081
+
1082
+ class Time
1083
+ # === Argument
1084
+ # year :: [Integer] year
1085
+ # month :: [Integer] month
1086
+ # day :: [Integer] day
1087
+ # hour :: [Integer] hour
1088
+ # minute :: [Integer] minute
1089
+ # second :: [Integer] second
1090
+ # neg :: [true / false] negative flag
1091
+ def initialize(year=0, month=0, day=0, hour=0, minute=0, second=0, neg=false, second_part=0)
1092
+ @year, @month, @day, @hour, @minute, @second, @neg, @second_part =
1093
+ year.to_i, month.to_i, day.to_i, hour.to_i, minute.to_i, second.to_i, neg, second_part.to_i
1094
+ end
1095
+ attr_accessor :year, :month, :day, :hour, :minute, :second, :neg, :second_part
1096
+ alias mon month
1097
+ alias min minute
1098
+ alias sec second
1099
+
1100
+ def ==(other) # :nodoc:
1101
+ other.is_a?(Mysql::Time) &&
1102
+ @year == other.year && @month == other.month && @day == other.day &&
1103
+ @hour == other.hour && @minute == other.minute && @second == other.second &&
1104
+ @neg == neg && @second_part == other.second_part
1105
+ end
1106
+
1107
+ def eql?(other) # :nodoc:
1108
+ self == other
1109
+ end
1110
+
1111
+ # === Return
1112
+ # [String] "yyyy-mm-dd HH:MM:SS"
1113
+ def to_s
1114
+ if year == 0 and mon == 0 and day == 0
1115
+ h = neg ? hour * -1 : hour
1116
+ sprintf "%02d:%02d:%02d", h, min, sec
1117
+ else
1118
+ sprintf "%04d-%02d-%02d %02d:%02d:%02d", year, mon, day, hour, min, sec
1119
+ end
1120
+ end
1121
+
1122
+ # === Return
1123
+ # [Integer] yyyymmddHHMMSS
1124
+ def to_i
1125
+ sprintf("%04d%02d%02d%02d%02d%02d", year, mon, day, hour, min, sec).to_i
1126
+ end
1127
+
1128
+ def inspect # :nodoc:
1129
+ sprintf "#<#{self.class.name}:%04d-%02d-%02d %02d:%02d:%02d>", year, mon, day, hour, min, sec
1130
+ end
1131
+
1132
+ end
1133
+
1134
+ end