tmm1-em-mysql 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/em-mysql.gemspec +21 -0
  2. data/lib/em/mysql.rb +55 -29
  3. data/lib/sequel/async.rb +25 -21
  4. metadata +4 -3
@@ -0,0 +1,21 @@
1
+ spec = Gem::Specification.new do |s|
2
+ s.name = 'em-mysql'
3
+ s.version = '0.3.0'
4
+ s.date = '2009-06-23'
5
+ s.summary = 'Async MySQL client API for Ruby/EventMachine'
6
+ s.email = "em-mysql@tmm1.net"
7
+ s.homepage = "http://github.com/tmm1/em-mysql"
8
+ s.description = 'Async MySQL client API for Ruby/EventMachine'
9
+ s.has_rdoc = false
10
+ s.authors = ["Aman Gupta"]
11
+ s.add_dependency('eventmachine', '>= 0.12.8')
12
+
13
+ # git ls-files
14
+ s.files = %w[
15
+ README
16
+ em-mysql.gemspec
17
+ lib/em/mysql.rb
18
+ lib/sequel/async.rb
19
+ test.rb
20
+ ]
21
+ end
@@ -1,27 +1,31 @@
1
1
  require 'rubygems'
2
2
  require 'eventmachine'
3
3
  require 'mysqlplus'
4
+ require 'fcntl'
5
+
6
+ class Mysql
7
+ def result
8
+ @cur_result
9
+ end
10
+ end
4
11
 
5
12
  class EventedMysql < EM::Connection
6
13
  def initialize mysql, opts
7
14
  @mysql = mysql
8
15
  @fd = mysql.socket
9
16
  @opts = opts
10
- @queue = []
11
- @pending = []
17
+ @current = nil
18
+ @@queue ||= []
12
19
  @processing = false
13
20
  @connected = true
14
21
 
15
22
  log 'mysql connected'
23
+ make_socket_blocking
24
+ EM.add_timer(0){ next_query }
16
25
  end
17
26
  attr_reader :processing, :connected, :opts
18
27
  alias :settings :opts
19
28
 
20
- def connection_completed
21
- @connected = true
22
- next_query
23
- end
24
-
25
29
  DisconnectErrors = [
26
30
  'query: not connected',
27
31
  'MySQL server has gone away',
@@ -30,11 +34,14 @@ class EventedMysql < EM::Connection
30
34
 
31
35
  def notify_readable
32
36
  log 'readable'
33
- if item = @queue.shift
37
+ if item = @current
38
+ @current = nil
34
39
  start, response, sql, cblk, eblk = item
35
40
  log 'mysql response', Time.now-start, sql
36
41
  arg = case response
37
42
  when :raw
43
+ result = @mysql.get_result
44
+ @mysql.instance_variable_set('@cur_result', result)
38
45
  @mysql
39
46
  when :select
40
47
  ret = []
@@ -64,8 +71,12 @@ class EventedMysql < EM::Connection
64
71
  end
65
72
  rescue Mysql::Error => e
66
73
  log 'mysql error', e.message
67
- if DisconnectErrors.include? e.message
68
- @pending << [response, sql, cblk, eblk]
74
+ if e.message =~ /Deadlock/
75
+ @@queue << [response, sql, cblk, eblk]
76
+ @processing = false
77
+ next_query
78
+ elsif DisconnectErrors.include? e.message
79
+ @@queue << [response, sql, cblk, eblk]
69
80
  return close
70
81
  elsif cb = (eblk || @opts[:on_error])
71
82
  cb.call(e)
@@ -101,6 +112,9 @@ class EventedMysql < EM::Connection
101
112
  @signature = EM.attach_fd @mysql.socket, true, false
102
113
  log 'mysql connected'
103
114
  EM.instance_variable_get('@conns')[@signature] = self
115
+ @connected = true
116
+ make_socket_blocking
117
+ next_query
104
118
  end
105
119
  end
106
120
 
@@ -115,7 +129,7 @@ class EventedMysql < EM::Connection
115
129
  # # log 'mysql errno', @mysql.errno
116
130
  # rescue
117
131
  # log 'mysql ping failed'
118
- # @pending << [response, sql, blk]
132
+ # @@queue << [response, sql, blk]
119
133
  # return close
120
134
  # end
121
135
 
@@ -124,13 +138,13 @@ class EventedMysql < EM::Connection
124
138
  log 'mysql sending', sql
125
139
  @mysql.send_query(sql)
126
140
  else
127
- @pending << [response, sql, cblk, eblk]
141
+ @@queue << [response, sql, cblk, eblk]
128
142
  return
129
143
  end
130
144
  rescue Mysql::Error => e
131
145
  log 'mysql error', e.message
132
146
  if DisconnectErrors.include? e.message
133
- @pending << [response, sql, cblk, eblk]
147
+ @@queue << [response, sql, cblk, eblk]
134
148
  return close
135
149
  else
136
150
  raise e
@@ -138,7 +152,7 @@ class EventedMysql < EM::Connection
138
152
  end
139
153
 
140
154
  log 'queuing', response, sql
141
- @queue << [Time.now, response, sql, cblk, eblk]
155
+ @current = [Time.now, response, sql, cblk, eblk]
142
156
  end
143
157
 
144
158
  def close
@@ -148,13 +162,23 @@ class EventedMysql < EM::Connection
148
162
  # EM.add_timer(0){ close_connection }
149
163
  # close_connection
150
164
  fd = detach
165
+ @io.close if @io
166
+ @io = nil
151
167
  log 'detached fd', fd
152
168
  end
153
169
 
154
170
  private
155
171
 
172
+ def make_socket_blocking
173
+ if defined?(Fcntl::F_GETFL)
174
+ @io = IO.for_fd(@mysql.socket)
175
+ m = @io.fcntl(Fcntl::F_GETFL, 0)
176
+ @io.fcntl(Fcntl::F_SETFL, ~Fcntl::O_NONBLOCK & m)
177
+ end
178
+ end
179
+
156
180
  def next_query
157
- if @connected and !@processing and pending = @pending.shift
181
+ if @connected and !@processing and pending = @@queue.shift
158
182
  response, sql, cblk, eblk = pending
159
183
  execute(sql, response, cblk, eblk)
160
184
  end
@@ -205,9 +229,7 @@ class EventedMysql < EM::Connection
205
229
  # XXX multi results require multiple callbacks to parse
206
230
  # Mysql::CLIENT_MULTI_RESULTS +
207
231
  # Mysql::CLIENT_MULTI_STATEMENTS +
208
-
209
- # XXX this should check for opts[:compression]
210
- Mysql::CLIENT_COMPRESS
232
+ (opts[:compress] == false ? 0 : Mysql::CLIENT_COMPRESS)
211
233
  )
