mysql2 0.1.4 → 0.1.5

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.
@@ -1,5 +1,5 @@
1
- #include <time.h>
2
1
  #include <ruby.h>
2
+ #include <fcntl.h>
3
3
 
4
4
  #ifdef HAVE_MYSQL_H
5
5
  #include <mysql.h>
@@ -15,17 +15,26 @@
15
15
 
16
16
  #ifdef HAVE_RUBY_ENCODING_H
17
17
  #include <ruby/encoding.h>
18
- int utf8Encoding, binaryEncoding;
18
+ static int utf8Encoding, binaryEncoding;
19
+ #endif
20
+
21
+ #if defined(__GNUC__) && (__GNUC__ >= 3)
22
+ #define RB_MYSQL_UNUSED __attribute__ ((unused))
23
+ #else
24
+ #define RB_MYSQL_UNUSED
19
25
  #endif
20
26
 
21
27
  static VALUE cBigDecimal, cDate, cDateTime;
22
- ID intern_new, intern_local;
28
+ static ID intern_new, intern_local;
23
29
 
24
30
  /* Mysql2::Error */
25
- VALUE cMysql2Error;
31
+ static VALUE cMysql2Error;
26
32
 
27
33
  /* Mysql2::Client */
28
- #define GetMysql2Client(obj, sval) (sval = (MYSQL*)DATA_PTR(obj));
34
+ typedef struct {
35
+ MYSQL * client;
36
+ } mysql2_client_wrapper;
37
+ #define GetMysql2Client(obj, sval) (sval = ((mysql2_client_wrapper*)(DATA_PTR(obj)))->client);
29
38
  static ID sym_socket, sym_host, sym_port, sym_username, sym_password,
30
39
  sym_database, sym_reconnect, sym_connect_timeout, sym_id, sym_version,
31
40
  sym_sslkey, sym_sslcert, sym_sslca, sym_sslcapath, sym_sslcipher,
@@ -40,7 +49,7 @@ static VALUE rb_mysql_client_socket(VALUE self);
40
49
  static VALUE rb_mysql_client_async_result(VALUE self);
41
50
  static VALUE rb_mysql_client_last_id(VALUE self);
42
51
  static VALUE rb_mysql_client_affected_rows(VALUE self);
43
- void rb_mysql_client_free(void * client);
52
+ static void rb_mysql_client_free(void * client);
44
53
 
45
54
  /* Mysql2::Result */
