tmtm-ruby-mysql 3.0.0 → 3.0.1

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/ChangeLog CHANGED
@@ -1,3 +1,20 @@
1
- == 3.0.0 / 2009-03-22
1
+ 2009-07-12 TOMITA Masahiro <tommy@tmtm.org>
2
2
 
3
- * initial release for v3.0.
3
+ * version 3.0.1-alpha
4
+
5
+ 2009-07-10 TOMITA Masahiro <tommy@tmtm.org>
6
+
7
+ * lib/mysql/compat.rb: updated.
8
+
9
+ * lib/mysql.rb: Mysql#query without parameter no longer use prepared statement.
10
+ Mysql::Statement#execute returns Mysql::Result.
11
+ Mysql::Statement no longer have some methods: fetch_row(), fetch_hash(), each(), each_hash().
12
+ Mysql::Result#fetch returns value that converted to Ruby object.
13
+
14
+ 2009-04-12 TOMITA Masahiro <tommy@tmtm.org>
15
+
16
+ * for Ruby 1.8.6
17
+
18
+ 2009-03-22 TOMITA Masahiro <tommy@tmtm.org>
19
+
20
+ * Version 3.0.0-alpha
data/README CHANGED
@@ -3,7 +3,7 @@
3
3
  == Description
4
4
  MySQL connector for Ruby.
5
5
 
6
- ALPHA バージョンです。将来のバージョンで互換がない変更がされる可能性あります。
6
+ ALPHA バージョンです。将来のバージョンで互換がない変更がされる可能性があります。
7
7
 
8
8
  == Installation
9
9
 
@@ -18,13 +18,14 @@ ALPHA バージョンです。将来のバージョンで互換がない変更
18
18
  * Ruby だけで書かれているのでコンパイル不要です。
19
19
  * Ruby 1.9 の M17N に対応しています。
20
20
  * Ruby/MySQL 0.x, MySQL/Ruby 2.x とは互換がありません。
21
+ require "mysql/compat" すれば、MySQL/Ruby 2.x 用のプログラムもそこそこ動くかもしれません。
21
22
  * 英語ドキュメントがありません。
22
23
 
23
24
  == Synopsis
24
25
 
25
26
  使用例:
26
27
 
27
- Mysql.connect("mysql://username@password:hostname:3306/dbname") do |my|
28
+ Mysql.connect("mysql://username:password@hostname:3306/dbname") do |my|
28
29
  my.query("select col1, col2 from tblname").each do |col1, col2|
29
30
  p col1, col2
30
31
  end
data/lib/mysql/charset.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # Copyright (C) 2008 TOMITA Masahiro
2
2
  # mailto:tommy@tmtm.org
3
3
 
4
- require "mysql/error"
4
+ require "#{File.dirname __FILE__}/error"
5
5
 
6
6
  class Mysql
7
7
  class Charset
data/lib/mysql/compat.rb CHANGED
@@ -5,11 +5,20 @@
5
5
 
6
6
  class Mysql
7
7
  class << self
8
- alias connect new
9
- alias real_connect new
8
+
9
+ def connect(*args)
10
+ my = self.allocate
11
+ my.instance_eval{initialize}
12
+ my.connect(*args)
13
+ my
14
+ end
15
+ alias new connect
16
+ alias real_connect connect
10
17
 
11
18
  def init
12
- self.allocate
19
+ my = self.allocate
20
+ my.instance_eval{initialize}
21
+ my
13
22
  end
14
23
 
15
24
  def client_version
@@ -35,24 +44,31 @@ class Mysql
35
44
  alias quote escape_string
36
45
  end
37
46
 
38
- alias orig_initialize initialize
39
- alias stmt_init stmt
40
- alias query simple_query
47
+ attr_accessor :query_with_result, :reconnect
48
+
49
+ alias stmt_init statement
50
+ alias real_connect connect
51
+ alias initialize_orig initialize
41
52
 
42
53
  def initialize(*args)
43
- if args.first.is_a? Hash || defined?(URI) && args.first.is_a?(URI) || args.first =~ /\Amysql:\/\//
44
- orig_initialize *args
45
- else
46
- host, user, password, db, port, socket, flag = args
47
- orig_initialize :host=>host, :user=>user, :password=>password, :db=>db, :port=>port, :socket=>socket, :flag=>flag
48
- end
54
+ initialize_orig(*args)
55
+ @query_with_result = true
56
+ @reconnect = false
49
57
  end
50
58
 
51
- def connect(host, user, password, db, port, socket, flag)
52
- initialize :host=>host, :user=>user, :password=>password, :db=>db, :port=>port, :socket=>socket, :flag=>flag
53
- self
59
+ def query(str)
60
+ res = simple_query str
61
+ if res
62
+ res.each do |rec|
63
+ rec.map!{|v| v && v.to_s}
64
+ rec.each_index do |i|
65
+ @fields[i].max_length = [rec[i] ? rec[i].length : 0, @fields[i].max_length||0].max
66
+ end
67
+ end
68
+ res.data_seek 0
69
+ end
70
+ res
54
71
  end
55
- alias real_connect connect
56
72
 
57
73
  def client_version
58
74
  self.class.client_version
@@ -102,10 +118,6 @@ class Mysql
102
118
  self
103
119
  end
104
120
 
105
- def sqlstate
106
- @stream ? @stream.sqlstate : "00000"
107
- end
108
-
109
121
  def store_result
