activerecord 3.1.12 → 3.2.0.rc1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (99) hide show
  1. data/CHANGELOG.md +6263 -103
  2. data/README.rdoc +2 -2
  3. data/examples/performance.rb +55 -31
  4. data/lib/active_record.rb +28 -2
  5. data/lib/active_record/aggregations.rb +2 -2
  6. data/lib/active_record/associations.rb +82 -69
  7. data/lib/active_record/associations/association.rb +2 -37
  8. data/lib/active_record/associations/association_scope.rb +3 -30
  9. data/lib/active_record/associations/builder/association.rb +6 -4
  10. data/lib/active_record/associations/builder/belongs_to.rb +3 -3
  11. data/lib/active_record/associations/builder/collection_association.rb +2 -2
  12. data/lib/active_record/associations/builder/has_many.rb +4 -4
  13. data/lib/active_record/associations/builder/has_one.rb +5 -6
  14. data/lib/active_record/associations/builder/singular_association.rb +3 -16
  15. data/lib/active_record/associations/collection_association.rb +55 -28
  16. data/lib/active_record/associations/collection_proxy.rb +1 -35
  17. data/lib/active_record/associations/has_many_association.rb +5 -1
  18. data/lib/active_record/associations/has_many_through_association.rb +11 -8
  19. data/lib/active_record/associations/join_dependency.rb +1 -1
  20. data/lib/active_record/associations/preloader/association.rb +3 -1
  21. data/lib/active_record/attribute_assignment.rb +221 -0
  22. data/lib/active_record/attribute_methods.rb +212 -32
  23. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +32 -0
  24. data/lib/active_record/attribute_methods/dirty.rb +3 -3
  25. data/lib/active_record/attribute_methods/primary_key.rb +62 -25
  26. data/lib/active_record/attribute_methods/read.rb +69 -80
  27. data/lib/active_record/attribute_methods/serialization.rb +89 -0
  28. data/lib/active_record/attribute_methods/time_zone_conversion.rb +9 -14
  29. data/lib/active_record/attribute_methods/write.rb +27 -5
  30. data/lib/active_record/autosave_association.rb +23 -8
  31. data/lib/active_record/base.rb +223 -1712
  32. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +98 -132
  33. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +82 -29
  34. data/lib/active_record/connection_adapters/abstract/database_statements.rb +13 -42
  35. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  36. data/lib/active_record/connection_adapters/abstract/quoting.rb +7 -4
  37. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +36 -25
  38. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +41 -13
  39. data/lib/active_record/connection_adapters/abstract_adapter.rb +78 -43
  40. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +653 -0
  41. data/lib/active_record/connection_adapters/mysql2_adapter.rb +138 -578
  42. data/lib/active_record/connection_adapters/mysql_adapter.rb +86 -658
  43. data/lib/active_record/connection_adapters/postgresql_adapter.rb +144 -94
  44. data/lib/active_record/connection_adapters/schema_cache.rb +50 -0
  45. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +2 -6
  46. data/lib/active_record/connection_adapters/sqlite_adapter.rb +43 -22
  47. data/lib/active_record/counter_cache.rb +1 -1
  48. data/lib/active_record/dynamic_matchers.rb +79 -0
  49. data/lib/active_record/errors.rb +11 -1
  50. data/lib/active_record/explain.rb +83 -0
  51. data/lib/active_record/explain_subscriber.rb +21 -0
  52. data/lib/active_record/fixtures.rb +31 -76
  53. data/lib/active_record/fixtures/file.rb +65 -0
  54. data/lib/active_record/identity_map.rb +1 -7
  55. data/lib/active_record/inheritance.rb +167 -0
  56. data/lib/active_record/integration.rb +49 -0
  57. data/lib/active_record/locking/optimistic.rb +19 -11
  58. data/lib/active_record/locking/pessimistic.rb +1 -1
  59. data/lib/active_record/log_subscriber.rb +3 -3
  60. data/lib/active_record/migration.rb +38 -29
  61. data/lib/active_record/migration/command_recorder.rb +7 -7
  62. data/lib/active_record/model_schema.rb +362 -0
  63. data/lib/active_record/nested_attributes.rb +3 -2
  64. data/lib/active_record/persistence.rb +51 -1
  65. data/lib/active_record/querying.rb +58 -0
  66. data/lib/active_record/railtie.rb +24 -28
  67. data/lib/active_record/railties/controller_runtime.rb +3 -1
  68. data/lib/active_record/railties/databases.rake +133 -77
  69. data/lib/active_record/readonly_attributes.rb +26 -0
  70. data/lib/active_record/reflection.rb +7 -15
  71. data/lib/active_record/relation.rb +78 -35
  72. data/lib/active_record/relation/batches.rb +5 -2
  73. data/lib/active_record/relation/calculations.rb +27 -6
  74. data/lib/active_record/relation/delegation.rb +49 -0
  75. data/lib/active_record/relation/finder_methods.rb +5 -4
  76. data/lib/active_record/relation/predicate_builder.rb +13 -16
  77. data/lib/active_record/relation/query_methods.rb +59 -4
  78. data/lib/active_record/result.rb +1 -1
  79. data/lib/active_record/sanitization.rb +194 -0
  80. data/lib/active_record/schema_dumper.rb +5 -2
  81. data/lib/active_record/scoping.rb +152 -0
  82. data/lib/active_record/scoping/default.rb +140 -0
  83. data/lib/active_record/scoping/named.rb +202 -0
  84. data/lib/active_record/serialization.rb +1 -43
  85. data/lib/active_record/serializers/xml_serializer.rb +2 -44
  86. data/lib/active_record/session_store.rb +11 -11
  87. data/lib/active_record/store.rb +50 -0
  88. data/lib/active_record/test_case.rb +11 -7
  89. data/lib/active_record/timestamp.rb +16 -3
  90. data/lib/active_record/transactions.rb +5 -5
  91. data/lib/active_record/translation.rb +22 -0
  92. data/lib/active_record/validations.rb +1 -1
  93. data/lib/active_record/validations/associated.rb +5 -4
  94. data/lib/active_record/validations/uniqueness.rb +4 -4
  95. data/lib/active_record/version.rb +3 -3
  96. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +1 -5
  97. metadata +48 -38
  98. checksums.yaml +0 -7
  99. data/lib/active_record/named_scope.rb +0 -200