46
55
  typedef struct {
@@ -53,9 +62,60 @@ typedef struct {
53
62
  MYSQL_RES *result;
54
63
  } mysql2_result_wrapper;
55
64
  #define GetMysql2Result(obj, sval) (sval = (mysql2_result_wrapper*)DATA_PTR(obj));
56
- VALUE cMysql2Result;
65
+ static VALUE cMysql2Result;
57
66
  static VALUE rb_mysql_result_to_obj(MYSQL_RES * res);
58
67
  static VALUE rb_mysql_result_fetch_row(int argc, VALUE * argv, VALUE self);
59
68
  static VALUE rb_mysql_result_each(int argc, VALUE * argv, VALUE self);
60
- void rb_mysql_result_free(void * wrapper);
61
- void rb_mysql_result_mark(void * wrapper);
69
+ static void rb_mysql_result_free(void * wrapper);
70
+ static void rb_mysql_result_mark(void * wrapper);
71
+ static void rb_mysql_result_free_result(mysql2_result_wrapper * wrapper);
72
+
73
+ /*
74
+ * used to pass all arguments to mysql_real_connect while inside
75
+ * rb_thread_blocking_region
76
+ */
77
+ struct nogvl_connect_args {
78
+ MYSQL *mysql;
79
+ const char *host;
80
+ const char *user;
81
+ const char *passwd;
82
+ const char *db;
83
+ unsigned int port;
84
+ const char *unix_socket;
85
+ unsigned long client_flag;
86
+ };
87
+
88
+ /*
89
+ * used to pass all arguments to mysql_send_query while inside
90
+ * rb_thread_blocking_region
91
+ */
92
+ struct nogvl_send_query_args {
93
+ MYSQL *mysql;
94
+ VALUE sql;
95
+ };
96
+
97
+ /*
98
+ * partial emulation of the 1.9 rb_thread_blocking_region under 1.8,
99
+ * this is enough for dealing with blocking I/O functions in the
100
+ * presence of threads.
101
+ */
102
+ #ifndef HAVE_RB_THREAD_BLOCKING_REGION
103
+ # include <rubysig.h>
104
+ # define RUBY_UBF_IO ((rb_unblock_function_t *)-1)
105
+ typedef void rb_unblock_function_t(void *);
106
+ typedef VALUE rb_blocking_function_t(void *);
107
+ static VALUE
108
+ rb_thread_blocking_region(
109
+ rb_blocking_function_t *func, void *data1,
110
+ RB_MYSQL_UNUSED rb_unblock_function_t *ubf,
111
+ RB_MYSQL_UNUSED void *data2)
112
+ {
113
+ VALUE rv;
114
+
115
+ TRAP_BEG;
116
+ rv = func(data1);
117
+ TRAP_END;
118
+
119
+ return rv;
120
+ }
121
+ #endif /* ! HAVE_RB_THREAD_BLOCKING_REGION */
@@ -13,6 +13,7 @@ module ActiveRecord
13
13
 
14
14
  module ConnectionAdapters
15
15
  class Mysql2Column < Column
16
+ BOOL = "tinyint(1)".freeze
16
17
  def extract_default(default)
17
18
  if sql_type =~ /blob/i || type == :text
18
19
  if default.blank?
@@ -39,7 +40,7 @@ module ActiveRecord
39
40
  when :float then Float
40
41
  when :decimal then BigDecimal
41
42
  when :datetime then Time
42
- when :date then Time
43
+ when :date then Date
43
44
  when :timestamp then Time
44
45
  when :time then Time
45
46
  when :text, :string then String
@@ -49,21 +50,36 @@ module ActiveRecord
49
50
  end
50
51
 
51
52
  def type_cast(value)
52
- if type == :boolean
53
- self.class.value_to_boolean(value)
54
- else
55
- value
53
+ return nil if value.nil?
54
+ case type
55
+ when :string then value
56
+ when :text then value
57
+ when :integer then value.is_a?(Fixnum) ? value : (value.to_i rescue value ? 1 : 0)
58
+ when :float then value.class == Float ? value : value.to_f
59
+ when :decimal then value.class == BigDecimal ? value : self.class.value_to_decimal(value)
60
+ when :datetime then value.class == Time ? value : self.class.string_to_time(value)
61
+ when :timestamp then value.class == Time ? value : self.class.string_to_time(value)
62
+ when :time then value.class == Time ? value : self.class.string_to_dummy_time(value)
63
+ when :date then value.class == Date ? value : self.class.string_to_date(value)
64
+ when :binary then value
65
+ when :boolean then self.class.value_to_boolean(value)
66
+ else value
56
67
  end
57
68
  end
58
69
 
59
70
  def type_cast_code(var_name)
60
- nil
71
+ case type
72
+ when :boolean then "#{self.class.name}.value_to_boolean(#{var_name})"
73
+ else
74
+ nil
75
+ end
61
76
  end
62
77
 
63
78
  private
64
79
  def simplified_type(field_type)
65
- return :boolean if Mysql2Adapter.emulate_booleans && field_type.downcase.index("tinyint(1)")
80
+ return :boolean if Mysql2Adapter.emulate_booleans && field_type.downcase.index(BOOL)
66
81
  return :string if field_type =~ /enum/i
82
+ return :integer if field_type =~ /year/i
67
83
  super
68
84
  end
69
85
 
@@ -107,6 +123,7 @@ module ActiveRecord
107
123
  self.emulate_booleans = true
108
124
 
109
125
  ADAPTER_NAME = 'MySQL'.freeze
126
+ PRIMARY = "PRIMARY".freeze
110
127
 
111
128
  LOST_CONNECTION_ERROR_MESSAGES = [
112
129
  "Server shutdown in progress",
@@ -479,7 +496,7 @@ module ActiveRecord
479
496
 
480
497
  def show_variable(name)
481
498
  variables = select_all("SHOW VARIABLES LIKE '#{name}'")
482
- variables.first[:Value] unless variables.empty?
499
+ variables.first['Value'] unless variables.empty?
483
500
  end
484
501
 
485
502
  def pk_and_sequence_for(table)
@@ -5,5 +5,5 @@ require 'mysql2_ext'
5
5
  #
6
6
  # A modern, simple and very fast Mysql library for Ruby - binding to libmysql
7
7
  module Mysql2
8
- VERSION = "0.1.4"
8
+ VERSION = "0.1.5"
9
9
  end
@@ -0,0 +1,238 @@
1
+ require 'mysql2' unless defined? Mysql2
2
+
3
+ Sequel.require %w'shared/mysql utils/stored_procedures', 'adapters'
4
+
5
+ module Sequel
6
+ # Module for holding all MySQL-related classes and modules for Sequel.
7
+ module Mysql2
8
+ # Mapping of type numbers to conversion procs
9
+ MYSQL_TYPES = {}
10
+
11
+ MYSQL2_LITERAL_PROC = lambda{|v| v}
12
+
13
+ # Use only a single proc for each type to save on memory
14
+ MYSQL_TYPE_PROCS = {
15
+ [0, 246] => MYSQL2_LITERAL_PROC, # decimal
16
+ [1] => lambda{|v| convert_tinyint_to_bool ? v != 0 : v}, # tinyint
17
+ [2, 3, 8, 9, 13, 247, 248] => MYSQL2_LITERAL_PROC, # integer
18
+ [4, 5] => MYSQL2_LITERAL_PROC, # float
19
+ [10, 14] => MYSQL2_LITERAL_PROC, # date
20
+ [7, 12] => MYSQL2_LITERAL_PROC, # datetime
21
+ [11] => MYSQL2_LITERAL_PROC, # time
22
+ [249, 250, 251, 252] => lambda{|v| Sequel::SQL::Blob.new(v)} # blob
23
+ }
24
+ MYSQL_TYPE_PROCS.each do |k,v|
25
+ k.each{|n| MYSQL_TYPES[n] = v}
26
+ end
27
+
28
+ @convert_invalid_date_time = false
29
+ @convert_tinyint_to_bool = true
30
+
31
+ class << self
32
+ # By default, Sequel raises an exception if in invalid date or time is used.
33
+ # However, if this is set to nil or :nil, the adapter treats dates
34
+ # like 0000-00-00 and times like 838:00:00 as nil values. If set to :string,
35
+ # it returns the strings as is.
36
+ attr_accessor :convert_invalid_date_time
37
+
38
+ # Sequel converts the column type tinyint(1) to a boolean by default when
39
+ # using the native MySQL adapter. You can turn off the conversion by setting
40
+ # this to false.
41
+ attr_accessor :convert_tinyint_to_bool
42
+ end
43
+
44
+ # Database class for MySQL databases used with Sequel.
45
+ class Database < Sequel::Database
46
+ include Sequel::MySQL::DatabaseMethods
47
+
48
+ # Mysql::Error messages that indicate the current connection should be disconnected
49
+ MYSQL_DATABASE_DISCONNECT_ERRORS = /\A(Commands out of sync; you can't run this command now|Can't connect to local MySQL server through socket|MySQL server has gone away)/
50
+
51
+ set_adapter_scheme :mysql2
52
+
53
+ # Support stored procedures on MySQL
54
+ def call_sproc(name, opts={}, &block)
55
+ args = opts[:args] || []
56
+ execute("CALL #{name}#{args.empty? ? '()' : literal(args)}", opts.merge(:sproc=>false), &block)
57
+ end
58
+
59
+ # Connect to the database. In addition to the usual database options,
60
+ # the following options have effect:
61
+ #
62
+ # * :auto_is_null - Set to true to use MySQL default behavior of having
63
+ # a filter for an autoincrement column equals NULL to return the last
64
+ # inserted row.
65
+ # * :charset - Same as :encoding (:encoding takes precendence)
66
+ # * :compress - Set to false to not compress results from the server
67
+ # * :config_default_group - The default group to read from the in
68
+ # the MySQL config file.
69
+ # * :config_local_infile - If provided, sets the Mysql::OPT_LOCAL_INFILE
70
+ # option on the connection with the given value.
71
+ # * :encoding - Set all the related character sets for this
72
+ # connection (connection, client, database, server, and results).
73
+ # * :socket - Use a unix socket file instead of connecting via TCP/IP.
74
+ # * :timeout - Set the timeout in seconds before the server will
75
+ # disconnect this connection.
76
+ def connect(server)
77
+ opts = server_opts(server)
78
+ conn = ::Mysql2::Client.new({
79
+ :host => opts[:host] || 'localhost',
80
+ :username => opts[:user],
81
+ :password => opts[:password],
82
+ :database => opts[:database],
83
+ :port => opts[:port],
84
+ :socket => opts[:socket]
85
+ })
86
+
87
+ # increase timeout so mysql server doesn't disconnect us
88
+ conn.query("set @@wait_timeout = #{opts[:timeout] || 2592000}")
89
+
90
+ # By default, MySQL 'where id is null' selects the last inserted id
91
+ conn.query("set SQL_AUTO_IS_NULL=0") unless opts[:auto_is_null]
92
+
93
+ conn
94
+ end
95
+
96
+ # Returns instance of Sequel::MySQL::Dataset with the given options.
97
+ def dataset(opts = nil)
98
+ Mysql2::Dataset.new(self, opts)
99
+ end
100
+
101
+ # Executes the given SQL using an available connection, yielding the
102
+ # connection if the block is given.
103
+ def execute(sql, opts={}, &block)
104
+ if opts[:sproc]
105
+ call_sproc(sql, opts, &block)
106
+ else
107
+ synchronize(opts[:server]){|conn| _execute(conn, sql, opts, &block)}
108
+ end
109
+ end
110
+
111
+ # Return the version of the MySQL server two which we are connecting.
112
+ def server_version(server=nil)
113
+ @server_version ||= (synchronize(server){|conn| conn.info[:id]})
114
+ end
115
+
116
+ private
117
+
118
+ # Execute the given SQL on the given connection. If the :type
119
+ # option is :select, yield the result of the query, otherwise
120
+ # yield the connection if a block is given.
121
+ def _execute(conn, sql, opts)
122
+ begin
123
+ # r = log_yield(sql){conn.query(sql)}
124
+ r = conn.query(sql)
125
+ if opts[:type] == :select
126
+ yield r if r
127
+ elsif block_given?
128
+ yield conn
129
+ end
130
+ rescue ::Mysql2::Error => e
131
+ raise_error(e, :disconnect=>MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message))
132
+ end
133
+ end
134
+
135
+ # MySQL connections use the query method to execute SQL without a result
136
+ def connection_execute_method
137
+ :query
138
+ end
139
+
140
+ # The MySQL adapter main error class is Mysql::Error
141
+ def database_error_classes
142
+ [::Mysql2::Error]
143
+ end
144
+
145
+ # The database name when using the native adapter is always stored in
146
+ # the :database option.
147
+ def database_name
148
+ @opts[:database]
149
+ end
150
+
151
+ # Closes given database connection.
152
+ def disconnect_connection(c)
153
+ c = nil
154
+ end
155
+
156
+ # Convert tinyint(1) type to boolean if convert_tinyint_to_bool is true
157
+ def schema_column_type(db_type)
158
+ Sequel::MySQL.convert_tinyint_to_bool && db_type == 'tinyint(1)' ? :boolean : super
159
+ end
160
+ end
161
+
162
+ # Dataset class for MySQL datasets accessed via the native driver.
163
+ class Dataset < Sequel::Dataset
164
+ include Sequel::MySQL::DatasetMethods
165
+ include StoredProcedures
166
+
167
+ # Methods for MySQL stored procedures using the native driver.
168
+ module StoredProcedureMethods
169
+ include Sequel::Dataset::StoredProcedureMethods
170
+
171
+ private
172
+
173
+ # Execute the database stored procedure with the stored arguments.
174
+ def execute(sql, opts={}, &block)
175
+ super(@sproc_name, {:args=>@sproc_args, :sproc=>true}.merge(opts), &block)
176
+ end
177
+
178
+ # Same as execute, explicit due to intricacies of alias and super.
179
+ def execute_dui(sql, opts={}, &block)
180
+ super(@sproc_name, {:args=>@sproc_args, :sproc=>true}.merge(opts), &block)
181
+ end
182
+ end
183
+
184
+ # Delete rows matching this dataset
185
+ def delete
186
+ execute_dui(delete_sql){|c| return c.affected_rows}
187
+ end
188
+
189
+ # Yield all rows matching this dataset. If the dataset is set to
190
+ # split multiple statements, yield arrays of hashes one per statement
191
+ # instead of yielding results for all statements as hashes.
192
+ def fetch_rows(sql, &block)
193
+ execute(sql) do |r|
194
+ r.each &block
195
+ end
196
+ self
197
+ end
198
+
199
+ # Don't allow graphing a dataset that splits multiple statements
200
+ def graph(*)
201
+ raise(Error, "Can't graph a dataset that splits multiple result sets") if opts[:split_multiple_result_sets]
202
+ super
203
+ end
204
+
205
+ # Insert a new value into this dataset
206
+ def insert(*values)
207
+ execute_dui(insert_sql(*values)){|c| return c.insert_id}
208
+ end
209
+
210
+ # Replace (update or insert) the matching row.
211
+ def replace(*args)
212
+ execute_dui(replace_sql(*args)){|c| return c.insert_id}
213
+ end
214
+
215
+ # Update the matching rows.
216
+ def update(values={})
217
+ execute_dui(update_sql(values)){|c| return c.affected_rows}
218
+ end
219
+
220
+ private
221
+
222
+ # Set the :type option to :select if it hasn't been set.
223
+ def execute(sql, opts={}, &block)
224
+ super(sql, {:type=>:select}.merge(opts), &block)
225
+ end
226
+
227
+ # Set the :type option to :dui if it hasn't been set.
228
+ def execute_dui(sql, opts={}, &block)
229
+ super(sql, {:type=>:dui}.merge(opts), &block)
230
+ end
231
+
232
+ # Handle correct quoting of strings using ::MySQL.quote.
233
+ def literal_string(v)
234
+ db.synchronize{|c| "'#{c.quote(v)}'"}
235
+ end
236
+ end
237
+ end
238
+ end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{mysql2}
8
- s.version = "0.1.4"
8
+ s.version = "0.1.5"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Brian Lopez"]
12
- s.date = %q{2010-04-23}
12
+ s.date = %q{2010-05-12}
13
13
  s.email = %q{seniorlopez@gmail.com}
14
14
  s.extensions = ["ext/extconf.rb"]
15
15
  s.extra_rdoc_files = [
@@ -24,7 +24,9 @@ Gem::Specification.new do |s|
24
24
  "VERSION",
25
25
  "benchmark/active_record.rb",
26
26
  "benchmark/escape.rb",
27
- "benchmark/query.rb",
27
+ "benchmark/query_with_mysql_casting.rb",
28
+ "benchmark/query_without_mysql_casting.rb",
29
+ "benchmark/sequel.rb",
28
30
  "benchmark/setup_db.rb",
29
31
  "examples/eventmachine.rb",
30
32
  "ext/extconf.rb",
@@ -34,6 +36,7 @@ Gem::Specification.new do |s|
34
36
  "lib/arel/engines/sql/compilers/mysql2_compiler.rb",
35
37
  "lib/mysql2.rb",
36
38
  "lib/mysql2/em.rb",
39
+ "lib/sequel/adapters/mysql2.rb",
37
40
  "mysql2.gemspec",
38
41
  "spec/active_record/active_record_spec.rb",
39
42
  "spec/em/em_spec.rb",