110
122
  raise ClientError, "no result set" unless @fields
111
123
  Result.new @fields, @stream
@@ -117,6 +129,12 @@ class Mysql
117
129
  end
118
130
 
119
131
  class Result
132
+ alias initialize_orig initialize
133
+ def initialize(*args)
134
+ initialize_orig *args
135
+ @field_index = 0
136
+ end
137
+
120
138
  def num_rows
121
139
  @records.length
122
140
  end
@@ -139,6 +157,11 @@ class Mysql
139
157
  # do nothing
140
158
  end
141
159
 
160
+ alias fetch_row_orig fetch_row
161
+ def fetch_row
162
+ @fetched_record = fetch_row_orig
163
+ end
164
+
142
165
  def fetch_field
143
166
  return nil if @field_index >= @fields.length
144
167
  ret = @fields[@field_index]
@@ -173,23 +196,54 @@ class Mysql
173
196
  end
174
197
  end
175
198
 
199
+ class Field
200
+ attr_accessor :max_length
201
+ def hash
202
+ {
203
+ "name" => @name,
204
+ "table" => @table,
205
+ "def" => @default,
206
+ "type" => @type,
207
+ "length" => @length,
208
+ "max_length" => @max_length,
209
+ "flags" => @flags,
210
+ "decimals" => @decimals
211
+ }
212
+ end
213
+ def inspect
214
+ "#<Mysql::Field:#{@name}>"
215
+ end
216
+ end
217
+
176
218
  class Statement
219
+ alias execute_orig execute
220
+ def execute(*args)
221
+ @res = execute_orig *args
222
+ end
223
+
224
+ def fetch
225
+ @res.fetch
226
+ end
227
+ alias fetch_row fetch
228
+
229
+ def each(*args, &block)
230
+ @res.each(*args, &block)
231
+ end
232
+
177
233
  def num_rows
178
- @records.length
234
+ @res.num_rows
179
235
  end
180
236
 
181
237
  def data_seek(n)
182
- @index = n
238
+ @res.data_seek(n)
183
239
  end
184
240
 
185
241
  def row_tell
186
- @index
242
+ @res.row_tell
187
243
  end
188
244
 
189
245
  def row_seek(n)
190
- ret = @index
191
- @index = n
192
- ret
246
+ @res.row_seek(n)
193
247
  end
194
248
 
195
249
  def field_count
@@ -202,7 +256,11 @@ class Mysql
202
256
 
203
257
  def result_metadata
204
258
  return nil if @fields.empty?
205
- Result.new @fields, nil, false
259
+ res = Result.allocate
260
+ res.instance_variable_set :@mysql, @mysql
261
+ res.instance_variable_set :@fields, @fields
262
+ res.instance_variable_set :@records, []
263
+ res
206
264
  end
207
265
  end
208
266
  Stmt = Statement
@@ -52,7 +52,7 @@ class Mysql
52
52
  lcb[0, 9] = ""
53
53
  return (v2 << 32) | v1
54
54
  else
55
- return ord! lcb
55
+ return ord!(lcb)
56
56
  end
57
57
  end
58
58
 
@@ -432,9 +432,9 @@ class Mysql
432
432
  affected_rows = Protocol.lcb2int! data
433
433
  insert_id = Protocol.lcb2int!(data)
434
434
  server_status, warning_count, message = data.unpack("vva*")
435
- return self.new field_count, affected_rows, insert_id, server_status, warning_count, message
435
+ return self.new(field_count, affected_rows, insert_id, server_status, warning_count, message)
436
436
  else
437
- return self.new field_count
437
+ return self.new(field_count)
438
438
  end
439
439
  end
440
440
 
@@ -457,7 +457,7 @@ class Mysql
457
457
  f0, charsetnr, length, type, flags, decimals, f1, data = data.unpack("CvVCvCva*")
458
458
  raise ProtocolError, "invalid packet: f1=#{f1}" unless f1 == 0
459
459
  default = Protocol.lcs2str! data
460
- return self.new db, table, org_table, name, org_name, charsetnr, length, type, flags, decimals, default
460
+ return self.new(db, table, org_table, name, org_name, charsetnr, length, type, flags, decimals, default)
461
461
  end
462
462
 
463
463
  attr_accessor :db, :table, :org_table, :name, :org_name, :charsetnr, :length, :type, :flags, :decimals, :default
data/lib/mysql.rb CHANGED
@@ -1,16 +1,26 @@
1
- # Copyright (C) 2008 TOMITA Masahiro
1
+ # Copyright (C) 2008-2009 TOMITA Masahiro
2
2
  # mailto:tommy@tmtm.org
3
3
 
4
- $LOAD_PATH.unshift File.dirname(__FILE__)
5
- require "mysql/constants"
6
- require "mysql/error"
7
- require "mysql/charset"
8
- require "mysql/protocol"
9
- require "mysql/cache"
10
-
4
+ require "enumerator"
5
+ require "uri"
6
+
7
+ # MySQL connection class.
8
+ # === Example
9
+ # Mysql.connect("mysql://user:password@hostname:port/dbname") do |my|
10
+ # res = my.query "select col1,col2 from tbl where id=?", 123
11
+ # res.each do |c1, c2|
12
+ # p c1, c2
13
+ # end
14
+ # end
11
15
  class Mysql