@@ -1,11 +1,8 @@
1
- require 'active_record/connection_adapters/abstract_adapter'
2
- require 'active_support/core_ext/kernel/requires'
3
- require 'active_support/core_ext/object/blank'
4
- require 'set'
1
+ require 'active_record/connection_adapters/abstract_mysql_adapter'
5
2
  require 'active_record/connection_adapters/statement_pool'
6
- require 'arel/visitors/bind_visitor'
3
+ require 'active_support/core_ext/hash/keys'
7
4
 
8
- gem 'mysql', '~> 2.8'
5
+ gem 'mysql', '~> 2.8.1'
9
6
  require 'mysql'
10
7
 
11
8
  class Mysql
@@ -43,9 +40,29 @@ module ActiveRecord
43
40
  end
44
41
 
45
42
  module ConnectionAdapters
46
- class MysqlColumn < Column #:nodoc:
47
- class << self
48
- def string_to_time(value)
43
+ # The MySQL adapter will work with both Ruby/MySQL, which is a Ruby-based MySQL adapter that comes bundled with Active Record, and with
44
+ # the faster C-based MySQL/Ruby adapter (available both as a gem and from http://www.tmtm.org/en/mysql/ruby/).
45
+ #
46
+ # Options:
47
+ #
48
+ # * <tt>:host</tt> - Defaults to "localhost".
49
+ # * <tt>:port</tt> - Defaults to 3306.
50
+ # * <tt>:socket</tt> - Defaults to "/tmp/mysql.sock".
51
+ # * <tt>:username</tt> - Defaults to "root"
52
+ # * <tt>:password</tt> - Defaults to nothing.
53
+ # * <tt>:database</tt> - The name of the database. No default, must be provided.
54
+ # * <tt>:encoding</tt> - (Optional) Sets the client encoding by executing "SET NAMES <encoding>" after connection.
55
+ # * <tt>:reconnect</tt> - Defaults to false (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/auto-reconnect.html).
56
+ # * <tt>:sslca</tt> - Necessary to use MySQL with an SSL connection.
57
+ # * <tt>:sslkey</tt> - Necessary to use MySQL with an SSL connection.
58
+ # * <tt>:sslcert</tt> - Necessary to use MySQL with an SSL connection.
59
+ # * <tt>:sslcapath</tt> - Necessary to use MySQL with an SSL connection.
60
+ # * <tt>:sslcipher</tt> - Necessary to use MySQL with an SSL connection.
61
+ #
62
+ class MysqlAdapter < AbstractMysqlAdapter
63
+
64
+ class Column < AbstractMysqlAdapter::Column #:nodoc:
65
+ def self.string_to_time(value)
49
66
  return super unless Mysql::Time === value
50
67
  new_time(
51
68
  value.year,
@@ -57,135 +74,23 @@ module ActiveRecord
57
74
  value.second_part)
58
75
  end
59
76
 
60
- def string_to_dummy_time(v)
77
+ def self.string_to_dummy_time(v)
61
78
  return super unless Mysql::Time === v
62
79
  new_time(2000, 01, 01, v.hour, v.minute, v.second, v.second_part)
63
80
  end
64
81
 
65
- def string_to_date(v)
82
+ def self.string_to_date(v)
66
83
  return super unless Mysql::Time === v
67
84
  new_date(v.year, v.month, v.day)
68
85
  end
69
- end
70
86
 
71
- def extract_default(default)
72
- if sql_type =~ /blob/i || type == :text
73
- if default.blank?
74
- return null ? nil : ''
75
- else
76
- raise ArgumentError, "#{type} columns cannot have a default value: #{default.inspect}"
77
- end
78
- elsif missing_default_forged_as_empty_string?(default)
79
- nil
80
- else
81
- super
87
+ def adapter
88
+ MysqlAdapter
82
89
  end
