drizzle 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Padraig O'Sullivan
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,7 @@
1
+ = drizzle-ruby
2
+
3
+ An interface to Drizzle using FFI and libdrizzle.
4
+
5
+ == Copyright
6
+
7
+ Copyright (c) 2010 Padraig O'Sullivan. See LICENSE for details.
@@ -0,0 +1,58 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+ require 'rake/rdoctask'
5
+
6
+ begin
7
+ require 'jeweler'
8
+ Jeweler::Tasks.new do |gem|
9
+ gem.name = "drizzle"
10
+ gem.summary = "A ruby interface to Drizzle using libdrizzle"
11
+ gem.description = "An interface to Drizzle for Ruby that uses libdrizzle"
12
+ gem.email = "osullivan.padraig@gmail.com"
13
+ gem.homepage = "http://github.com/posulliv/drizzle-ruby"
14
+ gem.authors = ["Padraig O'Sullivan"]
15
+ gem.rubyforge_project = "drizzle-ruby"
16
+ gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
17
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
18
+ end
19
+ Jeweler::GemcutterTasks.new
20
+ Jeweler::RubyforgeTasks.new do |rubyforge|
21
+ rubyforge.doc_task = "rdoc"
22
+ end
23
+ rescue LoadError
24
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
25
+ exit(1)
26
+ end
27
+
28
+ Rake::TestTask.new(:test) do |test|
29
+ test.libs << 'lib' << 'test'
30
+ test.pattern = 'test/**/test_*.rb'
31
+ test.verbose = true
32
+ end
33
+
34
+ begin
35
+ require 'rcov/rcovtask'
36
+ Rcov::RcovTask.new do |test|
37
+ test.libs << 'test'
38
+ test.pattern = 'test/**/test_*.rb'
39
+ test.verbose = true
40
+ end
41
+ rescue LoadError
42
+ task :rcov do
43
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
44
+ end
45
+ end
46
+
47
+ task :test => :check_dependencies
48
+
49
+ task :default => :test
50
+
51
+ Rake::RDocTask.new do |rdoc|
52
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
53
+
54
+ rdoc.rdoc_dir = 'rdoc'
55
+ rdoc.title = "drizzle #{version}"
56
+ rdoc.rdoc_files.include('README*')
57
+ rdoc.rdoc_files.include('lib/**/*.rb')
58
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,15 @@
1
+ # rubygems
2
+ require 'rubygems'
3
+
4
+ # 3rd party
5
+ require 'ffi'
6
+
7
+ # internal requires
8
+ require 'drizzle/ffidrizzle'
9
+ require 'drizzle/drizzle'
10
+ require 'drizzle/result'
11
+ require 'drizzle/connection'
12
+ require 'drizzle/exceptions'
13
+
14
+ module Drizzle
15
+ end
@@ -0,0 +1,207 @@
1
+ require 'drizzle/ffidrizzle'
2
+ require 'drizzle/exceptions'
3
+
4
+ module Drizzle
5
+
6
+ class ConnectionPtr < FFI::AutoPointer
7
+ def self.release(ptr)
8
+ LibDrizzle.drizzle_con_free(ptr)
9
+ end
10
+ end
11
+
12
+ #
13
+ # map of keywords that will be used with the SQL injection
14
+ # prevention plugin in drizzle
15
+ #
16
+ Keywords =
17
+ {
18
+ "add" => true,
19
+ "all" => true,
20
+ "alter" => true,
21
+ "analyze" => true,
22
+ "and" => true,
23
+ "any" => true,
24
+ "as" => true,
25
+ "asc" => true,
26
+ "before" => true,
27
+ "between" => true,
28
+ "by" => true,
29
+ "count" => true,
30
+ "distinct" => true,
31
+ "drop" => true,
32
+ "from" => true,
33
+ "having" => true,
34
+ "or" => true,
35
+ "select" => true,
36
+ "union" => true,
37
+ "where" => true
38
+ }
39
+
40
+
41
+ #
42
+ # connection options
43
+ #
44
+ NONE = 0
45
+ ALLOCATED = 1
46
+ MYSQL = 2
47
+ RAW_PACKET = 4
48
+ RAW_SCRAMBLE = 8
49
+ READY = 16
50
+ NO_RESULT_READ = 32
51
+ INJECTION_PREVENTION = 64
52
+
53
+ #
54
+ # A drizzle connection
55
+ #
56
+ class Connection
57
+
58
+ attr_accessor :host, :port, :db
59
+
60
+ #
61
+ # Creates a connection instance
62
+ #
63
+ # == parameters
64
+ #
65
+ # * host the hostname for the connection
66
+ # * port the port number for the connection
67
+ # * db the database name for the connection
68
+ # * opts connection options
69
+ # * drizzle_ptr FFI pointer to a drizzle_st object
70
+ #
71
+ # Some examples :
72
+ #
73
+ # c = Drizzle::Connection.new
74
+ # c = Drizzle::Connection.new("my_host", 4427)
75
+ # c = Drizzle::Connection.new("my_host", 4427, "my_db")
76
+ # c = Drizzle::Connection.new("my_host", 4427, "my_db", [Drizzle::NONE])
77
+ #
78
+ def initialize(host = "localhost", port = 4427, db = nil, opts = [], drizzle_ptr = nil)
79
+ @host = host
80
+ @port = port
81
+ @db = db
82
+ @drizzle_handle = drizzle_ptr || DrizzlePtr.new(LibDrizzle.drizzle_create(nil))
83
+ @con_ptr = ConnectionPtr.new(LibDrizzle.drizzle_con_create(@drizzle_handle, nil))
84
+ @rand_key = ""
85
+
86
+ opts.each do |opt|
87
+ if opt == INJECTION_PREVENTION
88
+ @randomize_queries = true
89
+ next
90
+ end
91
+ LibDrizzle.drizzle_con_add_options(@con_ptr, LibDrizzle::ConnectionOptions[opt])
92
+ end
93
+ LibDrizzle.drizzle_con_set_tcp(@con_ptr, @host, @port)
94
+ LibDrizzle.drizzle_con_set_db(@con_ptr, @db) if @db
95
+ @ret_ptr = FFI::MemoryPointer.new(:int)
96
+
97
+ #
98
+ # if SQL injection prevention is enabled, we need to retrieve
99
+ # the key to use for randomization from the server
100
+ #
101
+ if @randomize_queries == true
102
+ query = "show variables like '%stad_key%'"
103
+ res = LibDrizzle.drizzle_query_str(@con_ptr, nil, query, @ret_ptr)
104
+ check_return_code
105
+ result = Result.new(res)
106
+ result.buffer_result
107
+ result.each do |row|
108
+ @rand_key = row[1]
109
+ end
110
+ end
111
+ end
112
+
113
+ #
114
+ # set the host and port for the connection
115
+ #
116
+ def set_tcp(host, port)
117
+ @host = host
118
+ @port = port
119
+ LibDrizzle.drizzle_con_set_tcp(@con_ptr, @host, @port)
120
+ end
121
+
122
+ #
123
+ # set the database name for the connection
124
+ #
125
+ def set_db(db_name)
126
+ @db = db_name
127
+ LibDrizzle.drizzle_con_set_db(@con_ptr, @db)
128
+ end
129
+
130
+ #
131
+ # execute a query and construct a result object
132
+ #
133
+ def query(query)
134
+ res = LibDrizzle.drizzle_query_str(@con_ptr, nil, query, @ret_ptr)
135
+ check_return_code
136
+ Result.new(res)
137
+ end
138
+
139
+ #
140
+ # tokenize the input query and append the randomization key to
141
+ # keywords
142
+ #
143
+ def randomize_query(query)
144
+ if @rand_key.empty?
145
+ return query
146
+ end
147
+ toks = query.split(" ")
148
+ new_query = ""
149
+ toks.each do |token|
150
+ if Keywords[token.downcase] == true
151
+ token << @rand_key
152
+ end
153
+ new_query << token << " "
154
+ end
155
+ new_query
156
+ end
157
+
158
+ def check_return_code()
159
+ case LibDrizzle::ReturnCode[@ret_ptr.get_int(0)]
160
+ when :DRIZZLE_RETURN_IO_WAIT
161
+ raise IoWait.new(LibDrizzle.drizzle_error(@drizzle_handle))
162
+ when :DRIZZLE_RETURN_PAUSE
163
+ raise Pause.new(LibDrizzle.drizzle_error(@drizzle_handle))
164
+ when :DRIZZLE_RETURN_ROW_BREAK
165
+ raise RowBreak.new(LibDrizzle.drizzle_error(@drizzle_handle))
166
+ when :DRIZZLE_RETURN_MEMORY
167
+ raise Memory.new(LibDrizzle.drizzle_error(@drizzle_handle))
168
+ when :DRIZZLE_RETURN_INTERNAL_ERROR
169
+ raise InternalError.new(LibDrizzle.drizzle_error(@drizzle_handle))
170
+ when :DRIZZLE_RETURN_NOT_READY
171
+ raise NotReady.new(LibDrizzle.drizzle_error(@drizzle_handle))
172
+ when :DRIZZLE_RETURN_BAD_PACKET_NUMBER
173
+ raise BadPacketNumber.new(LibDrizzle.drizzle_error(@drizzle_handle))
174
+ when :DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET
175
+ raise BadHandshake.new(LibDrizzle.drizzle_error(@drizzle_handle))
176
+ when :DRIZZLE_RETURN_BAD_PACKET
177
+ raise BadPacket.new(LibDrizzle.drizzle_error(@drizzle_handle))
178
+ when :DRIZZLE_RETURN_PROTOCOL_NOT_SUPPORTED
179
+ raise ProtocolNotSupported.new(LibDrizzle.drizzle_error(@drizzle_handle))
180
+ when :DRIZZLE_RETURN_UNEXPECTED_DATA
181
+ raise UnexpectedData.new(LibDrizzle.drizzle_error(@drizzle_handle))
182
+ when :DRIZZLE_RETURN_NO_SCRAMBLE
183
+ raise NoScramble.new(LibDrizzle.drizzle_error(@drizzle_handle))
184
+ when :DRIZZLE_RETURN_AUTH_FAILED
185
+ raise AuthFailed.new(LibDrizzle.drizzle_error(@drizzle_handle))
186
+ when :DRIZZLE_RETURN_NULL_SIZE
187
+ raise NullSize.new(LibDrizzle.drizzle_error(@drizzle_handle))
188
+ when :DRIZZLE_RETURN_TOO_MANY_COLUMNS
189
+ raise TooManyColumns.new(LibDrizzle.drizzle_error(@drizzle_handle))
190
+ when :DRIZZLE_RETURN_ROW_END
191
+ raise RowEnd.new(LibDrizzle.drizzle_error(@drizzle_handle))
192
+ when :DRIZZLE_RETURN_LOST_CONNECTION
193
+ raise LostConnection.new(LibDrizzle.drizzle_error(@drizzle_handle))
194
+ when :DRIZZLE_RETURN_COULD_NOT_CONNECT
195
+ raise CouldNotConnect.new(LibDrizzle.drizzle_error(@drizzle_handle))
196
+ when :DRIZZLE_RETURN_NO_ACTIVE_CONNECTIONS
197
+ raise NoActiveConnections.new(LibDrizzle.drizzle_error(@drizzle_handle))
198
+ when :DRIZZLE_RETURN_HANDSHAKE_FAILED
199
+ raise HandshakeFailed.new(LibDrizzle.drizzle_error(@drizzle_handle))
200
+ when :DRIZZLE_RETURN_TIMEOUT
201
+ raise ReturnTimeout.new(LibDrizzle.drizzle_error(@drizzle_handle))
202
+ end
203
+ end
204
+
205
+ end
206
+
207
+ end
@@ -0,0 +1,58 @@
1
+ require 'drizzle/ffidrizzle'
2
+
3
+ module Drizzle
4
+
5
+ class DrizzlePtr < FFI::AutoPointer
6
+ def self.release(ptr)
7
+ LibDrizzle.drizzle_free(ptr)
8
+ end
9
+ end
10
+
11
+ #
12
+ # A Drizzle instance
13
+ #
14
+ class Drizzle
15
+
16
+ #
17
+ # creates a drizzle instance
18
+ #
19
+ def initialize()
20
+ @handle = DrizzlePtr.new(LibDrizzle.drizzle_create(nil))
21
+ end
22
+
23
+ #
24
+ # create a client connection
25
+ #
26
+ def create_client_connection(host, port, db)
27
+ Connection.new(host, port, db, @handle)
28
+ end
29
+
30
+ #
31
+ # return the libdrizzle API version
32
+ #
33
+ def version()
34
+ LibDrizzle.drizzle_version
35
+ end
36
+
37
+ #
38
+ # return the bug report URL to file libdrizzle bugs at
39
+ #
40
+ def bug_report_url()
41
+ LibDrizzle.drizzle_bugreport
42
+ end
43
+
44
+ #
45
+ # add a query to be run concurrently
46
+ #
47
+ def add_query(conn, query, query_num, opts = [])
48
+ end
49
+
50
+ #
51
+ # execute all queries concurrently
52
+ #
53
+ def run_all()
54
+ end
55
+
56
+ end
57
+
58
+ end
@@ -0,0 +1,28 @@
1
+ require 'drizzle/ffidrizzle'
2
+
3
+ module Drizzle
4
+
5
+ class IoWait < RuntimeError; end
6
+ class Pause < RuntimeError; end
7
+ class RowBreak < RuntimeError; end
8
+ class Memory < RuntimeError; end
9
+ class InternalError < RuntimeError; end
10
+ class NotReady < RuntimeError; end
11
+ class BadPacketNumber < RuntimeError; end
12
+ class BadHandshake < RuntimeError; end
13
+ class BadPacket < RuntimeError; end
14
+ class ProtocolNotSupported < RuntimeError; end
15
+ class UnexpectedData < RuntimeError; end
16
+ class NoScramble < RuntimeError; end
17
+ class AuthFailed < RuntimeError; end
18
+ class NullSize < RuntimeError; end
19
+ class TooManyColumns < RuntimeError; end
20
+ class RowEnd < RuntimeError; end
21
+ class LostConnection < RuntimeError; end
22
+ class CouldNotConnect < RuntimeError; end
23
+ class NoActiveConnections < RuntimeError; end
24
+ class HandshakeFailed < RuntimeError; end
25
+ class Timeout < RuntimeError; end
26
+ class GeneralError < RuntimeError; end
27
+
28
+ end
@@ -0,0 +1,130 @@
1
+ require 'rubygems'
2
+ require 'ffi'
3
+
4
+ module LibDrizzle
5
+ extend FFI::Library
6
+ ffi_lib 'drizzle'
7
+
8
+ # constants
9
+ MAX_ERROR_SIZE = 2048
10
+ MAX_USER_SIZE = 64
11
+ MAX_PASSWORD_SIZE = 32
12
+ MAX_DB_SIZE = 64
13
+ MAX_INFO_SIZE = 2048
14
+ MAX_SQLSTATE_SIZE = 5
15
+ MAX_CATALOG_SIZE = 128
16
+ MAX_TABLE_SIZE = 128
17
+
18
+ # return codes
19
+ ReturnCode = enum( :DRIZZLE_RETURN_OK, 0,
20
+ :DRIZZLE_RETURN_IO_WAIT,
21
+ :DRIZZLE_RETURN_PAUSE,
22
+ :DRIZZLE_RETURN_ROW_BREAK,
23
+ :DRIZZLE_RETURN_MEMORY,
24
+ :DRIZZLE_RETURN_ERRNO,
25
+ :DRIZZLE_RETURN_INTERNAL_ERROR,
26
+ :DRIZZLE_RETURN_GETADDRINFO,
27
+ :DRIZZLE_RETURN_NOT_READY,
28
+ :DRIZZLE_RETURN_BAD_PACKET_NUMBER,
29
+ :DRIZZLE_RETURN_BAD_HANDSHAKE_PACKET,
30
+ :DRIZZLE_RETURN_BAD_PACKET,
31
+ :DRIZZLE_RETURN_PROTOCOL_NOT_SUPPORTED,
32
+ :DRIZZLE_RETURN_UNEXPECTED_DATA,
33
+ :DRIZZLE_RETURN_NO_SCRAMBLE,
34
+ :DRIZZLE_RETURN_AUTH_FAILED,
35
+ :DRIZZLE_RETURN_NULL_SIZE,
36
+ :DRIZZLE_RETURN_ERROR_CODE,
37
+ :DRIZZLE_RETURN_TOO_MANY_COLUMNS,
38
+ :DRIZZLE_RETURN_ROW_END,
39
+ :DRIZZLE_RETURN_LOST_CONNECTION,
40
+ :DRIZZLE_RETURN_COULD_NOT_CONNECT,
41
+ :DRIZZLE_RETURN_NO_ACTIVE_CONNECTIONS,
42
+ :DRIZZLE_RETURN_HANDSHAKE_FAILED,
43
+ :DRIZZLE_RETURN_TIMEOUT,
44
+ :DRIZZLE_RETURN_MAX )
45
+
46
+ # verbosity levels
47
+ VerbosityLevel = enum( :DRIZZLE_VERBOSE_NEVER, 0,
48
+ :DRIZZLE_VERBOSE_FATAL,
49
+ :DRIZZLE_VERBOSE_ERROR,
50
+ :DRIZZLE_VERBOSE_INFO,
51
+ :DRIZZLE_VERBOSE_DEBUG,
52
+ :DRIZZLE_VERBOSE_CRAZY,
53
+ :DRIZZLE_VERBOSE_MAX )
54
+
55
+ # options for the Drizzle protocol functions.
56
+ CommandTypes = enum( :DRIZZLE_COMMAND_DRIZZLE_SLEEP, 0,
57
+ :DRIZZLE_COMMAND_DRIZZLE_QUIT,
58
+ :DRIZZLE_COMMAND_DRIZZLE_INIT_DB,
59
+ :DRIZZLE_COMMAND_DRIZZLE_QUERY,
60
+ :DRIZZLE_COMMAND_DRIZZLE_SHUTDOWN,
61
+ :DRIZZLE_COMMAND_DRIZZLE_CONNECT,
62
+ :DRIZZLE_COMMAND_DRIZZLE_PING,
63
+ :DRIZZLE_COMMAND_DRIZZLE_END )
64
+
65
+ # Status flags for a drizzle connection
66
+ ConnectionStatus = enum( :DRIZZLE_CON_STATUS_NONE, 0,
67
+ :DRIZZLE_CON_STATUS_IN_TRANS, (1 << 0),
68
+ :DRIZZLE_CON_STATUS_AUTOCOMMIT, (1 << 1),
69
+ :DRIZZLE_CON_STATUS_MORE_RESULTS_EXIST, (1 << 3),
70
+ :DRIZZLE_CON_STATUS_QUERY_NO_GOOD_INDEX_USED, (1 << 4),
71
+ :DRIZZLE_CON_STATUS_QUERY_NO_INDEX_USED, (1 << 5),
72
+ :DRIZZLE_CON_STATUS_CURSOR_EXISTS, (1 << 6),
73
+ :DRIZZLE_CON_STATUS_LAST_ROW_SENT, (1 << 7),
74
+ :DRIZZLE_CON_STATUS_DB_DROPPED, (1 << 8),
75
+ :DRIZZLE_CON_STATUS_NO_BACKSLASH_ESCAPES, (1 << 9),
76
+ :DRIZZLE_CON_STATUS_QUERY_WAS_SLOW, (1 << 10) )
77
+
78
+ # Options for connections
79
+ ConnectionOptions = enum( :DRIZZLE_CON_NONE, 0,
80
+ :DRIZZLE_CON_ALLOCATED, (1 << 0),
81
+ :DRIZZLE_CON_MYSQL, (1 << 1),
82
+ :DRIZZLE_CON_RAW_PACKET, (1 << 2),
83
+ :DRIZZLE_CON_RAW_SCRAMBLE, (1 << 3),
84
+ :DRIZZLE_CON_READY, (1 << 4),
85
+ :DRIZZLE_CON_NO_RESULT_READ, (1 << 5) )
86
+
87
+ # query options
88
+ QueryOptions = enum( :DRIZZLE_QUERY_ALLOCATED, (1 << 0) )
89
+
90
+ # options for main drizzle structure
91
+ Options = enum( :DRIZZLE_NONE, 0,
92
+ :DRIZZLE_ALLOCATED, (1 << 0),
93
+ :DRIZZLE_NON_BLOCKING, (1 << 1),
94
+ :DRIZZLE_FREE_OBJECTS, (1 << 2),
95
+ :DRIZZLE_ASSERT_DANGLING, (1 << 3) )
96
+
97
+ attach_function :drizzle_create, [ :pointer ], :pointer
98
+ attach_function :drizzle_free, [ :pointer ], :void
99
+ attach_function :drizzle_set_verbose, [ :pointer, VerbosityLevel ], :void
100
+
101
+ # connection related functions
102
+ attach_function :drizzle_con_create, [ :pointer, :pointer ], :pointer
103
+ attach_function :drizzle_con_free, [ :pointer ], :void
104
+ attach_function :drizzle_con_set_tcp, [ :pointer, :string, :int ], :void
105
+ attach_function :drizzle_con_set_db, [ :pointer, :string ], :void
106
+ attach_function :drizzle_con_add_options, [ :pointer, ConnectionOptions ], :void
107
+ attach_function :drizzle_con_fd, [ :pointer ], :int
108
+
109
+ # query related functions
110
+ attach_function :drizzle_query_str, [ :pointer, :pointer, :string, :pointer ], :pointer
111
+ attach_function :drizzle_query_add, [ :pointer, :pointer, :pointer, :pointer, :string, :size_t, QueryOptions, :pointer ], :pointer
112
+ attach_function :drizzle_result_read, [ :pointer, :pointer, :pointer ], :pointer
113
+ attach_function :drizzle_row_buffer, [ :pointer, :pointer ], :pointer
114
+ attach_function :drizzle_row_free, [ :pointer ], :void
115
+ attach_function :drizzle_result_buffer, [ :pointer ], ReturnCode
116
+ attach_function :drizzle_result_free, [ :pointer ], :void
117
+ attach_function :drizzle_row_next, [ :pointer ], :pointer
118
+ attach_function :drizzle_column_next, [ :pointer ], :pointer
119
+ attach_function :drizzle_column_name, [ :pointer ], :string
120
+ attach_function :drizzle_column_buffer, [ :pointer ], ReturnCode
121
+ attach_function :drizzle_result_column_count, [ :pointer ], :uint16
122
+ attach_function :drizzle_result_affected_rows, [ :pointer ], :uint64
123
+ attach_function :drizzle_result_insert_id, [ :pointer ], :uint64
124
+
125
+ # miscellaneous functions
126
+ attach_function :drizzle_version, [], :string
127
+ attach_function :drizzle_bugreport, [], :string
128
+ attach_function :drizzle_error, [ :pointer ], :string
129
+
130
+ end
@@ -0,0 +1,95 @@
1
+ require 'drizzle/ffidrizzle'
2
+
3
+ module Drizzle
4
+
5
+ #
6
+ # a result set from a drizzle query
7
+ #
8
+ class Result
9
+
10
+ attr_reader :columns, :rows, :affected_rows, :insert_id
11
+
12
+ #
13
+ # creates a result set instance
14
+ # This result set does not buffer any results until instructed
15
+ # to do so. Results can be fully buffered or buffered at the
16
+ # row level.
17
+ #
18
+ # == parameters
19
+ #
20
+ # * res_ptr FFI memory pointer to a drizzle_result_t object
21
+ #
22
+ def initialize(res_ptr)
23
+ @columns, @rows = [], []
24
+ @res_ptr = res_ptr
25
+ @affected_rows = LibDrizzle.drizzle_result_affected_rows(@res_ptr)
26
+ @insert_id = LibDrizzle.drizzle_result_insert_id(@res_ptr)
27
+ end
28
+
29
+ #
30
+ # buffer all rows and columns and copy them into ruby arrays
31
+ # Free the FFI pointer afterwards
32
+ #
33
+ def buffer_result()
34
+ ret = LibDrizzle.drizzle_result_buffer(@res_ptr)
35
+ if LibDrizzle::ReturnCode[ret] != LibDrizzle::ReturnCode[:DRIZZLE_RETURN_OK]
36
+ LibDrizzle.drizzle_result_free(@res_ptr)
37
+ end
38
+
39
+ loop do
40
+ col_ptr = LibDrizzle.drizzle_column_next(@res_ptr)
41
+ break if col_ptr.null?
42
+ @columns << LibDrizzle.drizzle_column_name(col_ptr).to_sym
43
+ end
44
+
45
+ loop do
46
+ row_ptr = LibDrizzle.drizzle_row_next(@res_ptr)
47
+ break if row_ptr.null?
48
+ @rows << row_ptr.get_array_of_string(0, @columns.size)
49
+ end
50
+
51
+ LibDrizzle.drizzle_result_free(@res_ptr)
52
+ end
53
+
54
+ #
55
+ # buffer an individual row.
56
+ #
57
+ def buffer_row()
58
+ # if the columns have not been read for this result
59
+ # set yet, then we need to do that here. If this is not
60
+ # performed here, we will receive a bad packet error
61
+ read_columns if @columns.empty?
62
+ ret_ptr = FFI::MemoryPointer.new(:int)
63
+ row_ptr = LibDrizzle.drizzle_row_buffer(@res_ptr, ret_ptr)
64
+ if LibDrizzle::ReturnCode[ret_ptr.get_int(0)] != :DRIZZLE_RETURN_OK
65
+ LibDrizzle.drizzle_result_free(@res_ptr)
66
+ end
67
+ if row_ptr.null?
68
+ LibDrizzle.drizzle_result_free(@res_ptr)
69
+ return nil
70
+ end
71
+ num_of_cols = LibDrizzle.drizzle_result_column_count(@res_ptr)
72
+ row = row_ptr.get_array_of_string(0, @columns.size)
73
+ end
74
+
75
+ #
76
+ # buffer all columns for this result set
77
+ #
78
+ def read_columns
79
+ ret = LibDrizzle.drizzle_column_buffer(@res_ptr)
80
+ loop do
81
+ col_ptr = LibDrizzle.drizzle_column_next(@res_ptr)
82
+ break if col_ptr.null?
83
+ @columns << LibDrizzle.drizzle_column_name(col_ptr).to_sym
84
+ end
85
+ end
86
+
87
+ def each
88
+ @rows.each do |row|
89
+ yield row if block_given?
90
+ end
91
+ end
92
+
93
+ end
94
+
95
+ end
@@ -0,0 +1,28 @@
1
+ require 'rubygems'
2
+
3
+ require File.join(File.dirname(__FILE__), *%w[.. lib drizzle])
4
+
5
+ require 'test/unit'
6
+ require 'shoulda'
7
+ require 'rr'
8
+
9
+ include Drizzle
10
+
11
+ PORT = 4427
12
+
13
+ class Test::Unit::TestCase
14
+ include RR::Adapters::TestUnit
15
+
16
+ def dest_dir(*subdirs)
17
+ File.join(File.dirname(__FILE__), 'dest', *subdirs)
18
+ end
19
+
20
+ def source_dir(*subdirs)
21
+ File.join(File.dirname(__FILE__), 'source', *subdirs)
22
+ end
23
+
24
+ def clear_dest
25
+ FileUtils.rm_rf(dest_dir)
26
+ end
27
+
28
+ end
@@ -0,0 +1,103 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestBasic < Test::Unit::TestCase
4
+
5
+ should "retrieve libdrizzle API version" do
6
+ drizzle = Drizzle::Drizzle.new
7
+ assert_equal "0.7", drizzle.version
8
+ end
9
+
10
+ should "retrieve libdrizzle bug report url" do
11
+ drizzle = Drizzle::Drizzle.new
12
+ assert_equal "https://launchpad.net/libdrizzle", drizzle.bug_report_url
13
+ end
14
+
15
+ should "connect to drizzle successfully" do
16
+ conn = Drizzle::Connection.new("localhost", PORT)
17
+ assert_equal conn.class, Drizzle::Connection
18
+ end
19
+
20
+ should "perform a simple query and buffer all results" do
21
+ conn = Drizzle::Connection.new("localhost", PORT, "data_dictionary")
22
+ res = conn.query("SELECT module_name, module_author FROM MODULES where module_name = 'SchemaEngine'")
23
+ assert_equal res.class, Drizzle::Result
24
+ res.buffer_result
25
+ assert_equal 2, res.columns.size
26
+ res.each do |row|
27
+ assert_equal "Brian Aker", row[1]
28
+ end
29
+ end
30
+
31
+ should "perform another simple query and buffer all results" do
32
+ conn = Drizzle::Connection.new("localhost", PORT, "information_schema", [Drizzle::NONE])
33
+ res = conn.query("SELECT table_schema, table_name FROM TABLES WHERE table_schema = 'DATA_DICTIONARY' AND table_name = 'GLOBAL_STATUS'")
34
+ assert_equal res.class, Drizzle::Result
35
+ res.buffer_result
36
+ assert_equal 2, res.columns.size
37
+ res.each do |row|
38
+ assert_equal "DATA_DICTIONARY", row[0]
39
+ assert_equal "GLOBAL_STATUS", row[1]
40
+ end
41
+ end
42
+
43
+ should "perform a simple query and buffer rows" do
44
+ conn = Drizzle::Connection.new("localhost", PORT, "information_schema")
45
+ res = conn.query("SELECT COUNT(*) FROM COLUMNS WHERE table_name = 'GLOBAL_STATUS'")
46
+ assert_equal res.class, Drizzle::Result
47
+ until (row = res.buffer_row).nil?
48
+ assert_equal "2", row[0]
49
+ end
50
+ end
51
+
52
+ should "create and drop a database" do
53
+ conn = Drizzle::Connection.new("localhost", PORT)
54
+ res = conn.query("CREATE DATABASE padraig")
55
+ res = conn.query("select table_schema from information_schema.tables where table_schema = 'padraig'")
56
+ assert_equal res.class, Drizzle::Result
57
+ res.buffer_result
58
+ assert_equal 1, res.columns.size
59
+ res.each do |row|
60
+ assert_equal "padraig", row[0]
61
+ end
62
+ res = conn.query("DROP DATABASE padraig")
63
+ res = conn.query("select table_schema from information_schema.tables where table_schema = 'padraig'")
64
+ assert_equal res.class, Drizzle::Result
65
+ res.buffer_result
66
+ assert_equal 1, res.columns.size
67
+ assert_equal true, res.rows.empty?
68
+ end
69
+
70
+ should "use different connection options" do
71
+ conn = Drizzle::Connection.new("localhost", PORT, "information_schema", [Drizzle::NONE])
72
+ res = conn.query("SELECT COUNT(*) FROM COLUMNS WHERE table_name = 'GLOBAL_STATUS'")
73
+ assert_equal res.class, Drizzle::Result
74
+ until (row = res.buffer_row).nil?
75
+ assert_equal "2", row[0]
76
+ end
77
+ conn = Drizzle::Connection.new("localhost", PORT, "information_schema", [Drizzle::NONE, Drizzle::MYSQL])
78
+ res = conn.query("SELECT COUNT(*) FROM COLUMNS WHERE table_name = 'GLOBAL_STATUS'")
79
+ assert_equal res.class, Drizzle::Result
80
+ until (row = res.buffer_row).nil?
81
+ assert_equal "2", row[0]
82
+ end
83
+ end
84
+
85
+ should "perform a query with a code block" do
86
+ conn = Drizzle::Connection.new("localhost", PORT, "information_schema", [Drizzle::NONE])
87
+ conn.query("SELECT COUNT(*) FROM COLUMNS WHERE table_name = 'GLOBAL_STATUS'") do |res|
88
+ assert_equal res.class, Drizzle::Result
89
+ until (row = res.buffer_row).nil?
90
+ assert_equal "2", row[0]
91
+ end
92
+ end
93
+ end
94
+
95
+ should "perform a simple show variables command" do
96
+ conn = Drizzle::Connection.new("localhost", PORT, "information_schema", [Drizzle::NONE])
97
+ assert_equal conn.class, Drizzle::Connection
98
+ res = conn.query("show variables")
99
+ assert_equal res.class, Drizzle::Result
100
+ res.buffer_result
101
+ end
102
+
103
+ end
@@ -0,0 +1,133 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestBasic < Test::Unit::TestCase
4
+
5
+ def setup
6
+ conn = Drizzle::Connection.new("localhost", PORT)
7
+ res = conn.query("CREATE DATABASE drizzleruby")
8
+ end
9
+
10
+ def teardown
11
+ conn = Drizzle::Connection.new("localhost", PORT)
12
+ res = conn.query("DROP DATABASE drizzleruby")
13
+ end
14
+
15
+ should "create and drop a table" do
16
+ conn = Drizzle::Connection.new("localhost", PORT, "drizzleruby")
17
+ res = conn.query("select table_schema from information_schema.tables where table_schema = 'drizzleruby'")
18
+ assert_equal res.class, Drizzle::Result
19
+ res.buffer_result
20
+ assert_equal 1, res.columns.size
21
+ res.each do |row|
22
+ assert_equal "drizzleruby", row[0]
23
+ end
24
+ res = conn.query("create table t1(a int, b varchar(255))")
25
+ res = conn.query("select table_schema, table_name from information_schema.tables where table_schema = 'drizzleruby'")
26
+ res.buffer_result
27
+ assert_equal 2, res.columns.size
28
+ res.each do |row|
29
+ assert_equal "t1", row[1]
30
+ end
31
+ res = conn.query("DROP TABLE t1")
32
+ res = conn.query("select table_name from information_schema.tables where table_schema = 'drizzleruby'")
33
+ assert_equal res.class, Drizzle::Result
34
+ res.buffer_result
35
+ assert_equal 1, res.columns.size
36
+ assert_equal true, res.rows.empty?
37
+ end
38
+
39
+ should "update affected rows appropriately" do
40
+ conn = Drizzle::Connection.new("localhost", PORT, "drizzleruby")
41
+ res = conn.query("select table_schema from information_schema.tables where table_schema = 'drizzleruby'")
42
+ assert_equal res.class, Drizzle::Result
43
+ res.buffer_result
44
+ assert_equal 1, res.columns.size
45
+ res.each do |row|
46
+ assert_equal "drizzleruby", row[0]
47
+ end
48
+ res = conn.query("create table t1(a int, b varchar(255))")
49
+ res = conn.query("select table_schema, table_name from information_schema.tables where table_schema = 'drizzleruby'")
50
+ res.buffer_result
51
+ assert_equal 2, res.columns.size
52
+ res.each do |row|
53
+ assert_equal "t1", row[1]
54
+ end
55
+ res = conn.query("insert into t1 values (1, 'padraig')")
56
+ assert_equal 1, res.affected_rows
57
+ assert_equal 0, res.insert_id
58
+ res = conn.query("insert into t1 values (2, 'sarah')")
59
+ assert_equal 1, res.affected_rows
60
+ assert_equal 0, res.insert_id
61
+ res = conn.query("select * from t1 where a = 2")
62
+ res.buffer_result
63
+ assert_equal 2, res.columns.size
64
+ res.each do |row|
65
+ assert_equal "2", row[0]
66
+ assert_equal "sarah", row[1]
67
+ end
68
+ res = conn.query("DROP TABLE t1")
69
+ res = conn.query("select table_name from information_schema.tables where table_schema = 'drizzleruby'")
70
+ assert_equal res.class, Drizzle::Result
71
+ res.buffer_result
72
+ assert_equal 1, res.columns.size
73
+ assert_equal true, res.rows.empty?
74
+ end
75
+
76
+ should "perform a multi-insert statement correctly" do
77
+ conn = Drizzle::Connection.new("localhost", PORT, "drizzleruby")
78
+ res = conn.query("select table_schema from information_schema.tables where table_schema = 'drizzleruby'")
79
+ assert_equal res.class, Drizzle::Result
80
+ res.buffer_result
81
+ assert_equal 1, res.columns.size
82
+ res.each do |row|
83
+ assert_equal "drizzleruby", row[0]
84
+ end
85
+ res = conn.query("create table t1(a int, b varchar(255))")
86
+ res = conn.query("select table_schema, table_name from information_schema.tables where table_schema = 'drizzleruby'")
87
+ res.buffer_result
88
+ assert_equal 2, res.columns.size
89
+ res.each do |row|
90
+ assert_equal "t1", row[1]
91
+ end
92
+ res = conn.query("insert into t1 values (1, 'padraig'), (2, 'sarah'), (3, 'tomas')")
93
+ assert_equal 3, res.affected_rows
94
+ res = conn.query("select * from t1 where a = 2")
95
+ res.buffer_result
96
+ assert_equal 2, res.columns.size
97
+ res.each do |row|
98
+ assert_equal "2", row[0]
99
+ assert_equal "sarah", row[1]
100
+ end
101
+ res = conn.query("DROP TABLE t1")
102
+ res = conn.query("select table_name from information_schema.tables where table_schema = 'drizzleruby'")
103
+ assert_equal res.class, Drizzle::Result
104
+ res.buffer_result
105
+ assert_equal 1, res.columns.size
106
+ assert_equal true, res.rows.empty?
107
+ end
108
+
109
+ should "insert and fetch a blob value correctly" do
110
+ conn = Drizzle::Connection.new("localhost", PORT, "drizzleruby")
111
+ res = conn.query("create table t1(a int, b blob)")
112
+ res = conn.query("insert into t1 values (1, 'padraig'), (2, 'sarah'), (3, 'tomas')")
113
+ assert_equal 3, res.affected_rows
114
+ res = conn.query("insert into t1 values (4, null), (5, 'blahblahblah'), (6, 'southy')")
115
+ assert_equal 3, res.affected_rows
116
+ res = conn.query("select * from t1 where a = 2")
117
+ res.buffer_result
118
+ assert_equal 2, res.columns.size
119
+ res.each do |row|
120
+ assert_equal "2", row[0]
121
+ assert_equal "sarah", row[1]
122
+ end
123
+ res = conn.query("select * from t1 where a = 4")
124
+ res.buffer_result
125
+ assert_equal 2, res.columns.size
126
+ res.each do |row|
127
+ assert_equal "4", row[0]
128
+ assert_equal nil, row[1]
129
+ end
130
+ res = conn.query("DROP TABLE t1")
131
+ end
132
+
133
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: drizzle
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - Padraig O'Sullivan
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-04-13 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: thoughtbot-shoulda
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 0
29
+ version: "0"
30
+ type: :development
31
+ version_requirements: *id001
32
+ description: An interface to Drizzle for Ruby that uses libdrizzle
33
+ email: osullivan.padraig@gmail.com
34
+ executables: []
35
+
36
+ extensions: []
37
+
38
+ extra_rdoc_files:
39
+ - LICENSE
40
+ - README.rdoc
41
+ files:
42
+ - LICENSE
43
+ - README.rdoc
44
+ - Rakefile
45
+ - VERSION
46
+ - lib/drizzle.rb
47
+ - lib/drizzle/connection.rb
48
+ - lib/drizzle/drizzle.rb
49
+ - lib/drizzle/exceptions.rb
50
+ - lib/drizzle/ffidrizzle.rb
51
+ - lib/drizzle/result.rb
52
+ - test/helper.rb
53
+ - test/test_basic.rb
54
+ - test/test_complex.rb
55
+ has_rdoc: true
56
+ homepage: http://github.com/posulliv/drizzle-ruby
57
+ licenses: []
58
+
59
+ post_install_message:
60
+ rdoc_options: []
61
+
62
+ require_paths:
63
+ - lib
64
+ required_ruby_version: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ segments:
69
+ - 0
70
+ version: "0"
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ segments:
76
+ - 0
77
+ version: "0"
78
+ requirements: []
79
+
80
+ rubyforge_project: drizzle-ruby
81
+ rubygems_version: 1.3.6
82
+ signing_key:
83
+ specification_version: 3
84
+ summary: A ruby interface to Drizzle using libdrizzle
85
+ test_files:
86
+ - test/helper.rb
87
+ - test/test_basic.rb
88
+ - test/test_complex.rb