yakischloba-drizzle-ffi 0.0.2

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,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
+