83
90
  end
84
91
 
85
- def has_default?
86
- return false if sql_type =~ /blob/i || type == :text #mysql forbids defaults on blob and text columns
87
- super
88
- end
89
-
90
- private
91
- def simplified_type(field_type)
92
- return :boolean if MysqlAdapter.emulate_booleans && field_type.downcase.index("tinyint(1)")
93
- return :string if field_type =~ /enum/i
94
- super
95
- end
96
-
97
- def extract_limit(sql_type)
98
- case sql_type
99
- when /blob|text/i
100
- case sql_type
101
- when /tiny/i
102
- 255
103
- when /medium/i
104
- 16777215
105
- when /long/i
106
- 2147483647 # mysql only allows 2^31-1, not 2^32-1, somewhat inconsistently with the tiny/medium/normal cases
107
- else
108
- super # we could return 65535 here, but we leave it undecorated by default
109
- end
110
- when /^bigint/i; 8
111
- when /^int/i; 4
112
- when /^mediumint/i; 3
113
- when /^smallint/i; 2
114
- when /^tinyint/i; 1
115
- else
116
- super
117
- end
118
- end
119
-
120
- # MySQL misreports NOT NULL column default when none is given.
121
- # We can't detect this for columns which may have a legitimate ''
122
- # default (string) but we can for others (integer, datetime, boolean,
123
- # and the rest).
124
- #
125
- # Test whether the column has default '', is not null, and is not
126
- # a type allowing default ''.
127
- def missing_default_forged_as_empty_string?(default)
128
- type != :string && !null && default == ''
129
- end
130
- end
131
-
132
- # The MySQL adapter will work with both Ruby/MySQL, which is a Ruby-based MySQL adapter that comes bundled with Active Record, and with
133
- # the faster C-based MySQL/Ruby adapter (available both as a gem and from http://www.tmtm.org/en/mysql/ruby/).
134
- #
135
- # Options:
136
- #
137
- # * <tt>:host</tt> - Defaults to "localhost".
138
- # * <tt>:port</tt> - Defaults to 3306.
139
- # * <tt>:socket</tt> - Defaults to "/tmp/mysql.sock".
140
- # * <tt>:username</tt> - Defaults to "root"
141
- # * <tt>:password</tt> - Defaults to nothing.
142
- # * <tt>:database</tt> - The name of the database. No default, must be provided.
143
- # * <tt>:encoding</tt> - (Optional) Sets the client encoding by executing "SET NAMES <encoding>" after connection.
144
- # * <tt>:reconnect</tt> - Defaults to false (See MySQL documentation: http://dev.mysql.com/doc/refman/5.0/en/auto-reconnect.html).
145
- # * <tt>:sslca</tt> - Necessary to use MySQL with an SSL connection.
146
- # * <tt>:sslkey</tt> - Necessary to use MySQL with an SSL connection.
147
- # * <tt>:sslcert</tt> - Necessary to use MySQL with an SSL connection.
148
- # * <tt>:sslcapath</tt> - Necessary to use MySQL with an SSL connection.
149
- # * <tt>:sslcipher</tt> - Necessary to use MySQL with an SSL connection.
150
- #
151
- class MysqlAdapter < AbstractAdapter
152
-
153
- ##
154
- # :singleton-method:
155
- # By default, the MysqlAdapter will consider all columns of type <tt>tinyint(1)</tt>
156
- # as boolean. If you wish to disable this emulation (which was the default
157
- # behavior in versions 0.13.1 and earlier) you can add the following line
158
- # to your application.rb file:
159
- #
160
- # ActiveRecord::ConnectionAdapters::MysqlAdapter.emulate_booleans = false
161
- cattr_accessor :emulate_booleans
162
- self.emulate_booleans = true
163
-
164
92
  ADAPTER_NAME = 'MySQL'
165
93
 
166
- LOST_CONNECTION_ERROR_MESSAGES = [
167
- "Server shutdown in progress",
168
- "Broken pipe",
169
- "Lost connection to MySQL server during query",
170
- "MySQL server has gone away" ]
171
-
172
- QUOTED_TRUE, QUOTED_FALSE = '1', '0'
173
-
174
- NATIVE_DATABASE_TYPES = {
175
- :primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY",
176
- :string => { :name => "varchar", :limit => 255 },
177
- :text => { :name => "text" },
178
- :integer => { :name => "int", :limit => 4 },
179
- :float => { :name => "float" },
180
- :decimal => { :name => "decimal" },
181
- :datetime => { :name => "datetime" },
182
- :timestamp => { :name => "datetime" },
183
- :time => { :name => "time" },
184
- :date => { :name => "date" },
185
- :binary => { :name => "blob" },
186
- :boolean => { :name => "tinyint", :limit => 1 }
187
- }
188
-
189
94
  class StatementPool < ConnectionAdapters::StatementPool
