activerecord 3.0.20 → 3.1.0.beta1
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.
- data/CHANGELOG +220 -91
- data/README.rdoc +3 -3
- data/examples/performance.rb +88 -109
- data/lib/active_record.rb +6 -2
- data/lib/active_record/aggregations.rb +22 -45
- data/lib/active_record/associations.rb +264 -991
- data/lib/active_record/associations/alias_tracker.rb +85 -0
- data/lib/active_record/associations/association.rb +231 -0
- data/lib/active_record/associations/association_scope.rb +120 -0
- data/lib/active_record/associations/belongs_to_association.rb +40 -60
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +15 -63
- data/lib/active_record/associations/builder/association.rb +53 -0
- data/lib/active_record/associations/builder/belongs_to.rb +85 -0
- data/lib/active_record/associations/builder/collection_association.rb +75 -0
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +63 -0
- data/lib/active_record/associations/builder/has_many.rb +65 -0
- data/lib/active_record/associations/builder/has_one.rb +63 -0
- data/lib/active_record/associations/builder/singular_association.rb +32 -0
- data/lib/active_record/associations/collection_association.rb +524 -0
- data/lib/active_record/associations/collection_proxy.rb +125 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +27 -118
- data/lib/active_record/associations/has_many_association.rb +50 -79
- data/lib/active_record/associations/has_many_through_association.rb +98 -67
- data/lib/active_record/associations/has_one_association.rb +45 -115
- data/lib/active_record/associations/has_one_through_association.rb +21 -25
- data/lib/active_record/associations/join_dependency.rb +215 -0
- data/lib/active_record/associations/join_dependency/join_association.rb +150 -0
- data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
- data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
- data/lib/active_record/associations/join_helper.rb +56 -0
- data/lib/active_record/associations/preloader.rb +177 -0
- data/lib/active_record/associations/preloader/association.rb +126 -0
- data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
- data/lib/active_record/associations/preloader/collection_association.rb +24 -0
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
- data/lib/active_record/associations/preloader/has_many.rb +17 -0
- data/lib/active_record/associations/preloader/has_many_through.rb +15 -0
- data/lib/active_record/associations/preloader/has_one.rb +23 -0
- data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
- data/lib/active_record/associations/preloader/singular_association.rb +21 -0
- data/lib/active_record/associations/preloader/through_association.rb +67 -0
- data/lib/active_record/associations/singular_association.rb +55 -0
- data/lib/active_record/associations/through_association.rb +80 -0
- data/lib/active_record/attribute_methods.rb +19 -5
- data/lib/active_record/attribute_methods/before_type_cast.rb +9 -8
- data/lib/active_record/attribute_methods/dirty.rb +8 -2
- data/lib/active_record/attribute_methods/primary_key.rb +33 -13
- data/lib/active_record/attribute_methods/read.rb +17 -17
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +7 -4
- data/lib/active_record/attribute_methods/write.rb +2 -1
- data/lib/active_record/autosave_association.rb +66 -45
- data/lib/active_record/base.rb +445 -273
- data/lib/active_record/callbacks.rb +24 -33
- data/lib/active_record/coders/yaml_column.rb +41 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +106 -13
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +16 -2
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +12 -11
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +83 -12
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +16 -16
- data/lib/active_record/connection_adapters/abstract/quoting.rb +61 -22
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +16 -273
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +80 -42
- data/lib/active_record/connection_adapters/abstract_adapter.rb +44 -25
- data/lib/active_record/connection_adapters/column.rb +268 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +686 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +331 -88
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +295 -267
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +3 -7
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +108 -26
- data/lib/active_record/counter_cache.rb +7 -4
- data/lib/active_record/fixtures.rb +174 -192
- data/lib/active_record/identity_map.rb +131 -0
- data/lib/active_record/locking/optimistic.rb +20 -14
- data/lib/active_record/locking/pessimistic.rb +4 -4
- data/lib/active_record/log_subscriber.rb +24 -4
- data/lib/active_record/migration.rb +265 -144
- data/lib/active_record/migration/command_recorder.rb +103 -0
- data/lib/active_record/named_scope.rb +68 -25
- data/lib/active_record/nested_attributes.rb +58 -15
- data/lib/active_record/observer.rb +3 -7
- data/lib/active_record/persistence.rb +58 -38
- data/lib/active_record/query_cache.rb +25 -3
- data/lib/active_record/railtie.rb +21 -12
- data/lib/active_record/railties/console_sandbox.rb +6 -0
- data/lib/active_record/railties/databases.rake +147 -116
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/reflection.rb +176 -44
- data/lib/active_record/relation.rb +125 -49
- data/lib/active_record/relation/batches.rb +7 -5
- data/lib/active_record/relation/calculations.rb +50 -18
- data/lib/active_record/relation/finder_methods.rb +47 -26
- data/lib/active_record/relation/predicate_builder.rb +24 -21
- data/lib/active_record/relation/query_methods.rb +117 -101
- data/lib/active_record/relation/spawn_methods.rb +27 -20
- data/lib/active_record/result.rb +34 -0
- data/lib/active_record/schema.rb +5 -6
- data/lib/active_record/schema_dumper.rb +11 -13
- data/lib/active_record/serialization.rb +2 -2
- data/lib/active_record/serializers/xml_serializer.rb +10 -10
- data/lib/active_record/session_store.rb +8 -2
- data/lib/active_record/test_case.rb +9 -20
- data/lib/active_record/timestamp.rb +21 -9
- data/lib/active_record/transactions.rb +16 -15
- data/lib/active_record/validations.rb +21 -22
- data/lib/active_record/validations/associated.rb +3 -1
- data/lib/active_record/validations/uniqueness.rb +48 -58
- data/lib/active_record/version.rb +3 -3
- data/lib/rails/generators/active_record.rb +6 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +10 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +2 -1
- data/lib/rails/generators/active_record/model/templates/migration.rb +6 -5
- data/lib/rails/generators/active_record/model/templates/model.rb +2 -0
- data/lib/rails/generators/active_record/model/templates/module.rb +2 -0
- data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +2 -1
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +2 -2
- metadata +106 -77
- checksums.yaml +0 -7
- data/lib/active_record/association_preload.rb +0 -431
- data/lib/active_record/associations/association_collection.rb +0 -572
- data/lib/active_record/associations/association_proxy.rb +0 -304
- data/lib/active_record/associations/through_association_scope.rb +0 -160
@@ -3,6 +3,21 @@ require 'active_support/core_ext/kernel/requires'
|
|
3
3
|
require 'active_support/core_ext/object/blank'
|
4
4
|
require 'set'
|
5
5
|
|
6
|
+
gem 'mysql', '~> 2.8.1'
|
7
|
+
require 'mysql'
|
8
|
+
|
9
|
+
class Mysql
|
10
|
+
class Time
|
11
|
+
###
|
12
|
+
# This monkey patch is for test_additional_columns_from_join_table
|
13
|
+
def to_date
|
14
|
+
Date.new(year, month, day)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
class Stmt; include Enumerable end
|
18
|
+
class Result; include Enumerable end
|
19
|
+
end
|
20
|
+
|
6
21
|
module ActiveRecord
|
7
22
|
class Base
|
8
23
|
# Establishes a connection to the database that's used by all Active Record objects.
|
@@ -15,18 +30,6 @@ module ActiveRecord
|
|
15
30
|
password = config[:password].to_s
|
16
31
|
database = config[:database]
|
17
32
|
|
18
|
-
unless defined? Mysql
|
19
|
-
begin
|
20
|
-
require 'mysql'
|
21
|
-
rescue LoadError
|
22
|
-
raise "!!! Missing the mysql2 gem. Add it to your Gemfile: gem 'mysql2'"
|
23
|
-
end
|
24
|
-
|
25
|
-
unless defined?(Mysql::Result) && Mysql::Result.method_defined?(:each_hash)
|
26
|
-
raise "!!! Outdated mysql gem. Upgrade to 2.8.1 or later. In your Gemfile: gem 'mysql', '2.8.1'. Or use gem 'mysql2'"
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
33
|
mysql = Mysql.init
|
31
34
|
mysql.ssl_set(config[:sslkey], config[:sslcert], config[:sslca], config[:sslcapath], config[:sslcipher]) if config[:sslca] || config[:sslkey]
|
32
35
|
|
@@ -39,6 +42,30 @@ module ActiveRecord
|
|
39
42
|
|
40
43
|
module ConnectionAdapters
|
41
44
|
class MysqlColumn < Column #:nodoc:
|
45
|
+
class << self
|
46
|
+
def string_to_time(value)
|
47
|
+
return super unless Mysql::Time === value
|
48
|
+
new_time(
|
49
|
+
value.year,
|
50
|
+
value.month,
|
51
|
+
value.day,
|
52
|
+
value.hour,
|
53
|
+
value.minute,
|
54
|
+
value.second,
|
55
|
+
value.second_part)
|
56
|
+
end
|
57
|
+
|
58
|
+
def string_to_dummy_time(v)
|
59
|
+
return super unless Mysql::Time === v
|
60
|
+
new_time(2000, 01, 01, v.hour, v.minute, v.second, v.second_part)
|
61
|
+
end
|
62
|
+
|
63
|
+
def string_to_date(v)
|
64
|
+
return super unless Mysql::Time === v
|
65
|
+
new_date(v.year, v.month, v.day)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
42
69
|
def extract_default(default)
|
43
70
|
if sql_type =~ /blob/i || type == :text
|
44
71
|
if default.blank?
|
@@ -132,7 +159,7 @@ module ActiveRecord
|
|
132
159
|
cattr_accessor :emulate_booleans
|
133
160
|
self.emulate_booleans = true
|
134
161
|
|
135
|
-
ADAPTER_NAME = 'MySQL'
|
162
|
+
ADAPTER_NAME = 'MySQL'
|
136
163
|
|
137
164
|
LOST_CONNECTION_ERROR_MESSAGES = [
|
138
165
|
"Server shutdown in progress",
|
@@ -140,10 +167,10 @@ module ActiveRecord
|
|
140
167
|
"Lost connection to MySQL server during query",
|
141
168
|
"MySQL server has gone away" ]
|
142
169
|
|
143
|
-
QUOTED_TRUE, QUOTED_FALSE = '1'
|
170
|
+
QUOTED_TRUE, QUOTED_FALSE = '1', '0'
|
144
171
|
|
145
172
|
NATIVE_DATABASE_TYPES = {
|
146
|
-
:primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY"
|
173
|
+
:primary_key => "int(11) DEFAULT NULL auto_increment PRIMARY KEY",
|
147
174
|
:string => { :name => "varchar", :limit => 255 },
|
148
175
|
:text => { :name => "text" },
|
149
176
|
:integer => { :name => "int", :limit => 4 },
|
@@ -161,6 +188,8 @@ module ActiveRecord
|
|
161
188
|
super(connection, logger)
|
162
189
|
@connection_options, @config = connection_options, config
|
163
190
|
@quoted_column_names, @quoted_table_names = {}, {}
|
191
|
+
@statements = {}
|
192
|
+
@client_encoding = nil
|
164
193
|
connect
|
165
194
|
end
|
166
195
|
|
@@ -168,14 +197,27 @@ module ActiveRecord
|
|
168
197
|
ADAPTER_NAME
|
169
198
|
end
|
170
199
|
|
200
|
+
def supports_bulk_alter? #:nodoc:
|
201
|
+
true
|
202
|
+
end
|
203
|
+
|
204
|
+
# Returns true, since this connection adapter supports prepared statement
|
205
|
+
# caching.
|
206
|
+
def supports_statement_cache?
|
207
|
+
true
|
208
|
+
end
|
209
|
+
|
210
|
+
# Returns true, since this connection adapter supports migrations.
|
171
211
|
def supports_migrations? #:nodoc:
|
172
212
|
true
|
173
213
|
end
|
174
214
|
|
215
|
+
# Returns true.
|
175
216
|
def supports_primary_key? #:nodoc:
|
176
217
|
true
|
177
218
|
end
|
178
219
|
|
220
|
+
# Returns true, since this connection adapter supports savepoints.
|
179
221
|
def supports_savepoints? #:nodoc:
|
180
222
|
true
|
181
223
|
end
|
@@ -198,8 +240,14 @@ module ActiveRecord
|
|
198
240
|
end
|
199
241
|
end
|
200
242
|
|
243
|
+
def type_cast(value, column)
|
244
|
+
return super unless value == true || value == false
|
245
|
+
|
246
|
+
value ? 1 : 0
|
247
|
+
end
|
248
|
+
|
201
249
|
def quote_column_name(name) #:nodoc:
|
202
|
-
@quoted_column_names[name] ||= "`#{name
|
250
|
+
@quoted_column_names[name] ||= "`#{name}`"
|
203
251
|
end
|
204
252
|
|
205
253
|
def quote_table_name(name) #:nodoc:
|
@@ -252,9 +300,12 @@ module ActiveRecord
|
|
252
300
|
|
253
301
|
def reconnect!
|
254
302
|
disconnect!
|
303
|
+
clear_cache!
|
255
304
|
connect
|
256
305
|
end
|
257
306
|
|
307
|
+
# Disconnects from the database if already connected. Otherwise, this
|
308
|
+
# method does nothing.
|
258
309
|
def disconnect!
|
259
310
|
@connection.close rescue nil
|
260
311
|
end
|
@@ -272,14 +323,105 @@ module ActiveRecord
|
|
272
323
|
|
273
324
|
def select_rows(sql, name = nil)
|
274
325
|
@connection.query_with_result = true
|
275
|
-
|
276
|
-
rows = []
|
277
|
-
result.each { |row| rows << row }
|
278
|
-
result.free
|
326
|
+
rows = exec_without_stmt(sql, name).rows
|
279
327
|
@connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
|
280
328
|
rows
|
281
329
|
end
|
282
330
|
|
331
|
+
# Clears the prepared statements cache.
|
332
|
+
def clear_cache!
|
333
|
+
@statements.values.each do |cache|
|
334
|
+
cache[:stmt].close
|
335
|
+
end
|
336
|
+
@statements.clear
|
337
|
+
end
|
338
|
+
|
339
|
+
if "<3".respond_to?(:encode)
|
340
|
+
# Taken from here:
|
341
|
+
# https://github.com/tmtm/ruby-mysql/blob/master/lib/mysql/charset.rb
|
342
|
+
# Author: TOMITA Masahiro <tommy@tmtm.org>
|
343
|
+
ENCODINGS = {
|
344
|
+
"armscii8" => nil,
|
345
|
+
"ascii" => Encoding::US_ASCII,
|
346
|
+
"big5" => Encoding::Big5,
|
347
|
+
"binary" => Encoding::ASCII_8BIT,
|
348
|
+
"cp1250" => Encoding::Windows_1250,
|
349
|
+
"cp1251" => Encoding::Windows_1251,
|
350
|
+
"cp1256" => Encoding::Windows_1256,
|
351
|
+
"cp1257" => Encoding::Windows_1257,
|
352
|
+
"cp850" => Encoding::CP850,
|
353
|
+
"cp852" => Encoding::CP852,
|
354
|
+
"cp866" => Encoding::IBM866,
|
355
|
+
"cp932" => Encoding::Windows_31J,
|
356
|
+
"dec8" => nil,
|
357
|
+
"eucjpms" => Encoding::EucJP_ms,
|
358
|
+
"euckr" => Encoding::EUC_KR,
|
359
|
+
"gb2312" => Encoding::EUC_CN,
|
360
|
+
"gbk" => Encoding::GBK,
|
361
|
+
"geostd8" => nil,
|
362
|
+
"greek" => Encoding::ISO_8859_7,
|
363
|
+
"hebrew" => Encoding::ISO_8859_8,
|
364
|
+
"hp8" => nil,
|
365
|
+
"keybcs2" => nil,
|
366
|
+
"koi8r" => Encoding::KOI8_R,
|
367
|
+
"koi8u" => Encoding::KOI8_U,
|
368
|
+
"latin1" => Encoding::ISO_8859_1,
|
369
|
+
"latin2" => Encoding::ISO_8859_2,
|
370
|
+
"latin5" => Encoding::ISO_8859_9,
|
371
|
+
"latin7" => Encoding::ISO_8859_13,
|
372
|
+
"macce" => Encoding::MacCentEuro,
|
373
|
+
"macroman" => Encoding::MacRoman,
|
374
|
+
"sjis" => Encoding::SHIFT_JIS,
|
375
|
+
"swe7" => nil,
|
376
|
+
"tis620" => Encoding::TIS_620,
|
377
|
+
"ucs2" => Encoding::UTF_16BE,
|
378
|
+
"ujis" => Encoding::EucJP_ms,
|
379
|
+
"utf8" => Encoding::UTF_8,
|
380
|
+
"utf8mb4" => Encoding::UTF_8,
|
381
|
+
}
|
382
|
+
else
|
383
|
+
ENCODINGS = Hash.new { |h,k| h[k] = k }
|
384
|
+
end
|
385
|
+
|
386
|
+
# Get the client encoding for this database
|
387
|
+
def client_encoding
|
388
|
+
return @client_encoding if @client_encoding
|
389
|
+
|
390
|
+
result = exec_query(
|
391
|
+
"SHOW VARIABLES WHERE Variable_name = 'character_set_client'",
|
392
|
+
'SCHEMA')
|
393
|
+
@client_encoding = ENCODINGS[result.rows.last.last]
|
394
|
+
end
|
395
|
+
|
396
|
+
def exec_query(sql, name = 'SQL', binds = [])
|
397
|
+
log(sql, name, binds) do
|
398
|
+
exec_stmt(sql, name, binds) do |cols, stmt|
|
399
|
+
ActiveRecord::Result.new(cols, stmt.to_a) if cols
|
400
|
+
end
|
401
|
+
end
|
402
|
+
end
|
403
|
+
|
404
|
+
def last_inserted_id(result)
|
405
|
+
@connection.insert_id
|
406
|
+
end
|
407
|
+
|
408
|
+
def exec_without_stmt(sql, name = 'SQL') # :nodoc:
|
409
|
+
# Some queries, like SHOW CREATE TABLE don't work through the prepared
|
410
|
+
# statement API. For those queries, we need to use this method. :'(
|
411
|
+
log(sql, name) do
|
412
|
+
result = @connection.query(sql)
|
413
|
+
cols = []
|
414
|
+
rows = []
|
415
|
+
|
416
|
+
if result
|
417
|
+
cols = result.fetch_fields.map { |field| field.name }
|
418
|
+
rows = result.to_a
|
419
|
+
result.free
|
420
|
+
end
|
421
|
+
ActiveRecord::Result.new(cols, rows)
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
283
425
|
# Executes an SQL query and returns a MySQL::Result object. Note that you have to free
|
284
426
|
# the Result object after you're done using it.
|
285
427
|
def execute(sql, name = nil) #:nodoc:
|
@@ -307,9 +449,18 @@ module ActiveRecord
|
|
307
449
|
@connection.affected_rows
|
308
450
|
end
|
309
451
|
|
452
|
+
def exec_delete(sql, name, binds)
|
453
|
+
log(sql, name, binds) do
|
454
|
+
exec_stmt(sql, name, binds) do |cols, stmt|
|
455
|
+
stmt.affected_rows
|
456
|
+
end
|
457
|
+
end
|
458
|
+
end
|
459
|
+
alias :exec_update :exec_delete
|
460
|
+
|
310
461
|
def begin_db_transaction #:nodoc:
|
311
|
-
|
312
|
-
rescue
|
462
|
+
exec_without_stmt "BEGIN"
|
463
|
+
rescue Mysql::Error
|
313
464
|
# Transactions aren't supported
|
314
465
|
end
|
315
466
|
|
@@ -348,6 +499,7 @@ module ActiveRecord
|
|
348
499
|
end
|
349
500
|
sql
|
350
501
|
end
|
502
|
+
deprecate :add_limit_offset!
|
351
503
|
|
352
504
|
# SCHEMA STATEMENTS ========================================
|
353
505
|
|
@@ -360,10 +512,13 @@ module ActiveRecord
|
|
360
512
|
|
361
513
|
select_all(sql).map do |table|
|
362
514
|
table.delete('Table_type')
|
363
|
-
|
515
|
+
sql = "SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}"
|
516
|
+
exec_without_stmt(sql).first['Create Table'] + ";\n\n"
|
364
517
|
end.join("")
|
365
518
|
end
|
366
519
|
|
520
|
+
# Drops the database specified on the +name+ attribute
|
521
|
+
# and creates it again using the provided +options+.
|
367
522
|
def recreate_database(name, options = {}) #:nodoc:
|
368
523
|
drop_database(name)
|
369
524
|
create_database(name, options)
|
@@ -384,6 +539,10 @@ module ActiveRecord
|
|
384
539
|
end
|
385
540
|
end
|
386
541
|
|
542
|
+
# Drops a MySQL database.
|
543
|
+
#
|
544
|
+
# Example:
|
545
|
+
# drop_database 'sebastian_development'
|
387
546
|
def drop_database(name) #:nodoc:
|
388
547
|
execute "DROP DATABASE IF EXISTS `#{name}`"
|
389
548
|
end
|
@@ -403,13 +562,8 @@ module ActiveRecord
|
|
403
562
|
end
|
404
563
|
|
405
564
|
def tables(name = nil, database = nil) #:nodoc:
|
406
|
-
|
407
|
-
|
408
|
-
sql = "SHOW TABLES "
|
409
|
-
sql << "IN #{quote_table_name(database)} " if database
|
410
|
-
|
411
|
-
result = execute(sql, 'SCHEMA')
|
412
|
-
result.each { |field| tables << field[0] }
|
565
|
+
result = execute(["SHOW TABLES", database].compact.join(' IN '), 'SCHEMA')
|
566
|
+
tables = result.collect { |field| field[0] }
|
413
567
|
result.free
|
414
568
|
tables
|
415
569
|
end
|
@@ -432,6 +586,7 @@ module ActiveRecord
|
|
432
586
|
super(table_name, options)
|
433
587
|
end
|
434
588
|
|
589
|
+
# Returns an array of indexes for the given table.
|
435
590
|
def indexes(table_name, name = nil)#:nodoc:
|
436
591
|
indexes = []
|
437
592
|
current_index = nil
|
@@ -450,11 +605,11 @@ module ActiveRecord
|
|
450
605
|
indexes
|
451
606
|
end
|
452
607
|
|
608
|
+
# Returns an array of +MysqlColumn+ objects for the table specified by +table_name+.
|
453
609
|
def columns(table_name, name = nil)#:nodoc:
|
454
610
|
sql = "SHOW FIELDS FROM #{quote_table_name(table_name)}"
|
455
|
-
|
456
|
-
|
457
|
-
result.each { |field| columns << MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") }
|
611
|
+
result = execute(sql, 'SCHEMA')
|
612
|
+
columns = result.collect { |field| MysqlColumn.new(field[0], field[4], field[1], field[2] == "YES") }
|
458
613
|
result.free
|
459
614
|
columns
|
460
615
|
end
|
@@ -463,15 +618,31 @@ module ActiveRecord
|
|
463
618
|
super(table_name, options.reverse_merge(:options => "ENGINE=InnoDB"))
|
464
619
|
end
|
465
620
|
|
621
|
+
# Renames a table.
|
622
|
+
#
|
623
|
+
# Example:
|
624
|
+
# rename_table('octopuses', 'octopi')
|
466
625
|
def rename_table(table_name, new_name)
|
467
626
|
execute "RENAME TABLE #{quote_table_name(table_name)} TO #{quote_table_name(new_name)}"
|
468
627
|
end
|
469
628
|
|
629
|
+
def bulk_change_table(table_name, operations) #:nodoc:
|
630
|
+
sqls = operations.map do |command, args|
|
631
|
+
table, arguments = args.shift, args
|
632
|
+
method = :"#{command}_sql"
|
633
|
+
|
634
|
+
if respond_to?(method)
|
635
|
+
send(method, table, *arguments)
|
636
|
+
else
|
637
|
+
raise "Unknown method called : #{method}(#{arguments.inspect})"
|
638
|
+
end
|
639
|
+
end.flatten.join(", ")
|
640
|
+
|
641
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} #{sqls}")
|
642
|
+
end
|
643
|
+
|
470
644
|
def add_column(table_name, column_name, type, options = {})
|
471
|
-
|
472
|
-
add_column_options!(add_column_sql, options)
|
473
|
-
add_column_position!(add_column_sql, options)
|
474
|
-
execute(add_column_sql)
|
645
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} #{add_column_sql(table_name, column_name, type, options)}")
|
475
646
|
end
|
476
647
|
|
477
648
|
def change_column_default(table_name, column_name, default) #:nodoc:
|
@@ -490,58 +661,24 @@ module ActiveRecord
|
|
490
661
|
end
|
491
662
|
|
492
663
|
def change_column(table_name, column_name, type, options = {}) #:nodoc:
|
493
|
-
|
494
|
-
|
495
|
-
unless options_include_default?(options)
|
496
|
-
options[:default] = column.default
|
497
|
-
end
|
498
|
-
|
499
|
-
unless options.has_key?(:null)
|
500
|
-
options[:null] = column.null
|
501
|
-
end
|
502
|
-
|
503
|
-
change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
504
|
-
add_column_options!(change_column_sql, options)
|
505
|
-
add_column_position!(change_column_sql, options)
|
506
|
-
execute(change_column_sql)
|
664
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} #{change_column_sql(table_name, column_name, type, options)}")
|
507
665
|
end
|
508
666
|
|
509
667
|
def rename_column(table_name, column_name, new_column_name) #:nodoc:
|
510
|
-
|
511
|
-
if column = columns(table_name).find { |c| c.name == column_name.to_s }
|
512
|
-
options[:default] = column.default
|
513
|
-
options[:null] = column.null
|
514
|
-
else
|
515
|
-
raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
|
516
|
-
end
|
517
|
-
current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
|
518
|
-
rename_column_sql = "ALTER TABLE #{quote_table_name(table_name)} CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
|
519
|
-
add_column_options!(rename_column_sql, options)
|
520
|
-
execute(rename_column_sql)
|
668
|
+
execute("ALTER TABLE #{quote_table_name(table_name)} #{rename_column_sql(table_name, column_name, new_column_name)}")
|
521
669
|
end
|
522
670
|
|
523
671
|
# Maps logical Rails types to MySQL-specific data types.
|
524
672
|
def type_to_sql(type, limit = nil, precision = nil, scale = nil)
|
525
|
-
|
526
|
-
|
527
|
-
|
528
|
-
|
529
|
-
|
530
|
-
|
531
|
-
|
532
|
-
|
533
|
-
|
534
|
-
end
|
535
|
-
when 'text'
|
536
|
-
case limit
|
537
|
-
when 0..0xff; 'tinytext'
|
538
|
-
when nil, 0x100..0xffff; 'text'
|
539
|
-
when 0x10000..0xffffff; 'mediumtext'
|
540
|
-
when 0x1000000..0xffffffff; 'longtext'
|
541
|
-
else raise(ActiveRecordError, "No text type has character length #{limit}")
|
542
|
-
end
|
543
|
-
else
|
544
|
-
super
|
673
|
+
return super unless type.to_s == 'integer'
|
674
|
+
|
675
|
+
case limit
|
676
|
+
when 1; 'tinyint'
|
677
|
+
when 2; 'smallint'
|
678
|
+
when 3; 'mediumint'
|
679
|
+
when nil, 4, 11; 'int(11)' # compatibility with MySQL default
|
680
|
+
when 5..8; 'bigint'
|
681
|
+
else raise(ActiveRecordError, "No integer type has byte size #{limit}")
|
545
682
|
end
|
546
683
|
end
|
547
684
|
|
@@ -562,7 +699,7 @@ module ActiveRecord
|
|
562
699
|
# Returns a table's primary key and belonging sequence.
|
563
700
|
def pk_and_sequence_for(table) #:nodoc:
|
564
701
|
keys = []
|
565
|
-
result = execute("describe #{quote_table_name(table)}")
|
702
|
+
result = execute("describe #{quote_table_name(table)}", 'SCHEMA')
|
566
703
|
result.each_hash do |h|
|
567
704
|
keys << h["Field"]if h["Key"] == "PRI"
|
568
705
|
end
|
@@ -579,6 +716,11 @@ module ActiveRecord
|
|
579
716
|
def case_sensitive_equality_operator
|
580
717
|
"= BINARY"
|
581
718
|
end
|
719
|
+
deprecate :case_sensitive_equality_operator
|
720
|
+
|
721
|
+
def case_sensitive_modifier(node)
|
722
|
+
Arel::Nodes::Bin.new(node)
|
723
|
+
end
|
582
724
|
|
583
725
|
def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
|
584
726
|
where_sql
|
@@ -611,7 +753,110 @@ module ActiveRecord
|
|
611
753
|
end
|
612
754
|
end
|
613
755
|
|
756
|
+
def add_column_sql(table_name, column_name, type, options = {})
|
757
|
+
add_column_sql = "ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
758
|
+
add_column_options!(add_column_sql, options)
|
759
|
+
add_column_position!(add_column_sql, options)
|
760
|
+
add_column_sql
|
761
|
+
end
|
762
|
+
|
763
|
+
def remove_column_sql(table_name, *column_names)
|
764
|
+
columns_for_remove(table_name, *column_names).map {|column_name| "DROP #{column_name}" }
|
765
|
+
end
|
766
|
+
alias :remove_columns_sql :remove_column
|
767
|
+
|
768
|
+
def change_column_sql(table_name, column_name, type, options = {})
|
769
|
+
column = column_for(table_name, column_name)
|
770
|
+
|
771
|
+
unless options_include_default?(options)
|
772
|
+
options[:default] = column.default
|
773
|
+
end
|
774
|
+
|
775
|
+
unless options.has_key?(:null)
|
776
|
+
options[:null] = column.null
|
777
|
+
end
|
778
|
+
|
779
|
+
change_column_sql = "CHANGE #{quote_column_name(column_name)} #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
|
780
|
+
add_column_options!(change_column_sql, options)
|
781
|
+
add_column_position!(change_column_sql, options)
|
782
|
+
change_column_sql
|
783
|
+
end
|
784
|
+
|
785
|
+
def rename_column_sql(table_name, column_name, new_column_name)
|
786
|
+
options = {}
|
787
|
+
|
788
|
+
if column = columns(table_name).find { |c| c.name == column_name.to_s }
|
789
|
+
options[:default] = column.default
|
790
|
+
options[:null] = column.null
|
791
|
+
else
|
792
|
+
raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
|
793
|
+
end
|
794
|
+
|
795
|
+
current_type = select_one("SHOW COLUMNS FROM #{quote_table_name(table_name)} LIKE '#{column_name}'")["Type"]
|
796
|
+
rename_column_sql = "CHANGE #{quote_column_name(column_name)} #{quote_column_name(new_column_name)} #{current_type}"
|
797
|
+
add_column_options!(rename_column_sql, options)
|
798
|
+
rename_column_sql
|
799
|
+
end
|
800
|
+
|
801
|
+
def add_index_sql(table_name, column_name, options = {})
|
802
|
+
index_name, index_type, index_columns = add_index_options(table_name, column_name, options)
|
803
|
+
"ADD #{index_type} INDEX #{index_name} (#{index_columns})"
|
804
|
+
end
|
805
|
+
|
806
|
+
def remove_index_sql(table_name, options = {})
|
807
|
+
index_name = index_name_for_remove(table_name, options)
|
808
|
+
"DROP INDEX #{index_name}"
|
809
|
+
end
|
810
|
+
|
811
|
+
def add_timestamps_sql(table_name)
|
812
|
+
[add_column_sql(table_name, :created_at, :datetime), add_column_sql(table_name, :updated_at, :datetime)]
|
813
|
+
end
|
814
|
+
|
815
|
+
def remove_timestamps_sql(table_name)
|
816
|
+
[remove_column_sql(table_name, :updated_at), remove_column_sql(table_name, :created_at)]
|
817
|
+
end
|
818
|
+
|
614
819
|
private
|
820
|
+
def exec_stmt(sql, name, binds)
|
821
|
+
cache = {}
|
822
|
+
if binds.empty?
|
823
|
+
stmt = @connection.prepare(sql)
|
824
|
+
else
|
825
|
+
cache = @statements[sql] ||= {
|
826
|
+
:stmt => @connection.prepare(sql)
|
827
|
+
}
|
828
|
+
stmt = cache[:stmt]
|
829
|
+
end
|
830
|
+
|
831
|
+
|
832
|
+
begin
|
833
|
+
stmt.execute(*binds.map { |col, val| type_cast(val, col) })
|
834
|
+
rescue Mysql::Error => e
|
835
|
+
# Older versions of MySQL leave the prepared statement in a bad
|
836
|
+
# place when an error occurs. To support older mysql versions, we
|
837
|
+
# need to close the statement and delete the statement from the
|
838
|
+
# cache.
|
839
|
+
stmt.close
|
840
|
+
@statements.delete sql
|
841
|
+
raise e
|
842
|
+
end
|
843
|
+
|
844
|
+
cols = nil
|
845
|
+
if metadata = stmt.result_metadata
|
846
|
+
cols = cache[:cols] ||= metadata.fetch_fields.map { |field|
|
847
|
+
field.name
|
848
|
+
}
|
849
|
+
end
|
850
|
+
|
851
|
+
result = yield [cols, stmt]
|
852
|
+
|
853
|
+
stmt.result_metadata.free if cols
|
854
|
+
stmt.free_result
|
855
|
+
stmt.close if binds.empty?
|
856
|
+
|
857
|
+
result
|
858
|
+
end
|
859
|
+
|
615
860
|
def connect
|
616
861
|
encoding = @config[:encoding]
|
617
862
|
if encoding
|
@@ -643,12 +888,9 @@ module ActiveRecord
|
|
643
888
|
execute("SET SQL_AUTO_IS_NULL=0", :skip_logging)
|
644
889
|
end
|
645
890
|
|
646
|
-
def select(sql, name = nil)
|
891
|
+
def select(sql, name = nil, binds = [])
|
647
892
|
@connection.query_with_result = true
|
648
|
-
|
649
|
-
rows = []
|
650
|
-
result.each_hash { |row| rows << row }
|
651
|
-
result.free
|
893
|
+
rows = exec_query(sql, name, binds).to_a
|
652
894
|
@connection.more_results && @connection.next_result # invoking stored procedures with CLIENT_MULTI_RESULTS requires this to tidy up else connection will be dropped
|
653
895
|
rows
|
654
896
|
end
|
@@ -657,6 +899,7 @@ module ActiveRecord
|
|
657
899
|
version[0] >= 5
|
658
900
|
end
|
659
901
|
|
902
|
+
# Returns the version of the connected MySQL server.
|
660
903
|
def version
|
661
904
|
@version ||= @connection.server_info.scan(/^(\d+)\.(\d+)\.(\d+)/).flatten.map { |v| v.to_i }
|
662
905
|
end
|