yakischloba-drizzle-ffi 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,42 @@
1
+ Currently a simple client-oriented hookup to libdrizzle with ffi, for querying drizzle and mysql servers.
2
+
3
+ Not really sure where I'm going with this. Ping me if you have input.
4
+
5
+ == Synopsis
6
+
7
+ === Normal synchronous query
8
+
9
+ irb(main):001:0> require 'rubygems'
10
+ => true
11
+ irb(main):002:0> require 'lib/drizzle'
12
+ => true
13
+ irb(main):003:0> c = Drizzle::Connection.new("127.0.0.1", "root", "password", nil, :DRIZZLE_CON_MYSQL)
14
+ => #<Native Pointer address=0x0>
15
+ irb(main):004:0> r = c.query("select now()")
16
+ => #<Drizzle::Result:0x12f4dc8 @rows=[["2009-05-21 07:41:26"]], @columns=[:"now()"], @affected_rows=0, @insert_id=0>
17
+ irb(main):005:0> r.each {|row| puts row[0] }
18
+ 2009-05-21 07:41:26
19
+ => [["2009-05-21 07:41:26"]]
20
+ irb(main):006:0> r.columns
21
+ => [:"now()"]
22
+
23
+ === Async query with EventMachine
24
+
25
+ irb(main):001:0> require 'rubygems'
26
+ => true
27
+ irb(main):002:0> require 'lib/drizzle'
28
+ r=> true
29
+ irb(main):003:0> require 'eventmachine'
30
+ => true
31
+ irb(main):004:0> c = Drizzle::Connection.new("127.0.0.1", "root", "password", "mysql", :DRIZZLE_CON_MYSQL)
32
+ => #<Native Pointer address=0x0>
33
+ irb(main):005:0> EM.run {
34
+ irb(main):006:1* c.em_query("show tables") do |result|
35
+ irb(main):007:2* p result.columns
36
+ irb(main):008:2> p result.rows
37
+ irb(main):009:2> end
38
+ irb(main):010:1> }
39
+
40
+ [:Tables_in_mysql]
41
+
42
+ [["columns_priv"], ["db"], ["func"], ["help_category"], ["help_keyword"], ["help_relation"], ["help_topic"], ["host"], ["proc"], ["procs_priv"], ["tables_priv"], ["time_zone"], ["time_zone_leap_second"], ["time_zone_name"], ["time_zone_transition"], ["time_zone_transition_type"], ["user"], ["user_info"]]
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ require 'rake' unless defined?(Rake)
2
+
3
+ task :default => [:test]
4
+
5
+ task :test do
6
+ require 'rubygems'
7
+ require 'lib/drizzle'
8
+ require 'bacon'
9
+ Bacon.summary_on_exit
10
+ load "tests/basic.rb"
11
+ end
data/drizzle.gemspec ADDED
@@ -0,0 +1,20 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "drizzle-ffi"
3
+ s.version = "0.0.2"
4
+ s.date = "2009-08-07"
5
+ s.authors = ["Jake Douglas"]
6
+ s.email = "jakecdouglas@gmail.com"
7
+ s.has_rdoc = false
8
+ s.add_dependency('ffi')
9
+ s.add_dependency('bacon')
10
+ s.summary = "libdrizzle ffi"
11
+ s.homepage = "http://www.github.com/yakischloba/libdrizzle-ruby-ffi"
12
+ s.description = "libdrizzle ffi"
13
+ s.files =
14
+ ["drizzle.gemspec",
15
+ "README.rdoc",
16
+ "Rakefile",
17
+ "lib/drizzle.rb",
18
+ "lib/drizzle/drizzle.rb",
19
+ "tests/basic.rb"]
20
+ end
@@ -0,0 +1,229 @@
1
+ module Drizzle
2
+ extend FFI::Library
3
+ ffi_lib "libdrizzle"
4
+
5
+ class DrizzleException < RuntimeError; end
6
+
7
+ enum :drizzle_return_t, [
8
+ :DRIZZLE_RETURN_OK,
9
+ :DRIZZLE_RETURN_IO_WAIT,
10
+ :DRIZZLE_RETURN_PAUSE,
11
+ :DRIZZLE_RETURN_ROW_BREAK,
12
+ :DRIZZLE_RETURN_MEMORY,
13
+ :DRIZZLE_RETURN_ERRNO,
14
+ :DRIZZLE_RETURN_INTERNAL_ERROR,
15
+ :DRIZZLE_RETURN_GETADDRINFO,
16
+ :DRIZZLE_RETURN_NOT_READY,
17
+ :DRIZZLE_RETURN_BAD_PACKET_NUMBER,
18
+ :DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET,
19
+ :DRIZZLE_RETURN_BAD_PACKET,
20
+ :DRIZZLE_RETURN_PROTOCOL_NOT_SUPPORTED,
21
+ :DRIZZLE_RETURN_UNEXPECTED_DATA,
22
+ :DRIZZLE_RETURN_NO_SCRAMBLE,
23
+ :DRIZZLE_RETURN_AUTH_FAILED,
24
+ :DRIZZLE_RETURN_NULL_SIZE,
25
+ :DRIZZLE_RETURN_ERROR_CODE,
26
+ :DRIZZLE_RETURN_TOO_MANY_COLUMNS,
27
+ :DRIZZLE_RETURN_ROW_END,
28
+ :DRIZZLE_RETURN_EOF,
29
+ :DRIZZLE_RETURN_COULD_NOT_CONNECT,
30
+ :DRIZZLE_RETURN_NO_ACTIVE_CONNECTIONS,
31
+ :DRIZZLE_RETURN_SERVER_GONE,
32
+ :DRIZZLE_RETURN_MAX
33
+ ]
34
+
35
+ enum :drizzle_con_options_t, [
36
+ :DRIZZLE_CON_NONE, 0,
37
+ :DRIZZLE_CON_ALLOCATED, (1 << 0),
38
+ :DRIZZLE_CON_MYSQL, (1 << 1),
39
+ :DRIZZLE_CON_RAW_PACKET, (1 << 2),
40
+ :DRIZZLE_CON_RAW_SCRAMBLE, (1 << 3),
41
+ :DRIZZLE_CON_READY, (1 << 4),
42
+ :DRIZZLE_CON_NO_RESULT_READ, (1 << 5)
43
+ ]
44
+
45
+ enum :drizzle_options_t, [
46
+ :DRIZZLE_NONE, 0,
47
+ :DRIZZLE_ALLOCATED, (1 << 0),
48
+ :DRIZZLE_NON_BLOCKING, (1 << 1)
49
+ ]
50
+
51
+ def self.options
52
+ enum_type(:drizzle_options_t)
53
+ end
54
+
55
+ def self.return_codes
56
+ enum_type(:drizzle_return_t)
57
+ end
58
+
59
+ def self.con_options
60
+ enum_type(:drizzle_con_options_t)
61
+ end
62
+
63
+ # Misc
64
+ attach_function :version, :drizzle_version, [], :string
65
+
66
+ # Drizzle objects
67
+ attach_function :create, :drizzle_create, [:pointer], :pointer
68
+ attach_function :free, :drizzle_free, [:pointer], :void
69
+ attach_function :error, :drizzle_error, [:pointer], :string
70
+ attach_function :add_options, :drizzle_add_options, [:pointer, :drizzle_options_t], :void
71
+ callback :event_watch_callback, [:pointer, :short, :pointer], :drizzle_return_t
72
+
73
+ attach_function :set_event_watch, :drizzle_set_event_watch, [:pointer, :event_watch_callback, :pointer], :void
74
+
75
+ # Connection objects
76
+ attach_function :con_create, :drizzle_con_create, [:pointer, :pointer], :pointer
77
+ attach_function :con_free, :drizzle_con_free, [:pointer], :void
78
+ attach_function :con_set_db, :drizzle_con_set_db, [:pointer, :string], :void
79
+ attach_function :con_set_auth, :drizzle_con_set_auth, [:pointer, :string, :string], :void
80
+ attach_function :con_add_options, :drizzle_con_add_options, [:pointer, :drizzle_con_options_t], :void
81
+ attach_function :con_status, :drizzle_con_status, [:pointer], :int
82
+ attach_function :con_fd, :drizzle_con_fd, [:pointer], :int
83
+ attach_function :con_clone, :drizzle_con_clone, [:pointer, :pointer, :pointer], :pointer
84
+ attach_function :con_set_revents, :drizzle_con_set_revents, [:pointer, :short], :void
85
+
86
+ # Querying
87
+ attach_function :query_str, :drizzle_query_str, [:pointer, :pointer, :string, :pointer], :pointer
88
+
89
+ # Results
90
+ attach_function :result_create, :drizzle_result_create, [:pointer, :pointer], :pointer
91
+ attach_function :result_buffer, :drizzle_result_buffer, [:pointer], :int
92
+ attach_function :result_free, :drizzle_result_free, [:pointer], :drizzle_return_t
93
+ attach_function :result_affected_rows, :drizzle_result_affected_rows, [:pointer], :uint64
94
+ attach_function :result_insert_id, :drizzle_result_insert_id, [:pointer], :uint64
95
+ attach_function :row_next, :drizzle_row_next, [:pointer], :pointer
96
+ attach_function :result_read, :drizzle_result_read, [:pointer, :pointer, :pointer], :pointer
97
+
98
+ # Columns
99
+ attach_function :column_next, :drizzle_column_next, [:pointer], :pointer
100
+ attach_function :column_name, :drizzle_column_name, [:pointer], :string
101
+
102
+ class Result
103
+ attr_reader :columns, :affected_rows, :insert_id, :rows
104
+
105
+ def initialize(ptr)
106
+ @columns, @rows = [], []
107
+
108
+ @insert_id = Drizzle.result_insert_id(ptr)
109
+ @affected_rows = Drizzle.result_affected_rows(ptr)
110
+
111
+ # Get columns
112
+ until (column = Drizzle.column_next(ptr)).null?
113
+ @columns << Drizzle.column_name(column).to_sym
114
+ end
115
+
116
+ # Get rows
117
+ until (row = Drizzle.row_next(ptr)).null?
118
+ @rows << row.get_array_of_string(0, @columns.size)
119
+ end
120
+
121
+ # Free the underlying buffers since we just copied it all to Ruby
122
+ Drizzle.result_free(ptr)
123
+ end
124
+
125
+ def each
126
+ @rows.each do |row|
127
+ yield row if block_given?
128
+ end
129
+ end
130
+ end
131
+
132
+ class Drizzleptr < FFI::AutoPointer
133
+ def self.release(ptr)
134
+ Drizzle.free(ptr)
135
+ end
136
+ end
137
+
138
+ class Connptr < FFI::AutoPointer
139
+ def self.release(ptr)
140
+ Drizzle.conn_free(ptr)
141
+ end
142
+ end
143
+
144
+ class Connection
145
+ attr_accessor :host, :user, :pass, :db, :opts, :fd
146
+
147
+ def initialize(host, user, pass, db=nil, opts=[], drizzle=nil)
148
+ opts = opts.is_a?(Array) ? opts : [opts]
149
+ @host, @user, @pass, @db, @opts = host, user, pass, db, opts
150
+ @from_pool = true if drizzle
151
+ @drizzle = drizzle || Drizzleptr.new(Drizzle.create(nil))
152
+ @conn = Connptr.new(Drizzle.con_create(@drizzle, nil))
153
+ Drizzle.con_add_options(@conn, opts.inject(0){|i,o| i | Drizzle.enum_value(o)} | Drizzle.enum_value(:DRIZZLE_CON_NO_RESULT_READ))
154
+ Drizzle.con_set_auth(@conn, @user, @pass)
155
+ Drizzle.con_set_db(@conn, @db) if @db
156
+ @retptr = FFI::MemoryPointer.new(:int)
157
+ end
158
+
159
+ # Indicates whether or not this connection was created using a drizzle_st object from somewhere else.
160
+ def from_pool?
161
+ @from_pool
162
+ end
163
+
164
+ # This executes a normal synchronous query. We simply call the async methods together.
165
+ def query(query, proc=nil, &blk)
166
+ proc ||= blk
167
+ async_query(query, proc)
168
+ async_result
169
+ end
170
+
171
+ # Sends off a query to the server. The return value is the file descriptor number of the socket used for this connection, for monitoring with an event loop etc.
172
+ def async_query(query, proc=nil, &blk)
173
+ proc ||= blk
174
+ Drizzle.query_str(@conn, nil, query, @retptr)
175
+ # Make sure it was successful
176
+ check_error
177
+ @callback = proc
178
+ # return fd to caller
179
+ @fd ||= Drizzle.con_fd(@conn)
180
+ end
181
+
182
+ # Do a blocking read for the result of an outstanding query. This results the Result object as well as fires a callback associated with it.
183
+ def async_result
184
+ # Do a partial blocking read into the the packet struct
185
+ result = Drizzle.result_read(@conn, nil, @retptr)
186
+
187
+ # See if the read was successful
188
+ check_error
189
+
190
+ # Buffer the result and check
191
+ ret = Drizzle.result_buffer(result)
192
+ if Drizzle.return_codes[ret] != :DRIZZLE_RETURN_OK
193
+ # Free the result struct if we fail.
194
+ Drizzle.result_free(result)
195
+ raise DrizzleException.new("Query failed: #{Drizzle.error(@drizzle)}")
196
+ end
197
+
198
+ # Fire and return
199
+ r = Result.new(result)
200
+ @callback.call(r) if @callback
201
+ @callback = nil
202
+ r
203
+ end
204
+
205
+ def em_query(query, proc=nil, &blk)
206
+ proc ||= blk
207
+ fd = async_query(query, proc)
208
+ EM.watch(fd, EMHandler, self) {|c| c.notify_readable = true}
209
+ end
210
+
211
+ def check_error
212
+ if Drizzle.return_codes[@retptr.get_int(0)] != :DRIZZLE_RETURN_OK
213
+ raise DrizzleException.new("Query failed: #{Drizzle.error(@drizzle)}")
214
+ end
215
+ end
216
+
217
+ end
218
+
219
+ module EMHandler
220
+ def initialize(conn)
221
+ @conn = conn
222
+ end
223
+ def notify_readable
224
+ detach
225
+ @conn.async_result
226
+ end
227
+ end
228
+
229
+ end
data/lib/drizzle.rb ADDED
@@ -0,0 +1,3 @@
1
+ $:.unshift File.expand_path(File.dirname(File.expand_path(__FILE__)))
2
+ require 'ffi'
3
+ require 'drizzle/drizzle'
data/tests/basic.rb ADDED
@@ -0,0 +1,104 @@
1
+ require 'set'
2
+ begin
3
+ require 'eventmachine'
4
+ rescue LoadError
5
+ end
6
+
7
+ HOST = "127.0.0.1"
8
+ USER = "root"
9
+ PASS = "password"
10
+
11
+ describe "Basic libdrizzle operation" do
12
+
13
+ it "perform a simple synchronous query on a MySQL server" do
14
+ c = Drizzle::Connection.new(HOST, USER, PASS, "information_schema", :DRIZZLE_CON_MYSQL)
15
+ schemas, tables = Set.new, Set.new
16
+ result = c.query("SELECT table_schema,table_name FROM tables")
17
+ result.class.should.equal Drizzle::Result
18
+ result.affected_rows.should.equal 0
19
+ result.insert_id.should.equal 0
20
+ result.columns.size.should.equal 2
21
+ result.columns.should.include :table_schema
22
+ result.columns.should.include :table_name
23
+
24
+ result.each do |row|
25
+ schemas << row[0]
26
+ tables << row[1]
27
+ end
28
+
29
+ schemas.should.include "information_schema"
30
+ tables.should.include "COLUMNS"
31
+ end
32
+
33
+ it "send and receive a query asynchronously" do
34
+ c = Drizzle::Connection.new(HOST, USER, PASS, "information_schema", :DRIZZLE_CON_MYSQL)
35
+ fd = c.async_query("SELECT table_schema,table_name FROM tables")
36
+
37
+ schemas, tables = Set.new, Set.new
38
+ result = c.async_result
39
+ result.class.should.equal Drizzle::Result
40
+ result.affected_rows.should.equal 0
41
+ result.insert_id.should.equal 0
42
+
43
+ result.columns.size.should.equal 2
44
+ result.columns.should.include :table_schema
45
+ result.columns.should.include :table_name
46
+
47
+ result.each do |row|
48
+ schemas << row[0]
49
+ tables << row[1]
50
+ end
51
+
52
+ schemas.should.include "information_schema"
53
+ tables.should.include "COLUMNS"
54
+ end
55
+
56
+ it "send and receive a query asynchronously using a callback" do
57
+ c = Drizzle::Connection.new(HOST, USER, PASS, "information_schema", :DRIZZLE_CON_MYSQL)
58
+ schemas, tables = Set.new, Set.new
59
+ columns = []
60
+ fd = c.async_query("SELECT table_schema,table_name FROM tables") do |result|
61
+ result.columns.each {|col| columns << col}
62
+ result.each do |row|
63
+ schemas << row[0]
64
+ tables << row[1]
65
+ end
66
+ end
67
+
68
+ schemas.should.be.empty
69
+ tables.should.be.empty
70
+ columns.should.be.empty
71
+
72
+ c.async_result
73
+
74
+ schemas.should.include "information_schema"
75
+ tables.should.include "COLUMNS"
76
+ columns.size.should.equal 2
77
+ columns.should.include :table_schema
78
+ columns.should.include :table_name
79
+ end
80
+
81
+ if defined?(EventMachine)
82
+ it "perform an async query with evented receive using EventMachine" do
83
+ schemas, tables = Set.new, Set.new
84
+ columns = []
85
+ EM.run {
86
+ c = Drizzle::Connection.new(HOST, USER, PASS, "information_schema", :DRIZZLE_CON_MYSQL)
87
+ c.em_query("SELECT table_schema,table_name FROM tables") do |result|
88
+ result.columns.each {|col| columns << col}
89
+ result.each do |row|
90
+ schemas << row[0]
91
+ tables << row[1]
92
+ end
93
+ EM.stop
94
+ end
95
+ }
96
+ schemas.should.include "information_schema"
97
+ tables.should.include "COLUMNS"
98
+ columns.size.should.equal 2
99
+ columns.should.include :table_schema
100
+ columns.should.include :table_name
101
+ end
102
+ end
103
+
104
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: yakischloba-drizzle-ffi
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Jake Douglas
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-08-07 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: ffi
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: bacon
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ description: libdrizzle ffi
36
+ email: jakecdouglas@gmail.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files: []
42
+
43
+ files:
44
+ - drizzle.gemspec
45
+ - README.rdoc
46
+ - Rakefile
47
+ - lib/drizzle.rb
48
+ - lib/drizzle/drizzle.rb
49
+ - tests/basic.rb
50
+ has_rdoc: false
51
+ homepage: http://www.github.com/yakischloba/libdrizzle-ruby-ffi
52
+ licenses:
53
+ post_install_message:
54
+ rdoc_options: []
55
+
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ version:
64
+ required_rubygems_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ version:
70
+ requirements: []
71
+
72
+ rubyforge_project:
73
+ rubygems_version: 1.3.5
74
+ signing_key:
75
+ specification_version: 2
76
+ summary: libdrizzle ffi
77
+ test_files: []
78
+