190
95
  def initialize(connection, max = 1000)
191
96
  super
@@ -219,114 +124,52 @@ module ActiveRecord
219
124
  end
220
125
 
221
126
  def initialize(connection, logger, connection_options, config)
222
- super(connection, logger)
223
- @connection_options, @config = connection_options, config
224
- @quoted_column_names, @quoted_table_names = {}, {}
225
- @statements = {}
127
+ super
226
128
  @statements = StatementPool.new(@connection,
227
129
  config.fetch(:statement_limit) { 1000 })
228
130
  @client_encoding = nil
229
131
  connect
230
132
  end
231
133
 
232
- class BindSubstitution < Arel::Visitors::MySQL # :nodoc:
233
- include Arel::Visitors::BindVisitor
234
- end
235
-
236
- def self.visitor_for(pool) # :nodoc:
237
- config = pool.spec.config
238
-
239
- if config.fetch(:prepared_statements) { true }
240
- Arel::Visitors::MySQL.new pool
241
- else
242
- BindSubstitution.new pool
243
- end
244
- end
245
-
246
- def adapter_name #:nodoc:
247
- ADAPTER_NAME
248
- end
249
-
250
- def supports_bulk_alter? #:nodoc:
251
- true
252
- end
253
-
254
134
  # Returns true, since this connection adapter supports prepared statement
255
135
  # caching.
256
136
  def supports_statement_cache?
257
137
  true
258
138
  end
259
139
 
260
- # Returns true, since this connection adapter supports migrations.
261
- def supports_migrations? #:nodoc:
262
- true
263
- end
140
+ # HELPER METHODS ===========================================
264
141
 
265
- # Returns true.
266
- def supports_primary_key? #:nodoc:
267
- true
142
+ def each_hash(result) # :nodoc:
143
+ if block_given?
144
+ result.each_hash do |row|
145
+ row.symbolize_keys!
146
+ yield row
147
+ end
148
+ else
149
+ to_enum(:each_hash, result)
150
+ end
268
151
  end
269
152
 
270
- # Returns true, since this connection adapter supports savepoints.
271
- def supports_savepoints? #:nodoc:
272
- true
153
+ def new_column(field, default, type, null, collation) # :nodoc:
154
+ Column.new(field, default, type, null, collation)
273
155
  end
274
156
 
275
- def native_database_types #:nodoc:
276
- NATIVE_DATABASE_TYPES
157
+ def error_number(exception) # :nodoc:
158
+ exception.errno if exception.respond_to?(:errno)
277
159
  end
278
160
 
279
-
280
161
  # QUOTING ==================================================
281
162
 
282
- def quote(value, column = nil)
283
- if value.kind_of?(String) && column && column.type == :binary && column.class.respond_to?(:string_to_binary)
284
- s = column.class.string_to_binary(value).unpack("H*")[0]
285
- "x'#{s}'"
286
- else
287
- super
288
- end
289
- end
290
-
291
163
  def type_cast(value, column)
292
164
  return super unless value == true || value == false
293
165
 
294
166
  value ? 1 : 0
295
167
  end
296
168
 
297
- def quote_column_name(name) #:nodoc:
298
- @quoted_column_names[name] ||= "`#{name.to_s.gsub('`', '``')}`"
299
- end
300
-
301
- def quote_table_name(name) #:nodoc:
302
- @quoted_table_names[name] ||= quote_column_name(name).gsub('.', '`.`')
303
- end
304
-
305
169
  def quote_string(string) #:nodoc:
306
170
  @connection.quote(string)
307
171
  end
308
172
 
309
- def quoted_true
310
- QUOTED_TRUE
311
- end
312
-
313
- def quoted_false
314
- QUOTED_FALSE
315
- end
316
-
317
- # REFERENTIAL INTEGRITY ====================================
318
-
319
- def disable_referential_integrity #:nodoc:
320
- old = select_value("SELECT @@FOREIGN_KEY_CHECKS")
321
-
322
- begin
323
- update("SET FOREIGN_KEY_CHECKS = 0")
324
- yield
325
- ensure
326
- update("SET FOREIGN_KEY_CHECKS = #{old}")
327
- end
328
- end
329
-
330
173
  # CONNECTION MANAGEMENT ====================================
331
174
 
332
175
  def active?
@@ -452,7 +295,7 @@ module ActiveRecord
452
295
 
453
296
  def exec_without_stmt(sql, name = 'SQL') # :nodoc:
454
297
  # Some queries, like SHOW CREATE TABLE don't work through the prepared
455
- # statement API. For those queries, we need to use this method. :'(
298
+ # statement API. For those queries, we need to use this method. :'(
456
299
  log(sql, name) do
457
300
  result = @connection.query(sql)
458
301
  cols = []
@@ -467,20 +310,11 @@ module ActiveRecord
467
310
  end
468
311
  end
469
312
 
