mysql2 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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",