ruby-mysql-ext 2.9.7

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.
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