470
- # Executes an SQL query and returns a MySQL::Result object. Note that you have to free
471
- # the Result object after you're done using it.
472
- def execute(sql, name = nil) #:nodoc:
473
- if name == :skip_logging
474
- @connection.query(sql)
475
- else
476
- log(sql, name) { @connection.query(sql) }
477
- end
478
- rescue ActiveRecord::StatementInvalid => exception
479
- if exception.message.split(":").first =~ /Packets out of order/
480
- raise ActiveRecord::StatementInvalid, "'Packets out of order' error was received from the database. Please update your mysql bindings (gem install mysql) and read http://dev.mysql.com/doc/mysql/en/password-hashing.html for more information. If you're on Windows, use the Instant Rails installer to get the updated mysql bindings."
481
- else
482
- raise
483
- end
313
+ def execute_and_free(sql, name = nil)
314
+ result = execute(sql, name)
315
+ ret = yield result
316
+ result.free
317
+ ret
484
318
  end
485
319
 
486
320
  def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) #:nodoc:
@@ -489,11 +323,6 @@ module ActiveRecord
489
323
  end
490
324
  alias :create :insert_sql
491
325
 
492
- def update_sql(sql, name = nil) #:nodoc:
493
- super
494
- @connection.affected_rows
495
- end
496
-
497
326
  def exec_delete(sql, name, binds)
498
327
  log(sql, name, binds) do
499
328
  exec_stmt(sql, name, binds) do |cols, stmt|
@@ -509,397 +338,8 @@ module ActiveRecord
509
338
  # Transactions aren't supported
510
339
  end
511
340
 