212
234
 
213
235
  # increase timeout so mysql server doesn't disconnect us
@@ -227,16 +249,6 @@ class EventedMysql < EM::Connection
227
249
  # get results for queries
228
250
  conn.query_with_result = true
229
251
 
230
- # if encoding = opts[:encoding] || opts[:charset]
231
- # conn.options(Mysql::SET_CHARSET_NAME, encoding) rescue nil
232
- # conn.query("set names '#{encoding}'")
233
- # conn.query("set character_set_connection = '#{encoding}'")
234
- # conn.query("set character_set_client = '#{encoding}'")
235
- # conn.query("set character_set_database = '#{encoding}'")
236
- # conn.query("set character_set_server = '#{encoding}'")
237
- # conn.query("set character_set_results = '#{encoding}'")
238
- # end
239
-
240
252
  conn
241
253
  rescue Mysql::Error => e
242
254
  if cb = opts[:on_error]
@@ -362,7 +374,7 @@ if __FILE__ == $0 and require 'em/spec'
362
374
  should 'have raw mode which yields the mysql object' do
363
375
  @mysql.execute('select 1+2 as num', :raw){ |mysql|
364
376
  mysql.should.is_a? Mysql
365
- mysql.get_result.all_hashes.should == [{'num' => '3'}]
377
+ mysql.result.all_hashes.should == [{'num' => '3'}]
366
378
  done
367
379
  }
368
380
  end
@@ -446,6 +458,20 @@ if __FILE__ == $0 and require 'em/spec'
446
458
  }
447
459
  end
448
460
 
461
+ should 'allow access to insert_id in raw mode' do
462
+ SQL.raw('insert into evented_mysql_test (num) values (20), (21), (22)'){ |mysql|
463
+ mysql.insert_id.should == 4
464
+ done
465
+ }
466
+ end
467
+
468
+ should 'allow access to affected_rows in raw mode' do
469
+ SQL.raw('update evented_mysql_test set num = num + 10'){ |mysql|
470
+ mysql.affected_rows.should == 6
471
+ done
472
+ }
473
+ end
474
+
449
475
  should 'fire error callback with exceptions' do