12
16
 
13
- VERSION = 30000 # Version number of this library
17
+ dir = File.dirname __FILE__
18
+ require "#{dir}/mysql/constants"
19
+ require "#{dir}/mysql/error"
20
+ require "#{dir}/mysql/charset"
21
+ require "#{dir}/mysql/protocol"
22
+
23
+ VERSION = 30001 # Version number of this library
14
24
  MYSQL_UNIX_PORT = "/tmp/mysql.sock" # UNIX domain socket filename
15
25
  MYSQL_TCP_PORT = 3306 # TCP socket port number
16
26
 
@@ -35,7 +45,6 @@ class Mysql
35
45
  # :report_data_truncation => x,
36
46
  # :reconnect => x,
37
47
  # :ssl_verify_server_cert => x,
38
- :prepared_statement_cache_size => Integer,
39
48
  } # :nodoc:
40
49
 
41
50
  OPT2FLAG = {
@@ -58,13 +67,14 @@ class Mysql
58
67
  attr_reader :warning_count #
59
68
  attr_reader :server_version #
60
69
  attr_reader :protocol #
70
+ attr_reader :sqlstate
61
71
 
62
72
  def self.new(*args, &block) # :nodoc:
63
73
  my = self.allocate
64
74
  my.instance_eval{initialize(*args)}
65
75
  return my unless block
66
76
  begin
67
- return block.call my
77
+ return block.call(my)
68
78
  ensure
69
79
  my.close
70
80
  end
@@ -74,11 +84,11 @@ class Mysql
74
84
  # The value that block returns if block is specified.
75
85
  # Otherwise this returns Mysql object.
76
86
  def self.connect(*args, &block)
77
- my = self.new *args
87
+ my = self.new(*args)
78
88
  my.connect
79
89
  return my unless block
80
90
  begin
81
- return block.call my
91
+ return block.call(my)
82
92
  ensure
83
93
  my.close
84
94
  end
@@ -120,13 +130,19 @@ class Mysql
120
130
  @init_command = nil
121
131
  @affected_rows = nil
122
132
  @server_version = nil
123
- @param, opt = conninfo *args
133
+ @sqlstate = "00000"
134
+ @param, opt = conninfo(*args)
124
135
  @connected = false
125
136
  set_option opt
126
137
  end
127
138
 
139
+ # :call-seq:
140
+ # connect(conninfo, opt={})
141
+ #
142
+ # connect to mysql server.
143
+ # arguments are same as new().
128
144
  def connect(*args)
129
- param, opt = conninfo *args
145
+ param, opt = conninfo(*args)
130
146
  set_option opt
131
147
  param = @param.merge param
132
148
  @protocol = Protocol.new param[:host], param[:port], param[:socket], @connect_timeout, @read_timeout, @write_timeout
@@ -145,11 +161,11 @@ class Mysql
145
161
  @protocol.send_packet auth_packet
146
162
  @protocol.read # skip OK packet
147
163
  end
148
- @stmt_cache = Cache.new(@prepared_statement_cache_size)
149
164
  simple_query @init_command if @init_command
150
165
  return self
151
166
  end
152
167
 
168
+ # disconnect from mysql.
153
169
  def close
154
170
  if @protocol
155
171
  @protocol.synchronize do
@@ -174,78 +190,82 @@ class Mysql
174
190
  end
175
191
 
176
192
  # Execute query string.
177
- # If str begin with "sel" or params is specified, then the query is executed as prepared-statement automatically.
178
- # So the values in result set are not only String.
193
+ # If params is specified, then the query is executed as prepared-statement automatically.
179
194
  # === Argument
180
195
  # str :: [String] Query.
181
196
  # params :: Parameters corresponding to place holder (`?') in str.
197
+ # block :: If it is given then it is evaluated with Result object as argument.
182
198
  # === Return
183
- # Mysql::Statement :: If result set exist when str begin with "sel".
184
- # Mysql::Result :: If result set exist when str does not begin with "sel".
185
- # nil :: If result set does not exist.
199
+ # Mysql::Result :: If result set exist.
200
+ # nil :: If the query does not return result set.
201
+ # self :: If block is specified.
202
+ # === Block parameter
203
+ # [ Mysql::Result ]
186
204
  # === Example
187
205
  # my.query("select 1,NULL,'abc'").fetch # => [1, nil, "abc"]
188
- def query(str, *params)
189
- if not params.empty? or str =~ /\A\s*sel/i
190
- st = @stmt_cache.get str do |s|
191
- prepare s
192
- end
193
- st.execute(*params)
194
- if st.fields.empty?
195
- @affected_rows = st.affected_rows
196
- @insert_id = st.insert_id
197
- @server_status = st.server_status
198
- @warning_count = st.warning_count
199
- return nil
200
- end
201
- return st
206
+ def query(str, *params, &block)
207
+ if params.empty?
208
+ res = simple_query(str, &block)
202
209
  else
203
- return simple_query(str)
210
+ res = prepare_query(str, *params, &block)
211
+ end
212
+ if res && block
213
+ yield res
214
+ return self
204
215
  end
216
+ return res
205
217
  end
206
218
 
207
- # Execute query string.
208
- # The values in result set are String even if it is numeric.
209
- # === Argument
210
- # str :: [String] query string
211
- # === Return
212
- # Mysql::Result :: If result set is exist.
213
- # nil :: If result set is not eixst.
214
- # === Example
215
- # my.simple_query("select 1,NULL,'abc'").fetch # => ["1", nil, "abc"]
216
- def simple_query(str, &block)
219
+ def simple_query(str) # :nodoc:
217
220
  @affected_rows = @insert_id = @server_status = @warning_count = 0
218
- @fields = nil
219
221
  @protocol.synchronize do
220
- @protocol.reset
221
- @protocol.send_packet Protocol::QueryPacket.new @charset.convert(str)
222
- res_packet = @protocol.read_result_packet
223
- if res_packet.field_count == 0
224
- @affected_rows, @insert_id, @server_status, @warning_conut =
225
- res_packet.affected_rows, res_packet.insert_id, res_packet.server_status, res_packet.warning_count
226
- else
227
- @fields = (1..res_packet.field_count).map{Field.new @protocol.read_field_packet}
228
- @protocol.read_eof_packet
229
- end
230
- if block
231
- yield Result.new(self, @fields)
232
- return self
222
+ begin
223
+ @protocol.reset
224
+ @protocol.send_packet Protocol::QueryPacket.new(@charset.convert(str))
225
+ res_packet = @protocol.read_result_packet
226
+ if res_packet.field_count == 0
227
+ @affected_rows, @insert_id, @server_status, @warning_conut =
228
+ res_packet.affected_rows, res_packet.insert_id, res_packet.server_status, res_packet.warning_count
229
+ return nil
230
+ else
231
+ @fields = (1..res_packet.field_count).map{Field.new @protocol.read_field_packet}
232
+ @protocol.read_eof_packet
233
+ return SimpleQueryResult.new self, @fields
234
+ end
235
+ rescue ServerError => e
236
+ @sqlstate = e.sqlstate
237
+ raise
233
238
  end
234
- return @fields && Result.new(self, @fields)
235
239
  end
236
240
  end
237
241
 
242
+ def prepare_query(str, *params) # :nodoc:
243
+ st = prepare(str)
244
+ res = st.execute(*params)
245
+ if st.fields.empty?
246
+ @affected_rows = st.affected_rows
247
+ @insert_id = st.insert_id
248
+ @server_status = st.server_status
249
+ @warning_count = st.warning_count
250
+ end
251
+ st.close
252
+ return res
253
+ end
254
+
238
255
  # Parse prepared-statement.
256
+ # If block is specified then prepared-statement is closed when exiting the block.
239
257
  # === Argument
240
- # str :: [String] query string
258
+ # str :: [String] query string
259
+ # block :: If it is given then it is evaluated with Mysql::Statement object as argument.
241
260
  # === Return
242
261
  # Mysql::Statement :: Prepared-statement object
262
+ # The block value if block is given.
243
263
  def prepare(str, &block)
244
264
  st = Statement.new self
245
265
  st.prepare str
246
266
  if block
247
267
  begin
248
- return block.call st
268
+ return block.call(st)
249
269
  ensure
250
270
  st.close
251
271
  end
@@ -285,7 +305,7 @@ class Mysql
285
305
  st = Statement.new self
286
306
  if block
287
307
  begin
288
- return block.call st
308
+ return block.call(st)
289
309
  ensure
290
310
  st.close
291
311
  end
@@ -330,9 +350,8 @@ class Mysql
330
350
  end
331
351
  else
332
352
  if args.first =~ /\Amysql:/
333
- require "uri" unless defined? URI
334
353
  uri = URI.parse args.first
335
- elsif defined? URI and args.first.is_a? URI
354
+ elsif args.first.is_a? URI
336
355
  uri = args.first
337
356
  else
338
357
  raise ArgumentError, "Invalid argument: #{args.first.inspect}"
@@ -370,8 +389,6 @@ class Mysql
370
389
  return param, opt
371
390
  end
372
391
 
373
- private
374
-
375
392
  def set_option(opt)
376
393
  opt.each do |k,v|
377
394
  raise ClientError, "unknown option: #{k.inspect}" unless OPTIONS.key? k
@@ -386,9 +403,9 @@ class Mysql
386
403
  @init_command = opt[:init_command] || @init_command
387
404
  @read_timeout = opt[:read_timeout] || @read_timeout
388
405
  @write_timeout = opt[:write_timeout] || @write_timeout
389
- @prepared_statement_cache_size = opt[:prepared_statement_cache_size] || @prepared_statement_cache_size || 10
390
406
  end
391
407
 
408
+ # Field class
392
409
  class Field
393
410
  attr_reader :db, :table, :org_table, :name, :org_name, :charsetnr, :length, :type, :flags, :decimals, :default
394
411
  alias :def :default
@@ -401,14 +418,17 @@ class Mysql
401
418
  @flags |= NUM_FLAG if is_num_type?
402
419
  end
403
420
 
421
+ # Return true if numeric field.
404
422
  def is_num?
405
423
  @flags & NUM_FLAG != 0
406
424
  end
407
425
 
426
+ # Return true if not null field.
408
427
  def is_not_null?
409
428
  @flags & NOT_NULL_FLAG != 0
410
429
  end
411
430
 
431
+ # Return true if primary key field.
412
432
  def is_pri_key?
413
433
  @flags & PRI_KEY_FLAG != 0
414
434
  end
@@ -421,26 +441,30 @@ class Mysql
421
441
 
422
442
  end
423
443
 
444
+ # Result set
424
445
  class Result
425
-
426
446
  include Enumerable
427
447
 
428
448
  attr_reader :fields
429
449
 
430
450
  def initialize(mysql, fields)
431
- @mysql = mysql
432
451
  @fields = fields
433
452
  @fieldname_with_table = nil
434
- @field_index = 0
435
- @records = recv_all_records mysql.protocol, @fields, mysql.charset
436
453
  @index = 0
454
+ @records = recv_all_records mysql.protocol, fields, mysql.charset
455
+ end
456
+
457
+ def size
458
+ @records.size
437
459
  end
438
460
 
439
461
  def fetch_row
462
+ return nil if @index >= @records.size
440
463
  rec = @records[@index]
441
- @index += 1 if @index < @records.length
464
+ @index += 1
442
465
  return rec
443
466
  end
467
+
444
468
  alias fetch fetch_row
445
469
 
446
470
  def fetch_hash(with_table=nil)
@@ -472,6 +496,10 @@ class Mysql
472
496
  end
473
497
  self
474
498
  end
499
+ end
500
+
501
+ # Result set for simple query
502
+ class SimpleQueryResult < Result
475
503
 
476
504
  private
477
505
 
@@ -482,59 +510,83 @@ class Mysql
482
510
  break if Protocol.eof_packet? data
483
511
  rec = fields.map do |f|
484
512
  v = Protocol.lcs2str! data
485
- v.nil? ? nil : f.flags & Field::BINARY_FLAG == 0 ? charset.force_encoding(v) : Charset.to_binary(v)
513
+ convert_str_to_ruby_value f, v, charset
486
514
  end
487
515
  ret.push rec
488
516
  end
489
517
  ret
490
518
  end
491
- end
492
519
 
493
- class Time
494
- def initialize(year=0, month=0, day=0, hour=0, minute=0, second=0, neg=false, second_part=0)
495
- @year, @month, @day, @hour, @minute, @second, @neg, @second_part =
496
- year.to_i, month.to_i, day.to_i, hour.to_i, minute.to_i, second.to_i, neg, second_part.to_i
520
+ def convert_str_to_ruby_value(field, value, charset)
521
+ return nil if value.nil?
522
+ case field.type
523
+ when Field::TYPE_BIT, Field::TYPE_DECIMAL, Field::TYPE_VARCHAR,
524
+ Field::TYPE_NEWDECIMAL, Field::TYPE_TINY_BLOB,
525
+ Field::TYPE_MEDIUM_BLOB, Field::TYPE_LONG_BLOB,
526
+ Field::TYPE_BLOB, Field::TYPE_VAR_STRING, Field::TYPE_STRING
527
+ field.flags & Field::BINARY_FLAG == 0 ? charset.force_encoding(value) : Charset.to_binary(value)
528
+ when Field::TYPE_TINY, Field::TYPE_SHORT, Field::TYPE_LONG,
529
+ Field::TYPE_LONGLONG, Field::TYPE_INT24, Field::TYPE_YEAR
530
+ value.to_i
531
+ when Field::TYPE_FLOAT, Field::TYPE_DOUBLE
532
+ value.to_f
533
+ when Field::TYPE_TIMESTAMP, Field::TYPE_DATE, Field::TYPE_DATETIME, Field::TYPE_NEWDATE
534
+ unless value =~ /\A(\d\d\d\d).(\d\d).(\d\d)(?:.(\d\d).(\d\d).(\d\d))?\z/
535
+ raise "unsupported format date type: #{value}"
536
+ end
537
+ Time.new($1, $2, $3, $4, $5, $6)
538
+ when Field::TYPE_TIME
539
+ unless value =~ /\A(-?)(\d+).(\d\d).(\d\d)?\z/
540
+ raise "unsupported format time type: #{value}"
541
+ end
542
+ Time.new(0, 0, 0, $2, $3, $4, $1=="-")
543
+ else
544
+ raise "unknown mysql type: #{field.type}"
545
+ end
497
546
  end
498
- attr_accessor :year, :month, :day, :hour, :minute, :second, :neg, :second_part
499
- alias mon month
500
- alias min minute
501
- alias sec second
547
+ end
502
548
 
503
- def ==(other)
504
- other.is_a?(Mysql::Time) &&
505
- @year == other.year && @month == other.month && @day == other.day &&
506
- @hour == other.hour && @minute == other.minute && @second == other.second &&
507
- @neg == neg && @second_part == other.second_part
508
- end
549
+ # Result set for prepared statement
550
+ class StatementResult < Result
509
551
 
510
- def eql?(other)
511
- self == other
512
- end
552
+ private
513
553
 
514
- def to_s
515
- if year == 0 and mon == 0 and day == 0
516
- sprintf "%02d:%02d:%02d", hour, min, sec
517
- else
518
- sprintf "%04d-%02d-%02d %02d:%02d:%02d", year, mon, day, hour, min, sec
554
+ def recv_all_records(protocol, fields, charset)
555
+ ret = []
556
+ while rec = parse_data(protocol.read, fields, charset)
557
+ ret.push rec
519
558
  end
559
+ ret
520
560
  end
521
561
 
562
+ def parse_data(data, fields, charset)
563
+ return nil if Protocol.eof_packet? data
564
+ data.slice!(0) # skip first byte
565
+ null_bit_map = data.slice!(0, (fields.length+7+2)/8).unpack("C*")
566
+ ret = (0...fields.length).map do |i|
567
+ if null_bit_map[(i+2)/8][(i+2)%8] == 1
568
+ nil
569
+ else
570
+ unsigned = fields[i].flags & Field::UNSIGNED_FLAG != 0
571
+ v = Protocol.net2value(data, fields[i].type, unsigned)
572
+ fields[i].flags & Field::BINARY_FLAG == 0 ? charset.force_encoding(v) : Charset.to_binary(v)
573
+ end
574
+ end
575
+ ret
576
+ end
522
577
  end
523
578
 
579
+ # Prepared statement
524
580
  class Statement
525
-
526
- include Enumerable
527
-
528
581
  attr_reader :affected_rows, :insert_id, :server_status, :warning_count
529
582
  attr_reader :param_count, :fields, :sqlstate
530
- attr_accessor :cursor_type
531
583
 
532
584
  def self.finalizer(protocol, statement_id)
533
585
  proc do
534
586
  Thread.new do
535
587
  protocol.synchronize do
536
588
  protocol.reset
537
- protocol.send_packet Protocol::StmtClosePacket.new statement_id
589
+ protocol.send_packet Protocol::StmtClosePacket.new(statement_id)
538
590
  end
539
591
  end
540
592
  end
@@ -545,9 +597,7 @@ class Mysql
545
597
  @protocol = mysql.protocol
546
598
  @statement_id = nil
547
599
  @affected_rows = @insert_id = @server_status = @warning_count = 0
548
- @eof = false
549
600
  @sqlstate = "00000"
550
- @cursor_type = CURSOR_TYPE_NO_CURSOR
551
601
  @param_count = nil
552
602
  end
553
603
 
@@ -562,7 +612,7 @@ class Mysql
562
612
  begin
563
613
  @sqlstate = "00000"
564
614
  @protocol.reset
565
- @protocol.send_packet Protocol::PreparePacket.new @mysql.charset.convert(str)
615
+ @protocol.send_packet Protocol::PreparePacket.new(@mysql.charset.convert(str))
566
616
  res_packet = @protocol.read_prepare_result_packet
567
617
  if res_packet.param_count > 0
568
618
  res_packet.param_count.times{@protocol.read} # skip parameter packet
@@ -586,6 +636,9 @@ class Mysql
586
636
  self
587
637
  end
588
638
 
639
+ # execute prepared-statement.
640
+ # === Return
641
+ # Mysql::Result
589
642
  def execute(*values)
590
643
  raise ClientError, "not prepared" unless @param_count
591
644
  raise ClientError, "parameter count mismatch" if values.length != @param_count
@@ -594,28 +647,18 @@ class Mysql
594
647
  begin
595
648
  @sqlstate = "00000"
596
649
  @protocol.reset
597
- cursor_type = @fields.empty? ? CURSOR_TYPE_NO_CURSOR : @cursor_type
598
- @protocol.send_packet Protocol::ExecutePacket.new @statement_id, cursor_type, values
650
+ @protocol.send_packet Protocol::ExecutePacket.new(@statement_id, CURSOR_TYPE_NO_CURSOR, values)
599
651
  res_packet = @protocol.read_result_packet
600
652
  raise ProtocolError, "invalid field_count" unless res_packet.field_count == @fields.length
601
653
  @fieldname_with_table = nil
602
654
  if res_packet.field_count == 0
603
655
  @affected_rows, @insert_id, @server_status, @warning_conut =
604
656
  res_packet.affected_rows, res_packet.insert_id, res_packet.server_status, res_packet.warning_count
605
- @records = nil
606
- else
607
- @fields = (1..res_packet.field_count).map{Field.new @protocol.read_field_packet}
608
- @protocol.read_eof_packet
609
- @eof = false
610
- @index = 0
611
- if @cursor_type == CURSOR_TYPE_NO_CURSOR
612
- @records = []
613
- while rec = parse_data(@protocol.read)
614
- @records.push rec
615
- end
616
- end
657
+ return nil
617
658
  end
618
- return self
659
+ @fields = (1..res_packet.field_count).map{Field.new @protocol.read_field_packet}
660
+ @protocol.read_eof_packet
661
+ return StatementResult.new(@mysql, @fields)
619
662
  rescue ServerError => e
620
663
  @sqlstate = e.sqlstate
621
664
  raise
@@ -623,86 +666,48 @@ class Mysql
623
666
  end
624
667
  end
625
668
 
626
- def fetch_row
627
- return nil if @fields.empty?
628
- if @records
629
- rec = @records[@index]
630
- @index += 1 if @index < @records.length
631
- return rec
632
- end
633
- return nil if @eof
669
+ def close
670
+ ObjectSpace.undefine_finalizer(self)
634
671
  @protocol.synchronize do
635
672
  @protocol.reset
636
- @protocol.send_packet Protocol::FetchPacket.new @statement_id, 1
637
- data = @protocol.read
638
- if Protocol.eof_packet? data
639
- @eof = true
640
- return nil
673
+ if @statement_id
674
+ @protocol.send_packet Protocol::StmtClosePacket.new(@statement_id)
675
+ @statement_id = nil
641
676
  end
642
- @protocol.read_eof_packet
643
- return parse_data data
644
- end
645
- end
646
- alias fetch fetch_row
647
-
648
- def fetch_hash(with_table=nil)
649
- row = fetch_row
650
- return nil unless row
651
- if with_table and @fieldname_with_table.nil?
652
- @fieldname_with_table = @fields.map{|f| [f.table, f.name].join(".")}
653
- end
654
- ret = {}
655
- @fields.each_index do |i|
656
- fname = with_table ? @fieldname_with_table[i] : @fields[i].name
657
- ret[fname] = row[i]
658
677
  end
659
- ret
660
678
  end
679
+ end
661
680
 
662
- def each(&block)
663
- return enum_for(:each) unless block
664
- while rec = fetch_row
665
- block.call rec
666
- end
667
- self
681
+ class Time
682
+ def initialize(year=0, month=0, day=0, hour=0, minute=0, second=0, neg=false, second_part=0)
683
+ @year, @month, @day, @hour, @minute, @second, @neg, @second_part =
684
+ year.to_i, month.to_i, day.to_i, hour.to_i, minute.to_i, second.to_i, neg, second_part.to_i
668
685
  end
686
+ attr_accessor :year, :month, :day, :hour, :minute, :second, :neg, :second_part
687
+ alias mon month
688
+ alias min minute
689
+ alias sec second
669
690
 
670
- def each_hash(with_table=nil, &block)
671
- return enum_for(:each_hash, with_table) unless block
672
- while rec = fetch_hash(with_table)
673
- block.call rec
674
- end
675
- self
691
+ def ==(other)
692
+ other.is_a?(Mysql::Time) &&
693
+ @year == other.year && @month == other.month && @day == other.day &&
694
+ @hour == other.hour && @minute == other.minute && @second == other.second &&
695
+ @neg == neg && @second_part == other.second_part
676
696
  end
677
697
 
678
- def close
679
- ObjectSpace.undefine_finalizer(self)
680
- @protocol.synchronize do
681
- @protocol.reset
682
- if @statement_id
683
- @protocol.send_packet Protocol::StmtClosePacket.new @statement_id
684
- @statement_id = nil
685
- end
686
- end
698
+ def eql?(other)
699
+ self == other
687
700
  end
688
701
 
689
- private
690
-
691
- def parse_data(data)
692
- return nil if Protocol.eof_packet? data
693
- data.slice!(0) # skip first byte
694
- null_bit_map = data.slice!(0, (@fields.length+7+2)/8).unpack("C*")
695
- ret = (0...@fields.length).map do |i|
696
- if null_bit_map[(i+2)/8][(i+2)%8] == 1
697
- nil
698
- else
699
- unsigned = @fields[i].flags & Field::UNSIGNED_FLAG != 0
700
- v = Protocol.net2value(data, @fields[i].type, unsigned)
701
- @fields[i].flags & Field::BINARY_FLAG == 0 ? @mysql.charset.force_encoding(v) : Charset.to_binary(v)
702
- end
702
+ def to_s
703
+ if year == 0 and mon == 0 and day == 0
704
+ h = neg ? hour * -1 : hour
705
+ sprintf "%02d:%02d:%02d", h, min, sec
706
+ else
707
+ sprintf "%04d-%02d-%02d %02d:%02d:%02d", year, mon, day, hour, min, sec
703
708
  end
704
- ret
705
709
  end
706
710
 
707
711
  end
712
+
708
713
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tmtm-ruby-mysql
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0
4
+ version: 3.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - tommy
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-03-22 00:00:00 -07:00
12
+ date: 2009-07-12 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -25,15 +25,12 @@ extra_rdoc_files:
25
25
  files:
26
26
  - README
27
27
  - ChangeLog
28
- - Rakefile
29
28
  - lib/mysql
30
29
  - lib/mysql/constants.rb
31
30
  - lib/mysql/compat.rb
32
31
  - lib/mysql/protocol.rb
33
- - lib/mysql/cache.rb
34
32
  - lib/mysql/charset.rb
35
33
  - lib/mysql/error.rb
36
- - lib/mysql.rb~
37
34
  - lib/mysql.rb
38
35
  has_rdoc: true
39
36
  homepage: http://github.com/tmtm/ruby-mysql
data/Rakefile DELETED
@@ -1,144 +0,0 @@
1
- require 'rubygems'
2
- require 'rake'
3
- require 'rake/clean'
4
- require 'rake/testtask'
5
- require 'rake/packagetask'
6
- require 'rake/gempackagetask'
7
- require 'rake/rdoctask'
8
- require 'rake/contrib/rubyforgepublisher'
9
- require 'rake/contrib/sshpublisher'
10
- require 'fileutils'
11
- require 'lib/mysql'
12
- include FileUtils
13
-
14
- NAME = "ruby-mysql"
15
- AUTHOR = "tommy"
16
- EMAIL = "tommy@tmtm.org"
17
- DESCRIPTION = "MySQL connector for Ruby"
18
- RUBYFORGE_PROJECT = "rubymysql"
19
- HOMEPATH = "http://github.com/tmtm/ruby-mysql"
20
- BIN_FILES = %w( )
21
-
22
- VERS = "3.0.0"
23
- REV = File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
24
- CLEAN.include ['**/.*.sw?', '*.gem', '.config']
25
- RDOC_OPTS = [
26
- '--title', "#{NAME} documentation",
27
- "--charset", "utf-8",
28
- "--opname", "index.html",
29
- "--line-numbers",
30
- "--main", "README",
31
- "--inline-source",
32
- ]
33
-
34
- task :default => [:test]
35
- task :package => [:clean]
36
-
37
- Rake::TestTask.new("test") do |t|
38
- t.libs << "test"
39
- t.pattern = "test/**/*_test.rb"
40
- t.verbose = true
41
- end
42
-
43
- spec = Gem::Specification.new do |s|
44
- s.name = NAME
45
- s.version = VERS
46
- s.platform = Gem::Platform::RUBY
47
- s.has_rdoc = true
48
- s.extra_rdoc_files = ["README", "ChangeLog"]
49
- s.rdoc_options += RDOC_OPTS + ['--exclude', '^(examples|extras)/']
50
- s.summary = DESCRIPTION
51
- s.description = DESCRIPTION
52
- s.author = AUTHOR
53
- s.email = EMAIL
54
- s.homepage = HOMEPATH
55
- s.executables = BIN_FILES
56
- s.rubyforge_project = RUBYFORGE_PROJECT
57
- s.bindir = "bin"
58
- s.require_path = "lib"
59
- #s.autorequire = ""
60
- s.test_files = Dir["test/*_test.rb"]
61
-
62
- #s.add_dependency('activesupport', '>=1.3.1')
63
- s.required_ruby_version = '>= 1.8.7'
64
-
65
- s.files = %w(README ChangeLog Rakefile) +
66
- Dir.glob("{bin,doc,test,lib,templates,generator,extras,website,script}/**/*") +
67
- Dir.glob("ext/**/*.{h,c,rb}") +
68
- Dir.glob("examples/**/*.rb") +
69
- Dir.glob("tools/*.rb") +
70
- Dir.glob("rails/*.rb")
71
-
72
- s.extensions = FileList["ext/**/extconf.rb"].to_a
73
- end
74
-
75
- Rake::GemPackageTask.new(spec) do |p|
76
- p.need_tar = true
77
- p.gem_spec = spec
78
- end
79
-
80
- task :install do
81
- name = "#{NAME}-#{VERS}.gem"
82
- sh %{rake package}
83
- sh %{sudo gem install pkg/#{name}}
84
- end
85
-
86
- task :uninstall => [:clean] do
87
- sh %{sudo gem uninstall #{NAME}}
88
- end
89
-
90
-
91
- Rake::RDocTask.new do |rdoc|
92
- rdoc.rdoc_dir = 'html'
93
- rdoc.options += RDOC_OPTS
94
- rdoc.template = "resh"
95
- #rdoc.template = "#{ENV['template']}.rb" if ENV['template']
96
- if ENV['DOC_FILES']
97
- rdoc.rdoc_files.include(ENV['DOC_FILES'].split(/,\s*/))
98
- else
99
- rdoc.rdoc_files.include('README', 'ChangeLog')
100
- rdoc.rdoc_files.include('lib/**/*.rb')
101
- rdoc.rdoc_files.include('ext/**/*.c')
102
- end
103
- end
104
-
105
- desc "Publish to RubyForge"
106
- task :rubyforge => [:rdoc, :package] do
107
- require 'rubyforge'
108
- Rake::RubyForgePublisher.new(RUBYFORGE_PROJECT, 'tommy').upload
109
- end
110
-
111
- desc 'Package and upload the release to rubyforge.'
112
- task :release => [:clean, :package] do |t|
113
- v = ENV["VERSION"] or abort "Must supply VERSION=x.y.z"
114
- abort "Versions don't match #{v} vs #{VERS}" unless v == VERS
115
- pkg = "pkg/#{NAME}-#{VERS}"
116
-
117
- require 'rubyforge'
118
- rf = RubyForge.new.configure
119
- puts "Logging in"
120
- rf.login
121
-
122
- c = rf.userconfig
123
- # c["release_notes"] = description if description
124
- # c["release_changes"] = changes if changes
125
- c["preformatted"] = true
126
-
127
- files = [
128
- "#{pkg}.tgz",
129
- "#{pkg}.gem"
130
- ].compact
131
-
132
- puts "Releasing #{NAME} v. #{VERS}"
133
- rf.add_release RUBYFORGE_PROJECT, NAME, VERS, *files
134
- end
135
-
136
- desc 'Show information about the gem.'
137
- task :debug_gem do
138
- puts spec.to_ruby
139
- end
140
-
141
- desc 'Update gem spec'
142
- task :gemspec do
143
- open("#{NAME}.gemspec", 'w').write spec.to_ruby
144
- end
data/lib/mysql/cache.rb DELETED
@@ -1,26 +0,0 @@
1
- # Copyright (C) 2008 TOMITA Masahiro
2
- # mailto:tommy@tmtm.org
3
-
4
- class Mysql
5
- class Cache
6
- def initialize(size)
7
- @size = size || 0
8
- @cache = {}
9
- @timestamp = {}
10
- end
11
- def get(key)
12
- if @size <= 0
13
- return yield key
14
- end
15
- if @cache.key? key
16
- @timestamp[key] = ::Time.now
17
- return @cache[key]
18
- end
19
- if @cache.size >= @size
20
- oldest_key = @timestamp.min_by{|k,v| v}.first
21
- @cache.delete oldest_key
22
- end
23
- @cache[key] = yield key
24
- end
25
- end
26
- end