512
- def commit_db_transaction #:nodoc:
513
- execute "COMMIT"
514
- rescue Exception
515
- # Transactions aren't supported
516
- end
517
-
518
- def rollback_db_transaction #:nodoc:
519
- execute "ROLLBACK"
520
- rescue Exception
521
- # Transactions aren't supported
522
- end
523
-
524
- def create_savepoint
525
- execute("SAVEPOINT #{current_savepoint_name}")
526
- end
527
-
528
- def rollback_to_savepoint
529
- execute("ROLLBACK TO SAVEPOINT #{current_savepoint_name}")
530
- end
531
-
532
- def release_savepoint
533
- execute("RELEASE SAVEPOINT #{current_savepoint_name}")
534
- end
535
-
536
- def add_limit_offset!(sql, options) #:nodoc:
537
- limit, offset = options[:limit], options[:offset]
538
- if limit && offset
539
- sql << " LIMIT #{offset.to_i}, #{sanitize_limit(limit)}"
540
- elsif limit
541
- sql << " LIMIT #{sanitize_limit(limit)}"
542
- elsif offset
543
- sql << " OFFSET #{offset.to_i}"
544
- end
545
- sql
546
- end
547
- deprecate :add_limit_offset!
548
-
549
- # In the simple case, MySQL allows us to place JOINs directly into the UPDATE
550
- # query. However, this does not allow for LIMIT, OFFSET and ORDER. To support
551
- # these, we must use a subquery. However, MySQL is too stupid to create a
552
- # temporary table for this automatically, so we have to give it some prompting
553
- # in the form of a subsubquery. Ugh!
554
- def join_to_update(update, select) #:nodoc:
555
- if select.limit || select.offset || select.orders.any?
556
- subsubselect = select.clone
557
- subsubselect.projections = [update.key]
558
-
559
- subselect = Arel::SelectManager.new(select.engine)
560
- subselect.project Arel.sql(update.key.name)
561
- subselect.from subsubselect.as('__active_record_temp')
562
-
563
- update.where update.key.in(subselect)
564
- else
565
- update.table select.source
566
- update.wheres = select.constraints
567
- end
568
- end
569
-
570
- # SCHEMA STATEMENTS ========================================
571
-
572
- def structure_dump #:nodoc:
573
- if supports_views?
574
- sql = "SHOW FULL TABLES WHERE Table_type = 'BASE TABLE'"
575
- else
576
- sql = "SHOW TABLES"
577
- end
578
-
579
- select_all(sql).map do |table|
580
- table.delete('Table_type')
581
- sql = "SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}"
582
- exec_without_stmt(sql).first['Create Table'] + ";\n\n"
583
- end.join("")
584
- end
585
-
586
- # Drops the database specified on the +name+ attribute
587
- # and creates it again using the provided +options+.
588
- def recreate_database(name, options = {}) #:nodoc:
589
- drop_database(name)
590
- create_database(name, options)
591
- end
592
-
593
- # Create a new MySQL database with optional <tt>:charset</tt> and <tt>:collation</tt>.
594
- # Charset defaults to utf8.
595
- #
596
- # Example:
597
- # create_database 'charset_test', :charset => 'latin1', :collation => 'latin1_bin'
598
- # create_database 'matt_development'
599
- # create_database 'matt_development', :charset => :big5
600
- def create_database(name, options = {})
601
- if options[:collation]
602
- execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}` COLLATE `#{options[:collation]}`"
603
- else
604
- execute "CREATE DATABASE `#{name}` DEFAULT CHARACTER SET `#{options[:charset] || 'utf8'}`"
605
- end
606
- end
607
-
608
- # Drops a MySQL database.
609
- #
610
- # Example:
611
- # drop_database 'sebastian_development'
612
- def drop_database(name) #:nodoc:
613
- execute "DROP DATABASE IF EXISTS `#{name}`"
614
- end
615
-
616
- def current_database
617
- select_value 'SELECT DATABASE() as db'
618
- end
619
-
620
- # Returns the database character set.
621
- def charset
622
- show_variable 'character_set_database'
623
- end
624
-
625
- # Returns the database collation strategy.
626
- def collation
627
- show_variable 'collation_database'
628
- end
629
-
630
- def tables(name = nil, database = nil) #:nodoc:
631
- sql = "SHOW TABLES "
632
- sql << "IN #{quote_table_name(database)} " if database
633
-
634
- result = execute(sql, 'SCHEMA')
635
- tables = result.collect { |field| field[0] }
636
- result.free
637
- tables
638
- end
639
-
640
- def table_exists?(name)
641
- return true if super
642
-
643
- name = name.to_s
644
- schema, table = name.split('.', 2)
645
-
646
- unless table # A table was provided without a schema
647
- table = schema
648
- schema = nil
649
- end
650
-
651
- tables(nil, schema).include? table
652
- end
653
-
654
- def drop_table(table_name, options = {})
655
- super(table_name, options)
656
- end
657
-
658
- # Returns an array of indexes for the given table.
659
- def indexes(table_name, name = nil)#:nodoc:
660
- indexes = []
661
- current_index = nil
662
- result = execute("SHOW KEYS FROM #{quote_table_name(table_name)}", name)
663
- result.each do |row|
664
- if current_index != row[2]
665
- next if row[2] == "PRIMARY" # skip the primary key
666
- current_index = row[2]
667
- indexes << IndexDefinition.new(row[0], row[2], row[1] == "0", [], [])
668
- end
669
-
670
- indexes.last.columns << row[4]
671
- indexes.last.lengths << row[7]
672
- end
673
- result.free
674
- indexes
675
- end
676
-
677
- # Returns an array of +MysqlColumn+ objects for the table specified by +table_name+.
678
- def columns(table_name, name = nil)#:nodoc:
679
- sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}"
680
- result = execute(sql, 'SCHEMA')
681
- columns = result.collect { |field| MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") }
682
- result.free
683
- columns
684
- end
685
-
686
- def create_table(table_name, options = {}) #:nodoc:
687
- super(table_name, options.reverse_merge(:options => "ENGINE=InnoDB"))
688
- end
689
-
690
- # Renames a table.
691
- #
692
- # Example:
693
- # rename_table('octopuses', 'octopi')
694
- def rename_table(table_name, new_name)
695
- execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
696
- end
697
-
698
- def bulk_change_table(table_name, operations) #:nodoc:
699
- sqls = operations.map do |command, args|
700
- table, arguments = args.shift, args
701
- method = :"#{command}_sql"
702
-
703
- if respond_to?(method, true)
704
- send(method, table, *arguments)
705
- else
706
- raise "Unknown method called : #{method}(#{arguments.inspect})"
707
- end
708
- end.flatten.join(", ")
709
-
710
- execute("ALTER TABLE #{quote_table_name(table_name)} #{sqls}")
711
- end
712
-
713
- def add_column(table_name, column_name, type, options = {})
714
- execute("ALTER TABLE #{quote_table_name(table_name)} #{add_column_sql(table_name, column_name, type, options)}")
715
- end
716
-
717
- def change_column_default(table_name, column_name, default) #:nodoc:
718
- column = column_for(table_name, column_name)
719
- change_column table_name, column_name, column.sql_type, :default => default
720
- end
721
-
722
- def change_column_null(table_name, column_name, null, default = nil)
723
- column = column_for(table_name, column_name)
724
-
725
- unless null || default.nil?
726
- execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
727
- end
728
-
729
- change_column table_name, column_name, column.sql_type, :null => null
730
- end
731
-
732
- def change_column(table_name, column_name, type, options = {}) #:nodoc:
733
- execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_sql(table_name, column_name, type, options)}")
734
- end
735
-
736
- def rename_column(table_name, column_name, new_column_name) #:nodoc:
737
- execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_sql(table_name, column_name, new_column_name)}")
738
- end
739
-
740
- # Maps logical Rails types to MySQL-specific data types.
741
- def type_to_sql(type, limit = nil, precision = nil, scale = nil)
742
- case type.to_s
743
- when 'integer'
744
- case limit
745
- when 1; 'tinyint'
746
- when 2; 'smallint'
747
- when 3; 'mediumint'
748
- when nil, 4, 11; 'int(11)' # compatibility with MySQL default
749
- when 5..8; 'bigint'
750
- else raise(ActiveRecordError, "No integer type has byte size #{limit}")
751
- end
752
- when 'text'
753
- case limit
754
- when 0..0xff; 'tinytext'
755
- when nil, 0x100..0xffff; 'text'
756
- when 0x10000..0xffffff; 'mediumtext'
757
- when 0x1000000..0xffffffff; 'longtext'
758
- else raise(ActiveRecordError, "No text type has character length #{limit}")
759
- end
760
- else
761
- super
762
- end
763
- end
764
-
765
- def add_column_position!(sql, options)
766
- if options[:first]
767
- sql << " FIRST"
768
- elsif options[:after]
769
- sql << " AFTER #{quote_column_name(options[:after])}"
770
- end
771
- end
772
-
773
- # SHOW VARIABLES LIKE 'name'
774
- def show_variable(name)
775
- variables = select_all("SHOW VARIABLES LIKE '#{name}'")
776
- variables.first['Value'] unless variables.empty?
777
- end
778
-
779
- # Returns a table's primary key and belonging sequence.
780
- def pk_and_sequence_for(table) #:nodoc:
781
- result = execute("SHOW CREATE TABLE #{quote_table_name(table)}", 'SCHEMA')
782
- create_table = result.fetch_hash["Create Table"]
783
- result.free
784
-
785
- if create_table.to_s =~ /PRIMARY KEY\s+\((.+)\)/
786
- keys = $1.split(",").map { |key| key.gsub(/[`"]/, "") }
787
- keys.length == 1 ? [keys.first, nil] : nil
788
- else
789
- nil
790
- end
791
- end
792
-
793
- # Returns just a table's primary key
794
- def primary_key(table)
795
- pk_and_sequence = pk_and_sequence_for(table)
796
- pk_and_sequence && pk_and_sequence.first
797
- end
798
-
799
- def case_sensitive_equality_operator
800
- "= BINARY"
801
- end
802
- deprecate :case_sensitive_equality_operator
803
-
804
- def case_sensitive_modifier(node)
805
- Arel::Nodes::Bin.new(node)
806
- end
807
-
808
- def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
809
- where_sql
810
- end
811
-
812
- protected
813
- def quoted_columns_for_index(column_names, options = {})
814
- length = options[:length] if options.is_a?(Hash)
815
-
816
- case length
817
- when Hash
818
- column_names.map {|name| length[name] ? "#{quote_column_name(name)}(#{length[name]})" : quote_column_name(name) }
819
- when Fixnum
820
- column_names.map {|name| "#{quote_column_name(name)}(#{length})"}
821
- else
822
- column_names.map {|name| quote_column_name(name) }
823
- end
824
- end
825
-
826
- def translate_exception(exception, message)
827
- return super unless exception.respond_to?(:errno)
828
-
829
- case exception.errno
830
- when 1062
831
- RecordNotUnique.new(message, exception)
832
- when 1452
833
- InvalidForeignKey.new(message, exception)
834
- else
835
- super
836
- end
837
- end
838
-
839
- def add_column_sql(table_name, column_name, type, options = {})
840
- add_column_sql = "ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
841
- add_column_options!(add_column_sql, options)
842
- add_column_position!(add_column_sql, options)
843
- add_column_sql
844
- end
845
-
846
- def remove_column_sql(table_name, *column_names)
847
- columns_for_remove(table_name, *column_names).map {|column_name| "DROP #{column_name}" }
848
- end
849
- alias :remove_columns_sql :remove_column
850
-
851
- def change_column_sql(table_name, column_name, type, options = {})
852
- column = column_for(table_name, column_name)
853
-
854
- unless options_include_default?(options)
855
- options[:default] = column.default
856
- end
857
-
858
- unless options.has_key?(:null)
859
- options[:null] = column.null
860
- end
861
-
862
- change_column_sql = "CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
863
- add_column_options!(change_column_sql, options)
864
- add_column_position!(change_column_sql, options)
865
- change_column_sql
866
- end
867
-
868
- def rename_column_sql(table_name, column_name, new_column_name)
869
- options = {}
870
-
871
- if column = columns(table_name).find { |c| c.name == column_name.to_s }
872
- options[:default] = column.default
873
- options[:null] = column.null
874
- else
875
- raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
876
- end
877
-
878
- current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
879
- rename_column_sql = "CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
880
- add_column_options!(rename_column_sql, options)
881
- rename_column_sql
882
- end
883
-
884
- def add_index_sql(table_name, column_name, options = {})
885
- index_name, index_type, index_columns = add_index_options(table_name, column_name, options)
886
- "ADD #{index_type} INDEX #{index_name} (#{index_columns})"
887
- end
888
-
889
- def remove_index_sql(table_name, options = {})
890
- index_name = index_name_for_remove(table_name, options)
891
- "DROP INDEX #{index_name}"
892
- end
893
-
894
- def add_timestamps_sql(table_name)
895
- [add_column_sql(table_name, :created_at, :datetime), add_column_sql(table_name, :updated_at, :datetime)]
896
- end
897
-
898
- def remove_timestamps_sql(table_name)
899
- [remove_column_sql(table_name, :updated_at), remove_column_sql(table_name, :created_at)]
900
- end
901
-
902
341
  private
342
+
903
343
  def exec_stmt(sql, name, binds)
904
344
  cache = {}
905
345
  if binds.empty?
@@ -911,12 +351,11 @@ module ActiveRecord
911
351
  stmt = cache[:stmt]
912
352
  end
913
353
 
914
-
915
354
  begin
916
355
  stmt.execute(*binds.map { |col, val| type_cast(val, col) })
917
356
  rescue Mysql::Error => e
918
357
  # Older versions of MySQL leave the prepared statement in a bad
919
- # place when an error occurs. To support older mysql versions, we
358
+ # place when an error occurs. To support older mysql versions, we
920
359
  # need to close the statement and delete the statement from the
921
360
  # cache.
922
361
  stmt.close
@@ -940,59 +379,48 @@ module ActiveRecord
940
379
  result
941
380
  end
942
381
 
943
- def connect
944
- encoding = @config[:encoding]
945
- if encoding
946
- @connection.options(Mysql::SET_CHARSET_NAME, encoding) rescue nil
947
- end
948
-
949
- if @config[:sslca] || @config[:sslkey]
950
- @connection.ssl_set(@config[:sslkey], @config[:sslcert], @config[:sslca], @config[:sslcapath], @config[:sslcipher])
951
- end
952
-
953
- @connection.options(Mysql::OPT_CONNECT_TIMEOUT, @config[:connect_timeout]) if @config[:connect_timeout]
954
- @connection.options(Mysql::OPT_READ_TIMEOUT, @config[:read_timeout]) if @config[:read_timeout]
955
- @connection.options(Mysql::OPT_WRITE_TIMEOUT, @config[:write_timeout]) if @config[:write_timeout]
382
+ def connect
383
+ encoding = @config[:encoding]
384
+ if encoding
385
+ @connection.options(Mysql::SET_CHARSET_NAME, encoding) rescue nil
386
+ end
956
387
 
957
- @connection.real_connect(*@connection_options)
388
+ if @config[:sslca] || @config[:sslkey]
389
+ @connection.ssl_set(@config[:sslkey], @config[:sslcert], @config[:sslca], @config[:sslcapath], @config[:sslcipher])
390
+ end
958
391
 
959
- # reconnect must be set after real_connect is called, because real_connect sets it to false internally
960
- @connection.reconnect = !!@config[:reconnect] if @connection.respond_to?(:reconnect=)
392
+ @connection.options(Mysql::OPT_CONNECT_TIMEOUT, @config[:connect_timeout]) if @config[:connect_timeout]
393
+ @connection.options(Mysql::OPT_READ_TIMEOUT, @config[:read_timeout]) if @config[:read_timeout]
394
+ @connection.options(Mysql::OPT_WRITE_TIMEOUT, @config[:write_timeout]) if @config[:write_timeout]
961
395
 
962
- configure_connection
963
- end
396
+ @connection.real_connect(*@connection_options)
964
397
 
965
- def configure_connection
966
- encoding = @config[:encoding]
967
- execute("SET NAMES '#{encoding}'", :skip_logging) if encoding
398
+ # reconnect must be set after real_connect is called, because real_connect sets it to false internally
399
+ @connection.reconnect = !!@config[:reconnect] if @connection.respond_to?(:reconnect=)
968
400
 
969
- # By default, MySQL 'where id is null' selects the last inserted id.
970
- # Turn this off. http://dev.rubyonrails.org/ticket/6778
971
- execute("SET SQL_AUTO_IS_NULL=0", :skip_logging)
972
- end
401
+ configure_connection
402
+ end
973
403
 
974
- def select(sql, name = nil, binds = [])
975
- @connection.query_with_result = true
976
- rows = exec_query(sql, name, binds).to_a
977
- @connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
978
- rows
979
- end
404
+ def configure_connection
405
+ encoding = @config[:encoding]
406
+ execute("SET NAMES '#{encoding}'", :skip_logging) if encoding
980
407
 
981
- def supports_views?
982
- version[0] >= 5
983
- end
408
+ # By default, MySQL 'where id is null' selects the last inserted id.
409
+ # Turn this off. http://dev.rubyonrails.org/ticket/6778
410
+ execute("SET SQL_AUTO_IS_NULL=0", :skip_logging)
411
+ end
984
412
 
985
- # Returns the version of the connected MySQL server.
986
- def version
987
- @version ||= @connection.server_info.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
988
- end
413
+ def select(sql, name = nil, binds = [])
414
+ @connection.query_with_result = true
415
+ rows = exec_query(sql, name, binds).to_a
416
+ @connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
417
+ rows
418
+ end
989
419
 
990
- def column_for(table_name, column_name)
991
- unless column = columns(table_name).find { |c| c.name == column_name.to_s }
992
- raise "No such column: #{table_name}.#{column_name}"
993
- end
994
- column
995
- end
420
+ # Returns the version of the connected MySQL server.
421
+ def version
422
+ @version ||= @connection.server_info.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
423
+ end
996
424
  end
997
425
  end
998
426
  end