450
476
  SQL.settings.update :on_error => proc{ |e|
451
477
  e.class.should == Mysql::Error
@@ -1,14 +1,14 @@
1
1
  # async sequel extensions, for use with em-mysql
2
2
  #
3
3
  # require 'em/mysql'
4
- # DB = Sequel.connect(...)
5
- # ADB = EventedMysql.connect(..., :on_error => proc{|e| log 'error', e })
4
+ # DB = Sequel.connect(:adapter => 'mysql', :user => 'root', :database => 'test', ...)
5
+ # EventedMysql.settings.update(..., :on_error => proc{|e| log 'error', e })
6
6
  #
7
7
  # def log *args
8
8
  # p [Time.now, *args]
9
9
  # end
10
10
  #
11
- # DB[:table].where(:id < 100).async_update do |num_updated|
11
+ # DB[:table].where(:id < 100).async_update(:field => 'new value') do |num_updated|
12
12
  # log "done updating #{num_updated} rows"
13
13
  # end
14
14
  #
@@ -48,49 +48,52 @@
48
48
  module Sequel
49
49
  class Dataset
50
50
  def async_insert *args, &cb
51
- ADB.insert insert_sql(*args), &cb
51
+ EventedMysql.insert insert_sql(*args), &cb
52
+ nil
53
+ end
54
+
55
+ def async_insert_ignore *args, &cb
56
+ EventedMysql.insert insert_sql(*args).sub(/insert/i, 'INSERT IGNORE'), &cb
52
57
  nil
53
58
  end
54
59
 
55
60
  def async_update *args, &cb
56
- ADB.update update_sql(*args), &cb
61
+ EventedMysql.update update_sql(*args), &cb
57
62
  nil
58
63
  end
59
64
 
60
65
  def async_delete &cb
61
- ADB.execute delete_sql, &cb
66
+ EventedMysql.execute delete_sql, &cb
62
67
  nil
63
68
  end
64
69
 
65
70
  def async_multi_insert *args, &cb
66
- ADB.execute multi_insert_sql(*args).first, &cb
71
+ EventedMysql.execute multi_insert_sql(*args).first, &cb
67
72
  nil
68
73
  end
69
74
 
70
75
  def async_multi_insert_ignore *args, &cb
71
- ADB.execute multi_insert_sql(*args).first.sub(/insert/i, "INSERT IGNORE"), &cb
76
+ EventedMysql.execute multi_insert_sql(*args).first.sub(/insert/i, "INSERT IGNORE"), &cb
72
77
  nil
73
78
  end
74
79
 
75
- def async_each *args
76
- ADB.select(select_sql(*args)) do |rows|
80
+ def async_each
81
+ EventedMysql.select(select_sql) do |rows|
77
82
  rows.each{|r|
78
- r = transform_load(r) if @transform
79
- r = row_proc[r] if row_proc
80
- yield r
83
+ if row_proc = @row_proc
84
+ yield row_proc.call(r)
85
+ else
86
+ yield r
87
+ end
81
88
  }
82
89
  end
83
90
  nil
84
91
  end
85
92
 
86
93
  def async_all
87
- ADB.select(sql) do |rows|
88
- if row_proc or transform
89
- yield(rows.map{|r|
90
- r = transform_load(r) if @transform
91
- r = row_proc[r] if row_proc
92
- r
93
- })
94
+ EventedMysql.select(sql) do |rows|
95
+ if row_proc = @row_proc
96
+ yield(rows.map{|r| row_proc.call(r) })
94
97
  else
95
98
  yield(rows)
96
99
  end
@@ -102,7 +105,7 @@ module Sequel
102
105
  if options_overlap(COUNT_FROM_SELF_OPTS)
103
106
  from_self.async_count(&cb)
104
107
  else
105
- naked.async_each(STOCK_COUNT_OPTS){|r|
108
+ clone(STOCK_COUNT_OPTS).async_each{|r|
106
109
  yield r.values.first.to_i
107
110
  }
108
111
  end
@@ -124,6 +127,7 @@ module Sequel
124
127
 
125
128
  class << self
126
129
  [ :async_insert,
130
+ :async_insert_ignore,
127
131
  :async_multi_insert,
128
132
  :async_multi_insert_ignore,
129
133
  :async_each,
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tmm1-em-mysql
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Aman Gupta
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-02-18 00:00:00 -08:00
12
+ date: 2009-06-23 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -20,7 +20,7 @@ dependencies:
20
20
  requirements:
21
21
  - - ">="
22
22
  - !ruby/object:Gem::Version
23
- version: 0.12.4
23
+ version: 0.12.8
24
24
  version:
25
25
  description: Async MySQL client API for Ruby/EventMachine
26
26
  email: em-mysql@tmm1.net
@@ -32,6 +32,7 @@ extra_rdoc_files: []
32
32
 
33
33
  files:
34
34
  - README
35
+ - em-mysql.gemspec
35
36
  - lib/em/mysql.rb
36
37
  - lib/sequel/async.rb
37
38
  - test.rb