Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

fixed up with the new ffi method etc

  • Loading branch information...
commit 80bd8e11444283362933cf6cf03564536b41f415 1 parent 4842e60
@jakedouglas authored
Showing with 120 additions and 163 deletions.
  1. +40 −1 README.rdoc
  2. +0 −1  lib/drizzle.rb
  3. +59 −98 lib/drizzle/drizzle.rb
  4. +21 −63 tests/basic.rb
View
41 README.rdoc
@@ -1,3 +1,42 @@
-Currently a simple client-oriented hookup to libdrizzle with ffi, for querying mysql/drizzle servers.
+Currently a simple client-oriented hookup to libdrizzle with ffi, for querying drizzle and mysql servers.
Not really sure where I'm going with this. Ping me if you have input.
+
+== Synopsis
+
+=== Normal synchronous query
+
+ irb(main):001:0> require 'rubygems'
+ => true
+ irb(main):002:0> require 'lib/drizzle'
+ => true
+ irb(main):003:0> c = Drizzle::Connection.new("127.0.0.1", "root", "password", nil, :DRIZZLE_CON_MYSQL)
+ => #<Native Pointer address=0x0>
+ irb(main):004:0> r = c.query("select now()")
+ => #<Drizzle::Result:0x12f4dc8 @rows=[["2009-05-21 07:41:26"]], @columns=[:"now()"], @affected_rows=0, @insert_id=0>
+ irb(main):005:0> r.each {|row| puts row[0] }
+ 2009-05-21 07:41:26
+ => [["2009-05-21 07:41:26"]]
+ irb(main):006:0> r.columns
+ => [:"now()"]
+
+=== Async query with EventMachine
+
+ irb(main):001:0> require 'rubygems'
+ => true
+ irb(main):002:0> require 'lib/drizzle'
+ r=> true
+ irb(main):003:0> require 'eventmachine'
+ => true
+ irb(main):004:0> c = Drizzle::Connection.new("127.0.0.1", "root", "password", "mysql", :DRIZZLE_CON_MYSQL)
+ => #<Native Pointer address=0x0>
+ irb(main):005:0> EM.run {
+ irb(main):006:1* c.em_query("show tables") do |result|
+ irb(main):007:2* p result.columns
+ irb(main):008:2> p result.rows
+ irb(main):009:2> end
+ irb(main):010:1> }
+
+ [:Tables_in_mysql]
+
+ [["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"]]
View
1  lib/drizzle.rb
@@ -1,4 +1,3 @@
$:.unshift File.expand_path(File.dirname(File.expand_path(__FILE__)))
require 'ffi'
-#require 'ffi/tools/const_generator'
require 'drizzle/drizzle'
View
157 lib/drizzle/drizzle.rb
@@ -1,10 +1,11 @@
module Drizzle
extend FFI::Library
- ffi_lib "libdrizzle.dylib"
+ ffi_lib "libdrizzle"
class DrizzleException < RuntimeError; end
- enum :drizzle_return_t, [:DRIZZLE_RETURN_OK,
+ enum :drizzle_return_t, [
+ :DRIZZLE_RETURN_OK,
:DRIZZLE_RETURN_IO_WAIT,
:DRIZZLE_RETURN_PAUSE,
:DRIZZLE_RETURN_ROW_BREAK,
@@ -31,16 +32,6 @@ class DrizzleException < RuntimeError; end
:DRIZZLE_RETURN_MAX
]
- enum :drizzle_con_status_t, [
- :DRIZZLE_CON_NONE, 0,
- :DRIZZLE_CON_ALLOCATED, (1 << 0),
- :DRIZZLE_CON_MYSQL, (1 << 1),
- :DRIZZLE_CON_RAW_PACKET, (1 << 2),
- :DRIZZLE_CON_RAW_SCRAMBLE, (1 << 3),
- :DRIZZLE_CON_READY, (1 << 4),
- :DRIZZLE_CON_NO_RESULT_READ, (1 << 5)
- ]
-
enum :drizzle_con_options_t, [
:DRIZZLE_CON_NONE, 0,
:DRIZZLE_CON_ALLOCATED, (1 << 0),
@@ -57,14 +48,6 @@ class DrizzleException < RuntimeError; end
:DRIZZLE_NON_BLOCKING, (1 << 1)
]
- #consts = FFI::ConstGenerator.new do |c|
- # c.include 'poll.h'
- # c.const("POLLIN")
- # c.const("POLLOUT")
- #end
-
- #eval consts.to_ruby
-
def self.options
enum_type(:drizzle_options_t)
end
@@ -77,10 +60,6 @@ def self.con_options
enum_type(:drizzle_con_options_t)
end
- def self.con_status
- enum_type(:drizzle_con_status_t)
- end
-
# Misc
attach_function :version, :drizzle_version, [], :string
@@ -120,140 +99,122 @@ def self.con_status
attach_function :column_next, :drizzle_column_next, [:pointer], :pointer
attach_function :column_name, :drizzle_column_name, [:pointer], :string
- class Result < FFI::AutoPointer
- attr_reader :columns, :affected_rows, :insert_id
+ class Result
+ attr_reader :columns, :affected_rows, :insert_id, :rows
def initialize(ptr)
- super(ptr)
- @rows, @columns, @rowptrs = [], [], []
+ @columns, @rows = [], []
- while (!(column = Drizzle.column_next(self)).null?)
+ @insert_id = Drizzle.result_insert_id(ptr)
+ @affected_rows = Drizzle.result_affected_rows(ptr)
+
+ # Get columns
+ until (column = Drizzle.column_next(ptr)).null?
@columns << Drizzle.column_name(column).to_sym
end
- @insert_id = Drizzle.result_insert_id(self)
- @affected_rows = Drizzle.result_affected_rows(self)
+ # Get rows
+ until (row = Drizzle.row_next(ptr)).null?
+ @rows << row.get_array_of_string(0, @columns.size)
+ end
+
+ # Free the underlying buffers since we just copied it all to Ruby
+ Drizzle.result_free(ptr)
end
def each
- if @rows.empty?
- while (!(row = Drizzle.row_next(self)).null?)
- @rowptrs << row.read_array_of_pointer(@columns.size)
- end
- @rowptrs.each do |rowptr|
- row = []
- @columns.size.times do |i|
- row << (rowptr[i].null? ? "" : rowptr[i].get_string(0))
- end
- yield row if block_given?
- @rows << row
- end
- else
- @rows.each do |row|
- yield row if block_given?
- end
+ @rows.each do |row|
+ yield row if block_given?
end
end
-
- def self.release(obj)
- Drizzle.result_free(obj)
- end
-
end
class Connection < FFI::AutoPointer
- attr_accessor :max_cons
+ attr_accessor :host, :user, :pass, :db, :opts, :fd
- def initialize(host, user, pass, db=nil, proto=:DRIZZLE_CON_MYSQL)
- @host, @user, @pass, @db, @proto = host, user, pass, db, proto
- @drizzle = Drizzle.create(nil)
+ def initialize(host, user, pass, db=nil, opts=[], drizzle=nil)
+ opts = opts.is_a?(Array) ? opts : [opts]
+ @host, @user, @pass, @db, @opts = host, user, pass, db, opts
+ @from_pool = true if drizzle
+ @drizzle = drizzle || Drizzle.create(nil)
@conn = Drizzle.con_create(@drizzle, nil)
- @busy_cons = {}
- @ready_cons = []
- @max_cons = 20
- Drizzle.con_add_options(@conn, Drizzle.enum_value(proto) | Drizzle.enum_value(:DRIZZLE_CON_NO_RESULT_READ))
+ Drizzle.con_add_options(@conn, opts.inject(0){|i,o| i | Drizzle.enum_value(o)} | Drizzle.enum_value(:DRIZZLE_CON_NO_RESULT_READ))
Drizzle.con_set_auth(@conn, @user, @pass)
Drizzle.con_set_db(@conn, @db) if @db
@retptr = FFI::MemoryPointer.new(:int)
end
+ # Indicates whether or not this connection was created using a drizzle_st object from somewhere else.
+ def from_pool?
+ @from_pool
+ end
+
# This executes a normal synchronous query. We simply call the async methods together.
- def query(query)
- async_result(async_query(query))
+ def query(query, proc=nil, &blk)
+ proc ||= blk
+ async_query(query, proc)
+ async_result
end
+ # 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.
def async_query(query, proc=nil, &blk)
proc ||= blk
- # Give nil back if we would exceed our max_cons limit.
- return nil if @busy_cons.size >= @max_cons and @ready_cons.empty?
- # Get a connection or create one
- c = @ready_cons.pop || Drizzle.con_clone(@drizzle, nil, @conn)
- # Attempt to send off the query
- Drizzle.query_str(c, nil, query, @retptr)
+ Drizzle.query_str(@conn, nil, query, @retptr)
# Make sure it was successful
- handle_error
-
- # Get the fd, store the info, return fd to caller
- fd = Drizzle.con_fd(c)
- @busy_cons[fd] = {:callback => proc, :ptr => c}
- fd
+ check_error
+ @callback = proc
+ # return fd to caller
+ @fd ||= Drizzle.con_fd(@conn)
end
- def async_result(fd)
- return nil unless (h = @busy_cons[fd])
-
- # Create result packet struct
- result = Drizzle.result_create(@conn, nil)
-
- # Do a blocking read into the the packet
- Drizzle.result_read(h[:ptr], result, @retptr)
+ # 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.
+ def async_result
+ # Do a partial blocking read into the the packet struct
+ result = Drizzle.result_read(@conn, nil, @retptr)
# See if the read was successful
- handle_error
+ check_error
# Buffer the result and check
ret = Drizzle.result_buffer(result)
- if Drizzle.return_codes[@retptr.get_int(0)] != :DRIZZLE_RETURN_OK
+ if Drizzle.return_codes[ret] != :DRIZZLE_RETURN_OK
# Free the result struct if we fail.
Drizzle.result_free(result)
raise DrizzleException.new("Query failed: #{Drizzle.error(@drizzle)}")
end
+ # Fire and return
r = Result.new(result)
- h[:callback].call(r) if h[:callback]
- @busy_cons.delete(fd)
- @ready_cons << h[:ptr] unless (@ready_cons.size + @busy_cons.size) >= @max_cons
+ @callback.call(r) if @callback
+ @callback = nil
r
end
def em_query(query, proc=nil, &blk)
proc ||= blk
fd = async_query(query, proc)
- EM.attach(fd, EMHandler, self, fd)
- fd
+ EM.attach(fd, EMHandler, self)
end
- def handle_error
+ def check_error
if Drizzle.return_codes[@retptr.get_int(0)] != :DRIZZLE_RETURN_OK
raise DrizzleException.new("Query failed: #{Drizzle.error(@drizzle)}")
end
end
- def self.release(obj)
- obj.instance_variable_get("@ready_cons").each {|c| Drizzle.con_free(c)}
- obj.instance_variable_get("@busy_cons").each {|fd, h| Drizzle.con_free(h[:ptr])}
- Drizzle.con_free(obj.instance_variable_get("@conn"))
- Drizzle.free(obj.instance_variable_get("@drizzle"))
+ def self.release(conn)
+ Drizzle.con_free(conn.instance_variable_get("@conn"))
+ Drizzle.free(conn.instance_variable_get("@drizzle")) unless conn.from_pool?
end
end
module EMHandler
- def initialize(obj, fd)
- @obj, @fd = obj, fd
+ def initialize(conn)
+ @conn = conn
end
def notify_readable
detach
- @obj.async_result(@fd)
+ @conn.async_result
end
end
View
84 tests/basic.rb
@@ -11,50 +11,50 @@
describe "Basic libdrizzle operation" do
it "perform a simple synchronous query on a MySQL server" do
- c = Drizzle::Connection.new(HOST, USER, PASS, "information_schema")
+ c = Drizzle::Connection.new(HOST, USER, PASS, "information_schema", :DRIZZLE_CON_MYSQL)
+ schemas, tables = Set.new, Set.new
result = c.query("SELECT table_schema,table_name FROM tables")
result.class.should.equal Drizzle::Result
- schemas, tables = Set.new, Set.new
+ result.affected_rows.should.equal 0
+ result.insert_id.should.equal 0
+ result.columns.size.should.equal 2
+ result.columns.should.include :table_schema
+ result.columns.should.include :table_name
+
result.each do |row|
schemas << row[0]
tables << row[1]
end
+
schemas.should.include "information_schema"
tables.should.include "COLUMNS"
+ end
+
+ it "send and receive a query asynchronously" do
+ c = Drizzle::Connection.new(HOST, USER, PASS, "information_schema", :DRIZZLE_CON_MYSQL)
+ fd = c.async_query("SELECT table_schema,table_name FROM tables")
+ schemas, tables = Set.new, Set.new
+ result = c.async_result
+ result.class.should.equal Drizzle::Result
result.affected_rows.should.equal 0
result.insert_id.should.equal 0
result.columns.size.should.equal 2
result.columns.should.include :table_schema
result.columns.should.include :table_name
- end
- it "send and receive a query asynchronously" do
- c = Drizzle::Connection.new(HOST, USER, PASS, "information_schema")
- fd = c.async_query("SELECT table_schema,table_name FROM tables")
-
- result = c.async_result(fd)
- result.class.should.equal Drizzle::Result
-
- schemas, tables = Set.new, Set.new
result.each do |row|
schemas << row[0]
tables << row[1]
end
+
schemas.should.include "information_schema"
tables.should.include "COLUMNS"
-
- result.affected_rows.should.equal 0
- result.insert_id.should.equal 0
-
- result.columns.size.should.equal 2
- result.columns.should.include :table_schema
- result.columns.should.include :table_name
end
it "send and receive a query asynchronously using a callback" do
- c = Drizzle::Connection.new(HOST, USER, PASS, "information_schema")
+ c = Drizzle::Connection.new(HOST, USER, PASS, "information_schema", :DRIZZLE_CON_MYSQL)
schemas, tables = Set.new, Set.new
columns = []
fd = c.async_query("SELECT table_schema,table_name FROM tables") do |result|
@@ -69,7 +69,7 @@
tables.should.be.empty
columns.should.be.empty
- c.async_result(fd)
+ c.async_result
schemas.should.include "information_schema"
tables.should.include "COLUMNS"
@@ -83,7 +83,7 @@
schemas, tables = Set.new, Set.new
columns = []
EM.run {
- c = Drizzle::Connection.new(HOST, USER, PASS, "information_schema")
+ c = Drizzle::Connection.new(HOST, USER, PASS, "information_schema", :DRIZZLE_CON_MYSQL)
c.em_query("SELECT table_schema,table_name FROM tables") do |result|
result.columns.each {|col| columns << col}
result.each do |row|
@@ -101,46 +101,4 @@
end
end
- it "automatically build up and maintain the connection pool as needed" do
- c = Drizzle::Connection.new(HOST, USER, PASS, "information_schema")
- fds = []
- 5.times do
- fds << c.async_query("select now()")
- end
- c.instance_variable_get("@ready_cons").size.should.equal 0
- c.instance_variable_get("@busy_cons").size.should.equal 5
- fds.each do |fd|
- c.async_result(fd).class.should.equal Drizzle::Result
- end
- c.instance_variable_get("@ready_cons").size.should.equal 5
- c.instance_variable_get("@busy_cons").size.should.equal 0
- end
-
- it "connection pool should not allocate connections in excess of maximum connection limit" do
- c = Drizzle::Connection.new(HOST, USER, PASS, "information_schema")
- c.max_cons = 10
- fds = []
- 10.times do
- fds << c.async_query("select now()")
- end
-
- # Fill
- c.instance_variable_get("@ready_cons").size.should.equal 0
- c.instance_variable_get("@busy_cons").size.should.equal 10
-
- # Attempt to over-fill
- c.async_query("select now()").should.equal nil
-
- c.instance_variable_get("@ready_cons").size.should.equal 0
- c.instance_variable_get("@busy_cons").size.should.equal 10
-
- # Drain
- fds.each do |fd|
- c.async_result(fd).class.should.equal Drizzle::Result
- end
-
- c.instance_variable_get("@ready_cons").size.should.equal 10
- c.instance_variable_get("@busy_cons").size.should.equal 0
- end
-
end
Please sign in to comment.
Something went wrong with that request. Please try again.